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
83 changes: 76 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,10 @@ jobs:

tmp_dir="$(mktemp -d)"
trap 'rm -rf "$tmp_dir"' EXIT
curl -fsSL "$nu_url" -o "$tmp_dir/nu.tar.gz"
# The release lookup above retries; this download did not, and it is the one that
# fails — a 500 from the asset host took the job down with curl's own exit 22 and no
# retry line in the log. curl treats HTTP 500 as transient, so `--retry` covers it.
curl -fsSL --retry 3 --retry-delay 2 "$nu_url" -o "$tmp_dir/nu.tar.gz"
tar -xzf "$tmp_dir/nu.tar.gz" -C "$tmp_dir"
nu_binary="$(find "$tmp_dir" -type f -name nu | head -n 1)"
if [[ -z "$nu_binary" ]]; then
Expand Down Expand Up @@ -258,11 +261,14 @@ jobs:
id: pack
shell: pwsh
run: |
# --no-build, like Test above: packing the same outputs the tests ran against is what lets
# docs/publishing.md promise a release ships exactly what CI exercised. Without it pack is
# free to re-run Build, and a regenerated assembly could reach a package untested.
if ($env:PUBLIC_RELEASE -eq 'true') {
dotnet pack src/Repl.slnx -c Release --no-restore -p:PublicRelease=true -p:WarnOnPackingNonPackableProject=false -o '${{ runner.temp }}/packages'
dotnet pack src/Repl.slnx -c Release --no-build --no-restore -p:PublicRelease=true -p:WarnOnPackingNonPackableProject=false -o '${{ runner.temp }}/packages'
}
else {
dotnet pack src/Repl.slnx -c Release --no-restore -p:WarnOnPackingNonPackableProject=false -o '${{ runner.temp }}/packages'
dotnet pack src/Repl.slnx -c Release --no-build --no-restore -p:WarnOnPackingNonPackableProject=false -o '${{ runner.temp }}/packages'
}

- name: Package readiness report (non-blocking)
Expand Down Expand Up @@ -508,13 +514,76 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
VERSION="${{ needs.build-test-pack.outputs.version }}"
PRERELEASE=""
[[ "$VERSION" == *-* ]] && PRERELEASE="--prerelease"
gh release create "v${VERSION}" packages/* \
--title "v${VERSION}" \
--generate-notes \
$PRERELEASE

# `gh release create` with assets is not one API call: gh's manual (Immutable Releases)
# states it creates the release as a draft, uploads the assets, then publishes it. A run
# interrupted mid-upload leaves an unpublished draft, which `gh release view` still finds
# because its lookup queries drafts too. Reading that as an existing release would skip
# the creation below and let Publish to NuGet run, putting packages on NuGet under a
# version no one can see a release for — so the three states are told apart here. A
# lookup that fails for any other reason reads as `missing` and falls through to
# `gh release create`, which then fails loudly rather than skipping anything.
RELEASE_STATE=missing
if IS_DRAFT="$(gh release view "v${VERSION}" --json isDraft --jq '.isDraft' 2>/dev/null)"; then
RELEASE_STATE=published
if [[ "$IS_DRAFT" == "true" ]]; then
RELEASE_STATE=draft
fi
fi

if [[ "$RELEASE_STATE" == draft ]]; then
# Detection, not repair: publishing the draft and deleting it are both decisions about
# what has already reached consumers, and this job is the one part of the workflow that
# pull-request CI never runs, so untested recovery logic here is worse than stopping.
echo "::error title=Release v${VERSION} exists as an unpublished draft::A previous run created it and did not finish. Nothing was published by this run. Inspect the draft, then either publish it with the packages it is missing or delete it and re-run."
{
echo "### Release \`v${VERSION}\` exists as an unpublished draft"
echo
echo "A previous run created the release and did not finish uploading its assets."
echo "**Nothing was published by this run** — not the release, not NuGet."
echo
echo "Inspect the draft, then either publish it with the packages it is missing or"
echo "delete it and re-run this workflow."
} >> "$GITHUB_STEP_SUMMARY"
exit 1
fi

# A release branch's version has no {height}, so every commit on it computes the same
# number. Creating the release twice is therefore an ordinary occurrence rather than an
# error, and failing here would skip Publish to NuGet for a run that had nothing wrong
# with it. So this path does nothing at all — deliberately, and loudly.
#
# Nothing, rather than re-uploading: a published version is immutable on NuGet, so
# replacing the release's assets would leave a direct GitHub download and a NuGet install
# of the same version carrying different binaries, with the tag describing neither.
# Whatever a repeat run built cannot ship under this version; that needs a version bump.
if [[ "$RELEASE_STATE" == published ]]; then
echo "::warning title=Release v${VERSION} already exists::Its assets and tag are left untouched. NuGet still runs and will skip every version it already has — but it accepts one it is missing, which is how a partial push recovers. If this commit changed shipped code, bump the version on the release branch: the release's own assets will not be updated."
{
echo "### Release \`v${VERSION}\` already exists — left untouched"
echo
echo "Its assets and tag are unchanged. \`Publish to NuGet\` still runs: it skips every"
echo "version already published, which is the normal outcome here, and uploads one that"
echo "is missing, which is how a partially failed push recovers."
echo
echo "So a code change in this commit cannot reach consumers as \`${VERSION}\` through the"
echo "release assets, and reaches NuGet only for a package NuGet does not yet have."
echo "Bump the version on the release branch if the change is meant to ship."
} >> "$GITHUB_STEP_SUMMARY"
else
# --target is what makes the tag point at the commit that built these packages. Without
# it, gh creates a missing tag from the latest state of the default branch, so a
# release/** publish would tag main instead of the release branch it ran on.
gh release create "v${VERSION}" packages/* \
--title "v${VERSION}" \
--target "${GITHUB_SHA}" \
--generate-notes \
Comment thread
carldebilly marked this conversation as resolved.
$PRERELEASE
fi

- name: Publish to NuGet
if: success()
Expand Down
Loading
Loading