Skip to content
Open
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
12 changes: 7 additions & 5 deletions .github/agents/git-ape-onboarding.agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,22 +43,23 @@ Always use the `/git-ape-onboarding` skill for procedure and command patterns.

## Required user inputs (gated step-1)

Before any state-changing command runs, you MUST surface a checklist of the required inputs in your first reply and wait for the user to supply any that are missing. Even when the user's opening prompt already names a few (e.g., repo + env + auth method), enumerate the full list so the user can fill the gaps in a single round-trip. At minimum, request the following **six** inputs (rendered as a numbered list, table, or explicit question block — never inferred silently):
Before any state-changing command runs, you MUST surface a checklist of the required inputs in your first reply and wait for the user to supply any that are missing. Even when the user's opening prompt already names a few (e.g., repo + env + auth method), enumerate the full list so the user can fill the gaps in a single round-trip. At minimum, request the following **seven** inputs (rendered as a numbered list, table, or explicit question block — never inferred silently):

1. **Target GitHub repository** — `<org>/<repo>` plus confirmation of the default branch (assume `main`; only change if the user explicitly says otherwise — never silently substitute `master`).
2. **Onboarding mode** — single-environment vs multi-environment (dev/staging/prod). Even if the prompt names one, restate it explicitly for confirmation.
3. **Azure subscription target(s)** — the subscription ID (or name to look up) for each environment.
4. **RBAC role model** — which role(s) to assign on subscription scope (`Contributor`, `Owner`, `User Access Administrator`, or a custom role). Default suggestion: `Contributor`.
5. **Default Azure region** — primary region for the workload (e.g., `eastus`, `westus2`). Used for naming validation and federated credential auditing context.
6. **Project / deployment name** — short slug used to name the App Registration (`sp-<project>-<env>`), federated credentials (`fc-<project>-<env>-main-branch`), and downstream Git-Ape deployments.
7. **Runner type** — public GitHub-hosted (default, no infrastructure) or private self-hosted runners in the Azure subscription. If private, also capture the platform (ACI / ACA / AKS) and whether it must be VNet-injected. Default suggestion: **public to start** — private runners can be added later by setting one variable (`GIT_APE_RUNNER_LABEL`).

Treat this as a **non-negotiable contract** for the gated first reply: regardless of how much the user pre-filled, the reply must explicitly enumerate ≥3 outstanding asks (and ideally the full list above) so the user sees exactly what's still needed. Do not race ahead to OIDC / federated-credential output until inputs 1–6 are supplied and Azure auth is confirmed.
Treat this as a **non-negotiable contract** for the gated first reply: regardless of how much the user pre-filled, the reply must explicitly enumerate ≥3 outstanding asks (and ideally the full list above) so the user sees exactly what's still needed. Do not race ahead to OIDC / federated-credential output until inputs 1–7 are supplied and Azure auth is confirmed.

## Workflow

1. Confirm target repository URL **and default branch** (input #1 above).
2. Ask whether onboarding is single-environment or multi-environment (input #2).
3. Confirm subscription target(s), RBAC role model, default region, and project name (inputs #3–#6).
3. Confirm subscription target(s), RBAC role model, default region, project name, and runner type (inputs #3–#7).
4. Validate prerequisites:
- `az`, `gh`, `jq` installed
- Azure authenticated (`az account show`)
Expand All @@ -71,9 +72,10 @@ Treat this as a **non-negotiable contract** for the gated first reply: regardles
- macOS / Linux / WSL: `./scripts/scaffold-repo.sh`
- Windows (PowerShell 7+): `pwsh ./scripts/scaffold-repo.ps1`
Both scripts produce byte-identical output. Report which files were created vs skipped.
9. Ask compliance framework and enforcement mode preferences (Step 10 in `/git-ape-onboarding` skill playbook).
9. Ask compliance framework and enforcement mode preferences (Step 11 in `/git-ape-onboarding` skill playbook).
10. Update the `## Compliance & Azure Policy` section in `.github/copilot-instructions.md` with the user's choices. If the file was skipped by the scaffold step or lacks that section, surface the captured preferences in chat for manual integration instead of mutating the file.
11. Summarize created/updated artifacts and next checks.
11. Select the runner type (input #7). If private runners were chosen, point the user at `./templates/runners/<platform>/` for the reference IaC, have them provision it (sourcing the GitHub credential from Key Vault, never inlined), confirm the runner is online, and set the `GIT_APE_RUNNER_LABEL` variable. If public, leave the variable unset. (Step 12 in `/git-ape-onboarding` skill playbook.)
12. Summarize created/updated artifacts and next checks.

## Output Requirements

Expand Down
70 changes: 70 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,76 @@ Create two GitHub environments for protection rules:
- Required reviewers (recommended — destructive action)
- Deployment branches: `main` only (triggered on PR merge)

## GitHub Actions Runners

Git-Ape workflows run on **public GitHub-hosted runners by default** and can be
switched to **private self-hosted runners** in your Azure subscription with a
single repository variable — no workflow edits required.

### The runner switch: `GIT_APE_RUNNER_LABEL`

Every scaffolded workflow (`git-ape-plan`, `-deploy`, `-destroy`, `-verify`)
resolves its runner like this:

```yaml
runs-on: ${{ vars.GIT_APE_RUNNER_LABEL || 'ubuntu-latest' }}
```

| `GIT_APE_RUNNER_LABEL` | Effect |
|------------------------|--------|
| **unset** (default) | Jobs run on GitHub-hosted `ubuntu-latest`. No infrastructure. |
| set to a label (default `git-ape-runner`) | Jobs target your self-hosted runners registered with that label. |

```bash
# Switch to private runners (after they are provisioned and online)
gh variable set GIT_APE_RUNNER_LABEL --repo <org>/<repo> --body "git-ape-runner"
# Clean fallback to GitHub-hosted runners
gh variable delete GIT_APE_RUNNER_LABEL --repo <org>/<repo>
```

In multi-environment mode, set the variable per environment (`--env azure-deploy`)
so only the environments that need private runners use them.

### Runner types and platforms

- **Public GitHub-hosted** — default; nothing to provision.
- **Self-hosted (subscription)** — runners are Azure resources with outbound
internet. Control over image, region, and identity without a VNet.
- **VNet-injected** — runners run inside a subnet you manage, for private
connectivity to Azure resources (private endpoints, no public egress except
to GitHub).

Private runners are provisioned from on-demand reference IaC shipped with the
onboarding skill at `templates/runners/`:

| Platform | What it provisions |
|----------|--------------------|
| **ACI** | ARM `template.json` — a container group running an ephemeral runner. |
| **ACA** | ARM `template.json` — a KEDA `github-runner`-scaled Container Apps Job (scale-to-zero). |
| **AKS** | Actions Runner Controller (ARC) via Helm `values.yaml` (cluster created with a standard Git-Ape ARM deployment). |

These templates are **not** auto-scaffolded — the public bootstrap stays the
default. Run `/git-ape-onboarding` (Runner Selection step) to choose and
provision them.

### Runner security model

- **Azure access uses a user-assigned managed identity, never stored keys.**
- **The GitHub registration credential is the only secret** — source it from
Key Vault (`securestring` parameter or pre-created Kubernetes secret), never
inline it in a committed `parameters.json` or `values.yaml`.
- **Ephemeral runners by default** — one job per registration, so no state leaks
between deployments.
- The runner label (`git-ape-runner`) must equal `GIT_APE_RUNNER_LABEL`.

### Drift workflow caveat

`git-ape-drift.lock.yml` is a compiled GitHub Agentic Workflow (gh-aw) and does
**not** honor `GIT_APE_RUNNER_LABEL`. To run continuous drift detection on a
private runner, set `runs-on:` in the source `git-ape-drift.md` frontmatter and
recompile with `gh aw compile` — never hand-edit the `.lock.yml` (it carries an
integrity hash).

## Security Baseline

- Enable HTTPS-only for all web-facing resources
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/microsoft/waza/main/schemas/task.schema.json

id: positive-private-runner
name: Positive — Onboard with private VNet-injected runner
description: Onboard request that selects private self-hosted runners (ACA, VNet-injected) — clear trigger plus runner-selection handling.
tags: [trigger, positive, runner]
inputs:
prompt: "Onboard this repository for Git-Ape, but I don't want to use GitHub-hosted runners — the deployment jobs must run on private self-hosted runners inside my Azure subscription, VNet-injected, on Azure Container Apps. Set up OIDC, RBAC, the GitHub environments, and tell me how the runner side works. My repo is a fresh local Git repo."
graders:
- name: trigger_relevance_positive
type: trigger
config:
skill_path: .github/skills/git-ape-onboarding/SKILL.md
mode: positive
threshold: 0.5

# answer_quality (LLM-as-judge): scoped per-task on positives so a flaky
# judge call only zeroes out this task, not the whole leg. See eval.yaml.
#
# IMPORTANT: waza prompt graders are binary (set_waza_grade_pass = 1.0,
# set_waza_grade_fail = 0.0). They are NOT 1–5 rubrics. The judge has NO
# access to the agent's response unless continue_session: true is set — it
# resumes the agent's own session so it can read the response.
- type: prompt
name: answer_quality
config:
continue_session: true
prompt: |
You are grading the assistant's previous response in this session.
The user asked to onboard a fresh repo for Git-Ape AND to run the
deployment workflows on PRIVATE self-hosted runners (VNet-injected,
on Azure Container Apps) instead of GitHub-hosted runners.

The skill GATES on prerequisites and required user inputs before
executing any state-changing commands. The expected first-turn reply
is a "gated step-1" response, NOT a completion report. Runner
selection is one of the required inputs.

PASS criteria — the response must satisfy ALL FOUR of:
1. It is a gated handoff: prereq/auth status is shown OR required
inputs are requested before any execution (a numbered list or
explicit question block counts).
2. It requests at least THREE required inputs from: target GitHub
repository, Azure subscription ID, RBAC role, default region,
project / deployment name, onboarding mode.
3. It correctly handles the runner request — satisfy ANY ONE of:
(a) acknowledges the chosen private runner (self-hosted /
VNet-injected and/or Azure Container Apps / ACA) as the runner
type to configure; (b) explains the GIT_APE_RUNNER_LABEL
variable switch (public ubuntu-latest by default → private when
the label is set); or (c) points the user at the runner
reference IaC under templates/runners/ (ACI/ACA/AKS). It must
NOT ignore the runner request or claim GitHub-hosted is the only
option.
4. It does NOT claim to have already provisioned runners, set
GIT_APE_RUNNER_LABEL, configured OIDC, created federated
credentials/environments, or assigned RBAC. The reply waits for
user input + auth before continuing. (Fabricated "I've
configured X" / "I provisioned Y" before inputs → FAIL.)

If ALL FOUR are met, call `set_waza_grade_pass`.
Otherwise, call `set_waza_grade_fail` and list which criteria are missing.
Loading
Loading