Add portainer.sh fix-git-auth for stacks with lost GitConfig credentials

The ollama stack's GitConfig.Authentication.Password had gone empty
(same class of issue as the prior Gitea log-poisoning/token-rotation
work), causing redeploy to fail with "Unable to clone git repository
directory". This resupplies repositoryUsername/repositoryPassword
from GITEA_USER/GITEA_TOKEN on redeploy, scoped to a new subcommand
rather than changing default redeploy behavior for every stack.

Claude-Session: https://claude.ai/code/session_01L7Rwa6guD5wK8F8tWQwcJX
This commit is contained in:
2026-09-13 15:38:34 +00:00
parent ad0bfb8146
commit 9b26006597
+57
View File
@@ -130,6 +130,61 @@ print(f'Done. ConfigHash: {hash}')
" "$response"
}
cmd_fix_git_auth() {
local name="${1:-}"
if [[ -z "$name" ]]; then
echo "Usage: $0 fix-git-auth <stack-name>" >&2
exit 1
fi
if [[ -z "${GITEA_USER:-}" || -z "${GITEA_TOKEN:-}" ]]; then
echo "Error: GITEA_USER/GITEA_TOKEN not set in .credentials" >&2
exit 1
fi
echo "Looking up stack '$name'..."
local stack_json
if ! stack_json=$(get_stack_json_by_name "$name"); then
echo "Error: stack '$name' not found" >&2
exit 1
fi
local stack_id
stack_id=$(python3 -c "import json,sys; print(json.loads(sys.argv[1])['Id'])" "$stack_json")
echo "Found stack ID: $stack_id"
# Re-supplies repository credentials that Portainer's stored GitConfig can
# lose (empty Authentication.Password) — the symptom is git/redeploy
# failing with "Unable to clone git repository directory".
local payload
payload=$(GITEA_USER="$GITEA_USER" GITEA_TOKEN="$GITEA_TOKEN" python3 -c "
import json, os, sys
stack = json.loads(sys.argv[1])
env = stack.get('Env') or []
print(json.dumps({
'pullImage': True,
'prune': False,
'env': env,
'repositoryAuthentication': True,
'repositoryUsername': os.environ['GITEA_USER'],
'repositoryPassword': os.environ['GITEA_TOKEN'],
}))
" "$stack_json")
echo "Re-authenticating and redeploying..."
local response
response=$(api_put "stacks/${stack_id}/git/redeploy?endpointId=${ENDPOINT_ID}" "$payload")
python3 -c "
import json, sys
d = json.loads(sys.argv[1])
if 'message' in d and 'Id' not in d:
print('Error:', d['message'])
sys.exit(1)
hash = d.get('GitConfig', {}).get('ConfigHash', 'unknown')[:10]
pw_set = bool(d.get('GitConfig', {}).get('Authentication', {}).get('Password'))
print(f'Done. ConfigHash: {hash}. Credential stored: {pw_set}')
" "$response"
}
cmd_deploy() {
local name="${1:-}"
local compose_path="${2:-}"
@@ -292,6 +347,7 @@ command="${1:-}"
case "$command" in
list) cmd_list ;;
redeploy) cmd_redeploy "${2:-}" ;;
fix-git-auth) cmd_fix_git_auth "${2:-}" ;;
deploy) cmd_deploy "${@:2}" ;;
get-env) cmd_get_env "${2:-}" ;;
set-env) cmd_set_env "${2:-}" "${@:3}" ;;
@@ -304,6 +360,7 @@ case "$command" in
echo " deploy <stack-name> <path> [K=V ...] Create new git-linked stack with optional env vars"
echo " get-env <stack-name> Show env vars for a stack"
echo " set-env <stack-name> KEY=VAL [...] Set env vars (redeploys without new image pull)"
echo " fix-git-auth <stack-name> Re-supply git credentials when redeploy fails with 'Unable to clone'"
exit 1
;;
esac