Skip to content
Merged
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
45 changes: 45 additions & 0 deletions .github/actions/check-ci-status/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Check CI Status
description: Verifies that CI checks passed for a candidate commit before promotion.

inputs:
commit-sha:
description: Commit SHA to check.
required: true
token:
description: GitHub token with repository read access.
required: true
required-checks:
description: Comma-separated checks that must have succeeded.
default: ''

runs:
using: composite
steps:
- name: Verify CI checks passed
shell: bash
env:
GH_TOKEN: ${{ inputs.token }}
COMMIT_SHA: ${{ inputs.commit-sha }}
REQUIRED_CHECKS: ${{ inputs.required-checks }}
REPO: ${{ github.repository }}
run: |
CHECK_RUNS=$(gh api "repos/$REPO/commits/$COMMIT_SHA/check-runs" --paginate \
--jq '.check_runs[] | {name: .name, status: .status, conclusion: .conclusion}')
if [ -z "$CHECK_RUNS" ]; then
echo "No check-runs found for $COMMIT_SHA"
exit 1
fi

FAILED=0
IFS=',' read -ra CHECKS <<< "$REQUIRED_CHECKS"
for CHECK in "${CHECKS[@]}"; do
CHECK=$(echo "$CHECK" | xargs)
[ -z "$CHECK" ] && continue
MATCH=$(echo "$CHECK_RUNS" | jq -c --arg name "$CHECK" 'select(.name == $name)' | head -1)
if [ -z "$MATCH" ] || [ "$(echo "$MATCH" | jq -r '.status')" != 'completed' ] || [ "$(echo "$MATCH" | jq -r '.conclusion')" != 'success' ]; then
echo "Required check '$CHECK' did not succeed"
FAILED=1
fi
done

[ "$FAILED" -eq 0 ] || exit 1
18 changes: 18 additions & 0 deletions .github/actions/npm-install-with-retries/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: pnpm-install-with-retries
description: Installs workspace dependencies with retry support for promotion workflows.

inputs:
ignore-scripts:
description: Skip lifecycle scripts during installation.
default: 'false'

runs:
using: composite
steps:
- name: Enable pnpm
shell: bash
run: corepack enable
- name: Install dependencies
uses: salesforcecli/github-workflows/.github/actions/retry@main
with:
command: pnpm install --frozen-lockfile ${{ inputs.ignore-scripts == 'true' && '--ignore-scripts' || '' }}
42 changes: 42 additions & 0 deletions .github/actions/publish-vsix/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Publish VSIX
description: Publishes a VSIX to the selected public marketplace with dry-run support.

inputs:
vsix-path:
description: Path to the VSIX file.
required: true
publish-tool:
description: 'Marketplace publisher: vsce or ovsx.'
required: true
pre-release:
description: Publish as a pre-release.
default: 'false'
dry-run:
description: Skip the live publish request.
default: 'false'

runs:
using: composite
steps:
- name: Publish VSIX
shell: bash
env:
VSIX_PATH: ${{ inputs.vsix-path }}
PUBLISH_TOOL: ${{ inputs.publish-tool }}
PRE_RELEASE: ${{ inputs.pre-release }}
DRY_RUN: ${{ inputs.dry-run }}
run: |
set -euo pipefail
[ -f "$VSIX_PATH" ] || { echo "VSIX not found: $VSIX_PATH"; exit 1; }
case "$PUBLISH_TOOL" in vsce|ovsx) ;; *) echo "Unsupported publish tool: $PUBLISH_TOOL"; exit 1;; esac
[ "$DRY_RUN" = true ] && { echo "DRY RUN: Would publish $VSIX_PATH with $PUBLISH_TOOL"; exit 0; }

PRE_RELEASE_FLAG=''
[ "$PRE_RELEASE" = true ] && PRE_RELEASE_FLAG='--pre-release'
if [ "$PUBLISH_TOOL" = vsce ]; then
[ -n "${VSCE_PERSONAL_ACCESS_TOKEN:-}" ] || { echo 'VSCE_PERSONAL_ACCESS_TOKEN is required'; exit 1; }
VSCE_PAT="$VSCE_PERSONAL_ACCESS_TOKEN" npx @vscode/vsce publish --packagePath "$VSIX_PATH" --skip-duplicate $PRE_RELEASE_FLAG
else
[ -n "${OVSX_PAT:-}" ] || { echo 'OVSX_PAT is required'; exit 1; }
npx ovsx publish "$VSIX_PATH" -p "$OVSX_PAT" --skip-duplicate $PRE_RELEASE_FLAG
fi
52 changes: 52 additions & 0 deletions .github/actions/repackage-vsix-stable/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: Repackage VSIX for Stable
description: Repackages a pre-release VSIX as a stable Lana build.

inputs:
source-vsix-path:
description: Source pre-release VSIX.
required: true
prerelease-version:
description: Pre-release version, used for logging.
required: true
stable-version:
description: Stable version to stamp into the VSIX.
required: true
output-dir:
description: Output directory for the stable VSIX.
default: ./vsix-artifacts

outputs:
vsix-path:
description: Path to the repackaged stable VSIX.
value: ${{ steps.repack.outputs.vsix-path }}

runs:
using: composite
steps:
- name: Repackage VSIX with stable version
id: repack
shell: bash
env:
SOURCE_VSIX: ${{ inputs.source-vsix-path }}
STABLE_VERSION: ${{ inputs.stable-version }}
OUTPUT_DIR: ${{ inputs.output-dir }}
run: |
set -euo pipefail
[ -f "$SOURCE_VSIX" ] || { echo "Source VSIX not found: $SOURCE_VSIX"; exit 1; }

WORK_DIR=./vsix-repack
rm -rf "$WORK_DIR"
mkdir -p "$WORK_DIR" "$OUTPUT_DIR"
unzip -q "$SOURCE_VSIX" -d "$WORK_DIR"

PKG_JSON="$WORK_DIR/extension/package.json"
[ -f "$PKG_JSON" ] || { echo "extension/package.json not found in VSIX"; exit 1; }
node -e "const fs=require('fs'); const path='$PKG_JSON'; const pkg=JSON.parse(fs.readFileSync(path,'utf8')); pkg.version='$STABLE_VERSION'; delete pkg.preRelease; fs.writeFileSync(path, JSON.stringify(pkg, null, 2) + '\n');"

MANIFEST="$WORK_DIR/extension.vsixmanifest"
[ -f "$MANIFEST" ] || { echo "extension.vsixmanifest not found in VSIX"; exit 1; }
STABLE_VERSION="$STABLE_VERSION" node -e 'const fs=require("fs"); const path=process.argv[1]; const xml=fs.readFileSync(path,"utf8"); const updated=xml.replace(/(<Identity[^>]*\bVersion=")[^"]*(")/, `$1${process.env.STABLE_VERSION}$2`); if (updated === xml) throw new Error("VSIX manifest does not contain an Identity Version"); fs.writeFileSync(path, updated);' "$MANIFEST"

OUT_ABS="$(cd "$OUTPUT_DIR" && pwd)/lana-${STABLE_VERSION}.vsix"
(cd "$WORK_DIR" && zip -q -r "$OUT_ABS" .)
echo "vsix-path=$OUT_ABS" >> "$GITHUB_OUTPUT"
47 changes: 47 additions & 0 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Nightly Release

on:
workflow_dispatch:
inputs:
dry-run:
description: 'Run in dry-run mode (no actual publishing)'
required: false
default: true
type: boolean
# schedule:
# - cron: '0 4 * * *'

concurrency:
group: nightly-${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: false

permissions:
contents: write
packages: write
actions: read

jobs:
nightly-release:
uses: salesforcecli/github-workflows/.github/workflows/vscode-publish-extensions.yml@ph/W-23832274-pnpm-stable-promotion
with:
branch: main
extensions: lana
registries: 'all'
pre-release: 'true'
nightly: true
version-bump: 'auto'
extensions-root: .
exclude-web-vsix: 'true'
node-version: '24'
package-manager: pnpm
package-manager-version: '10'
cache-dependency-path: pnpm-lock.yaml
install-command: pnpm run ci:install
package-command: cd lana && pnpm dlx @vscode/vsce package --target web --no-dependencies --out lana-$(node -p "require('./package.json').version").vsix
prerelease-package-command: cd lana && pnpm dlx @vscode/vsce package --pre-release --target web --no-dependencies --out lana-$(node -p "require('./package.json').version").vsix
web-package-command: cd lana && pnpm dlx @vscode/vsce package --target web --no-dependencies --out lana-web-$(node -p "require('./package.json').version").vsix
web-prerelease-package-command: cd lana && pnpm dlx @vscode/vsce package --pre-release --target web --no-dependencies --out lana-web-$(node -p "require('./package.json').version").vsix
artifact-glob: lana/*.vsix
publish-web-vsix: false
dry-run: ${{ inputs.dry-run && 'true' || 'false' }}
secrets: inherit
34 changes: 34 additions & 0 deletions .github/workflows/promote-prerelease.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Promote Nightly to Pre-release

on:
# schedule:
# # Wednesdays at 07:00 UTC. Enable once nightly releases are established.
# - cron: '0 7 * * 3'
workflow_dispatch:
inputs:
min-tag-age-days:
description: 'Minimum nightly age in days before eligible for promotion'
required: false
default: '7'
type: string
dry-run:
description: 'Run in dry-run mode (no actual publishing or tagging)'
required: false
default: true
type: boolean

permissions:
contents: write
packages: write
actions: read

jobs:
promote:
uses: salesforcecli/github-workflows/.github/workflows/vscode-promote-prerelease.yml@ph/W-23832274-pnpm-stable-promotion
with:
min-tag-age-days: ${{ inputs.min-tag-age-days || '7' }}
vsix-name-pattern: 'lana-*.vsix'
exclude-web-vsix: 'true'
extension-name: lana
dry-run: ${{ inputs.dry-run && 'true' || 'false' }}
secrets: inherit
38 changes: 38 additions & 0 deletions .github/workflows/promote-stable.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Promote Pre-release to Stable

on:
# Cron schedule intentionally disabled — promotion to stable is manual only.
# To enable scheduled promotion, restore the schedule trigger below:
# schedule:
# # Wednesdays at 06:00 UTC, before pre-release promotion.
# - cron: '0 6 * * 3'
workflow_dispatch:
inputs:
dry-run:
description: 'Run in dry-run mode (no actual publishing or tagging)'
required: false
default: true
type: boolean

permissions:
contents: write
packages: write
actions: read

jobs:
promote:
uses: salesforcecli/github-workflows/.github/workflows/vscode-promote-stable.yml@ph/W-23832274-pnpm-stable-promotion
with:
extension-name: lana
vsix-name-pattern: 'lana-*.vsix'
exclude-web-vsix: 'true'
extensions-root: .
node-version: '24'
package-manager: pnpm
package-manager-version: '10'
cache-dependency-path: pnpm-lock.yaml
lockfile-path: pnpm-lock.yaml
install-command: pnpm run ci:install
required-checks: E2E
dry-run: ${{ inputs.dry-run && 'true' || 'false' }}
secrets: inherit
46 changes: 44 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ permissions:

jobs:
publish:
uses: salesforcecli/github-workflows/.github/workflows/vscode-publish-extensions.yml@main
uses: salesforcecli/github-workflows/.github/workflows/vscode-publish-extensions.yml@ph/W-23832274-pnpm-stable-promotion
with:
branch: ${{ github.ref_name }}
extensions: lana
Expand All @@ -56,5 +56,47 @@ jobs:
web-package-command: cd lana && pnpm dlx @vscode/vsce package --target web --no-dependencies --out lana-web-$(node -p "require('./package.json').version").vsix
web-prerelease-package-command: cd lana && pnpm dlx @vscode/vsce package --target web --pre-release --no-dependencies --out lana-web-$(node -p "require('./package.json').version").vsix
artifact-glob: lana/*.vsix
publish-web-vsix: true
publish-web-vsix: false
secrets: inherit

publish-to-cbweb-marketplace:
name: Publish to CBWeb Internal Marketplace
needs: publish
runs-on: ubuntu-latest
steps:
- name: Download release artifacts
uses: actions/download-artifact@v8
with:
path: ./vsix-artifacts

- name: Find web VSIX
id: web-vsix
run: |
mapfile -t VSIX_FILES < <(find ./vsix-artifacts -type f -name 'lana-web-*.vsix')
if [ "${#VSIX_FILES[@]}" -ne 1 ]; then
echo "Expected exactly one web VSIX artifact, found ${#VSIX_FILES[@]}"
exit 1
fi

echo "vsix_file=${VSIX_FILES[0]}" >> "$GITHUB_OUTPUT"

- name: Publish web VSIX to CBWeb internal marketplace
if: inputs.dry-run == false
env:
MARKETPLACE_URL: ${{ vars.MARKETPLACE_URL }}
MARKETPLACE_DEPLOY_TOKEN: ${{ secrets.MARKETPLACE_DEPLOY_TOKEN }}
VSIX_FILE: ${{ steps.web-vsix.outputs.vsix_file }}
run: |
if [ -z "$MARKETPLACE_URL" ] || [ -z "$MARKETPLACE_DEPLOY_TOKEN" ]; then
echo "CBWeb marketplace credentials are required for live publishing"
exit 1
fi

curl --fail-with-body \
-X POST "${MARKETPLACE_URL}/api/internal/publish" \
-H "Authorization: Bearer ${MARKETPLACE_DEPLOY_TOKEN}" \
-F "vsix=@${VSIX_FILE}"

- name: Dry-run summary
if: inputs.dry-run
run: 'echo "DRY RUN: Would publish ${{ steps.web-vsix.outputs.vsix_file }} to CBWeb marketplace"'
Loading
Loading