Skip to content

test(go): resolve Box provenance by name (#195) #10

test(go): resolve Box provenance by name (#195)

test(go): resolve Box provenance by name (#195) #10

name: Go module release
# After a go/ sync (or API change) merges to main, cut the next go/vX.Y.Z tag
# and GitHub Release. Requires an initial manual go/v0.1.0 tag first.
on:
push:
branches: [main]
# Everything the module publishes, minus what it does not. Stated as
# subtraction so a new source file releases by default; an allowlist of
# filenames silently stops releasing the day someone adds one. Later
# patterns win, so the negations must stay below 'go/**'.
paths:
- 'go/**'
- '!go/*_test.go'
- '!go/**/*_test.go'
- '!go/internal/**'
- '!go/README.md'
- '!go/check.sh'
workflow_dispatch:
inputs:
bump:
description: Semver bump (patch, minor, major)
required: true
default: patch
type: choice
options:
- patch
- minor
- major
concurrency:
group: go-module-release
cancel-in-progress: false
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.AGENT_PAT || secrets.GITHUB_TOKEN }}
- uses: actions/setup-go@v5
with:
go-version-file: go/go.mod
cache-dependency-path: |
go/go.mod
go/internal/gen/go.sum
- name: Resolve next version
id: ver
env:
BUMP_INPUT: ${{ github.event.inputs.bump }}
run: |
set -euo pipefail
# workflow_dispatch wins. On push, a [minor] or [major] marker in the
# merge commit message picks the bump, so an API change ships as one
# tag instead of a throwaway patch followed by a dispatched bump.
bump="${BUMP_INPUT:-}"
if [ -z "$bump" ]; then
msg="$(git log -1 --pretty=%B)"
case "$msg" in
*'[major]'*) bump="major" ;;
*'[minor]'*) bump="minor" ;;
*) bump="patch" ;;
esac
fi
echo "Bump: $bump"
latest="$(git tag -l 'go/v*' --sort=-v:refname | grep -E '^go/v[0-9]+\.[0-9]+\.[0-9]+$' | head -1 || true)"
if [ -z "$latest" ]; then
echo "No go/v* tags yet. Cut go/v0.1.0 manually once, then re-run."
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "skip=false" >> "$GITHUB_OUTPUT"
echo "latest=$latest" >> "$GITHUB_OUTPUT"
ver="${latest#go/v}"
IFS=. read -r major minor patch <<<"$ver"
case "$bump" in
major) major=$((major + 1)); minor=0; patch=0 ;;
minor) minor=$((minor + 1)); patch=0 ;;
patch) patch=$((patch + 1)) ;;
*) echo "unknown bump: $bump"; exit 1 ;;
esac
next="go/v${major}.${minor}.${patch}"
module_ver="v${major}.${minor}.${patch}"
echo "tag=$next" >> "$GITHUB_OUTPUT"
echo "module_ver=$module_ver" >> "$GITHUB_OUTPUT"
echo "Next tag: $next (module @${module_ver})"
- name: Verify module before tagging
if: steps.ver.outputs.skip != 'true'
run: |
set -euo pipefail
(cd go/internal/gen && go run .)
if [ -n "$(git status --porcelain -- go/)" ]; then
echo "go/ drift on main; refusing to tag:"
git status --porcelain -- go/
exit 1
fi
(cd go && gofmt -l . | tee /tmp/gofmt.out)
if [ -s /tmp/gofmt.out ]; then
echo "gofmt needed before release"
exit 1
fi
(cd go && go vet . && go test .)
(cd go/internal/gen && go test .)
- name: Create tag and GitHub Release
if: steps.ver.outputs.skip != 'true'
env:
GH_TOKEN: ${{ secrets.AGENT_PAT || secrets.GITHUB_TOKEN }}
TAG: ${{ steps.ver.outputs.tag }}
MODULE_VER: ${{ steps.ver.outputs.module_ver }}
PREV: ${{ steps.ver.outputs.latest }}
run: |
set -euo pipefail
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "Tag $TAG already exists; nothing to do"
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag -a "$TAG" -m "mcp-setup-docs Go module ${MODULE_VER}"
git push origin "$TAG"
notes_file="${RUNNER_TEMP:-/tmp}/go-module-release-notes.md"
base_guides="${RUNNER_TEMP:-/tmp}/go-module-release-guides-base.txt"
head_guides="${RUNNER_TEMP:-/tmp}/go-module-release-guides-head.txt"
git ls-tree -d --name-only "${PREV}:go/generated/guides" 2>/dev/null \
| sort -u > "$base_guides" || : > "$base_guides"
git ls-tree -d --name-only "HEAD:go/generated/guides" 2>/dev/null \
| sort -u > "$head_guides" || : > "$head_guides"
mapfile -t added_guides < <(comm -13 "$base_guides" "$head_guides")
mapfile -t removed_guides < <(comm -23 "$base_guides" "$head_guides")
updated_guides=()
while IFS= read -r slug; do
[ -z "$slug" ] && continue
if ! git diff --quiet "$PREV" -- "go/generated/guides/${slug}"; then
updated_guides+=("$slug")
fi
done < <(comm -12 "$base_guides" "$head_guides")
mapfile -t other_files < <(
git diff --name-only "$PREV" -- go/ \
| grep -v '^go/generated/guides/' \
| sort -u || true
)
fmt_backticks() {
local out="" s
for s in "$@"; do
[ -z "$s" ] && continue
if [ -n "$out" ]; then out+=", "; fi
out+="\`${s}\`"
done
printf '%s' "$out"
}
{
echo "Go module \`github.com/speakeasy-api/mcp-setup-docs/go@${MODULE_VER}\`"
echo
echo '```bash'
echo "go get github.com/speakeasy-api/mcp-setup-docs/go@${MODULE_VER}"
echo '```'
echo
echo "### Changes since ${PREV}"
if [ "${#added_guides[@]}" -gt 0 ]; then
echo "- **Added guides:** $(fmt_backticks "${added_guides[@]}")"
fi
if [ "${#updated_guides[@]}" -gt 0 ]; then
echo "- **Updated guides:** $(fmt_backticks "${updated_guides[@]}")"
fi
if [ "${#removed_guides[@]}" -gt 0 ]; then
echo "- **Removed guides:** $(fmt_backticks "${removed_guides[@]}")"
fi
if [ "${#other_files[@]}" -gt 0 ]; then
echo "- **Other:** $(fmt_backticks "${other_files[@]}")"
fi
if [ "${#added_guides[@]}" -eq 0 ] \
&& [ "${#updated_guides[@]}" -eq 0 ] \
&& [ "${#removed_guides[@]}" -eq 0 ] \
&& [ "${#other_files[@]}" -eq 0 ]; then
echo "- Sync publishable guides into the Go module embed"
fi
} > "$notes_file"
gh release create "$TAG" --title "$TAG" --notes-file "$notes_file"
- name: Prime module proxy
if: steps.ver.outputs.skip != 'true'
env:
MODULE_VER: ${{ steps.ver.outputs.module_ver }}
run: |
set -euo pipefail
# Best-effort: proxy indexing can lag; don't fail the release on it.
GOPROXY=https://proxy.golang.org go list -m "github.com/speakeasy-api/mcp-setup-docs/go@${MODULE_VER}" \
|| echo "proxy prime not ready yet for @${MODULE_VER}"