Skip to content

docs: refocus gran around local workspace #277

docs: refocus gran around local workspace

docs: refocus gran around local workspace #277

Workflow file for this run

name: CI
on:
push:
pull_request:
workflow_dispatch:
inputs:
publish:
description: Force a publish candidate review for the selected ref
type: boolean
default: false
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: voidzero-dev/setup-vp@v1
with:
node-version: 24
cache: true
- name: Install
run: vp install --frozen-lockfile
- name: Install Docs Dependencies
run: npm ci --prefix docs
- name: Check Web Bundle
run: npm run web:check
- name: Check
run: vp check
- name: SDK Check
run: npm run sdk:check
- name: Test
run: vp test
- name: SDK Test
run: npm run sdk:test
- name: Coverage
run: npm run coverage
- name: Standalone Smoke Build
run: npm run standalone:smoke
- name: Pack
run: vp pack
- name: SDK Pack
run: npm run sdk:build
- name: Build Docs
run: npm run docs:check
- name: Install Playwright Browser
run: npx playwright install --with-deps chromium
- name: Browser E2E
run: npm run browser:e2e
- name: Pack Dry Run
run: npm pack --dry-run
- name: SDK Pack Dry Run
run: npm run sdk:pack:dry-run
prepare-publish:
if: |
(github.event_name == 'push' &&
github.ref == 'refs/heads/main' &&
startsWith(github.event.head_commit.message, 'chore: release v')) ||
(github.event_name == 'workflow_dispatch' && inputs.publish)
needs: verify
runs-on: ubuntu-latest
outputs:
package_name: ${{ steps.package.outputs.package_name }}
package_version: ${{ steps.package.outputs.package_version }}
publish_matrix: ${{ steps.npm.outputs.publish_matrix }}
should_publish: ${{ steps.npm.outputs.should_publish }}
steps:
- uses: actions/checkout@v6
- id: package
name: Read package metadata
run: |
node <<'EOF'
const fs = require('node:fs');
const rootPkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
const sdkPkg = JSON.parse(fs.readFileSync('packages/sdk/package.json', 'utf8'));
const packages = [
{ name: rootPkg.name, version: rootPkg.version, workspace: '.', publishName: rootPkg.name },
{ name: sdkPkg.name, version: sdkPkg.version, workspace: sdkPkg.name, publishName: sdkPkg.name },
];
fs.appendFileSync(process.env.GITHUB_OUTPUT, `package_name=${rootPkg.name}\n`);
fs.appendFileSync(process.env.GITHUB_OUTPUT, `package_version=${rootPkg.version}\n`);
fs.appendFileSync(process.env.GITHUB_OUTPUT, `packages=${JSON.stringify(packages)}\n`);
EOF
- id: npm
name: Check whether package versions are already published
env:
PACKAGES_JSON: ${{ steps.package.outputs.packages }}
run: |
node <<'EOF'
const { execSync } = require('node:child_process');
const fs = require('node:fs');
const packages = JSON.parse(process.env.PACKAGES_JSON);
const publishable = [];
for (const pkg of packages) {
try {
execSync(`npm view "${pkg.name}@${pkg.version}" version`, { stdio: 'ignore' });
console.log(`Version ${pkg.name}@${pkg.version} is already on npm.`);
} catch {
console.log(`Version ${pkg.name}@${pkg.version} is unpublished and ready for review.`);
publishable.push(pkg);
}
}
fs.appendFileSync(
process.env.GITHUB_OUTPUT,
`should_publish=${publishable.length > 0 ? 'true' : 'false'}\n`,
);
fs.appendFileSync(
process.env.GITHUB_OUTPUT,
`publish_matrix=${JSON.stringify(publishable)}\n`,
);
EOF
publish:
if: needs.prepare-publish.outputs.should_publish == 'true'
needs:
- verify
- prepare-publish
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
concurrency:
group: npm-publish-production
cancel-in-progress: false
strategy:
fail-fast: false
matrix:
include: ${{ fromJson(needs.prepare-publish.outputs.publish_matrix) }}
environment:
name: production
url: https://www.npmjs.com/package/${{ needs.prepare-publish.outputs.package_name }}
steps:
- uses: actions/checkout@v6
- uses: voidzero-dev/setup-vp@v1
with:
node-version: 24
cache: true
- name: Install
run: vp install --frozen-lockfile
- name: Publish
env:
PACKAGE_NAME: ${{ matrix.name }}
PACKAGE_VERSION: ${{ matrix.version }}
PACKAGE_WORKSPACE: ${{ matrix.workspace }}
run: |
echo "Publishing ${PACKAGE_NAME}@${PACKAGE_VERSION}"
if [ "${PACKAGE_WORKSPACE}" = "." ]; then
npm publish --provenance --access public
else
npm publish --provenance --access public --workspace "${PACKAGE_WORKSPACE}"
fi
standalone-assets:
if: needs.prepare-publish.outputs.should_publish == 'true'
needs:
- verify
- prepare-publish
strategy:
fail-fast: false
matrix:
include:
- runner: ubuntu-latest
target: linux-x64
- runner: macos-14
target: darwin-arm64
- runner: windows-latest
target: win32-x64
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v6
- uses: voidzero-dev/setup-vp@v1
with:
node-version: 24
cache: true
- name: Install
run: vp install --frozen-lockfile
- name: Build standalone archive
run: npm run standalone:build -- --target ${{ matrix.target }}
- uses: actions/upload-artifact@v4
with:
name: standalone-${{ matrix.target }}
path: |
dist/release-assets/*.tar.gz
dist/release-assets/*.zip
github-release:
if: needs.prepare-publish.outputs.should_publish == 'true'
needs:
- verify
- prepare-publish
- publish
- standalone-assets
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: actions/download-artifact@v5
with:
merge-multiple: true
path: release-assets
pattern: standalone-*
- name: Build asset checksums
run: |
cd release-assets
sha256sum * > SHA256SUMS.txt
- name: Create GitHub Release
env:
GH_TOKEN: ${{ github.token }}
PACKAGE_NAME: ${{ needs.prepare-publish.outputs.package_name }}
PACKAGE_VERSION: ${{ needs.prepare-publish.outputs.package_version }}
run: |
tag="v${PACKAGE_VERSION}"
title="${PACKAGE_NAME} v${PACKAGE_VERSION}"
mapfile -t assets < <(find release-assets -maxdepth 1 -type f | sort)
notes_file="$(mktemp)"
node scripts/release-notes.mjs > "${notes_file}"
if gh release view "${tag}" >/dev/null 2>&1; then
echo "GitHub Release ${tag} already exists; refreshing notes and assets."
gh release edit "${tag}" \
--latest \
--notes-file "${notes_file}" \
--title "${title}"
else
gh release create "${tag}" \
--latest \
--notes-file "${notes_file}" \
--title "${title}"
fi
gh release upload "${tag}" "${assets[@]}" --clobber
auto-approve-publish:
if: needs.prepare-publish.outputs.should_publish == 'true'
needs:
- verify
- prepare-publish
runs-on: ubuntu-latest
steps:
- id: approver
name: Check auto-approver configuration
env:
GH_RELEASE_APPROVER_TOKEN: ${{ secrets.GH_RELEASE_APPROVER_TOKEN }}
run: |
if [ -n "${GH_RELEASE_APPROVER_TOKEN}" ]; then
echo "configured=true" >> "$GITHUB_OUTPUT"
else
echo "configured=false" >> "$GITHUB_OUTPUT"
echo "No GH_RELEASE_APPROVER_TOKEN secret configured; leaving the publish gate manual."
fi
- uses: actions/github-script@v8
if: steps.approver.outputs.configured == 'true'
env:
TARGET_ENVIRONMENT: production
with:
github-token: ${{ secrets.GH_RELEASE_APPROVER_TOKEN }}
script: |
const runId = context.runId;
const environmentName = process.env.TARGET_ENVIRONMENT;
for (let attempt = 1; attempt <= 60; attempt += 1) {
const { data: pending } = await github.request(
"GET /repos/{owner}/{repo}/actions/runs/{run_id}/pending_deployments",
{
owner: context.repo.owner,
repo: context.repo.repo,
run_id: runId,
},
);
const deployment = pending.find(
(candidate) => candidate.environment?.name === environmentName,
);
if (deployment) {
await github.request(
"POST /repos/{owner}/{repo}/actions/runs/{run_id}/pending_deployments",
{
owner: context.repo.owner,
repo: context.repo.repo,
run_id: runId,
environment_ids: [deployment.environment.id],
state: "approved",
comment: `Automatically approved ${environmentName} publish candidate`,
},
);
core.info(
`Approved pending deployment for ${environmentName} on run ${runId}.`,
);
return;
}
core.info(
`No pending deployment for ${environmentName} yet (attempt ${attempt}/60). Waiting 5 seconds...`,
);
await new Promise((resolve) => setTimeout(resolve, 5_000));
}
core.setFailed(
`Timed out waiting for a pending ${environmentName} deployment on run ${runId}.`,
);