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
2 changes: 1 addition & 1 deletion .github/workflows/attest-install-scripts.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Attest Install Scripts
name: Finalize Install Script Release

on:
workflow_dispatch:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/install-scripts.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Publish Install Scripts
name: Start Install Script Release

on:
workflow_dispatch:
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/publish-release.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Publish Release
name: Start App Release

on:
workflow_dispatch:
Expand Down Expand Up @@ -54,7 +54,7 @@ jobs:
shell: bash
run: |
set -euo pipefail
test '${{ github.ref }}' = 'refs/heads/main' || { echo "::error::Publish Release must be dispatched from main."; exit 1; }
test '${{ github.ref }}' = 'refs/heads/main' || { echo "::error::Start App Release must be dispatched from main."; exit 1; }
RELEASE_STATE_REF=$(git ls-remote --heads origin release-state)
if [ -n "$RELEASE_STATE_REF" ]; then
git fetch origin release-state
Expand Down Expand Up @@ -161,4 +161,4 @@ jobs:
echo "- **Tag:** \`${TAG}\`" >> "$GITHUB_STEP_SUMMARY"
echo "- **CI run:** \`${CI_RUN_ID}\`" >> "$GITHUB_STEP_SUMMARY"
echo "- **Commit:** \`${CI_HEAD_SHA}\`" >> "$GITHUB_STEP_SUMMARY"
echo "- If dispatch failed, run **Release** manually on \`${TAG}\` with CI run \`${CI_RUN_ID}\`." >> "$GITHUB_STEP_SUMMARY"
echo "- If dispatch failed, run **Finalize App Release** manually on \`${TAG}\` with CI run \`${CI_RUN_ID}\`." >> "$GITHUB_STEP_SUMMARY"
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Release
name: Finalize App Release

on:
workflow_dispatch:
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -670,10 +670,10 @@ The release system is split into narrowly scoped workflows:
- `pr.yml` validates scripts, restore/build/test behavior, NativeAOT, and packaged runtime behavior.
- `ci.yml` runs on main pushes or manual dispatch, calculates versions, publishes six development and six promotable archives, creates a versioned development prerelease, and advances `release-state`.
- `bump-version.yml` moves the release state between `pre`, `rc`, and `rtm`.
- `publish-release.yml` validates a successful `main` CI run, creates its annotated version tag, and dispatches promotion.
- `release.yml` promotes the exact prebuilt bundle without rebuilding, requires production approval, signs every Windows executable payload, attests final archives, publishes generated release notes, and advances release state.
- `install-scripts.yml` signs and snapshots both installers to the protected `install-scripts` branch.
- `attest-install-scripts.yml` attests the immutable installer snapshot and publishes its non-latest release.
- **Start App Release** (`publish-release.yml`) is the normal manual entry point for an app release. It validates a successful `main` CI run, creates its annotated version tag, and dispatches finalization.
- **Finalize App Release** (`release.yml`) runs automatically after **Start App Release**. It promotes the exact prebuilt bundle without rebuilding, requires production approval, signs every Windows executable payload, attests final archives, publishes generated release notes, and advances release state. Run it manually only to recover a failed dispatch after the release tag was created.
- **Start Install Script Release** (`install-scripts.yml`) is the normal manual entry point for publishing the installers. It signs and snapshots both installers to the protected `install-scripts` branch.
- **Finalize Install Script Release** (`attest-install-scripts.yml`) runs automatically after **Start Install Script Release**. It attests the immutable installer snapshot and publishes its non-latest release. Run it manually only to recover a failed dispatch on the generated snapshot tag.
- `releases-cleanup.yml` retains a configurable number of development and installer snapshots.

Mutable version state lives in `version-state.json` on the workflow-managed `release-state` branch. Release-state writers share one concurrency group.
Expand All @@ -687,9 +687,9 @@ verification procedures.
1. Open a pull request and let `pr.yml` validate restore/build/test behavior.
2. Merge to `main`, which lets `ci.yml` calculate versions, publish native assets, create a versioned development prerelease, and update `release-state`.
3. When you want to move between `pre`, `rc`, or `rtm`, run `bump-version.yml`.
4. Run `publish-release.yml` for the successful CI run to tag and promote its already-built official bundle.
4. Run **Start App Release** (`publish-release.yml`) for the successful CI run to tag and promote its already-built official bundle.
5. Approve the `production` deployment. The release workflow signs, attests, and publishes the exact tagged bundle.
6. Run `install-scripts.yml` when installer source changes, then approve its signed immutable snapshot.
6. Run **Start Install Script Release** (`install-scripts.yml`) when installer source changes, then approve its signed immutable snapshot.

## Native release asset layout

Expand Down
33 changes: 21 additions & 12 deletions docs/release-and-provenance.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,10 @@ discarding pending runs.

### Promotion dispatcher

`.github/workflows/publish-release.yml` is the maintainer entry point for an
official release. It has its own `publish-release` concurrency group because it
dispatches `release.yml`; putting the parent and child runs in the same group
**Start App Release** (`.github/workflows/publish-release.yml`) is the
maintainer entry point for an official release. It has its own
`publish-release` concurrency group because it dispatches **Finalize App
Release** (`release.yml`); putting the parent and child runs in the same group
can strand the child run behind its completed parent.

Before the production approval gate, it verifies that the selected CI run:
Expand All @@ -125,8 +126,9 @@ dispatches `release.yml` on that exact tag.

### Signing and release publication

`.github/workflows/release.yml` validates the tag and CI run again, downloads
the promotable bundle, and performs the official publication:
**Finalize App Release** (`.github/workflows/release.yml`) validates the tag and
CI run again, downloads the promotable bundle, and performs the official
publication:

1. The `production` environment gates the Windows signing job.
2. Azure Artifact Signing signs every Windows executable payload:
Expand Down Expand Up @@ -366,27 +368,34 @@ Then merge a change or dispatch `ci.yml` to produce artifacts from that state.
### Publish an official release

1. Identify the successful `ci.yml` run to promote.
2. Dispatch `publish-release.yml` on `main` with that run ID.
2. Manually run **Start App Release** (`publish-release.yml`) on `main` with
that run ID.
3. Approve the production tag-publication deployment.
4. Confirm the annotated tag points to the CI source SHA.
5. Approve the production signing deployment in `release.yml`.
5. Approve the production signing deployment in **Finalize App Release**.
6. Confirm the release, attestations, and release-state advancement.

If tag dispatch fails after the tag is pushed, dispatch `release.yml` manually
on the existing tag with the original CI run ID and phase. Do not recreate the
tag.
**Finalize App Release** normally starts automatically. If tag dispatch fails
after the tag is pushed, run it manually on the existing tag with the original
CI run ID and phase. Do not recreate the tag.

Promote a CI run whose source commit contains the current workflow files.
GitHub correctly rejects a workflow token attempting to create a tag at an
older commit when that operation would introduce different workflow content.

### Publish installers

1. Dispatch `install-scripts.yml` on `main`.
1. Manually run **Start Install Script Release** (`install-scripts.yml`) on
`main`.
2. Approve signing and branch/tag publication.
3. Approve `attest-install-scripts.yml` for the generated snapshot tag.
3. Approve **Finalize Install Script Release** when it starts automatically for
the generated snapshot tag.
4. Verify the vanity and raw branch URLs return the same script bytes.

If the automatic dispatch fails after the snapshot tag is pushed, run
**Finalize Install Script Release** manually on that existing tag. Do not
recreate the tag.

### Clean old snapshots

`releases-cleanup.yml` runs weekly and can be dispatched manually. It keeps the
Expand Down