From e58caa5dfd72b500e596b3cdf8f8796e85502a20 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 10 Apr 2026 14:07:49 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20bug:=20setup=5Fforge's=20`admin=5Ftoken`?= =?UTF-8?q?=20is=20a=20local=20variable,=20not=20exported=20=E2=80=94=20se?= =?UTF-8?q?tup=5Fops=5Frepo=20falls=20back=20to=20dev-bot=20token=20and=20?= =?UTF-8?q?fails=20with=20403=20(#583)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bin/disinto | 2 +- lib/forge-setup.sh | 54 ++++++++++++++++++++++------------------------ lib/ops-setup.sh | 15 +++++++------ 3 files changed, 35 insertions(+), 36 deletions(-) diff --git a/bin/disinto b/bin/disinto index fd4fc3b..7f3876a 100755 --- a/bin/disinto +++ b/bin/disinto @@ -685,7 +685,7 @@ p.write_text(text) # the calling user (e.g. johba) but the ops repo belongs to disinto-admin. local ops_slug="disinto-admin/${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) # This brings pre-#407 deployments up to date with the canonical structure diff --git a/lib/forge-setup.sh b/lib/forge-setup.sh index 7250ba4..d81e10a 100644 --- a/lib/forge-setup.sh +++ b/lib/forge-setup.sh @@ -247,38 +247,36 @@ setup_forge() { fi # Get or create human user 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) - local existing_human_token_id - existing_human_token_id=$(curl -sf \ + local human_token="" + # Delete existing human token if present (token sha1 is only returned at creation time) + local existing_human_token_id + 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}" \ - "${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}" \ - "${forge_url}/api/v1/users/${human_user}/tokens/${existing_human_token_id}" >/dev/null 2>&1 || true - fi + "${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) - human_token=$(curl -sf -X POST \ - -u "${human_user}:${human_pass}" \ - -H "Content-Type: application/json" \ - "${forge_url}/api/v1/users/${human_user}/tokens" \ - -d '{"name":"disinto-human-token","scopes":["all"]}' 2>/dev/null \ - | jq -r '.sha1 // empty') || human_token="" + # Create human token (fresh, so sha1 is returned) + human_token=$(curl -sf -X POST \ + -u "${human_user}:${human_pass}" \ + -H "Content-Type: application/json" \ + "${forge_url}/api/v1/users/${human_user}/tokens" \ + -d '{"name":"disinto-human-token","scopes":["all"]}' 2>/dev/null \ + | jq -r '.sha1 // empty') || human_token="" - if [ -n "$human_token" ]; then - # Store human token in .env - if grep -q '^HUMAN_TOKEN=' "$env_file" 2>/dev/null; then - sed -i "s|^HUMAN_TOKEN=.*|HUMAN_TOKEN=${human_token}|" "$env_file" - else - printf 'HUMAN_TOKEN=%s\n' "$human_token" >> "$env_file" - fi - export HUMAN_TOKEN="$human_token" - echo " Human token saved (HUMAN_TOKEN)" + if [ -n "$human_token" ]; then + # Store human token in .env + if grep -q '^HUMAN_TOKEN=' "$env_file" 2>/dev/null; then + sed -i "s|^HUMAN_TOKEN=.*|HUMAN_TOKEN=${human_token}|" "$env_file" + else + printf 'HUMAN_TOKEN=%s\n' "$human_token" >> "$env_file" fi + export HUMAN_TOKEN="$human_token" + echo " Human token saved (HUMAN_TOKEN)" fi # Create bot users and tokens diff --git a/lib/ops-setup.sh b/lib/ops-setup.sh index db6e674..bacefd1 100644 --- a/lib/ops-setup.sh +++ b/lib/ops-setup.sh @@ -5,10 +5,10 @@ # source "$(dirname "$0")/../lib/ops-setup.sh" # # 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: -# setup_ops_repo [primary_branch] +# setup_ops_repo [primary_branch] [admin_token] # - Create ops repo on Forgejo if it doesn't exist # - Configure bot collaborators with appropriate permissions # - Clone or initialize ops repo locally @@ -26,6 +26,7 @@ set -euo pipefail setup_ops_repo() { 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 ops_name="${ops_slug##*/}" @@ -55,12 +56,12 @@ setup_ops_repo() { echo "Creating ops repo in namespace: ${org_name}" # Create org if it doesn't exist curl -sf -X POST \ - -H "Authorization: token ${admin_token:-${FORGE_TOKEN}}" \ + -H "Authorization: token ${admin_token}" \ -H "Content-Type: application/json" \ "${forge_url}/api/v1/orgs" \ -d "{\"username\":\"${org_name}\",\"visibility\":\"public\"}" >/dev/null 2>&1 || true if curl -sf -X POST \ - -H "Authorization: token ${admin_token:-${FORGE_TOKEN}}" \ + -H "Authorization: token ${admin_token}" \ -H "Content-Type: application/json" \ "${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 @@ -70,7 +71,7 @@ setup_ops_repo() { # Fallback: use admin API to create repo under the target namespace http_code=$(curl -s -o /dev/null -w "%{http_code}" \ -X POST \ - -H "Authorization: token ${admin_token:-${FORGE_TOKEN}}" \ + -H "Authorization: token ${admin_token}" \ -H "Content-Type: application/json" \ "${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") @@ -104,7 +105,7 @@ setup_ops_repo() { for bot_user in "${!bot_permissions[@]}"; do bot_perm="${bot_permissions[$bot_user]}" if curl -sf -X PUT \ - -H "Authorization: token ${admin_token:-${FORGE_TOKEN}}" \ + -H "Authorization: token ${admin_token}" \ -H "Content-Type: application/json" \ "${forge_url}/api/v1/repos/${actual_ops_slug}/collaborators/${bot_user}" \ -d "{\"permission\":\"${bot_perm}\"}" >/dev/null 2>&1; then @@ -116,7 +117,7 @@ setup_ops_repo() { # Add disinto-admin as admin collaborator if curl -sf -X PUT \ - -H "Authorization: token ${admin_token:-${FORGE_TOKEN}}" \ + -H "Authorization: token ${admin_token}" \ -H "Content-Type: application/json" \ "${forge_url}/api/v1/repos/${actual_ops_slug}/collaborators/disinto-admin" \ -d '{"permission":"admin"}' >/dev/null 2>&1; then