diff --git a/.github/workflows/vscode-find-nightly-candidate.yml b/.github/workflows/vscode-find-nightly-candidate.yml new file mode 100644 index 0000000..782934b --- /dev/null +++ b/.github/workflows/vscode-find-nightly-candidate.yml @@ -0,0 +1,94 @@ +name: Find Nightly Candidate + +# Reusable workflow to find eligible nightly tag for promotion +# Used by repos to implement their own gate-check before calling vscode-promote-prerelease.yml + +on: + workflow_call: + inputs: + min-tag-age-days: + description: "Minimum nightly age in days before eligible for promotion" + required: false + default: "7" + type: string + release-tag: + description: "Specific release tag to use (overrides min-tag-age-days)" + required: false + type: string + outputs: + commit-sha: + description: "Commit SHA that the nightly tag points to" + value: ${{ jobs.find-nightly.outputs.commit-sha }} + nightly-tag: + description: "Nightly tag selected for promotion" + value: ${{ jobs.find-nightly.outputs.nightly-tag }} + +jobs: + find-nightly: + runs-on: ubuntu-latest + outputs: + commit-sha: ${{ steps.find.outputs.commit-sha }} + nightly-tag: ${{ steps.find.outputs.nightly-tag }} + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Find eligible nightly + id: find + env: + RELEASE_TAG: ${{ inputs.release-tag || '' }} + MIN_TAG_AGE_DAYS: ${{ inputs.min-tag-age-days || '7' }} + run: | + # If specific release-tag provided, use it directly + if [ -n "$RELEASE_TAG" ]; then + echo "Using specific release tag: $RELEASE_TAG" + + if ! git rev-parse "$RELEASE_TAG" >/dev/null 2>&1; then + echo "❌ Release tag $RELEASE_TAG does not exist" + exit 1 + fi + + COMMIT_SHA=$(git rev-parse "$RELEASE_TAG^{commit}") + echo "nightly-tag=$RELEASE_TAG" >> $GITHUB_OUTPUT + echo "commit-sha=$COMMIT_SHA" >> $GITHUB_OUTPUT + echo "✅ Found release tag: $RELEASE_TAG (commit: $COMMIT_SHA)" + exit 0 + fi + + # Auto-detect mode: find nightly tags older than MIN_TAG_AGE_DAYS + echo "Finding nightly tags older than $MIN_TAG_AGE_DAYS days..." + + NIGHTLY_TAGS=$(git tag -l '*-nightly.*' --sort=-creatordate) + + if [ -z "$NIGHTLY_TAGS" ]; then + echo "❌ No nightly tags found" + exit 1 + fi + + CUTOFF_TIMESTAMP=$(date -u -d "$MIN_TAG_AGE_DAYS days ago" +%s 2>/dev/null || date -u -v-${MIN_TAG_AGE_DAYS}d +%s) + + while IFS= read -r tag; do + [ -z "$tag" ] && continue + + TAG_TIMESTAMP=$(git log -1 --format=%ct "$tag" 2>/dev/null) + [ -z "$TAG_TIMESTAMP" ] && continue + + if [ "$TAG_TIMESTAMP" -lt "$CUTOFF_TIMESTAMP" ]; then + VERSION=$(echo "$tag" | grep -oP '\d+\.\d+\.\d+' | head -1) + PROMO_TAG="marketplace-prerelease-*-v${VERSION}" + + if ! git tag -l "$PROMO_TAG" | grep -q .; then + SELECTED_TAG="$tag" + SELECTED_SHA=$(git rev-list -n 1 "$tag") + echo "nightly-tag=$SELECTED_TAG" >> $GITHUB_OUTPUT + echo "commit-sha=$SELECTED_SHA" >> $GITHUB_OUTPUT + echo "✅ Found eligible nightly: $SELECTED_TAG (SHA: $SELECTED_SHA)" + exit 0 + fi + fi + done <<< "$NIGHTLY_TAGS" + + echo "❌ No eligible nightly found (all nightlies either too new or already promoted)" + exit 1 diff --git a/.github/workflows/vscode-promote-prerelease.yml b/.github/workflows/vscode-promote-prerelease.yml index b8462c3..2e193bf 100644 --- a/.github/workflows/vscode-promote-prerelease.yml +++ b/.github/workflows/vscode-promote-prerelease.yml @@ -1,53 +1,51 @@ name: Promote Nightly to Pre-release -# Promotes a vetted nightly build to pre-release on VS Code Marketplace and Open VSX. +# Publishes a nightly build to pre-release on VS Code Marketplace and Open VSX. # -# Two modes: -# 1. Auto-detect (default): finds oldest unpromoted nightly ≥ min-tag-age-days -# 2. Specific tag: publishes a specific release tag (for emergency pre-releases) +# NOTE: Callers should use vscode-find-nightly-candidate.yml and check-ci-status action +# BEFORE calling this workflow to implement their own gate-check logic. # -# Usage - Auto-detect mode: +# Usage: # jobs: -# promote: -# uses: salesforcecli/github-workflows/.github/workflows/vscode-promote-prerelease.yml@main +# find-nightly: +# uses: salesforcecli/github-workflows/.github/workflows/vscode-find-nightly-candidate.yml@main # with: -# extension-name: 'apex-lsp-vscode-extension' # min-tag-age-days: '7' -# vsix-name-pattern: 'apex-language-server-extension-*.vsix' # optional, default: *.vsix -# exclude-web-vsix: 'true' # optional, default: false -# required-ci-checks: '["CI Complete"]' -# dry-run: 'false' -# secrets: inherit # -# Usage - Specific tag mode (emergency pre-releases): -# jobs: +# gate-check: +# needs: find-nightly +# runs-on: ubuntu-latest +# steps: +# - uses: salesforcecli/github-workflows/.github/actions/vscode/check-ci-status@main +# with: +# commit-sha: ${{ needs.find-nightly.outputs.commit-sha }} +# token: ${{ secrets.GITHUB_TOKEN }} +# required-checks: '["CI Complete"]' +# # promote: +# needs: [find-nightly, gate-check] # uses: salesforcecli/github-workflows/.github/workflows/vscode-promote-prerelease.yml@main # with: -# release-tag: 'v67.13.7-nightly.develop.20260820' # specific tag to publish +# release-tag: ${{ needs.find-nightly.outputs.nightly-tag }} # extension-name: 'apex-lsp-vscode-extension' +# vsix-name-pattern: 'apex-language-server-extension-*.vsix' +# exclude-web-vsix: 'true' +# dry-run: 'false' # secrets: inherit # # Requirements - calling repository must have: # - Nightly tags matching *-nightly.* (e.g., v1.2.3-nightly.20260629) # - GitHub releases for each nightly tag with VSIX file(s) attached -# - Passing CI checks on nightly commits # - Secrets: IDEE_GH_TOKEN, VSCE_PERSONAL_ACCESS_TOKEN, IDEE_OVSX_PAT # -# Workflow: finds eligible nightly (or uses release-tag) → verifies CI → -# downloads VSIX → publishes to both marketplaces → creates tracking tag +# Workflow: downloads VSIX → publishes to both marketplaces → creates tracking tag on: workflow_call: inputs: release-tag: - description: "Specific release tag to publish (e.g., v67.13.7-nightly.develop.20260820). Overrides min-tag-age-days auto-detection." - required: false - type: string - min-tag-age-days: - description: "Minimum nightly age in days before eligible for promotion (ignored if release-tag provided)" - required: false - default: "7" + description: "Nightly tag to publish (e.g., v67.13.7-nightly.develop.20260820). Callers should use vscode-find-nightly-candidate.yml to find this." + required: true type: string extension-name: description: "Extension name for tracking tag (e.g., 'apex-lsp-vscode-extension')" @@ -63,11 +61,6 @@ on: required: false default: "false" type: string - required-ci-checks: - description: "JSON array of CI check names required before promotion" - required: false - default: '["CI Complete"]' - type: string dry-run: description: "Run in dry-run mode (no actual publishing or tagging)" required: false @@ -81,13 +74,8 @@ on: workflow_dispatch: inputs: release-tag: - description: "Specific release tag to publish (e.g., v67.13.7-nightly.develop.20260820). Overrides min-tag-age-days auto-detection." - required: false - type: string - min-tag-age-days: - description: "Minimum nightly age in days before eligible for promotion (default: 7, ignored if release-tag provided)" - required: false - default: "7" + description: "Nightly tag to publish (e.g., v67.13.7-nightly.develop.20260820)" + required: true type: string extension-name: description: "Extension name for tracking tag (e.g., 'apex-lsp-vscode-extension')" @@ -106,11 +94,6 @@ on: options: - "false" - "true" - required-ci-checks: - description: "JSON array of CI check names required before promotion" - required: false - default: '["CI Complete"]' - type: string dry-run: description: "Run in dry-run mode (no actual publishing or tagging)" required: false @@ -135,116 +118,7 @@ permissions: actions: read jobs: - find-nightly-candidate: - runs-on: ubuntu-latest - outputs: - commit-sha: ${{ steps.find.outputs.commit-sha }} - nightly-tag: ${{ steps.find.outputs.nightly-tag }} - steps: - - name: Checkout - uses: actions/checkout@v6 - with: - fetch-depth: 0 - token: ${{ secrets.IDEE_GH_TOKEN }} - - - name: Find eligible nightly - id: find - env: - RELEASE_TAG: ${{ inputs.release-tag || '' }} - MIN_TAG_AGE_DAYS: ${{ inputs.min-tag-age-days || '7' }} - run: | - # If specific release-tag provided, use it directly - if [ -n "$RELEASE_TAG" ]; then - echo "Using specific release tag: $RELEASE_TAG" - - # Verify tag exists - if ! git rev-parse "$RELEASE_TAG" >/dev/null 2>&1; then - echo "::error::Release tag $RELEASE_TAG does not exist" - exit 1 - fi - - COMMIT_SHA=$(git rev-parse "$RELEASE_TAG^{commit}") - echo "nightly-tag=$RELEASE_TAG" >> $GITHUB_OUTPUT - echo "commit-sha=$COMMIT_SHA" >> $GITHUB_OUTPUT - echo "✓ Found release tag: $RELEASE_TAG (commit: $COMMIT_SHA)" - exit 0 - fi - - # Auto-detect mode: find nightly tags older than MIN_TAG_AGE_DAYS - echo "Finding nightly tags older than $MIN_TAG_AGE_DAYS days..." - - # Get all nightly tags sorted by date (newest first) - NIGHTLY_TAGS=$(git tag -l '*-nightly.*' --sort=-creatordate) - - if [ -z "$NIGHTLY_TAGS" ]; then - echo "No nightly tags found" - echo "nightly-tag=" >> $GITHUB_OUTPUT - echo "commit-sha=" >> $GITHUB_OUTPUT - exit 0 - fi - - # Calculate cutoff timestamp (MIN_TAG_AGE_DAYS ago) - CUTOFF_TIMESTAMP=$(date -u -d "$MIN_TAG_AGE_DAYS days ago" +%s 2>/dev/null || date -u -v-${MIN_TAG_AGE_DAYS}d +%s) - - # Find first tag older than cutoff that hasn't been promoted - SELECTED_TAG="" - SELECTED_SHA="" - - while IFS= read -r tag; do - [ -z "$tag" ] && continue - - # Get tag creation timestamp - TAG_TIMESTAMP=$(git log -1 --format=%ct "$tag" 2>/dev/null) - [ -z "$TAG_TIMESTAMP" ] && continue - - # Check if tag is old enough - if [ "$TAG_TIMESTAMP" -lt "$CUTOFF_TIMESTAMP" ]; then - # Check if already promoted (has corresponding marketplace-prerelease tag) - VERSION=$(echo "$tag" | grep -oP '\d+\.\d+\.\d+' | head -1) - PROMO_TAG="marketplace-prerelease-*-v${VERSION}" - - if ! git tag -l "$PROMO_TAG" | grep -q .; then - SELECTED_TAG="$tag" - SELECTED_SHA=$(git rev-list -n 1 "$tag") - break - fi - fi - done <<< "$NIGHTLY_TAGS" - - if [ -n "$SELECTED_TAG" ]; then - echo "Found eligible nightly: $SELECTED_TAG (SHA: $SELECTED_SHA)" - echo "nightly-tag=$SELECTED_TAG" >> $GITHUB_OUTPUT - echo "commit-sha=$SELECTED_SHA" >> $GITHUB_OUTPUT - else - echo "No eligible nightly found" - echo "nightly-tag=" >> $GITHUB_OUTPUT - echo "commit-sha=" >> $GITHUB_OUTPUT - fi - - - name: Fail if no candidate found - if: steps.find.outputs.nightly-tag == '' - run: | - echo "No eligible nightly candidate found. Nothing to promote this week." - exit 1 - - quality-gate: - needs: find-nightly-candidate - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v6 - - - name: Check CI status for candidate commit - uses: salesforcecli/github-workflows/.github/actions/vscode/check-ci-status@main - with: - commit-sha: ${{ needs.find-nightly-candidate.outputs.commit-sha }} - token: ${{ secrets.IDEE_GH_TOKEN }} - # `CI Complete` is the aggregate release gate emitted by vscode-ci-template. - # Callers can override it for their repository's release-worthy checks. - required-checks: ${{ inputs.required-ci-checks }} - publish: - needs: [find-nightly-candidate, quality-gate] runs-on: ubuntu-latest strategy: matrix: @@ -267,7 +141,7 @@ jobs: - name: Download VSIX from nightly GitHub release env: GH_TOKEN: ${{ secrets.IDEE_GH_TOKEN }} - NIGHTLY_TAG: ${{ needs.find-nightly-candidate.outputs.nightly-tag }} + NIGHTLY_TAG: ${{ inputs.release-tag }} VSIX_PATTERN: ${{ inputs.vsix-name-pattern || '*.vsix' }} EXCLUDE_WEB: ${{ inputs.exclude-web-vsix || 'false' }} run: | @@ -304,7 +178,7 @@ jobs: OVSX_PAT: ${{ secrets.IDEE_OVSX_PAT }} tag-promoted: - needs: [find-nightly-candidate, publish] + needs: publish runs-on: ubuntu-latest steps: - name: Checkout @@ -315,7 +189,7 @@ jobs: - name: Create marketplace-prerelease tracking tag env: - NIGHTLY_TAG: ${{ needs.find-nightly-candidate.outputs.nightly-tag }} + NIGHTLY_TAG: ${{ inputs.release-tag }} EXTENSION_NAME: ${{ inputs.extension-name }} DRY_RUN: ${{ inputs.dry-run || 'false' }} GH_TOKEN: ${{ secrets.IDEE_GH_TOKEN }} @@ -327,8 +201,10 @@ jobs: exit 1 fi + # Get commit SHA from the tag + COMMIT_SHA=$(git rev-list -n 1 "$NIGHTLY_TAG") + TRACKING_TAG="marketplace-prerelease-${EXTENSION_NAME}-v${VERSION}" - COMMIT_SHA="${{ needs.find-nightly-candidate.outputs.commit-sha }}" echo "Creating tracking tag: $TRACKING_TAG → $COMMIT_SHA"