mirror of https://github.com/vernonkeenan/lib
feat: replace manual release ritual with `make release` (ADR-KV-014)
The README's "Update Procedure" required three web UIs (GitHub branch defaults, GitHub releases, Gitea "Synchronize Now") and a go-swagger CLI installed nowhere. Now that lib is main+tags instead of branch-per-version, replace it with scripts/release.sh driven through the Makefile: - `make install-swagger` installs a pinned go-swagger (v0.35.0) and verifies the version. - `make regen` copies the sibling service specs and regenerates api/ clients exactly as the old `make swagger` ritual did, failing fast if a sibling spec is missing. Regen output is reviewed/committed as its own PR, same as any other change. - `make release VERSION=vX.Y.Z` / `make release-dry-run VERSION=vX.Y.Z` validate semver + monotonicity, require a clean main checkout synced with origin/main, tag, push, `gh release create`, and POST to the Gitea mirror-sync API for the vernonkeenan/lib and work/lib mirrors (skipped with a loud warning if GITEA_TOKEN is unset). Also scope the CI push trigger to `main` + `v*` tags instead of every branch, so PRs (which already run via the `pull_request` trigger) stop double-running. Verified locally: bash -n + shellcheck clean; install-swagger into a temp GOBIN across go-swagger v0.33.2-v0.35.0; semver/monotonicity/ tag-exists/branch/origin-sync validation failure paths; a full regen against the real sibling specs in a scratch dir (never the checked-out api/swagger). That regen surfaced a real go-swagger limitation -- documented in the README and as a warning in the script -- reproduced across all four installable versions: an untagged operation in sf-gate-vernonkeenan.yaml produces a client that doesn't compile. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>pull/3/head
parent
a906d56678
commit
8ef777c7e6
|
|
@ -3,9 +3,17 @@ name: CI (lib)
|
|||
# First-ever CI for go/lib (ADR-KV-014). lib is publicly mirrored on
|
||||
# code.tnxs.net, so every push and PR gets a build/vet/test job plus a
|
||||
# gitleaks secret-scan job. lib is a Go library — no Dockerfile, no deploy.
|
||||
#
|
||||
# push trigger is scoped to main + release tags (not every branch): with
|
||||
# `branches: ['**']` *and* `pull_request` both set, every PR ran CI twice —
|
||||
# once for the pull_request event, once for the push to the PR's source
|
||||
# branch. main+tags replaced branch-per-version (ADR-KV-014, DT-KV-004), so
|
||||
# there's no longer a fleet of long-lived version branches that need their
|
||||
# own push-triggered runs independent of a PR.
|
||||
on:
|
||||
push:
|
||||
branches: ['**']
|
||||
branches: [main]
|
||||
tags: ['v*']
|
||||
pull_request:
|
||||
|
||||
defaults:
|
||||
|
|
|
|||
188
Makefile
188
Makefile
|
|
@ -1,157 +1,35 @@
|
|||
|
||||
.PHONY: swagger
|
||||
# Pinned go-swagger version for `make install-swagger` / `make regen`.
|
||||
# Bump deliberately, verify `swagger version` matches, and record the change
|
||||
# here and in README.md's Release Procedure. Chosen 2026-07-12: latest
|
||||
# stable go-swagger release at the time (ADR-KV-014).
|
||||
SWAGGER_VERSION := v0.35.0
|
||||
export SWAGGER_VERSION
|
||||
|
||||
swagger:
|
||||
cp ../auth/swagger/auth-vernonkeenan.yaml ./swagger
|
||||
cp ../auth/swagger/auth-vernonkeenan.yaml ./swagger/external
|
||||
cp ../crm/swagger/crm-vernonkeenan.yaml ./swagger
|
||||
cp ../crm/swagger/crm-vernonkeenan.yaml ./swagger/external
|
||||
cp ../stash/swagger/stash-vernonkeenan.yaml ./swagger
|
||||
cp ../stash/swagger/stash-vernonkeenan.yaml ./swagger/external
|
||||
cp ../sf-gate/swagger/sf-gate-vernonkeenan.yaml ./swagger
|
||||
cp ../sf-gate/swagger/sf-gate-vernonkeenan.yaml ./swagger/external
|
||||
cp ../research/swagger/research-vernonkeenan.yaml ./swagger
|
||||
cp ../research/swagger/research-vernonkeenan.yaml ./swagger/external
|
||||
cp ../members/swagger/members-vernonkeenan.yaml ./swagger
|
||||
cp ../members/swagger/members-vernonkeenan.yaml ./swagger/external
|
||||
cp ../plex/swagger/plex-vernonkeenan.yaml ./swagger
|
||||
cp ../plex/swagger/plex-vernonkeenan.yaml ./swagger/external
|
||||
#
|
||||
rm -rf api
|
||||
#
|
||||
# generate auth client
|
||||
#
|
||||
mkdir -p api/auth
|
||||
swagger generate client \
|
||||
--log-output=./swagger/logs/generate-auth-client.log \
|
||||
--copyright-file=./build/COPYRIGHT \
|
||||
--name=auth \
|
||||
--spec=./swagger/auth-vernonkeenan.yaml \
|
||||
--target=./api/auth \
|
||||
--client-package=auth-client \
|
||||
--model-package=auth-models \
|
||||
--principal=app.User
|
||||
#
|
||||
# update external auth client
|
||||
#
|
||||
sed -i 's|"http"|"https"|g' ./swagger/external/auth-vernonkeenan.yaml
|
||||
sed -i 's|auth.vernonkeenan.com:8080|gw.tnxs.net|g' ./swagger/external/auth-vernonkeenan.yaml
|
||||
sed -i 's|"/v1"|"/vk/auth/v1"|g' ./swagger/external/auth-vernonkeenan.yaml
|
||||
#
|
||||
# generate crm client
|
||||
#
|
||||
mkdir api/crm
|
||||
swagger generate client \
|
||||
--log-output=./swagger/logs/generate-crm-client.log \
|
||||
--copyright-file=./build/COPYRIGHT \
|
||||
--name=crm \
|
||||
--spec=./swagger/crm-vernonkeenan.yaml \
|
||||
--target=./api/crm \
|
||||
--client-package=crm-client \
|
||||
--model-package=crm-models \
|
||||
--principal=app.User
|
||||
#
|
||||
# update external crm client
|
||||
#
|
||||
sed -i 's|"http"|"https"|g' ./swagger/external/crm-vernonkeenan.yaml
|
||||
sed -i 's|crm.vernonkeenan.com:8080|gw.tnxs.net|g' ./swagger/external/crm-vernonkeenan.yaml
|
||||
sed -i 's|"/v1"|"/vk/crm/v1"|g' ./swagger/external/crm-vernonkeenan.yaml
|
||||
#
|
||||
# generate stash client
|
||||
#
|
||||
mkdir api/stash
|
||||
swagger generate client \
|
||||
--log-output=./swagger/logs/generate-stash-client.log \
|
||||
--copyright-file=./build/COPYRIGHT \
|
||||
--name=stash \
|
||||
--spec=./swagger/stash-vernonkeenan.yaml \
|
||||
--target=./api/stash \
|
||||
--client-package=stash-client \
|
||||
--model-package=stash-models \
|
||||
--principal=app.User
|
||||
#
|
||||
# update external stash client
|
||||
#
|
||||
sed -i 's|"http"|"https"|g' ./swagger/external/stash-vernonkeenan.yaml
|
||||
sed -i 's|stash.vernonkeenan.com:8080|gw.tnxs.net|g' ./swagger/external/stash-vernonkeenan.yaml
|
||||
sed -i 's|"/v1"|"/vk/stash/v1"|g' ./swagger/external/stash-vernonkeenan.yaml
|
||||
#
|
||||
# generate sfgate client
|
||||
#
|
||||
mkdir api/sfgate
|
||||
swagger generate client \
|
||||
--log-output=./swagger/logs/generate-sfgate-client.log \
|
||||
--copyright-file=./build/COPYRIGHT \
|
||||
--name=sfgate \
|
||||
--spec=./swagger/sf-gate-vernonkeenan.yaml \
|
||||
--target=./api/sfgate \
|
||||
--client-package=sfgate-client \
|
||||
--model-package=sfgate-models \
|
||||
--principal=app.User
|
||||
#
|
||||
# update external crm client
|
||||
#
|
||||
sed -i 's|"http"|"https"|g' ./swagger/external/sf-gate-vernonkeenan.yaml
|
||||
sed -i 's|sf-gate.vernonkeenan.com:8080|gw.tnxs.net|g' ./swagger/external/sf-gate-vernonkeenan.yaml
|
||||
sed -i 's|"/v1"|"/vk/sf-gate/v1"|g' ./swagger/external/sf-gate-vernonkeenan.yaml
|
||||
#
|
||||
# generate research client
|
||||
#
|
||||
mkdir api/research
|
||||
swagger generate client \
|
||||
--log-output=./swagger/logs/generate-research-client.log \
|
||||
--copyright-file=./build/COPYRIGHT \
|
||||
--name=research \
|
||||
--spec=./swagger/research-vernonkeenan.yaml \
|
||||
--target=./api/research \
|
||||
--client-package=research-client \
|
||||
--model-package=research-models \
|
||||
--principal=app.User
|
||||
#
|
||||
# update external research client
|
||||
#
|
||||
sed -i 's|"http"|"https"|g' ./swagger/external/research-vernonkeenan.yaml
|
||||
sed -i 's|research.vernonkeenan.com:8080|gw.tnxs.net|g' ./swagger/external/research-vernonkeenan.yaml
|
||||
sed -i 's|"/v1"|"/vk/research/v1"|g' ./swagger/external/research-vernonkeenan.yaml
|
||||
#
|
||||
# generate members client
|
||||
#
|
||||
mkdir api/members
|
||||
swagger generate client \
|
||||
--log-output=./swagger/logs/generate-members-client.log \
|
||||
--copyright-file=./build/COPYRIGHT \
|
||||
--name=members \
|
||||
--spec=./swagger/members-vernonkeenan.yaml \
|
||||
--target=./api/members \
|
||||
--client-package=members-client \
|
||||
--model-package=members-models \
|
||||
--principal=app.User
|
||||
#
|
||||
# update external members client
|
||||
#
|
||||
sed -i 's|"http"|"https"|g' ./swagger/external/members-vernonkeenan.yaml
|
||||
sed -i 's|members.vernonkeenan.com:8080|gw.tnxs.net|g' ./swagger/external/members-vernonkeenan.yaml
|
||||
sed -i 's|"/v1"|"/vk/members/v1"|g' ./swagger/external/members-vernonkeenan.yaml
|
||||
#
|
||||
# generate plex client
|
||||
#
|
||||
mkdir api/plex
|
||||
swagger generate client \
|
||||
--log-output=./swagger/logs/generate-plex-client.log \
|
||||
--copyright-file=./build/COPYRIGHT \
|
||||
--name=plex \
|
||||
--spec=./swagger/plex-vernonkeenan.yaml \
|
||||
--target=./api/plex \
|
||||
--client-package=plex-client \
|
||||
--model-package=plex-models \
|
||||
--principal=app.User
|
||||
#
|
||||
# update external plex client
|
||||
#
|
||||
sed -i 's|"http"|"https"|g' ./swagger/external/plex-vernonkeenan.yaml
|
||||
sed -i 's|plex.vernonkeenan.com:8080|gw.tnxs.net|g' ./swagger/external/plex-vernonkeenan.yaml
|
||||
sed -i 's|"/v1"|"/vk/plex/v1"|g' ./swagger/external/plex-vernonkeenan.yaml
|
||||
#
|
||||
# copy external swagger files
|
||||
#
|
||||
scp ./swagger/external/*.yaml root@whm.noc.tnxs.net:/home/taxassets/www/swagger/external
|
||||
.PHONY: install-swagger regen release release-dry-run swagger
|
||||
|
||||
# Install the pinned go-swagger CLI and verify its version.
|
||||
install-swagger:
|
||||
./scripts/release.sh install-swagger
|
||||
|
||||
# Regenerate api/ clients from the sibling service specs (../{auth,crm,stash,
|
||||
# sf-gate,research,members,plex}/swagger/*.yaml). Requires the sibling repos
|
||||
# to be cloned adjacently (same prerequisite the old README ritual had).
|
||||
# Review and commit the result as its own PR before `make release`.
|
||||
regen: install-swagger
|
||||
./scripts/release.sh regen
|
||||
|
||||
# Cut a release: tag HEAD of main, push the tag, publish the GitHub release,
|
||||
# and trigger the Gitea mirror-sync. Requires a clean main checkout that
|
||||
# matches origin/main. Usage: make release VERSION=v0.7.5
|
||||
release:
|
||||
./scripts/release.sh release "$(VERSION)"
|
||||
|
||||
# Same as `release` but stops before tag/push/release/mirror-sync — prints
|
||||
# what would happen. Usage: make release-dry-run VERSION=v0.7.5
|
||||
release-dry-run:
|
||||
./scripts/release.sh release "$(VERSION)" --dry-run
|
||||
|
||||
# Deprecated alias for `regen`, kept because old muscle memory (and the
|
||||
# README ritual) called this `make swagger`.
|
||||
swagger: regen
|
||||
|
|
|
|||
71
README.md
71
README.md
|
|
@ -4,24 +4,69 @@ vernonkeenan Shared Libraries (go)
|
|||
|
||||
## Dependent Systems
|
||||
|
||||
The Gitea server <https://code.tnxs.net> must be up and running with admin access.
|
||||
The Gitea server <https://code.tnxs.net> must be up and running with admin access. It hosts world-readable
|
||||
pull-mirrors of this repo at `vernonkeenan/lib` and `work/lib`.
|
||||
|
||||
## Update Procedure
|
||||
## Release Procedure
|
||||
|
||||
Prerequisite: All the other repos are cloned locally in adjacent directories.
|
||||
As of 2026-07-12 (ADR-KV-014, DT-KV-004) this repo is `main` + `v*` tags — branch-per-version is retired, and
|
||||
so is the old three-web-UI-plus-CLI manual ritual. Releasing is one command: `make release VERSION=vX.Y.Z`.
|
||||
|
||||
* Increment branch number in local repo
|
||||
### Prerequisites
|
||||
|
||||
* Generate all OpenAPI libraries and then cleanup modules
|
||||
* All the sibling service repos (`auth`, `crm`, `stash`, `sf-gate`, `research`, `members`, `plex`) are cloned
|
||||
locally in adjacent directories — `regen` reads each one's `swagger/<service>-vernonkeenan.yaml`.
|
||||
* [`gh`](https://cli.github.com/) installed and authenticated (`gh auth login`) with push access to
|
||||
`vernonkeenan/lib` — the release step calls `gh release create`.
|
||||
* `GITEA_TOKEN` exported, set to a Gitea API token (Settings -> Applications on <https://code.tnxs.net>) with
|
||||
rights to trigger a mirror-sync on `vernonkeenan/lib` and `work/lib`. If unset, `make release` still tags,
|
||||
pushes, and publishes the GitHub release — it just skips the Gitea mirror-sync step with a loud warning, so
|
||||
the mirrors will lag until someone clicks Synchronize Now manually or reruns with the token set. (There is
|
||||
no existing Gitea/tnxs token variable defined anywhere in this workstation's env files today — `GITEA_TOKEN`
|
||||
is a new name, not a rename of something already in use.)
|
||||
|
||||
### 1. Regenerate the API clients (its own PR)
|
||||
|
||||
```bash
|
||||
make swagger
|
||||
go get -u -f ./...
|
||||
go mod tidy
|
||||
make regen
|
||||
```
|
||||
|
||||
* Commit and push new branch to GitHub
|
||||
* On GitHub goto <https://github.com/vernonkeenan/lib/settings/branches> set the new branch to default.
|
||||
* On GitHub goto <https://github.com/vernonkeenan/lib/releases> and publish a new release for the new branch. Make the new tag the branch name.
|
||||
* On Gitea goto <https://code.tnxs.net/vernonkeenan/lib/settings> and click Synchronize Now.
|
||||
* Update the ``go.mod`` line with ``code.tnxs.net/vernonkeenan/lib`` in each project to match the latest version.
|
||||
This installs the pinned go-swagger CLI (`SWAGGER_VERSION` in the `Makefile`, currently `v0.35.0` — bump
|
||||
deliberately and update both places when you do), copies each sibling repo's `swagger/<service>-vernonkeenan.yaml`
|
||||
into `./swagger` and `./swagger/external`, regenerates every client under `api/`, and rewrites the external
|
||||
specs' scheme/host/path exactly as the old `make swagger` ritual did. It fails fast, before touching anything,
|
||||
if any sibling spec is missing.
|
||||
|
||||
Review `git status`/`git diff`, commit, open a PR, and merge it to `main` like any other change — client regen
|
||||
is not bundled into the release step itself. `make regen` runs a non-fatal `go build ./...` at the end as an
|
||||
early warning; the real gate is CI on that PR.
|
||||
|
||||
**Known gap:** if any sibling spec has an operation with no top-level `tags:`, go-swagger routes it into a
|
||||
fallback `operations` package whose import path in `<name>_client/<name>_client.go` doesn't respect
|
||||
`--client-package` — reproduced across go-swagger v0.33.2 through v0.35.0 while building this tooling. As of
|
||||
this writing that's `sf-gate-vernonkeenan.yaml`'s `PUT /researchprojectservices` (no `tags:`). Fix it at the
|
||||
source (add a `tags:` entry in the `sf-gate` repo's spec) rather than hand-patching generated code, since the
|
||||
next regen would just reintroduce it.
|
||||
|
||||
### 2. Cut the release (from a clean `main` checkout)
|
||||
|
||||
```bash
|
||||
make release VERSION=v0.7.5
|
||||
```
|
||||
|
||||
`scripts/release.sh` (invoked by the `release` target):
|
||||
|
||||
1. Validates `VERSION` is `vMAJOR.MINOR.PATCH` and strictly greater than the latest existing tag.
|
||||
2. Requires you're on `main`, the tree is clean, and local `main` matches `origin/main` (no local-only commits).
|
||||
3. Runs `go build ./...` and `go vet ./...` as a last sanity check.
|
||||
4. Creates an annotated tag at HEAD, pushes it, and runs `gh release create` with generated notes.
|
||||
5. POSTs to the Gitea mirror-sync API for `vernonkeenan/lib` and `work/lib` (skipped with a warning if
|
||||
`GITEA_TOKEN` is unset).
|
||||
|
||||
Use `make release-dry-run VERSION=v0.7.5` to run steps 1-3 and print what steps 4-5 would do, without tagging,
|
||||
pushing, publishing, or syncing anything.
|
||||
|
||||
### 3. Downstream consumers
|
||||
|
||||
Update the `go.mod` line for `code.tnxs.net/vernonkeenan/lib` in each dependent project to the new tag, same
|
||||
as before — that part of the old ritual is unchanged.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,356 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# scripts/release.sh — scripted release for vernonkeenan/lib (ADR-KV-014).
|
||||
#
|
||||
# Replaces the manual "Update Procedure" in README.md (three web UIs + a
|
||||
# go-swagger CLI installed nowhere) with a single, repeatable script driven
|
||||
# through `make`. See README.md's "Release Procedure" section for the
|
||||
# operator-facing walkthrough.
|
||||
#
|
||||
# Subcommands:
|
||||
# install-swagger Install the pinned go-swagger CLI and verify its version.
|
||||
# regen Regenerate api/ clients from the sibling service specs.
|
||||
# release <VERSION> [--dry-run]
|
||||
# Validate, tag, push, publish the GitHub release, and
|
||||
# trigger the Gitea mirror sync. With --dry-run, every
|
||||
# step up to (but not including) push/tag/release/mirror
|
||||
# is performed and the remaining steps are only printed.
|
||||
#
|
||||
# Env:
|
||||
# SWAGGER_VERSION Pinned go-swagger version (default set by Makefile; can override).
|
||||
# GITHUB_REPO owner/repo on GitHub (default vernonkeenan/lib).
|
||||
# GITEA_BASE_URL Gitea API base (default https://code.tnxs.net/api/v1).
|
||||
# GITEA_TOKEN Gitea API token with repo mirror-sync rights. If unset, the
|
||||
# mirror-sync step is skipped with a loud warning — it is
|
||||
# never required to fail the release. There is no
|
||||
# Gitea/tnxs API token variable defined in this workstation's
|
||||
# env files today (checked kv.env, taxnexus.env, telnexus.env,
|
||||
# rosetta.env, wdwai.env); GITEA_TOKEN is a new variable name
|
||||
# introduced by this script, not a rename of an existing one.
|
||||
# GITEA_MIRRORS Space-separated "owner/repo" mirror list (default
|
||||
# "vernonkeenan/lib work/lib").
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
|
||||
SWAGGER_VERSION="${SWAGGER_VERSION:-v0.35.0}"
|
||||
GITHUB_REPO="${GITHUB_REPO:-vernonkeenan/lib}"
|
||||
GITEA_BASE_URL="${GITEA_BASE_URL:-https://code.tnxs.net/api/v1}"
|
||||
GITEA_MIRRORS="${GITEA_MIRRORS:-vernonkeenan/lib work/lib}"
|
||||
|
||||
# Sibling service repos this ritual pulls specs from, and the go-swagger
|
||||
# generate flags the README's `make swagger` target used to run inline.
|
||||
# Format: "dir:spec_basename:client_name:target_subdir:client_pkg:model_pkg"
|
||||
SERVICES=(
|
||||
"auth:auth-vernonkeenan:auth:api/auth:auth-client:auth-models"
|
||||
"crm:crm-vernonkeenan:crm:api/crm:crm-client:crm-models"
|
||||
"stash:stash-vernonkeenan:stash:api/stash:stash-client:stash-models"
|
||||
"sf-gate:sf-gate-vernonkeenan:sfgate:api/sfgate:sfgate-client:sfgate-models"
|
||||
"research:research-vernonkeenan:research:api/research:research-client:research-models"
|
||||
"members:members-vernonkeenan:members:api/members:members-client:members-models"
|
||||
"plex:plex-vernonkeenan:plex:api/plex:plex-client:plex-models"
|
||||
)
|
||||
|
||||
# Host/path substitutions the ritual applied to swagger/external/*.yaml,
|
||||
# keyed by service dir (same mapping the Makefile hardcoded per client).
|
||||
external_host_for() {
|
||||
case "$1" in
|
||||
auth) echo "auth.vernonkeenan.com:8080" ;;
|
||||
crm) echo "crm.vernonkeenan.com:8080" ;;
|
||||
stash) echo "stash.vernonkeenan.com:8080" ;;
|
||||
sf-gate) echo "sf-gate.vernonkeenan.com:8080" ;;
|
||||
research) echo "research.vernonkeenan.com:8080" ;;
|
||||
members) echo "members.vernonkeenan.com:8080" ;;
|
||||
plex) echo "plex.vernonkeenan.com:8080" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
external_path_for() {
|
||||
case "$1" in
|
||||
auth) echo "/vk/auth/v1" ;;
|
||||
crm) echo "/vk/crm/v1" ;;
|
||||
stash) echo "/vk/stash/v1" ;;
|
||||
sf-gate) echo "/vk/sf-gate/v1" ;;
|
||||
research) echo "/vk/research/v1" ;;
|
||||
members) echo "/vk/members/v1" ;;
|
||||
plex) echo "/vk/plex/v1" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
log() { printf '[release] %s\n' "$*" >&2; }
|
||||
warn() { printf '\033[33m[release] WARNING: %s\033[0m\n' "$*" >&2; }
|
||||
die() { printf '\033[31m[release] ERROR: %s\033[0m\n' "$*" >&2; exit 1; }
|
||||
|
||||
usage() {
|
||||
cat >&2 <<'EOF'
|
||||
Usage:
|
||||
scripts/release.sh install-swagger
|
||||
scripts/release.sh regen
|
||||
scripts/release.sh release <VERSION> [--dry-run]
|
||||
|
||||
See the top of this file for env vars.
|
||||
EOF
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# install-swagger
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
swagger_bin() {
|
||||
local gobin
|
||||
gobin="$(go env GOBIN)"
|
||||
if [[ -z "$gobin" ]]; then
|
||||
gobin="$(go env GOPATH)/bin"
|
||||
fi
|
||||
echo "${gobin}/swagger"
|
||||
}
|
||||
|
||||
cmd_install_swagger() {
|
||||
log "Installing go-swagger ${SWAGGER_VERSION} (pinned; see README.md and Makefile SWAGGER_VERSION)"
|
||||
GOFLAGS=-mod=mod go install "github.com/go-swagger/go-swagger/cmd/swagger@${SWAGGER_VERSION}"
|
||||
|
||||
local bin
|
||||
bin="$(swagger_bin)"
|
||||
[[ -x "$bin" ]] || die "go install reported success but ${bin} is not executable"
|
||||
|
||||
local got
|
||||
got="$("$bin" version 2>&1 | head -1)"
|
||||
log "swagger version: ${got}"
|
||||
case "$got" in
|
||||
*"${SWAGGER_VERSION#v}"*) ;;
|
||||
*) die "installed swagger version does not match pinned ${SWAGGER_VERSION} (got: ${got})" ;;
|
||||
esac
|
||||
log "go-swagger ${SWAGGER_VERSION} verified at ${bin}"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# regen
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
check_sibling_specs() {
|
||||
local missing=()
|
||||
local svc dir spec_base rest
|
||||
for svc in "${SERVICES[@]}"; do
|
||||
IFS=':' read -r dir spec_base rest <<<"$svc"
|
||||
local spec_path="${REPO_ROOT}/../${dir}/swagger/${spec_base}.yaml"
|
||||
if [[ ! -f "$spec_path" ]]; then
|
||||
missing+=("$spec_path")
|
||||
fi
|
||||
done
|
||||
if [[ ${#missing[@]} -gt 0 ]]; then
|
||||
die "missing sibling spec(s), cannot regen (need all sibling repos cloned adjacently per README prerequisite):
|
||||
$(printf ' - %s\n' "${missing[@]}")"
|
||||
fi
|
||||
log "all ${#SERVICES[@]} sibling specs present"
|
||||
}
|
||||
|
||||
cmd_regen() {
|
||||
check_sibling_specs
|
||||
|
||||
local bin
|
||||
bin="$(swagger_bin)"
|
||||
[[ -x "$bin" ]] || die "swagger CLI not found at ${bin} — run 'make install-swagger' first"
|
||||
|
||||
cd "$REPO_ROOT"
|
||||
mkdir -p swagger/logs
|
||||
|
||||
log "copying sibling specs into ./swagger and ./swagger/external"
|
||||
local svc dir spec_base name target pkg model
|
||||
for svc in "${SERVICES[@]}"; do
|
||||
IFS=':' read -r dir spec_base name target pkg model <<<"$svc"
|
||||
cp "../${dir}/swagger/${spec_base}.yaml" "./swagger/"
|
||||
cp "../${dir}/swagger/${spec_base}.yaml" "./swagger/external/"
|
||||
done
|
||||
|
||||
log "removing generated api/ tree"
|
||||
rm -rf api
|
||||
|
||||
for svc in "${SERVICES[@]}"; do
|
||||
IFS=':' read -r dir spec_base name target pkg model <<<"$svc"
|
||||
log "generating ${name} client -> ${target}"
|
||||
mkdir -p "$target"
|
||||
"$bin" generate client \
|
||||
--log-output="./swagger/logs/generate-${name}-client.log" \
|
||||
--copyright-file=./build/COPYRIGHT \
|
||||
--name="${name}" \
|
||||
--spec="./swagger/${spec_base}.yaml" \
|
||||
--target="${target}" \
|
||||
--client-package="${pkg}" \
|
||||
--model-package="${model}" \
|
||||
--principal=app.User
|
||||
|
||||
local host path ext="./swagger/external/${spec_base}.yaml"
|
||||
host="$(external_host_for "$dir")"
|
||||
path="$(external_path_for "$dir")"
|
||||
sed -i 's|"http"|"https"|g' "$ext"
|
||||
sed -i "s|${host}|gw.tnxs.net|g" "$ext"
|
||||
sed -i "s|\"/v1\"|\"${path}\"|g" "$ext"
|
||||
done
|
||||
|
||||
log "regen complete for: $(printf '%s ' "${SERVICES[@]%%:*}")"
|
||||
|
||||
# Best-effort publish of the external swagger docs, exactly as the manual
|
||||
# ritual's last step did. Never fails the regen — CI/dev boxes typically
|
||||
# have no SSH access to whm.noc.tnxs.net.
|
||||
if command -v scp >/dev/null 2>&1; then
|
||||
log "attempting best-effort scp of swagger/external/*.yaml to whm.noc.tnxs.net (skip on failure)"
|
||||
if ! scp -o ConnectTimeout=5 -o BatchMode=yes \
|
||||
./swagger/external/*.yaml root@whm.noc.tnxs.net:/home/taxassets/www/swagger/external 2>/dev/null; then
|
||||
warn "scp to whm.noc.tnxs.net failed or is unreachable from this host — skipped (not fatal)."
|
||||
warn "If external swagger docs need publishing, run the scp manually from a host with access."
|
||||
fi
|
||||
fi
|
||||
|
||||
log "sanity build (non-fatal; real gate is CI on the regen PR)"
|
||||
if ! go build ./... 2>&1; then
|
||||
warn "generated code does not build. Known cause: any sibling spec operation missing a top-level"
|
||||
warn "'tags:' entry lands in a fallback 'operations' package whose import path in"
|
||||
warn "<name>_client/<name>_client.go does not respect --client-package across at least go-swagger"
|
||||
warn "v0.33.2-v0.35.0 (reproduced on all four during this tooling's own verification). As of this"
|
||||
warn "writing that's sf-gate-vernonkeenan.yaml's 'PUT /researchprojectservices' (no tags:). Fix at"
|
||||
warn "the source (add a tags: entry in the sf-gate repo's spec) rather than hand-patching generated"
|
||||
warn "code, since the next regen would just reintroduce it."
|
||||
fi
|
||||
|
||||
log "review 'git status' / 'git diff' and commit the regen output as its own PR before running 'make release'."
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# release
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
validate_semver() {
|
||||
local v="$1"
|
||||
[[ "$v" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] || \
|
||||
die "VERSION '${v}' is not a valid semver tag (expected vMAJOR.MINOR.PATCH, e.g. v0.7.5)"
|
||||
}
|
||||
|
||||
latest_tag() {
|
||||
git -C "$REPO_ROOT" tag --list 'v*' | sort -V | tail -1
|
||||
}
|
||||
|
||||
check_monotonic() {
|
||||
local new="$1" latest="$2"
|
||||
[[ -n "$latest" ]] || { log "no existing v* tags found; ${new} will be the first"; return; }
|
||||
local top
|
||||
top="$(printf '%s\n%s\n' "$latest" "$new" | sort -V | tail -1)"
|
||||
if [[ "$new" == "$latest" ]]; then
|
||||
die "VERSION ${new} already exists as a tag"
|
||||
fi
|
||||
if [[ "$top" != "$new" ]]; then
|
||||
die "VERSION ${new} is not greater than latest tag ${latest} (releases must be monotonically increasing)"
|
||||
fi
|
||||
log "monotonicity OK: ${latest} -> ${new}"
|
||||
}
|
||||
|
||||
cmd_release() {
|
||||
local version="${1:-}"
|
||||
local dry_run="false"
|
||||
shift || true
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--dry-run) dry_run="true" ;;
|
||||
*) die "unknown release argument: $1" ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
[[ -n "$version" ]] || die "usage: scripts/release.sh release <VERSION> [--dry-run] (or: make release VERSION=v0.7.5)"
|
||||
validate_semver "$version"
|
||||
|
||||
cd "$REPO_ROOT"
|
||||
|
||||
log "fetching tags from origin"
|
||||
git fetch --tags --quiet origin
|
||||
|
||||
local latest
|
||||
latest="$(latest_tag)"
|
||||
check_monotonic "$version" "$latest"
|
||||
|
||||
local branch
|
||||
branch="$(git rev-parse --abbrev-ref HEAD)"
|
||||
[[ "$branch" == "main" ]] || die "must be on 'main' to release (currently on '${branch}'); main+tags replaced branch-per-version (ADR-KV-014)"
|
||||
|
||||
if [[ -n "$(git status --porcelain)" ]]; then
|
||||
die "working tree is not clean; commit or stash changes before releasing. (Regen output belongs in its own PR, reviewed and merged to main *before* running 'make release'.)"
|
||||
fi
|
||||
|
||||
git fetch origin main --quiet
|
||||
local head_sha origin_sha
|
||||
head_sha="$(git rev-parse HEAD)"
|
||||
origin_sha="$(git rev-parse origin/main)"
|
||||
if [[ "$head_sha" != "$origin_sha" ]]; then
|
||||
die "local main (${head_sha:0:12}) does not match origin/main (${origin_sha:0:12}); pull/push so the tag lands on a commit that's actually on GitHub main"
|
||||
fi
|
||||
|
||||
log "sanity build/vet before tagging ${version} at ${head_sha:0:12}"
|
||||
go build ./...
|
||||
go vet ./...
|
||||
|
||||
log "About to release ${version}:"
|
||||
log " - annotated tag ${version} @ ${head_sha:0:12} on ${GITHUB_REPO}"
|
||||
log " - push tag to origin"
|
||||
log " - gh release create ${version} --repo ${GITHUB_REPO} --generate-notes"
|
||||
log " - Gitea mirror-sync: ${GITEA_MIRRORS}"
|
||||
|
||||
if [[ "$dry_run" == "true" ]]; then
|
||||
log "DRY RUN: stopping before tag/push/release/mirror-sync. Nothing was written."
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "creating annotated tag ${version}"
|
||||
git tag -a "$version" -m "Release ${version}"
|
||||
|
||||
log "pushing tag ${version} to origin"
|
||||
git push origin "$version"
|
||||
|
||||
log "publishing GitHub release ${version}"
|
||||
gh release create "$version" \
|
||||
--repo "$GITHUB_REPO" \
|
||||
--title "$version" \
|
||||
--generate-notes
|
||||
|
||||
gitea_mirror_sync
|
||||
}
|
||||
|
||||
gitea_mirror_sync() {
|
||||
if [[ -z "${GITEA_TOKEN:-}" ]]; then
|
||||
warn "GITEA_TOKEN is not set — skipping Gitea mirror-sync for: ${GITEA_MIRRORS}"
|
||||
warn "Run manually later: curl -sS -X POST -H \"Authorization: token \$GITEA_TOKEN\" ${GITEA_BASE_URL}/repos/<owner>/<repo>/mirror-sync"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local mirror owner repo status
|
||||
for mirror in $GITEA_MIRRORS; do
|
||||
owner="${mirror%%/*}"
|
||||
repo="${mirror##*/}"
|
||||
log "Gitea mirror-sync: ${mirror}"
|
||||
status="$(curl -sS -o /dev/null -w '%{http_code}' -X POST \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
"${GITEA_BASE_URL}/repos/${owner}/${repo}/mirror-sync")"
|
||||
if [[ "$status" != "200" ]]; then
|
||||
warn "mirror-sync for ${mirror} returned HTTP ${status} (expected 200) — check manually at https://code.tnxs.net/${mirror}/settings"
|
||||
else
|
||||
log "mirror-sync triggered for ${mirror} (HTTP 200; sync happens async on the Gitea side)"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# main
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
main() {
|
||||
local cmd="${1:-}"
|
||||
shift || true
|
||||
case "$cmd" in
|
||||
install-swagger) cmd_install_swagger "$@" ;;
|
||||
regen) cmd_regen "$@" ;;
|
||||
release) cmd_release "$@" ;;
|
||||
*) usage; exit 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Loading…
Reference in New Issue