#!/usr/bin/env fish # SPDX-FileCopyrightText: Amolith # # SPDX-License-Identifier: CC0-1.0 function __release_git_format_usage echo "Usage: release.fish [--from ] [--only ] [--help] Stages (run in order): tag Compute next version, create and push annotated tag build Cross-compile for all release targets pack Compress Linux binaries with UPX upload Upload artifacts via release(1) Flags: --from Start at this stage and continue through the rest --only Run just this one stage --help, -h Show this help With no flags, all stages run in order (tag → build → pack → upload). The tag stage also notifies the Go module proxy after pushing. Examples: ./release.fish Full release ./release.fish --from build Rebuild, pack, and upload (skip tagging) ./release.fish --only build Just cross-compile ./release.fish --only upload Re-upload existing dist/ artifacts" end set -l targets \ linux/amd64 \ linux/arm64 \ darwin/amd64 \ darwin/arm64 \ windows/amd64 \ freebsd/amd64 # UPX 5.x dropped macOS support; windows/amd64 is PE32 only and # freebsd/amd64 is ELF32 only, so only Linux targets are packed. set -l pack_targets \ linux-amd64 \ linux-arm64 # Global so __should_run can see them. Prefixed to avoid collisions. set -g __rgf_stages tag build pack upload set -g __rgf_from "" set -g __rgf_only "" # --- Parse flags --- set -l i 1 while test $i -le (count $argv) switch $argv[$i] case --from set i (math $i + 1) set -g __rgf_from $argv[$i] case --only set i (math $i + 1) set -g __rgf_only $argv[$i] case --help -h __release_git_format_usage exit 0 case '*' echo "Error: unknown flag '$argv[$i]'" >&2 __release_git_format_usage >&2 exit 1 end set i (math $i + 1) end if test -n "$__rgf_from" -a -n "$__rgf_only" echo "Error: --from and --only are mutually exclusive" >&2 exit 1 end if test -n "$__rgf_from"; and not contains -- "$__rgf_from" $__rgf_stages echo "Error: unknown stage '$__rgf_from' (valid: $__rgf_stages)" >&2 exit 1 end if test -n "$__rgf_only"; and not contains -- "$__rgf_only" $__rgf_stages echo "Error: unknown stage '$__rgf_only' (valid: $__rgf_stages)" >&2 exit 1 end # Returns 0 if the given stage should execute, 1 otherwise. function __should_run -a stage if test -n "$__rgf_only" test "$stage" = "$__rgf_only" return end if test -z "$__rgf_from" return 0 end set -l reached 0 for s in $__rgf_stages if test "$s" = "$__rgf_from" set reached 1 end if test $reached -eq 1 -a "$s" = "$stage" return 0 end end return 1 end # --- Resolve tag --- # # When skipping the tag stage we still need a tag for filenames and # ldflags, so fall back to git describe. set -l tag if __should_run tag # --- Guards --- set -l branch (git symbolic-ref --short HEAD) if test "$branch" != main -a "$branch" != dev echo "Error: not on main or dev branch (on $branch)" >&2 exit 1 end if test (git status --porcelain=2 | wc -l) -ne 0 echo "Error: git working tree is dirty" >&2 exit 1 end # --- Fetch tags --- git fetch soft --tags # --- Compute next version --- set -l current (git describe --tags --abbrev=0 2>/dev/null; or echo v0.0.0) set -l bump (gum choose major minor patch prerelease) # Detect whether the current tag is already a prerelease # (e.g. v1.2.3-rc.4). set -l current_is_prerelease no if string match -rq -- '-[a-zA-Z]+\.[0-9]+$' $current set current_is_prerelease yes end # Ask whether to create a prerelease, unless the user already chose # "prerelease". set -l is_prerelease no if test "$bump" = prerelease set is_prerelease yes else if gum confirm "Create pre-release?" set is_prerelease yes end end # Determine the prerelease suffix (e.g. "beta", "rc"). set -l prerelease_suffix "" if test "$bump" = prerelease -a "$current_is_prerelease" = yes # Reuse the suffix from the current prerelease tag. set prerelease_suffix (string replace -r '.*-([a-zA-Z]+)\.[0-9]+$' '$1' $current) else if test "$is_prerelease" = yes set prerelease_suffix (gum input --placeholder "Enter pre-release suffix (e.g. beta, rc)") if test -z "$prerelease_suffix" echo "Error: pre-release suffix is required" >&2 exit 1 end end # Compute the base version (without prerelease suffix). set -l base_next if test "$bump" = prerelease -a "$current_is_prerelease" = yes # Strip the prerelease suffix to get the base # (e.g. v1.2.3-rc.4 → v1.2.3). set base_next (string replace -r -- '-[a-zA-Z]+\.[0-9]+$' '' $current) else set base_next (svu $bump) end # Compute the suffix version number. set -l suffix_ver "" if test "$is_prerelease" = yes -a -n "$prerelease_suffix" if test "$bump" = prerelease -a "$current_is_prerelease" = yes # Increment the current prerelease number. set -l current_num (string replace -r '.*-[a-zA-Z]+\.([0-9]+)$' '$1' $current) set suffix_ver (math $current_num + 1) else # Find existing tags with this suffix and increment past the # highest. set -l highest (git tag -l "$base_next-$prerelease_suffix.*" \ | string replace -r ".*-$prerelease_suffix\." '' \ | sort -n | tail -1) if test -n "$highest" set suffix_ver (math $highest + 1) else set suffix_ver 0 end end end # Assemble the final tag. if test "$is_prerelease" = yes -a -n "$prerelease_suffix" set tag "$base_next-$prerelease_suffix.$suffix_ver" else set tag "$base_next" end # --- Confirm --- read -P "Release $tag? [y/N] " confirm if test "$confirm" != y -a "$confirm" != Y echo Aborted. exit 0 end # --- Tag and push --- git tag -a $tag; or begin echo "Error: tagging failed" >&2 exit 1 end git push soft $tag; or begin echo "Error: tag push failed — deleting local tag" >&2 git tag -d $tag 2>/dev/null exit 1 end echo "Released $tag" # --- Notify Go module proxy --- go list -m git.secluded.site/git-format@$tag >/dev/null; or begin echo "Warning: module proxy notification failed" >&2 end else set tag (git describe --tags --always 2>/dev/null; or echo dev) end # --- Build --- if __should_run build set -l ldflags "-s -w -X main.version=$tag" rm -rf dist mkdir -p dist for target in $targets set -l os (echo $target | cut -d/ -f1) set -l arch (echo $target | cut -d/ -f2) set -l ext "" if test "$os" = windows set ext .exe end echo "Building $os/$arch..." env CGO_ENABLED=0 GOOS=$os GOARCH=$arch \ go build -o "dist/git-format-$tag-$os-$arch$ext" -ldflags "$ldflags"; or begin echo "Error: build failed for $os/$arch" >&2 exit 1 end end end # --- Pack --- if __should_run pack for suffix in $pack_targets set -l bin "dist/git-format-$tag-$suffix" if test -f "$bin" echo "Packing $suffix..." upx -q "$bin" end end end # --- Upload --- if __should_run upload fish -c "release upload git-format $tag --latest dist/*" end