The Gitea registry stays authoritative; GHCR becomes a copy of it, the way the GitHub repository is a copy of the Gitea one. After the tag build has pushed the release image to the registry, a new ghcr job copies it to ghcr.io under the same version tag and :latest with `imagetools create` -- a copy, not a rebuild, so the digest on GHCR is the digest on the registry. Anything still pulling the old ghcr.io name, including the TrueNAS app submission, keeps receiving releases. The job uses the run's own token and is left out of the status reported to Gitea, so a GHCR problem cannot fail a release.
467 lines
23 KiB
YAML
467 lines
23 KiB
YAML
# CI and publishing on GitHub, for the repository Gitea mirrors here.
|
|
#
|
|
# Gitea (git.coffeylabs.org) is where this project lives: pull requests,
|
|
# issues, releases and the container registry are all there, and it pushes
|
|
# every branch and tag to this GitHub copy as it changes. GitHub's hosted
|
|
# runners are faster than the self-hosted ones -- and have native arm64 -- so
|
|
# the building happens here, and the answer goes back to Gitea as a commit
|
|
# status that Gitea's own ci.yml / publish.yml wait on.
|
|
#
|
|
# One switch decides which side builds: the Actions variable BUILD_ON, set on
|
|
# both forges. BUILD_ON=github runs every job below and turns Gitea's heavy
|
|
# jobs into a wait for this one; anything else leaves Gitea building exactly
|
|
# as before and every job here skips. If GitHub is ever unavailable, unset it
|
|
# on Gitea and nothing else has to change.
|
|
#
|
|
# Needs, as organization settings rather than anything in this file:
|
|
# variables BUILD_ON=github, REGISTRY (the Gitea container registry),
|
|
# GITEA_URL (the Gitea base URL)
|
|
# secret GITEA_TOKEN -- jcoffey-dev, write:repository + write:package:
|
|
# commit statuses, the release and its assets, the registry push
|
|
#
|
|
# There is no pull_request trigger: pull requests happen on Gitea, and their
|
|
# branch arrives here as an ordinary push. Branch pushes get what Gitea's
|
|
# ci.yml checks; v* tags get what its publish.yml does. Schedules (the weekly
|
|
# release, the upstream watch) and the release announcement stay on Gitea.
|
|
#
|
|
# Every `uses:` is pinned to a full commit SHA with the release in the
|
|
# trailing comment. A tag is a mutable pointer; do not "simplify" a pin back
|
|
# to one. Only GitHub's own actions and the three docker/* ones are used.
|
|
name: ci
|
|
|
|
on:
|
|
push:
|
|
branches: ['**']
|
|
tags: ['**']
|
|
workflow_dispatch:
|
|
|
|
# A newer push to a branch cancels the run for the older one, whose answer is
|
|
# about code nobody is looking at any more. A tag run is never cancelled: it
|
|
# publishes.
|
|
concurrency:
|
|
group: ci-${{ github.ref }}
|
|
cancel-in-progress: ${{ github.ref_type == 'branch' }}
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
env:
|
|
GITEA_URL: ${{ vars.GITEA_URL }}
|
|
# The Gitea status this run answers for. Gitea waits on the one matching
|
|
# its own event: "(branch)" from ci.yml, "(tag)" from publish.yml.
|
|
STATUS_CONTEXT: github/ci (${{ github.ref_type }})
|
|
|
|
jobs:
|
|
# Tells Gitea a run has started, so a pull request shows it as pending
|
|
# rather than missing while the build is still going.
|
|
start:
|
|
if: ${{ vars.BUILD_ON == 'github' }}
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- env:
|
|
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
|
run: |
|
|
jq -n --arg c "$STATUS_CONTEXT" \
|
|
--arg u "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" \
|
|
'{state:"pending", context:$c, target_url:$u, description:"GitHub Actions"}' |
|
|
curl -fsS -o /dev/null -X POST -H "Authorization: token $GITEA_TOKEN" \
|
|
-H 'Content-Type: application/json' --data @- \
|
|
"$GITEA_URL/api/v1/repos/$GITHUB_REPOSITORY/statuses/$GITHUB_SHA"
|
|
|
|
# ----------------------------------------------------------- branches ------
|
|
# What an upstream merge can bring in or leave behind without a conflict:
|
|
# the upstream name in a new string literal, and a changed upstream file
|
|
# without the AGPL 5(a) notice. Seconds, and needs no toolchain. The notice
|
|
# check diffs against the upstream snapshot in the history, hence the full
|
|
# fetch.
|
|
fork-checks:
|
|
if: ${{ vars.BUILD_ON == 'github' && github.ref_type == 'branch' }}
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
fetch-depth: 0
|
|
- run: python3 tools/fork/name-check.py
|
|
- if: always()
|
|
run: python3 tools/fork/notice-check.py
|
|
# Cargo can patch a dependency to a directory in this repository, and
|
|
# the image builds from a context .dockerignore prunes to almost
|
|
# nothing. CI never sees the difference; a release does.
|
|
- if: always()
|
|
run: python3 tools/fork/context-check.py
|
|
# The personal-data catalog must classify every object and field the
|
|
# schema has, and name nothing that is gone.
|
|
- if: always()
|
|
run: python3 tools/fork/privacy-check.py
|
|
# The admin reads each expression field's allowed values and variables
|
|
# from the schema; they're generated from the registry and must match it.
|
|
- if: always()
|
|
run: python3 tools/fork/expr-schema.py --check
|
|
- if: always()
|
|
run: python3 -m unittest discover -s tools/fork/tests
|
|
|
|
# The build, and that every test target compiles. The suites are not run:
|
|
# they need a store, fixed ports and containers (docs/spec/
|
|
# container-tests.md), and are run by hand.
|
|
build:
|
|
if: ${{ vars.BUILD_ON == 'github' && github.ref_type == 'branch' }}
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
CARGO_INCREMENTAL: "0"
|
|
# Debug info is most of a dev target dir, and nothing here runs a
|
|
# debugger. Without it the dev and test builds fit the runner's disk and
|
|
# the cache below stays small enough to be worth restoring.
|
|
CARGO_PROFILE_DEV_DEBUG: "0"
|
|
CARGO_PROFILE_TEST_DEBUG: "0"
|
|
steps:
|
|
# The hosted image carries toolchains this build never touches; a dev,
|
|
# test and release build of RocksDB and the workspace needs the room.
|
|
- run: |
|
|
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache/CodeQL
|
|
df -h /
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
# Current stable, as Gitea's rust:1 image is.
|
|
- id: rust
|
|
run: |
|
|
rustup toolchain install stable --profile minimal
|
|
rustup default stable
|
|
echo "version=$(rustc -V | cut -d' ' -f2)" >> "$GITHUB_OUTPUT"
|
|
- run: sudo apt-get update -qq && sudo apt-get install -y -qq --no-install-recommends clang >/dev/null
|
|
# Cargo's download cache and the dev/test target dir, keyed on the
|
|
# lockfile and the compiler. Saved from main only, so the one cache
|
|
# every branch restores is main's, and branches cannot evict it.
|
|
- uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: |
|
|
~/.cargo/registry/index
|
|
~/.cargo/registry/cache
|
|
~/.cargo/git/db
|
|
target/debug
|
|
key: cargo-${{ steps.rust.outputs.version }}-${{ hashFiles('Cargo.lock') }}
|
|
restore-keys: cargo-${{ steps.rust.outputs.version }}-
|
|
- run: cargo build -p inbuxa --locked
|
|
# --no-run: compiles every test target without running them, which
|
|
# catches a test that no longer builds without needing a store.
|
|
- run: cargo test --workspace --locked --no-run
|
|
- if: github.ref == 'refs/heads/main'
|
|
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: |
|
|
~/.cargo/registry/index
|
|
~/.cargo/registry/cache
|
|
~/.cargo/git/db
|
|
target/debug
|
|
key: cargo-${{ steps.rust.outputs.version }}-${{ hashFiles('Cargo.lock') }}
|
|
# The release profile, on main only. It is the profile the image is
|
|
# built with, and it fails in ways the dev profile does not: v2026.9.24
|
|
# was tagged on a commit whose CI was green and whose release build
|
|
# could not compile the scim crate at all.
|
|
- if: github.ref == 'refs/heads/main'
|
|
run: cargo build -p inbuxa --locked --release
|
|
|
|
# --------------------------------------------------------------- tags ------
|
|
# Two guards before anything is pushed, the same as Gitea's publish.yml:
|
|
# * the tag must be v<brand_version!>. The version is a string in
|
|
# crates/types/src/branding.rs, not Cargo.toml, and the image is tagged
|
|
# with it, so a tag beside an unbumped macro would publish an image that
|
|
# reports a different version from its tag.
|
|
# * the tag must be on main or on a release/* branch, so an image never
|
|
# describes code that was never reviewed onto one of them. A release/*
|
|
# branch carries a hotfix cut from an earlier release tag.
|
|
version:
|
|
if: ${{ vars.BUILD_ON == 'github' && github.ref_type == 'tag' && startsWith(github.ref_name, 'v') }}
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
version: ${{ steps.v.outputs.version }}
|
|
steps:
|
|
# Full history, and every branch as origin/*: the ancestry check cannot
|
|
# be answered from a shallow clone.
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
fetch-depth: 0
|
|
- id: v
|
|
env:
|
|
TAG: ${{ github.ref_name }}
|
|
run: |
|
|
set -euo pipefail
|
|
# Scoped to the macro body: branding.rs holds other string literals,
|
|
# and tagging an image from one of those would be worse than failing.
|
|
V="$(awk '/macro_rules! brand_version /,/^}/' crates/types/src/branding.rs \
|
|
| grep -om1 '"[0-9][^"]*"' | tr -d '"')"
|
|
[ -n "$V" ] || { echo "could not read brand_version! from branding.rs" >&2; exit 1; }
|
|
if [ "$TAG" != "v$V" ]; then
|
|
echo "Tag $TAG names a commit whose brand_version! says $V." >&2
|
|
echo "Refusing to publish an image that would report the wrong version." >&2
|
|
exit 1
|
|
fi
|
|
commit="$(git rev-parse "${TAG}^{commit}")"
|
|
on=""
|
|
for ref in origin/main $(git for-each-ref --format='%(refname:short)' 'refs/remotes/origin/release/*'); do
|
|
if git merge-base --is-ancestor "$commit" "$ref"; then on="$ref"; break; fi
|
|
done
|
|
[ -n "$on" ] || { echo "$TAG is not on main or a release/* branch" >&2; exit 1; }
|
|
echo "$TAG is on $on"
|
|
echo "version=$V" >> "$GITHUB_OUTPUT"
|
|
|
|
# Each architecture on its own native runner, side by side. The Dockerfile
|
|
# cross-compiles from the build platform, and on the self-hosted runners one
|
|
# machine built both one after the other; here two machines build at once,
|
|
# each natively (the builder stage picks the matching target, and the
|
|
# aarch64 toolchain it installs exists on arm64 too), and the small final
|
|
# stage needs no QEMU. amd64 also moves :<version> as soon as it is done, so
|
|
# a production deploy can start from it; :latest waits for the index below,
|
|
# so it never names an image without arm64.
|
|
publish:
|
|
needs: [version]
|
|
runs-on: ${{ matrix.runner }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- arch: amd64
|
|
runner: ubuntu-latest
|
|
- arch: arm64
|
|
runner: ubuntu-24.04-arm
|
|
env:
|
|
VERSION: ${{ needs.version.outputs.version }}
|
|
steps:
|
|
- run: |
|
|
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache/CodeQL
|
|
echo "IMAGE=${{ vars.REGISTRY }}/${GITHUB_REPOSITORY,,}" >> "$GITHUB_ENV"
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
- uses: docker/setup-buildx-action@594f3bf4285d9ea8dc53c9a0c9c4092420091003 # v4.4.0
|
|
- uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
|
with:
|
|
registry: ${{ vars.REGISTRY }}
|
|
username: jcoffey-dev
|
|
password: ${{ secrets.GITEA_TOKEN }}
|
|
# Attestations off: they add manifests of their own, and the index
|
|
# should hold the two images and nothing else. No build cache: GitHub
|
|
# scopes a tag run's cache to that tag, so the next release could never
|
|
# read it, and each one would park several GB in the repository's 10 GB
|
|
# cache and evict main's cargo cache.
|
|
- uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
|
|
with:
|
|
context: .
|
|
platforms: linux/${{ matrix.arch }}
|
|
provenance: false
|
|
sbom: false
|
|
push: true
|
|
tags: |
|
|
${{ env.IMAGE }}:${{ env.VERSION }}-${{ matrix.arch }}
|
|
${{ matrix.arch == 'amd64' && format('{0}:{1}', env.IMAGE, env.VERSION) || '' }}
|
|
|
|
# Joins the two per-architecture tags into :<version> and :latest. Built
|
|
# from the per-architecture tags rather than :<version>, which by now is
|
|
# the amd64 image and would be read as such.
|
|
index:
|
|
needs: [version, publish]
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
VERSION: ${{ needs.version.outputs.version }}
|
|
steps:
|
|
- run: echo "IMAGE=${{ vars.REGISTRY }}/${GITHUB_REPOSITORY,,}" >> "$GITHUB_ENV"
|
|
- uses: docker/setup-buildx-action@594f3bf4285d9ea8dc53c9a0c9c4092420091003 # v4.4.0
|
|
- uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
|
with:
|
|
registry: ${{ vars.REGISTRY }}
|
|
username: jcoffey-dev
|
|
password: ${{ secrets.GITEA_TOKEN }}
|
|
- run: |
|
|
docker buildx imagetools create \
|
|
--tag "$IMAGE:$VERSION" \
|
|
--tag "$IMAGE:latest" \
|
|
"$IMAGE:$VERSION-amd64" "$IMAGE:$VERSION-arm64"
|
|
docker buildx imagetools inspect "$IMAGE:$VERSION"
|
|
# Gitea keeps a container package on its owner; linking it shows it on
|
|
# the repository's Packages tab. Idempotent.
|
|
- env:
|
|
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
|
run: |
|
|
owner="${GITHUB_REPOSITORY%%/*}"; name="${GITHUB_REPOSITORY#*/}"
|
|
curl -fsS -o /dev/null -X POST -H "Authorization: token $GITEA_TOKEN" \
|
|
"$GITEA_URL/api/v1/packages/${owner,,}/container/$name/-/link/$name" \
|
|
|| echo "package already linked (or link refused); not fatal"
|
|
|
|
# The weekly release creates its Release (and so the tag) on Gitea first; a
|
|
# tag pushed by hand has none. Either way the tag ends up with exactly one
|
|
# Release there, created once the image exists so its pull instructions
|
|
# work.
|
|
release:
|
|
needs: [version, index]
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- env:
|
|
TAG: ${{ github.ref_name }}
|
|
VERSION: ${{ needs.version.outputs.version }}
|
|
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
|
REGISTRY: ${{ vars.REGISTRY }}
|
|
run: |
|
|
set -euo pipefail
|
|
api="$GITEA_URL/api/v1/repos/$GITHUB_REPOSITORY"
|
|
code="$(curl -sS -o /dev/null -w '%{http_code}' -H "Authorization: token $GITEA_TOKEN" "$api/releases/tags/$TAG")"
|
|
if [ "$code" = 200 ]; then echo "$TAG already has a release"; exit 0; fi
|
|
[ "$code" = 404 ] || { echo "looking up the release for $TAG answered $code" >&2; exit 1; }
|
|
image="$REGISTRY/${GITHUB_REPOSITORY,,}:$VERSION"
|
|
body="Container image: \`$image\` (linux/amd64, linux/arm64); also \`:latest\`.
|
|
|
|
Binaries for a host install are attached: \`inbuxa-linux-amd64.tar.gz\` and \`inbuxa-linux-arm64.tar.gz\`, with \`SHA256SUMS\`. Each is the binary out of this release's image for that architecture, so it is the same build. The image grants it \`cap_net_bind_service\`; a host install has to grant that itself (\`setcap\`, or \`AmbientCapabilities\` in the unit) to bind port 25."
|
|
jq -n --arg tag "$TAG" --arg name "INBUXA $VERSION" --arg body "$body" \
|
|
'{tag_name:$tag, name:$name, body:$body}' |
|
|
curl -fsS -X POST -H "Authorization: token $GITEA_TOKEN" -H 'Content-Type: application/json' \
|
|
--data @- "$api/releases" | jq -r '"created release " + .tag_name'
|
|
|
|
# The binaries for a host install, taken out of the image that was just
|
|
# pushed rather than compiled again: the binary in the tarball is the file
|
|
# the image runs. `docker create` starts nothing, so copying a file out of
|
|
# the arm64 image on an amd64 runner needs no emulation.
|
|
binaries:
|
|
needs: [version, index, release]
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
VERSION: ${{ needs.version.outputs.version }}
|
|
TAG: ${{ github.ref_name }}
|
|
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
|
steps:
|
|
- run: echo "IMAGE=${{ vars.REGISTRY }}/${GITHUB_REPOSITORY,,}" >> "$GITHUB_ENV"
|
|
- uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
|
with:
|
|
registry: ${{ vars.REGISTRY }}
|
|
username: jcoffey-dev
|
|
password: ${{ secrets.GITEA_TOKEN }}
|
|
- name: take the binaries out of the image
|
|
run: |
|
|
set -euo pipefail
|
|
mkdir -p out && cd out
|
|
for arch in amd64 arm64; do
|
|
docker pull -q --platform "linux/$arch" "$IMAGE:$VERSION"
|
|
id="$(docker create --platform "linux/$arch" "$IMAGE:$VERSION")"
|
|
docker cp "$id:/usr/local/bin/inbuxa" inbuxa
|
|
docker rm -f "$id" >/dev/null
|
|
chmod 0755 inbuxa
|
|
tar -czf "inbuxa-linux-$arch.tar.gz" inbuxa
|
|
rm inbuxa
|
|
done
|
|
sha256sum inbuxa-linux-*.tar.gz > SHA256SUMS
|
|
cat SHA256SUMS
|
|
# A re-run of a tag replaces its assets rather than leaving two files
|
|
# with the same name and different contents.
|
|
- name: attach them to the release
|
|
run: |
|
|
set -euo pipefail
|
|
api="$GITEA_URL/api/v1/repos/$GITHUB_REPOSITORY"
|
|
auth="Authorization: token $GITEA_TOKEN"
|
|
rel="$(curl -fsS -H "$auth" "$api/releases/tags/$TAG" | jq -r .id)"
|
|
assets="$(curl -fsS -H "$auth" "$api/releases/$rel/assets")"
|
|
for f in out/inbuxa-linux-amd64.tar.gz out/inbuxa-linux-arm64.tar.gz out/SHA256SUMS; do
|
|
name="$(basename "$f")"
|
|
old="$(jq -r --arg n "$name" '.[] | select(.name == $n) | .id' <<<"$assets")"
|
|
for id in $old; do curl -fsS -o /dev/null -X DELETE -H "$auth" "$api/releases/$rel/assets/$id"; done
|
|
curl -fsS -o /dev/null -X POST -H "$auth" -F "attachment=@$f" "$api/releases/$rel/assets?name=$name"
|
|
echo "attached $name"
|
|
done
|
|
|
|
# ------------------------------------------------------ ghcr replica ------
|
|
# Copies the release image from the Gitea registry, which stays the
|
|
# authoritative one, to ghcr.io under the same version tag and :latest. It is
|
|
# a copy, not a second build: the digest on GHCR is the digest on the
|
|
# registry, so `docker pull ghcr.io/...` gets exactly the same image. Left
|
|
# out of the report to Gitea, like the release copy, so a GHCR problem
|
|
# cannot fail a release.
|
|
ghcr:
|
|
if: ${{ vars.BUILD_ON == 'github' && github.ref_type == 'tag' }}
|
|
needs: [version, index]
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
packages: write
|
|
steps:
|
|
- env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
TAG: ${{ needs.version.outputs.version }}
|
|
run: |
|
|
set -euo pipefail
|
|
src="${{ vars.REGISTRY }}/${GITHUB_REPOSITORY,,}"
|
|
dst="ghcr.io/${GITHUB_REPOSITORY,,}"
|
|
tag="$TAG"
|
|
echo "$GH_TOKEN" | docker login ghcr.io -u "$GITHUB_ACTOR" --password-stdin
|
|
docker buildx imagetools create -t "$dst:$tag" -t "$dst:latest" "$src:$tag"
|
|
want="$(docker buildx imagetools inspect "$src:$tag" --format '{{json .Manifest.Digest}}')"
|
|
got="$(docker buildx imagetools inspect "$dst:$tag" --format '{{json .Manifest.Digest}}')"
|
|
echo "registry $src:$tag = $want"
|
|
echo "ghcr $dst:$tag = $got"
|
|
[ "$want" = "$got" ] || echo "::warning::GHCR digest differs from the registry's"
|
|
docker logout ghcr.io
|
|
|
|
# ---------------------------------------------------- github release ------
|
|
# Copies this tag's Gitea release -- notes and files -- to a GitHub release,
|
|
# so the replica's Releases page, and anyone watching it, keeps up. Gitea's
|
|
# release is the real one; this is left out of the report to Gitea, so a
|
|
# failure here cannot fail a release. PR and issue numbers in the notes are
|
|
# rewritten to Gitea links: on GitHub a bare #16 is some other PR.
|
|
github-release:
|
|
if: ${{ vars.BUILD_ON == 'github' && github.ref_type == 'tag' }}
|
|
needs: [binaries]
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: write
|
|
env:
|
|
GITEA_URL: ${{ vars.GITEA_URL }}
|
|
GH_TOKEN: ${{ github.token }}
|
|
TAG: ${{ github.ref_name }}
|
|
steps:
|
|
- run: |
|
|
set -euo pipefail
|
|
if gh release view "$TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
|
|
echo "GitHub already has a release for $TAG"; exit 0
|
|
fi
|
|
# The Gitea release exists by now if this run made it; if the weekly
|
|
# release job made it, it came before the tag. Allow a few minutes.
|
|
code=0
|
|
for _ in $(seq 1 15); do
|
|
code="$(curl -sS -o rel.json -w '%{http_code}' "$GITEA_URL/api/v1/repos/$GITHUB_REPOSITORY/releases/tags/$TAG")"
|
|
[ "$code" = 200 ] && break
|
|
sleep 20
|
|
done
|
|
if [ "$code" != 200 ]; then echo "No Gitea release for $TAG; nothing to copy"; exit 0; fi
|
|
if [ "$(jq -r .draft rel.json)" = true ]; then echo "The Gitea release is a draft; not copying"; exit 0; fi
|
|
export BASE="$(jq -r '.html_url | sub("/releases/tag/.*$"; "")' rel.json)"
|
|
jq -r '.body // ""' rel.json | perl -pe 's{(?<![\w/&\[])#(\d+)\b}{[#$1]($ENV{BASE}/pulls/$1)}g' > notes.md
|
|
printf '\n\n_Mirrored from [the Gitea release](%s); report issues on [Gitea](%s/issues)._\n' \
|
|
"$(jq -r .html_url rel.json)" "$BASE" >> notes.md
|
|
files=()
|
|
mkdir -p files
|
|
while IFS=$'\t' read -r name url; do
|
|
curl -fsSL -o "files/$name" "$url"; files+=("files/$name")
|
|
done < <(jq -r '.assets[]? | [.name, .browser_download_url] | @tsv' rel.json)
|
|
title="$(jq -r '.name // ""' rel.json)"; [ -n "$title" ] || title="$TAG"
|
|
if [ "$(jq -r .prerelease rel.json)" = true ]; then kind=--prerelease; else kind=--latest; fi
|
|
gh release create "$TAG" --repo "$GITHUB_REPOSITORY" --verify-tag --title "$title" \
|
|
--notes-file notes.md "$kind" "${files[@]}"
|
|
echo "created the GitHub release for $TAG with ${#files[@]} file(s)"
|
|
|
|
# ------------------------------------------------------------- report ------
|
|
# One commit status on Gitea for the whole run: what Gitea's ci.yml and
|
|
# publish.yml wait on. Skipped jobs (the tag jobs on a branch, and the other
|
|
# way round) count as passing; a failed or cancelled one does not.
|
|
report:
|
|
if: ${{ always() && vars.BUILD_ON == 'github' }}
|
|
needs: [start, fork-checks, build, version, publish, index, release, binaries]
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- env:
|
|
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
|
STATE: ${{ contains(needs.*.result, 'failure') && 'failure' || (contains(needs.*.result, 'cancelled') && 'cancelled' || 'success') }}
|
|
run: |
|
|
# A cancelled run was superseded by a newer run for the same commit (the
|
|
# mirror can push one commit twice); that run reports. Posting "failure"
|
|
# here would fail the Gitea check while the real build is still going.
|
|
if [ "$STATE" = cancelled ]; then echo "cancelled: leaving the result to the newer run"; exit 0; fi
|
|
jq -n --arg s "$STATE" --arg c "$STATUS_CONTEXT" \
|
|
--arg u "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" \
|
|
'{state:$s, context:$c, target_url:$u, description:"GitHub Actions"}' |
|
|
curl -fsS -o /dev/null -X POST -H "Authorization: token $GITEA_TOKEN" \
|
|
-H 'Content-Type: application/json' --data @- \
|
|
"$GITEA_URL/api/v1/repos/$GITHUB_REPOSITORY/statuses/$GITHUB_SHA"
|
|
echo "$STATUS_CONTEXT: $STATE"
|