Skip to content

fix: resolve signed-out model slot references to env slots, add regression test coverage #1

fix: resolve signed-out model slot references to env slots, add regression test coverage

fix: resolve signed-out model slot references to env slots, add regression test coverage #1

Workflow file for this run

name: Publish ZCode CLI release
on:
workflow_dispatch:
inputs:
publish:
description: Create the matching Git tag and GitHub release
required: true
default: true
type: boolean
pull_request:
branches:
- main
types:
- closed
permissions: {}
concurrency:
group: zcode-cli-publish
cancel-in-progress: false
jobs:
validate:
if: >-
(
github.event_name == 'workflow_dispatch' &&
github.ref_name == github.event.repository.default_branch
) ||
(
github.event_name == 'pull_request' &&
github.event.pull_request.merged == true &&
github.event.pull_request.head.repo.full_name == github.repository &&
(
github.event.pull_request.head.ref == 'release/zcode-cli' ||
github.event.pull_request.head.ref == 'release/zcode-upstream'
)
)
runs-on: ubuntu-latest
timeout-minutes: 45
permissions:
contents: read
outputs:
artifact_name: ${{ steps.package.outputs.artifact_name }}
package_name: ${{ steps.expected.outputs.package_name }}
package_version: ${{ steps.expected.outputs.package_version }}
expected_commit: ${{ steps.expected.outputs.commit }}
tarball: ${{ steps.package.outputs.tarball }}
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
fetch-depth: 0
fetch-tags: true
persist-credentials: false
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.merge_commit_sha || github.sha }}
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
with:
node-version: 24
package-manager-cache: false
- name: Prepare reproducible npm toolchain
run: |
npm install --global npm@12.0.1 --ignore-scripts
node --version
npm --version
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: 1.3.12
- name: Install extraction tools
run: sudo apt-get update && sudo apt-get install -y p7zip-full
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Capture committed release
id: expected
run: |
git ls-files --error-unmatch zcode-runtime.lock.json >/dev/null
PACKAGE_NAME=$(node -p "JSON.parse(require('fs').readFileSync('package.json', 'utf8')).name")
PACKAGE_VERSION=$(node -p "JSON.parse(require('fs').readFileSync('package.json', 'utf8')).version")
echo "package_name=$PACKAGE_NAME" >> "$GITHUB_OUTPUT"
echo "package_version=$PACKAGE_VERSION" >> "$GITHUB_OUTPUT"
echo "commit=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
- name: Build committed release
run: bun run release:build
- name: Verify release did not drift
env:
EXPECTED_VERSION: ${{ steps.expected.outputs.package_version }}
run: |
ACTUAL_VERSION=$(node -p "JSON.parse(require('fs').readFileSync('package.json', 'utf8')).version")
if [[ "$ACTUAL_VERSION" != "$EXPECTED_VERSION" ]]; then
echo "The locked release changed while rebuilding: expected ${EXPECTED_VERSION}, resolved ${ACTUAL_VERSION}." >&2
exit 1
fi
git diff --exit-code -- package.json zcode-runtime.lock.json
- name: Pack and install-test release
id: package
env:
ARTIFACT_NAME: validated-release-${{ github.run_id }}
run: |
bun run release:pack
TARBALL=$(node -p "JSON.parse(require('fs').readFileSync('.release/release.json', 'utf8')).tarball")
[[ -f "$TARBALL" ]] || { echo "Validated tarball is missing: $TARBALL" >&2; exit 1; }
rm -rf release-artifact
mkdir release-artifact
cp .release/release.json "$TARBALL" release-artifact/
echo "artifact_name=$ARTIFACT_NAME" >> "$GITHUB_OUTPUT"
- name: Upload validated release
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: validated-release-${{ github.run_id }}
path: release-artifact
if-no-files-found: error
retention-days: 7
publish:
needs: validate
if: needs.validate.result == 'success'
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: write
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
fetch-depth: 0
fetch-tags: true
persist-credentials: false
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.merge_commit_sha || github.sha }}
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
with:
node-version: 24
package-manager-cache: false
- name: Download validated release
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: ${{ needs.validate.outputs.artifact_name }}
path: .release
- name: Verify validated tarball
env:
EXPECTED_NAME: ${{ needs.validate.outputs.package_name }}
EXPECTED_VERSION: ${{ needs.validate.outputs.package_version }}
EXPECTED_COMMIT: ${{ needs.validate.outputs.expected_commit }}
VALIDATED_TARBALL: ${{ needs.validate.outputs.tarball }}
run: |
PACKAGE_NAME=$(node -p "JSON.parse(require('fs').readFileSync('package.json', 'utf8')).name")
PACKAGE_VERSION=$(node -p "JSON.parse(require('fs').readFileSync('package.json', 'utf8')).version")
COMMIT=$(git rev-parse HEAD)
[[ "$PACKAGE_NAME" == "$EXPECTED_NAME" ]] || { echo "Package name changed while transferring the release." >&2; exit 1; }
[[ "$PACKAGE_VERSION" == "$EXPECTED_VERSION" ]] || { echo "Package version changed while transferring the release." >&2; exit 1; }
[[ "$COMMIT" == "$EXPECTED_COMMIT" ]] || { echo "Release commit changed while transferring the release." >&2; exit 1; }
git ls-files --error-unmatch zcode-runtime.lock.json >/dev/null
[[ -f "$VALIDATED_TARBALL" ]] || { echo "Validated tarball is missing: $VALIDATED_TARBALL" >&2; exit 1; }
node - "$VALIDATED_TARBALL" "$EXPECTED_NAME" "$EXPECTED_VERSION" <<'NODE'
const [tarball, expectedName, expectedVersion] = process.argv.slice(2);
const release = JSON.parse(require("fs").readFileSync(".release/release.json", "utf8"));
if (release.name !== expectedName || release.version !== expectedVersion) {
throw new Error("Validated release metadata does not match the checked-out package.");
}
if (release.tarball !== tarball) {
throw new Error(`Validated tarball path differs: ${release.tarball} != ${tarball}`);
}
NODE
- name: Rebuild tarball without project scripts
env:
VALIDATED_TARBALL: ${{ needs.validate.outputs.tarball }}
run: |
rm -rf .release/publish .release/rebuilt
mkdir -p .release/publish .release/rebuilt
tar -xzf "$VALIDATED_TARBALL" -C .release/publish
[[ -f .release/publish/package/package.json ]] || { echo "Validated tarball has no package root." >&2; exit 1; }
npm pack ./.release/publish/package \
--ignore-scripts \
--json \
--pack-destination .release/rebuilt > .release/rebuilt.json
REBUILT_TARBALL=$(find .release/rebuilt -maxdepth 1 -type f -name '*.tgz' -print -quit)
[[ -n "$REBUILT_TARBALL" ]] || { echo "Rebuilding the release tarball produced no archive." >&2; exit 1; }
# npm pack names the archive after the package; the validated asset
# carries the same project name. Compare the contents under the
# rebuilt name, then publish the renamed asset.
mv "$REBUILT_TARBALL" ".release/rebuilt/$(basename "$VALIDATED_TARBALL")"
REBUILT_TARBALL=".release/rebuilt/$(basename "$VALIDATED_TARBALL")"
if ! cmp -- "$VALIDATED_TARBALL" "$REBUILT_TARBALL"; then
echo "The privileged publish job produced a different tarball than the validated job." >&2
exit 1
fi
- name: Inspect release state
id: release
shell: bash
env:
EXPECTED_COMMIT: ${{ needs.validate.outputs.expected_commit }}
PACKAGE_NAME: ${{ needs.validate.outputs.package_name }}
PACKAGE_VERSION: ${{ needs.validate.outputs.package_version }}
PUBLISH_REQUESTED: ${{ github.event_name != 'workflow_dispatch' || inputs.publish }}
GH_TOKEN: ${{ github.token }}
run: |
TAG="v${PACKAGE_VERSION}"
git fetch --tags --force origin
TAG_COMMIT=$(git rev-list -n 1 "$TAG" 2>/dev/null || true)
if [[ -n "$TAG_COMMIT" && "$TAG_COMMIT" != "$EXPECTED_COMMIT" ]]; then
echo "${TAG} already points to ${TAG_COMMIT}, not ${EXPECTED_COMMIT}." >&2
exit 1
fi
if gh release view "$TAG" >/dev/null 2>&1; then
RELEASE_EXISTS=true
else
RELEASE_EXISTS=false
fi
echo "enabled=$PUBLISH_REQUESTED" >> "$GITHUB_OUTPUT"
echo "create_tag=$([[ -n "$TAG_COMMIT" ]] && echo false || echo true)" >> "$GITHUB_OUTPUT"
echo "create_release=$([[ "$RELEASE_EXISTS" == "true" ]] && echo false || echo true)" >> "$GITHUB_OUTPUT"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
- name: Create immutable Git tag
if: steps.release.outputs.enabled == 'true' && steps.release.outputs.create_tag == 'true'
env:
GH_TOKEN: ${{ github.token }}
PACKAGE_NAME: ${{ needs.validate.outputs.package_name }}
PACKAGE_VERSION: ${{ needs.validate.outputs.package_version }}
EXPECTED_COMMIT: ${{ needs.validate.outputs.expected_commit }}
TAG: ${{ steps.release.outputs.tag }}
run: |
TAG_OBJECT=$(gh api --method POST "repos/${GITHUB_REPOSITORY}/git/tags" \
-f tag="$TAG" \
-f message="${PACKAGE_NAME}@${PACKAGE_VERSION}" \
-f object="$EXPECTED_COMMIT" \
-f type=commit \
--jq .sha)
gh api --method POST "repos/${GITHUB_REPOSITORY}/git/refs" \
-f ref="refs/tags/${TAG}" \
-f sha="$TAG_OBJECT" >/dev/null
- name: Create GitHub Release
if: steps.release.outputs.enabled == 'true' && steps.release.outputs.create_release == 'true'
env:
GH_TOKEN: ${{ github.token }}
PACKAGE_NAME: ${{ needs.validate.outputs.package_name }}
PACKAGE_VERSION: ${{ needs.validate.outputs.package_version }}
TAG: ${{ steps.release.outputs.tag }}
run: |
gh release create "$TAG" --verify-tag --generate-notes --title "${PACKAGE_NAME}@${PACKAGE_VERSION}"
gh release upload "$TAG" ".release/zcode-cli-${PACKAGE_VERSION}.tgz" --clobber
- name: Attach tarball to an existing GitHub Release
if: steps.release.outputs.enabled == 'true' && steps.release.outputs.create_release == 'false'
env:
GH_TOKEN: ${{ github.token }}
PACKAGE_NAME: ${{ needs.validate.outputs.package_name }}
PACKAGE_VERSION: ${{ needs.validate.outputs.package_version }}
TAG: ${{ steps.release.outputs.tag }}
run: gh release upload "$TAG" ".release/zcode-cli-${PACKAGE_VERSION}.tgz" --clobber
- name: Summarize release
if: always()
env:
ENABLED: ${{ steps.release.outputs.enabled }}
PACKAGE_NAME: ${{ needs.validate.outputs.package_name }}
PACKAGE_VERSION: ${{ needs.validate.outputs.package_version }}
TAG: ${{ steps.release.outputs.tag }}
run: |
echo "Release: ${PACKAGE_NAME}@${PACKAGE_VERSION}" >> "$GITHUB_STEP_SUMMARY"
echo "Mutations enabled: ${ENABLED:-unknown}" >> "$GITHUB_STEP_SUMMARY"
echo "Git tag: ${TAG:-unknown}" >> "$GITHUB_STEP_SUMMARY"