@codyswann/lisa 2.198.1 → 2.199.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/cdk/create-only/.github/workflows/deploy.yml +27 -3
  2. package/expo/create-only/.github/workflows/deploy.yml +27 -3
  3. package/nestjs/create-only/.github/workflows/deploy.yml +26 -3
  4. package/package.json +1 -1
  5. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  6. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  7. package/plugins/lisa/.codex-plugin/skills/lisa-setup-github-repo/SKILL.md +58 -3
  8. package/plugins/lisa/commands/lisa/setup/github-repo.md +1 -1
  9. package/plugins/lisa/rules/reference/config-resolution.md +6 -0
  10. package/plugins/lisa/skills/lisa-setup-github-repo/SKILL.md +59 -4
  11. package/plugins/lisa-agy/commands/lisa/setup/github-repo.md +1 -1
  12. package/plugins/lisa-agy/plugin.json +1 -1
  13. package/plugins/lisa-agy/skills/lisa-setup-github-repo/SKILL.md +59 -4
  14. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  15. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  16. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  17. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  18. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  19. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  20. package/plugins/lisa-copilot/commands/lisa/setup/github-repo.md +1 -1
  21. package/plugins/lisa-copilot/rules/reference/config-resolution.md +6 -0
  22. package/plugins/lisa-copilot/skills/lisa-setup-github-repo/SKILL.md +59 -4
  23. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  24. package/plugins/lisa-cursor/commands/lisa/setup/github-repo.md +1 -1
  25. package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +6 -0
  26. package/plugins/lisa-cursor/skills/lisa-setup-github-repo/SKILL.md +59 -4
  27. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  28. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  29. package/plugins/lisa-expo-agy/plugin.json +1 -1
  30. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  31. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  32. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  33. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  34. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  35. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  36. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  37. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  38. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  39. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  40. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  41. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  42. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  43. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  44. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  45. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  46. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  47. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  49. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  50. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  52. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  53. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  54. package/plugins/lisa-rails-agy/plugin.json +1 -1
  55. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  56. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  57. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  58. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  59. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  60. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  63. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  64. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  65. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  66. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  67. package/plugins/src/base/commands/lisa/setup/github-repo.md +1 -1
  68. package/plugins/src/base/rules/reference/config-resolution.md +6 -0
  69. package/plugins/src/base/skills/lisa-setup-github-repo/SKILL.md +59 -4
  70. package/scripts/lisa-github-environments.sh +256 -0
  71. package/scripts/lisa-github-repo-setup.sh +11 -4
  72. package/typescript/copy-overwrite/.github/GITHUB_ACTIONS.md +30 -1
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: lisa-setup-github-repo
3
- description: "Apply Lisa's GitHub repository governance baseline to the current project's repo: repository settings (merge-only, auto-merge, delete-branch-on-merge, update-branch suggestions, wiki off, secret scanning where available), branch + tag rulesets from Lisa templates (base PR gate with CodeRabbit/GitGuardian/Quality Checks, prevent delete, protect tags, stack overlays), and a write-access deploy key + DEPLOY_KEY secret so release workflows can push version bumps through the rulesets' DeployKey bypass. Idempotent — re-running updates settings and rulesets in place and skips an already-configured deploy key."
4
- allowed-tools: ["Bash", "Read", "AskUserQuestion"]
3
+ description: "Apply Lisa's GitHub repository governance baseline to the current project's repo: repository settings (merge-only, auto-merge, delete-branch-on-merge, update-branch suggestions, wiki off, secret scanning where available), branch + tag rulesets from Lisa templates (base PR gate with CodeRabbit/GitGuardian/Quality Checks, prevent delete, protect tags, stack overlays), a write-access deploy key + DEPLOY_KEY secret so release workflows can push version bumps through the rulesets' DeployKey bypass, and optional deployment environments with human-approval gates (required reviewers) from the github.environments block in .lisa.config.json. Idempotent — re-running updates settings, rulesets, and environments in place and skips an already-configured deploy key."
4
+ allowed-tools: ["Bash", "Read", "Edit", "AskUserQuestion"]
5
5
  ---
6
6
 
7
7
  # Setup GitHub Repository Governance
@@ -29,6 +29,28 @@ Apply the fleet-standard GitHub repository configuration to this project's repo.
29
29
  - `prevent delete`, `protect tags` (`v*`), plus stack overlays (`cdk validation`, staging-only `playwright`)
30
30
  - Repos without `.github/workflows/` get only app-based required checks — an Actions check that can never report would block every PR forever
31
31
  3. **Deploy key** (`scripts/setup-deploy-key.sh --yes`) — write-access deploy key + `DEPLOY_KEY` secret, skipped when already configured. The `base` ruleset's `DeployKey: always` bypass is what lets CI version-bump pushes through protected branches.
32
+ 4. **Deployment environments** (`scripts/lisa-github-environments.sh`) — entirely optional; only runs when `.lisa.config.json` declares environments:
33
+ ```json
34
+ {
35
+ "github": {
36
+ "environments": {
37
+ "production": {
38
+ "branch": "main",
39
+ "require_approval": true,
40
+ "reviewers": ["some-user", "some-org/some-team"],
41
+ "prevent_self_review": false,
42
+ "wait_timer": 0
43
+ },
44
+ "staging": { "branch": "staging" }
45
+ }
46
+ }
47
+ }
48
+ ```
49
+ - Environment names are friendly names (`production`), mapped to branches. Branch resolution order: explicit `branch` → `deploy.branches[<name>]` → the name itself.
50
+ - `require_approval: true` provisions **required reviewers** (usernames or `org/team-slug`, max 6) — GitHub pauses any workflow job bound to the environment until a listed reviewer approves it in the Actions UI. Reviewers are mandatory when `require_approval` is true; the script refuses a gate nobody can approve.
51
+ - Every declared environment also gets a **deployment branch policy** pinned to its branch, so only that branch can deploy to it.
52
+ - Provisioning matters: GitHub silently auto-creates an environment **without** protection rules the first time a workflow references it, so an unprovisioned approval gate gates on nothing.
53
+ - The stack `deploy.yml` templates read this same config at runtime and pass `require_approval`/`approval_environment` to `release.yml`, whose `release_approval` job is where the run pauses. Requires a public repo or a paid plan for private repos (the script skips gracefully otherwise).
32
54
 
33
55
  ## Workflow
34
56
 
@@ -56,7 +78,31 @@ bash "$LISA_SCRIPTS/lisa-github-repo-setup.sh" .
56
78
 
57
79
  Requires `gh` authenticated with **admin** permission on the repo. A 403 on rulesets means the plan doesn't support them (private repo on a free personal plan) — settings and deploy key still apply; the script skips rulesets gracefully.
58
80
 
59
- ### Step 4 — Verify
81
+ ### Step 4 — Wire the approval gate into an existing deploy.yml (guided edit)
82
+
83
+ Only when `.lisa.config.json` has a `github.environments` entry with `require_approval: true` AND `.github/workflows/deploy.yml` exists but does not reference `approval_environment`:
84
+
85
+ ```bash
86
+ [ -f .github/workflows/deploy.yml ] \
87
+ && jq -e '[.github.environments // {} | .[] | select(.require_approval == true)] | length > 0' .lisa.config.json > /dev/null 2>&1 \
88
+ && ! grep -q 'approval_environment' .github/workflows/deploy.yml \
89
+ && echo "deploy.yml needs approval wiring"
90
+ ```
91
+
92
+ A `grep` hit is only a first pass — before skipping the edit, read the `release` job's `with:` block and confirm it actually passes both `require_approval` and `approval_environment` from the `determine_environment` outputs (a commented-out or unrelated occurrence doesn't count as wired).
93
+
94
+ `deploy.yml` is create-only — the project owns it, so Lisa never patches it automatically. Show the user the two changes from the current stack template (`<stack>/create-only/.github/workflows/deploy.yml` in the Lisa repo) and offer to apply them via Edit:
95
+
96
+ 1. In `determine_environment`, add a checkout step plus the `🚦 Resolve approval gate from .lisa.config.json` step, and expose the `approval_environment` / `require_approval` outputs.
97
+ 2. In the `release` job's `with:` block, replace `require_approval: false` with:
98
+ ```yaml
99
+ require_approval: ${{ needs.determine_environment.outputs.require_approval == 'true' }}
100
+ approval_environment: ${{ needs.determine_environment.outputs.approval_environment }}
101
+ ```
102
+
103
+ Skip this step (and say why) if the project's deploy.yml doesn't call Lisa's `release.yml` (rails uses `release-rails.yml` and harper-fabric has no release call — approval gating for those stacks is not wired yet).
104
+
105
+ ### Step 5 — Verify
60
106
 
61
107
  ```bash
62
108
  gh api "repos/$(gh repo view --json nameWithOwner -q .nameWithOwner)" \
@@ -64,4 +110,13 @@ gh api "repos/$(gh repo view --json nameWithOwner -q .nameWithOwner)" \
64
110
  gh api "repos/$(gh repo view --json nameWithOwner -q .nameWithOwner)/rulesets" --jq '[.[].name]'
65
111
  ```
66
112
 
67
- Report the applied settings and ruleset names. If any step failed, surface the error — do not mark the setup complete.
113
+ When environments were configured, also confirm each one carries its protection rules and branch policy:
114
+
115
+ ```bash
116
+ gh api "repos/$(gh repo view --json nameWithOwner -q .nameWithOwner)/environments" \
117
+ --jq '.environments[] | {name, protection_rules, deployment_branch_policy}'
118
+ ```
119
+
120
+ For every environment declared in `github.environments`, treat a missing `deployment_branch_policy` — or, when `require_approval` is true, an empty `protection_rules` — as a failure: the environment exists but is unprotected, so an approval gate bound to it would block nothing.
121
+
122
+ Report the applied settings, ruleset names, and environments. If any step failed, surface the error — do not mark the setup complete.
@@ -0,0 +1,256 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # lisa-github-environments.sh
4
+ #
5
+ # Provisions GitHub deployment Environments declared in .lisa.config.json:
6
+ # { "github": { "environments": {
7
+ # "production": {
8
+ # "branch": "main",
9
+ # "require_approval": true,
10
+ # "reviewers": ["some-user", "some-org/some-team"],
11
+ # "prevent_self_review": false,
12
+ # "wait_timer": 0
13
+ # } } } }
14
+ #
15
+ # For each declared environment it applies:
16
+ # - required reviewers (the human approval gate), resolved from usernames
17
+ # ("login") and team slugs ("org/team-slug") to reviewer ids
18
+ # - prevent_self_review / wait_timer protection rules
19
+ # - a custom deployment branch policy pinned to the environment's branch,
20
+ # so only that branch can deploy to the environment
21
+ #
22
+ # Branch resolution order: .branch → deploy.branches[<name>] → <name> itself.
23
+ #
24
+ # Entirely optional: repos without github.environments in .lisa.config.json
25
+ # are left untouched. Provisioning matters because GitHub silently
26
+ # auto-creates an environment WITHOUT protection rules the first time a
27
+ # workflow references it — an approval gate bound to a non-provisioned
28
+ # environment gates on nothing.
29
+ #
30
+ # Usage:
31
+ # lisa-github-environments.sh [options] [project-path]
32
+ #
33
+ # Options:
34
+ # -n, --dry-run Show the environment payloads without applying them
35
+ # -v, --verbose Show detailed output
36
+ # -h, --help Show this help message
37
+ #
38
+ # Requires:
39
+ # - gh CLI (authenticated with repo admin permissions)
40
+ # - jq
41
+ #
42
+
43
+ set -eo pipefail
44
+
45
+ RED='\033[0;31m'
46
+ GREEN='\033[0;32m'
47
+ YELLOW='\033[0;33m'
48
+ BLUE='\033[0;34m'
49
+ NC='\033[0m'
50
+
51
+ DRY_RUN=false
52
+ VERBOSE=false
53
+ PROJECT_PATH=""
54
+ REPO=""
55
+
56
+ log_info() { echo -e "${BLUE}ℹ${NC} $1"; }
57
+ log_success() { echo -e "${GREEN}✓${NC} $1"; }
58
+ log_warning() { echo -e "${YELLOW}⚠${NC} $1"; }
59
+ log_error() { echo -e "${RED}✗${NC} $1" >&2; }
60
+ log_verbose() { [[ "$VERBOSE" == "true" ]] && echo -e " $1" || true; }
61
+
62
+ show_help() {
63
+ sed -n '2,38p' "$0" | sed 's/^# \{0,1\}//'
64
+ }
65
+
66
+ read_environments() {
67
+ local project_path="$1"
68
+ local envs="{}"
69
+
70
+ if [[ -f "$project_path/.lisa.config.json" ]]; then
71
+ if ! envs=$(jq '.github.environments // {}' "$project_path/.lisa.config.json" 2>/dev/null); then
72
+ log_warning ".lisa.config.json could not be parsed — ignoring github.environments" >&2
73
+ envs="{}"
74
+ fi
75
+ fi
76
+
77
+ echo "$envs"
78
+ }
79
+
80
+ resolve_branch() {
81
+ local project_path="$1"
82
+ local name="$2"
83
+ local env_json="$3"
84
+ local branch
85
+
86
+ branch=$(echo "$env_json" | jq -r '.branch // empty')
87
+ if [[ -z "$branch" && -f "$project_path/.lisa.config.json" ]]; then
88
+ branch=$(jq -r --arg n "$name" '.deploy.branches[$n] // empty' "$project_path/.lisa.config.json" 2>/dev/null) || branch=""
89
+ fi
90
+
91
+ echo "${branch:-$name}"
92
+ }
93
+
94
+ # Resolves a reviewer entry to the {type, id} shape the environments API
95
+ # expects. Entries containing "/" are org team slugs; anything else is a user.
96
+ resolve_reviewer() {
97
+ local entry="$1"
98
+ local id
99
+
100
+ if [[ "$entry" == */* ]]; then
101
+ local org="${entry%%/*}"
102
+ local slug="${entry#*/}"
103
+ if ! id=$(gh api "orgs/$org/teams/$slug" --jq .id 2>/dev/null); then
104
+ log_error "Could not resolve team reviewer '$entry' — check the org/team-slug and your token's read:org scope"
105
+ return 1
106
+ fi
107
+ jq -n --argjson id "$id" '{type: "Team", id: $id}'
108
+ else
109
+ if ! id=$(gh api "users/$entry" --jq .id 2>/dev/null); then
110
+ log_error "Could not resolve user reviewer '$entry' — check the username"
111
+ return 1
112
+ fi
113
+ jq -n --argjson id "$id" '{type: "User", id: $id}'
114
+ fi
115
+ }
116
+
117
+ build_payload() {
118
+ local env_json="$1"
119
+ local reviewers_json="$2"
120
+
121
+ jq -n \
122
+ --argjson reviewers "$reviewers_json" \
123
+ --argjson prevent "$(echo "$env_json" | jq '.prevent_self_review // false')" \
124
+ --argjson wait "$(echo "$env_json" | jq '.wait_timer // 0')" \
125
+ '{
126
+ wait_timer: $wait,
127
+ prevent_self_review: $prevent,
128
+ reviewers: $reviewers,
129
+ deployment_branch_policy: {
130
+ protected_branches: false,
131
+ custom_branch_policies: true
132
+ }
133
+ }'
134
+ }
135
+
136
+ ensure_branch_policy() {
137
+ local name="$1"
138
+ local branch="$2"
139
+ local existing
140
+
141
+ existing=$(gh api "repos/$REPO/environments/$name/deployment-branch-policies" --jq '.branch_policies[].name' 2>/dev/null) || existing=""
142
+ if echo "$existing" | grep -qxF "$branch"; then
143
+ log_verbose "Deployment branch policy '$branch' already present on '$name'"
144
+ return 0
145
+ fi
146
+
147
+ if jq -n --arg b "$branch" '{name: $b}' | gh api -X POST "repos/$REPO/environments/$name/deployment-branch-policies" --input - > /dev/null 2>&1; then
148
+ log_success "Pinned '$name' deployments to branch '$branch'"
149
+ else
150
+ log_warning "Could not create deployment branch policy '$branch' on '$name'"
151
+ fi
152
+ }
153
+
154
+ provision_environment() {
155
+ local name="$1"
156
+ local env_json="$2"
157
+ local branch
158
+ branch=$(resolve_branch "$PROJECT_PATH" "$name" "$env_json")
159
+
160
+ local require_approval
161
+ require_approval=$(echo "$env_json" | jq -r '.require_approval // false')
162
+
163
+ local reviewer_entries
164
+ reviewer_entries=$(echo "$env_json" | jq -r '.reviewers // [] | .[]')
165
+
166
+ # An approval gate with nobody able to approve would silently gate on
167
+ # nothing — refuse rather than provision a no-op.
168
+ if [[ "$require_approval" == "true" && -z "$reviewer_entries" ]]; then
169
+ log_error "Environment '$name' has require_approval: true but no reviewers — list at least one username or org/team-slug in github.environments.$name.reviewers"
170
+ exit 1
171
+ fi
172
+
173
+ local reviewers_json="[]"
174
+ local entry reviewer
175
+ while IFS= read -r entry; do
176
+ [[ -z "$entry" ]] && continue
177
+ if ! reviewer=$(resolve_reviewer "$entry"); then
178
+ exit 1
179
+ fi
180
+ reviewers_json=$(echo "$reviewers_json" | jq --argjson r "$reviewer" '. + [$r]')
181
+ done <<< "$reviewer_entries"
182
+
183
+ local payload
184
+ payload=$(build_payload "$env_json" "$reviewers_json")
185
+ log_verbose "Environment '$name' payload: $(echo "$payload" | jq -c .)"
186
+
187
+ if [[ "$DRY_RUN" == "true" ]]; then
188
+ log_info "[DRY RUN] Would provision environment '$name' (branch: $branch):"
189
+ echo "$payload" | jq .
190
+ log_info "[DRY RUN] Would pin deployment branch policy '$branch' on '$name'"
191
+ return 0
192
+ fi
193
+
194
+ local response
195
+ if ! response=$(echo "$payload" | gh api -X PUT "repos/$REPO/environments/$name" --input - 2>&1); then
196
+ # Environments with protection rules need a public repo or a paid plan —
197
+ # skip gracefully like the rulesets script does.
198
+ if echo "$response" | grep -qiE "HTTP 403|HTTP 422|upgrade to github"; then
199
+ log_warning "Environments are not available on this repository's plan — skipped '$name'. Approval gates will not be enforced until the repo is public or on a paid plan."
200
+ return 0
201
+ fi
202
+ log_error "Failed to provision environment '$name': $response"
203
+ exit 1
204
+ fi
205
+ log_success "Provisioned environment '$name' (branch: $branch)"
206
+
207
+ ensure_branch_policy "$name" "$branch"
208
+ }
209
+
210
+ main() {
211
+ while [[ $# -gt 0 ]]; do
212
+ case $1 in
213
+ -n|--dry-run) DRY_RUN=true; shift ;;
214
+ -v|--verbose) VERBOSE=true; shift ;;
215
+ -h|--help) show_help; exit 0 ;;
216
+ -*) log_error "Unknown option: $1"; show_help; exit 1 ;;
217
+ *) PROJECT_PATH="$1"; shift ;;
218
+ esac
219
+ done
220
+
221
+ PROJECT_PATH="${PROJECT_PATH:-.}"
222
+ PROJECT_PATH="$(cd "$PROJECT_PATH" && pwd)"
223
+
224
+ if ! command -v gh &> /dev/null || ! command -v jq &> /dev/null; then
225
+ log_error "Requires gh and jq"
226
+ exit 1
227
+ fi
228
+
229
+ local envs
230
+ envs=$(read_environments "$PROJECT_PATH")
231
+ if [[ "$(echo "$envs" | jq 'length')" == "0" ]]; then
232
+ log_info "No github.environments configured in .lisa.config.json — skipping"
233
+ return 0
234
+ fi
235
+
236
+ if ! gh auth status &> /dev/null; then
237
+ log_error "GitHub CLI is not authenticated. Run 'gh auth login' first."
238
+ exit 1
239
+ fi
240
+
241
+ cd "$PROJECT_PATH"
242
+ REPO=$(gh repo view --json nameWithOwner -q '.nameWithOwner' 2>/dev/null) || {
243
+ log_error "Could not determine repository for $PROJECT_PATH"
244
+ exit 1
245
+ }
246
+ log_info "Repository: $REPO"
247
+
248
+ local name env_json
249
+ while IFS= read -r name; do
250
+ [[ -z "$name" ]] && continue
251
+ env_json=$(echo "$envs" | jq --arg n "$name" '.[$n]')
252
+ provision_environment "$name" "$env_json"
253
+ done <<< "$(echo "$envs" | jq -r 'keys[]')"
254
+ }
255
+
256
+ main "$@"
@@ -8,6 +8,9 @@
8
8
  # 3. CI deploy key + DEPLOY_KEY secret (setup-deploy-key.sh --yes),
9
9
  # so release workflows can push version bumps through the rulesets'
10
10
  # DeployKey bypass.
11
+ # 4. Deployment environments with required-reviewer approval gates
12
+ # (lisa-github-environments.sh), from the optional
13
+ # github.environments block in .lisa.config.json.
11
14
  #
12
15
  # Usage:
13
16
  # lisa-github-repo-setup.sh [options] [project-path]
@@ -35,7 +38,7 @@ while [[ $# -gt 0 ]]; do
35
38
  case $1 in
36
39
  -n|--dry-run) DRY_RUN=true; PASSTHROUGH+=("--dry-run"); shift ;;
37
40
  -v|--verbose) VERBOSE=true; PASSTHROUGH+=("--verbose"); shift ;;
38
- -h|--help) sed -n '2,24p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
41
+ -h|--help) sed -n '2,27p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
39
42
  -*) echo "Unknown option: $1" >&2; exit 1 ;;
40
43
  *) PROJECT_PATH="$1"; shift ;;
41
44
  esac
@@ -44,15 +47,15 @@ done
44
47
  PROJECT_PATH="${PROJECT_PATH:-.}"
45
48
  PROJECT_PATH="$(cd "$PROJECT_PATH" && pwd)"
46
49
 
47
- echo "==> Step 1/3: repository settings"
50
+ echo "==> Step 1/4: repository settings"
48
51
  bash "$SCRIPT_DIR/lisa-github-repo-settings.sh" "${PASSTHROUGH[@]}" "$PROJECT_PATH"
49
52
 
50
53
  echo ""
51
- echo "==> Step 2/3: rulesets"
54
+ echo "==> Step 2/4: rulesets"
52
55
  bash "$SCRIPT_DIR/lisa-github-rulesets.sh" --yes "${PASSTHROUGH[@]}" "$PROJECT_PATH"
53
56
 
54
57
  echo ""
55
- echo "==> Step 3/3: deploy key"
58
+ echo "==> Step 3/4: deploy key"
56
59
  if [[ "$DRY_RUN" == "true" ]]; then
57
60
  echo "[DRY RUN] Would ensure a write-access deploy key + DEPLOY_KEY secret exist"
58
61
  else
@@ -69,5 +72,9 @@ else
69
72
  fi
70
73
  fi
71
74
 
75
+ echo ""
76
+ echo "==> Step 4/4: deployment environments"
77
+ bash "$SCRIPT_DIR/lisa-github-environments.sh" "${PASSTHROUGH[@]}" "$PROJECT_PATH"
78
+
72
79
  echo ""
73
80
  echo "✓ GitHub repository governance setup complete"
@@ -123,6 +123,31 @@ Enterprise-grade release management:
123
123
  - Sentry release creation
124
124
  - Jira release creation
125
125
  - Compliance validation (SOC2, ISO27001, HIPAA, PCI-DSS)
126
+ - Optional human-approval gate (`require_approval` + `approval_environment`)
127
+
128
+ **Human-approval gate**: pass `require_approval: true` and the run pauses at the
129
+ `🚦 Release Approval` job until a required reviewer approves it in the Actions UI.
130
+ The pause is enforced by the GitHub Environment named in `approval_environment`
131
+ (falls back to `environment`, i.e. the branch name):
132
+
133
+ ```yaml
134
+ uses: CodySwannGT/lisa/.github/workflows/release.yml@main
135
+ with:
136
+ require_approval: true
137
+ approval_environment: 'production'
138
+ ```
139
+
140
+ Because everything downstream (versioning, GitHub Release, and any deploy job
141
+ that `needs: release`) chains off this gate, approval blocks the whole pipeline.
142
+ The Lisa stack `deploy.yml` templates wire both inputs automatically from the
143
+ optional `github.environments` block in `.lisa.config.json`, mapping the branch
144
+ to its friendly environment name (`main` → `production` via `deploy.branches`).
145
+ The environment itself — required reviewers and a deployment branch policy —
146
+ is provisioned by `/lisa:setup:github-repo`. Provision before enabling the gate:
147
+ GitHub auto-creates unprovisioned environments **without** protection rules, so
148
+ an unprovisioned gate blocks nothing. Not yet wired for rails
149
+ (`release-rails.yml` doesn't thread approval inputs) or harper-fabric (no
150
+ release.yml call).
126
151
 
127
152
  **Blackout Periods** (configurable):
128
153
  - Production: No weekends, no late nights (10 PM - 6 AM)
@@ -576,7 +601,11 @@ with:
576
601
  approval_environment: 'production'
577
602
  ```
578
603
 
579
- **Note**: Create the environment in **Settings** > **Environments** first.
604
+ **Note**: Create the environment in **Settings** > **Environments** first (or
605
+ declare it under `github.environments` in `.lisa.config.json` and run
606
+ `/lisa:setup:github-repo`). Unlike release.yml's environment-enforced gate,
607
+ quality.yml's `approval_gate` job only records an audit artifact — it does not
608
+ pause the run. For an enforced human gate, use release.yml's `require_approval`.
580
609
 
581
610
  ### Custom Node Version
582
611