Files
recipe-maintainer/.claude/commands/recipe-create-pr.md
T
autonomic-bot f283a371bb recipe-maintainer: public snapshot (secrets + deployment plans removed, single commit)
Sanitized single-commit public mirror of recipe-maintainer.
- Removed test-ssh/.testenv (live creds); added test-ssh/.testenv.example placeholders.
- Removed plans/ and planned-updates/ (deployment-planning docs) so no client/
  deployment domains appear in the public repo.
- All other secret stores were already gitignored.
- docs.coopcloud.tech retained as a submodule (public upstream).
2026-06-16 20:18:24 +00:00

192 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
description: Push local recipe commits to git.autonomic.zone and open a PR against an upstream-synced main branch
argument-hint: <recipe-name>
allowed-tools: [Bash, Read]
---
# Recipe Create PR
Take the local commits made to a recipe (e.g. during `/recipe-upgrade-apply`) and open a pull request on the Gitea instance at `git.autonomic.zone`. The Gitea repo's `main` branch is force-synced from the recipe's upstream `main` so the PR diff shows only the local changes.
The recipe name is: $ARGUMENTS
## Prerequisites
Credentials are read from `test-ssh/.testenv`. The following variables must be set:
- `GITEA_USERNAME` — bot account on the Gitea instance
- `GITEA_PASSWORD` — bot password
- `GITEA_URL` — Gitea host (e.g. `git.autonomic.zone`)
Optional:
- `GITEA_NAMESPACE` — owner under which to create/find repos. Defaults to `recipe-maintainers` (the org whose `recipe-maintainers` team auto-grants access to all repos within it).
The recipe must be checked out at `~/.abra/recipes/$ARGUMENTS` with at least one commit beyond `origin/main`.
## Steps
Run the following script with the recipe name substituted in:
```bash
#!/usr/bin/env bash
set -euo pipefail
RECIPE="$ARGUMENTS"
WORKSPACE="/workspace"
RECIPE_DIR="${HOME}/.abra/recipes/${RECIPE}"
TESTENV="${WORKSPACE}/test-ssh/.testenv"
# --- Load credentials ---
[ -f "${TESTENV}" ] || { echo "ERROR: ${TESTENV} not found"; exit 1; }
set -a; . "${TESTENV}"; set +a
: "${GITEA_USERNAME:?missing in .testenv}"
: "${GITEA_PASSWORD:?missing in .testenv}"
: "${GITEA_URL:?missing in .testenv}"
NAMESPACE="${GITEA_NAMESPACE:-recipe-maintainers}"
PASS_ENC=$(python3 -c "import urllib.parse,sys;print(urllib.parse.quote(sys.argv[1],safe=''))" "${GITEA_PASSWORD}")
API="https://${GITEA_URL}/api/v1"
AUTH=(-u "${GITEA_USERNAME}:${GITEA_PASSWORD}")
# --- Validate recipe checkout ---
[ -d "${RECIPE_DIR}/.git" ] || { echo "ERROR: ${RECIPE_DIR} is not a git repo. Run 'abra recipe fetch ${RECIPE}' first."; exit 1; }
cd "${RECIPE_DIR}"
# --- Fetch upstream main ---
echo "→ Fetching upstream main from origin..."
git fetch origin main
# --- Find diverged commits ---
DIVERGED=$(git log --oneline origin/main..HEAD 2>/dev/null || true)
if [ -z "${DIVERGED}" ]; then
echo "ERROR: HEAD has no commits beyond origin/main. Nothing to PR."
exit 1
fi
echo "→ Local commits to PR:"
echo "${DIVERGED}" | sed 's/^/ /'
# --- Determine PR branch name from the most recent commit ---
LATEST_MSG=$(git log -1 --pretty=%s HEAD)
if echo "${LATEST_MSG}" | grep -qiE "upgrade to [0-9]"; then
VERSION=$(echo "${LATEST_MSG}" | grep -oiE "upgrade to [0-9][^[:space:]]+" | awk '{print $NF}')
BRANCH="upgrade-${VERSION}"
else
BRANCH="pr-$(date -u +%Y%m%d-%H%M%S)"
fi
echo "→ PR branch: ${BRANCH}"
# --- Check / create Gitea repo ---
REPO_URL_API="${API}/repos/${NAMESPACE}/${RECIPE}"
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${AUTH[@]}" "${REPO_URL_API}")
if [ "${STATUS}" = "404" ]; then
echo "→ Repo ${NAMESPACE}/${RECIPE} does not exist; creating..."
CREATE_BODY=$(python3 -c "import json;print(json.dumps({'name':'${RECIPE}','private':True,'default_branch':'main','auto_init':False}))")
# Try org namespace first
CREATE_OUT=$(mktemp)
CREATE_STATUS=$(curl -s -o "${CREATE_OUT}" -w "%{http_code}" "${AUTH[@]}" \
-H "Content-Type: application/json" \
-X POST "${API}/orgs/${NAMESPACE}/repos" \
-d "${CREATE_BODY}")
if [ "${CREATE_STATUS}" != "201" ]; then
echo " ! create under org ${NAMESPACE} returned HTTP ${CREATE_STATUS} — falling back to user namespace ${GITEA_USERNAME}"
cat "${CREATE_OUT}" >&2
echo "" >&2
CREATE_STATUS=$(curl -s -o "${CREATE_OUT}" -w "%{http_code}" "${AUTH[@]}" \
-H "Content-Type: application/json" \
-X POST "${API}/user/repos" \
-d "${CREATE_BODY}")
if [ "${CREATE_STATUS}" != "201" ]; then
echo "ERROR: failed to create repo (HTTP ${CREATE_STATUS}):"
cat "${CREATE_OUT}"
rm -f "${CREATE_OUT}"
exit 1
fi
NAMESPACE="${GITEA_USERNAME}"
fi
rm -f "${CREATE_OUT}"
echo " ✓ created ${NAMESPACE}/${RECIPE}"
elif [ "${STATUS}" = "200" ]; then
echo "→ Repo ${NAMESPACE}/${RECIPE} already exists"
else
echo "ERROR: unexpected HTTP ${STATUS} when checking ${REPO_URL_API}"
exit 1
fi
# --- Set up the gitea remote with credentials embedded ---
REMOTE_URL="https://${GITEA_USERNAME}:${PASS_ENC}@${GITEA_URL}/${NAMESPACE}/${RECIPE}.git"
if git remote | grep -qx gitea; then
git remote set-url gitea "${REMOTE_URL}"
else
git remote add gitea "${REMOTE_URL}"
fi
# --- Force-sync Gitea main with origin/main so the PR diff is clean ---
echo "→ Force-syncing gitea/main from origin/main..."
git push --force gitea "refs/remotes/origin/main:refs/heads/main"
# --- Push local commits as the PR branch ---
echo "→ Pushing local commits as branch '${BRANCH}'..."
git push --force gitea "HEAD:refs/heads/${BRANCH}"
# --- Create the PR ---
# NOTE: for an upgrade PR the body (passed in via RECIPE_PR_BODY) MUST link the upstream release notes
# — one explicit line per upgraded image/service, e.g.
# **Upstream release notes:** <service> <old>→<new>: <url>
# pulling each URL from recipe-info/<recipe>/upstream.md (between the current → new version). These links
# belong in the PR body itself, NOT only in a side report, so the reviewer sees what changed upstream.
PR_TITLE="${LATEST_MSG}"
if [ -n "${RECIPE_PR_BODY:-}" ]; then
PR_BODY="${RECIPE_PR_BODY}"
else
PR_BODY=$(printf "Local commits on top of upstream main:\n\n%s\n" "$(git log origin/main..HEAD --pretty='- %h %s')")
fi
PR_BODY="${PR_BODY}
cc @trav @notplants"
PR_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'],
}))" "${PR_TITLE}" "${PR_BODY}" "${BRANCH}")
PR_RESPONSE=$(mktemp)
PR_STATUS=$(curl -s -o "${PR_RESPONSE}" -w "%{http_code}" "${AUTH[@]}" \
-H "Content-Type: application/json" \
-X POST "${API}/repos/${NAMESPACE}/${RECIPE}/pulls" \
-d "${PR_PAYLOAD}")
if [ "${PR_STATUS}" = "201" ]; then
PR_URL=$(python3 -c "import json;print(json.load(open('${PR_RESPONSE}'))['html_url'])")
echo ""
echo "✓ PR created: ${PR_URL}"
elif [ "${PR_STATUS}" = "409" ] || grep -q "pull request already exists" "${PR_RESPONSE}" 2>/dev/null; then
echo ""
echo " A PR for branch '${BRANCH}' already exists. See:"
echo " https://${GITEA_URL}/${NAMESPACE}/${RECIPE}/pulls"
else
echo "ERROR: PR creation failed (HTTP ${PR_STATUS}):"
cat "${PR_RESPONSE}"
rm -f "${PR_RESPONSE}"
exit 1
fi
rm -f "${PR_RESPONSE}"
```
After the script runs, report back the PR URL (or the existing-PR list URL if Gitea returned 409).
## Notes
- The `gitea` remote is created/updated in `~/.abra/recipes/<recipe>/.git/config` with the password embedded — this is acceptable here since `.testenv` already stores the password in plaintext, but be aware the remote URL is now stored in that local file.
- Re-running the skill is safe: the script force-pushes both the synced `main` and the PR branch, and reports gracefully if a PR for that branch already exists.
- The PR branch name is derived from the most recent commit message — if it matches `upgrade to <version>`, the branch becomes `upgrade-<version>`; otherwise a timestamped name is used.
- **For upgrade PRs, `RECIPE_PR_BODY` must carry the upstream release-notes links** (one line per upgraded service: `**Upstream release notes:** <service> <old>→<new>: <url>`, sourced from `recipe-info/<recipe>/upstream.md`). `/recipe-upgrade-apply` composes the body with these links already; if you invoke this command directly for an upgrade, include them in `RECIPE_PR_BODY` yourself so the reviewer sees what changed upstream.