fix: bug: setup_forge's admin_token is a local variable, not exported — setup_ops_repo falls back to dev-bot token and fails with 403 (#583)
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful
ci/woodpecker/pr/smoke-init Pipeline was successful

This commit is contained in:
Claude 2026-04-10 14:07:49 +00:00
parent 6305597156
commit e58caa5dfd
3 changed files with 35 additions and 36 deletions

View file

@ -685,7 +685,7 @@ p.write_text(text)
# the calling user (e.g. johba) but the ops repo belongs to disinto-admin. # the calling user (e.g. johba) but the ops repo belongs to disinto-admin.
local ops_slug="disinto-admin/${project_name}-ops" local ops_slug="disinto-admin/${project_name}-ops"
local ops_root="/home/${USER}/${project_name}-ops" local ops_root="/home/${USER}/${project_name}-ops"
setup_ops_repo "$forge_url" "$ops_slug" "$ops_root" "$branch" setup_ops_repo "$forge_url" "$ops_slug" "$ops_root" "$branch" "${HUMAN_TOKEN:-}"
# Migrate ops repo to canonical structure (seed missing directories/files) # Migrate ops repo to canonical structure (seed missing directories/files)
# This brings pre-#407 deployments up to date with the canonical structure # This brings pre-#407 deployments up to date with the canonical structure

View file

@ -247,38 +247,36 @@ setup_forge() {
fi fi
# Get or create human user token # Get or create human user token
local human_token local human_token=""
if curl -sf --max-time 5 -H "Authorization: token ${admin_token}" "${forge_url}/api/v1/users/${human_user}" >/dev/null 2>&1; then # Delete existing human token if present (token sha1 is only returned at creation time)
# Delete existing human token if present (token sha1 is only returned at creation time) local existing_human_token_id
local existing_human_token_id existing_human_token_id=$(curl -sf \
existing_human_token_id=$(curl -sf \ -u "${human_user}:${human_pass}" \
"${forge_url}/api/v1/users/${human_user}/tokens" 2>/dev/null \
| jq -r '.[] | select(.name == "disinto-human-token") | .id') || existing_human_token_id=""
if [ -n "$existing_human_token_id" ]; then
curl -sf -X DELETE \
-u "${human_user}:${human_pass}" \ -u "${human_user}:${human_pass}" \
"${forge_url}/api/v1/users/${human_user}/tokens" 2>/dev/null \ "${forge_url}/api/v1/users/${human_user}/tokens/${existing_human_token_id}" >/dev/null 2>&1 || true
| jq -r '.[] | select(.name == "disinto-human-token") | .id') || existing_human_token_id="" fi
if [ -n "$existing_human_token_id" ]; then
curl -sf -X DELETE \
-u "${human_user}:${human_pass}" \
"${forge_url}/api/v1/users/${human_user}/tokens/${existing_human_token_id}" >/dev/null 2>&1 || true
fi
# Create human token (fresh, so sha1 is returned) # Create human token (fresh, so sha1 is returned)
human_token=$(curl -sf -X POST \ human_token=$(curl -sf -X POST \
-u "${human_user}:${human_pass}" \ -u "${human_user}:${human_pass}" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
"${forge_url}/api/v1/users/${human_user}/tokens" \ "${forge_url}/api/v1/users/${human_user}/tokens" \
-d '{"name":"disinto-human-token","scopes":["all"]}' 2>/dev/null \ -d '{"name":"disinto-human-token","scopes":["all"]}' 2>/dev/null \
| jq -r '.sha1 // empty') || human_token="" | jq -r '.sha1 // empty') || human_token=""
if [ -n "$human_token" ]; then if [ -n "$human_token" ]; then
# Store human token in .env # Store human token in .env
if grep -q '^HUMAN_TOKEN=' "$env_file" 2>/dev/null; then if grep -q '^HUMAN_TOKEN=' "$env_file" 2>/dev/null; then
sed -i "s|^HUMAN_TOKEN=.*|HUMAN_TOKEN=${human_token}|" "$env_file" sed -i "s|^HUMAN_TOKEN=.*|HUMAN_TOKEN=${human_token}|" "$env_file"
else else
printf 'HUMAN_TOKEN=%s\n' "$human_token" >> "$env_file" printf 'HUMAN_TOKEN=%s\n' "$human_token" >> "$env_file"
fi
export HUMAN_TOKEN="$human_token"
echo " Human token saved (HUMAN_TOKEN)"
fi fi
export HUMAN_TOKEN="$human_token"
echo " Human token saved (HUMAN_TOKEN)"
fi fi
# Create bot users and tokens # Create bot users and tokens

View file

@ -5,10 +5,10 @@
# source "$(dirname "$0")/../lib/ops-setup.sh" # source "$(dirname "$0")/../lib/ops-setup.sh"
# #
# Required globals: FORGE_URL, FORGE_TOKEN, FACTORY_ROOT # Required globals: FORGE_URL, FORGE_TOKEN, FACTORY_ROOT
# Optional: admin_token (falls back to FORGE_TOKEN for admin operations) # Optional: HUMAN_TOKEN (falls back to FORGE_TOKEN for admin operations)
# #
# Functions: # Functions:
# setup_ops_repo <forge_url> <ops_slug> <ops_root> [primary_branch] # setup_ops_repo <forge_url> <ops_slug> <ops_root> [primary_branch] [admin_token]
# - Create ops repo on Forgejo if it doesn't exist # - Create ops repo on Forgejo if it doesn't exist
# - Configure bot collaborators with appropriate permissions # - Configure bot collaborators with appropriate permissions
# - Clone or initialize ops repo locally # - Clone or initialize ops repo locally
@ -26,6 +26,7 @@ set -euo pipefail
setup_ops_repo() { setup_ops_repo() {
local forge_url="$1" ops_slug="$2" ops_root="$3" primary_branch="${4:-main}" local forge_url="$1" ops_slug="$2" ops_root="$3" primary_branch="${4:-main}"
local admin_token="${5:-${HUMAN_TOKEN:-${FORGE_TOKEN}}}"
local org_name="${ops_slug%%/*}" local org_name="${ops_slug%%/*}"
local ops_name="${ops_slug##*/}" local ops_name="${ops_slug##*/}"
@ -55,12 +56,12 @@ setup_ops_repo() {
echo "Creating ops repo in namespace: ${org_name}" echo "Creating ops repo in namespace: ${org_name}"
# Create org if it doesn't exist # Create org if it doesn't exist
curl -sf -X POST \ curl -sf -X POST \
-H "Authorization: token ${admin_token:-${FORGE_TOKEN}}" \ -H "Authorization: token ${admin_token}" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
"${forge_url}/api/v1/orgs" \ "${forge_url}/api/v1/orgs" \
-d "{\"username\":\"${org_name}\",\"visibility\":\"public\"}" >/dev/null 2>&1 || true -d "{\"username\":\"${org_name}\",\"visibility\":\"public\"}" >/dev/null 2>&1 || true
if curl -sf -X POST \ if curl -sf -X POST \
-H "Authorization: token ${admin_token:-${FORGE_TOKEN}}" \ -H "Authorization: token ${admin_token}" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
"${forge_url}/api/v1/orgs/${org_name}/repos" \ "${forge_url}/api/v1/orgs/${org_name}/repos" \
-d "{\"name\":\"${ops_name}\",\"auto_init\":true,\"default_branch\":\"${primary_branch}\",\"description\":\"Operational data for ${org_name}/${ops_name%-ops}\"}" >/dev/null 2>&1; then -d "{\"name\":\"${ops_name}\",\"auto_init\":true,\"default_branch\":\"${primary_branch}\",\"description\":\"Operational data for ${org_name}/${ops_name%-ops}\"}" >/dev/null 2>&1; then
@ -70,7 +71,7 @@ setup_ops_repo() {
# Fallback: use admin API to create repo under the target namespace # Fallback: use admin API to create repo under the target namespace
http_code=$(curl -s -o /dev/null -w "%{http_code}" \ http_code=$(curl -s -o /dev/null -w "%{http_code}" \
-X POST \ -X POST \
-H "Authorization: token ${admin_token:-${FORGE_TOKEN}}" \ -H "Authorization: token ${admin_token}" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
"${forge_url}/api/v1/admin/users/${org_name}/repos" \ "${forge_url}/api/v1/admin/users/${org_name}/repos" \
-d "{\"name\":\"${ops_name}\",\"auto_init\":true,\"default_branch\":\"${primary_branch}\",\"description\":\"Operational data for ${org_name}/${ops_name%-ops}\"}" 2>/dev/null || echo "0") -d "{\"name\":\"${ops_name}\",\"auto_init\":true,\"default_branch\":\"${primary_branch}\",\"description\":\"Operational data for ${org_name}/${ops_name%-ops}\"}" 2>/dev/null || echo "0")
@ -104,7 +105,7 @@ setup_ops_repo() {
for bot_user in "${!bot_permissions[@]}"; do for bot_user in "${!bot_permissions[@]}"; do
bot_perm="${bot_permissions[$bot_user]}" bot_perm="${bot_permissions[$bot_user]}"
if curl -sf -X PUT \ if curl -sf -X PUT \
-H "Authorization: token ${admin_token:-${FORGE_TOKEN}}" \ -H "Authorization: token ${admin_token}" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
"${forge_url}/api/v1/repos/${actual_ops_slug}/collaborators/${bot_user}" \ "${forge_url}/api/v1/repos/${actual_ops_slug}/collaborators/${bot_user}" \
-d "{\"permission\":\"${bot_perm}\"}" >/dev/null 2>&1; then -d "{\"permission\":\"${bot_perm}\"}" >/dev/null 2>&1; then
@ -116,7 +117,7 @@ setup_ops_repo() {
# Add disinto-admin as admin collaborator # Add disinto-admin as admin collaborator
if curl -sf -X PUT \ if curl -sf -X PUT \
-H "Authorization: token ${admin_token:-${FORGE_TOKEN}}" \ -H "Authorization: token ${admin_token}" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
"${forge_url}/api/v1/repos/${actual_ops_slug}/collaborators/disinto-admin" \ "${forge_url}/api/v1/repos/${actual_ops_slug}/collaborators/disinto-admin" \
-d '{"permission":"admin"}' >/dev/null 2>&1; then -d '{"permission":"admin"}' >/dev/null 2>&1; then