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
54 changes: 54 additions & 0 deletions .github/actions/marketplace-auth/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Implements [SWR-VSIX-PUBLISH-AUTH]: one authentication path for preflight and publishing.
name: Verify Marketplace authentication
description: Prefer Entra OIDC when configured, otherwise use the configured Marketplace PAT.
inputs:
azure_client_id:
description: Entra workload identity client ID
required: false
azure_tenant_id:
description: Entra tenant ID
required: false
marketplace_pat:
description: Existing Marketplace publishing token
required: false
outputs:
flags:
description: Nonsecret authentication flag for vsce
value: ${{ steps.auth.outputs.flags }}
runs:
using: composite
steps:
- name: Azure login (OIDC)
if: inputs.azure_client_id != '' && inputs.azure_tenant_id != ''
uses: azure/login@a457da9ea143d694b1b9c7c869ebb04ebe844ef5 # v2.3.0
with:
client-id: ${{ inputs.azure_client_id }}
tenant-id: ${{ inputs.azure_tenant_id }}
allow-no-subscriptions: true
- name: Select publishing credentials
id: auth
shell: bash
env:
CLIENT_ID: ${{ inputs.azure_client_id }}
TENANT_ID: ${{ inputs.azure_tenant_id }}
MARKETPLACE_PAT: ${{ inputs.marketplace_pat }}
run: |
set -euo pipefail
if [ -n "$CLIENT_ID" ] && [ -n "$TENANT_ID" ]; then
echo 'flags=--azure-credential' >> "$GITHUB_OUTPUT"
elif [ -n "$MARKETPLACE_PAT" ]; then
printf 'VSCE_PAT=%s\n' "$MARKETPLACE_PAT" >> "$GITHUB_ENV"
echo 'flags=' >> "$GITHUB_OUTPUT"
else
echo '::error::Marketplace publishing credentials are not configured.' >&2
exit 1
fi
- name: Verify publisher access without publishing
shell: bash
env:
AUTH_FLAGS: ${{ steps.auth.outputs.flags }}
run: |
set -euo pipefail
publisher="$(node -p "require('./Lql/LqlExtension/package.json').publisher")"
read -r -a auth_args <<< "$AUTH_FLAGS"
npx --yes @vscode/vsce@4.0.0 verify-pat "$publisher" "${auth_args[@]}"
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ jobs:
run: make lint

- name: Test release version stamper
run: python3 tools/test-version-stamp.py
run: make _test_version_stamp

- name: Install website dependencies
run: npm ci --prefix Website --ignore-scripts --no-audit --no-fund
Expand Down
32 changes: 18 additions & 14 deletions .github/workflows/publish-vsix-per-platform.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Adapted from Shipwright templates/gh-actions/publish-vsix-per-platform.yml
# One VSIX per vsceTarget (never a fat native VSIX): build lql-lsp, stage it under
# bin/<target>/, package, verify contents, attest provenance, then publish each VSIX
# to the Marketplace via Entra OIDC (no stored PAT).
# to the Marketplace via Entra OIDC or the configured Marketplace token.
# [SWR-VSIX-PACKAGE] [SWR-VSIX-LAYOUT] [SWR-VSIX-VERIFY] [SWR-VSIX-PUBLISH-OIDC] [SWR-VSIX-PROVENANCE]
name: publish-vsix-per-platform

Expand All @@ -24,6 +24,8 @@ on:
required: false
azure_tenant_id:
required: false
marketplace_pat:
required: false

permissions:
contents: read
Expand Down Expand Up @@ -176,35 +178,37 @@ jobs:
contents: read
id-token: write
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
path: dist
merge-multiple: true
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
with:
node-version: 22
# Entra workload-identity federation — no stored VSCE_PAT. [SWR-VSIX-PUBLISH-OIDC]
- name: Azure login (OIDC)
uses: azure/login@a457da9ea143d694b1b9c7c869ebb04ebe844ef5 # v2.3.0
# Implements [SWR-VSIX-PUBLISH-AUTH]: reuse the same credential verification as preflight.
- name: Verify Marketplace publishing credentials
id: auth
uses: ./.github/actions/marketplace-auth
with:
client-id: ${{ secrets.azure_client_id }}
tenant-id: ${{ secrets.azure_tenant_id }}
allow-no-subscriptions: true
azure_client_id: ${{ secrets.azure_client_id }}
azure_tenant_id: ${{ secrets.azure_tenant_id }}
marketplace_pat: ${{ secrets.marketplace_pat }}
- name: Publish each VSIX to the Marketplace
shell: bash
env:
PRE_RELEASE: ${{ inputs.pre_release }}
AUTH_FLAGS: ${{ steps.auth.outputs.flags }}
run: |
set -euo pipefail
VSCE_PAT="$(az account get-access-token \
--resource 499b84ac-1321-427f-aa17-267ca6975798 \
--query accessToken -o tsv)"
export VSCE_PAT
flag=""
if [ "$PRE_RELEASE" = "true" ]; then flag="--pre-release"; fi
flags=()
if [ "$PRE_RELEASE" = "true" ]; then flags+=(--pre-release); fi
read -r -a auth_args <<< "$AUTH_FLAGS"
shopt -s nullglob
for vsix in dist/*.vsix; do
echo "::group::publish $vsix"
npx vsce publish --packagePath "$vsix" $flag
npx --yes @vscode/vsce@4.0.0 publish --packagePath "$vsix" "${flags[@]}" "${auth_args[@]}" --skip-duplicate
echo "::endgroup::"
done
50 changes: 36 additions & 14 deletions .github/workflows/release-dotnet-aot-multiplatform.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ jobs:
- uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1
with:
dotnet-version: "10.0.x"
- name: Stamp version from tag
shell: bash
run: python3 tools/shipwright-version-stamp.py --tag "v${{ inputs.version }}" --root .
# Native AOT needs the platform C toolchain; ubuntu/macos runners ship it,
# Windows runners ship MSVC. Linux additionally needs clang + zlib headers.
- name: Install Linux AOT toolchain
Expand All @@ -88,17 +91,6 @@ jobs:
test -f "$path"
echo "path=$path" >> "$GITHUB_OUTPUT"
echo "exe=$exe" >> "$GITHUB_OUTPUT"
- name: Native smoke test (migrate example schema → SQLite)
shell: bash
env:
DATAPROVIDERMIGRATE_AOT_BIN: ${{ steps.bin.outputs.path }}
run: |
set -euo pipefail
db="${RUNNER_TEMP}/aot_smoke.db"
"${DATAPROVIDERMIGRATE_AOT_BIN}" migrate \
--schema Migration/DataProviderMigrate/example-schema.yaml \
--output "$db" --provider sqlite
test -f "$db"
- name: Package archive
id: pkg
shell: bash
Expand All @@ -107,14 +99,44 @@ jobs:
stage="${{ inputs.binary_name }}-${{ inputs.version }}-${{ matrix.platform }}"
mkdir -p "${GITHUB_WORKSPACE}/dist"
if [ "${{ runner.os }}" = "Windows" ]; then
7z a "${GITHUB_WORKSPACE}/dist/${stage}.zip" "${{ steps.bin.outputs.path }}" >/dev/null
cd aot-out
7z a "${GITHUB_WORKSPACE}/dist/${stage}.zip" . >/dev/null
echo "archive=dist/${stage}.zip" >> "$GITHUB_OUTPUT"
else
tar -czf "${GITHUB_WORKSPACE}/dist/${stage}.tar.gz" \
-C "$(dirname "${{ steps.bin.outputs.path }}")" \
"$(basename "${{ steps.bin.outputs.path }}")"
-C aot-out .
echo "archive=dist/${stage}.tar.gz" >> "$GITHUB_OUTPUT"
fi
- name: Extract distributed archive
id: unpack
shell: bash
env:
ARCHIVE: ${{ steps.pkg.outputs.archive }}
EXE: ${{ steps.bin.outputs.exe }}
BINARY_NAME: ${{ inputs.binary_name }}
run: |
set -euo pipefail
dest="${RUNNER_TEMP}/aot-packaged"
mkdir -p "$dest"
if [ "${{ runner.os }}" = "Windows" ]; then
7z x "$ARCHIVE" "-o${dest}" >/dev/null
else
tar -xzf "$ARCHIVE" -C "$dest"
fi
path="${dest}/${BINARY_NAME}${EXE}"
test -f "$path"
echo "path=$path" >> "$GITHUB_OUTPUT"
- name: Packaged native smoke test (migrate example schema → SQLite)
shell: bash
env:
DATAPROVIDERMIGRATE_AOT_BIN: ${{ steps.unpack.outputs.path }}
run: |
set -euo pipefail
db="${RUNNER_TEMP}/aot_smoke.db"
"${DATAPROVIDERMIGRATE_AOT_BIN}" migrate \
--schema Migration/DataProviderMigrate/example-schema.yaml \
--output "$db" --provider sqlite
test -f "$db"
- name: Generate CycloneDX SBOM
uses: anchore/sbom-action@f08f31f53bbe4451c46f9c5d8a8ff4cbb2c5ed27 # v0.21.0
with:
Expand Down
65 changes: 57 additions & 8 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
name: Release

on:
workflow_dispatch:
inputs:
version:
description: Version to preflight without publishing (for example 0.10.0-beta)
required: true
type: string
push:
tags:
- "v[0-9]+.[0-9]+.[0-9]+"
Expand All @@ -33,19 +39,22 @@ jobs:
environment: release
permissions:
contents: read
id-token: write
outputs:
version: ${{ steps.v.outputs.version }}
prerelease: ${{ steps.v.outputs.prerelease }}
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Derive version from tag
- name: Derive release version
id: v
shell: bash
env:
RELEASE_TAG: ${{ inputs.version || github.ref_name }}
run: |
set -euo pipefail
version="${GITHUB_REF_NAME#v}"
version="${RELEASE_TAG#v}"
echo "version=${version}" >> "$GITHUB_OUTPUT"
if [[ "$version" == *-* ]]; then
echo "prerelease=true" >> "$GITHUB_OUTPUT"
Expand All @@ -54,13 +63,22 @@ jobs:
fi
- name: Stamp version into runner working tree (never committed)
shell: bash
run: python3 tools/shipwright-version-stamp.py --tag "${GITHUB_REF_NAME}" --root .
run: python3 tools/shipwright-version-stamp.py --tag "v${{ steps.v.outputs.version }}" --root .
- name: Validate Shipwright manifests
shell: bash
run: |
set -euo pipefail
python3 tools/validate-manifest.py shipwright.json
python3 tools/validate-manifest.py Lql/LqlExtension/shipwright.json
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
with:
node-version: 22
- name: Verify Marketplace publishing credentials
uses: ./.github/actions/marketplace-auth
with:
azure_client_id: ${{ secrets.AZURE_CLIENT_ID }}
azure_tenant_id: ${{ secrets.AZURE_TENANT_ID }}
marketplace_pat: ${{ secrets.VSCODE_MARKETPLACE_PAT }}

# Implements [CI-RELEASE-GATE]: run the same CI on the tagged SHA before publishing.
ci-gate:
Expand All @@ -82,6 +100,10 @@ jobs:

# ── NuGet packages ────────────────────────────────────────────────────────
publish-nuget:
if: >-
github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') &&
needs.prepare.result == 'success' && needs.ci-gate.result == 'success' &&
needs.codeql-gate.result == 'success'
needs: [prepare, ci-gate, codeql-gate]
runs-on: ubuntu-latest
timeout-minutes: 15
Expand Down Expand Up @@ -128,6 +150,10 @@ jobs:

# ── Per-platform binaries → GitHub Release ────────────────────────────────
release-binaries:
if: >-
github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') &&
needs.prepare.result == 'success' && needs.ci-gate.result == 'success' &&
needs.codeql-gate.result == 'success'
needs: [prepare, ci-gate, codeql-gate]
uses: ./.github/workflows/release-binary-multiplatform.yml
with:
Expand All @@ -140,6 +166,10 @@ jobs:

# ── Per-platform .NET Native AOT migration CLI → GitHub Release [MIG-AOT-CI] ─
release-dotnet-binaries:
if: >-
github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') &&
needs.prepare.result == 'success' && needs.ci-gate.result == 'success' &&
needs.codeql-gate.result == 'success'
needs: [prepare, ci-gate, codeql-gate]
uses: ./.github/workflows/release-dotnet-aot-multiplatform.yml
with:
Expand Down Expand Up @@ -179,7 +209,9 @@ jobs:
# ── Homebrew tap ──────────────────────────────────────────────────────────
publish-brew:
needs: [prepare, checksums]
if: needs.prepare.outputs.prerelease == 'false'
if: >-
github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') &&
needs.checksums.result == 'success' && needs.prepare.outputs.prerelease == 'false'
uses: ./.github/workflows/publish-brew-tap.yml
with:
tap_repo: Nimblesite/homebrew-lql
Expand All @@ -196,7 +228,9 @@ jobs:
# ── Scoop bucket ──────────────────────────────────────────────────────────
publish-scoop:
needs: [prepare, checksums]
if: needs.prepare.outputs.prerelease == 'false'
if: >-
github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') &&
needs.checksums.result == 'success' && needs.prepare.outputs.prerelease == 'false'
uses: ./.github/workflows/publish-scoop-bucket.yml
with:
bucket_repo: Nimblesite/scoop-lql
Expand Down Expand Up @@ -239,7 +273,9 @@ jobs:
# ── Homebrew tap (migration CLI) ──────────────────────────────────────────
publish-brew-migrate:
needs: [prepare, dotnet-checksums]
if: needs.prepare.outputs.prerelease == 'false'
if: >-
github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') &&
needs.dotnet-checksums.result == 'success' && needs.prepare.outputs.prerelease == 'false'
uses: ./.github/workflows/publish-brew-tap.yml
with:
tap_repo: Nimblesite/homebrew-lql
Expand All @@ -256,7 +292,9 @@ jobs:
# ── Scoop bucket (migration CLI) ──────────────────────────────────────────
publish-scoop-migrate:
needs: [prepare, dotnet-checksums]
if: needs.prepare.outputs.prerelease == 'false'
if: >-
github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') &&
needs.dotnet-checksums.result == 'success' && needs.prepare.outputs.prerelease == 'false'
uses: ./.github/workflows/publish-scoop-bucket.yml
with:
bucket_repo: Nimblesite/scoop-lql
Expand All @@ -270,8 +308,12 @@ jobs:
secrets:
bucket_token: ${{ secrets.SCOOP_BUCKET_TOKEN }}

# ── Per-platform VSIX → VS Code Marketplace (Entra OIDC, no PAT) ───────────
# ── Per-platform VSIX → VS Code Marketplace ───────────────────────────────
publish-vsix:
if: >-
github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') &&
needs.prepare.result == 'success' && needs.ci-gate.result == 'success' &&
needs.codeql-gate.result == 'success' && needs.release-binaries.result == 'success'
needs: [prepare, ci-gate, codeql-gate, release-binaries]
uses: ./.github/workflows/publish-vsix-per-platform.yml
with:
Expand All @@ -281,13 +323,17 @@ jobs:
secrets:
azure_client_id: ${{ secrets.AZURE_CLIENT_ID }}
azure_tenant_id: ${{ secrets.AZURE_TENANT_ID }}
marketplace_pat: ${{ secrets.VSCODE_MARKETPLACE_PAT }}
permissions:
contents: read
id-token: write
attestations: write

# ── Documentation sites (both deploy on release) ──────────────────────────
deploy-lql-website:
if: >-
github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') &&
needs.publish-nuget.result == 'success'
needs: [prepare, publish-nuget]
uses: ./.github/workflows/deploy-lql-website.yml
with:
Expand All @@ -298,6 +344,9 @@ jobs:
id-token: write

deploy-website:
if: >-
github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') &&
needs.publish-nuget.result == 'success' && needs.deploy-lql-website.result == 'success'
needs: [prepare, publish-nuget, deploy-lql-website]
uses: ./.github/workflows/deploy-pages.yml
with:
Expand Down
Loading
Loading