All checks were successful
PR Checks / rust-gates (pull_request) Successful in 1m0s
The "Commit version bump and push tag" step had two problems, and the first hid the second. It has never once executed. An empty template expression written literally in one of its comments makes the runner fail to build the step's script, and a step it cannot build is skipped WITHOUT failing the job. That is why sidecar/Cargo.toml still says 0.1.0 after six releases, and why the tag-reuse handling added in #27 was dead on arrival. The tags exist because Gitea's release API creates one when it publishes -- the pipeline has been working by accident. And had it executed, it would have been declined: main is protected, so the push is rejected by the pre-receive hook. The installer's bundle job hit exactly that today. A release must not depend on a write to a protected branch. So the tag is the version, as it already is in servuo-plugins, whose release workflow was written this way on purpose and has never needed a protection exception. The workflow still writes the real version into Cargo.toml before building, so a released binary self-reports correctly; what it no longer does is commit that edit back. Nothing downstream reads the file -- the next version is computed from the newest tag. The comment is reworded so the step can actually run, and warns against writing that token in a comment again. No literal occurrence is left in this file. Co-Authored-By: Claude <noreply@anthropic.com>
401 lines
22 KiB
YAML
401 lines
22 KiB
YAML
# Automated build + release for the uo-link Rust sidecar.
|
|
#
|
|
# Trigger: every push to `main` (i.e. every merged PR).
|
|
#
|
|
# Flow (two conceptual halves, kept separate on purpose):
|
|
#
|
|
# ┌── RELEASE ENGINE (language-agnostic) ─────────────────────────────┐
|
|
# │ reads: latest v* git tag + conventional-commit subjects │
|
|
# │ produces: next version, changelog, and (at the end) the release │
|
|
# └───────────────────────────────────────────────────────────────────┘
|
|
# ┌── RUST ADAPTER (the only Rust-specific part) ─────────────────────┐
|
|
# │ consumes: the version │
|
|
# │ produces: the artifacts (linux bin, windows exe, SHA256SUMS) │
|
|
# └───────────────────────────────────────────────────────────────────┘
|
|
#
|
|
# To retarget this engine at a C#/Node/Docker/static project later, only the
|
|
# "Rust adapter" steps change — the plan + release steps consume just
|
|
# {version, changelog, artifacts} and know nothing about Rust.
|
|
#
|
|
# Version bump (conventional commits since the last v* tag):
|
|
# feat!: / BREAKING CHANGE -> major feat: -> minor fix|perf: -> patch
|
|
# nothing releasable -> no release is cut
|
|
# (first ever run, no tag) -> releases the current Cargo.toml version as-is
|
|
#
|
|
# Prerequisites (Settings → Actions → Secrets on RunicGateway/link):
|
|
# REGISTRY_USER — Gitea username the token below belongs to
|
|
# REGISTRY_TOKEN — Gitea access token. For image builds it needed
|
|
# write:package; THIS workflow additionally needs
|
|
# `write:repository` so it can push the bump commit + tag
|
|
# and create the release. Grant that scope to the token.
|
|
# The final step also dispatches RunicGateway/installer's
|
|
# bundle workflow, so the token ideally has write there too
|
|
# — but that is a nicety, not a requirement: without it the
|
|
# step warns and the installer's nightly cron picks the
|
|
# release up instead.
|
|
# Also: `main` must accept a direct push from that user (disable branch
|
|
# protection for it, or add it as an exception) — the bump commit lands on main.
|
|
#
|
|
# The bump commit carries `[skip ci]`, so it does not re-trigger this workflow.
|
|
|
|
name: Release sidecar
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
workflow_dispatch: {}
|
|
|
|
concurrency:
|
|
group: release-sidecar
|
|
cancel-in-progress: false
|
|
|
|
env:
|
|
GITEA_HOST: gitea.whitlocktech.com
|
|
REPO: RunicGateway/link
|
|
WORKDIR: sidecar
|
|
BIN: uo-link-sidecar
|
|
LINUX_TARGET: x86_64-unknown-linux-gnu
|
|
WINDOWS_TARGET: x86_64-pc-windows-gnu
|
|
# Ampere/Graviton instances and Pi-class boxes are a realistic ServUO home,
|
|
# and the shard dials the sidecar out on loopback — so wherever the shard
|
|
# runs, this binary has to run too (installer PLAN.md §5.2).
|
|
ARM64_TARGET: aarch64-unknown-linux-gnu
|
|
# Notified after a release so the installer's compat matrix picks up this
|
|
# version immediately rather than at its next nightly run (PLAN.md §7.2).
|
|
INSTALLER_REPO: RunicGateway/installer
|
|
|
|
jobs:
|
|
release:
|
|
runs-on: ubuntu-latest
|
|
# Don't loop on our own bump commit (belt-and-suspenders with [skip ci]).
|
|
# Quoted because the expression contains a colon (`chore(release):`), which an
|
|
# unquoted YAML scalar would misparse as a mapping value.
|
|
if: "${{ !contains(github.event.head_commit.message, 'chore(release): bump version') }}"
|
|
steps:
|
|
- name: Check out full history (need tags + commit log for the bump)
|
|
uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
# ── RELEASE ENGINE: decide the next version + changelog ──────────────
|
|
- name: Plan the release (version + changelog)
|
|
id: plan
|
|
env:
|
|
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
mkdir -p dist
|
|
git fetch --tags --force >/dev/null 2>&1 || true
|
|
|
|
CARGO_VERSION="$(grep -m1 '^version' "${WORKDIR}/Cargo.toml" | sed -E 's/.*"([^"]+)".*/\1/')"
|
|
LAST_TAG="$(git describe --tags --match 'v*' --abbrev=0 2>/dev/null || true)"
|
|
if [ -n "$LAST_TAG" ]; then RANGE="${LAST_TAG}..HEAD"; else RANGE="HEAD"; fi
|
|
|
|
SUBJECTS="$(git log --no-merges --format='%s' $RANGE || true)"
|
|
BODIES="$(git log --no-merges --format='%B' $RANGE || true)"
|
|
|
|
BUMP=none
|
|
if echo "$BODIES" | grep -qE 'BREAKING[ -]CHANGE' ; then BUMP=major; fi
|
|
if echo "$SUBJECTS" | grep -qE '^[a-z]+(\([^)]+\))?!:' ; then BUMP=major; fi
|
|
if [ "$BUMP" = none ] && echo "$SUBJECTS" | grep -qE '^feat(\([^)]+\))?:' ; then BUMP=minor; fi
|
|
if [ "$BUMP" = none ] && echo "$SUBJECTS" | grep -qE '^(fix|perf)(\([^)]+\))?:'; then BUMP=patch; fi
|
|
|
|
bump() { # <x.y.z> <major|minor|patch> -> bumped
|
|
IFS=. read -r MA MI PA <<< "$1"
|
|
case "$2" in
|
|
major) echo "$((MA+1)).0.0" ;;
|
|
minor) echo "${MA}.$((MI+1)).0" ;;
|
|
patch) echo "${MA}.${MI}.$((PA+1))" ;;
|
|
esac
|
|
}
|
|
|
|
RELEASE=true
|
|
if [ -z "$LAST_TAG" ]; then
|
|
VERSION="$CARGO_VERSION" # first release: ship what's in Cargo.toml
|
|
elif [ "$BUMP" = none ]; then
|
|
RELEASE=false # no feat/fix/breaking since last tag
|
|
VERSION="${LAST_TAG#v}"
|
|
else
|
|
VERSION="$(bump "${LAST_TAG#v}" "$BUMP")"
|
|
fi
|
|
|
|
# An existing tag is NOT automatically "nothing to do". A tag with no
|
|
# release behind it means a previous run tagged and then died before
|
|
# publishing — which is exactly what happened on servuo-plugins' first
|
|
# release, where absent REGISTRY_* secrets took the release API call to
|
|
# 401 after the tag had already been pushed. Standing down on the tag
|
|
# alone makes that state permanent: every later run sees the tag, sets
|
|
# RELEASE=false, and the release never appears. Note this deliberately
|
|
# OVERRIDES the RELEASE=false decided just above — with the tag in
|
|
# place there are no releasable commits after it, so the normal path
|
|
# would stand down, which is exactly why it could never self-heal.
|
|
REUSE_TAG=false
|
|
if git rev-parse -q --verify "refs/tags/v${VERSION}" >/dev/null; then
|
|
CI_TOKEN="$(printf '%s' "${REGISTRY_TOKEN:-}" | tr -d '\r\n')"
|
|
REL_HTTP="$(curl -s -o /dev/null -w '%{http_code}' -H "Authorization: token ${CI_TOKEN}" "https://${GITEA_HOST}/api/v1/repos/${REPO}/releases/tags/v${VERSION}" || echo 000)"
|
|
if [ "$REL_HTTP" = "200" ]; then
|
|
echo "Tag v${VERSION} already has a release — nothing to do."
|
|
RELEASE=false
|
|
elif [ "$REL_HTTP" = "404" ]; then
|
|
echo "::warning::Tag v${VERSION} exists but has no release — a previous run failed after tagging. Reusing the tag and publishing the release it is missing."
|
|
REUSE_TAG=true
|
|
RELEASE=true
|
|
else
|
|
# Anything else (000 from a network failure, 401/403 from a bad
|
|
# token) is not evidence of absence. Guessing "no release" would
|
|
# re-publish over a good one, so refuse instead.
|
|
echo "::error::Could not determine whether a release exists for v${VERSION} (HTTP ${REL_HTTP}). Refusing to guess."
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# Changelog range. A recovery run has nothing after the tag, so
|
|
# summarize what the tag itself contains rather than emitting an empty
|
|
# list: the range that produced it, i.e. previous-tag..this-tag.
|
|
if [ "$REUSE_TAG" = true ]; then
|
|
PREV_TAG="$(git describe --tags --match 'v*' --abbrev=0 "v${VERSION}^" 2>/dev/null || true)"
|
|
if [ -n "$PREV_TAG" ]; then CL_RANGE="${PREV_TAG}..v${VERSION}"; else CL_RANGE="v${VERSION}"; fi
|
|
SINCE="$PREV_TAG"
|
|
else
|
|
CL_RANGE="$RANGE"
|
|
SINCE="$LAST_TAG"
|
|
fi
|
|
CL_SUBJECTS="$(git log --no-merges --format='%s' $CL_RANGE || true)"
|
|
|
|
{
|
|
echo "## ${BIN} v${VERSION}"
|
|
echo
|
|
FEATS="$(echo "$CL_SUBJECTS" | grep -E '^feat' || true)"
|
|
FIXES="$(echo "$CL_SUBJECTS" | grep -E '^(fix|perf)' || true)"
|
|
[ -n "$FEATS" ] && { echo "### Features"; echo "$FEATS" | sed 's/^/- /'; echo; }
|
|
[ -n "$FIXES" ] && { echo "### Fixes"; echo "$FIXES" | sed 's/^/- /'; echo; }
|
|
echo "### All changes"
|
|
if [ -n "$SINCE" ]; then echo "Since ${SINCE}:"; fi
|
|
echo "$CL_SUBJECTS" | sed 's/^/- /'
|
|
} > dist/CHANGELOG.md
|
|
|
|
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
|
echo "tag=v${VERSION}" >> "$GITHUB_OUTPUT"
|
|
echo "release=${RELEASE}" >> "$GITHUB_OUTPUT"
|
|
echo "bump=${BUMP}" >> "$GITHUB_OUTPUT"
|
|
echo "==> release=${RELEASE} version=${VERSION} bump=${BUMP} last_tag=${LAST_TAG:-<none>}"
|
|
|
|
# ── RUST ADAPTER: toolchain + cross-compile deps ─────────────────────
|
|
- name: Install Rust toolchain, cross targets, and their linkers
|
|
if: ${{ steps.plan.outputs.release == 'true' }}
|
|
run: |
|
|
set -euo pipefail
|
|
SUDO=""; [ "$(id -u)" -ne 0 ] && SUDO="sudo"
|
|
$SUDO apt-get update
|
|
# The arm64 cross toolchain is not optional for this crate: sqlx's
|
|
# sqlite feature pulls libsqlite3-sys, which compiles bundled SQLite
|
|
# from C, so a Rust-only cross build fails at the first .c file.
|
|
#
|
|
# libc6-dev-arm64-cross is named explicitly because gcc-aarch64-linux-gnu
|
|
# only *recommends* it, and this install is --no-install-recommends: the
|
|
# compiler arrives without arm64 libc headers and SQLite's build dies on
|
|
# `bits/libc-header-start.h: No such file or directory`. Verified by
|
|
# reproducing both the failure and the fix in a rust:1-slim-bookworm
|
|
# container.
|
|
$SUDO apt-get install -y --no-install-recommends \
|
|
build-essential gcc-mingw-w64-x86-64 \
|
|
gcc-aarch64-linux-gnu libc6-dev-arm64-cross \
|
|
curl ca-certificates git jq
|
|
|
|
if ! command -v cargo >/dev/null 2>&1; then
|
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
|
|
| sh -s -- -y --profile minimal --default-toolchain stable
|
|
fi
|
|
echo "${HOME}/.cargo/bin" >> "$GITHUB_PATH"
|
|
export PATH="${HOME}/.cargo/bin:${PATH}"
|
|
rustup component add rustfmt
|
|
rustup target add "${WINDOWS_TARGET}"
|
|
rustup target add "${ARM64_TARGET}"
|
|
|
|
- name: Set the crate version to match the release
|
|
if: ${{ steps.plan.outputs.release == 'true' }}
|
|
run: |
|
|
set -euo pipefail
|
|
VERSION="${{ steps.plan.outputs.version }}"
|
|
# Replace only the [package] version (the first `version = "..."`).
|
|
sed -i -E "0,/^version = \"[^\"]+\"/s//version = \"${VERSION}\"/" "${WORKDIR}/Cargo.toml"
|
|
grep -m1 '^version' "${WORKDIR}/Cargo.toml"
|
|
# Bumping the manifest version desyncs this crate's own entry in
|
|
# Cargo.lock, which would make the `--locked` fmt/test/build steps below
|
|
# fail ("cannot update the lock file ... --locked was passed"). Sync just
|
|
# the workspace member(s) into the lock — dependency pins are untouched.
|
|
cargo update --manifest-path "${WORKDIR}/Cargo.toml" --workspace
|
|
|
|
# ── RUST ADAPTER: gates ──────────────────────────────────────────────
|
|
- name: cargo fmt --check
|
|
if: ${{ steps.plan.outputs.release == 'true' }}
|
|
working-directory: sidecar
|
|
run: cargo fmt --check
|
|
|
|
- name: cargo test
|
|
if: ${{ steps.plan.outputs.release == 'true' }}
|
|
working-directory: sidecar
|
|
run: cargo test --locked
|
|
|
|
# ── RUST ADAPTER: build both targets ─────────────────────────────────
|
|
- name: cargo build --release (Linux)
|
|
if: ${{ steps.plan.outputs.release == 'true' }}
|
|
working-directory: sidecar
|
|
run: cargo build --release --locked --target "${LINUX_TARGET}"
|
|
|
|
- name: cargo build --release (Windows, cross via MinGW)
|
|
if: ${{ steps.plan.outputs.release == 'true' }}
|
|
working-directory: sidecar
|
|
env:
|
|
CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER: x86_64-w64-mingw32-gcc
|
|
CC_x86_64_pc_windows_gnu: x86_64-w64-mingw32-gcc
|
|
AR_x86_64_pc_windows_gnu: x86_64-w64-mingw32-ar
|
|
run: cargo build --release --locked --target "${WINDOWS_TARGET}"
|
|
|
|
# Same shape as the Windows step: a linker for Rust's output and a CC/AR
|
|
# pair for the cc-crate build of bundled SQLite.
|
|
- name: cargo build --release (Linux arm64, cross via aarch64-linux-gnu)
|
|
if: ${{ steps.plan.outputs.release == 'true' }}
|
|
working-directory: sidecar
|
|
env:
|
|
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
|
|
CC_aarch64_unknown_linux_gnu: aarch64-linux-gnu-gcc
|
|
AR_aarch64_unknown_linux_gnu: aarch64-linux-gnu-ar
|
|
run: cargo build --release --locked --target "${ARM64_TARGET}"
|
|
|
|
# ── RUST ADAPTER: package artifacts (+ checksums) ────────────────────
|
|
- name: Package artifacts and SHA256SUMS
|
|
if: ${{ steps.plan.outputs.release == 'true' }}
|
|
run: |
|
|
set -euo pipefail
|
|
cp "${WORKDIR}/target/${LINUX_TARGET}/release/${BIN}" "dist/${BIN}-linux-x86_64"
|
|
cp "${WORKDIR}/target/${ARM64_TARGET}/release/${BIN}" "dist/${BIN}-linux-aarch64"
|
|
cp "${WORKDIR}/target/${WINDOWS_TARGET}/release/${BIN}.exe" "dist/${BIN}-windows-x86_64.exe"
|
|
# Every artifact must appear here: the installer verifies its download
|
|
# against these sums, and `sha256sum -c` passes silently over a file
|
|
# this list does not mention.
|
|
( cd dist && sha256sum "${BIN}-linux-x86_64" "${BIN}-linux-aarch64" \
|
|
"${BIN}-windows-x86_64.exe" > SHA256SUMS )
|
|
ls -l dist && echo "----" && cat dist/SHA256SUMS
|
|
|
|
# ── RELEASE ENGINE: commit the bump, tag, push ───────────────────────
|
|
# Tag only — `main` is never pushed to.
|
|
#
|
|
# This step used to commit the version bump back to main first. Two things
|
|
# were wrong with that. It has never once executed: an EMPTY template
|
|
# expression written literally in a comment (the `$`+`{{ }}` token, which
|
|
# is why it is spelled out here) made the runner fail to build the script
|
|
# and skip the whole step silently, which is why sidecar/Cargo.toml still
|
|
# says 0.1.0 after six releases (the tags exist because the release API
|
|
# creates one when it publishes). And had it executed, it would have been
|
|
# declined — main is protected, and a release must not depend on a write
|
|
# to a protected branch.
|
|
#
|
|
# So the tag is the version, as it already is in servuo-plugins. The
|
|
# workflow still writes the real version into Cargo.toml before building,
|
|
# so a released binary self-reports correctly; what it no longer does is
|
|
# commit that edit back. The next version is computed from the newest tag,
|
|
# never from Cargo.toml, so nothing downstream depends on the file.
|
|
- name: Push the release tag
|
|
if: ${{ steps.plan.outputs.release == 'true' }}
|
|
env:
|
|
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
|
|
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
TAG="${{ steps.plan.outputs.tag }}"
|
|
# Secrets can arrive with a trailing newline (depending on how they were
|
|
# pasted); a stray CR/LF corrupts the remote URL ("credential url cannot
|
|
# be parsed"). Strip line breaks before building the URL. They are passed
|
|
# via env rather than interpolated into this script, so a newline cannot
|
|
# break it — do NOT write a template token literally in a comment here,
|
|
# or the runner will skip this step without failing the job.
|
|
CI_USER="$(printf '%s' "${REGISTRY_USER}" | tr -d '\r\n')"
|
|
CI_TOKEN="$(printf '%s' "${REGISTRY_TOKEN}" | tr -d '\r\n')"
|
|
git config user.name "uo-link-ci"
|
|
git config user.email "ci@whitlocktech.com"
|
|
git remote set-url origin \
|
|
"https://${CI_USER}:${CI_TOKEN}@${GITEA_HOST}/${REPO}.git"
|
|
# The tag may already exist when finishing a run that died after tagging
|
|
# (see the plan step). `git tag` on an existing name fails under
|
|
# `set -e`; pushing an identical existing tag is a harmless no-op. A
|
|
# push that fails here means the remote tag points somewhere else,
|
|
# which SHOULD stop the run.
|
|
if git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null; then
|
|
echo "Tag ${TAG} already exists — reusing it."
|
|
else
|
|
git tag "${TAG}"
|
|
fi
|
|
git push origin "${TAG}"
|
|
|
|
# ── RELEASE ENGINE: create the Gitea release + upload assets ─────────
|
|
- name: Create Gitea release and upload assets
|
|
if: ${{ steps.plan.outputs.release == 'true' }}
|
|
env:
|
|
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
TAG="${{ steps.plan.outputs.tag }}"
|
|
API="https://${GITEA_HOST}/api/v1/repos/${REPO}"
|
|
BODY="$(cat dist/CHANGELOG.md)"
|
|
# Same newline hygiene as the push step: a stray CR/LF in the token would
|
|
# corrupt the Authorization header.
|
|
CI_TOKEN="$(printf '%s' "${REGISTRY_TOKEN}" | tr -d '\r\n')"
|
|
|
|
REL_ID="$(curl -sSf -X POST "${API}/releases" \
|
|
-H "Authorization: token ${CI_TOKEN}" \
|
|
-H "Content-Type: application/json" \
|
|
-d "$(jq -n --arg tag "$TAG" --arg body "$BODY" \
|
|
'{tag_name:$tag, name:$tag, body:$body, draft:false, prerelease:false}')" \
|
|
| jq -r '.id')"
|
|
echo "Created release ${TAG} (id=${REL_ID})"
|
|
|
|
for f in "${BIN}-linux-x86_64" "${BIN}-linux-aarch64" "${BIN}-windows-x86_64.exe" SHA256SUMS; do
|
|
curl -sSf -X POST "${API}/releases/${REL_ID}/assets?name=${f}" \
|
|
-H "Authorization: token ${CI_TOKEN}" \
|
|
-F "attachment=@dist/${f}" >/dev/null
|
|
echo " uploaded ${f}"
|
|
done
|
|
|
|
# ── Recompose the installer's bundle manifest ────────────────────────
|
|
# The installer does not resolve "latest" at run time — it installs the
|
|
# exact combination named by a published bundle (docs/installer/PLAN.md
|
|
# §7.1). So a sidecar release that nobody recomposes around is a release
|
|
# no operator will ever be offered. This step tells the installer repo to
|
|
# rebuild that manifest now, instead of leaving the new version invisible
|
|
# until its nightly cron.
|
|
#
|
|
# DISPATCH, DON'T WAIT (PLAN.md §7.3). Gitea's workflow-dispatch endpoint
|
|
# returns no run handle, so there is nothing to poll: a waiting step would
|
|
# have to guess which run is its own and hold a runner idle to do it. The
|
|
# bundle job runs its own gates regardless of who started it.
|
|
#
|
|
# A failure here is a WARNING, never a failure of this job. The release is
|
|
# already published and correct by this point; failing the run would
|
|
# misreport that. The installer's nightly cron recomposes from whatever
|
|
# the latest releases actually are, so a dropped dispatch self-heals — it
|
|
# costs latency, not correctness.
|
|
#
|
|
# `repository_dispatch` is deliberately not used: support for it is
|
|
# uncertain on this Gitea version, while dispatching an existing
|
|
# workflow_dispatch workflow via the API works today.
|
|
- name: Ask the installer repo to recompose its bundle
|
|
if: ${{ steps.plan.outputs.release == 'true' }}
|
|
env:
|
|
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
CI_TOKEN="$(printf '%s' "${REGISTRY_TOKEN}" | tr -d '\r\n')"
|
|
HTTP="$(curl -s -o /dev/null -w '%{http_code}' -X POST \
|
|
-H "Authorization: token ${CI_TOKEN}" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"ref":"main"}' \
|
|
"https://${GITEA_HOST}/api/v1/repos/${INSTALLER_REPO}/actions/workflows/bundle.yml/dispatches" || echo 000)"
|
|
case "$HTTP" in
|
|
20*) echo "Dispatched ${INSTALLER_REPO} bundle.yml (HTTP ${HTTP}) — not waiting for it." ;;
|
|
403|404)
|
|
echo "::warning::Could not dispatch ${INSTALLER_REPO} bundle.yml (HTTP ${HTTP}). REGISTRY_TOKEN likely lacks write:repository on that repo. Release ${{ steps.plan.outputs.tag }} is published and fine; its bundle will be composed by the installer's nightly cron instead." ;;
|
|
*)
|
|
echo "::warning::Dispatching ${INSTALLER_REPO} bundle.yml returned HTTP ${HTTP}. Release ${{ steps.plan.outputs.tag }} is published and fine; the nightly cron will recompose the bundle." ;;
|
|
esac
|