Release 2.0.0 — explicit monorepo_url, drop first-target-host inference
Breaking change. The monorepo's gitea host was previously inferred from the first target's subrepo_url, silently coupling the two and breaking on any multi-host setup (monorepo on host A, target on host B). v2.0.0 replaces the inference with a required josh.monorepo_url field that mirrors subrepo_url — same parsing, same SSH/HTTPS auth options, same shape. - schema_version: 2 is now required; v1 configs are rejected with a clear migration error - josh.monorepo_url is required; josh.monorepo_auth is optional (default https) - mono_auth_url() in lib/auth.sh mirrors subrepo_auth_url() - MONOREPO_API derives from monorepo_url's host instead of .[0].gitea_host; env-var override preserved as escape hatch - initial_import() and force-push call mono_auth_url() instead of building URLs inline - ADR-012 documents the rationale; CHANGELOG includes migration snippet Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
24
lib/auth.sh
24
lib/auth.sh
@@ -2,7 +2,8 @@
|
||||
# lib/auth.sh — Authenticated URLs, remote queries, and PR creation
|
||||
#
|
||||
# Requires: lib/core.sh and lib/config.sh sourced first
|
||||
# Expects: JOSH_PROXY_URL, MONOREPO_PATH, BOT_USER, GITEA_TOKEN, JOSH_FILTER,
|
||||
# Expects: JOSH_PROXY_URL, MONOREPO_PATH, MONOREPO_URL, MONOREPO_AUTH,
|
||||
# BOT_USER, GITEA_TOKEN, JOSH_FILTER,
|
||||
# SUBREPO_URL, SUBREPO_AUTH, SUBREPO_TOKEN (set by parse_config + load_target)
|
||||
|
||||
# ─── Josh-Proxy Auth URL ───────────────────────────────────────────
|
||||
@@ -29,13 +30,24 @@ subrepo_auth_url() {
|
||||
}
|
||||
|
||||
# ─── Monorepo Auth URL ─────────────────────────────────────────────
|
||||
# Derives a push-capable HTTPS URL from MONOREPO_API.
|
||||
# MONOREPO_API shape: https://<host>/api/v1/repos/<org/repo>
|
||||
# HTTPS: injects user:token into MONOREPO_URL (auto-converts git@ form to https)
|
||||
# SSH: returns bare MONOREPO_URL (auth via user's ssh-agent / ~/.ssh/config,
|
||||
# or via GIT_SSH_COMMAND if the caller wired one up for the monorepo host)
|
||||
|
||||
mono_auth_url() {
|
||||
local api_host_path
|
||||
api_host_path=$(echo "$MONOREPO_API" | sed 's|https://||; s|/api/v1/repos/|/|')
|
||||
echo "https://${BOT_USER}:${GITEA_TOKEN}@${api_host_path}.git"
|
||||
if [ "${MONOREPO_AUTH:-https}" = "ssh" ]; then
|
||||
echo "$MONOREPO_URL"
|
||||
else
|
||||
local https_url="$MONOREPO_URL"
|
||||
# Convert git@host:org/repo.git → https://host/org/repo.git so token injection works
|
||||
if [[ "$https_url" == git@* ]]; then
|
||||
https_url=$(echo "$https_url" | sed -E 's|^git@([^:]+):(.+)$|https://\1/\2|')
|
||||
elif [[ "$https_url" == ssh://* ]]; then
|
||||
https_url=$(echo "$https_url" | sed -E 's|^ssh://[^@]*@([^/]+)/(.+)$|https://\1/\2|')
|
||||
fi
|
||||
# shellcheck disable=SC2001
|
||||
echo "$https_url" | sed "s|https://|https://${BOT_USER}:${GITEA_TOKEN}@|"
|
||||
fi
|
||||
}
|
||||
|
||||
# ─── Remote Queries ─────────────────────────────────────────────────
|
||||
|
||||
@@ -16,16 +16,21 @@ parse_config() {
|
||||
local config_json
|
||||
config_json=$(yq -o json "$config_file") || die "Failed to parse ${config_file}"
|
||||
|
||||
# Schema version check (optional field — absent means v1)
|
||||
# Schema version check — required, must be 2 (v1 configs are no longer supported)
|
||||
local schema_version
|
||||
schema_version=$(echo "$config_json" | jq -r '.schema_version // 1')
|
||||
[ "$schema_version" = "1" ] || die "Unsupported schema_version ${schema_version} in ${config_file} (this version of josh-sync supports schema_version 1 — see CHANGELOG.md for migration instructions)"
|
||||
schema_version=$(echo "$config_json" | jq -r '.schema_version // empty')
|
||||
[ -n "$schema_version" ] || die "schema_version missing in ${config_file} (required since v2.0.0 — set 'schema_version: 2' at the top of the file; see CHANGELOG.md for migration instructions)"
|
||||
[ "$schema_version" = "2" ] || die "Unsupported schema_version ${schema_version} in ${config_file} (this version of josh-sync requires schema_version 2 — see CHANGELOG.md for migration instructions)"
|
||||
|
||||
# Export global values
|
||||
export JOSH_PROXY_URL
|
||||
JOSH_PROXY_URL=$(echo "$config_json" | jq -r '.josh.proxy_url')
|
||||
export MONOREPO_PATH
|
||||
MONOREPO_PATH=$(echo "$config_json" | jq -r '.josh.monorepo_path')
|
||||
export MONOREPO_URL
|
||||
MONOREPO_URL=$(echo "$config_json" | jq -r '.josh.monorepo_url')
|
||||
export MONOREPO_AUTH
|
||||
MONOREPO_AUTH=$(echo "$config_json" | jq -r '.josh.monorepo_auth // "https"')
|
||||
export BOT_NAME
|
||||
BOT_NAME=$(echo "$config_json" | jq -r '.bot.name')
|
||||
export BOT_EMAIL
|
||||
@@ -35,8 +40,27 @@ parse_config() {
|
||||
|
||||
[ -n "$JOSH_PROXY_URL" ] && [ "$JOSH_PROXY_URL" != "null" ] || die "josh.proxy_url missing in config"
|
||||
[ -n "$MONOREPO_PATH" ] && [ "$MONOREPO_PATH" != "null" ] || die "josh.monorepo_path missing in config"
|
||||
[ -n "$MONOREPO_URL" ] && [ "$MONOREPO_URL" != "null" ] || die "josh.monorepo_url is required (added in schema_version 2). Set it to your monorepo's git clone URL, e.g. 'git@code.example.io:org/repo.git'."
|
||||
[ "$MONOREPO_AUTH" = "ssh" ] || [ "$MONOREPO_AUTH" = "https" ] || die "josh.monorepo_auth must be 'ssh' or 'https' (got '${MONOREPO_AUTH}')"
|
||||
[ -n "$BOT_TRAILER" ] && [ "$BOT_TRAILER" != "null" ] || die "bot.trailer missing in config"
|
||||
|
||||
# Derive monorepo gitea_host and repo_path from monorepo_url (mirrors target derivation below)
|
||||
local monorepo_meta
|
||||
monorepo_meta=$(jq -nr --arg url "$MONOREPO_URL" '
|
||||
$url as $u |
|
||||
if ($u | test("^ssh://")) then
|
||||
($u | capture("ssh://[^@]*@(?<h>[^/]+)/(?<p>.+?)(\\.git)?$") // {h: "", p: ""})
|
||||
elif ($u | test("^git@")) then
|
||||
($u | capture("git@(?<h>[^:/]+)[:/](?<p>.+?)(\\.git)?$") // {h: "", p: ""})
|
||||
elif ($u | test("^https?://")) then
|
||||
($u | capture("https?://(?<h>[^/]+)/(?<p>.+?)(\\.git)?$") // {h: "", p: ""})
|
||||
else
|
||||
{h: "", p: ""}
|
||||
end | "\(.h) \(.p)"')
|
||||
export MONOREPO_GITEA_HOST="${monorepo_meta% *}"
|
||||
export MONOREPO_REPO_PATH="${monorepo_meta#* }"
|
||||
[ -n "$MONOREPO_GITEA_HOST" ] || die "Could not derive gitea host from josh.monorepo_url='${MONOREPO_URL}' (expected git@host:org/repo.git or https://host/org/repo.git)"
|
||||
|
||||
# Enrich targets with derived fields (gitea_host, subrepo_repo_path, auto josh_filter)
|
||||
export JOSH_SYNC_TARGETS
|
||||
JOSH_SYNC_TARGETS=$(echo "$config_json" | jq '[.targets[] | . +
|
||||
@@ -73,10 +97,8 @@ parse_config() {
|
||||
export GITEA_TOKEN="${GITEA_TOKEN:-${SYNC_BOT_TOKEN:-}}"
|
||||
export BOT_USER="${BOT_USER:-${SYNC_BOT_USER:-}}"
|
||||
|
||||
# Monorepo API URL (derived from first target's host, overridable via env)
|
||||
local gitea_host
|
||||
gitea_host=$(echo "$JOSH_SYNC_TARGETS" | jq -r '.[0].gitea_host')
|
||||
export MONOREPO_API="${MONOREPO_API:-https://${gitea_host}/api/v1/repos/${MONOREPO_PATH}}"
|
||||
# Monorepo API URL (derived from monorepo_url's host, overridable via env for custom API endpoints)
|
||||
export MONOREPO_API="${MONOREPO_API:-https://${MONOREPO_GITEA_HOST}/api/v1/repos/${MONOREPO_PATH}}"
|
||||
|
||||
log "INFO" "Config loaded: $(echo "$JOSH_SYNC_TARGETS" | jq 'length') target(s)"
|
||||
}
|
||||
|
||||
@@ -475,12 +475,7 @@ initial_import() {
|
||||
log "INFO" "=== Initial import: subrepo/${subrepo_branch} → mono/${mono_branch}:${subfolder}/ ==="
|
||||
|
||||
# 1. Clone monorepo (directly, not through josh — we need the real repo)
|
||||
local mono_auth_url
|
||||
local api_host_path
|
||||
api_host_path=$(echo "$MONOREPO_API" | sed 's|https://||; s|/api/v1/repos/|/|')
|
||||
mono_auth_url="https://${BOT_USER}:${GITEA_TOKEN}@${api_host_path}.git"
|
||||
|
||||
git clone "$mono_auth_url" \
|
||||
git clone "$(mono_auth_url)" \
|
||||
--branch "$mono_branch" --single-branch \
|
||||
"${work_dir}/monorepo" || die "Failed to clone monorepo"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user