Skip to content

Commit 30bfcce

Browse files
committed
adding optional human check after push and before publish pipeline runs.
1 parent 8f3db02 commit 30bfcce

6 files changed

Lines changed: 113 additions & 11 deletions

File tree

src/services/identity-guide-service.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,9 @@ done
382382
rm -f env-body.json
383383
\`\`\`
384384
385-
**Note:** Environment approvals and checks must be configured via the Azure DevOps UI (Project Settings > Environments).
385+
**To require human approval before deploying to an environment:**
386+
1. Go to **Pipelines > Environments > <environment-name>** in Azure DevOps.
387+
2. Open **Approvals and checks** and add an **Approvals** check with the required approvers.
386388
387389
---
388390

src/templates/copilot/identity-setup-prompt.ts

Lines changed: 84 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,43 @@ gh secret set APIM_RESOURCE_GROUP_${env.toUpperCase()} --body "\${APIM_RG_${env.
4848
gh secret set APIM_SERVICE_NAME_${env.toUpperCase()} --body "\${APIM_NAME_${env.toUpperCase()}}" --env ${env}`
4949
).join('\n\n');
5050

51+
const ghEnvironmentSetupCmds = config.environments.map((env) =>
52+
`# ${env} environment: create + baseline protection via GitHub API
53+
cat > env-${env}-config.json <<'JSON'
54+
{
55+
"wait_timer": 0,
56+
"prevent_self_review": true,
57+
"deployment_branch_policy": {
58+
"protected_branches": false,
59+
"custom_branch_policies": true
60+
}
61+
}
62+
JSON
63+
64+
gh api --method PUT "repos/\${GITHUB_ORG}/\${GITHUB_REPO}/environments/${env}" --input env-${env}-config.json
65+
66+
# Restrict deployments to main branch
67+
gh api --method POST "repos/\${GITHUB_ORG}/\${GITHUB_REPO}/environments/${env}/deployment-branch-policies" -f name='main' -f type='branch'
68+
69+
# Optional: require deployment approval reviewers (replace IDs with your approver IDs)
70+
# Resolve a user ID: gh api "users/<github-login>" --jq .id
71+
cat > env-${env}-reviewers.json <<'JSON'
72+
{
73+
"wait_timer": 0,
74+
"prevent_self_review": true,
75+
"reviewers": [
76+
{ "type": "User", "id": 12345678 }
77+
],
78+
"deployment_branch_policy": {
79+
"protected_branches": false,
80+
"custom_branch_policies": true
81+
}
82+
}
83+
JSON
84+
85+
gh api --method PUT "repos/\${GITHUB_ORG}/\${GITHUB_REPO}/environments/${env}" --input env-${env}-reviewers.json`
86+
).join('\n\n');
87+
5188
return `# Setup GitHub Actions Identity for APIOps
5289
5390
> **How to use:** Open this file in VS Code with GitHub Copilot and ask
@@ -238,15 +275,59 @@ ${envFedCreds}
238275
239276
---
240277
241-
## Step 5 — Create GitHub Environments
278+
## Step 5 — Configure GitHub Environments with CLI (Create + Protection + Approvals)
279+
280+
> Copilot: run these command-line commands (do not use manual UI-only setup) to create environments and configure protection rules.
242281
282+
**On macOS/Linux (Bash):**
243283
\`\`\`bash
284+
${ghEnvironmentSetupCmds}
285+
\`\`\`
286+
287+
**On Windows (PowerShell):**
288+
\`\`\`powershell
244289
${config.environments.map((env) =>
245-
`# Create the ${env} environment (requires GitHub CLI)
246-
gh api --method PUT "repos/\${GITHUB_ORG}/\${GITHUB_REPO}/environments/${env}"`
290+
`# ${env} environment: create + baseline protection via GitHub API
291+
@'
292+
{
293+
"wait_timer": 0,
294+
"prevent_self_review": true,
295+
"deployment_branch_policy": {
296+
"protected_branches": false,
297+
"custom_branch_policies": true
298+
}
299+
}
300+
'@ | Set-Content -Path env-${env}-config.json
301+
302+
gh api --method PUT "repos/\${GITHUB_ORG}/\${GITHUB_REPO}/environments/${env}" --input env-${env}-config.json
303+
304+
# Restrict deployments to main branch
305+
gh api --method POST "repos/\${GITHUB_ORG}/\${GITHUB_REPO}/environments/${env}/deployment-branch-policies" -f name='main' -f type='branch'
306+
307+
# Optional: require deployment approval reviewers (replace IDs with your approver IDs)
308+
# Resolve a user ID: gh api "users/<github-login>" --jq .id
309+
@'
310+
{
311+
"wait_timer": 0,
312+
"prevent_self_review": true,
313+
"reviewers": [
314+
{ "type": "User", "id": 12345678 }
315+
],
316+
"deployment_branch_policy": {
317+
"protected_branches": false,
318+
"custom_branch_policies": true
319+
}
320+
}
321+
'@ | Set-Content -Path env-${env}-reviewers.json
322+
323+
gh api --method PUT "repos/\${GITHUB_ORG}/\${GITHUB_REPO}/environments/${env}" --input env-${env}-reviewers.json`
247324
).join('\n\n')}
248325
\`\`\`
249326
327+
> Rerun note: environment PUT calls are idempotent, but branch-policy creation can return "already exists" on reruns after the first successful create; treat that as expected when main is already configured.
328+
329+
> If reviewer configuration is restricted by repository plan/policy, keep environment creation and branch-policy commands in CLI and apply required reviewers using the same API payload once policy allows it.
330+
250331
---
251332
252333
## Step 6 — Set GitHub Repository Secrets

src/templates/github-actions/publish-workflow.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@ export function generatePublishWorkflow(config: PublishWorkflowConfig): string {
1818
const jobComment = idx === 0
1919
? ` # Automatically deploys to ${env} on push to main (incremental mode) or when selected via workflow_dispatch`
2020
: ` # Deploys to ${env} after ${previousEnvironment} succeeds (sequential promotion).
21-
# To require human approval before deploying to ${env}:
22-
# 1. Go to Settings > Environments > ${env} in your GitHub repository
23-
# 2. Add "Required reviewers" under "Environment protection rules"`;
21+
# Configure environment protection rules to require approval before deploying to ${env}.`;
2422

2523
const jobCondition = `github.event.inputs.ENVIRONMENT == '${env}' || github.event_name == 'push'`;
2624

tests/unit/services/identity-guide-service.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,5 +126,17 @@ describe('identity-guide-service', () => {
126126
expect(guide).toContain('my-rg');
127127
});
128128

129+
it('should include Azure DevOps approvals and checks guidance for human approval', () => {
130+
const guide = identityGuideService.generateAzureDevOpsGuide(
131+
'sub-12345',
132+
'my-rg',
133+
['dev', 'prod']
134+
);
135+
expect(guide).toContain('Pipelines > Environments > <environment-name>');
136+
expect(guide).toContain('Approvals and checks');
137+
expect(guide).toContain('Approvals');
138+
expect(guide).toContain('required approvers');
139+
});
140+
129141
});
130142
});

tests/unit/templates/copilot/identity-setup-prompt.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,15 @@ describe('copilot/identity-setup-prompt', () => {
6666
expect(prompt).toContain('gh api --method PUT');
6767
expect(prompt).toContain('environments/dev');
6868
expect(prompt).toContain('environments/prod');
69+
expect(prompt).toContain('Configure GitHub Environments with CLI');
70+
expect(prompt).toContain('do not use manual UI-only setup');
71+
expect(prompt).toContain('deployment-branch-policies');
72+
expect(prompt).toContain('prevent_self_review');
73+
expect(prompt).toContain('"reviewers"');
74+
expect(prompt).toContain('gh api "users/<github-login>" --jq .id');
75+
expect(prompt).toContain("gh api --method POST \"repos/${GITHUB_ORG}/${GITHUB_REPO}/environments/dev/deployment-branch-policies\"");
76+
expect(prompt).toContain("gh api --method POST \"repos/${GITHUB_ORG}/${GITHUB_REPO}/environments/prod/deployment-branch-policies\"");
77+
expect(prompt).toContain("-f name='main' -f type='branch'");
6978
});
7079

7180
it('should include gh secret set commands for repository secrets', () => {

tests/unit/templates/github-actions/publish-workflow.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -226,14 +226,14 @@ describe('github-actions/publish-workflow', () => {
226226
expect(workflow).toContain("ENVIRONMENT == 'prod' || github.event_name == 'push'");
227227
});
228228

229-
it('should include approval guidance comment for non-first environments', () => {
229+
it('should include generic approval guidance for non-first environments without GitHub settings steps', () => {
230230
const workflow = generatePublishWorkflow({
231231
artifactDir: './apim-artifacts',
232232
environments: ['dev', 'staging'],
233233
});
234-
// Non-first environments should have a comment guiding users to set up required reviewers
235-
expect(workflow).toContain('Required reviewers');
236-
expect(workflow).toContain('Settings > Environments > staging');
234+
expect(workflow).toContain('Configure environment protection rules to require approval before deploying to staging.');
235+
expect(workflow).not.toContain('Settings > Environments > staging');
236+
expect(workflow).not.toContain('Required reviewers');
237237
});
238238

239239
it('should pass commit_id on push trigger via incremental step condition', () => {

0 commit comments

Comments
 (0)