Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions .claude/skills/provision-tutorial-repo/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
---
name: provision-tutorial-repo
description: Provision a new sap-tutorials tutorial repo pair (public source + private -Contribution), their four org teams, and the content-rebuild CI/notify workflows — matching the org convention used by every existing product repo. Use when asked to set up / create a new tutorial repository, repo pair, or product area in the sap-tutorials org.
---

# Provision a new tutorial repo pair

Sets up a new product area in the `sap-tutorials` org exactly like the existing
ones: a **public source repo** `<name>`, a **private QA repo**
`<name>-Contribution`, four org teams, base CI, and the content-rebuild notify
workflow that wires the repo into the `tutorials-ims` platform.

## When to use

Asked to "set up a new repo in sap-tutorials", "create the main and
-Contribution repos like the others", or stand up a new product/topic area.

## Prerequisites

- `gh` authenticated to **github.com** as a `sap-tutorials` **org owner**, or
with an active **just-in-time (20-minute) admin elevation**. A plain org
member cannot create repos or teams — `provision.sh` fails its preflight.
Ask the requester to elevate right before you run the mutating steps.
- Node/`gh` `workflow` scope (needed to commit workflow files via the API).

## Key facts (why this works)

- **Templates carry the base CI.** `tutorial-repo-template` and
`tutorial-repo-Contribution-template` are real GitHub template repos that
already ship `community-requester-id.yaml`, `label-issues.yml`, and the two
`tutorial-ci` caller workflows (`tutorial-pr-checks.yml` + `tutorial-pr-comment.yml`,
pinned `@v1`). **No `tutorial-ci` rollout is required** — CI comes with the
template.
- **Templates omit the notify workflow.** The per-repo rebuild-dispatch workflow
is added separately: `notify-tutorials-ims.yml` on the source repo,
`notify-qa.yml` on the Contribution repo. Both are repo-agnostic
(`${{ github.repository }}`) — copy verbatim from any current repo
(`btp-adai` / `btp-adai-Contribution` are the canonical source).
- **No tutorials-ims code change.** The platform auto-discovers any org repo
that has a `tutorials/` folder (`scripts/parsers/github.ts` →
`discoverAllTutorials`, skipping archived/fork/`EXCLUDED_REPOS`). A new source
repo is picked up automatically once it has content; `-Contribution` repos are
private and excluded by naming, like all the others.
- **Notify auth is org-level.** The notify workflow fires only when
`vars.USE_GITHUB_APP == 'true'` and the `sap-tutorials-builder` App is
installed on the repo (org secrets `TUTORIALS_APP_ID` / `TUTORIALS_APP_PRIVATE_KEY`
are org-wide, visibility all). App installation is UI-only and org-owner-gated.

## Team convention

For product `<name>` (mirrors `ai-core`):

| Team | Repo | Grant |
|------|------|-------|
| `<name>-admin` | `<name>` | admin |
| `<name>-team` | `<name>` | maintain |
| `<name>-contribution-admin` | `<name>-Contribution` | admin |
| `<name>-contribution-team` | `<name>-Contribution` | maintain |

Admin grant tries the org JIT custom role `admin-ondemand` first, falling back
to plain `admin`. Admin-team membership usually mirrors between the source and
contribution admin teams; likewise for the maintain teams.

## Steps

1. **Confirm inputs** with the requester: product `<name>` (prefer a neutral
name over a marketing product name), repo description, admin logins, team
member logins. Confirm the 4-team layout (some ask for only 2).
2. **Dry-run first** to review every mutation:
```bash
.claude/skills/provision-tutorial-repo/provision.sh \
--name integration \
--description "Developer Tutorials for SAP integration" \
--admins "ajmaradiaga,jung-thomas" \
--members "ajmaradiaga,PalakGarg7,manouxnam,shyam-corpworks" \
--dry-run
```
3. **Ask the requester to elevate** (org-owner / 20-min JIT) — say so explicitly;
creation fails without it.
4. **Run for real** (drop `--dry-run`). The script creates both repos, copies the
notify workflows, sets `USE_GITHUB_APP=true`, creates the four teams, adds
members, and applies the repo grants. It is re-runnable — existing teams are
skipped.
5. **Complete the org-owner-only follow-ups** the script prints:
- Install `sap-tutorials-builder` on both new repos (Contents:write on
`tutorials-ims`).
- Confirm the org `TUTORIALS_APP_*` secrets reach the new repos.
- (Optional) mark `tutorial-pr-checks` Required in branch protection to make
the structural checks blocking.
6. **Verify**: `gh repo view <org>/<name>`, list `.github/workflows` on both
repos (5 workflows: 4 base + 1 notify), and `gh api orgs/<org>/teams/<team>/repos`
shows the grants. A first tutorial push to `main` should trigger the notify
workflow and a `tutorial-updated` rebuild in `tutorials-ims`.

## Gotchas

- Both templates default to `main`; keep it — the notify workflows trigger on
`[master, main]` so `main` is correct and `master`-default repos still work.
- `-Contribution` MUST be **private**; source MUST be **public** (fork PRs get no
secrets, and the public `tutorial-ci` must be checkout-able by every consumer).
- Team **slug == name** (all lowercase here), so membership/grant API calls use
the name directly.
188 changes: 188 additions & 0 deletions .claude/skills/provision-tutorial-repo/provision.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
#!/usr/bin/env bash
#
# provision.sh — provision a new sap-tutorials tutorial repo pair.
#
# Creates, for a product <name>:
# - <name> (public source repo, from tutorial-repo-template)
# - <name>-Contribution (private QA repo, from tutorial-repo-Contribution-template)
# - the content-rebuild notify workflows on each repo
# - four org teams with the standard admin/maintain grants
#
# The templates already ship the base CI (community-requester-id, label-issues,
# and the two tutorial-ci caller workflows tutorial-pr-checks / tutorial-pr-comment
# pinned @v1) — no tutorial-ci rollout is needed. This script only adds the
# per-repo notify workflow that the templates deliberately omit.
#
# REQUIRES: `gh` authenticated to github.com as a sap-tutorials ORG OWNER, or
# with an active just-in-time (20-minute) admin elevation. A plain org member
# CANNOT create repos or teams; the preflight fails fast in that case.
#
# It does NOT (cannot, without extra org config) do the following — see the
# reminders printed at the end:
# - install the `sap-tutorials-builder` GitHub App on the new repos
# - guarantee the org actions secrets TUTORIALS_APP_ID / _PRIVATE_KEY reach them
#
set -euo pipefail

ORG="sap-tutorials"
TEMPLATE_SOURCE="tutorial-repo-template"
TEMPLATE_CONTRIB="tutorial-repo-Contribution-template"
# Canonical repos to copy the (repo-agnostic) notify workflows from.
REF_SOURCE="btp-adai"
REF_CONTRIB="btp-adai-Contribution"
# Admin grant: the org uses a just-in-time custom role first, plain admin as fallback.
ADMIN_ROLE_PRIMARY="admin-ondemand"
ADMIN_ROLE_FALLBACK="admin"
TEAM_ROLE="maintain"

NAME=""
DESCRIPTION=""
ADMINS=""
MEMBERS=""
DRY_RUN=0

usage() {
cat <<'EOF'
Usage: provision.sh --name <product> --description "<text>" \
--admins "user1,user2" --members "user1,user2,user3" [--dry-run]

--name product name, e.g. "integration" (avoid marketing product names)
--description repo description, applied to BOTH repos
--admins comma-separated logins for the *-admin teams (admin grant)
--members comma-separated logins for the *-team teams (maintain grant)
--dry-run print the gh commands instead of running them
EOF
}

while [ $# -gt 0 ]; do
case "$1" in
--name) NAME="$2"; shift 2 ;;
--description) DESCRIPTION="$2"; shift 2 ;;
--admins) ADMINS="$2"; shift 2 ;;
--members) MEMBERS="$2"; shift 2 ;;
--dry-run) DRY_RUN=1; shift ;;
-h|--help) usage; exit 0 ;;
*) echo "Unknown argument: $1" >&2; usage; exit 2 ;;
esac
done

[ -n "$NAME" ] || { echo "ERROR: --name is required" >&2; usage; exit 2; }
[ -n "$ADMINS" ] || echo "WARN: no --admins given; admin teams will be created empty" >&2
[ -n "$MEMBERS" ] || echo "WARN: no --members given; team teams will be created empty" >&2

run() {
if [ "$DRY_RUN" -eq 1 ]; then
printf 'DRY-RUN:'; printf ' %q' "$@"; printf '\n'
else
"$@"
fi
}

CONTRIB="${NAME}-Contribution"

# --- Preflight: must be an org admin/owner ---------------------------------
SELF="$(gh api user --jq '.login')"
ROLE="$(gh api "orgs/${ORG}/memberships/${SELF}" --jq '.role' 2>/dev/null || echo "unknown")"
if [ "$ROLE" != "admin" ]; then
echo "ERROR: '${SELF}' is org role '${ROLE}', not 'admin'." >&2
echo " Repo/team creation needs org-owner rights (or active JIT elevation)." >&2
[ "$DRY_RUN" -eq 1 ] || exit 1
fi

echo "==> Provisioning '${NAME}' (+ '${CONTRIB}') as ${SELF} [role=${ROLE}]"

# --- 1. Repos from templates -----------------------------------------------
echo "==> Creating repos from templates"
run gh repo create "${ORG}/${NAME}" --template "${ORG}/${TEMPLATE_SOURCE}" \
--public --description "${DESCRIPTION}"
run gh repo create "${ORG}/${CONTRIB}" --template "${ORG}/${TEMPLATE_CONTRIB}" \
--private --description "${DESCRIPTION}"

# --- 2. Notify workflows (copied verbatim from the reference repos) ---------
# Both notify workflows are repo-agnostic (they use ${{ github.repository }}),
# so no editing is required — copy the current canonical version straight over.
copy_workflow() {
local ref_repo="$1" wf_path="$2" dst_repo="$3"
local content
content="$(gh api "repos/${ORG}/${ref_repo}/contents/${wf_path}" --jq '.content' | tr -d '\n')"
run gh api --method PUT "repos/${ORG}/${dst_repo}/contents/${wf_path}" \
-f message="chore(ci): add ${wf_path##*/} rebuild-notify workflow" \
-f content="${content}"
}
echo "==> Adding notify workflows"
copy_workflow "${REF_SOURCE}" ".github/workflows/notify-tutorials-ims.yml" "${NAME}"
copy_workflow "${REF_CONTRIB}" ".github/workflows/notify-qa.yml" "${CONTRIB}"

# --- 3. USE_GITHUB_APP repo variable (belt-and-suspenders vs the org var) ---
echo "==> Setting USE_GITHUB_APP=true repo variable"
run gh variable set USE_GITHUB_APP --body "true" --repo "${ORG}/${NAME}"
run gh variable set USE_GITHUB_APP --body "true" --repo "${ORG}/${CONTRIB}"

# --- 4. Teams --------------------------------------------------------------
# Naming follows the ai-core convention:
# <name>-admin / <name>-team -> source repo
# <name>-contribution-admin / -team -> Contribution repo
T_ADMIN="${NAME}-admin"
T_TEAM="${NAME}-team"
T_CADMIN="${NAME}-contribution-admin"
T_CTEAM="${NAME}-contribution-team"

create_team() {
local team="$1"
echo "==> Creating team ${team}"
run gh api --method POST "orgs/${ORG}/teams" -f name="${team}" -f privacy="closed" \
>/dev/null 2>&1 || echo " (team ${team} may already exist)"
}
for t in "$T_ADMIN" "$T_TEAM" "$T_CADMIN" "$T_CTEAM"; do create_team "$t"; done

add_members() {
local team="$1" csv="$2"
[ -n "$csv" ] || return 0
local IFS=','
for user in $csv; do
user="$(echo "$user" | tr -d '[:space:]')"
[ -n "$user" ] || continue
echo "==> Adding ${user} to ${team}"
run gh api --method PUT "orgs/${ORG}/teams/${team}/memberships/${user}" -f role="member" \
>/dev/null
done
}
add_members "$T_ADMIN" "$ADMINS"
add_members "$T_CADMIN" "$ADMINS"
add_members "$T_TEAM" "$MEMBERS"
add_members "$T_CTEAM" "$MEMBERS"

# --- 5. Team -> repo grants ------------------------------------------------
grant() {
local team="$1" repo="$2" role="$3"
echo "==> Granting ${team} '${role}' on ${repo}"
if [ "$DRY_RUN" -eq 1 ]; then
run gh api --method PUT "orgs/${ORG}/teams/${team}/repos/${ORG}/${repo}" -f permission="${role}"
return 0
fi
if ! gh api --method PUT "orgs/${ORG}/teams/${team}/repos/${ORG}/${repo}" \
-f permission="${role}" >/dev/null 2>&1; then
echo " custom role '${role}' rejected; retrying with '${ADMIN_ROLE_FALLBACK}'"
gh api --method PUT "orgs/${ORG}/teams/${team}/repos/${ORG}/${repo}" \
-f permission="${ADMIN_ROLE_FALLBACK}" >/dev/null
fi
}
grant "$T_ADMIN" "$NAME" "$ADMIN_ROLE_PRIMARY"
grant "$T_TEAM" "$NAME" "$TEAM_ROLE"
grant "$T_CADMIN" "$CONTRIB" "$ADMIN_ROLE_PRIMARY"
grant "$T_CTEAM" "$CONTRIB" "$TEAM_ROLE"

# --- Post-provision reminders ----------------------------------------------
cat <<EOF

==> Done. Manual follow-ups that need org-owner UI / App admin:
1. Install the 'sap-tutorials-builder' GitHub App on ${ORG}/${NAME} and
${ORG}/${CONTRIB} (Contents:write on ${ORG}/tutorials-ims). Without it the
notify workflow's App-token step is skipped and dispatch fails.
2. Confirm org actions secrets TUTORIALS_APP_ID / TUTORIALS_APP_PRIVATE_KEY are
visible to the new repos (they are org-wide, visibility=all).
3. Add real tutorials under tutorials/<slug>/ on ${ORG}/${NAME}; the platform
auto-discovers the repo (no tutorials-ims code change needed).
4. Optionally mark the tutorial-pr-checks status as Required in branch
protection if you want the structural checks to block merges.
EOF
2 changes: 1 addition & 1 deletion .deploy/mta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ ID: tutorials-ims
# Bump this on each release you deploy — it's the version shown by `cf mtas`
# and in the mtar filename (tutorials-ims_<version>.mtar). Deploy is manual:
# `cd .deploy && mbt build && cf deploy mta_archives/tutorials-ims_<version>.mtar -e ../deploy/<env>.mtaext -f`.
version: 1.23.0
version: 1.24.0

# Top-level parameters (overridable per-env via deploy/<env>.mtaext).
parameters:
Expand Down
4 changes: 4 additions & 0 deletions db/src/COM_SAP_DEVELOPERS_IMS_PUZZLES_SEQ.hdbsequence
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
SEQUENCE "COM_SAP_DEVELOPERS_IMS_PUZZLES_SEQ"
START WITH 10000001
INCREMENT BY 1
NO CYCLE
Loading
Loading