PLAN.md §30 as approved, plus D119/D120 from the build. Server: - rust_map_images (one row per server: picture as MEDIUMBLOB, geometry, monuments, DERIVATION_VERSION) and rust_map_overrides; purge.sql pair. - mapImages.js: D110. The board poll notices a new boot/wipe/seed/size and asks map.info; a new key or hash from the free Rust+ cache (or a render kept on disk) is fetched in slices, checked against its SHA-256 and stored in one statement. One fetch per server, a backoff on failure, `stale` abandons a fetch that straddles a map change. Render now (D109) is admin-only and watched to completion. - mapLive.js: D111. One map.live per server per 5 s whoever asks; positions are held in memory only. - model/map: four layers (world, events public; players, bases staff), a fleet default plus per-server override (D114), the players layer capped by presence (D113), own dot and online first-party clan mates for a linked viewer (D115, D117, D118). A layer the viewer may not see is absent from the answer, never sent and hidden. - Routes: public /servers/:id/map, /map/image (immutable under its hash), /map/live; admin /servers/:id/map/fetch and /render; the Map card on the visibility PUT. Swagger fragment and frozen manifest regenerated. Client: - A Map tab: Leaflet over the picture in CRS.Simple, the game's own grid (labels only when a cell is wide enough to hold one), a legend that lists hidden layers with who can see them, polled every 10 s while visible. - D120: Leaflet is a lazy split chunk beside entry.js, not in it. release.yml copies every dist/*.js; checkExternals and build.test.js hold both ends. - The Map card on Admin -> Rust visibility, with Fetch again and Render now. Capability `map` declared for the Android app (phase 15). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
446 lines
23 KiB
YAML
446 lines
23 KiB
YAML
# Build and publish the installable bundle: `module-rust-<version>.tar.gz` plus
|
|
# the manifest carrying its sha256 (docs/website/MODULE_SYSTEM.md §2.3, §2.5).
|
|
#
|
|
# ── What a release IS here ──────────────────────────────────────────────────
|
|
#
|
|
# **An operator never builds anything** (MODULE_SYSTEM.md §1.14 — the constraint
|
|
# the whole module system is shaped around). So a release is not source: it is the
|
|
# directory core's loader expects to find at `modules/rust/`, already assembled —
|
|
# the prebuilt client chunk, the schema fragment and the OpenAPI fragment — packed
|
|
# as it will be unpacked. Core's admin install downloads the tarball, verifies it
|
|
# against the `sha256` in the manifest, and unpacks it onto the volume. Nothing
|
|
# runs `npm` on the way.
|
|
#
|
|
# **And nothing is installed into the bundle either**, which is where this repo
|
|
# differs from Module-uo: the shipped half declares no runtime dependencies, so
|
|
# there is no `npm ci --omit=dev` and no `server/node_modules` in the tarball (org
|
|
# lead, phase 2). That is a decision worth being loud about rather than a detail —
|
|
# `server/scripts/checkBundle.js` fails the PR that adds a dependency without also
|
|
# teaching this file to install and pack it, because a bundle that declares an
|
|
# import it does not carry fails the same way a missing directory does.
|
|
#
|
|
# ── The version is DERIVED, and the declaration is a floor ──────────────────
|
|
#
|
|
# The engine `link`, `installer` and `Module-uo` already run (MODULE_SYSTEM
|
|
# §2.7.1, decision 19 as amended):
|
|
#
|
|
# feat!: / BREAKING CHANGE -> major feat: -> minor fix|perf: -> patch
|
|
# nothing releasable -> no release is cut
|
|
# (first ever run, no tag) -> releases what module.json declares
|
|
#
|
|
# Module-uo learned this the expensive way: it released only when a merge left
|
|
# `module.json` at a version with no release yet — the version DECLARED, never
|
|
# computed — and between 2026-08-12 and 2026-08-19 that cost it *every* bundle,
|
|
# because nine phases of work landed without anyone touching that line.
|
|
#
|
|
# **The declared version is kept as a floor, not deleted.** If `module.json` names
|
|
# a version above the newest tag, that version releases. Raising it by hand is how
|
|
# you say "this one is a minor, whatever the subjects imply", and it is the natural
|
|
# place to move when a `coreApi` bump forces the question.
|
|
#
|
|
# The number that ships is therefore the TAG, and CI writes it into the
|
|
# `module.json` inside the bundle at assembly time. The committed `module.json` is
|
|
# a floor and a starting point, not a record of the last release — a release engine
|
|
# that has to commit a bump back to `main` stops working the day someone protects
|
|
# the branch, and this one is protected.
|
|
#
|
|
# ── The backdoor ────────────────────────────────────────────────────────────
|
|
#
|
|
# `workflow_dispatch` publishes on demand, for the case the rules above cannot
|
|
# reach: `module.json` changed in a way worth shipping — a widened `coreApi`, a
|
|
# new mount, a capability — with no releasable code behind it. Leave `version`
|
|
# blank to bump the newest tag by `bump` (default `patch`), or name an exact
|
|
# version to publish that. A dispatch releases even when nothing in the log is
|
|
# releasable; that is the entire point of pressing the button.
|
|
#
|
|
# Re-running on a version that is already released is a no-op, so a rerun after an
|
|
# unrelated failure is safe. A tag that exists with no release behind it is NOT a
|
|
# no-op — see the recovery branch in the plan step. That state is not theoretical:
|
|
# `servuo-plugins`' first release pushed its tag and then 401'd on the release API
|
|
# because the secret was absent, and without the recovery branch the repo would
|
|
# have been stuck there permanently.
|
|
#
|
|
# This workflow never writes to a branch. It tags and publishes, so `main` needs
|
|
# no push exception.
|
|
#
|
|
# Prerequisites (Settings → Actions → Secrets on RunicGateway/Module-Rust):
|
|
# REGISTRY_TOKEN — Gitea access token with `write:repository`, to push the tag
|
|
# and create the release.
|
|
|
|
name: Release
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
workflow_dispatch:
|
|
inputs:
|
|
version:
|
|
description: 'Exact version to publish (e.g. 0.1.1). Blank = bump the newest tag by the level below.'
|
|
required: false
|
|
default: ''
|
|
bump:
|
|
description: 'Bump level when version is blank: patch | minor | major'
|
|
required: false
|
|
default: 'patch'
|
|
|
|
concurrency:
|
|
group: release-module-rust
|
|
cancel-in-progress: false
|
|
|
|
env:
|
|
GITEA_HOST: gitea.whitlocktech.com
|
|
REPO: RunicGateway/Module-Rust
|
|
|
|
jobs:
|
|
release:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 30
|
|
steps:
|
|
# Full history: the plan step reads every tag and every subject since the
|
|
# newest one, and a shallow clone has neither.
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 20
|
|
|
|
- name: Plan the release (version + changelog)
|
|
id: plan
|
|
env:
|
|
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
|
EVENT: ${{ github.event_name }}
|
|
IN_VERSION: ${{ github.event.inputs.version }}
|
|
IN_BUMP: ${{ github.event.inputs.bump }}
|
|
run: |
|
|
set -euo pipefail
|
|
mkdir -p dist
|
|
git fetch --tags --force >/dev/null 2>&1 || true
|
|
|
|
DECLARED="$(node -p "require('./module.json').version")"
|
|
LAST_TAG="$(git describe --tags --match 'v*' --abbrev=0 2>/dev/null || true)"
|
|
CURRENT="${LAST_TAG#v}"
|
|
RANGE="${LAST_TAG:+${LAST_TAG}..}HEAD"
|
|
echo "module.json declares ${DECLARED}; newest tag is ${LAST_TAG:-<none>}"
|
|
|
|
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
|
|
}
|
|
|
|
# `sort -V` orders version strings, so the higher of two is its last
|
|
# line. Used rather than a hand-rolled field compare because 0.10.0 vs
|
|
# 0.9.0 is exactly the comparison a string sort gets wrong.
|
|
higher() { printf '%s\n%s\n' "$1" "$2" | sort -V | tail -1; }
|
|
|
|
rank() { case "$1" in major) echo 3 ;; minor) echo 2 ;; patch) echo 1 ;; *) echo 0 ;; esac; }
|
|
bigger_bump() { if [ "$(rank "$1")" -ge "$(rank "$2")" ]; then echo "$1"; else echo "$2"; fi; }
|
|
|
|
VERSION=""
|
|
if [ -n "${IN_VERSION:-}" ]; then
|
|
# The backdoor's exact form. Deliberately unvalidated against the log:
|
|
# a human typed it, and the already-released check below is the only
|
|
# guard that matters.
|
|
VERSION="${IN_VERSION}"
|
|
echo "dispatch: publishing the requested version ${VERSION}"
|
|
else
|
|
LEVEL="$BUMP"
|
|
# A dispatch with nothing releasable in the log still releases — that
|
|
# is what the button is for. Where the log DOES say something, the
|
|
# larger of the two wins rather than the input: pressing the button on
|
|
# a log full of `feat:` without touching the dropdown would otherwise
|
|
# publish its `patch` default over a minor's worth of work, and a
|
|
# version that undersells its own contents cannot be taken back.
|
|
if [ "${EVENT:-}" = workflow_dispatch ]; then
|
|
LEVEL="$(bigger_bump "$LEVEL" "${IN_BUMP:-patch}")"
|
|
if [ "$BUMP" = none ]; then
|
|
echo "dispatch: nothing releasable in the log, bumping ${LEVEL} anyway"
|
|
elif [ "$LEVEL" != "$BUMP" ]; then
|
|
echo "dispatch: the log says ${BUMP}, the run asked for ${LEVEL} — taking ${LEVEL}"
|
|
fi
|
|
fi
|
|
|
|
if [ -z "$CURRENT" ]; then
|
|
VERSION="$DECLARED" # first ever release: ship what is declared
|
|
elif [ "$LEVEL" != none ]; then
|
|
VERSION="$(bump "$CURRENT" "$LEVEL")"
|
|
fi
|
|
|
|
# The floor. A `module.json` above the newest tag releases at that
|
|
# version even when the log says nothing and even when the log says
|
|
# patch.
|
|
if [ -n "$CURRENT" ] && [ "$DECLARED" != "$CURRENT" ] \
|
|
&& [ "$(higher "$DECLARED" "$CURRENT")" = "$DECLARED" ]; then
|
|
if [ -z "$VERSION" ] || [ "$(higher "$DECLARED" "$VERSION")" = "$DECLARED" ]; then
|
|
echo "module.json declares ${DECLARED}, above both ${CURRENT} and the derived version — releasing that."
|
|
VERSION="$DECLARED"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
RELEASE=true
|
|
if [ -z "$VERSION" ]; then
|
|
RELEASE=false
|
|
VERSION="$CURRENT"
|
|
echo "Nothing releasable since ${LAST_TAG} (no feat/fix/perf/breaking subject) — standing down."
|
|
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 what happened on servuo-plugins' first release,
|
|
# where absent 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. Note this deliberately OVERRIDES the RELEASE=false above:
|
|
# with the tag in place there is nothing releasable after it, so the
|
|
# normal path would stand down, which is why it could never self-heal.
|
|
# Anything other than 200/404 — a network failure, a bad token — is not
|
|
# evidence of absence, and guessing "no" would publish over a good
|
|
# release, so refuse instead.
|
|
REUSE_TAG=false
|
|
if [ -n "$VERSION" ] && 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)"
|
|
case "$REL_HTTP" in
|
|
200) echo "v${VERSION} is already released — nothing to do."; RELEASE=false ;;
|
|
404) 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 ;;
|
|
*) echo "::error::Could not determine whether v${VERSION} is released (HTTP ${REL_HTTP}). Refusing to guess."; exit 1 ;;
|
|
esac
|
|
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)"
|
|
CL_RANGE="${PREV_TAG:+${PREV_TAG}..}v${VERSION}"
|
|
SINCE="$PREV_TAG"
|
|
else
|
|
CL_RANGE="$RANGE"
|
|
SINCE="$LAST_TAG"
|
|
fi
|
|
CL_SUBJECTS="$(git log --no-merges --format='%s' $CL_RANGE || true)"
|
|
|
|
{
|
|
echo "## module-rust v${VERSION}"
|
|
echo
|
|
echo "Install from the website's Admin → Modules screen by pasting the URL of"
|
|
echo "\`module-rust-${VERSION}.json\`, or unpack the tarball onto the modules volume"
|
|
echo "as \`modules/rust/\`. Requires a core whose \`MODULE_API_VERSION\` satisfies"
|
|
echo "\`$(node -p "require('./module.json').coreApi")\`."
|
|
echo
|
|
echo "A Rust server also needs the other two halves of the bridge:"
|
|
echo "[Rust-Link](https://${GITEA_HOST}/RunicGateway/Rust-Link) (the sidecar) and"
|
|
echo "[Rust-Plugins](https://${GITEA_HOST}/RunicGateway/Rust-Plugins) (the Oxide/Carbon plugin)."
|
|
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/^/- /'
|
|
echo
|
|
echo "### Verifying this download"
|
|
echo
|
|
echo "Releases are **unsigned** — the \`sha256\` in \`module-rust-${VERSION}.json\` is the"
|
|
echo "trust anchor, and the website verifies it before unpacking."
|
|
echo
|
|
echo '```bash'
|
|
echo "sha256sum -c SHA256SUMS --ignore-missing"
|
|
echo '```'
|
|
} > dist/CHANGELOG.md
|
|
|
|
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
|
echo "tag=v${VERSION}" >> "$GITHUB_OUTPUT"
|
|
echo "release=${RELEASE}" >> "$GITHUB_OUTPUT"
|
|
echo "reuse_tag=${REUSE_TAG}" >> "$GITHUB_OUTPUT"
|
|
echo "bump=${BUMP}" >> "$GITHUB_OUTPUT"
|
|
echo "==> release=${RELEASE} version=${VERSION} bump=${BUMP} declared=${DECLARED} last_tag=${LAST_TAG:-<none>}"
|
|
|
|
# Before anything is built or tagged, so a repo without secrets fails
|
|
# legibly rather than half-publishing: the tag push can succeed on the
|
|
# credential actions/checkout left in the local git config while the release
|
|
# API call 401s, leaving the repo tagged and unreleased.
|
|
- name: Verify release credentials are configured
|
|
if: ${{ steps.plan.outputs.release == 'true' }}
|
|
env:
|
|
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
if [ -z "$(printf '%s' "${REGISTRY_TOKEN:-}" | tr -d '\r\n')" ]; then
|
|
echo "::error::Missing Actions secret REGISTRY_TOKEN (needs write:repository) on ${REPO}."
|
|
exit 1
|
|
fi
|
|
echo "Release credentials present."
|
|
|
|
- name: Build the client chunk
|
|
if: ${{ steps.plan.outputs.release == 'true' }}
|
|
run: |
|
|
npm ci --prefix client
|
|
npm run build --prefix client
|
|
|
|
# ── Assemble exactly what an operator's volume gets ──────────────────
|
|
#
|
|
# Stated as an INCLUDE list, not an exclude list. An exclude list ships
|
|
# whatever it forgot: the day someone adds `server/tools/` with a scratch
|
|
# credential in it, an exclude list packs it and nobody finds out.
|
|
#
|
|
# The list itself lives in `ci/bundle.json`, not here, because it has a
|
|
# second reader: `server/scripts/checkBundle.js` runs in PR checks and asks
|
|
# whether the list still covers everything `server/index.js` reaches. One
|
|
# declaration, two readers, so a new directory cannot go missing quietly.
|
|
- name: Assemble the bundle
|
|
if: ${{ steps.plan.outputs.release == 'true' }}
|
|
run: |
|
|
set -euo pipefail
|
|
VERSION="${{ steps.plan.outputs.version }}"
|
|
OUT="dist/module-rust-${VERSION}"
|
|
rm -rf "$OUT" && mkdir -p "$OUT"
|
|
|
|
# The manifest core reads — with the RELEASED version written into it.
|
|
# The committed `module.json` is a floor, not a record of the last
|
|
# release (see the header), so copying it verbatim would ship a bundle
|
|
# whose `installed_modules` row and admin screen disagree with the tag it
|
|
# came from. This is the one place the derived number becomes the
|
|
# module's own.
|
|
jq --arg v "$VERSION" '.version = $v' module.json > "$OUT/module.json"
|
|
|
|
# The two fragments, and the licence the code is under — a bundle that
|
|
# ships GPL code without its licence is not distributable.
|
|
for f in $(jq -r '.root[]' ci/bundle.json); do
|
|
cp "$f" "$OUT/"
|
|
done
|
|
|
|
# The server half, minus what never runs inside core's process. No
|
|
# node_modules: the shipped half declares no runtime dependencies, and
|
|
# check:bundle is what keeps that true.
|
|
mkdir -p "$OUT/server"
|
|
for d in $(jq -r '.server[]' ci/bundle.json); do
|
|
cp -r "server/$d" "$OUT/server/"
|
|
done
|
|
|
|
# The client half is the BUILT chunks only. `client/src` is source an
|
|
# operator has no use for and core will never read. Every `.js`, not
|
|
# `entry.js` by name: since D120 the Map tab imports Leaflet's split
|
|
# chunk from beside it, and a release that shipped the entry alone
|
|
# would load everywhere and spin for ever on that tab.
|
|
mkdir -p "$OUT/client/dist"
|
|
cp client/dist/*.js "$OUT/client/dist/"
|
|
|
|
# Prove the bundle is loadable before it is published: these are the
|
|
# paths core's loader resolves out of module.json, and a release whose
|
|
# entry point is missing fails on an operator's box with a
|
|
# `startup_failed` row instead of here. The version assertion guards the
|
|
# rewrite above — a bundle that still carried the declared version would
|
|
# install under a number that is not the one it was released as.
|
|
node -e '
|
|
const fs = require("fs"), path = require("path");
|
|
const [root, want] = process.argv.slice(1);
|
|
const m = JSON.parse(fs.readFileSync(path.join(root, "module.json"), "utf8"));
|
|
if (m.version !== want) {
|
|
console.error(`bundle declares ${m.version}, but this is release ${want}`);
|
|
process.exit(1);
|
|
}
|
|
for (const p of [m.server, m.schema, m.purge, m.client.entry, "swagger-fragment.json"]) {
|
|
if (!fs.existsSync(path.join(root, p))) { console.error("bundle is missing " + p); process.exit(1); }
|
|
}
|
|
console.log("bundle contents check: ok");
|
|
' "$OUT" "$VERSION"
|
|
|
|
# ── And that it can actually LOAD ─────────────────────────────────
|
|
#
|
|
# The check above stats the paths `module.json` declares, which is a real
|
|
# question but a shallow one: Module-uo's v1.0.0 passed exactly that and
|
|
# was still missing `server/commands/`, because a file reached only by a
|
|
# require inside `register()` is named nowhere in `module.json`. This
|
|
# resolves every relative require in the assembled tree and asserts the
|
|
# target is in it — asked of the artifact, so it also catches a copy that
|
|
# half failed or a list naming a path that has since moved.
|
|
#
|
|
# Run from the SOURCE tree (`server/scripts/` never ships) against the
|
|
# assembled bundle.
|
|
node server/scripts/checkBundle.js --bundle "$OUT"
|
|
|
|
tar -C dist -czf "dist/module-rust-${VERSION}.tar.gz" "module-rust-${VERSION}"
|
|
rm -rf "$OUT"
|
|
|
|
SHA="$(sha256sum "dist/module-rust-${VERSION}.tar.gz" | cut -d' ' -f1)"
|
|
SIZE="$(stat -c%s "dist/module-rust-${VERSION}.tar.gz")"
|
|
|
|
# The install manifest. Same shape as the installer's bundle JSON — a
|
|
# per-asset sha256 fetched over HTTPS, no signatures — because that is
|
|
# the model this project already has and a second one would be a second
|
|
# thing to get right (MODULE_SYSTEM.md §1.11).
|
|
jq -n \
|
|
--arg id "$(node -p "require('./module.json').id")" \
|
|
--arg name "$(node -p "require('./module.json').name")" \
|
|
--arg version "$VERSION" \
|
|
--arg coreApi "$(node -p "require('./module.json').coreApi")" \
|
|
--arg artifact "module-rust-${VERSION}.tar.gz" \
|
|
--arg sha256 "$SHA" \
|
|
--argjson size "$SIZE" \
|
|
--arg url "https://${GITEA_HOST}/${REPO}/releases/download/v${VERSION}/module-rust-${VERSION}.tar.gz" \
|
|
'{schema:1, id:$id, name:$name, version:$version, coreApi:$coreApi,
|
|
artifact:$artifact, url:$url, sha256:$sha256, size:$size}' \
|
|
> "dist/module-rust-${VERSION}.json"
|
|
|
|
echo "${SHA} module-rust-${VERSION}.tar.gz" > dist/SHA256SUMS
|
|
cat "dist/module-rust-${VERSION}.json"
|
|
|
|
# Skipped on a recovery run: the tag is already there and is the thing being
|
|
# published against.
|
|
- name: Tag the release
|
|
if: ${{ steps.plan.outputs.release == 'true' && steps.plan.outputs.reuse_tag != 'true' }}
|
|
env:
|
|
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
TAG="${{ steps.plan.outputs.tag }}"
|
|
git config user.name 'Runic Gateway CI'
|
|
git config user.email 'ci@whitlocktech.net'
|
|
git tag -a "$TAG" -m "module-rust ${TAG}"
|
|
git push origin "$TAG"
|
|
|
|
- name: Create the Gitea release and upload the bundle
|
|
if: ${{ steps.plan.outputs.release == 'true' }}
|
|
env:
|
|
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
TAG="${{ steps.plan.outputs.tag }}"
|
|
VERSION="${{ steps.plan.outputs.version }}"
|
|
API="https://${GITEA_HOST}/api/v1/repos/${REPO}"
|
|
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 "$(cat dist/CHANGELOG.md)" \
|
|
'{tag_name:$tag, name:$tag, body:$body, draft:false, prerelease:false}')" \
|
|
| jq -r '.id')"
|
|
echo "Created release ${TAG} (id=${REL_ID})"
|
|
|
|
for f in "module-rust-${VERSION}.tar.gz" "module-rust-${VERSION}.json" 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
|