diff --git a/.claude/skills/recipe-upgrade/open-recipe-pr.sh b/.claude/skills/recipe-upgrade/open-recipe-pr.sh index 0aaa5b2..c9ff199 100755 --- a/.claude/skills/recipe-upgrade/open-recipe-pr.sh +++ b/.claude/skills/recipe-upgrade/open-recipe-pr.sh @@ -37,7 +37,7 @@ NAMESPACE="${GITEA_NAMESPACE:-recipe-maintainers}" RECIPE_DIR="${HOME}/.abra/recipes/${RECIPE}" export PATH="/run/current-system/sw/bin:${PATH}" -PASS_ENC=$(python3 -c "import urllib.parse,sys;print(urllib.parse.quote(sys.argv[1],safe=''))" "${GITEA_PASSWORD}") +PASS_ENC=$(printf '%s' "${GITEA_PASSWORD}" | jq -Rr '@uri') API="https://${GITEA_URL}/api/v1" AUTH=(-u "${GITEA_USERNAME}:${GITEA_PASSWORD}") @@ -57,7 +57,7 @@ if [ "${STATUS}" = "404" ]; then echo "→ Mirror ${NAMESPACE}/${RECIPE} does not exist; nothing to reconcile."; exit 0 fi echo "→ Mirror ${NAMESPACE}/${RECIPE} missing; creating..." - BODY=$(python3 -c "import json;print(json.dumps({'name':'${RECIPE}','private':True,'default_branch':'main','auto_init':False}))") + BODY=$(jq -n --arg name "${RECIPE}" '{name:$name,private:true,default_branch:"main",auto_init:false}') CS=$(curl -s -o /dev/null -w "%{http_code}" "${AUTH[@]}" -H "Content-Type: application/json" -X POST "${API}/orgs/${NAMESPACE}/repos" -d "${BODY}") if [ "${CS}" != "201" ]; then CS=$(curl -s -o /dev/null -w "%{http_code}" "${AUTH[@]}" -H "Content-Type: application/json" -X POST "${API}/user/repos" -d "${BODY}") @@ -96,7 +96,7 @@ close_pr() { # local idx="$1" reason="$2" curl -s -o /dev/null "${AUTH[@]}" -H "Content-Type: application/json" -X POST \ "${API}/repos/${NAMESPACE}/${RECIPE}/issues/${idx}/comments" \ - -d "$(python3 -c "import json,sys;print(json.dumps({'body':sys.argv[1]}))" "Auto-closed by /recipe-upgrade: ${reason}")" || true + -d "$(jq -n --arg body "Auto-closed by /recipe-upgrade: ${reason}" '{body:$body}')" || true curl -s -o /dev/null "${AUTH[@]}" -H "Content-Type: application/json" -X PATCH \ "${API}/repos/${NAMESPACE}/${RECIPE}/pulls/${idx}" -d '{"state":"closed"}' || true echo " ✗ closed PR #${idx} — ${reason}" @@ -125,13 +125,7 @@ while IFS=$'\t' read -r IDX HEAD_REF; do else echo " • PR #${IDX} (${HEAD_REF}) still open vs upstream main — left as-is (reconcile-only)" fi -done < <(echo "${OPEN_PRS}" | python3 -c " -import json,sys -try: data=json.load(sys.stdin) -except Exception: data=[] -for pr in data: - print(f\"{pr['number']}\t{pr['head']['ref']}\") -") +done < <(echo "${OPEN_PRS}" | jq -r '.[] | [(.number | tostring), .head.ref] | @tsv' 2>/dev/null || true) if [ "${MODE}" = "--reconcile-only" ]; then echo "✓ reconcile-only done for ${NAMESPACE}/${RECIPE} (main synced; merged-upstream PRs closed)." @@ -148,15 +142,16 @@ PR_BODY="${PR_BODY} Tested green on the cc-ci recipe CI server (full suite, cold, against this PR head). NOT merged — for operator review. cc @trav @notplants" -PAYLOAD=$(python3 -c " -import json,sys -print(json.dumps({'title':sys.argv[1],'body':sys.argv[2],'head':sys.argv[3],'base':'main','reviewers':['trav','notplants']}))" \ - "${LATEST_MSG}" "${PR_BODY}" "${BRANCH}") +PAYLOAD=$(jq -n \ + --arg title "${LATEST_MSG}" \ + --arg body "${PR_BODY}" \ + --arg head "${BRANCH}" \ + '{title:$title,body:$body,head:$head,base:"main",reviewers:["trav","notplants"]}') RESP=$(mktemp) PS=$(curl -s -o "${RESP}" -w "%{http_code}" "${AUTH[@]}" -H "Content-Type: application/json" -X POST "${API}/repos/${NAMESPACE}/${RECIPE}/pulls" -d "${PAYLOAD}") if [ "${PS}" = "201" ]; then - echo "PR_URL=$(python3 -c "import json;print(json.load(open('${RESP}'))['html_url'])")" + echo "PR_URL=$(jq -r '.html_url' "${RESP}")" elif [ "${PS}" = "409" ] || grep -q "pull request already exists" "${RESP}" 2>/dev/null; then echo "PR_URL=https://${GITEA_URL}/${NAMESPACE}/${RECIPE}/pulls (branch ${BRANCH} already has a PR — updated by force-push)" else