diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d500652..ebf9be7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -489,14 +489,40 @@ jobs: api="$FORGE/api/v1/repos/$REPO" auth="Authorization: token $TOKEN" + # ◆ THE FIRST "id" IN THE DOCUMENT, AND NOT THE LAST ONE A GREEDY MATCH LANDS ON. + # + # This was `sed -n 's/.*"id":\([0-9]*\).*/\1/p'`, and the leading .* is greedy, so it walked past + # the release's own id to the last "id": in the response — which belongs to the embedded author + # object and is -2. [0-9]* then matched no digits at all and the answer was the empty string. A + # release had been created and the step failed saying none had. + # + # It broke both readings, and the other one silently: the existing-release lookup below got the + # same empty id, so the delete never fired, so the second run would have failed to create a + # release for a tag that already had one. A rolling channel that works exactly once. + # + # grep matches left to right and [0-9]\+ needs a digit, so the author's -2 cannot match at all + # and head -1 takes the release's. jq would say this in four characters and is not on this + # runner; anything parsing JSON with a regex should say which assumption it is making, and this + # one is: the release id is the first "id": with digits after it. + release_id() { + grep -oE '"id":[0-9]+' | head -1 | cut -d: -f2 + } + # Deleted and recreated rather than updated in place. A rolling tag has to move, and moving one # through this API is two calls with no atomic form either way — so the shape with the fewest # states is to remove both and make them again. The window where no nightly exists is a few # seconds and the client's answer to it is the same as to an unreachable forge: try later. existing="$(curl -fsS -H "$auth" "$api/releases/tags/nightly" 2>/dev/null || true)" if [ -n "$existing" ]; then - id="$(printf '%s' "$existing" | sed -n 's/.*"id":\([0-9]*\).*/\1/p' | head -1)" - [ -n "$id" ] && curl -fsS -X DELETE -H "$auth" "$api/releases/$id" >/dev/null || true + id="$(printf '%s' "$existing" | release_id)" + if [ -n "$id" ]; then + echo "Removing the previous nightly release $id" + curl -fsS -X DELETE -H "$auth" "$api/releases/$id" >/dev/null || true + else + echo "A nightly release exists and its id could not be read:" >&2 + printf '%s\n' "$existing" >&2 + exit 1 + fi fi curl -fsS -X DELETE -H "$auth" "$api/tags/nightly" >/dev/null 2>&1 || true @@ -505,7 +531,7 @@ jobs: "$GITHUB_SHA" "$VERSION" "$GITHUB_SHA")" \ "$api/releases")" - release="$(printf '%s' "$created" | sed -n 's/.*"id":\([0-9]*\).*/\1/p' | head -1)" + release="$(printf '%s' "$created" | release_id)" if [ -z "$release" ]; then echo "Gitea accepted the release call and returned no id:" >&2 printf '%s\n' "$created" >&2 @@ -515,14 +541,31 @@ jobs: # The APK first and the manifest last, which is the ordering the client depends on: it reads the # manifest and then fetches what the manifest names, so a manifest published before its APK is a # few seconds in which every phone is told to download something that is not there yet. + names="" for file in "$STAGED"/*.apk "$STAGED"/android-nightly.json; do - echo "Uploading $(basename "$file")" + name="$(basename "$file")" + echo "Uploading $name ($(wc -c < "$file") bytes)" curl -fsS -X POST -H "$auth" \ -F "attachment=@$file" \ - "$api/releases/$release/assets?name=$(basename "$file")" >/dev/null + "$api/releases/$release/assets?name=$name" >/dev/null + names="$names $name" done - echo "Published nightly $VERSION" + # Asked for back rather than assumed, because the failure this whole channel is exposed to is a + # release that exists and carries nothing — which is what a phone sees as a feed it can read and + # never update from, and what a person sees as a release page offering source tarballs. That is + # exactly what the run before this one left behind, and it reported success for every upload it + # never made. + published="$(curl -fsS -H "$auth" "$api/releases/$release")" + for name in $names; do + if ! printf '%s' "$published" | grep -qF "\"name\":\"$name\""; then + echo "The release was created but $name is not on it:" >&2 + printf '%s\n' "$published" >&2 + exit 1 + fi + done + + echo "Published nightly $VERSION with$names" image: name: api image