Skip to content

Implement tak v0 - minimal claims tracker #295

Implement tak v0 - minimal claims tracker

Implement tak v0 - minimal claims tracker #295

Workflow file for this run

name: Release Go Projects
on:
push:
branches:
- main
paths:
- "**/*.go"
- "**/go.mod"
- "**/go.sum"
- ".github/workflows/release.yml"
pull_request:
types: [opened, synchronize, reopened]
paths:
- "**/*.go"
- "**/go.mod"
- "**/go.sum"
- ".github/workflows/release.yml"
workflow_dispatch:
inputs:
projects:
description: "Comma or newline separated list of project directories to release (e.g. ingest,want)"
required: false
default: ""
jobs:
# First job: detect which Go projects need releases
detect:
runs-on: ubuntu-latest
outputs:
projects: ${{ steps.find-projects.outputs.projects }}
has_projects: ${{ steps.find-projects.outputs.has_projects }}
mirror_projects: ${{ steps.mirror-config.outputs.projects }}
mirror_metadata: ${{ steps.mirror-config.outputs.metadata }}
mirror_repo: ${{ steps.mirror-config.outputs.repo }}
steps:
- name: Checkout code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
persist-credentials: false
- name: Find Go projects
id: find-projects
run: |
python3 scripts/detect_go_projects.py \
--event "${{ github.event_name }}" \
--before-sha "${{ github.event.before }}" \
--pr-base "${GITHUB_EVENT_PULL_REQUEST_BASE_REF}" \
--ref-sha "${{ github.sha }}" \
--manual-projects "${GITHUB_EVENT_INPUTS_PROJECTS}" \
--output "$GITHUB_OUTPUT"
env:
GITHUB_EVENT_PULL_REQUEST_BASE_REF: ${{ github.event.pull_request.base.ref }}
GITHUB_EVENT_INPUTS_PROJECTS: ${{ github.event.inputs.projects }}
- name: Load release mirror config
id: mirror-config
run: |
python3 - <<'PY'
import json
import os
from pathlib import Path
import tomllib
data = tomllib.loads(Path("release-mirror.toml").read_text())
mirror = data.get("mirror", {})
projects = []
metadata = {}
for entry in mirror.get("projects", []):
name = entry.get("name")
if not name:
continue
projects.append(name)
metadata[name] = entry
output = os.environ["GITHUB_OUTPUT"]
with open(output, "a", encoding="utf-8") as fh:
fh.write(f"projects={json.dumps(projects)}\n")
fh.write(f"metadata={json.dumps(metadata)}\n")
fh.write(f"repo={mirror.get('public_repo', '')}\n")
PY
# Second job: build and release each project
release:
needs: detect
if: needs.detect.outputs.has_projects == 'true'
runs-on: ubuntu-latest
strategy:
matrix:
project: ${{ fromJson(needs.detect.outputs.projects) }}
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
persist-credentials: false
- name: Set up Go
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
with:
go-version: "1.24.7"
cache-dependency-path: ${{ matrix.project }}/go.sum
- name: Determine version and branch
id: version
run: |
PROJECT="${MATRIX_PROJECT}"
# Determine branch/ref name
if [ "${{ github.event_name }}" = "pull_request" ]; then
# For PRs, use pr-NUMBER format
REF="pr-${{ github.event.pull_request.number }}"
else
# For push to main
REF="main"
fi
# Sanitize ref name (replace / with -)
REF_SAFE="${REF//\//-}"
# Find existing tags for this project+ref combination
TAG_PREFIX="${PROJECT}--${REF_SAFE}."
EXISTING_TAGS=$(git tag -l "${TAG_PREFIX}*" | sort -V)
if [ -z "$EXISTING_TAGS" ]; then
# No existing tags, start with .1
VERSION_NUM=1
else
# Get the highest version number
LAST_TAG=$(printf '%s\n' "$EXISTING_TAGS" | tail -n 1)
prefix="$TAG_PREFIX"
LAST_NUM="${LAST_TAG#"$prefix"}"
VERSION_NUM=$((LAST_NUM + 1))
fi
TAG="${PROJECT}--${REF_SAFE}.${VERSION_NUM}"
VERSION="${REF_SAFE}.${VERSION_NUM}"
{
echo "project=$PROJECT"
echo "ref=$REF"
echo "ref_safe=$REF_SAFE"
echo "tag=$TAG"
echo "version=$VERSION"
echo "version_num=$VERSION_NUM"
} >> "$GITHUB_OUTPUT"
echo "Building ${PROJECT} version ${VERSION} (tag: ${TAG})"
env:
MATRIX_PROJECT: ${{ matrix.project }}
- name: Build binaries
id: build
run: |
set -euo pipefail
PROJECT="${MATRIX_PROJECT}"
VERSION="${STEPS_VERSION_OUTPUTS_VERSION}"
if [ -f "$PROJECT/cmd/main.go" ]; then
BUILD_TARGET="./cmd"
else
BUILD_TARGET="."
fi
mkdir -p dist
DIST_DIR="$(pwd)/dist"
PLATFORMS=(
"linux/amd64"
"linux/arm64"
"darwin/amd64"
"darwin/arm64"
)
for platform in "${PLATFORMS[@]}"; do
IFS='/' read -r GOOS GOARCH <<< "$platform"
OUTPUT_NAME="${PROJECT}-${VERSION}-${GOOS}-${GOARCH}"
echo "Building for $GOOS/$GOARCH..."
BUILD_TIME="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
LDFLAGS="-X main.Version=${VERSION} -X main.GitCommit=${{ github.sha }} -X main.BuildTime=${BUILD_TIME}"
if ! (cd "$PROJECT" && GOOS="$GOOS" GOARCH="$GOARCH" go build -ldflags="$LDFLAGS" -o "$DIST_DIR/$OUTPUT_NAME" "$BUILD_TARGET"); then
echo "Failed to build for $GOOS/$GOARCH" >&2
exit 1
fi
echo "Built: dist/$OUTPUT_NAME"
done
ls -lh dist/
env:
MATRIX_PROJECT: ${{ matrix.project }}
STEPS_VERSION_OUTPUTS_VERSION: ${{ steps.version.outputs.version }}
- name: Package Homebrew archives
if: ${{ (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.ref == 'refs/heads/main' && contains(fromJson(needs.detect.outputs.mirror_projects), matrix.project) }}
run: |
set -euo pipefail
PROJECT="${MATRIX_PROJECT}"
VERSION="${STEPS_VERSION_OUTPUTS_VERSION}"
for binary in dist/"${PROJECT}-${VERSION}-"*; do
if [[ ! -f "$binary" ]]; then
continue
fi
if [[ "$binary" == *.tar.gz ]]; then
continue
fi
prefix="dist/${PROJECT}-${VERSION}-"
platform="${binary#"$prefix"}"
tmpdir="$(mktemp -d)"
cp "$binary" "$tmpdir/${PROJECT}"
tarball="dist/${PROJECT}-${VERSION}-${platform}.tar.gz"
(
cd "$tmpdir"
tar -czf "$OLDPWD/$tarball" "${PROJECT}"
)
rm -rf "$tmpdir"
echo "Created $tarball"
done
env:
MATRIX_PROJECT: ${{ matrix.project }}
STEPS_VERSION_OUTPUTS_VERSION: ${{ steps.version.outputs.version }}
- name: Create Release
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1
with:
tag_name: ${{ steps.version.outputs.tag }}
name: "${{ matrix.project }} ${{ steps.version.outputs.version }}"
body: |
Release of **${{ matrix.project }}** version `${{ steps.version.outputs.version }}`
Built from: ${{ github.event_name == 'pull_request' && format('PR #{0}', github.event.pull_request.number) || 'main branch' }}
Commit: ${{ github.sha }}
## Installation
Download the appropriate binary for your platform, make it executable, and move it to your PATH:
```bash
# Linux AMD64
wget https://github.com/${{ github.repository }}/releases/download/${{ steps.version.outputs.tag }}/${{ matrix.project }}-${{ steps.version.outputs.version }}-linux-amd64
chmod +x ${{ matrix.project }}-${{ steps.version.outputs.version }}-linux-amd64
sudo mv ${{ matrix.project }}-${{ steps.version.outputs.version }}-linux-amd64 /usr/local/bin/${{ matrix.project }}
# macOS ARM64 (M1/M2)
wget https://github.com/${{ github.repository }}/releases/download/${{ steps.version.outputs.tag }}/${{ matrix.project }}-${{ steps.version.outputs.version }}-darwin-arm64
chmod +x ${{ matrix.project }}-${{ steps.version.outputs.version }}-darwin-arm64
sudo mv ${{ matrix.project }}-${{ steps.version.outputs.version }}-darwin-arm64 /usr/local/bin/${{ matrix.project }}
```
## Available Binaries
- Linux: amd64, arm64
- macOS: amd64 (Intel), arm64 (Apple Silicon)
files: dist/*
draft: false
prerelease: ${{ github.event_name == 'pull_request' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Checkout Homebrew tap repository
if: ${{ (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.ref == 'refs/heads/main' && contains(fromJson(needs.detect.outputs.mirror_projects), matrix.project) }}
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
repository: ${{ needs.detect.outputs.mirror_repo }}
token: ${{ secrets.PUBLIC_RELEASE_TOKEN }}
path: tap
persist-credentials: false
- name: Update Homebrew formula
if: ${{ (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.ref == 'refs/heads/main' && contains(fromJson(needs.detect.outputs.mirror_projects), matrix.project) }}
working-directory: tap
env:
PROJECT: ${{ matrix.project }}
VERSION: ${{ steps.version.outputs.version }}
TAG: ${{ steps.version.outputs.tag }}
MIRROR_REPO: ${{ needs.detect.outputs.mirror_repo }}
MIRROR_METADATA: ${{ needs.detect.outputs.mirror_metadata }}
SOURCE_REPO: ${{ github.repository }}
run: |
python3 - <<'PY'
import hashlib
import json
import os
from pathlib import Path
project = os.environ["PROJECT"]
version = os.environ["VERSION"]
tag = os.environ["TAG"]
mirror_repo = os.environ["MIRROR_REPO"]
source_repo = os.environ["SOURCE_REPO"]
metadata = json.loads(os.environ["MIRROR_METADATA"])
project_meta = metadata.get(project, {})
binary_name = project_meta.get("binary", project)
def escape(value: str) -> str:
return value.replace("\\", "\\\\").replace("\"", "\\\"")
desc = escape(project_meta.get("desc", f"{project} CLI"))
homepage = escape(project_meta.get("homepage", f"https://github.com/{source_repo}"))
test_args = project_meta.get("test_args", ["--help"])
class_name = project_meta.get("formula_class")
if not class_name:
class_name = "".join(part.capitalize() for part in project.split("-"))
dist_dir = Path(os.getcwd()).parent / "dist"
artifacts = {}
for archive in dist_dir.glob(f"{project}-{version}-*.tar.gz"):
name = archive.name
platform = name.split(f"{project}-{version}-", 1)[1].removesuffix(".tar.gz")
digest = hashlib.sha256(archive.read_bytes()).hexdigest()
artifacts[platform] = {
"filename": name,
"sha256": digest,
}
if not artifacts:
raise SystemExit(f"No packaged Homebrew archives found for {project}")
def url_for(filename: str) -> str:
return f"https://github.com/{source_repo}/releases/download/{tag}/{filename}"
def block_lines(os_name: str, arch: str):
entry = artifacts.get(f"{os_name}-{arch}")
if not entry:
return None
return [
f'url "{url_for(entry["filename"])}"',
f'sha256 "{entry["sha256"]}"',
]
mac_blocks = {
"arm64": block_lines("darwin", "arm64"),
"amd64": block_lines("darwin", "amd64"),
}
linux_blocks = {
"arm64": block_lines("linux", "arm64"),
"amd64": block_lines("linux", "amd64"),
}
lines = [
f"class {class_name} < Formula",
f" desc \"{desc}\"",
f" homepage \"{homepage}\"",
f" version \"{version}\"",
]
def append_os_section(buffer, os_name: str, blocks: dict):
order = ["arm64", "amd64"]
entries = []
for arch_name in order:
block = blocks.get(arch_name)
if not block:
continue
cpu_method = "arm?" if arch_name == "arm64" else "intel?"
entries.append((cpu_method, block))
if not entries:
return
buffer.append(f" on_{os_name} do")
for cpu_method, block in entries:
buffer.append(f" if Hardware::CPU.{cpu_method}")
for line in block:
buffer.append(f" {line}")
buffer.append(" end")
buffer.append(" end")
append_os_section(lines, "macos", mac_blocks)
append_os_section(lines, "linux", linux_blocks)
test_args_ruby = ", ".join([f'"{arg}"' for arg in test_args]) if test_args else '"--help"'
lines.extend([
" def install",
f" bin.install \"{binary_name}\"",
" end",
"",
" test do",
f" system \"#{{bin}}/{binary_name}\", {test_args_ruby}",
" end",
"end",
])
formula_dir = Path("Formula")
formula_dir.mkdir(parents=True, exist_ok=True)
formula_path = formula_dir / f"{project}.rb"
formula_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
PY
- name: Run brew audit
if: ${{ (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.ref == 'refs/heads/main' && contains(fromJson(needs.detect.outputs.mirror_projects), matrix.project) }}
env:
HOMEBREW_NO_AUTO_UPDATE: "1"
MATRIX_PROJECT: ${{ matrix.project }}
run: |
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
brew tap-new --no-git neongreen/mono || true
TAP_DIR="$(brew --repository)/Library/Taps/neongreen/homebrew-mono"
rm -rf "$TAP_DIR"
ln -s "${GITHUB_WORKSPACE}/tap" "$TAP_DIR"
brew audit --formula "neongreen/mono/${MATRIX_PROJECT}"
- name: Commit Homebrew tap update
if: ${{ (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.ref == 'refs/heads/main' && contains(fromJson(needs.detect.outputs.mirror_projects), matrix.project) }}
working-directory: tap
env:
PROJECT: ${{ matrix.project }}
VERSION: ${{ steps.version.outputs.version }}
run: |
set -euo pipefail
git config user.name "mono-release-bot"
git config user.email "mono-release-bot@example.com"
git fetch origin
git checkout main
git pull --rebase origin main
git add "Formula/${PROJECT}.rb"
if git diff --staged --quiet; then
echo "No Homebrew changes to commit"
exit 0
fi
git commit -m "brew: update ${PROJECT} to ${VERSION}"
git push origin HEAD:main