mirror of https://github.com/garrytan/gstack.git
fix(artifacts-init): preserve user-supplied remote URL scheme under --push-protocol auto
The original git_protocol detection keyed only on --host, so it only ran for remotes gstack creates via gh/glab. A URL the user hands us through --remote, an existing origin, or a manual paste fell through to the SSH default — silently rewriting an explicitly-configured HTTPS remote to SSH. That's the July report on #1348: a user whose recovery URL was HTTPS got it converted back to SSH and the push failed. Track where REMOTE_URL came from. Under auto: - explicit / existing / manual: keep the scheme the URL already states (git@|ssh:// -> ssh, http(s):// -> https, anything else preserved verbatim). Never consult git_protocol for a URL the user supplied. - provider (gh/glab-created): honor git_protocol as before, SSH fallback. Explicit --push-protocol https|ssh still overrides everything. The reachability error now has a third branch for pass-through URLs. Refs #1348.
This commit is contained in:
parent
78aaa2017f
commit
c454e2a263
|
|
@ -12,9 +12,11 @@
|
|||
# [--push-protocol auto|https|ssh]
|
||||
#
|
||||
# --push-protocol controls the URL form used for `git push origin`:
|
||||
# auto (default) — honors `gh config get git_protocol` (github) or
|
||||
# `glab config get git_protocol` (gitlab). Falls back to
|
||||
# SSH when neither is set / readable.
|
||||
# auto (default) — for a remote you supplied (--remote, an existing origin, or
|
||||
# a manually pasted URL), keeps that URL's own protocol.
|
||||
# For a remote gstack creates via gh/glab, honors
|
||||
# `gh config get git_protocol` / `glab config get git_protocol`,
|
||||
# falling back to SSH when unset / unreadable.
|
||||
# https — force HTTPS (requires gh/glab credential helper or PAT).
|
||||
# ssh — force SSH (legacy default; requires SSH key on remote).
|
||||
#
|
||||
|
|
@ -52,9 +54,13 @@ REMOTE_URL=""
|
|||
HOST_PREF=""
|
||||
URL_FORM_SUPPORTED="false"
|
||||
PUSH_PROTOCOL="auto"
|
||||
# Where REMOTE_URL came from. Only `provider` (a remote we created via gh/glab)
|
||||
# gets its push form from the CLI's git_protocol — a URL the user handed us
|
||||
# already states its own protocol, and rewriting it is what broke #1348.
|
||||
REMOTE_SOURCE="provider"
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--remote) REMOTE_URL="$2"; shift 2 ;;
|
||||
--remote) REMOTE_URL="$2"; REMOTE_SOURCE="explicit"; shift 2 ;;
|
||||
--host) HOST_PREF="$2"; shift 2 ;;
|
||||
--url-form-supported) URL_FORM_SUPPORTED="$2"; shift 2 ;;
|
||||
--push-protocol)
|
||||
|
|
@ -103,6 +109,7 @@ if command -v glab >/dev/null 2>&1 && glab auth status >/dev/null 2>&1; then gla
|
|||
# ---- choose remote URL ----
|
||||
if [ -z "$REMOTE_URL" ] && [ -n "$EXISTING_REMOTE" ]; then
|
||||
REMOTE_URL="$EXISTING_REMOTE"
|
||||
REMOTE_SOURCE="existing"
|
||||
echo "Using existing remote: $REMOTE_URL"
|
||||
fi
|
||||
|
||||
|
|
@ -178,6 +185,7 @@ if [ -z "$REMOTE_URL" ]; then
|
|||
echo "No URL provided. Aborting." >&2
|
||||
exit 1
|
||||
fi
|
||||
REMOTE_SOURCE="manual"
|
||||
;;
|
||||
*) echo "Unknown --host: $HOST_PREF (expected github|gitlab|manual)" >&2; exit 1 ;;
|
||||
esac
|
||||
|
|
@ -193,42 +201,63 @@ if [ -z "$CANONICAL_HTTPS" ]; then
|
|||
CANONICAL_HTTPS="$REMOTE_URL"
|
||||
fi
|
||||
|
||||
# Resolve push protocol. `auto` honors the user's CLI git_protocol setting
|
||||
# (gh / glab); explicit `https` / `ssh` skips detection. SSH is the historical
|
||||
# default for repeated pushes (no token refresh dance), but HTTPS-configured
|
||||
# users hit a hard wall when we force SSH and they lack an SSH key on the
|
||||
# remote — see issue #1348.
|
||||
# Resolve push protocol. `auto` keys off where the URL came from:
|
||||
#
|
||||
# explicit / existing / manual — the user already chose a protocol by handing
|
||||
# us that URL. Keep it. Rewriting an explicitly-configured HTTPS remote to
|
||||
# SSH is the exact failure reported in #1348, and consulting git_protocol
|
||||
# here would reintroduce it for anyone whose CLI disagrees with their URL.
|
||||
# Forms we don't recognize (local bare paths, file://, self-hosted) pass
|
||||
# through verbatim rather than being coerced.
|
||||
#
|
||||
# provider — we created this remote via gh/glab, so there is no user intent to
|
||||
# preserve. Honor the CLI's git_protocol. Unset/unreadable falls back to SSH,
|
||||
# the historical default, so users who never set git_protocol see no change.
|
||||
#
|
||||
# Explicit `--push-protocol https|ssh` always wins over all of this.
|
||||
RESOLVED_PROTOCOL="$PUSH_PROTOCOL"
|
||||
if [ "$RESOLVED_PROTOCOL" = "auto" ]; then
|
||||
case "$HOST_PREF" in
|
||||
github)
|
||||
DETECTED=$(gh config get git_protocol 2>/dev/null || echo "")
|
||||
case "$REMOTE_SOURCE" in
|
||||
explicit|existing|manual)
|
||||
case "$REMOTE_URL" in
|
||||
git@*|ssh://*) RESOLVED_PROTOCOL="ssh" ;;
|
||||
http://*|https://*) RESOLVED_PROTOCOL="https" ;;
|
||||
*) RESOLVED_PROTOCOL="preserve" ;;
|
||||
esac
|
||||
;;
|
||||
gitlab)
|
||||
DETECTED=$(glab config get git_protocol 2>/dev/null || echo "")
|
||||
provider)
|
||||
case "$HOST_PREF" in
|
||||
github)
|
||||
DETECTED=$(gh config get git_protocol 2>/dev/null || echo "")
|
||||
;;
|
||||
gitlab)
|
||||
DETECTED=$(glab config get git_protocol 2>/dev/null || echo "")
|
||||
;;
|
||||
*)
|
||||
DETECTED=""
|
||||
;;
|
||||
esac
|
||||
case "$DETECTED" in
|
||||
https) RESOLVED_PROTOCOL="https" ;;
|
||||
ssh) RESOLVED_PROTOCOL="ssh" ;;
|
||||
*) RESOLVED_PROTOCOL="ssh" ;; # historical default when unset/unknown
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
DETECTED=""
|
||||
;;
|
||||
esac
|
||||
case "$DETECTED" in
|
||||
https) RESOLVED_PROTOCOL="https" ;;
|
||||
ssh) RESOLVED_PROTOCOL="ssh" ;;
|
||||
*) RESOLVED_PROTOCOL="ssh" ;; # historical default when unset/unknown
|
||||
esac
|
||||
fi
|
||||
|
||||
if [ "$RESOLVED_PROTOCOL" = "https" ]; then
|
||||
PUSH_URL="$CANONICAL_HTTPS"
|
||||
else
|
||||
PUSH_URL=$("$URL_BIN" --to ssh "$CANONICAL_HTTPS" 2>/dev/null || echo "$CANONICAL_HTTPS")
|
||||
fi
|
||||
case "$RESOLVED_PROTOCOL" in
|
||||
preserve) PUSH_URL="$REMOTE_URL" ;;
|
||||
https) PUSH_URL="$CANONICAL_HTTPS" ;;
|
||||
*) PUSH_URL=$("$URL_BIN" --to ssh "$CANONICAL_HTTPS" 2>/dev/null || echo "$CANONICAL_HTTPS") ;;
|
||||
esac
|
||||
|
||||
# ---- verify push URL is reachable ----
|
||||
echo "Verifying remote connectivity: $PUSH_URL"
|
||||
if ! git ls-remote "$PUSH_URL" >/dev/null 2>&1; then
|
||||
if [ "$RESOLVED_PROTOCOL" = "ssh" ]; then
|
||||
cat >&2 <<EOF
|
||||
case "$RESOLVED_PROTOCOL" in
|
||||
ssh)
|
||||
cat >&2 <<EOF
|
||||
Remote not reachable via SSH: $PUSH_URL
|
||||
This could mean:
|
||||
- Wrong URL
|
||||
|
|
@ -237,8 +266,9 @@ This could mean:
|
|||
- You use HTTPS for git (gh config get git_protocol = https) — re-run with --push-protocol https
|
||||
Fix and re-run gstack-artifacts-init.
|
||||
EOF
|
||||
else
|
||||
cat >&2 <<EOF
|
||||
;;
|
||||
https)
|
||||
cat >&2 <<EOF
|
||||
Remote not reachable via HTTPS: $PUSH_URL
|
||||
This could mean:
|
||||
- Wrong URL
|
||||
|
|
@ -246,7 +276,19 @@ This could mean:
|
|||
- Network issue
|
||||
Fix and re-run gstack-artifacts-init.
|
||||
EOF
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
cat >&2 <<EOF
|
||||
Remote not reachable: $PUSH_URL
|
||||
This URL isn't a recognized SSH or HTTPS form, so it was used as given.
|
||||
This could mean:
|
||||
- Wrong URL
|
||||
- Credentials not configured for this remote
|
||||
- Network issue
|
||||
Fix and re-run gstack-artifacts-init, or pin a form with --push-protocol https|ssh.
|
||||
EOF
|
||||
;;
|
||||
esac
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -377,6 +377,51 @@ describe('gstack-artifacts-init push protocol (issue #1348)', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('gstack-artifacts-init explicit-remote protocol preservation (issue #1348)', () => {
|
||||
// The provider-created path (--host github/gitlab) reads git_protocol. A URL
|
||||
// the USER supplies already states its own protocol, so `auto` must keep it
|
||||
// rather than rewrite it. The original fix keyed only on --host, so an
|
||||
// explicit `--remote https://...` with no --host fell through to the SSH
|
||||
// default and silently converted the user's HTTPS URL to SSH.
|
||||
test('--remote HTTPS (no --host) keeps HTTPS instead of defaulting to SSH', () => {
|
||||
makeFakeGh({});
|
||||
const r = run(['--remote', 'https://github.com/testuser/gstack-artifacts-testuser.git']);
|
||||
if (r.status !== 0) console.error('STDERR:', r.stderr);
|
||||
expect(r.status).toBe(0);
|
||||
const remote = spawnSync('git', ['-C', tmpHome, 'remote', 'get-url', 'origin'], { encoding: 'utf-8' }).stdout.trim();
|
||||
expect(remote.startsWith('https://')).toBe(true);
|
||||
expect(remote.startsWith('git@')).toBe(false);
|
||||
});
|
||||
|
||||
test('--remote SSH keeps SSH', () => {
|
||||
makeFakeGh({});
|
||||
const r = run(['--remote', 'git@github.com:testuser/gstack-artifacts-testuser.git']);
|
||||
expect(r.status).toBe(0);
|
||||
const remote = spawnSync('git', ['-C', tmpHome, 'remote', 'get-url', 'origin'], { encoding: 'utf-8' }).stdout.trim();
|
||||
expect(remote.startsWith('git@')).toBe(true);
|
||||
});
|
||||
|
||||
test('explicit HTTPS --remote is NOT overridden by ambient gh git_protocol=ssh', () => {
|
||||
// The strongest form of the regression: even with a git_protocol=ssh config
|
||||
// reachable on PATH, a user-supplied HTTPS URL must win. Consulting config
|
||||
// here is exactly what would reintroduce #1348.
|
||||
makeFakeGh({ gitProtocol: 'ssh' });
|
||||
const r = run(['--remote', 'https://github.com/testuser/gstack-artifacts-testuser.git']);
|
||||
expect(r.status).toBe(0);
|
||||
const remote = spawnSync('git', ['-C', tmpHome, 'remote', 'get-url', 'origin'], { encoding: 'utf-8' }).stdout.trim();
|
||||
expect(remote.startsWith('https://')).toBe(true);
|
||||
expect(remote.startsWith('git@')).toBe(false);
|
||||
});
|
||||
|
||||
test('--push-protocol ssh still overrides an explicit HTTPS --remote', () => {
|
||||
makeFakeGh({});
|
||||
const r = run(['--remote', 'https://github.com/testuser/gstack-artifacts-testuser.git', '--push-protocol', 'ssh']);
|
||||
expect(r.status).toBe(0);
|
||||
const remote = spawnSync('git', ['-C', tmpHome, 'remote', 'get-url', 'origin'], { encoding: 'utf-8' }).stdout.trim();
|
||||
expect(remote.startsWith('git@')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('gstack-artifacts-init idempotency', () => {
|
||||
test('--remote <url> bypasses provider selection entirely', () => {
|
||||
makeFakeGh({});
|
||||
|
|
|
|||
Loading…
Reference in New Issue