fix: accept either bootstrap URL shape, in dotup and in the harness

Three files each appended /bootstrap.env to a value whose shape nobody had
pinned down, and they did not agree:

  - the rotation scripts print the FULL file URL and say to store THAT in
    Bitwarden, so that is what gets pasted
  - dotup asked for the directory and appended /bootstrap.env itself
  - .tests/e2e.sh independently duplicated dotup's assumption

Pasting the saved value made the request .../bootstrap.env/bootstrap.env. The
404 tripped curl --fail and surfaced as "endpoint refused the credentials. The
machine stays public-only" -- blaming the password for a URL shape, which is
about the most expensive wrong error message this path could produce.

dotup and the wrapper now both strip a trailing slash and a trailing
/bootstrap.env before building the request, so either form works and the
existing Bitwarden entry needs no editing.

Four assertions cover all four shapes -- directory and file URL, each with and
without a trailing slash. They eval the normalisation lifted straight out of
dotup rather than a copy, and that binding was mutation-tested: deleting the
line from dotup makes them fail with exactly the production symptom,
https://h/r/bootstrap.env/bootstrap.env. 101 -> 105.

The second instance, in e2e.sh, was found only by running the harness with the
file URL instead of the directory. Every earlier run passed because it was fed
the shape that happened to be in a variable, not the shape a person has in a
password manager. Both shapes now run end to end and pass.

The normalisation exists in two places because the harness cannot source
dotup's internals. That is the same duplication that caused this, and only
dotup's copy is covered by the suite.
This commit is contained in:
bcherb2
2026-08-17 22:52:11 -04:00
parent 2a93bfd5b1
commit 2afdfd093b
3 changed files with 31 additions and 0 deletions
+9
View File
@@ -40,6 +40,15 @@ trap cleanup EXIT INT TERM
printf 'Bootstrap URL (paste from Bitwarden): ' >&2
IFS= read -r BOOT_URL
[ -n "$BOOT_URL" ] || { echo "no URL given"; exit 1; }
# Normalise exactly as dotup does, and for the same reason: the rotation scripts
# print the full file URL, so that is what Bitwarden holds and what gets pasted.
# This wrapper appends /bootstrap.env below, so without stripping it first the
# request becomes .../bootstrap.env/bootstrap.env and the preflight reports
# "wrong password, wrong route, or endpoint down" -- blaming the credentials for
# a URL shape, which is the precise failure dotup was just fixed to stop doing.
# Caught by running this harness with the file URL rather than the directory.
BOOT_URL=${BOOT_URL%/}
BOOT_URL=${BOOT_URL%/bootstrap.env}
printf 'Username [ben]: ' >&2
IFS= read -r BOOT_USER
+15
View File
@@ -162,6 +162,21 @@ is "…and leaves a credential-free remote untouched" \
is "…including scp-style ssh" \
"<none> git@git.example.com:ben/x.git" \
"$(split 'git@git.example.com:ben/x.git')"
# The bootstrap URL a human pastes comes from Bitwarden, where the rotation
# scripts put the FULL file URL. dotup appends /bootstrap.env itself, so without
# normalisation that becomes .../bootstrap.env/bootstrap.env -- a 404 that
# curl --fail turns into "endpoint refused the credentials", blaming the
# password for a URL shape. Both forms must land on the same request.
norm() { P_URL=$1; eval "$(sed -n '/^\tP_URL=\${P_URL%\//p;/^\tP_URL=\${P_URL%\/bootstrap.env}/p' $D)"
printf '%s\n' "${P_URL%/}/bootstrap.env"; }
is "a directory URL resolves to the blob" "https://h/r/bootstrap.env" \
"$(norm 'https://h/r/')"
is "...without a trailing slash too" "https://h/r/bootstrap.env" \
"$(norm 'https://h/r')"
is "...and the full file URL, which is what Bitwarden holds" "https://h/r/bootstrap.env" \
"$(norm 'https://h/r/bootstrap.env')"
is "...even with a trailing slash on the file URL" "https://h/r/bootstrap.env" \
"$(norm 'https://h/r/bootstrap.env/')"
rd() { eval "$(sed -n '/^redact_url()/p' $D)"; redact_url "$1"; }
is "redact_url strips userinfo" "https://<redacted>@git.example.com/x.git" \
"$(rd 'https://ben:deadbeefcafe@git.example.com/x.git')"