From 13e48672c07ed8ddc01dcf63ae47d3f0bffcda23 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Mon, 27 Jul 2026 23:10:42 -0400 Subject: [PATCH] =?UTF-8?q?packaging:=20bare=20backticks=20=E2=80=94=20a?= =?UTF-8?q?=20heredoc's=20backslash=20isn't=20the=20JSON's?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Run 2981 built everything and then died posting the release: HTTP 422, "invalid escape sequence \`". The body's other backticks are written \` because they sit in an UNQUOTED heredoc, where that backslash is the shell's and is gone before any JSON exists. Copying the idiom into a single-quoted variable changed what it meant — single quotes already stop substitution, so the backslash survived into the body as an escape JSON has no rule for. bash -n passes either way; it checks syntax, not what a string becomes. So parse the assembled body for every branch it can take instead, and write down the recipe next to the one for formatting Rust. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01MKsUY9Z45KQd34V956hZ9Q --- ci-requirements.md | 14 ++++++++++++++ desktop/packaging/publish-release.sh | 6 +++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/ci-requirements.md b/ci-requirements.md index b5d8db7..0489762 100644 --- a/ci-requirements.md +++ b/ci-requirements.md @@ -183,3 +183,17 @@ bash -n desktop/packaging/publish-release.sh Where a script resolves URLs from the Forgejo API, exercise the resolution against the live instance (plain `curl` reads, no install) rather than trusting the regex by eye. Both channel paths in `install.sh` were verified that way. + +**Hand-assembled JSON: parse it before you push it.** `publish-release.sh` builds +its request bodies as shell strings, and quoting context decides what survives +into the JSON — a `` \` `` inside an unquoted heredoc loses its backslash to the +shell, the same `` \` `` inside a single-quoted variable does not, and reaches +Forgejo as an illegal escape (HTTP 422, one wasted build). `sh -n` cannot see +this. Extract the body block and parse it for every branch it can take: + +``` +sed -n '/^# The install command printed/,/^JSON$/p' desktop/packaging/publish-release.sh > /tmp/body.sh +echo ')' >> /tmp/body.sh +bash -c 'GITHUB_SERVER_URL=https://git.fabledsword.com GITHUB_REPOSITORY=o/r \ + TAG=dev RELEASE_PRERELEASE=true; . /tmp/body.sh; printf "%s" "$BODY" | python3 -m json.tool >/dev/null' +``` diff --git a/desktop/packaging/publish-release.sh b/desktop/packaging/publish-release.sh index 3aee7f6..ebcf027 100755 --- a/desktop/packaging/publish-release.sh +++ b/desktop/packaging/publish-release.sh @@ -100,7 +100,11 @@ first_id() { grep -oE '"id"[[:space:]]*:[[:space:]]*[0-9]+' | head -1 | grep -oE # wonders why the version they were sent isn't what they got. if [ "$TAG" = "dev" ]; then INSTALL_TAIL='sh -s -- --channel dev' - CHANNEL_NOTE='\n\nThis is the rolling **dev** channel: republished on every green push to \`dev\`, and pruned to the current build.' + # Backticks BARE, not `\``. The heredoc below is unquoted, so there the backslash + # is the shell's — it suppresses command substitution and never reaches the JSON. + # Here single quotes already do that job, so a backslash would survive into the + # body as `\``, which is not a legal JSON escape: Forgejo answers 422. + CHANNEL_NOTE='\n\nThis is the rolling **dev** channel: republished on every green push to `dev`, and pruned to the current build.' else INSTALL_TAIL='sh' CHANNEL_NOTE=''