first commit
This commit is contained in:
+367
@@ -0,0 +1,367 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
VERSION_PATH="plugins/cad/VERSION"
|
||||
VERSION_FILE="$REPO_ROOT/$VERSION_PATH"
|
||||
|
||||
PART=""
|
||||
SET_VERSION=""
|
||||
FROM_VERSION=""
|
||||
DRY_RUN=0
|
||||
CHECK=0
|
||||
CHECK_INCREMENTED_FROM=""
|
||||
COMMIT=0
|
||||
AMEND=0
|
||||
NO_EDIT=0
|
||||
NO_VERIFY=0
|
||||
SIGNOFF=0
|
||||
TAG=0
|
||||
FORCE_TAG=0
|
||||
MESSAGES=()
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage:
|
||||
scripts/release/bump-version.sh major|minor|patch [options]
|
||||
scripts/release/bump-version.sh --set-version X.Y.Z [options]
|
||||
scripts/release/bump-version.sh --check
|
||||
scripts/release/bump-version.sh --check-incremented-from REF
|
||||
|
||||
Bumps or checks the canonical repo release version in plugins/cad/VERSION.
|
||||
Duplicate package and plugin metadata is synchronized from that file by
|
||||
scripts/release/sync-version.mjs during release preparation and production
|
||||
bundling.
|
||||
|
||||
Common options:
|
||||
--dry-run Show planned edits and git actions without changing files.
|
||||
--no-commit Write files but do not commit. This is the default.
|
||||
--commit Commit the bump.
|
||||
--amend Amend the current commit instead of creating a new commit.
|
||||
-m, --message MSG Pass a commit message to git commit. May be repeated.
|
||||
--no-edit Reuse the current commit message with --amend.
|
||||
--no-verify Pass --no-verify to git commit.
|
||||
--signoff Pass --signoff to git commit.
|
||||
--no-tag Do not create a local tag. This is the default.
|
||||
--tag Create a local tag after committing. Manual fallback only.
|
||||
--force-tag Move an existing local release tag to HEAD.
|
||||
--from-version X Require the current canonical version to be X.Y.Z.
|
||||
EOF
|
||||
}
|
||||
|
||||
die() {
|
||||
echo "error: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
validate_semver() {
|
||||
local version="$1"
|
||||
if [[ ! "$version" =~ ^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]]; then
|
||||
die "expected a plain semver version like 1.2.3, got '$version'"
|
||||
fi
|
||||
}
|
||||
|
||||
read_version() {
|
||||
local version
|
||||
[ -f "$VERSION_FILE" ] || die "missing canonical version file: $VERSION_PATH"
|
||||
version="$(tr -d '[:space:]' < "$VERSION_FILE")"
|
||||
validate_semver "$version"
|
||||
printf '%s\n' "$version"
|
||||
}
|
||||
|
||||
bump_version() {
|
||||
local current="$1"
|
||||
local part="$2"
|
||||
local major minor patch
|
||||
validate_semver "$current"
|
||||
IFS=. read -r major minor patch <<< "$current"
|
||||
case "$part" in
|
||||
major)
|
||||
printf '%s.0.0\n' "$((10#$major + 1))"
|
||||
;;
|
||||
minor)
|
||||
printf '%s.%s.0\n' "$major" "$((10#$minor + 1))"
|
||||
;;
|
||||
patch)
|
||||
printf '%s.%s.%s\n' "$major" "$minor" "$((10#$patch + 1))"
|
||||
;;
|
||||
*)
|
||||
die "unknown bump part: $part"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
semver_greater() {
|
||||
local left="$1"
|
||||
local right="$2"
|
||||
local left_major left_minor left_patch right_major right_minor right_patch
|
||||
validate_semver "$left"
|
||||
validate_semver "$right"
|
||||
IFS=. read -r left_major left_minor left_patch <<< "$left"
|
||||
IFS=. read -r right_major right_minor right_patch <<< "$right"
|
||||
|
||||
if [ "$((10#$left_major))" -ne "$((10#$right_major))" ]; then
|
||||
[ "$((10#$left_major))" -gt "$((10#$right_major))" ]
|
||||
return
|
||||
fi
|
||||
if [ "$((10#$left_minor))" -ne "$((10#$right_minor))" ]; then
|
||||
[ "$((10#$left_minor))" -gt "$((10#$right_minor))" ]
|
||||
return
|
||||
fi
|
||||
[ "$((10#$left_patch))" -gt "$((10#$right_patch))" ]
|
||||
}
|
||||
|
||||
git_text_at_ref() {
|
||||
local ref="$1"
|
||||
[ -n "$ref" ] || die "base ref must not be empty"
|
||||
if [[ "$ref" =~ ^0+$ ]]; then
|
||||
die "base ref must be a real commit, not an empty all-zero ref"
|
||||
fi
|
||||
git -C "$REPO_ROOT" show "$ref:$VERSION_PATH"
|
||||
}
|
||||
|
||||
stage_paths() {
|
||||
git -C "$REPO_ROOT" add -- "$VERSION_PATH"
|
||||
}
|
||||
|
||||
commit_version_bump() {
|
||||
local next_version="$1"
|
||||
local command=(commit)
|
||||
|
||||
stage_paths
|
||||
if [ "$AMEND" -eq 1 ]; then
|
||||
command+=(--amend)
|
||||
fi
|
||||
if [ "$NO_VERIFY" -eq 1 ]; then
|
||||
command+=(--no-verify)
|
||||
fi
|
||||
if [ "$SIGNOFF" -eq 1 ]; then
|
||||
command+=(--signoff)
|
||||
fi
|
||||
if [ "$NO_EDIT" -eq 1 ]; then
|
||||
command+=(--no-edit)
|
||||
elif [ "${#MESSAGES[@]}" -gt 0 ]; then
|
||||
local message
|
||||
for message in "${MESSAGES[@]}"; do
|
||||
command+=(-m "$message")
|
||||
done
|
||||
elif [ "$AMEND" -eq 1 ]; then
|
||||
command+=(--no-edit)
|
||||
else
|
||||
command+=(-m "Bump version to $next_version")
|
||||
fi
|
||||
git -C "$REPO_ROOT" "${command[@]}"
|
||||
}
|
||||
|
||||
tag_exists() {
|
||||
git -C "$REPO_ROOT" rev-parse --verify --quiet "refs/tags/$1" >/dev/null
|
||||
}
|
||||
|
||||
create_release_tag() {
|
||||
local tag_name="$1"
|
||||
local tag_commit head_commit
|
||||
if tag_exists "$tag_name"; then
|
||||
tag_commit="$(git -C "$REPO_ROOT" rev-list -n 1 "$tag_name")"
|
||||
head_commit="$(git -C "$REPO_ROOT" rev-parse HEAD)"
|
||||
if [ "$tag_commit" = "$head_commit" ]; then
|
||||
echo "Release tag already exists on HEAD: $tag_name"
|
||||
return
|
||||
fi
|
||||
[ "$FORCE_TAG" -eq 1 ] || die "release tag already exists on a different commit: $tag_name"
|
||||
git -C "$REPO_ROOT" tag -f "$tag_name" HEAD
|
||||
echo "Moved release tag: $tag_name"
|
||||
return
|
||||
fi
|
||||
git -C "$REPO_ROOT" tag "$tag_name" HEAD
|
||||
echo "Created release tag: $tag_name"
|
||||
}
|
||||
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
major|minor|patch)
|
||||
[ -z "$PART" ] || die "provide only one semver bump part"
|
||||
PART="$1"
|
||||
;;
|
||||
--set-version)
|
||||
[ "$#" -ge 2 ] || die "--set-version requires a value"
|
||||
SET_VERSION="$2"
|
||||
shift
|
||||
;;
|
||||
--set-version=*)
|
||||
SET_VERSION="${1#--set-version=}"
|
||||
;;
|
||||
--from-version)
|
||||
[ "$#" -ge 2 ] || die "--from-version requires a value"
|
||||
FROM_VERSION="$2"
|
||||
shift
|
||||
;;
|
||||
--from-version=*)
|
||||
FROM_VERSION="${1#--from-version=}"
|
||||
;;
|
||||
--dry-run)
|
||||
DRY_RUN=1
|
||||
;;
|
||||
--check)
|
||||
CHECK=1
|
||||
;;
|
||||
--check-incremented-from)
|
||||
[ "$#" -ge 2 ] || die "--check-incremented-from requires a ref"
|
||||
CHECK=1
|
||||
CHECK_INCREMENTED_FROM="$2"
|
||||
shift
|
||||
;;
|
||||
--check-incremented-from=*)
|
||||
CHECK=1
|
||||
CHECK_INCREMENTED_FROM="${1#--check-incremented-from=}"
|
||||
;;
|
||||
--no-commit)
|
||||
COMMIT=0
|
||||
TAG=0
|
||||
;;
|
||||
--commit)
|
||||
COMMIT=1
|
||||
;;
|
||||
--amend)
|
||||
AMEND=1
|
||||
;;
|
||||
-m|--message)
|
||||
[ "$#" -ge 2 ] || die "$1 requires a value"
|
||||
MESSAGES+=("$2")
|
||||
shift
|
||||
;;
|
||||
--message=*)
|
||||
MESSAGES+=("${1#--message=}")
|
||||
;;
|
||||
--no-edit)
|
||||
NO_EDIT=1
|
||||
;;
|
||||
--no-verify)
|
||||
NO_VERIFY=1
|
||||
;;
|
||||
--signoff)
|
||||
SIGNOFF=1
|
||||
;;
|
||||
--no-tag)
|
||||
TAG=0
|
||||
;;
|
||||
--tag)
|
||||
TAG=1
|
||||
;;
|
||||
--force-tag)
|
||||
FORCE_TAG=1
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
die "unknown argument: $1"
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
cd "$REPO_ROOT"
|
||||
|
||||
if [ "$CHECK" -eq 1 ]; then
|
||||
if [ -n "$PART" ] || [ -n "$SET_VERSION" ] || [ -n "$FROM_VERSION" ] || [ "$DRY_RUN" -eq 1 ] ||
|
||||
[ "$COMMIT" -eq 1 ] || [ "$AMEND" -eq 1 ] || [ "${#MESSAGES[@]}" -gt 0 ] ||
|
||||
[ "$NO_EDIT" -eq 1 ] || [ "$NO_VERIFY" -eq 1 ] || [ "$SIGNOFF" -eq 1 ] ||
|
||||
[ "$TAG" -eq 1 ] || [ "$FORCE_TAG" -eq 1 ]; then
|
||||
die "version bump, git commit, and tag arguments cannot be combined with check modes"
|
||||
fi
|
||||
|
||||
current_version="$(read_version)"
|
||||
if [ -n "$CHECK_INCREMENTED_FROM" ]; then
|
||||
base_version="$(git_text_at_ref "$CHECK_INCREMENTED_FROM" | tr -d '[:space:]')"
|
||||
validate_semver "$base_version"
|
||||
if ! semver_greater "$current_version" "$base_version"; then
|
||||
die "current version $current_version must be greater than $base_version from $CHECK_INCREMENTED_FROM"
|
||||
fi
|
||||
echo "Canonical release version is incremented from $CHECK_INCREMENTED_FROM: $base_version -> $current_version"
|
||||
else
|
||||
echo "Canonical release version is valid: $current_version"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -n "$PART" ] && [ -n "$SET_VERSION" ]; then
|
||||
die "provide exactly one of major/minor/patch or --set-version"
|
||||
fi
|
||||
if [ -z "$PART" ] && [ -z "$SET_VERSION" ]; then
|
||||
die "provide exactly one of major/minor/patch or --set-version"
|
||||
fi
|
||||
if [ -n "$SET_VERSION" ]; then
|
||||
validate_semver "$SET_VERSION"
|
||||
fi
|
||||
if [ -n "$FROM_VERSION" ]; then
|
||||
validate_semver "$FROM_VERSION"
|
||||
fi
|
||||
if [ "$COMMIT" -eq 1 ] && [ "$AMEND" -eq 1 ]; then
|
||||
die "--commit and --amend are mutually exclusive"
|
||||
fi
|
||||
if [ "$NO_EDIT" -eq 1 ] && [ "$AMEND" -eq 0 ]; then
|
||||
die "--no-edit only applies with --amend"
|
||||
fi
|
||||
if [ "$NO_EDIT" -eq 1 ] && [ "${#MESSAGES[@]}" -gt 0 ]; then
|
||||
die "--no-edit cannot be combined with --message"
|
||||
fi
|
||||
if [ "$TAG" -eq 1 ] && [ "$COMMIT" -eq 0 ] && [ "$AMEND" -eq 0 ] && [ "$DRY_RUN" -eq 0 ]; then
|
||||
die "--tag requires --commit or --amend"
|
||||
fi
|
||||
if [ "$FORCE_TAG" -eq 1 ] && [ "$TAG" -eq 0 ]; then
|
||||
die "--force-tag requires --tag"
|
||||
fi
|
||||
|
||||
current_version="$(read_version)"
|
||||
if [ -n "$FROM_VERSION" ] && [ "$current_version" != "$FROM_VERSION" ]; then
|
||||
die "$VERSION_PATH is $current_version, expected $FROM_VERSION"
|
||||
fi
|
||||
|
||||
if [ -n "$SET_VERSION" ]; then
|
||||
next_version="$SET_VERSION"
|
||||
else
|
||||
next_version="$(bump_version "$current_version" "$PART")"
|
||||
fi
|
||||
|
||||
if [ "$next_version" = "$current_version" ]; then
|
||||
die "next version matches current version: $current_version"
|
||||
fi
|
||||
|
||||
echo "Version bump: $current_version -> $next_version"
|
||||
echo "- $VERSION_PATH (canonical plugin version)"
|
||||
|
||||
if [ "$DRY_RUN" -eq 1 ]; then
|
||||
echo "Dry run only; no files changed."
|
||||
if [ "$COMMIT" -eq 1 ] || [ "$AMEND" -eq 1 ]; then
|
||||
if [ "$AMEND" -eq 1 ]; then
|
||||
echo "Would amend the current commit."
|
||||
else
|
||||
echo "Would create a commit."
|
||||
fi
|
||||
fi
|
||||
if [ "$TAG" -eq 1 ]; then
|
||||
echo "Would create release tag: $next_version"
|
||||
fi
|
||||
echo "Release workflow: gh workflow run release.yml"
|
||||
echo "Local fallback: scripts/release/bump-version.sh --set-version $next_version --no-commit"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
printf '%s\n' "$next_version" > "$VERSION_FILE"
|
||||
echo "Updated 1 file."
|
||||
|
||||
if [ "$COMMIT" -eq 1 ] || [ "$AMEND" -eq 1 ]; then
|
||||
commit_version_bump "$next_version"
|
||||
echo "Committed version bump."
|
||||
fi
|
||||
|
||||
if [ "$TAG" -eq 1 ]; then
|
||||
create_release_tag "$next_version"
|
||||
elif [ "$COMMIT" -eq 1 ] || [ "$AMEND" -eq 1 ]; then
|
||||
echo "Release tag to create separately: $next_version"
|
||||
else
|
||||
echo "Release workflow: gh workflow run release.yml"
|
||||
echo "Local fallback: scripts/release/bump-version.sh --set-version $next_version --no-commit"
|
||||
fi
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="${RELEASE_REPO_ROOT:-$(cd "$SCRIPT_DIR/../.." && pwd)}"
|
||||
|
||||
SOURCE_REF="HEAD"
|
||||
TARGET_REF="origin/main"
|
||||
PRINT_PREVIOUS_SOURCE=0
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage:
|
||||
scripts/release/check-publish-source.sh [--source-ref REF] [--target-ref REF]
|
||||
scripts/release/check-publish-source.sh --print-previous-source [--target-ref REF]
|
||||
|
||||
Checks that a publish source contains the source commit used by the previous
|
||||
publish target. This keeps main as a generated-output branch without requiring
|
||||
develop to merge generated publish commits back into source history.
|
||||
|
||||
Options:
|
||||
--source-ref REF Source branch, tag, or SHA to publish. Defaults to HEAD.
|
||||
--target-ref REF Previous publish target ref. Defaults to origin/main.
|
||||
--print-previous-source Print only the resolved previous source commit.
|
||||
-h, --help Show this help.
|
||||
EOF
|
||||
}
|
||||
|
||||
die() {
|
||||
echo "error: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
resolve_commit() {
|
||||
local ref="$1"
|
||||
local label="$2"
|
||||
local commit
|
||||
commit="$(git rev-parse --verify "$ref^{commit}" 2>/dev/null)" ||
|
||||
die "could not resolve $label ref as a commit: $ref"
|
||||
printf '%s\n' "$commit"
|
||||
}
|
||||
|
||||
source_commit_from_message() {
|
||||
local target_commit="$1"
|
||||
local recorded
|
||||
recorded="$(
|
||||
git log -1 --format=%B "$target_commit" |
|
||||
sed -nE 's/^Source commit:[[:space:]]*([0-9a-fA-F]{7,64})[[:space:]]*$/\1/p' |
|
||||
head -n 1
|
||||
)"
|
||||
[ -n "$recorded" ] || return 1
|
||||
resolve_commit "$recorded" "recorded source"
|
||||
}
|
||||
|
||||
previous_source_for_target() {
|
||||
local target_commit="$1"
|
||||
local previous_source
|
||||
|
||||
if previous_source="$(source_commit_from_message "$target_commit")"; then
|
||||
printf '%s\n' "$previous_source"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if git rev-parse --verify --quiet "$target_commit^" >/dev/null; then
|
||||
git rev-parse "$target_commit^"
|
||||
return 0
|
||||
fi
|
||||
|
||||
die "could not determine previous publish source for $TARGET_REF; expected a Source commit line or a parent commit"
|
||||
}
|
||||
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
--source-ref)
|
||||
[ "$#" -ge 2 ] || die "--source-ref requires a value"
|
||||
SOURCE_REF="$2"
|
||||
shift
|
||||
;;
|
||||
--source-ref=*)
|
||||
SOURCE_REF="${1#--source-ref=}"
|
||||
;;
|
||||
--target-ref)
|
||||
[ "$#" -ge 2 ] || die "--target-ref requires a value"
|
||||
TARGET_REF="$2"
|
||||
shift
|
||||
;;
|
||||
--target-ref=*)
|
||||
TARGET_REF="${1#--target-ref=}"
|
||||
;;
|
||||
--print-previous-source)
|
||||
PRINT_PREVIOUS_SOURCE=1
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
die "unknown argument: $1"
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
cd "$REPO_ROOT"
|
||||
|
||||
source_sha="$(resolve_commit "$SOURCE_REF" "source")"
|
||||
target_sha="$(resolve_commit "$TARGET_REF" "target")"
|
||||
previous_source="$(previous_source_for_target "$target_sha")"
|
||||
|
||||
if [ "$PRINT_PREVIOUS_SOURCE" -eq 1 ]; then
|
||||
printf '%s\n' "$previous_source"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! git merge-base --is-ancestor "$previous_source" "$source_sha"; then
|
||||
{
|
||||
echo "previous publish source is not an ancestor of the requested source"
|
||||
echo "target ref: $TARGET_REF"
|
||||
echo "target commit: $target_sha"
|
||||
echo "previous source: $previous_source"
|
||||
echo "requested source: $source_sha"
|
||||
echo
|
||||
echo "Use source_ref=develop after the release PR has merged, or rebase/merge the source branch so it contains the previous release source."
|
||||
} >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Publish source is valid: $source_sha contains previous publish source $previous_source from $TARGET_REF."
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage:
|
||||
scripts/release/check-version.sh [--incremented-from REF]
|
||||
|
||||
Checks that plugins/cad/VERSION contains a valid canonical release version.
|
||||
With --incremented-from, also checks that the current version is greater than
|
||||
the version at REF.
|
||||
|
||||
Options:
|
||||
--incremented-from REF Compare current release version against REF.
|
||||
-h, --help Show this help.
|
||||
EOF
|
||||
}
|
||||
|
||||
BASE_REF=""
|
||||
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
--incremented-from)
|
||||
[ "$#" -ge 2 ] || {
|
||||
echo "--incremented-from requires a ref" >&2
|
||||
exit 2
|
||||
}
|
||||
BASE_REF="$2"
|
||||
shift
|
||||
;;
|
||||
--incremented-from=*)
|
||||
BASE_REF="${1#--incremented-from=}"
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown argument: $1" >&2
|
||||
usage >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
cd "$REPO_ROOT"
|
||||
|
||||
if [ -n "$BASE_REF" ]; then
|
||||
"$SCRIPT_DIR/bump-version.sh" --check-incremented-from "$BASE_REF"
|
||||
else
|
||||
"$SCRIPT_DIR/bump-version.sh" --check
|
||||
fi
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
#!/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."
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd -P)"
|
||||
|
||||
REMOTE="${RELEASE_REMOTE:-origin}"
|
||||
REPO=""
|
||||
DRY_RUN=0
|
||||
CREATE_RELEASE=1
|
||||
DRAFT=1
|
||||
TARGET_REF="HEAD"
|
||||
SKIP_EXISTING_VERSION=0
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage:
|
||||
scripts/release/publish-github-release.sh [options]
|
||||
|
||||
Creates the immutable release identity for the current repo version:
|
||||
|
||||
1. verifies plugins/cad/VERSION contains a valid canonical version
|
||||
2. verifies a new version is greater than the latest local semver tag
|
||||
3. creates and pushes the semver git tag for plugins/cad/VERSION
|
||||
4. creates a GitHub Release for that tag with generated notes
|
||||
|
||||
Options:
|
||||
--target REF Commit/ref to tag. Defaults to HEAD.
|
||||
--remote NAME Git remote to push tags to. Defaults to origin.
|
||||
--repo OWNER/REPO GitHub repository for gh release commands.
|
||||
--dry-run Print planned tag/release actions without writing.
|
||||
--skip-existing-version
|
||||
Exit successfully when the version tag already exists.
|
||||
--skip-release Create/push the tag but do not create a GitHub Release.
|
||||
--draft Create a draft GitHub Release. This is the default.
|
||||
--publish Publish the GitHub Release immediately.
|
||||
-h, --help Show this help.
|
||||
|
||||
The workflow path should run this from the production branch after generated
|
||||
outputs have been validated. Local use is a manual fallback.
|
||||
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
|
||||
--target)
|
||||
[ "$#" -ge 2 ] || die "--target requires a value"
|
||||
TARGET_REF="$2"
|
||||
shift
|
||||
;;
|
||||
--remote)
|
||||
[ "$#" -ge 2 ] || die "--remote requires a value"
|
||||
REMOTE="$2"
|
||||
shift
|
||||
;;
|
||||
--repo)
|
||||
[ "$#" -ge 2 ] || die "--repo requires a value"
|
||||
REPO="$2"
|
||||
shift
|
||||
;;
|
||||
--dry-run)
|
||||
DRY_RUN=1
|
||||
;;
|
||||
--skip-existing-version)
|
||||
SKIP_EXISTING_VERSION=1
|
||||
;;
|
||||
--skip-release)
|
||||
CREATE_RELEASE=0
|
||||
;;
|
||||
--draft)
|
||||
DRAFT=1
|
||||
;;
|
||||
--publish)
|
||||
DRAFT=0
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
die "unknown argument: $1"
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
cd "$REPO_ROOT"
|
||||
|
||||
require_command git
|
||||
|
||||
"$SCRIPT_DIR/check-version.sh"
|
||||
|
||||
version="$(tr -d '[:space:]' < "$REPO_ROOT/plugins/cad/VERSION")"
|
||||
[ -n "$version" ] || die "plugins/cad/VERSION is empty"
|
||||
tag_name="$version"
|
||||
target_commit="$(git rev-parse "$TARGET_REF^{commit}")"
|
||||
latest_tag="$(git tag --list '[0-9]*.[0-9]*.[0-9]*' --sort=-version:refname | head -n 1 || true)"
|
||||
|
||||
tag_commit=""
|
||||
if git rev-parse --verify --quiet "refs/tags/$tag_name" >/dev/null; then
|
||||
tag_commit="$(git rev-list -n 1 "$tag_name")"
|
||||
fi
|
||||
|
||||
if [ -n "$tag_commit" ]; then
|
||||
if [ "$tag_commit" != "$target_commit" ]; then
|
||||
if [ "$SKIP_EXISTING_VERSION" -eq 1 ]; then
|
||||
echo "Release tag already exists for $tag_name at $tag_commit; skipping $target_commit."
|
||||
exit 0
|
||||
fi
|
||||
die "release tag $tag_name already points at $tag_commit, not $target_commit"
|
||||
fi
|
||||
echo "Release tag already points at target commit: $tag_name"
|
||||
else
|
||||
if [ -n "$latest_tag" ]; then
|
||||
"$SCRIPT_DIR/check-version.sh" --incremented-from "refs/tags/$latest_tag"
|
||||
fi
|
||||
if [ "$DRY_RUN" -eq 1 ]; then
|
||||
echo "Would create release tag: $tag_name -> $target_commit"
|
||||
echo "Would push release tag to $REMOTE"
|
||||
else
|
||||
git tag "$tag_name" "$target_commit"
|
||||
git push "$REMOTE" "refs/tags/$tag_name"
|
||||
echo "Created and pushed release tag: $tag_name"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$CREATE_RELEASE" -eq 0 ]; then
|
||||
echo "Skipping GitHub Release creation."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
gh_repo_args=()
|
||||
if [ -n "$REPO" ]; then
|
||||
gh_repo_args=(-R "$REPO")
|
||||
fi
|
||||
|
||||
if [ "$DRY_RUN" -eq 1 ]; then
|
||||
if [ "$DRAFT" -eq 1 ]; then
|
||||
echo "Would create draft GitHub Release: $tag_name"
|
||||
else
|
||||
echo "Would create published GitHub Release: $tag_name"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
require_command gh
|
||||
|
||||
if gh "${gh_repo_args[@]}" release view "$tag_name" >/dev/null 2>&1; then
|
||||
echo "GitHub Release already exists: $tag_name"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
release_args=(release create "$tag_name" --verify-tag --generate-notes --title "$tag_name")
|
||||
if [ "$DRAFT" -eq 1 ]; then
|
||||
release_args+=(--draft)
|
||||
fi
|
||||
gh "${gh_repo_args[@]}" "${release_args[@]}"
|
||||
echo "Created GitHub Release: $tag_name"
|
||||
+262
@@ -0,0 +1,262 @@
|
||||
#!/usr/bin/env node
|
||||
import { readFileSync, writeFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import process from "node:process";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
||||
const repoRoot = path.resolve(scriptDir, "../..");
|
||||
const canonicalVersionPath = "plugins/cad/VERSION";
|
||||
const semverPattern = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
|
||||
|
||||
const jsonTargets = [
|
||||
{ path: "docs/package.json", fields: [["version"]] },
|
||||
{ path: "docs/package-lock.json", fields: [["version"], ["packages", "", "version"]] },
|
||||
{ path: "packages/cadjs/package.json", fields: [["version"]] },
|
||||
{ path: "packages/cadjs/package-lock.json", fields: [["version"], ["packages", "", "version"]] },
|
||||
{ path: "viewer/package.json", fields: [["version"]] },
|
||||
{
|
||||
path: "viewer/package-lock.json",
|
||||
fields: [
|
||||
["version"],
|
||||
["packages", "", "version"],
|
||||
["packages", "packages/cadjs", "version"],
|
||||
],
|
||||
},
|
||||
{ path: "skills/cad-viewer/scripts/viewer/package.json", fields: [["version"]] },
|
||||
{ path: "plugins/cad/skills/cad-viewer/scripts/viewer/package.json", fields: [["version"]] },
|
||||
{ path: "plugins/cad/.claude-plugin/plugin.json", fields: [["version"]] },
|
||||
{ path: "plugins/cad/.codex-plugin/plugin.json", fields: [["version"]] },
|
||||
{ path: ".claude-plugin/marketplace.json", fields: [["version"]], pluginEntries: ["cad"] },
|
||||
{ path: "viewer/packages/cadjs/package.json", fields: [["version"]], required: false },
|
||||
{ path: "viewer/packages/cadjs/package-lock.json", fields: [["version"], ["packages", "", "version"]], required: false },
|
||||
{ path: "skills/cad-viewer/scripts/viewer/packages/cadjs/package.json", fields: [["version"]], required: false },
|
||||
{
|
||||
path: "skills/cad-viewer/scripts/viewer/packages/cadjs/package-lock.json",
|
||||
fields: [["version"], ["packages", "", "version"]],
|
||||
required: false,
|
||||
},
|
||||
{ path: "plugins/cad/skills/cad-viewer/scripts/viewer/packages/cadjs/package.json", fields: [["version"]], required: false },
|
||||
{
|
||||
path: "plugins/cad/skills/cad-viewer/scripts/viewer/packages/cadjs/package-lock.json",
|
||||
fields: [["version"], ["packages", "", "version"]],
|
||||
required: false,
|
||||
},
|
||||
];
|
||||
|
||||
const tomlTargets = [
|
||||
"packages/cadpy/pyproject.toml",
|
||||
"packages/cadpy_metadata/pyproject.toml",
|
||||
"viewer/moveit2_server/pyproject.toml",
|
||||
"viewer/packages/cadpy/pyproject.toml",
|
||||
"skills/cad-viewer/scripts/viewer/moveit2_server/pyproject.toml",
|
||||
"skills/cad-viewer/scripts/viewer/packages/cadpy/pyproject.toml",
|
||||
"skills/cad/scripts/packages/cadpy/pyproject.toml",
|
||||
"skills/sdf/scripts/packages/cadpy_metadata/pyproject.toml",
|
||||
"skills/srdf/scripts/packages/cadpy_metadata/pyproject.toml",
|
||||
"skills/urdf/scripts/packages/cadpy_metadata/pyproject.toml",
|
||||
"plugins/cad/skills/cad/scripts/packages/cadpy/pyproject.toml",
|
||||
"plugins/cad/skills/cad-viewer/scripts/viewer/moveit2_server/pyproject.toml",
|
||||
"plugins/cad/skills/cad-viewer/scripts/viewer/packages/cadpy/pyproject.toml",
|
||||
"plugins/cad/skills/sdf/scripts/packages/cadpy_metadata/pyproject.toml",
|
||||
"plugins/cad/skills/srdf/scripts/packages/cadpy_metadata/pyproject.toml",
|
||||
"plugins/cad/skills/urdf/scripts/packages/cadpy_metadata/pyproject.toml",
|
||||
];
|
||||
|
||||
function usage() {
|
||||
console.log(`Usage:
|
||||
scripts/release/sync-version.mjs [--check]
|
||||
|
||||
Synchronizes duplicate package and plugin metadata versions from
|
||||
plugins/cad/VERSION. Release preparation edits the canonical VERSION file, then
|
||||
stamps derived metadata from it; the bundle script and the Release workflow
|
||||
re-check the same metadata before production output is written.
|
||||
|
||||
Options:
|
||||
--check Fail if derived version metadata is stale.
|
||||
-h, --help`);
|
||||
}
|
||||
|
||||
function parseArgs(argv) {
|
||||
const options = { check: false };
|
||||
for (const arg of argv) {
|
||||
if (arg === "--check") {
|
||||
options.check = true;
|
||||
} else if (arg === "-h" || arg === "--help") {
|
||||
usage();
|
||||
process.exit(0);
|
||||
} else {
|
||||
throw new Error(`unknown argument: ${arg}`);
|
||||
}
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
function repoPath(relativePath) {
|
||||
return path.join(repoRoot, relativePath);
|
||||
}
|
||||
|
||||
function readRequiredText(relativePath) {
|
||||
return readFileSync(repoPath(relativePath), "utf8");
|
||||
}
|
||||
|
||||
function readOptionalText(relativePath, required = true) {
|
||||
try {
|
||||
return readRequiredText(relativePath);
|
||||
} catch (error) {
|
||||
if (!required && error && error.code === "ENOENT") {
|
||||
return null;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function canonicalVersion() {
|
||||
const version = readRequiredText(canonicalVersionPath).trim();
|
||||
if (!semverPattern.test(version)) {
|
||||
throw new Error(`${canonicalVersionPath} must contain a plain X.Y.Z semver version`);
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
function formatJsonPath(parts) {
|
||||
const labels = [];
|
||||
for (const part of parts) {
|
||||
if (part === "") {
|
||||
labels[labels.length - 1] = `${labels[labels.length - 1]}[""]`;
|
||||
} else {
|
||||
labels.push(part);
|
||||
}
|
||||
}
|
||||
return labels.join(".");
|
||||
}
|
||||
|
||||
function syncJsonField(data, fieldPath, version, staleLabels) {
|
||||
let cursor = data;
|
||||
for (const key of fieldPath.slice(0, -1)) {
|
||||
if (!cursor || typeof cursor !== "object" || Array.isArray(cursor) || !(key in cursor)) {
|
||||
throw new Error(`missing JSON path: ${formatJsonPath(fieldPath)}`);
|
||||
}
|
||||
cursor = cursor[key];
|
||||
}
|
||||
const key = fieldPath[fieldPath.length - 1];
|
||||
if (!cursor || typeof cursor !== "object" || Array.isArray(cursor) || !(key in cursor)) {
|
||||
throw new Error(`missing JSON path: ${formatJsonPath(fieldPath)}`);
|
||||
}
|
||||
if (cursor[key] !== version) {
|
||||
cursor[key] = version;
|
||||
staleLabels.push(formatJsonPath(fieldPath));
|
||||
}
|
||||
}
|
||||
|
||||
function syncPluginEntry(data, pluginName, version, staleLabels) {
|
||||
if (!Array.isArray(data.plugins)) {
|
||||
throw new Error("plugins must be an array");
|
||||
}
|
||||
const matches = data.plugins.filter((entry) => entry && typeof entry === "object" && entry.name === pluginName);
|
||||
if (matches.length !== 1) {
|
||||
throw new Error(`expected exactly one plugin entry named ${JSON.stringify(pluginName)}`);
|
||||
}
|
||||
if (matches[0].version !== version) {
|
||||
matches[0].version = version;
|
||||
staleLabels.push(`plugins[${pluginName}].version`);
|
||||
}
|
||||
}
|
||||
|
||||
function syncJsonTarget(target, version) {
|
||||
const text = readOptionalText(target.path, target.required !== false);
|
||||
if (text === null) {
|
||||
return null;
|
||||
}
|
||||
const data = JSON.parse(text);
|
||||
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
||||
throw new Error(`${target.path} must contain a JSON object`);
|
||||
}
|
||||
const staleLabels = [];
|
||||
for (const field of target.fields) {
|
||||
syncJsonField(data, field, version, staleLabels);
|
||||
}
|
||||
for (const pluginName of target.pluginEntries ?? []) {
|
||||
syncPluginEntry(data, pluginName, version, staleLabels);
|
||||
}
|
||||
if (staleLabels.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
path: target.path,
|
||||
labels: staleLabels,
|
||||
text: `${JSON.stringify(data, null, 2)}\n`,
|
||||
};
|
||||
}
|
||||
|
||||
function syncTomlTarget(relativePath, version) {
|
||||
const text = readOptionalText(relativePath);
|
||||
if (text === null) {
|
||||
return null;
|
||||
}
|
||||
const matches = [...text.matchAll(/^(version\s*=\s*)"([^"]+)"/gm)];
|
||||
if (matches.length !== 1) {
|
||||
throw new Error(`${relativePath} must contain exactly one double-quoted version field`);
|
||||
}
|
||||
const match = matches[0];
|
||||
if (match[2] === version) {
|
||||
return null;
|
||||
}
|
||||
const start = match.index + match[1].length + 1;
|
||||
const end = start + match[2].length;
|
||||
return {
|
||||
path: relativePath,
|
||||
labels: ["version"],
|
||||
text: `${text.slice(0, start)}${version}${text.slice(end)}`,
|
||||
};
|
||||
}
|
||||
|
||||
function main() {
|
||||
const options = parseArgs(process.argv.slice(2));
|
||||
const version = canonicalVersion();
|
||||
const changes = [];
|
||||
|
||||
for (const target of jsonTargets) {
|
||||
const change = syncJsonTarget(target, version);
|
||||
if (change) {
|
||||
changes.push(change);
|
||||
}
|
||||
}
|
||||
for (const target of tomlTargets) {
|
||||
const change = syncTomlTarget(target, version);
|
||||
if (change) {
|
||||
changes.push(change);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.check) {
|
||||
if (changes.length > 0) {
|
||||
console.error(`Derived version metadata is stale for ${version}:`);
|
||||
for (const change of changes) {
|
||||
console.error(`- ${change.path} (${change.labels.join(", ")})`);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`Derived version metadata is synced from ${canonicalVersionPath}: ${version}`);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const change of changes) {
|
||||
writeFileSync(repoPath(change.path), change.text, "utf8");
|
||||
}
|
||||
if (changes.length === 0) {
|
||||
console.log(`Derived version metadata already synced from ${canonicalVersionPath}: ${version}`);
|
||||
return;
|
||||
}
|
||||
console.log(`Synced derived version metadata from ${canonicalVersionPath}: ${version}`);
|
||||
for (const change of changes) {
|
||||
console.log(`- ${change.path} (${change.labels.join(", ")})`);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
main();
|
||||
} catch (error) {
|
||||
console.error(`error: ${error.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
Reference in New Issue
Block a user