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).
This commit is contained in:
2026-06-16 20:18:24 +00:00
commit f283a371bb
253 changed files with 15975 additions and 0 deletions
+191
View File
@@ -0,0 +1,191 @@
---
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.