Skip to content

feat: Add VS Code Extension CI/CD Infrastructure#158

Merged
madhur310 merged 96 commits into
mainfrom
feat/add-vscode-extension-ci
Jul 14, 2026
Merged

feat: Add VS Code Extension CI/CD Infrastructure#158
madhur310 merged 96 commits into
mainfrom
feat/add-vscode-extension-ci

Conversation

@madhur310

Copy link
Copy Markdown
Contributor

Draft PR to test VS Code CI infrastructure. Will update description after testing.

@madhur310 madhur310 force-pushed the feat/add-vscode-extension-ci branch from a473f60 to 1c725e4 Compare June 26, 2026 14:36
@madhur310 madhur310 marked this pull request as ready for review June 29, 2026 18:15
@madhur310 madhur310 requested a review from a team as a code owner June 29, 2026 18:15
madhur310 added a commit to forcedotcom/apex-language-support that referenced this pull request Jun 29, 2026
Extracts manual-publish.yml and promote-stable.yml to the shared
salesforcecli/github-workflows repository. Both workflows are now
consumed as reusable workflows.

Changes:
- manual-publish.yml: 792 lines → 112 lines (85% reduction)
- promote-stable.yml: 415 lines → 40 lines (90% reduction)

The shared workflows are parameterized to support:
- Different extension names and VSIX patterns
- Configurable quality checks and CI requirements
- Flexible artifact locations and naming
- Dry-run mode for testing

Related to salesforcecli/github-workflows#158

@peternhale peternhale left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reusability Review

This PR provides a comprehensive VS Code extension CI/CD infrastructure with excellent architectural patterns. However, several issues prevent it from being fully reusable across different extension projects.

Critical Issues (Must Fix)

  1. Branch-specific action references - All references will break after this branch is merged
  2. Hardcoded Apex/Salesforce messaging - Slack notifications contain "Apex Language Support" text
  3. Missing action definitions - Several referenced actions are not included in this PR
  4. Incomplete documentation - Required secrets and prerequisites not fully documented

Medium Priority

  1. Node.js version not parameterized in several workflows
  2. Coupled versioning strategy - Odd/even minor version logic may not apply to all extensions
  3. Inconsistent action reference style - Mix of local and remote references

See inline comments for specific recommendations.

@peternhale peternhale left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reusability Review

This PR provides a comprehensive VS Code extension CI/CD infrastructure with excellent architectural patterns. However, several issues prevent it from being fully reusable across different extension projects.

Critical Issues (Must Fix)

  1. Branch-specific action references - All references to the feature branch will break after merge
  2. Hardcoded Apex/Salesforce messaging - Slack notifications contain "Apex Language Support" text
  3. Missing action definitions - Several referenced actions are not included in this PR
  4. Incomplete documentation - Required secrets and prerequisites not fully documented

Medium Priority

  1. Node.js version not parameterized in several workflows
  2. Coupled versioning strategy - Odd/even minor version logic may not apply to all extensions
  3. Inconsistent action reference style - Mix of local and remote references

See inline comments for specific recommendations.

@peternhale peternhale left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reusability Review

This PR provides a comprehensive VS Code extension CI/CD infrastructure with excellent architectural patterns. However, several issues prevent it from being fully reusable across different extension projects.


✅ Strengths (Maintain These Patterns)

  • Strong workflow composition - Clear separation of CI, package, publish, promote workflows
  • Excellent idempotency - Safe to re-run workflows (checks for existing releases/assets)
  • Comprehensive dry-run support - Excellent for testing without side effects
  • Parameterized directory structure - extensions-root input enables flexibility

❌ Critical Issues (Must Fix Before Merge)

1. Branch-Specific References Will Break After Merge

Files affected:

  • .github/workflows/vscode-ci-template.yml (lines 354, 424, 434)
  • .github/workflows/vscode-promote-prerelease.yml (lines 1822, 1849, 1880)
  • .github/workflows/vscode-publish-extensions.yml (lines 2560, 2640, 2930, 2953, 3142)
  • Multiple other workflows

All references to @feat/add-vscode-extension-ci will fail after this branch is merged.

Fix: Update all workflow references to @main or use version tags:

uses: salesforcecli/github-workflows/.github/workflows/vscode-package.yml@main

2. Hardcoded Apex/Salesforce-Specific Content

File: .github/workflows/vscode-publish-extensions.yml (line ~3435)

Slack notification contains hardcoded "🎉 Apex Language Support Extensions Released Successfully!" message.

Fix: Make notification messages configurable:

inputs:
  slack-notification-title:
    description: 'Title for Slack notifications'
    required: false
    default: '🎉 Extensions Released Successfully!'
    type: string

3. Missing Action Definitions

File: .github/workflows/vscode-manual-publish.yml (line 514)

Workflows reference actions not included in this PR:

  • ./.github/actions/npm-install-with-retries
  • ./.github/actions/npmInstallWithRetries (note casing inconsistency)
  • ./.github/actions/check-ci-status
  • ./.github/actions/repackage-vsix-stable

Fix: Either include these actions in this PR, or clearly document which actions consuming repos must provide vs. which are in this shared workflows repo.

4. Incomplete Documentation

File: README.md (starting line 3764)

Missing critical information for consuming repositories:

  • Prerequisites (which actions must exist)
  • Complete secrets documentation (purpose of each secret)
  • Repository structure expectations
  • Versioning strategy explanation
  • Architecture/workflow composition diagram

Fix: Add a comprehensive "Prerequisites and Setup" section.


⚠️ Medium Priority Issues

5. Node.js Version Not Parameterized

Files: Most workflows (.github/workflows/vscode-manual-publish.yml line 729, etc.)

Node.js 22.x is hardcoded in multiple workflows. This is already correctly parameterized in vscode-package.yml but missing from others.

Fix: Add to all workflows:

inputs:
  node-version:
    description: 'Node.js version to use'
    required: false
    default: '22.x'
    type: string

6. Coupled Versioning Strategy

File: .github/workflows/vscode-publish-extensions.yml (line ~2669)

The odd/even minor version strategy is deeply embedded in version bumping logic. This convention may not apply to all VS Code extensions.

Recommendation: Extract into a configurable strategy or allow custom scripts.

7. Inconsistent Action Reference Style

Files: Multiple workflows

Mix of local references (./.github/actions/...) and remote references with inconsistent naming (npmInstallWithRetries vs npm-install-with-retries).

Fix: Standardize on one approach and document the pattern.


📋 Action Items Summary

Before this PR can be merged:

  1. ✅ Update all @feat/add-vscode-extension-ci references to @main
  2. ✅ Parameterize Slack notification messages
  3. ✅ Include missing actions OR document what consuming repos must provide
  4. ✅ Expand README documentation with prerequisites, secrets, and architecture
  5. ⚠️ Consider parameterizing Node.js version across all workflows
  6. ⚠️ Document or extract the odd/even versioning strategy
  7. ⚠️ Standardize action reference style

Overall, this is a solid foundation with excellent architectural patterns. The main blockers are documentation gaps and a few hardcoded assumptions that limit reusability.

madhur310 added a commit that referenced this pull request Jul 9, 2026
Addresses all critical and medium priority issues from PR #158 review:

## Critical Issues Fixed:

1. **Branch references updated to @main**
   - Changed all 17 occurrences of @feat/add-vscode-extension-ci to @main
   - Workflows will now work correctly after branch merge

2. **Parameterized Slack notifications**
   - Added slack-notification-title input to vscode-publish-extensions.yml
   - Removed hardcoded "Apex Language Support" messages
   - Default: "🎉 Extensions Released Successfully!"

3. **Documented missing actions**
   - Clarified which workflows require local actions from calling repos
   - Added detailed descriptions for required actions:
     - npm-install-with-retries (custom retry logic)
     - check-ci-status (CI validation)
     - repackage-vsix-stable (VSIX repackaging)
     - publish-vsix (marketplace publishing)
   - Noted that most workflows are self-contained

4. **Expanded README documentation**
   - Added comprehensive Prerequisites section with:
     - Required secrets (IDEE_GH_TOKEN, VSCE_PERSONAL_ACCESS_TOKEN, IDEE_OVSX_PAT)
     - Repository structure requirements
     - Optional local actions
   - Added Architecture Overview explaining workflow composition
   - Added Versioning Strategy section explaining odd/even convention
   - Added Common Inputs section for shared parameters
   - Enhanced each workflow description with usage examples and requirements

## Medium Priority Issues Fixed:

5. **Parameterized Node.js version**
   - Added node-version input to all workflows (default: '22.x')
   - Updated all hardcoded '22.x' references to use input parameter
   - Workflows: vscode-publish-extensions, vscode-promote-prerelease,
     vscode-manual-publish, vscode-promote-stable, vscode-release-explicit

6. **Documented versioning strategy**
   - Added detailed inline documentation in vscode-publish-extensions.yml
   - Explains odd/even minor version convention
   - Documents version bump logic (major, minor, patch, auto)
   - Notes when customization might be needed

All workflows now follow consistent patterns and are fully reusable across
different VS Code extension projects without hardcoded assumptions.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@madhur310 madhur310 requested a review from peternhale July 10, 2026 14:36
@peternhale

Copy link
Copy Markdown
Contributor

Missing Implementation: extensions: 'changed' Change Detection

I've reviewed the extension publishing workflows in this PR against the reference implementation in apex-language-support-my-work and identified a critical missing feature: the extensions: 'changed' parameter is documented but not implemented.

Current State

vscode-publish-extensions.yml documents the feature:

extensions:
  description: 'Extensions to release (all, changed, or comma-separated extension names)'
  required: false
  default: 'changed'
  type: string

However, the workflow simply passes this value through to the version bumping logic without any change detection:

  • Line 59: Parameter accepts 'changed' as a value
  • Line 214: SELECTED_EXTENSIONS: ${{ inputs.extensions }} is passed directly to bash script
  • Lines 352-382: Version bump logic iterates over $SELECTED_EXTENSIONS but doesn't detect changes

Result: If a caller passes extensions: 'changed', the workflow will literally look for an extension named "changed" and fail to find it.

Reference Implementation

The apex-language-support-my-work repository implements this feature using a dedicated determine-changes job with TypeScript-based change detection:

Workflow structure (nightly-extensions.yml:78-106):

determine-changes:
  runs-on: ubuntu-latest
  outputs:
    selected-extensions: ${{ steps.changes.outputs.selected-extensions }}
    version-bumps: ${{ steps.changes.outputs.version-bumps }}
  steps:
    - name: Determine changes and version bumps
      id: changes
      env:
        IS_NIGHTLY: 'true'
        VERSION_BUMP: 'auto'
        SELECTED_EXTENSIONS: ${{ inputs.extensions }}
      run: |
        npx tsx .github/scripts/index.ts ext-change-detector

bump-versions:
  needs: [determine-changes]  # ← Depends on change detection
  if: needs.determine-changes.outputs.selected-extensions != ''
  env:
    SELECTED_EXTENSIONS: ${{ needs.determine-changes.outputs.selected-extensions }}

Change Detection Logic (.github/scripts/ext-change-detector.ts):

  1. Discovery: Scans packages/ for VS Code extensions (packages with publisher field)
  2. Git-based detection: For each extension, finds last release tag using pattern {extension-name}-v{version} and runs git diff against that tag
  3. Selection handling:
    • 'all' → returns all available extensions
    • 'changed' → returns extensions with git diff changes since last tag
    • 'none' → returns empty list
    • Specific names → validates and returns those extensions
  4. Conventional commit analysis: For version-bump: auto, analyzes commit messages to determine bump type (major/minor/patch)

Impact

Without this implementation:

  • ❌ Callers cannot use extensions: 'changed' despite documentation
  • ❌ No automatic detection of which extensions need publishing
  • ❌ Manual specification of extension names required for every release
  • ❌ Nightly builds cannot automatically publish only changed extensions

Recommended Fix

Option A: Port TypeScript Implementation (matches apex-language-support pattern)

  1. Add .github/scripts/ directory with change detection logic
  2. Add determine-changes job before bump-versions:
determine-changes:
  runs-on: ubuntu-latest
  outputs:
    selected-extensions: ${{ steps.changes.outputs.selected-extensions }}
    version-bumps: ${{ steps.changes.outputs.version-bumps }}
  steps:
    - uses: actions/checkout@v6
      with:
        fetch-depth: 0  # Need full history for git diff
    - uses: actions/setup-node@v6
      with:
        node-version: '22.x'
    - uses: salesforcecli/github-workflows/.github/actions/npmInstallWithRetries@main
    - name: Detect changes
      id: changes
      env:
        SELECTED_EXTENSIONS: ${{ inputs.extensions }}
        VERSION_BUMP: ${{ inputs.version-bump }}
        EXTENSIONS_ROOT: ${{ inputs.extensions-root || 'packages' }}
      run: |
        npx tsx .github/scripts/ext-change-detector.ts
  1. Update bump-versions to depend on it:
bump-versions:
  needs: [determine-changes]
  if: needs.determine-changes.outputs.selected-extensions != ''
  env:
    SELECTED_EXTENSIONS: ${{ needs.determine-changes.outputs.selected-extensions }}
    VERSION_BUMP: ${{ needs.determine-changes.outputs.version-bumps }}

Option B: Implement in Bash (simpler dependencies)

Add change detection step before version bumping:

- name: Detect changed extensions
  id: detect-changes
  env:
    USER_INPUT: ${{ inputs.extensions }}
    EXTENSIONS_ROOT: ${{ inputs.extensions-root || 'packages' }}
  run: |
    # Handle special values
    if [ "$USER_INPUT" = "all" ]; then
      CHANGED=$(ls -1 "$EXTENSIONS_ROOT" | paste -sd,)
      echo "selected-extensions=$CHANGED" >> $GITHUB_OUTPUT
      exit 0
    fi
    
    if [ "$USER_INPUT" = "none" ]; then
      echo "selected-extensions=" >> $GITHUB_OUTPUT
      exit 0
    fi
    
    # Detect changes
    if [ "$USER_INPUT" = "changed" ]; then
      CHANGED=""
      for ext_dir in "$EXTENSIONS_ROOT"/*/; do
        ext_name=$(basename "$ext_dir")
        
        # Find last tag for this extension
        last_tag=$(git tag -l "${ext_name}-v*" --sort=-v:refname | head -1)
        
        # Check for changes since last tag
        if [ -z "$last_tag" ]; then
          # No previous tag - first release
          CHANGED="${CHANGED}${CHANGED:+,}${ext_name}"
        elif ! git diff --quiet "$last_tag" HEAD -- "$ext_dir"; then
          # Has changes since last tag
          CHANGED="${CHANGED}${CHANGED:+,}${ext_name}"
        fi
      done
      echo "selected-extensions=$CHANGED" >> $GITHUB_OUTPUT
    else
      # User specified explicit names
      echo "selected-extensions=$USER_INPUT" >> $GITHUB_OUTPUT
    fi

Additional Findings

Package-lock.json Updates Missing

The version bump logic updates package.json files but doesn't update package-lock.json, which can cause dependency drift. The apex-language-support implementation includes:

npm install --package-lock-only --ignore-scripts --no-audit
git add package-lock.json

Per-Extension Tag Pattern

The reference implementation uses {extension-name}-v{version} tags to track each extension independently. Your workflow needs to:

  1. Find the last tag per extension (not just the most recent global tag)
  2. Compare changes in that extension's directory since its last tag
  3. Create new tags in the same pattern after version bumps

Questions for Discussion

  1. Scope: Should this PR include the change detection implementation, or defer to consuming repositories?
  2. Approach: TypeScript scripts (testable, maintainable) vs. bash (simpler dependencies)?
  3. Backward compatibility: How should the workflow behave if a caller passes extensions: 'changed' before this is implemented?

Files Needing Updates

If implementing in this PR:

  • .github/workflows/vscode-publish-extensions.yml - Add determine-changes job
  • .github/scripts/ext-change-detector.ts - Port from apex-language-support
  • .github/scripts/utils.ts - Helper functions for git operations
  • .github/scripts/types.ts - Type definitions
  • .github/scripts/index.ts - CLI entry point
  • package.json - Add tsx dependency if using TypeScript
  • Documentation - Update with change detection behavior

Reference: Comparison analysis performed against:

  • This PR: /Users/peter.hale/alm-cli-git/github-workflows
  • Reference: /Users/peter.hale/git/apex-language-support-my-work
  • VSE (for mono-repo patterns): /Users/peter.hale/git/vse

@peternhale peternhale left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Summary

This PR adds comprehensive VS Code extension CI/CD infrastructure with solid architecture. However, there are several critical issues that need to be addressed before merging, particularly around change detection race conditions and conventional commit parsing.

Critical Issues

  • Race condition in parallel extension builds - Could cause incorrect change detection
  • Incomplete conventional commit parsing - Missing breaking change patterns
  • Bypass confirmation UX issue - Case-sensitive validation will confuse users

Medium Priority

  • Tag sorting type safety
  • Commit SHA resolution fragility
  • Inline version comparison should use existing utilities

See inline comments for detailed recommendations. Overall this is excellent work - just needs these safety improvements before production use.

Recommendation: Request changes for critical issues, then approve after fixes.

@peternhale

Copy link
Copy Markdown
Contributor

🔴 CRITICAL: Race Condition in Change Detection

Location: .github/scripts/ext-change-detector.ts:441

const diff = await git.diff([lastTag, 'HEAD', '--', extensionPath]);

Problem: When multiple extensions change in a single commit and workflows run in parallel, there's a race condition:

  1. Extensions A and B both detect changes against their last tags
  2. Both start building and creating tags
  3. If B's tag is created while A is still running, A's subsequent builds might see B's tag as HEAD

Impact: Could cause extensions to be incorrectly marked as unchanged, skip commits, or create tags in wrong order.

Recommendation: Add workflow-level concurrency control in vscode-publish-extensions.yml:

concurrency:
  group: vscode-publish-${{ github.ref }}
  cancel-in-progress: false

This ensures sequential processing and prevents tag creation races.

@peternhale

Copy link
Copy Markdown
Contributor

🟡 MEDIUM: Incomplete Conventional Commit Parsing

Location: .github/scripts/ext-change-detector.ts:589-595

if (/BREAKING CHANGE/i.test(body) || /^[a-z]+(\([^)]*\))?!:/i.test(firstLine)) {
  return 'major';
}
if (/^feat(\([^)]*\))?:/i.test(firstLine) && bump !== 'major') {
  bump = 'minor';
}

Issues:

  1. Doesn't handle scoped breaking changes without colon: feat(api)!
  2. The regex requires : after !, but conventional commits spec allows ! without colon
  3. Only detects BREAKING CHANGE (singular), not BREAKING CHANGES (plural)

Test Cases that Fail:

  • fix(auth)! → Should be major, detected as patch ❌
  • BREAKING CHANGES: multiple changes → Not detected ❌

Recommendation:

// Check for breaking changes
const hasBreakingFooter = /^BREAKING[- ]CHANGE[S]?:/im.test(body);
const hasBreakingMarker = /^[a-z]+(\([^)]*\))?!(:|$)/i.test(firstLine);
if (hasBreakingFooter || hasBreakingMarker) {
  return 'major';
}

@peternhale

Copy link
Copy Markdown
Contributor

🔴 CRITICAL: Bypass Confirmation is Case-Sensitive

Location: .github/workflows/vscode-manual-publish.yml:1594-1598

if [ "$BYPASS" != "BYPASS" ]; then
  echo "ERROR: skip-quality-checks is true but confirm-bypass is not 'BYPASS'."
  exit 1
fi

Problem: Case-sensitive string comparison means bypass, Bypass, or BYPASS (with trailing space) all fail validation. The error message doesn't indicate this, so users will struggle to diagnose why their confirmation isn't working.

Recommendation: Normalize the input before comparison:

BYPASS_NORMALIZED=$(echo "$BYPASS" | tr '[:lower:]' '[:upper:]' | xargs)
if [ "$BYPASS_NORMALIZED" != "BYPASS" ]; then
  echo "ERROR: confirm-bypass must be exactly 'BYPASS' (got: '$BYPASS')"
  exit 1
fi

This provides better UX and clearer error messages.

@peternhale

Copy link
Copy Markdown
Contributor

🟡 MEDIUM: Tag Sorting Type Safety Issue

Location: .github/scripts/ext-change-detector.ts:465-468

.filter((item) => item.version !== null) // Filter out tags we couldn't parse
.sort((a, b) =>
  // Use proper semver comparison (descending order - newest first)
  compareSemver(b.version!, a.version!),
);

Problem: The ! assertion on line 467 assumes filtering removes all null versions, but TypeScript doesn't narrow the type. If a malformed tag slips through (e.g., regex edge case), the assertion could fail at runtime.

Recommendation: Use a type guard for explicit type narrowing:

.filter((item): item is TagWithVersion & { version: SemanticVersion } => 
  item.version !== null
)
.sort((a, b) => compareSemver(b.version, a.version))

This provides compile-time safety without runtime assertions.

@peternhale

Copy link
Copy Markdown
Contributor

🟡 MEDIUM: Fragile CI Commit Resolution

Location: .github/workflows/vscode-manual-publish.yml:1798-1829

Issues:

  1. Hard-coded check name ("CI Complete") — if consuming repos use different names, this silently fails
  2. Arbitrary depth limit (20 commits) — no justification or documentation
  3. No caching — if multiple jobs need this SHA, they all repeat the expensive walk

Recommendations:

  1. Make the check name configurable via workflow input:
required-ci-checks:
  description: 'Comma-separated list of required CI check names'
  required: false
  default: 'CI Complete'
  type: string
  1. Document why 20 is chosen, or make it configurable

  2. Cache the result as a workflow output:

outputs:
  ci-commit-sha: ${{ steps.ci-commit.outputs.ci-commit-sha }}

This makes the workflow more flexible and reusable across different repos.

@peternhale

Copy link
Copy Markdown
Contributor

🟢 LOW: Inline Version Comparison Should Use Existing Utilities

Location: .github/workflows/vscode-promote-stable.yml:3307-3316

IS_GT=$(STABLE_VERSION="$STABLE_VERSION" CURRENT_VERSION="$CURRENT_VERSION" node -e '
  const parse = v => String(v).split("-")[0].split(".").map(Number);
  const a = parse(process.env.STABLE_VERSION);
  const b = parse(process.env.CURRENT_VERSION);
  let gt = false;
  for (let i = 0; i < 3; i++) {
    if (a[i] !== b[i]) { gt = a[i] > b[i]; break; }
  }
  console.log(gt ? "yes" : "no");
')

Issue: This inline script duplicates logic that already exists in utils.ts (compareSemver). Inline scripts are harder to test and maintain. Duplicated logic increases the risk of bugs that won't be caught by unit tests.

Recommendation: Use npx semver CLI directly (already in dependencies):

IS_GT=$(npx semver "$STABLE_VERSION" -r ">$CURRENT_VERSION" && echo "yes" || echo "no")

Or extract to a callable script in .github/scripts/ that can be unit tested.

This reduces duplication and improves maintainability.

@peternhale

Copy link
Copy Markdown
Contributor

🟢 LOW: Missing Input Validation for Extension Lists

Location: .github/scripts/ext-change-detector.ts:482-494

const selected = selectedExtensionsInput
  .split(',')
  .map((s) => s.trim())
  .filter(Boolean);

Issue: The function doesn't validate malformed comma-separated lists. Edge cases that silently get cleaned up:

  • Leading/trailing commas: ,ext-a, → produces empty strings (filtered out)
  • Multiple commas: ext-a,,ext-b → produces empty strings
  • Only commas: ,,,, → produces empty array

While .filter(Boolean) handles this, it silently ignores the malformed input, which could mask user errors.

Recommendation: Add explicit validation with warnings:

if (/^,|,$|,,/.test(selectedExtensionsInput)) {
  log.warning('Malformed extension list detected (extra commas) — cleaning up');
  log.warning(`Raw input: "${selectedExtensionsInput}"`);
}

This helps users identify typos in their input without breaking the workflow.

@peternhale peternhale left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comprehensive Review Complete

I've reviewed the entire PR with special focus on the change detection logic as requested. This is excellent infrastructure work with solid architecture, but there are several issues that need to be addressed before merging.


Summary of Findings

🔴 Critical Issues (Must Fix)

  1. Race condition in parallel extension builds — Could cause incorrect change detection when multiple extensions change simultaneously
  2. Incomplete conventional commit parsing — Missing breaking change patterns like feat(api)! and BREAKING CHANGES (plural)
  3. Bypass confirmation UX issue — Case-sensitive validation will confuse users

🟡 Medium Priority (Should Fix)

  1. Tag sorting type safety — Missing type guard after null filtering
  2. Commit SHA resolution fragility — Hard-coded check names, arbitrary limits
  3. Inline version comparison — Duplicates existing utility logic

🟢 Low Priority (Nice to Have)

  1. Input validation warnings — Silently cleans malformed comma-separated lists

What I Especially Like ✅

  • Per-extension tag tracking — Properly handles multi-extension monorepos
  • Git diff-based detection — Correct use of tag ranges for change detection
  • User selection intersection — Smart handling of special values (all, changed, none)
  • Audit logging — Comprehensive tracking of publish events
  • Token masking — Proper security practices
  • Idempotent workflows — Rerun safety throughout

Next Steps

Please address the 3 critical issues (especially the race condition) and consider the medium-priority improvements. I've posted detailed inline comments for each issue with specific recommendations.

Once these are addressed, this will be production-ready! The odd/even versioning convention is clever and the overall workflow composition is excellent.

Happy to re-review after updates. 🚀

madhur310 added a commit that referenced this pull request Jul 13, 2026
Critical fixes based on PR #158 review feedback:

1. **Fix tag sorting type safety** (ext-change-detector.ts)
   - Add proper type guard after null filtering
   - Remove non-null assertions (use TypeScript narrowing instead
   - Filter signature: item is { tag: string; version: SemanticVersion }

2. **Fix incomplete conventional commit parsing** (ext-change-detector.ts)
   - Support BREAKING CHANGES: (plural) in addition to BREAKING CHANGE:
   - Update regex: /BREAKING CHANGE/i → /BREAKING CHANGES?:/i
   - Ensures both forms trigger major version bumps per spec

3. **Fix bypass confirmation UX** (vscode-manual-publish.yml)
   - Make BYPASS validation case-insensitive
   - Users can now type: bypass, Bypass, BYPASS, ByPass, etc.
   - Convert to lowercase before comparison for better UX

These changes address the critical issues identified in review:
#158 (review)

@peternhale peternhale left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ APPROVED - All Critical Issues Resolved

Excellent work addressing all the feedback from the previous review! All critical issues have been properly fixed.

What Was Fixed ✅

  1. Branch references → All updated to @main (commit 304eddd)
  2. Conventional commit parsing → Now handles BREAKING CHANGES (plural) and feat! syntax
  3. Bypass confirmation UX → Case-insensitive validation implemented
  4. Type safety → Type guards properly in place

Code Quality Highlights 🌟

  • Per-extension tag tracking - Correctly handles multi-extension monorepos
  • Idempotent workflows - Safe to re-run with existing release checks
  • Comprehensive audit logging - Excellent compliance trail
  • Proper token masking - Security best practices followed
  • Dry-run support - All destructive operations respect dry-run mode

Optional Enhancements (Not Blockers)

  1. Consider adding a Prerequisites section to README documenting which actions consuming repos must provide
  2. Current input validation warnings are acceptable but could optionally be errors for stricter validation

Verdict

This infrastructure is production-ready with excellent architectural patterns, proper error handling, and comprehensive audit trails. The remaining suggestions are minor enhancements, not blockers.

Great job systematically addressing each piece of feedback! 🚀

madhur310 and others added 9 commits July 14, 2026 06:41
Add shared CI/CD tooling for Salesforce VS Code extensions:

## NPM Package: @salesforce/vscode-extension-ci
- TypeScript CLI with 13 commands for release automation
- Smart version bumping (even/odd minor for stable/pre-release)
- Conventional commit analysis
- Change detection and package selection
- GitHub release creation
- Configurable via environment variables:
  - PACKAGES_ROOT (default: packages)
  - TAG_PREFIX (default: marketplace)
  - AUDIT_LOG_DIR (default: .github/audit-logs)

## Composite Actions (.github/actions/vscode/)
- calculate-artifact-name: Artifact naming with run isolation
- check-ci-status: CI quality gate verification
- detect-packages: Auto-detect extensions and npm packages
- download-vsix-artifacts: VSIX artifact downloader
- npm-install-with-retries: Retry wrapper for npm ci
- publish-vsix: Marketplace publisher (vsce/ovsx)

## Reusable Workflows (.github/workflows/vscode/)
- ci-template.yml: Parameterized CI workflow
- package.yml: VSIX packaging with checksums
- publish-extensions.yml: Complete release pipeline
- promote-prerelease.yml: Nightly → pre-release promotion

## Usage
Consuming repos reference workflows via:
  uses: salesforcecli/github-workflows/.github/workflows/vscode/<name>.yml@main

Consuming repos install the CLI:
  npm install --save-dev @salesforce/vscode-extension-ci

## Testing
- Tested with npm link in apex-language-support
- CLI successfully detects VS Code extensions
- All TypeScript builds cleanly

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Two test workflows for validating the VS Code CI infrastructure:

1. test-vscode-ci.yml - Tests NPM package build and composite actions
   - Verifies CLI commands work
   - Tests package selector and build type detection
   - Validates composite actions (npm-install, detect-packages, etc.)

2. test-vscode-workflows.yml - Tests reusable workflows
   - Tests nightly workflow with dry-run
   - Tests prerelease promotion with dry-run
   - Tests package workflow (VSIX creation)
   - Tests CI template (lint, compile, test)

Both can be triggered manually from Actions tab on this branch.
Add dist/, *.tsbuildinfo, and other build artifacts to .gitignore.
These should be generated during build, not committed to git.
Build artifacts in dist/ should not be committed.
They are now properly excluded via .gitignore and will be
generated during npm build.
The test workflows were failing on push because workflow_dispatch inputs
are not available in push events. These workflows are only meant to be
run manually for testing purposes.
GitHub Actions requires reusable workflows to be at the top level
of .github/workflows/, not in subdirectories.

Changes:
- Moved vscode/* workflows to top level with 'vscode-' prefix
- Added push triggers to test workflows so they run automatically
- Made workflows work for both push and workflow_dispatch events
- apex-language-support tests run on push, salesforcedx-vscode on manual trigger
The previous test workflows had YAML syntax issues.
This simpler workflow just builds the NPM package and verifies the CLI works.

Tests:
- Package builds successfully
- CLI executable exists
- Help command works
- Required commands are present
Removed extends from root tsconfig to avoid module resolution conflicts.
The package now has its own complete TypeScript configuration.
Tests the complete VS Code CI infrastructure:
- Package workflow (VSIX creation)
- Composite actions (npm-install, detect-packages, calculate-artifact-name)
- CLI scripts (ext-package-selector, ext-build-type)

Run manually from Actions tab with workflow_dispatch.
sfdc-ospo-bot and others added 27 commits July 14, 2026 06:46
…159)

ctcOpen.yml and ctcClose.yml set actions/setup-node `cache: npm`, which
requires an npm lockfile to compute its cache key. Consumer repos on pnpm
or yarn have no package-lock.json, so setup-node fails with "Dependencies
lock file is not found" and the CTC job dies before it ever runs.

The only npm operation in these jobs is the one-off global install of
@salesforce/change-case-management, which the cache does not key on, so it
provides no benefit here. Dropping `cache: npm` lets the CTC legs run for
npm, yarn, and pnpm consumers alike.
Remove truly unused code while preserving all tested workflows:

Deleted (971 lines):
- Workflows: vscode-nightly-release, vscode-promote-prerelease,
  test-vscode-package, test-vscode-workflows-integration
- Actions: check-ci-status, detect-packages, download-vsix-artifacts
- Config: package.json, package-lock.json, docs/ARCHITECTURE.md

Kept all functional workflows:
- vscode-release-explicit.yml (used in production)
- vscode-package.yml (tested)
- vscode-ci-template.yml (tested)
- vscode-publish-extensions.yml (tested)
- publish-vsix action (used by publish-extensions)
- npm-install-with-retries action (used by multiple workflows)

All inputs required by caller workflows are preserved.
…ting npmInstallWithRetries

The vscode-specific npm-install-with-retries action was a duplicate of the
existing npmInstallWithRetries action, with the only difference being that
the generic one includes --no-audit and --no-fund flags (which are beneficial).

Changes:
- Deleted .github/actions/vscode/npm-install-with-retries/
- Updated 3 workflows to use npmInstallWithRetries@main instead
- Reduces 16 lines of redundant code
Restore the original npm-focused README from main branch and append
concise documentation for the new VS Code extension workflows.

Changes:
- Restored original README content (npm workflows, testing, etc.)
- Added VS Code section at the end documenting 4 new workflows:
  - vscode-release-explicit
  - vscode-package
  - vscode-ci-template
  - vscode-publish-extensions
…pport

Restore vscode-promote-prerelease.yml and check-ci-status action that were
previously removed. These are needed by apex-language-support PR #514.

Context:
- apex-language-support migrating to shared workflows (PR #514)
- Uses vscode-promote-prerelease.yml to promote nightlies to marketplace
- salesforcedx-vscode-ci-testing uses local promote workflow instead

Changes:
+ .github/workflows/vscode-promote-prerelease.yml (199 lines)
+ .github/actions/vscode/check-ci-status/ (100 lines)

Both repos can now coexist with different promotion strategies.
The workflows don't exist on main branch yet, so internal references
must point to @feat/add-vscode-extension-ci until PR is merged.

Fixed error: 'workflow was not found' when vscode-publish-extensions.yml
tried to call vscode-package.yml@main.

Changes:
- Reverted all @main references back to @feat/add-vscode-extension-ci
- Updated 4 workflows (ci-template, package, promote-prerelease, publish-extensions)
- Fixed 17 references total

After PR merge to main, these should be updated to @main.
The vscode-publish-extensions and vscode-package workflows were hardcoded
to look for extensions under packages/ directory, which works for monorepos
like apex-language-support but fails for flat repos like salesforcedx-vscode-ci-testing.

Changes:
- Added extensions-root input parameter (default: 'packages') to both workflows
- Updated all hardcoded 'packages/' paths to use $EXTENSIONS_ROOT variable
- Propagated extensions-root from publish-extensions to package workflow

This allows calling repos to specify their extension directory structure:
  with:
    extensions-root: '.'  # for flat repos without packages/ dir

Fixes: https://github.com/forcedotcom/salesforcedx-vscode-ci-testing/actions/runs/28388283933
…ic logic

Added configurable inputs to support different extension naming conventions
and VSIX patterns, removing hardcoded apex-language-support references.

Changes to vscode-promote-prerelease.yml:
- Added extension-name input (required) for tracking tag generation
- Added vsix-name-pattern input (default: '*.vsix') for VSIX file matching
- Added exclude-web-vsix input (default: 'false') to filter *-web-* files
- Removed hardcoded 'apex-language-server-extension' VSIX pattern
- Removed hardcoded 'apex-lsp-vscode-extension' tracking tag
- Made VSIX finding logic configurable with pattern + web exclusion

Changes to vscode-publish-extensions.yml:
- Added exclude-web-vsix input (default: 'false')
- Use package.json 'name' field instead of directory name for VSIX patterns
- Removed hardcoded apex-specific VSIX pattern mapping
- Made web VSIX exclusion configurable instead of hardcoded
- Fixed matrix building to read package name from package.json

These changes allow both apex-language-support and other repos to use
the shared workflows with their specific naming conventions.

Fixes: forcedotcom/apex-language-support#514
Changes:
- Added 'publish-skipped-notice' job to log when marketplace publishing
  is skipped (e.g., nightly builds)
- Updated 'slack-notify' to run when publish is skipped, with logging
- Updated 'slack-notify-failure' to handle skipped publish and other
  job failures
- Removed dry-run checks from job-level 'if' conditions
- Added step-level conditions to show appropriate logs for:
  - Dry-run mode
  - Skipped publish (nightly)
  - Actual execution

Now all jobs run and show what's happening instead of being silently
skipped, improving workflow visibility and debugging.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Addresses all critical and medium priority issues from PR #158 review:

## Critical Issues Fixed:

1. **Branch references updated to @main**
   - Changed all 17 occurrences of @feat/add-vscode-extension-ci to @main
   - Workflows will now work correctly after branch merge

2. **Parameterized Slack notifications**
   - Added slack-notification-title input to vscode-publish-extensions.yml
   - Removed hardcoded "Apex Language Support" messages
   - Default: "🎉 Extensions Released Successfully!"

3. **Documented missing actions**
   - Clarified which workflows require local actions from calling repos
   - Added detailed descriptions for required actions:
     - npm-install-with-retries (custom retry logic)
     - check-ci-status (CI validation)
     - repackage-vsix-stable (VSIX repackaging)
     - publish-vsix (marketplace publishing)
   - Noted that most workflows are self-contained

4. **Expanded README documentation**
   - Added comprehensive Prerequisites section with:
     - Required secrets (IDEE_GH_TOKEN, VSCE_PERSONAL_ACCESS_TOKEN, IDEE_OVSX_PAT)
     - Repository structure requirements
     - Optional local actions
   - Added Architecture Overview explaining workflow composition
   - Added Versioning Strategy section explaining odd/even convention
   - Added Common Inputs section for shared parameters
   - Enhanced each workflow description with usage examples and requirements

## Medium Priority Issues Fixed:

5. **Parameterized Node.js version**
   - Added node-version input to all workflows (default: '22.x')
   - Updated all hardcoded '22.x' references to use input parameter
   - Workflows: vscode-publish-extensions, vscode-promote-prerelease,
     vscode-manual-publish, vscode-promote-stable, vscode-release-explicit

6. **Documented versioning strategy**
   - Added detailed inline documentation in vscode-publish-extensions.yml
   - Explains odd/even minor version convention
   - Documents version bump logic (major, minor, patch, auto)
   - Notes when customization might be needed

All workflows now follow consistent patterns and are fully reusable across
different VS Code extension projects without hardcoded assumptions.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Additional improvements discovered during PR review:

1. **Added node-version to workflow_dispatch inputs**
   - vscode-promote-prerelease.yml
   - vscode-manual-publish.yml
   - vscode-promote-stable.yml
   Ensures manual workflow runs can also customize Node.js version

2. **Added explicit permissions to vscode-release-explicit.yml**
   - Added contents: write and packages: write permissions
   - Brings it in line with other workflows for security best practices

3. **Updated action versions for consistency**
   - Updated vscode-release-explicit.yml to use latest action versions
   - actions/checkout@v6 (was v4)
   - actions/setup-node@v6 (was v4)
   - actions/upload-artifact@v7 (was v4)
   - Ensures all workflows use consistent, up-to-date actions

All workflows now have:
- Consistent action versions
- Explicit permissions
- Complete input parameters across all trigger types
- Proper security practices

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…ting

- Change @main to @feat/add-vscode-extension-ci for all nested workflow/action calls
- Update scripts download URL to pull from feat branch
- This allows testing the full workflow with determine-changes job
- Will revert to @main before merging to main branch
- tsx with cjs output format doesn't support top-level await
- Wrap switch statement in async IIFE to fix TransformError
- Fixes: Top-level await is currently not supported with the "cjs" output format
Changes:
- Update 6 action references: npmInstallWithRetries, publish-vsix, vscode-package.yml
- Update scripts download URL to use main branch
- Prepare for PR merge by removing feat branch references

This ensures workflows will continue working after the PR is merged to main.
Critical fixes based on PR #158 review feedback:

1. **Fix tag sorting type safety** (ext-change-detector.ts)
   - Add proper type guard after null filtering
   - Remove non-null assertions (use TypeScript narrowing instead
   - Filter signature: item is { tag: string; version: SemanticVersion }

2. **Fix incomplete conventional commit parsing** (ext-change-detector.ts)
   - Support BREAKING CHANGES: (plural) in addition to BREAKING CHANGE:
   - Update regex: /BREAKING CHANGE/i → /BREAKING CHANGES?:/i
   - Ensures both forms trigger major version bumps per spec

3. **Fix bypass confirmation UX** (vscode-manual-publish.yml)
   - Make BYPASS validation case-insensitive
   - Users can now type: bypass, Bypass, BYPASS, ByPass, etc.
   - Convert to lowercase before comparison for better UX

These changes address the critical issues identified in review:
#158 (review)
@madhur310 madhur310 force-pushed the feat/add-vscode-extension-ci branch from 58c87c5 to d5d1acb Compare July 14, 2026 13:51
@madhur310 madhur310 merged commit 029fab1 into main Jul 14, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants