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
10 changes: 9 additions & 1 deletion .github/scripts/update_manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,11 @@ def set_available(entry: dict) -> dict:


def replace_entry(entries: list[dict], predicate, entry: dict) -> list[dict]:
return [item for item in entries if not predicate(item)] + [entry]
# Newest first: consumers (release channels in the update UI, and the
# migration target lookup) take the head of the list as the current entry.
# The unstable channel is re-sorted after this, so prepending is neutral
# there.
return [entry] + [item for item in entries if not predicate(item)]


def sort_unstable(entries: list[dict]) -> list[dict]:
Expand Down Expand Up @@ -140,6 +144,8 @@ def update_release(args: argparse.Namespace) -> None:
"source_sha": args.sha,
"version": args.version,
"store_path": args.store_path or None,
"migration_url": args.migration_url or None,
"migration_sha256_url": args.migration_sha256_url or None,
}
set_available(entry)
manifest["channels"][channel] = replace_entry(
Expand Down Expand Up @@ -177,6 +183,8 @@ def parser() -> argparse.ArgumentParser:
release.add_argument("--version", required=True)
release.add_argument("--release-type", choices=("stable", "beta"), required=True)
release.add_argument("--store-path", required=True)
release.add_argument("--migration-url")
release.add_argument("--migration-sha256-url")
release.add_argument("--title")
release.add_argument("--notes")
release.set_defaults(func=update_release)
Expand Down
72 changes: 54 additions & 18 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,18 @@ jobs:
timeout-minutes: 90
outputs:
store_path: ${{ steps.push.outputs.store_path }}
source_sha: ${{ steps.sha.outputs.source_sha }}
steps:
- uses: actions/checkout@v4
with:
repository: ${{ inputs.source_repository || github.repository }}
ref: ${{ inputs.source_branch }}
persist-credentials: false

- name: Record source SHA
id: sha
run: echo "source_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"

- name: Ensure nix is on PATH (self-hosted runner)
run: |
echo "/nix/var/nix/profiles/default/bin" >> "$GITHUB_PATH"
Expand Down Expand Up @@ -122,13 +127,18 @@ jobs:
timeout-minutes: 120
outputs:
store_path: ${{ steps.push.outputs.store_path }}
source_sha: ${{ steps.sha.outputs.source_sha }}
steps:
- uses: actions/checkout@v4
with:
repository: ${{ inputs.source_repository || github.repository }}
ref: ${{ inputs.source_branch }}
persist-credentials: false

- name: Record source SHA
id: sha
run: echo "source_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"

- uses: DeterminateSystems/nix-installer-action@main
with:
determinate: false
Expand Down Expand Up @@ -179,20 +189,29 @@ jobs:
permissions:
contents: write
steps:
# Same-repo only: needed to tag the source commit and to run the TRUSTED
# manifest scripts. Skipped for fork builds (those steps are skipped too).
- name: Checkout source
if: (inputs.source_repository || github.repository) == github.repository
# Base repo (this repo, not the fork source): supplies the TRUSTED manifest
# scripts and the `origin` the manifest branch is pushed to. Keeps default
# credentials for that push.
- name: Checkout base repo
uses: actions/checkout@v4

# Download native and hosted images to separate dirs. If both builds ran
# (native slow → fallback fired) their images share a filename, so merging
# them would corrupt the archive — never merge; the assemble step prefers
# native.
- name: Download native SD image
continue-on-error: true
uses: actions/download-artifact@v4
with:
ref: ${{ inputs.source_branch }}
name: sdimage-native
path: img-native

- name: Download built SD image
- name: Download hosted SD image
continue-on-error: true
uses: actions/download-artifact@v4
with:
pattern: sdimage-*
merge-multiple: true
path: result-sd/sd-image
name: sdimage-hosted
path: img-hosted

- name: Compute tag
run: |
Expand All @@ -203,7 +222,15 @@ jobs:
- name: Assemble image + migration tarball + checksums
run: |
set -euo pipefail
IMAGE_SRC=$(find result-sd/sd-image -type f -name '*.img.zst' | head -1)
# Only one dir may exist (downloads are continue-on-error); create both
# so find can't fail under pipefail. Prefer the native image (listed
# first); fall back to hosted.
mkdir -p img-native img-hosted
IMAGE_SRC=$(find img-native img-hosted -type f -name '*.img.zst' -print -quit)
if [ -z "$IMAGE_SRC" ]; then
echo "No SD image artifact found from either builder" >&2
exit 1
fi
mkdir -p release
cp "$IMAGE_SRC" "release/pifinder-${TAG}.img.zst"

Expand Down Expand Up @@ -246,10 +273,15 @@ jobs:
# `origin` we can't push to with GITHUB_TOKEN; the GitHub Release below
# still publishes the artifacts to this repo.
if: (inputs.source_repository || github.repository) == github.repository
env:
SOURCE_SHA: ${{ needs.build-native.outputs.source_sha || needs.build-hosted.outputs.source_sha }}
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag "$TAG"
# The base checkout is at the workflow's ref, not the built source —
# fetch and tag the commit the build job actually checked out.
git fetch --depth=1 origin "$SOURCE_SHA"
git tag "$TAG" "$SOURCE_SHA"
git push origin "$TAG"

- name: Create GitHub Release
Expand All @@ -264,29 +296,33 @@ jobs:
release/pifinder-*.sha256

- name: Update generated manifest branch
# Same-repo only: publish_manifest.sh pushes the nixos-manifest branch to
# `origin`, and depends on the source tag above. Skipped when building a
# fork; update the manifest once the source lands in this repo.
if: (inputs.source_repository || github.repository) == github.repository
# Records this release in the metadata-only nixos-manifest branch so
# devices discover it (upgrade uses store_path; migration uses the tarball
# URL). Pushes to this repo's origin via the base checkout above, so it
# runs for fork-source builds too. source_sha is the built source commit;
# the tarball URL points at this repo's release assets.
env:
STORE_PATH: ${{ needs.build-native.outputs.store_path || needs.build-hosted.outputs.store_path }}
SOURCE_SHA: ${{ needs.build-native.outputs.source_sha || needs.build-hosted.outputs.source_sha }}
RELEASE_NOTES: ${{ inputs.notes }}
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

SOURCE_SHA="$(git rev-parse "$TAG^{commit}")"
MIGRATION_URL="https://github.com/${{ github.repository }}/releases/download/${TAG}/pifinder-migration-${TAG}.tar.zst"

.github/scripts/publish_manifest.sh \
bash .github/scripts/publish_manifest.sh \
"chore: update release manifest [skip ci]" \
python3 .github/scripts/update_manifest.py release \
--manifest @MANIFEST@ \
--repository "${{ github.repository }}" \
--repository "${{ inputs.source_repository || github.repository }}" \
--sha "$SOURCE_SHA" \
--tag "$TAG" \
--version "${{ inputs.version }}" \
--release-type "${{ inputs.type }}" \
--store-path "$STORE_PATH" \
--migration-url "$MIGRATION_URL" \
--migration-sha256-url "${MIGRATION_URL}.sha256" \
--title "PiFinder $TAG" \
--notes "$RELEASE_NOTES"
Loading