Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions .github/workflows/vscode-find-nightly-candidate.yml
Original file line number Diff line number Diff line change
@@ -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
188 changes: 32 additions & 156 deletions .github/workflows/vscode-promote-prerelease.yml
Original file line number Diff line number Diff line change
@@ -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')"
Expand All @@ -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
Expand All @@ -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')"
Expand All @@ -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
Expand All @@ -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:
Expand All @@ -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: |
Expand Down Expand Up @@ -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
Expand All @@ -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 }}
Expand All @@ -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"

Expand Down