Files
earthtojake--text-to-cad/scripts/release/create-github-release.sh
T
2026-07-13 13:11:15 +08:00

215 lines
5.8 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd -P)"
BUMP_VERSION="$SCRIPT_DIR/bump-version.sh"
REMOTE="${RELEASE_REMOTE:-origin}"
REPO=""
DRY_RUN=0
SKIP_CHECKS=0
RUN_TESTS=0
SKIP_PUSH=0
SKIP_RELEASE=0
PUBLISH=0
BUMP_ARGS=()
usage() {
cat <<'EOF'
Usage:
scripts/release/create-github-release.sh <major|minor|patch> [options]
scripts/release/create-github-release.sh --set-version X.Y.Z [options]
Runs the repo release-prep flow:
1. calls scripts/release/bump-version.sh
2. bundles generated skill/plugin outputs
3. runs generated-output and plugin checks
4. commits the release version bump
5. creates and pushes the git tag
6. creates a draft GitHub Release for that tag
Options:
--from-version X.Y.Z Pass through to bump-version.sh.
--set-version X.Y.Z Pass through to bump-version.sh instead of bumping a part.
--dry-run Show the planned bump and release steps without writing.
--skip-checks Skip bundle/check scripts after bumping.
--run-tests Run scripts/test/test.sh before committing.
--skip-push Do not push the branch or tag.
--skip-release Do not create the GitHub Release.
--publish Publish the GitHub Release immediately instead of creating a draft.
--remote NAME Git remote to push to. Defaults to origin.
--repo OWNER/REPO GitHub repository for gh release commands.
-h, --help Show this help.
The worktree must be clean before a non-dry-run release prep starts.
This is a manual all-in-one fallback. Prefer the Release GitHub Actions
workflow for normal releases.
EOF
}
die() {
echo "error: $*" >&2
exit 1
}
require_command() {
command -v "$1" >/dev/null 2>&1 || die "$1 is required"
}
while [ "$#" -gt 0 ]; do
case "$1" in
major|minor|patch)
BUMP_ARGS+=("$1")
;;
--from-version|--set-version|--remote|--repo)
[ "$#" -ge 2 ] || die "$1 requires a value"
case "$1" in
--remote)
REMOTE="$2"
;;
--repo)
REPO="$2"
;;
*)
BUMP_ARGS+=("$1" "$2")
;;
esac
shift
;;
--dry-run)
DRY_RUN=1
;;
--skip-checks)
SKIP_CHECKS=1
;;
--run-tests)
RUN_TESTS=1
;;
--skip-push)
SKIP_PUSH=1
;;
--skip-release)
SKIP_RELEASE=1
;;
--publish)
PUBLISH=1
;;
-h|--help)
usage
exit 0
;;
*)
die "unknown argument: $1"
;;
esac
shift
done
cd "$REPO_ROOT"
require_command git
[ -x "$BUMP_VERSION" ] || die "missing executable bump-version script: $BUMP_VERSION"
if [ "$SKIP_PUSH" -eq 1 ] && [ "$SKIP_RELEASE" -eq 0 ]; then
die "--skip-push requires --skip-release because gh release create --verify-tag needs the remote tag"
fi
bump_output="$("$BUMP_VERSION" "${BUMP_ARGS[@]}" --dry-run)"
version_pair="$(printf '%s\n' "$bump_output" | sed -nE 's/^Version bump: ([0-9]+\.[0-9]+\.[0-9]+) -> ([0-9]+\.[0-9]+\.[0-9]+)$/\1 \2/p')"
[ -n "$version_pair" ] || die "could not determine target version from bump-version.sh"
read -r current_version next_version <<<"$version_pair"
if [ "$DRY_RUN" -eq 1 ]; then
printf '%s\n' "$bump_output"
echo ""
echo "Release prep dry run for $current_version -> $next_version:"
echo "- Bundle generated outputs with scripts/bundle/bundle.sh"
if [ "$SKIP_CHECKS" -eq 0 ]; then
echo "- Check generated outputs and plugin metadata with scripts/bundle/bundle.sh --check"
fi
if [ "$RUN_TESTS" -eq 1 ]; then
echo "- Run scripts/test/test.sh"
fi
echo "- Commit canonical release version and bundled outputs as: Release $next_version"
echo "- Create git tag: $next_version"
if [ "$SKIP_PUSH" -eq 0 ]; then
echo "- Push current branch and tag to $REMOTE"
fi
if [ "$SKIP_RELEASE" -eq 0 ]; then
if [ "$PUBLISH" -eq 1 ]; then
echo "- Create published GitHub Release: $next_version"
else
echo "- Create draft GitHub Release: $next_version"
fi
fi
exit 0
fi
branch="$(git symbolic-ref -q --short HEAD || true)"
[ -n "$branch" ] || die "release prep must run on a named branch, not detached HEAD"
if ! git diff --quiet || ! git diff --cached --quiet || [ -n "$(git ls-files --others --exclude-standard)" ]; then
die "working tree must be clean before release prep starts"
fi
if git rev-parse -q --verify "refs/tags/$next_version" >/dev/null; then
die "local tag already exists: $next_version"
fi
if git ls-remote --exit-code --tags "$REMOTE" "refs/tags/$next_version" >/dev/null 2>&1; then
die "remote tag already exists on $REMOTE: $next_version"
else
ls_remote_status=$?
if [ "$ls_remote_status" -ne 2 ]; then
die "failed to check remote tags on $REMOTE"
fi
fi
if [ "$SKIP_RELEASE" -eq 0 ]; then
require_command gh
gh_repo_args=()
if [ -n "$REPO" ]; then
gh_repo_args=(-R "$REPO")
fi
if gh "${gh_repo_args[@]}" release view "$next_version" >/dev/null 2>&1; then
die "GitHub Release already exists: $next_version"
fi
fi
"$BUMP_VERSION" "${BUMP_ARGS[@]}"
"$REPO_ROOT/scripts/bundle/bundle.sh"
if [ "$SKIP_CHECKS" -eq 0 ]; then
"$REPO_ROOT/scripts/bundle/bundle.sh" --check
fi
if [ "$RUN_TESTS" -eq 1 ]; then
"$REPO_ROOT/scripts/test/test.sh"
fi
git add -A
if git diff --cached --quiet; then
die "release prep did not produce any committed changes"
fi
git commit -m "Release $next_version"
git tag "$next_version"
if [ "$SKIP_PUSH" -eq 0 ]; then
git push "$REMOTE" "$branch"
git push "$REMOTE" "$next_version"
fi
if [ "$SKIP_RELEASE" -eq 0 ]; then
release_args=(release create "$next_version" --verify-tag --generate-notes --title "$next_version")
if [ "$PUBLISH" -eq 0 ]; then
release_args+=(--draft)
fi
gh "${gh_repo_args[@]}" "${release_args[@]}"
fi
echo "Prepared release $next_version."