Skip to content
Merged
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
263 changes: 167 additions & 96 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ on:
paths:
- build.zig.zon
workflow_dispatch:
inputs:
force_release:
description: Force the detected version to release (used for recovery)
required: false
type: boolean
default: false

permissions:
contents: read
Expand Down Expand Up @@ -49,8 +55,14 @@ jobs:
previous_file=previous-build.zig.zon
fi

metadata=$(mktemp)
sh .forgejo/scripts/release-ci.sh detect-version-change build.zig.zon "$previous_file" \
>> "$GITHUB_OUTPUT"
> "$metadata"
cat "$metadata" >> "$GITHUB_OUTPUT"
if [ "${{ github.event_name }}" = "workflow_dispatch" ] && \
[ "${{ inputs.force_release }}" = "true" ]; then
printf 'version_changed=true\n' >> "$GITHUB_OUTPUT"
fi

changelog=$(sh .forgejo/scripts/release-ci.sh changelog)
{
Expand Down Expand Up @@ -95,7 +107,7 @@ jobs:
cd "$GITHUB_WORKSPACE"
zig build test --summary all

release-x86_64:
create-release:
needs: [detect, test]
if: needs.detect.outputs.version_changed == 'true'
runs-on: ubuntu-24.04
Expand All @@ -105,8 +117,6 @@ jobs:
contents: write
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
outputs:
checksum: ${{ steps.build.outputs.checksum }}
steps:
- name: Git checkout
run: |
Expand All @@ -118,23 +128,6 @@ jobs:
git fetch --depth=50 --tags origin "$GITHUB_SHA"
git checkout --detach FETCH_HEAD

- name: Build and package
id: build
run: |
set -eu
cd "$GITHUB_WORKSPACE"

version="${{ needs.detect.outputs.version }}"
arch=$(uname -m)
base=$(sh .forgejo/scripts/release-ci.sh asset-base "$version" linux "$arch")

zig build -Doptimize=ReleaseSmall
mkdir -p dist
cp zig-out/bin/mere "dist/${base}"

checksum=$(cd dist && sha256sum "$base")
printf 'checksum=%s\n' "$checksum" >> "$GITHUB_OUTPUT"

- name: Push release tag
run: |
set -eu
Expand All @@ -146,13 +139,12 @@ jobs:
tag="${{ needs.detect.outputs.tag }}"
if git ls-remote --exit-code --tags origin "refs/tags/${tag}" >/dev/null 2>&1; then
printf 'Tag %s already exists on origin.\n' "$tag"
exit 0
else
git tag -a "$tag" -m "${{ needs.detect.outputs.release_name }}" "$GITHUB_SHA"
git push origin "refs/tags/${tag}"
fi

git tag -a "$tag" -m "${{ needs.detect.outputs.release_name }}" "$GITHUB_SHA"
git push origin "refs/tags/${tag}"

- name: Create release and upload assets
- name: Create draft release
env:
CHANGELOG: ${{ needs.detect.outputs.changelog }}
TAG: ${{ needs.detect.outputs.tag }}
Expand All @@ -165,7 +157,6 @@ jobs:
api_base="$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/releases"
auth_header="Authorization: Bearer $GH_TOKEN"
tag="$TAG"
version="$VERSION"

release_status=$(
curl -sS \
Expand All @@ -178,17 +169,25 @@ jobs:

case "$release_status" in
200)
printf 'Release %s already exists; reusing it.\n' "$tag"
immutable=$(python3 -c 'import json; print(json.load(open("release.json")).get("immutable", False))')
draft=$(python3 -c 'import json; print(json.load(open("release.json"))["draft"])')
if [ "$immutable" = "True" ] || [ "$draft" != "True" ]; then
printf 'Release %s already exists and cannot be reused safely (immutable=%s draft=%s).\n' "$tag" "$immutable" "$draft" >&2
exit 1
fi
printf 'Draft release %s already exists; reusing it.\n' "$tag"
;;
404)
python3 .forgejo/scripts/release-body.py > release-create.json
python3 -c 'import json; from pathlib import Path; path = Path("release-create.json"); body = json.loads(path.read_text()); body["draft"] = True; path.write_text(json.dumps(body))'
curl -fsS \
-X POST \
-H "$auth_header" \
-H "Accept: application/vnd.github+json" \
-H "Content-Type: application/json" \
--data-binary @release-create.json \
"$api_base" > release.json
printf 'Created draft release %s.\n' "$tag"
;;
*)
printf 'Failed to query release %s (HTTP %s).\n' "$tag" "$release_status" >&2
Expand All @@ -197,47 +196,87 @@ jobs:
;;
esac

release_id=$(python3 -c 'import json; print(json.load(open("release.json"))["id"])')
release-x86_64:
needs: [detect, test, create-release]
if: needs.detect.outputs.version_changed == 'true'
runs-on: ubuntu-24.04
container:
image: mere/ci:v0.15.0
permissions:
contents: write
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
outputs:
checksum: ${{ steps.build.outputs.checksum }}
steps:
- name: Git checkout
run: |
set -eu
git init "$GITHUB_WORKSPACE"
git config --global --add safe.directory "$GITHUB_WORKSPACE"
cd "$GITHUB_WORKSPACE"
git remote add origin "https://x-access-token:${GH_TOKEN}@${GITHUB_SERVER_URL#https://}/$GITHUB_REPOSITORY.git"
git fetch --depth=50 --tags origin "$GITHUB_SHA"
git checkout --detach FETCH_HEAD

- name: Build and package
id: build
run: |
set -eu
cd "$GITHUB_WORKSPACE"

version="${{ needs.detect.outputs.version }}"
arch=$(uname -m)
base=$(sh .forgejo/scripts/release-ci.sh asset-base "$version" linux "$arch")

zig build -Doptimize=ReleaseSmall
mkdir -p dist
cp zig-out/bin/mere "dist/${base}"

checksum=$(cd dist && sha256sum "$base")
printf 'checksum=%s\n' "$checksum" >> "$GITHUB_OUTPUT"

- name: Upload x86_64 asset to draft release
run: |
set -eu
cd "$GITHUB_WORKSPACE"

api_base="$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/releases"
auth_header="Authorization: Bearer $GH_TOKEN"
tag="${{ needs.detect.outputs.tag }}"
curl -fsS \
-H "$auth_header" \
-H "Accept: application/vnd.github+json" \
"$api_base/tags/$tag" > release.json

upload_url=$(python3 -c 'import json; print(json.load(open("release.json"))["upload_url"].split("{")[0])')
[ -n "$release_id" ] && [ -n "$upload_url" ] || {
printf '%s\n' "Failed to parse release upload information from API response." >&2
cat release.json >&2
exit 1
}

for asset in dist/*; do
name=$(basename "$asset")
status=$(
curl -sS \
-o upload.json \
-w '%{http_code}' \
-X POST \
-H "$auth_header" \
-H "Accept: application/vnd.github+json" \
-H "Content-Type: application/octet-stream" \
--data-binary "@$asset" \
"$upload_url?name=$name&label=$name"
)

case "$status" in
201)
printf 'Uploaded %s\n' "$name"
;;
422)
printf 'Asset %s may already exist; refusing to replace it.\n' "$name" >&2
cat upload.json >&2
exit 1
;;
*)
printf 'Failed to upload %s (HTTP %s).\n' "$name" "$status" >&2
cat upload.json >&2
exit 1
;;
esac
done
asset="dist/$(basename dist/*)"
name=$(basename "$asset")
existing=$(python3 -c 'import json,sys; print(any(a["name"] == sys.argv[1] for a in json.load(open("release.json"))["assets"]))' "$name")
if [ "$existing" = "True" ]; then
printf 'Asset %s already exists in draft release; keeping it.\n' "$name"
exit 0
fi

status=$(
curl -sS \
-o upload.json \
-w '%{http_code}' \
-X POST \
-H "$auth_header" \
-H "Accept: application/vnd.github+json" \
-H "Content-Type: application/octet-stream" \
--data-binary "@$asset" \
"$upload_url?name=$name&label=$name"
)

case "$status" in
201) printf 'Uploaded %s\n' "$name" ;;
*) printf 'Failed to upload %s (HTTP %s).\n' "$name" "$status" >&2; cat upload.json >&2; exit 1 ;;
esac

release-aarch64:
needs: [detect, test, release-x86_64]
needs: [detect, test, create-release]
if: needs.detect.outputs.version_changed == 'true'
runs-on: ubuntu-24.04-arm
container:
Expand Down Expand Up @@ -276,7 +315,7 @@ jobs:
checksum=$(cd dist && sha256sum "$base")
printf 'checksum=%s\n' "$checksum" >> "$GITHUB_OUTPUT"

- name: Upload asset
- name: Upload aarch64 asset to draft release
run: |
set -eu
cd "$GITHUB_WORKSPACE"
Expand All @@ -289,10 +328,15 @@ jobs:
-H "Accept: application/vnd.github+json" \
"$api_base/tags/$tag" > release.json

release_id=$(python3 -c 'import json; print(json.load(open("release.json"))["id"])')
upload_url=$(python3 -c 'import json; print(json.load(open("release.json"))["upload_url"].split("{")[0])')
asset="dist/$(basename dist/*)"
name=$(basename "$asset")
existing=$(python3 -c 'import json,sys; print(any(a["name"] == sys.argv[1] for a in json.load(open("release.json"))["assets"]))' "$name")
if [ "$existing" = "True" ]; then
printf 'Asset %s already exists in draft release; keeping it.\n' "$name"
exit 0
fi

status=$(
curl -sS \
-o upload.json \
Expand All @@ -306,23 +350,12 @@ jobs:
)

case "$status" in
201)
printf 'Uploaded %s\n' "$name"
;;
422)
printf 'Asset %s may already exist; refusing to replace it.\n' "$name" >&2
cat upload.json >&2
exit 1
;;
*)
printf 'Failed to upload %s (HTTP %s).\n' "$name" "$status" >&2
cat upload.json >&2
exit 1
;;
201) printf 'Uploaded %s\n' "$name" ;;
*) printf 'Failed to upload %s (HTTP %s).\n' "$name" "$status" >&2; cat upload.json >&2; exit 1 ;;
esac

checksums:
needs: [detect, release-x86_64, release-aarch64]
needs: [detect, create-release, release-x86_64, release-aarch64]
if: needs.detect.outputs.version_changed == 'true'
runs-on: ubuntu-24.04-arm
container:
Expand All @@ -332,7 +365,7 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Upload SHA256SUMS
- name: Upload SHA256SUMS to draft release
env:
X86_CHECKSUM: ${{ needs.release-x86_64.outputs.checksum }}
AARCH64_CHECKSUM: ${{ needs.release-aarch64.outputs.checksum }}
Expand All @@ -350,6 +383,12 @@ jobs:
"$api_base/tags/$tag" > release.json

upload_url=$(python3 -c 'import json; print(json.load(open("release.json"))["upload_url"].split("{")[0])')
existing=$(python3 -c 'import json; print(any(a["name"] == "SHA256SUMS" for a in json.load(open("release.json"))["assets"]))')
if [ "$existing" = "True" ]; then
printf '%s\n' 'SHA256SUMS already exists in draft release; keeping it.'
exit 0
fi

status=$(
curl -sS \
-o upload.json \
Expand All @@ -363,17 +402,49 @@ jobs:
)

case "$status" in
201)
printf 'Uploaded SHA256SUMS\n'
;;
422)
printf '%s\n' 'SHA256SUMS may already exist; refusing to replace it.' >&2
cat upload.json >&2
exit 1
;;
*)
printf 'Failed to upload SHA256SUMS (HTTP %s).\n' "$status" >&2
cat upload.json >&2
exit 1
;;
201) printf '%s\n' 'Uploaded SHA256SUMS' ;;
*) printf 'Failed to upload SHA256SUMS (HTTP %s).\n' "$status" >&2; cat upload.json >&2; exit 1 ;;
esac

publish:
needs: [detect, checksums]
if: needs.detect.outputs.version_changed == 'true'
runs-on: ubuntu-24.04-arm
container:
image: mere/ci:v0.15.0
permissions:
contents: write
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Publish complete release
run: |
set -eu

api_base="$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/releases"
auth_header="Authorization: Bearer $GH_TOKEN"
tag="${{ needs.detect.outputs.tag }}"
curl -fsS \
-H "$auth_header" \
-H "Accept: application/vnd.github+json" \
"$api_base/tags/$tag" > release.json

immutable=$(python3 -c 'import json; print(json.load(open("release.json")).get("immutable", False))')
draft=$(python3 -c 'import json; print(json.load(open("release.json"))["draft"])')
if [ "$immutable" = "True" ]; then
printf 'Release %s is already immutable; refusing to modify it.\n' "$tag" >&2
exit 1
fi
if [ "$draft" != "True" ]; then
printf 'Release %s is already published.\n' "$tag"
exit 0
fi

curl -fsS \
-X PATCH \
-H "$auth_header" \
-H "Accept: application/vnd.github+json" \
-H "Content-Type: application/json" \
--data '{"draft":false}' \
"$api_base/$(python3 -c 'import json; print(json.load(open("release.json"))["id"])')" >/dev/null
printf 'Published complete release %s.\n' "$tag"