fix(#1356): tea login resolution fails closed on a declared git identity (#1361)
ci/woodpecker/push/publish Pipeline was canceled
ci/woodpecker/push/publish Pipeline was canceled
Co-authored-by: fred <[email protected]>
This commit was merged in pull request #1361.
This commit is contained in:
@@ -102,6 +102,36 @@ of their own — `MOSAIC_GIT_IDENTITY=<id>` with a provisioned slot. There is de
|
||||
environment variable that restores the fallback; one would reintroduce exactly the
|
||||
substitution this removes.
|
||||
|
||||
### The tea path: login resolution (#1356)
|
||||
|
||||
The wrappers that go through `tea` (`issue-list.sh`, `pr-list.sh`, `pr-view.sh`,
|
||||
`lane-brief.sh`, and the tea half of `issue-close.sh`) cannot use a token directly: tea
|
||||
0.14 only acts as a **login** already stored in `~/.config/tea/config.yml`. Those wrappers
|
||||
therefore resolve a login name, not a token, and the resolution follows the same identity
|
||||
as above:
|
||||
|
||||
1. Resolve the identity (`MOSAIC_GIT_IDENTITY`, then `git config mosaic.gitIdentity`).
|
||||
2. Derive the Gitea instance from the repo host (`git.mosaicstack.dev` → `mosaicstack`,
|
||||
`git.uscllc.com` → `usc`), or from the owner when `--repo owner/name` is given.
|
||||
3. The canonical login is `<instance>-<identity>`. If tea has it, that login acts.
|
||||
4. If the identity is set but that login is missing, the wrapper **fails closed**: nonzero
|
||||
exit, empty stdout, and a stderr line naming the login it wanted and the source of the
|
||||
identity. When `tea` itself is not installed the message says so instead, since "no such
|
||||
login" would send the reader to create a login they cannot create.
|
||||
5. With **no identity set**, the old host-default behaviour is unchanged (first login
|
||||
configured for that host, else the API fallback).
|
||||
|
||||
Step 4 replaced a fallback that picked any login configured for the host, which meant a
|
||||
seat with no login of its own silently acted as whichever seat had configured one. That
|
||||
satisfied the author≠reviewer gate on paper while one actor held both names.
|
||||
|
||||
**Provisioning the logins.** `tools/fleet/seat-logins.sh` projects each seat's token from
|
||||
its secrets store into tea's config under the canonical name. tea's config is a derived
|
||||
cache of the secrets store: regenerate it with the script, never hand-edit it. Run it with
|
||||
`--seat <seat>` for one seat (all seats when omitted), dry-run by default, `--apply` to write. A hand-made
|
||||
alias holding a seat's token blocks its canonical name (tea refuses one token under two
|
||||
names); `--adopt` renames it.
|
||||
|
||||
### Enabling it for a clone
|
||||
|
||||
The framework installer syncs `git-credential-mosaic` to
|
||||
|
||||
@@ -180,6 +180,66 @@ raise SystemExit(1)
|
||||
PY
|
||||
}
|
||||
|
||||
# Map a host to the instance prefix used in canonical tea login names
|
||||
# ("<instance>-<identity>"). This deliberately mirrors the _idpfx case in
|
||||
# get_gitea_token(): the two credential paths must agree on what a host is called,
|
||||
# or an agent authenticates as itself on one path and as somebody else on the other.
|
||||
gitea_instance_for_host() {
|
||||
case "${1:-}" in
|
||||
git.uscllc.com) echo usc ;;
|
||||
git.mosaicstack.dev) echo mosaicstack ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Resolve the acting git identity, same precedence as get_gitea_token() step 0.
|
||||
# Prints "<identity>\t<source>" so the caller can name the source in an error.
|
||||
resolve_git_identity() {
|
||||
local ident src
|
||||
ident="${MOSAIC_GIT_IDENTITY:-}"
|
||||
src="MOSAIC_GIT_IDENTITY"
|
||||
if [[ -z "$ident" ]]; then
|
||||
ident="$(git config --get mosaic.gitIdentity 2>/dev/null || true)"
|
||||
src="git config mosaic.gitIdentity"
|
||||
fi
|
||||
[[ -n "$ident" ]] || return 1
|
||||
printf '%s\t%s\n' "$ident" "$src"
|
||||
}
|
||||
|
||||
# Map a repo owner to an instance. Used only by the --repo override path, which
|
||||
# has an owner and no host. Previously lived inline in lane-brief.sh; one copy so
|
||||
# the two override callers cannot drift apart.
|
||||
gitea_instance_for_owner() {
|
||||
local owner="${1:-}"
|
||||
owner="${owner%%/*}"
|
||||
case "$owner" in
|
||||
usc|USC) echo usc ;;
|
||||
mosaicstack|mosaic) echo mosaicstack ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Does a login of this name exist at all? The --repo override path cannot check
|
||||
# host agreement, because it has no host.
|
||||
tea_login_exists() {
|
||||
local login_name="$1"
|
||||
local logins_json
|
||||
command -v tea >/dev/null 2>&1 || return 1
|
||||
logins_json=$(tea login list --output json 2>/dev/null) || return 1
|
||||
TEA_LOGINS_JSON="$logins_json" python3 - "$login_name" <<'PY_INNER'
|
||||
import json, os, sys
|
||||
want = sys.argv[1]
|
||||
try:
|
||||
logins = json.loads(os.environ.get("TEA_LOGINS_JSON", "[]"))
|
||||
except Exception:
|
||||
raise SystemExit(1)
|
||||
for login in logins if isinstance(logins, list) else []:
|
||||
if str(login.get("name") or login.get("Name") or "") == want:
|
||||
raise SystemExit(0)
|
||||
raise SystemExit(1)
|
||||
PY_INNER
|
||||
}
|
||||
|
||||
tea_login_matches_host() {
|
||||
local login_name="$1" host="$2"
|
||||
local logins_json
|
||||
@@ -276,6 +336,40 @@ get_gitea_login_for_host() {
|
||||
fi
|
||||
fi
|
||||
|
||||
# IDENTITY LADDER (#1356). Below this point the old code took the FIRST login
|
||||
# matching the host, which is not an identity — with 43 logins on a fleet host,
|
||||
# ~22 match one server, so a seat with no login of its own silently acted as
|
||||
# whichever happened to be first in ~/.config/tea/config.yml. Gate 16 depends on
|
||||
# author != reviewer, and borrowing satisfies it mechanically while violating it
|
||||
# in fact. The token path already refuses to borrow; this is the same refusal.
|
||||
#
|
||||
# Enforced ONLY when an identity is resolvable, exactly like get_gitea_token():
|
||||
# no identity means a human at a terminal, and neither path enforces there.
|
||||
local ident ident_src inst canon
|
||||
if IFS=$'\t' read -r ident ident_src < <(resolve_git_identity); then
|
||||
if inst=$(gitea_instance_for_host "$host"); then
|
||||
canon="${inst}-${ident}"
|
||||
if tea_login_matches_host "$canon" "$host"; then
|
||||
echo "$canon"
|
||||
return 0
|
||||
fi
|
||||
# Say which of the two it is. "No such login" when tea is simply not
|
||||
# installed is a diagnosis of a cause that was never checked, and it
|
||||
# sends the reader off to create a login they cannot create.
|
||||
if ! command -v tea >/dev/null 2>&1; then
|
||||
echo "Error: git identity '$ident' requested (via $ident_src) for host '$host', but tea is not installed," >&2
|
||||
echo " so no login can be resolved. Refusing to guess an identity." >&2
|
||||
return 1
|
||||
fi
|
||||
echo "Error: git identity '$ident' requested (via $ident_src) for host '$host', but no tea login named '$canon' exists." >&2
|
||||
echo " Refusing to borrow another login. Acting as a different identity would satisfy gate 16 mechanically while violating it." >&2
|
||||
echo " Create it with: ~/.config/mosaic/tools/fleet/seat-logins.sh --apply --seat $ident" >&2
|
||||
return 1
|
||||
fi
|
||||
# Identity known but the host is not a Mosaic instance. Fall through: the
|
||||
# canonical name is undefined for it, so there is nothing to enforce.
|
||||
fi
|
||||
|
||||
login=$(find_tea_login_for_host "$host" || true)
|
||||
if [[ -n "$login" ]]; then
|
||||
echo "$login"
|
||||
@@ -351,14 +445,41 @@ raise SystemExit(1)
|
||||
PY
|
||||
}
|
||||
|
||||
# Resolve a login for an explicit --repo override, which supplies an owner and no
|
||||
# host. Takes "owner" or "owner/repo".
|
||||
#
|
||||
# The old body fell through to get_default_tea_login(), which returns the
|
||||
# default-marked login or, failing that, the first login of ANY host — arbitrary
|
||||
# identity, chosen by config file order. That is the #1356 fail-open in its worst
|
||||
# form, because unlike the host path it does not even constrain the server.
|
||||
get_gitea_login_for_repo_override() {
|
||||
local login
|
||||
local owner="${1:-}"
|
||||
local login ident ident_src inst canon
|
||||
|
||||
if [[ -n "${GITEA_LOGIN:-}" ]]; then
|
||||
echo "$GITEA_LOGIN"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if IFS=$'\t' read -r ident ident_src < <(resolve_git_identity); then
|
||||
if inst=$(gitea_instance_for_owner "$owner"); then
|
||||
canon="${inst}-${ident}"
|
||||
if tea_login_exists "$canon"; then
|
||||
echo "$canon"
|
||||
return 0
|
||||
fi
|
||||
echo "Error: git identity '$ident' (via $ident_src) has no tea login '$canon' for owner '${owner%%/*}'." >&2
|
||||
echo " Create it with: ~/.config/mosaic/tools/fleet/seat-logins.sh --apply --seat $ident" >&2
|
||||
return 1
|
||||
fi
|
||||
echo "Error: git identity '$ident' (via $ident_src) is set, but owner '${owner%%/*}' maps to no known instance," >&2
|
||||
echo " so the login name cannot be derived. Refusing to fall back to an arbitrary login." >&2
|
||||
echo " Set GITEA_LOGIN to name the login explicitly." >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# No identity: a human at a terminal. Unchanged, and the same place the token
|
||||
# path stops enforcing.
|
||||
login=$(get_default_tea_login || true)
|
||||
if [[ -n "$login" ]]; then
|
||||
echo "$login"
|
||||
|
||||
@@ -99,7 +99,7 @@ case "$PLATFORM" in
|
||||
;;
|
||||
gitea)
|
||||
if [[ -n "$REPO_OVERRIDE" ]]; then
|
||||
GITEA_LOGIN_NAME=$(get_gitea_login_for_repo_override) || {
|
||||
GITEA_LOGIN_NAME=$(get_gitea_login_for_repo_override "$REPO_OVERRIDE") || {
|
||||
echo "Error: Could not resolve Gitea login for --repo override. Set GITEA_LOGIN or configure a default tea login." >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
@@ -49,11 +49,27 @@ if [[ -z "$LOGIN" ]]; then
|
||||
if [[ -n "${GITEA_LOGIN:-}" ]]; then
|
||||
LOGIN="$GITEA_LOGIN"
|
||||
else
|
||||
case "${REPO%%/*}" in
|
||||
usc|USC) LOGIN=usc ;;
|
||||
mosaicstack|mosaic) LOGIN=mosaicstack ;;
|
||||
*) LOGIN="$(get_gitea_login_for_repo_override 2>/dev/null || true)" ;;
|
||||
esac
|
||||
# #1356: the owner-derived map below picks a SHARED login (bare `usc` /
|
||||
# `mosaicstack`). On a seat that is borrowing another identity, which is
|
||||
# exactly what gate 16 forbids. So the identity ladder goes first and the
|
||||
# map is only the no-identity fallback (a human at a terminal), which is
|
||||
# where the token path stops enforcing too.
|
||||
if LOGIN="$(get_gitea_login_for_repo_override "$REPO")"; then
|
||||
:
|
||||
elif resolve_git_identity >/dev/null 2>&1; then
|
||||
# A git identity IS set and the ladder still could not resolve a login.
|
||||
# The named reason is already on stderr. Falling through to the map here
|
||||
# would hand this seat a SHARED login (bare `usc` / `mosaicstack`) — the
|
||||
# identity-borrowing #1356 exists to stop. Fail closed instead.
|
||||
exit 2
|
||||
else
|
||||
# No identity: a human at a terminal. Owner-derived map, unchanged. This
|
||||
# is the same point at which the token path stops enforcing.
|
||||
case "${REPO%%/*}" in
|
||||
usc|USC) LOGIN=usc ;;
|
||||
mosaicstack|mosaic) LOGIN=mosaicstack ;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
[[ -n "$LOGIN" ]] || { echo "FATAL: could not resolve a Gitea login for $REPO (pass -L or set GITEA_LOGIN)" >&2; exit 2; }
|
||||
|
||||
@@ -94,7 +94,7 @@ case "$PLATFORM" in
|
||||
;;
|
||||
gitea)
|
||||
if [[ -n "$REPO_OVERRIDE" ]]; then
|
||||
GITEA_LOGIN_NAME=$(get_gitea_login_for_repo_override) || {
|
||||
GITEA_LOGIN_NAME=$(get_gitea_login_for_repo_override "$REPO_OVERRIDE") || {
|
||||
echo "Error: Could not resolve Gitea login for --repo override. Set GITEA_LOGIN or configure a default tea login." >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ if [[ "$PLATFORM" == "github" ]]; then
|
||||
gh pr view "$PR_NUMBER" --repo "$REPO_INFO"
|
||||
elif [[ "$PLATFORM" == "gitea" ]]; then
|
||||
if [[ -n "$REPO_OVERRIDE" ]]; then
|
||||
GITEA_LOGIN_NAME=$(get_gitea_login_for_repo_override) || {
|
||||
GITEA_LOGIN_NAME=$(get_gitea_login_for_repo_override "$REPO_OVERRIDE") || {
|
||||
echo "Error: Could not resolve Gitea login for --repo override. Set GITEA_LOGIN or configure a default tea login." >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
@@ -110,8 +110,20 @@ chmod +x "$BIN_DIR/tea" "$BIN_DIR/curl"
|
||||
|
||||
run_in_repo() {
|
||||
(
|
||||
# HERMETICITY, second half (#1356). The empty repo-local `mosaic.gitIdentity`
|
||||
# above pins the git-config route into identity resolution. It does NOT pin
|
||||
# the environment route, and MOSAIC_GIT_IDENTITY is checked FIRST — so on any
|
||||
# provisioned seat, where the launcher exports it, this suite failed before
|
||||
# any change: rc=1 as-is, rc=0 under `env -u MOSAIC_GIT_IDENTITY`, one
|
||||
# variable. A suite that cannot run on a seat cannot guard this code for the
|
||||
# agents that actually run it.
|
||||
#
|
||||
# Unset rather than set empty: an empty MOSAIC_GIT_IDENTITY and an absent one
|
||||
# take different branches in resolve_git_identity(), and the case under test
|
||||
# is "no identity at all".
|
||||
cd "$REPO_DIR"
|
||||
PATH="$BIN_DIR:$PATH" \
|
||||
env -u MOSAIC_GIT_IDENTITY \
|
||||
PATH="${_SANDBOX_BIN:-$BIN_DIR}:$PATH" \
|
||||
HOME="$HOME_DIR" \
|
||||
MOSAIC_CREDENTIALS_FILE="$CREDENTIALS_FILE" \
|
||||
MOSAIC_TEST_LOG="$LOG_FILE" \
|
||||
@@ -307,14 +319,11 @@ SH
|
||||
chmod +x "$BIN_DIR2/tea"
|
||||
|
||||
run_in_repo2() {
|
||||
(
|
||||
cd "$REPO_DIR"
|
||||
PATH="$BIN_DIR2:$PATH" \
|
||||
HOME="$HOME_DIR" \
|
||||
MOSAIC_CREDENTIALS_FILE="$CREDENTIALS_FILE" \
|
||||
MOSAIC_TEST_LOG="$LOG_FILE" \
|
||||
"$@"
|
||||
)
|
||||
# Same sandbox as run_in_repo, different mock tea (BIN_DIR2 defines a
|
||||
# mosaicstack login). This MUST delegate rather than re-implement: it was a
|
||||
# copy once, and the copy silently missed the MOSAIC_GIT_IDENTITY unset, so
|
||||
# the suite kept failing on a seat after run_in_repo was already fixed.
|
||||
_SANDBOX_BIN="$BIN_DIR2" run_in_repo "$@"
|
||||
}
|
||||
|
||||
git -C "$REPO_DIR" remote set-url origin https://git.mosaicstack.dev/mosaicstack/stack.git
|
||||
@@ -340,6 +349,123 @@ if [[ "$override_wins" != "mosaicstack" ]]; then
|
||||
fi
|
||||
git -C "$REPO_DIR" remote set-url origin https://git.uscllc.com/USC/uconnect.git
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# #1356: the git-identity ladder. A seat declares who it is (MOSAIC_GIT_IDENTITY
|
||||
# or `git config mosaic.gitIdentity`); resolution must use THAT seat's login and
|
||||
# must REFUSE to borrow another one when it is absent. Silently borrowing
|
||||
# satisfies gate 16 mechanically (a review exists) while violating it (the
|
||||
# reviewer and the author are the same actor under two names).
|
||||
#
|
||||
# BIN_DIR3 mocks a tea that holds a canonical per-seat login, which is what a
|
||||
# projected seat looks like. BIN_DIR2 (mosaicstack only) is reused as the
|
||||
# "seat has no login" case — no third mock needed for the negative branch.
|
||||
# ---------------------------------------------------------------------------
|
||||
BIN_DIR3="$WORK_DIR/bin3"
|
||||
mkdir -p "$BIN_DIR3"
|
||||
cp "$BIN_DIR/curl" "$BIN_DIR3/curl"
|
||||
cat > "$BIN_DIR3/tea" <<'SH'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
if [[ "$*" == "login list --output json" ]]; then
|
||||
cat <<'JSON'
|
||||
[
|
||||
{"name":"mosaicstack","url":"https://git.mosaicstack.dev","user":"ci-bot"},
|
||||
{"name":"mosaicstack-testseat","url":"https://git.mosaicstack.dev","user":"testseat"},
|
||||
{"name":"usc","url":"https://git.uscllc.com","user":"ci-bot"}
|
||||
]
|
||||
JSON
|
||||
exit 0
|
||||
fi
|
||||
printf 'tea %s\n' "$*" >> "$MOSAIC_TEST_LOG"
|
||||
exit 0
|
||||
SH
|
||||
chmod +x "$BIN_DIR3/tea"
|
||||
|
||||
run_in_repo3() { _SANDBOX_BIN="$BIN_DIR3" run_in_repo "$@"; }
|
||||
|
||||
git -C "$REPO_DIR" remote set-url origin https://git.mosaicstack.dev/mosaicstack/stack.git
|
||||
|
||||
# Branch 1 (host path): identity set, canonical login PRESENT -> that login wins
|
||||
# over the shared `mosaicstack` one, which is what host-matching alone would pick.
|
||||
ladder_hit=$(run_in_repo3 env MOSAIC_GIT_IDENTITY=testseat bash -c '
|
||||
source "'"$SCRIPT_DIR"'/detect-platform.sh"
|
||||
get_gitea_login_for_host git.mosaicstack.dev
|
||||
')
|
||||
if [[ "$ladder_hit" != "mosaicstack-testseat" ]]; then
|
||||
echo "Expected identity ladder to select 'mosaicstack-testseat'; got '$ladder_hit'" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# CONTROL for branch 1: the same mock, no identity, must still resolve by host.
|
||||
# Without this, branch 1 passing proves nothing about the ladder specifically --
|
||||
# it would also pass if the code just picked the last matching login.
|
||||
ladder_none=$(run_in_repo3 bash -c '
|
||||
source "'"$SCRIPT_DIR"'/detect-platform.sh"
|
||||
get_gitea_login_for_host git.mosaicstack.dev
|
||||
')
|
||||
if [[ "$ladder_none" != "mosaicstack" ]]; then
|
||||
echo "Expected no-identity host resolution to stay 'mosaicstack'; got '$ladder_none'" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Branch 2 (host path): identity set, canonical login ABSENT -> fail closed with a
|
||||
# named error. Two assertions, and they are not the same one twice: rc!=0 proves
|
||||
# it refused, and the ABSENCE of any login on stdout proves it did not borrow the
|
||||
# `mosaicstack` login that is sitting right there matching the host.
|
||||
ladder_err=$(run_in_repo2 env MOSAIC_GIT_IDENTITY=testseat bash -c '
|
||||
source "'"$SCRIPT_DIR"'/detect-platform.sh"
|
||||
get_gitea_login_for_host git.mosaicstack.dev
|
||||
' 2>&1 1>/dev/null || true)
|
||||
ladder_out=$(run_in_repo2 env MOSAIC_GIT_IDENTITY=testseat bash -c '
|
||||
source "'"$SCRIPT_DIR"'/detect-platform.sh"
|
||||
get_gitea_login_for_host git.mosaicstack.dev
|
||||
' 2>/dev/null || true)
|
||||
if [[ -n "$ladder_out" ]]; then
|
||||
echo "Identity ladder BORROWED login '$ladder_out' instead of failing closed" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! grep -q "mosaicstack-testseat" <<<"$ladder_err"; then
|
||||
echo "Expected the error to name the login it wanted; got: $ladder_err" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Branch 3: `git config mosaic.gitIdentity` is the second rung and must work when
|
||||
# the environment variable is absent -- a seat may be configured either way.
|
||||
git -C "$REPO_DIR" config mosaic.gitIdentity testseat
|
||||
ladder_gitcfg=$(run_in_repo3 bash -c '
|
||||
source "'"$SCRIPT_DIR"'/detect-platform.sh"
|
||||
get_gitea_login_for_host git.mosaicstack.dev
|
||||
')
|
||||
git -C "$REPO_DIR" config --unset mosaic.gitIdentity || true
|
||||
if [[ "$ladder_gitcfg" != "mosaicstack-testseat" ]]; then
|
||||
echo "Expected git-config identity rung to select 'mosaicstack-testseat'; got '$ladder_gitcfg'" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Branch 4 (--repo override path): same rule, owner-derived instead of host-derived.
|
||||
override_ladder=$(run_in_repo3 env MOSAIC_GIT_IDENTITY=testseat bash -c '
|
||||
source "'"$SCRIPT_DIR"'/detect-platform.sh"
|
||||
get_gitea_login_for_repo_override mosaicstack/stack
|
||||
')
|
||||
if [[ "$override_ladder" != "mosaicstack-testseat" ]]; then
|
||||
echo "Expected --repo override ladder to select 'mosaicstack-testseat'; got '$override_ladder'" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Branch 5: explicit GITEA_LOGIN outranks the ladder. An operator naming a login
|
||||
# by hand is a deliberate act, not an accident to be second-guessed.
|
||||
override_explicit=$(run_in_repo3 env MOSAIC_GIT_IDENTITY=testseat GITEA_LOGIN=mosaicstack bash -c '
|
||||
source "'"$SCRIPT_DIR"'/detect-platform.sh"
|
||||
get_gitea_login_for_repo_override mosaicstack/stack
|
||||
')
|
||||
if [[ "$override_explicit" != "mosaicstack" ]]; then
|
||||
echo "Expected explicit GITEA_LOGIN to outrank the identity ladder; got '$override_explicit'" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git -C "$REPO_DIR" remote set-url origin https://git.uscllc.com/USC/uconnect.git
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# #865 Blocker 1 & 2: get_gitea_token_for_login must resolve the SAME token as
|
||||
# PyYAML would (or fail closed identically) even when PyYAML is ABSENT, and must
|
||||
|
||||
@@ -76,7 +76,22 @@ exit 0
|
||||
EOF
|
||||
chmod +x "$MOCK_BIN/tea"
|
||||
}
|
||||
# #1356: login resolution is now identity-aware, so the tea-branch fixture must
|
||||
# offer the login the RUNNER's identity resolves to; otherwise every case below
|
||||
# fails closed before reaching the branch under test.
|
||||
#
|
||||
# This does NOT make the suite hermetic, and it is not trying to. The API-path
|
||||
# cases (5-7) need a usable Gitea token, and with an identity set the token path
|
||||
# reads that seat's credential file rather than the GITEA_TOKEN exported above.
|
||||
# So this suite passes only where the runner owns a real credential for its own
|
||||
# identity, and fails with no identity at all -- on this branch and on its base
|
||||
# alike. That is a pre-existing hole in the fixture, filed separately; pinning a
|
||||
# synthetic identity here would only convert it into a confident-looking green.
|
||||
_LOGIN_IDENT="${MOSAIC_GIT_IDENTITY:-}"
|
||||
LOGIN_JSON='[{"name":"git.mosaicstack.dev","url":"https://git.mosaicstack.dev"}]'
|
||||
if [[ -n "$_LOGIN_IDENT" ]]; then
|
||||
LOGIN_JSON='[{"name":"mosaicstack-'"$_LOGIN_IDENT"'","url":"https://git.mosaicstack.dev"},{"name":"git.mosaicstack.dev","url":"https://git.mosaicstack.dev"}]'
|
||||
fi
|
||||
|
||||
# The mocks must be the ones that run. Without this, a failed setup silently falls through
|
||||
# to the real tea/curl and the "test" mutates the real provider.
|
||||
|
||||
@@ -10,6 +10,12 @@
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# HERMETICITY (#1356): this suite's subject is body quoting, not identity. An
|
||||
# ambient MOSAIC_GIT_IDENTITY (every provisioned seat exports one) would make the
|
||||
# identity ladder demand a per-seat login this fixture does not define, and the
|
||||
# suite would fail for a reason it is not testing.
|
||||
unset MOSAIC_GIT_IDENTITY
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
WORK_DIR="${MOSAIC_TEST_WORK_DIR:-$PWD/.mosaic-test-work/issue-create-body-safety}"
|
||||
REPO_DIR="$WORK_DIR/repo"
|
||||
|
||||
Reference in New Issue
Block a user