CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-semantic-release--github

semantic-release plugin to publish a GitHub release and comment on released Pull Requests/Issues

Pending
Overview
Eval results
Files

asset-management.mddocs/

Asset Management

File asset handling system supporting glob patterns, metadata, and upload management for GitHub releases. The asset management system provides flexible file selection with powerful globbing capabilities and customizable metadata.

Capabilities

Asset Globbing

Resolves glob patterns and file paths into concrete asset objects ready for upload to GitHub releases.

/**
 * Resolves glob patterns for asset files
 * @param context - Context object containing current working directory
 * @param assets - Array of asset configurations with paths/globs
 * @returns Promise resolving to array of concrete asset file paths
 */
async function globAssets(
  context: { cwd: string },
  assets: AssetConfig[]
): Promise<ResolvedAsset[]>;

type AssetConfig = string | {
  path: string | string[];
  name?: string;
  label?: string;
};

interface ResolvedAsset {
  path: string;
  name?: string;
  label?: string;
}

Glob Pattern Support:

  • Standard glob patterns (*.js, **/*.json)
  • Multiple patterns per asset (["dist/*.js", "docs/*.pdf"])
  • Negation patterns (!**/*.test.js)
  • Directory expansion with automatic recursion
  • Unique file deduplication across patterns

Usage Examples:

import globAssets from "@semantic-release/github/lib/glob-assets.js";

// Simple glob patterns
const assets1 = await globAssets(
  { cwd: "/project" },
  [
    "dist/*.js",
    "docs/*.pdf",
    "build/assets/*.css"
  ]
);
// Returns: [{ path: "dist/app.js" }, { path: "docs/guide.pdf" }, ...]

// Complex asset configurations
const assets2 = await globAssets(
  { cwd: "/project" },
  [
    {
      path: ["dist/*.js", "dist/*.js.map"],
      label: "JavaScript Distribution"
    },
    {
      path: "docs/**/*.pdf",
      name: "documentation-${nextRelease.version}.pdf", // Template support
      label: "Documentation Bundle"
    }
  ]
);

// Mixed string and object configurations
const assets3 = await globAssets(
  { cwd: "/project" },
  [
    "README.md",                    // Simple file
    "dist/*.zip",                   // Glob pattern
    {
      path: "build/**/*.{js,css}",  // Complex glob with metadata
      label: "Built Assets"
    }
  ]
);

Asset Configuration Types

Different ways to configure assets for release uploads.

// String configuration - simple file path or glob
type StringAsset = string;

// Object configuration - path with metadata
interface ObjectAsset {
  /** File path or glob pattern(s) */
  path: string | string[];
  
  /** Custom filename for uploaded asset (supports templates) */
  name?: string;
  
  /** Human-readable label shown in GitHub release UI */
  label?: string;
}

// Combined asset configuration type
type AssetConfig = StringAsset | ObjectAsset;

Configuration Examples:

// String configurations
const stringAssets = [
  "dist/app.js",                    // Single file
  "build/*.zip",                    // Glob pattern
  "docs/api.pdf"                    // Documentation file
];

// Object configurations with metadata
const objectAssets = [
  {
    path: "dist/app.min.js",
    name: "app-${nextRelease.version}.min.js",
    label: "Minified Application Bundle"
  },
  {
    path: ["src/**/*.ts", "!src/**/*.test.ts"],
    name: "source-code.zip",
    label: "TypeScript Source Code"
  },
  {
    path: "coverage/lcov-report/**/*",
    label: "Test Coverage Report"
  }
];

// Mixed configurations
const mixedAssets = [
  "LICENSE",                        // Simple file
  "CHANGELOG.md",                   // Another simple file
  {
    path: "dist/*.{js,css,map}",
    label: "Distribution Files"
  }
];

Asset Processing Pipeline

The asset processing pipeline handles glob resolution, file validation, and metadata preparation.

/**
 * Asset processing pipeline stages:
 * 1. Glob expansion - Convert patterns to file lists
 * 2. Directory handling - Expand directories to files  
 * 3. File validation - Check existence and accessibility
 * 4. Deduplication - Remove duplicate file paths
 * 5. Metadata preparation - Apply names and labels
 * 6. Template processing - Resolve template variables
 */

interface ProcessingStages {
  /** Expand glob patterns using globby library */
  expandGlobs(patterns: string[], options: GlobOptions): Promise<string[]>;
  
  /** Validate files exist and are readable */
  validateFiles(paths: string[]): Promise<string[]>;
  
  /** Remove duplicate file paths */
  deduplicateFiles(paths: string[]): string[];
  
  /** Apply asset metadata and templates */
  prepareMetadata(assets: AssetConfig[], files: string[]): ResolvedAsset[];
}

Processing Example:

// Input configuration
const assetConfig = {
  path: ["dist/**/*.js", "!dist/**/*.test.js"],
  name: "${basename}-${nextRelease.version}${extname}",
  label: "JavaScript Distribution"
};

// Processing stages:
// 1. Glob expansion: ["dist/app.js", "dist/utils.js", "dist/app.test.js"]
// 2. Negation filtering: ["dist/app.js", "dist/utils.js"] 
// 3. File validation: Check files exist and are readable
// 4. Metadata application: Add name templates and labels
// 5. Result: [
//      { path: "dist/app.js", name: "app-1.0.0.js", label: "JavaScript Distribution" },
//      { path: "dist/utils.js", name: "utils-1.0.0.js", label: "JavaScript Distribution" }
//    ]

Upload Process

Asset upload handling within the publish lifecycle function.

/**
 * Asset upload process during release publication:
 * 1. Create draft release
 * 2. Resolve asset globs
 * 3. Upload assets in parallel
 * 4. Publish release (if not draft mode)
 */

interface UploadProcess {
  /** File stat checking and validation */
  validateAssetFile(filePath: string): Promise<FileStats>;
  
  /** MIME type detection for proper Content-Type headers */
  detectMimeType(filePath: string): string;
  
  /** Asset upload with retry logic */
  uploadAsset(asset: ResolvedAsset, uploadUrl: string): Promise<AssetResponse>;
  
  /** Parallel upload coordination */
  uploadAllAssets(assets: ResolvedAsset[]): Promise<AssetResponse[]>;
}

interface FileStats {
  size: number;
  isFile: boolean;
  isDirectory: boolean;
}

interface AssetResponse {
  id: number;
  name: string;
  size: number;
  download_count: number;
  browser_download_url: string;
}

Upload Error Handling:

// The upload process includes comprehensive error handling:

try {
  await uploadAsset(asset, uploadUrl);
} catch (error) {
  if (error.code === 'ENOENT') {
    logger.error(`Asset file not found: ${asset.path}`);
    // File is skipped, upload continues
  } else if (error.status === 422) {
    logger.error(`Asset already exists: ${asset.name}`);
    // GitHub rejects duplicate asset names
  } else if (error.status === 413) {
    logger.error(`Asset too large: ${asset.path}`);
    // GitHub has file size limits
  } else {
    // Other errors are retried or escalated
    throw error;
  }
}

Template Support

Asset names and labels support Lodash templates for dynamic content generation.

// Available template variables in asset configurations:
interface AssetTemplateContext {
  nextRelease: {
    version: string;      // "1.0.0"
    gitTag: string;       // "v1.0.0"
    name: string;         // "1.0.0"
    channel?: string;     // Release channel
  };
  branch: {
    name: string;         // "main"
  };
  // File path utilities (for name templates)
  basename: string;       // "app.js" from "dist/app.js"
  extname: string;        // ".js" from "dist/app.js"
  dirname: string;        // "dist" from "dist/app.js"
  filename: string;       // "app" from "dist/app.js"
}

Template Examples:

{
  // Version-based naming
  name: "${basename}-v${nextRelease.version}${extname}",
  // "app.js" becomes "app-v1.0.0.js"
  
  // Channel-aware naming
  name: "${filename}-${nextRelease.channel || 'stable'}${extname}",
  // "app.js" becomes "app-stable.js" or "app-beta.js"
  
  // Complex naming with metadata
  name: "${dirname}-${filename}-${nextRelease.version}-${branch.name}${extname}",
  // "dist/app.js" becomes "dist-app-1.0.0-main.js"
  
  // Dynamic labels
  label: "${basename} (${nextRelease.version}${nextRelease.channel ? ` - ${nextRelease.channel}` : ''})"
  // "App Bundle (1.0.0)" or "App Bundle (1.0.0 - beta)"
}

Advanced Patterns

Complex asset management patterns for sophisticated release workflows.

Conditional Assets:

// Assets based on build configuration
const assets = [
  // Always include core files
  "LICENSE",
  "README.md",
  
  // Conditional inclusion based on environment
  ...(process.env.NODE_ENV === 'production' ? [
    "dist/app.min.js",
    "dist/app.min.css"
  ] : [
    "dist/app.js", 
    "dist/app.css"
  ]),
  
  // Platform-specific assets
  {
    path: "build/darwin/**/*",
    label: "macOS Distribution"
  },
  {
    path: "build/win32/**/*", 
    label: "Windows Distribution"
  }
];

Multi-Architecture Builds:

const architectures = ['x64', 'arm64', 'x86'];

const assets = architectures.flatMap(arch => [
  {
    path: `dist/${arch}/app`,
    name: `app-${arch}-${nextRelease.version}`,
    label: `Application (${arch.toUpperCase()})`
  },
  {
    path: `dist/${arch}/app.exe`,
    name: `app-${arch}-${nextRelease.version}.exe`, 
    label: `Application (${arch.toUpperCase()} Windows)`
  }
]);

Documentation Bundles:

const assets = [
  // Individual documentation files
  {
    path: "docs/**/*.md",
    label: "Individual Documentation Files"
  },
  
  // Generated documentation bundle
  {
    path: "docs-build/api-docs.zip",
    name: "api-documentation-${nextRelease.version}.zip",
    label: "Complete API Documentation"
  },
  
  // Coverage reports
  {
    path: "coverage/lcov.info",
    name: "test-coverage-${nextRelease.version}.lcov",
    label: "Test Coverage Data"
  }
];

Performance Considerations

Asset management includes several performance optimizations:

  • Parallel Uploads: Multiple assets uploaded simultaneously
  • Glob Caching: File system scans cached within single run
  • File Streaming: Large files streamed rather than loaded into memory
  • Resume Support: Failed uploads can be resumed (GitHub API dependent)
  • Progress Reporting: Upload progress logged for large files
  • Size Validation: File size checked before upload attempt

Install with Tessl CLI

npx tessl i tessl/npm-semantic-release--github

docs

asset-management.md

configuration.md

github-integration.md

index.md

plugin-lifecycle.md

utilities.md

tile.json