fix(ci): strip CR/LF from registry secrets before use

The release run built its push URL and auth header directly from
`secrets.REGISTRY_USER` / `REGISTRY_TOKEN`. A trailing newline in
REGISTRY_USER produced a malformed remote:

    warning: url contains a newline in its username component
    fatal: credential url cannot be parsed

Pass the secrets through `env:` and strip CR/LF (`tr -d '\r\n'`) before
building the URL (push step) and the `Authorization: token` header (release
step). Using env instead of inline `${{ }}` also stops a newline from
breaking the shell script itself. Verified the file still parses and the
trim turns `Whitlocktech\n` into a clean single-line URL.

Underlying cause is secret hygiene (the value was saved with a trailing
newline); this makes the workflow robust to it either way.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0114TpmrNW4wNXsHq5CR72jQ
This commit is contained in:
2026-07-14 12:09:46 -05:00
parent 09c59b256e
commit 0550129f8e

View File

@@ -200,14 +200,23 @@ jobs:
# ── RELEASE ENGINE: commit the bump, tag, push ─────────────────────── # ── RELEASE ENGINE: commit the bump, tag, push ───────────────────────
- name: Commit version bump and push tag - name: Commit version bump and push tag
if: ${{ steps.plan.outputs.release == 'true' }} if: ${{ steps.plan.outputs.release == 'true' }}
env:
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
run: | run: |
set -euo pipefail set -euo pipefail
VERSION="${{ steps.plan.outputs.version }}" VERSION="${{ steps.plan.outputs.version }}"
TAG="${{ steps.plan.outputs.tag }}" TAG="${{ steps.plan.outputs.tag }}"
# Secrets can arrive with a trailing newline (depending on how they were
# pasted); a stray CR/LF corrupts the remote URL ("credential url cannot
# be parsed"). Strip line breaks before building the URL. Passing them via
# env (not inline ${{ }}) also keeps a newline from breaking this script.
CI_USER="$(printf '%s' "${REGISTRY_USER}" | tr -d '\r\n')"
CI_TOKEN="$(printf '%s' "${REGISTRY_TOKEN}" | tr -d '\r\n')"
git config user.name "uo-link-ci" git config user.name "uo-link-ci"
git config user.email "ci@whitlocktech.com" git config user.email "ci@whitlocktech.com"
git remote set-url origin \ git remote set-url origin \
"https://${{ secrets.REGISTRY_USER }}:${{ secrets.REGISTRY_TOKEN }}@${GITEA_HOST}/${REPO}.git" "https://${CI_USER}:${CI_TOKEN}@${GITEA_HOST}/${REPO}.git"
git add "${WORKDIR}/Cargo.toml" "${WORKDIR}/Cargo.lock" git add "${WORKDIR}/Cargo.toml" "${WORKDIR}/Cargo.lock"
if ! git diff --cached --quiet; then if ! git diff --cached --quiet; then
@@ -222,14 +231,19 @@ jobs:
# ── RELEASE ENGINE: create the Gitea release + upload assets ───────── # ── RELEASE ENGINE: create the Gitea release + upload assets ─────────
- name: Create Gitea release and upload assets - name: Create Gitea release and upload assets
if: ${{ steps.plan.outputs.release == 'true' }} if: ${{ steps.plan.outputs.release == 'true' }}
env:
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
run: | run: |
set -euo pipefail set -euo pipefail
TAG="${{ steps.plan.outputs.tag }}" TAG="${{ steps.plan.outputs.tag }}"
API="https://${GITEA_HOST}/api/v1/repos/${REPO}" API="https://${GITEA_HOST}/api/v1/repos/${REPO}"
BODY="$(cat dist/CHANGELOG.md)" BODY="$(cat dist/CHANGELOG.md)"
# Same newline hygiene as the push step: a stray CR/LF in the token would
# corrupt the Authorization header.
CI_TOKEN="$(printf '%s' "${REGISTRY_TOKEN}" | tr -d '\r\n')"
REL_ID="$(curl -sSf -X POST "${API}/releases" \ REL_ID="$(curl -sSf -X POST "${API}/releases" \
-H "Authorization: token ${{ secrets.REGISTRY_TOKEN }}" \ -H "Authorization: token ${CI_TOKEN}" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d "$(jq -n --arg tag "$TAG" --arg body "$BODY" \ -d "$(jq -n --arg tag "$TAG" --arg body "$BODY" \
'{tag_name:$tag, name:$tag, body:$body, draft:false, prerelease:false}')" \ '{tag_name:$tag, name:$tag, body:$body, draft:false, prerelease:false}')" \
@@ -238,7 +252,7 @@ jobs:
for f in "${BIN}-linux-x86_64" "${BIN}-windows-x86_64.exe" SHA256SUMS; do for f in "${BIN}-linux-x86_64" "${BIN}-windows-x86_64.exe" SHA256SUMS; do
curl -sSf -X POST "${API}/releases/${REL_ID}/assets?name=${f}" \ curl -sSf -X POST "${API}/releases/${REL_ID}/assets?name=${f}" \
-H "Authorization: token ${{ secrets.REGISTRY_TOKEN }}" \ -H "Authorization: token ${CI_TOKEN}" \
-F "attachment=@dist/${f}" >/dev/null -F "attachment=@dist/${f}" >/dev/null
echo " uploaded ${f}" echo " uploaded ${f}"
done done