CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-np

A better npm publish tool with automated workflows, version bumping, testing, and git integration

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

git-operations.mddocs/

Git Operations

Comprehensive git integration for branch validation, tagging, history management, and pushing during the npm publishing workflow.

Capabilities

Git Information Functions

Functions for retrieving git repository information and status.

/**
 * Get the latest git tag in the repository
 * @returns Promise resolving to the latest tag string
 */
function latestTag(): Promise<string>;

/**
 * Get the git repository root directory
 * @returns Promise resolving to absolute path of git root
 */
function root(): Promise<string>;

/**
 * Get the name of the current git branch
 * @returns Promise resolving to current branch name
 */
function getCurrentBranch(): Promise<string>;

/**
 * Get the default branch name (main/master)
 * @returns Promise resolving to default branch name
 */
function defaultBranch(): Promise<string>;

/**
 * Check if current branch has an upstream
 * @returns Promise resolving to true if upstream exists
 */
function hasUpstream(): Promise<boolean>;

/**
 * Check if HEAD is in detached state
 * @returns Promise resolving to true if HEAD is detached
 */
function isHeadDetached(): Promise<boolean>;

Usage Examples:

import * as git from "np/source/git-util.js";

// Get repository information
const currentBranch = await git.getCurrentBranch();
const latestTag = await git.latestTag();
const gitRoot = await git.root();

console.log(`On branch: ${currentBranch}`);
console.log(`Latest tag: ${latestTag}`);
console.log(`Git root: ${gitRoot}`);

// Check repository state
const hasUpstream = await git.hasUpstream();
const isDetached = await git.isHeadDetached();

if (isDetached) {
  throw new Error("Cannot publish from detached HEAD");
}

Git Validation Functions

Functions for validating git repository state before publishing.

/**
 * Verify current branch is the designated release branch
 * @param releaseBranch - Name of the release branch (e.g., 'main', 'master')
 * @throws Error if not on release branch
 */
function verifyCurrentBranchIsReleaseBranch(releaseBranch: string): Promise<void>;

/**
 * Verify working tree has no uncommitted changes
 * @throws Error if working tree is dirty
 */
function verifyWorkingTreeIsClean(): Promise<void>;

/**
 * Verify remote history is clean (no unpushed/unpulled commits)
 * @throws Error if remote is out of sync
 */
function verifyRemoteHistoryIsClean(): Promise<void>;

/**
 * Verify remote repository is valid and accessible
 * @throws Error if remote is invalid
 */
function verifyRemoteIsValid(): Promise<void>;

/**
 * Verify git version meets minimum requirements
 * @throws Error if git version is too old
 */
function verifyRecentGitVersion(): Promise<void>;

/**
 * Verify tag does not exist on remote repository
 * @param tagName - Name of the tag to check
 * @throws Error if tag already exists on remote
 */
function verifyTagDoesNotExistOnRemote(tagName: string): Promise<void>;

Usage Examples:

import * as git from "np/source/git-util.js";

// Validate repository state before publishing
async function validateGitState(releaseBranch = "main") {
  try {
    // Check branch
    await git.verifyCurrentBranchIsReleaseBranch(releaseBranch);
    
    // Check working tree
    await git.verifyWorkingTreeIsClean();
    
    // Check remote sync
    await git.verifyRemoteHistoryIsClean();
    
    // Check git version
    await git.verifyRecentGitVersion();
    
    console.log("Git validation passed");
  } catch (error) {
    console.error(`Git validation failed: ${error.message}`);
    throw error;
  }
}

// Check if tag already exists
async function checkTagAvailability(version) {
  const tagName = `v${version}`;
  try {
    await git.verifyTagDoesNotExistOnRemote(tagName);
    console.log(`Tag ${tagName} is available`);
  } catch (error) {
    throw new Error(`Tag ${tagName} already exists on remote`);
  }
}

Git Operations Functions

Functions for performing git operations during the publishing workflow.

/**
 * Fetch latest changes from remote repository
 * @returns Promise that resolves when fetch completes
 */
function fetch(): Promise<void>;

/**
 * Push commits and tags to remote with graceful error handling
 * @param remoteIsOnGitHub - True if remote is hosted on GitHub
 * @returns Promise resolving to push result object
 */
function pushGraceful(remoteIsOnGitHub: boolean): Promise<GitPushResult>;

/**
 * Delete a git tag locally and remotely
 * @param tagName - Name of the tag to delete
 * @returns Promise that resolves when tag is deleted
 */
function deleteTag(tagName: string): Promise<void>;

/**
 * Remove the last commit from current branch
 * @returns Promise that resolves when commit is removed
 */
function removeLastCommit(): Promise<void>;

Usage Examples:

import * as git from "np/source/git-util.js";

// Fetch before validation
await git.fetch();

// Push with error handling
const pushResult = await git.pushGraceful(true); // GitHub remote
if (pushResult.reason) {
  console.warn(`Push warning: ${pushResult.reason}`);
}

// Rollback operations
async function rollbackRelease(tagName) {
  try {
    await git.deleteTag(tagName);
    await git.removeLastCommit();
    console.log("Successfully rolled back release");
  } catch (error) {
    console.error(`Rollback failed: ${error.message}`);
  }
}

Git History Functions

Functions for analyzing git history and file changes.

/**
 * Get files that have changed since the last release
 * @param rootDirectory - Root directory of the project
 * @returns Promise resolving to array of changed file paths
 */
function newFilesSinceLastRelease(rootDirectory: string): Promise<string[]>;

/**
 * Read file content from the last release
 * @param file - Path to the file
 * @returns Promise resolving to file content from last release
 */
function readFileFromLastRelease(file: string): Promise<string>;

/**
 * Get the previous tag or first commit if no tags exist
 * @returns Promise resolving to previous tag or commit hash
 */
function previousTagOrFirstCommit(): Promise<string>;

/**
 * Get the latest tag or first commit if no tags exist
 * @returns Promise resolving to latest tag or commit hash
 */
function latestTagOrFirstCommit(): Promise<string>;

/**
 * Get commit log from a specific revision to HEAD
 * @param revision - Git revision (tag, commit hash, branch)
 * @returns Promise resolving to formatted commit log string
 */
function commitLogFromRevision(revision: string): Promise<string>;

Usage Examples:

import * as git from "np/source/git-util.js";

// Analyze changes since last release
async function analyzeChanges(rootDirectory) {
  const changedFiles = await git.newFilesSinceLastRelease(rootDirectory);
  const previousTag = await git.previousTagOrFirstCommit();
  
  console.log(`Changed files since ${previousTag}:`);
  changedFiles.forEach(file => console.log(`  ${file}`));
  
  // Get commit history
  const commits = await git.commitLogFromRevision(previousTag);
  console.log(`Commits since last release: ${commits.length}`);
}

// Compare file versions
async function compareFileVersion(filePath) {
  try {
    const lastReleaseContent = await git.readFileFromLastRelease(filePath);
    const currentContent = require('fs').readFileSync(filePath, 'utf8');
    
    if (lastReleaseContent !== currentContent) {
      console.log(`${filePath} has changed since last release`);
    }
  } catch (error) {
    console.log(`${filePath} is a new file`);
  }
}

Git Workflow Integration

Pre-publish Validation

Git validation steps performed before publishing:

  1. Git Version Check: Ensure git version ≥ 2.11.0
  2. Remote Validation: Verify remote repository is accessible
  3. Branch Validation: Confirm on correct release branch
  4. Working Tree: Ensure no uncommitted changes
  5. Remote Sync: Verify local and remote are in sync
  6. Tag Availability: Check version tag doesn't exist

During Publishing

Git operations during the publishing workflow:

  1. Fetch: Update remote tracking information
  2. Version Bump: Package manager creates version commit and tag
  3. Validation: Verify tag was created successfully
  4. Push: Push commits and tags to remote

Post-publish Operations

Git operations after successful publishing:

  1. Push Verification: Confirm push was successful
  2. Release Notes: Generate release notes from commits
  3. GitHub Integration: Create release draft if configured

Error Recovery

Git rollback operations on publishing failure:

  1. Tag Deletion: Remove version tag if created
  2. Commit Removal: Remove version bump commit
  3. State Restoration: Return to pre-publish state

Configuration

Branch Configuration

Configure release branch behavior:

// Default branch detection
const branch = await git.defaultBranch(); // 'main' or 'master'

// Custom branch
const options = {
  branch: 'release',
  anyBranch: false  // Strict branch enforcement
};

// Allow any branch
const options = {
  anyBranch: true   // Skip branch validation
};

Remote Configuration

Handle different remote configurations:

// GitHub integration
const isGitHub = hostedGitInfo.fromUrl(repoUrl)?.type === 'github';
await git.pushGraceful(isGitHub);

// Custom remote handling
if (!await git.hasUpstream()) {
  console.warn('No upstream configured, skipping push');
}

Tag Configuration

Configure git tag behavior:

// Tag prefix from package manager
const tagPrefix = await getTagVersionPrefix(packageManager);
const tagName = `${tagPrefix}${version}`; // e.g., 'v1.2.3'

// Custom tag message
const tagMessage = options.message?.replace('%s', version) || version;

Error Conditions

Common Git Errors

Branch Errors:

  • Not on release branch
  • Detached HEAD state
  • Branch doesn't exist

Working Tree Errors:

  • Uncommitted changes
  • Untracked files in publish directory
  • Merge conflicts

Remote Errors:

  • No remote configured
  • Remote unreachable
  • Authentication failures
  • Push/pull conflicts

Tag Errors:

  • Tag already exists locally
  • Tag already exists on remote
  • Invalid tag format

Error Messages

Git utilities provide detailed error messages:

// Branch validation error
"Not on release branch. Currently on 'feature-branch', expected 'main'"

// Working tree error  
"Working tree is not clean. Uncommitted changes in: package.json, src/index.js"

// Remote sync error
"Remote history has diverged. Run 'git pull' to sync with remote"

// Tag conflict error
"Tag 'v1.2.3' already exists on remote. Use a different version or delete the existing tag"

Install with Tessl CLI

npx tessl i tessl/npm-np

docs

configuration.md

core-publishing.md

git-operations.md

index.md

npm-operations.md

package-manager.md

utilities.md

version-management.md

tile.json