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/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: ci

on:
pull_request:
push:
branches: [main]

permissions:
contents: read

jobs:
validate:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c
with:
toolchain: 1.95.0
targets: wasm32-unknown-unknown
components: clippy,rustfmt
- name: Install Lua and populate locked dependency caches
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends lua5.4
cargo fetch --locked
cargo fetch --manifest-path tools/component-conformance/Cargo.toml --locked
- name: Install pinned build tools
run: |
cargo install just --version 1.57.0 --locked
cargo install wasm-tools --version 1.252.0 --locked
cargo install b3sum --version 1.8.3 --locked
- name: Build checksum-verified zstd 1.5.7
working-directory: ${{ runner.temp }}
run: |
curl --proto '=https' --tlsv1.2 -fsSLO https://github.com/facebook/zstd/releases/download/v1.5.7/zstd-1.5.7.tar.gz
echo 'eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-1.5.7.tar.gz' | sha256sum --check --strict
tar -xzf zstd-1.5.7.tar.gz
make -C zstd-1.5.7 -j2 zstd
echo "$PWD/zstd-1.5.7/programs" >> "$GITHUB_PATH"
- name: Check source and execute actual component
run: |
just check
just component-check
55 changes: 55 additions & 0 deletions .github/workflows/prepare-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: prepare-release

on:
workflow_dispatch:

permissions:
contents: read

jobs:
assets:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c
with:
toolchain: 1.95.0
targets: wasm32-unknown-unknown
components: clippy,rustfmt
- name: Install Lua and populate locked dependency caches
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends lua5.4
cargo fetch --locked
cargo fetch --manifest-path tools/component-conformance/Cargo.toml --locked
- name: Install pinned build tools
run: |
cargo install just --version 1.57.0 --locked
cargo install wasm-tools --version 1.252.0 --locked
cargo install b3sum --version 1.8.3 --locked
- name: Build checksum-verified zstd 1.5.7
working-directory: ${{ runner.temp }}
run: |
curl --proto '=https' --tlsv1.2 -fsSLO https://github.com/facebook/zstd/releases/download/v1.5.7/zstd-1.5.7.tar.gz
echo 'eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-1.5.7.tar.gz' | sha256sum --check --strict
tar -xzf zstd-1.5.7.tar.gz
make -C zstd-1.5.7 -j2 zstd
echo "$PWD/zstd-1.5.7/programs" >> "$GITHUB_PATH"
- name: Build exact package and canonical release identity
run: |
set -euo pipefail
test "$GITHUB_REF" = refs/heads/main
test "$(sed -n 's/^source = "\([^"]*\)"$/\1/p' plugin.toml)" = "github:${GITHUB_REPOSITORY}"
test "$GITHUB_REPOSITORY" = sigil-plugins/temporal
test "$GITHUB_RUN_ATTEMPT" = 1
python3 scripts/acquire-release-sigil.py
just release-dist "$GITHUB_SHA" "$PWD/target/release-tools/sigil"
test "$(find dist -mindepth 1 -maxdepth 1 -type f | wc -l)" -eq 3
test "$(find dist -mindepth 1 -maxdepth 1 | wc -l)" -eq 3
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: release-candidate-assets
path: dist/
if-no-files-found: error
274 changes: 274 additions & 0 deletions .github/workflows/publish-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
name: publish-release

on:
workflow_dispatch:
inputs:
version:
description: Exact approved SemVer without v prefix
required: true
type: string
source_commit:
description: Exact approved 40-hex main commit
required: true
type: string
candidate_run_id:
description: Exact first-attempt prepare-release run
required: true
type: string
package_sha256:
description: Exact approved package SHA-256
required: true
type: string
checksums_sha256:
description: Exact approved SHA256SUMS SHA-256
required: true
type: string
release_manifest_sha256:
description: Exact approved release-manifest.json SHA-256
required: true
type: string
immutable_releases_verified:
description: Release approver verified enabled=true and enforced_by_owner=true via the admin API
required: true
type: boolean

concurrency:
group: official-publication
cancel-in-progress: false

permissions: {}

jobs:
publish:
runs-on: ubuntu-24.04
environment: release
permissions:
actions: read
artifact-metadata: write
attestations: write
contents: write
id-token: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Require authorized immutable-release preflight
env:
IMMUTABLE_RELEASES_VERIFIED: ${{ inputs.immutable_releases_verified }}
run: |
test "$IMMUTABLE_RELEASES_VERIFIED" = true
- name: Verify dispatch and candidate build identity
env:
GH_TOKEN: ${{ github.token }}
CANDIDATE_RUN_ID: ${{ inputs.candidate_run_id }}
SOURCE_COMMIT: ${{ inputs.source_commit }}
run: |
set -euo pipefail
[[ "$CANDIDATE_RUN_ID" =~ ^[1-9][0-9]*$ ]]
[[ "$SOURCE_COMMIT" =~ ^[0-9a-f]{40}$ ]]
test "$GITHUB_REF" = refs/heads/main
test "$GITHUB_REPOSITORY" = sigil-plugins/temporal
test "$GITHUB_RUN_ATTEMPT" = 1
test "$GITHUB_SHA" = "$SOURCE_COMMIT"
test "$(git rev-parse HEAD)" = "$SOURCE_COMMIT"
run_json="$(mktemp)"
gh api "repos/${GITHUB_REPOSITORY}/actions/runs/${CANDIDATE_RUN_ID}" > "$run_json"
jq -e --arg repository "$GITHUB_REPOSITORY" --arg commit "$SOURCE_COMMIT" '
.repository.full_name == $repository and
.path == ".github/workflows/prepare-release.yml" and
.event == "workflow_dispatch" and
.head_branch == "main" and
.head_sha == $commit and
.status == "completed" and
.conclusion == "success" and
.run_attempt == 1
' "$run_json" >/dev/null
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: release-candidate-assets
path: dist
github-token: ${{ github.token }}
run-id: ${{ inputs.candidate_run_id }}
- name: Verify exact approved unpublished bytes
id: release
env:
VERSION: ${{ inputs.version }}
SOURCE_COMMIT: ${{ inputs.source_commit }}
PACKAGE_SHA256: ${{ inputs.package_sha256 }}
CHECKSUMS_SHA256: ${{ inputs.checksums_sha256 }}
RELEASE_MANIFEST_SHA256: ${{ inputs.release_manifest_sha256 }}
run: |
set -euo pipefail
[[ "$VERSION" =~ ^0\.1\.0(-rc\.[1-9][0-9]*)?$ ]]
[[ "$SOURCE_COMMIT" =~ ^[0-9a-f]{40}$ ]]
[[ "$PACKAGE_SHA256" =~ ^[0-9a-f]{64}$ ]]
[[ "$CHECKSUMS_SHA256" =~ ^[0-9a-f]{64}$ ]]
[[ "$RELEASE_MANIFEST_SHA256" =~ ^[0-9a-f]{64}$ ]]
name="$(sed -n 's/^name = "\([^"]*\)"$/\1/p' plugin.toml)"
source="$(sed -n 's/^source = "\([^"]*\)"$/\1/p' plugin.toml)"
test "$source" = "github:${GITHUB_REPOSITORY}"
test "$(sed -n 's/^version = "\([^"]*\)"$/\1/p' plugin.toml)" = "$VERSION"
package="${name}-${VERSION}.sigil-plugin.tar.zst"
test -f "dist/$package" && test ! -L "dist/$package"
test -f dist/SHA256SUMS && test ! -L dist/SHA256SUMS
test -f dist/release-manifest.json && test ! -L dist/release-manifest.json
test "$(find dist -mindepth 1 -maxdepth 1 -type f | wc -l)" -eq 3
test "$(find dist -mindepth 1 -maxdepth 1 | wc -l)" -eq 3
printf '%s %s\n' "$PACKAGE_SHA256" "dist/$package" | sha256sum --check --strict
printf '%s %s\n' "$CHECKSUMS_SHA256" dist/SHA256SUMS | sha256sum --check --strict
printf '%s %s\n' "$RELEASE_MANIFEST_SHA256" dist/release-manifest.json | sha256sum --check --strict
test "$(cat dist/SHA256SUMS)" = "$PACKAGE_SHA256 $package"
python3 -I - "$name" "$VERSION" "$SOURCE_COMMIT" "$source" "$package" "$PACKAGE_SHA256" <<'PY'
import json
from pathlib import Path
import re
import sys

name, version, commit, source, package, package_sha256 = sys.argv[1:]
path = Path("dist/release-manifest.json")
raw = path.read_bytes()
value = json.loads(raw)
assert set(value) == {
"schema_version", "source", "source_commit", "name", "version",
"asset_name", "package_sha256", "package_blake3",
"manifest_blake3", "component_blake3",
}
assert value["schema_version"] == 1
assert value["source"] == source
assert value["source_commit"] == commit
assert value["name"] == name
assert value["version"] == version
assert value["asset_name"] == package
assert value["package_sha256"] == f"sha256:{package_sha256}"
for field in ("package_blake3", "manifest_blake3", "component_blake3"):
assert re.fullmatch(r"blake3:[0-9a-f]{64}", value[field])
canonical = json.dumps(
value, allow_nan=False, ensure_ascii=True, sort_keys=True,
separators=(",", ":"),
).encode("ascii")
assert raw == canonical
PY
echo "package_name=$package" >> "$GITHUB_OUTPUT"
echo "subject_digest=sha256:$PACKAGE_SHA256" >> "$GITHUB_OUTPUT"
- name: Stage and read back the exact draft
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ inputs.version }}
SOURCE_COMMIT: ${{ inputs.source_commit }}
PACKAGE_SHA256: ${{ inputs.package_sha256 }}
CHECKSUMS_SHA256: ${{ inputs.checksums_sha256 }}
RELEASE_MANIFEST_SHA256: ${{ inputs.release_manifest_sha256 }}
PACKAGE_NAME: ${{ steps.release.outputs.package_name }}
run: |
set -euo pipefail
tag="v${VERSION}"
prerelease=false
if [[ "$VERSION" == *-rc.* ]]; then prerelease=true; fi
if git ls-remote --exit-code --tags origin "refs/tags/${tag}" >/dev/null 2>&1; then
echo "existing tag burns ${tag}; refusing publication" >&2
exit 1
fi
if gh release view "$tag" >/dev/null 2>&1; then
echo "existing release burns ${tag}; refusing publication" >&2
exit 1
fi
status="$(curl --proto '=https' --tlsv1.2 --silent --show-error \
--output "${RUNNER_TEMP}/preexisting-attestations.json" --write-out '%{http_code}' \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${GH_TOKEN}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"https://api.github.com/repos/${GITHUB_REPOSITORY}/attestations/sha256:${PACKAGE_SHA256}?predicate_type=provenance&per_page=30")"
test "$status" = 404
gh release create "$tag" \
--target "$SOURCE_COMMIT" \
--title "${PACKAGE_NAME%.sigil-plugin.tar.zst}" \
--notes "Immutable keyless-provenance Sigil plugin ${VERSION}." \
--draft --prerelease="$prerelease" \
"dist/$PACKAGE_NAME" dist/SHA256SUMS dist/release-manifest.json
release_json="$(mktemp)"
gh release view "$tag" --json isDraft,isPrerelease,targetCommitish,assets > "$release_json"
jq -e --arg commit "$SOURCE_COMMIT" --arg package "$PACKAGE_NAME" --argjson prerelease "$prerelease" '
.isDraft == true and .isPrerelease == $prerelease and .targetCommitish == $commit and
([.assets[].name] | sort) == (["SHA256SUMS", "release-manifest.json", $package] | sort)
' "$release_json" >/dev/null
readback="$(mktemp -d)"
gh release download "$tag" --dir "$readback"
test "$(find "$readback" -mindepth 1 -maxdepth 1 -type f | wc -l)" -eq 3
test "$(find "$readback" -mindepth 1 -maxdepth 1 | wc -l)" -eq 3
test "$(sha256sum "$readback/$PACKAGE_NAME" | cut -d' ' -f1)" = "$PACKAGE_SHA256"
test "$(sha256sum "$readback/SHA256SUMS" | cut -d' ' -f1)" = "$CHECKSUMS_SHA256"
test "$(sha256sum "$readback/release-manifest.json" | cut -d' ' -f1)" = "$RELEASE_MANIFEST_SHA256"
test "$(cat "$readback/SHA256SUMS")" = "$PACKAGE_SHA256 $PACKAGE_NAME"
- name: Create GitHub OIDC build provenance for the exact package
id: attest
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
with:
subject-name: ${{ steps.release.outputs.package_name }}
subject-digest: ${{ steps.release.outputs.subject_digest }}
show-summary: false
- name: Publish once and verify immutable public state
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ inputs.version }}
SOURCE_COMMIT: ${{ inputs.source_commit }}
PACKAGE_SHA256: ${{ inputs.package_sha256 }}
CHECKSUMS_SHA256: ${{ inputs.checksums_sha256 }}
RELEASE_MANIFEST_SHA256: ${{ inputs.release_manifest_sha256 }}
PACKAGE_NAME: ${{ steps.release.outputs.package_name }}
ATTESTATION_ID: ${{ steps.attest.outputs.attestation-id }}
BUNDLE_PATH: ${{ steps.attest.outputs.bundle-path }}
run: |
set -euo pipefail
test -n "$ATTESTATION_ID"
test -f "$BUNDLE_PATH" && test ! -L "$BUNDLE_PATH"
attestation_found=false
for _ in 1 2 3 4 5 6; do
status="$(curl --proto '=https' --tlsv1.2 --silent --show-error \
--output "${RUNNER_TEMP}/attestations.json" --write-out '%{http_code}' \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${GH_TOKEN}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"https://api.github.com/repos/${GITHUB_REPOSITORY}/attestations/sha256:${PACKAGE_SHA256}?predicate_type=provenance&per_page=30")"
if test "$status" = 200 && jq -e --argjson repository_id "$GITHUB_REPOSITORY_ID" '
(.attestations | length) == 1 and
.attestations[0].repository_id == $repository_id and
.attestations[0].initiator == "user"
' "${RUNNER_TEMP}/attestations.json" >/dev/null; then
attestation_found=true
break
fi
sleep 5
done
test "$attestation_found" = true
tag="v${VERSION}"
test "$(gh release view "$tag" --json isDraft,targetCommitish --jq '.isDraft')" = true
test "$(gh release view "$tag" --json isDraft,targetCommitish --jq '.targetCommitish')" = "$SOURCE_COMMIT"
if git ls-remote --exit-code --tags origin "refs/tags/${tag}" >/dev/null 2>&1; then
echo "tag appeared after preflight; ${tag} is burned" >&2
exit 1
fi
gh api --method POST "repos/${GITHUB_REPOSITORY}/git/refs" \
-f ref="refs/tags/${tag}" -f sha="$SOURCE_COMMIT" >/dev/null
git fetch --force origin "refs/tags/$tag:refs/tags/$tag"
test "$(git rev-list -n 1 "$tag")" = "$SOURCE_COMMIT"
gh release edit "$tag" --draft=false --latest=false
immutable=false
for _ in 1 2 3 4 5 6; do
if test "$(gh api "repos/${GITHUB_REPOSITORY}/releases/tags/${tag}" --jq '.immutable')" = true; then
immutable=true
break
fi
sleep 5
done
test "$immutable" = true
test "$(gh release view "$tag" --json isDraft --jq '.isDraft')" = false
prerelease=false
if [[ "$VERSION" == *-rc.* ]]; then prerelease=true; fi
test "$(gh release view "$tag" --json isPrerelease --jq '.isPrerelease')" = "$prerelease"
readback="$(mktemp -d)"
gh release download "$tag" --dir "$readback"
test "$(find "$readback" -mindepth 1 -maxdepth 1 -type f | wc -l)" -eq 3
test "$(find "$readback" -mindepth 1 -maxdepth 1 | wc -l)" -eq 3
test "$(sha256sum "$readback/$PACKAGE_NAME" | cut -d' ' -f1)" = "$PACKAGE_SHA256"
test "$(sha256sum "$readback/SHA256SUMS" | cut -d' ' -f1)" = "$CHECKSUMS_SHA256"
test "$(sha256sum "$readback/release-manifest.json" | cut -d' ' -f1)" = "$RELEASE_MANIFEST_SHA256"
5 changes: 4 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,7 @@ Preserve exact payload bytes, numeric/time values, enum numbers and error/effect
classification. Host infrastructure faults remain sticky in Sigil.

Local checks are not official provenance, a stable Sigil release, or CAPI acceptance.
Do not weaken requirements or create an installable release without those gates.
Do not weaken these requirements. An independently approved, officially
provenanced RC requiring a supporting stable Sigil release may be published to
enable normal locked CAPI acceptance. Stable Temporal promotion additionally
requires that CAPI acceptance; the RC and local harness do not substitute for it.
Loading