Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ These rules apply to `--gh-release` and to `git-release-api.sh`.

The package version is the tag and release name, and release notes are generated by GitHub. Alpha, beta, RC, and dev versions are marked as prereleases. Archived repositories are rejected, since GitHub does not allow releases on them. For stable, alpha, beta, and RC versions an existing tag or release stops the operation; those releases and assets are never replaced.

A version ending in `-dev` is a rolling prerelease. The first release creates it like any other release. Each later release moves the tag to the new commit, regenerates the release notes, and replaces the ZIP in the same GitHub release, so its URL stays the same. The new ZIP is uploaded under a temporary name and only replaces the old one after the upload is confirmed. The scripts print `Updated rolling release:` for this path and `Published release:` for a new release. A dry run shows what would change but never touches the tag, release, or asset. If the tag has to move, the dry-run notes still reflect where the tag points now.
A version ending in `-dev` is a rolling prerelease, including a build on the way to an alpha, beta or RC such as `1.1.0-a2-dev`. The first release creates it like any other release. Each later release moves the tag to the new commit, regenerates the release notes, and replaces the ZIP in the same GitHub release, so its URL stays the same. The new ZIP is uploaded under a temporary name and only replaces the old one after the upload is confirmed. The scripts print `Updated rolling release:` for this path and `Published release:` for a new release. A dry run shows what would change but never touches the tag, release, or asset. If the tag has to move, the dry-run notes still reflect where the tag points now.

The tag is reserved at the pinned commit before the release is created. A failure after tag creation may leave that tag without a release. The release stays a draft until its asset upload is confirmed. If uploading or publishing fails, the script reports the release URL and exits with an error. Inspect the remote state before retrying. For stable, alpha, beta, and RC versions a retry stops when it finds the existing tag or release. For a `-dev` version a retry reuses the leftover tag or draft release and finishes the update. A publication timeout may mean publication succeeded but could not be confirmed. Requests have finite timeouts and remote write requests are not retried automatically.

Expand Down
6 changes: 4 additions & 2 deletions git-release-api.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ Stable, alpha, beta, and RC releases are immutable: an existing release or tag
is rejected, and an upload/publication failure leaves the draft release for
inspection.

A version ending in -dev is a rolling prerelease. The first --release creates
A version ending in -dev, such as 1.1.0-dev or 1.1.0-a2-dev, is a rolling
prerelease. The first --release creates
it; each later --release moves its tag to the default branch, regenerates its notes,
and replaces its ZIP in the same release, keeping its URL. --dry-run never
changes the tag, release, or asset.
Expand Down Expand Up @@ -112,7 +113,8 @@ git -C "$scratch/source" show "$commit:composer.json" >"$scratch/composer.json"
package=$(jq -er '.name | select(type == "string")' "$scratch/composer.json")
version=$(jq -er '.version | select(type == "string")' "$scratch/composer.json")
[[ "$package" =~ ^[a-z0-9][a-z0-9._-]*/[a-z0-9][a-z0-9._-]*$ ]] || fail 'composer.json needs a safe vendor/name package name.'
[[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-(dev|a(lpha)?[0-9]*|b(eta)?[0-9]*|[Rr][Cc][0-9]*|p(atch)?[0-9]*))?$ ]] || fail 'composer.json has an unsupported version.'
# An alpha, beta or RC may end in -dev (1.1.0-a2-dev): a rolling build on the way to it.
[[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-(dev|(a(lpha)?|b(eta)?|[Rr][Cc])[0-9]*(-dev)?|p(atch)?[0-9]*))?$ ]] || fail 'composer.json has an unsupported version.'
vendor=${package%%/*}
releasename=${package#*/}
filename="$vendor-$releasename-$version.zip"
Expand Down
18 changes: 17 additions & 1 deletion tests/test-release-api.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ printf '%s\n' '{"name":"acme/ext","version":"1.2.3-dev"}' >"$FIXTURE_ROOT/source
git -C "$FIXTURE_ROOT/source" -c user.name='William Jacoby' -c user.email=bonelifer@gmail.com commit -qam dev
DEV_COMMIT=$(git -C "$FIXTURE_ROOT/source" rev-parse HEAD)
export DEV_COMMIT
# A rolling build on the way to an alpha, also on its own branch.
git -C "$FIXTURE_ROOT/source" checkout -qb alpha-dev main
printf '%s\n' '{"name":"acme/ext","version":"1.2.3-a2-dev"}' >"$FIXTURE_ROOT/source/composer.json"
git -C "$FIXTURE_ROOT/source" -c user.name='William Jacoby' -c user.email=bonelifer@gmail.com commit -qam alpha-dev
ALPHA_DEV_COMMIT=$(git -C "$FIXTURE_ROOT/source" rev-parse HEAD)
export ALPHA_DEV_COMMIT
git -C "$FIXTURE_ROOT/source" checkout -q main
# A dirty local version must never change the remotely committed package version.
printf '%s\n' '{"name":"acme/ext","version":"9.9.9"}' >"$FIXTURE_ROOT/source/composer.json"
Expand Down Expand Up @@ -72,7 +78,9 @@ case "$endpoint" in
repos/acme/ext/commits/main|repos/acme/ext/commits/master)
[[ "$endpoint" == */main || "${SCENARIO:-}" == master_branch ]] || exit 1
[[ "${SCENARIO:-}" != main_failure ]] || exit 1
if [[ "${SCENARIO:-}" == dev_* ]]; then sha="$DEV_COMMIT"; else sha="$FIXTURE_COMMIT"; fi
if [[ "${SCENARIO:-}" == dev_* ]]; then sha="$DEV_COMMIT"
elif [[ "${SCENARIO:-}" == alpha_dev ]]; then sha="$ALPHA_DEV_COMMIT"
else sha="$FIXTURE_COMMIT"; fi
jq -n --arg sha "$sha" '{sha:$sha}' ;;
'repos/acme/ext/releases?per_page=100')
echo '[]'
Expand All @@ -88,6 +96,8 @@ case "$endpoint" in
if [[ "${SCENARIO:-}" == dev_existing ]]; then
jq -n --arg sha "$FIXTURE_COMMIT" '[{ref:"refs/tags/1.2.3-dev",object:{sha:$sha}}]'
else echo '[]'; fi ;;
repos/acme/ext/git/matching-refs/tags/1.2.3-a2-dev)
echo '[]' ;;
repos/acme/ext/git/refs/tags/1.2.3-dev)
jq -n --arg sha "$DEV_COMMIT" '{ref:"refs/tags/1.2.3-dev",object:{sha:$sha}}' ;;
repos/acme/ext/releases/456)
Expand Down Expand Up @@ -220,6 +230,12 @@ grep -q 'Updated rolling release: https://github.com/acme/ext/releases/tag/1.2.3
if grep -q 'Published release' "$FIXTURE_ROOT/stdout"; then exit 1; fi
echo 'ok - dev release moves the tag, regenerates notes, and replaces the ZIP in the same release'

export SCENARIO=alpha_dev
run --dry-run --repo acme/ext --output "$FIXTURE_ROOT/output"
grep -q 'Release: 1.2.3-a2-dev (prerelease: true)' "$FIXTURE_ROOT/stdout"
[[ "$(unzip -p "$FIXTURE_ROOT/output/acme-ext-1.2.3-a2-dev.zip" acme/ext/composer.json | jq -r .version)" == 1.2.3-a2-dev ]]
echo 'ok - an alpha with -dev (1.2.3-a2-dev) is accepted as a rolling prerelease'

export SCENARIO=dev_new
run --release --repo acme/ext --output "$FIXTURE_ROOT/output"
jq -e --arg sha "$DEV_COMMIT" '.draft == true and .prerelease == true and .tag_name == "1.2.3-dev" and .target_commitish == $sha' "$FIXTURE_ROOT/create.json" >/dev/null
Expand Down
Loading