Skip to content

Commit a4f4469

Browse files
[Feature]: Introduce full GitHub Actions automation suite for repository lifecycle (CI/CD, linting, and issue-driven repo creation) (#14)
## Description This pull request introduces a comprehensive GitHub Actions automation suite for repository management, including CI/CD, linting, and a multi-stage issue-driven workflow for creating new repositories via Terraform. The workflows automate validation, assignment, approval, and fulfillment of repository creation requests, as well as infrastructure provisioning and feedback to users. The most important changes are: **Repository Creation Workflow Automation:** * Added `.github/workflows/validate.yml` to automate validation of "create-repo" issues, including form parsing, validation, branch/PR creation, auto-labeling, auto-assignment, and auto-merge/squash of PRs. Also includes bot-based PR approval and user feedback via comments. * Added `.github/workflows/assign.yml` to automatically assign new "create-repo" issues to a reviewer team and acknowledge the request with a comment. **CI/CD and Linting Workflows:** * Added `.github/workflows/ci.yml` to run Terraform CI on pull requests affecting `.tf` files, including speculative planning and posting the plan as a PR comment. * Added `.github/workflows/fullfill.yml` for CD, which applies Terraform on pushes to `main`, acknowledges successful merges, and closes the associated issue with a comment. * Added `.github/workflows/Linter.yml` to run super-linter on pull requests for code quality checks. --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent e346a71 commit a4f4469

5 files changed

Lines changed: 629 additions & 0 deletions

File tree

.github/workflows/Linter.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Linter
2+
3+
run-name: "Linter - [${{ github.event.pull_request.title }} #${{ github.event.pull_request.number }}] by @${{ github.actor }}"
4+
5+
on: [pull_request]
6+
7+
concurrency:
8+
group: ${{ github.workflow }}-${{ github.ref }}
9+
cancel-in-progress: true
10+
11+
permissions:
12+
contents: read
13+
statuses: write
14+
15+
jobs:
16+
Lint:
17+
name: Lint code base
18+
runs-on: ubuntu-latest
19+
steps:
20+
- name: Checkout repo
21+
uses: actions/checkout@v4
22+
with:
23+
fetch-depth: 0
24+
25+
- name: Lint code base
26+
uses: super-linter/super-linter/slim@latest
27+
env:
28+
GITHUB_TOKEN: ${{ github.token }}

.github/workflows/assign.yml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
name: Create Repo - Assign
2+
on:
3+
issues:
4+
types:
5+
- opened
6+
7+
permissions:
8+
contents: read
9+
10+
env:
11+
number_of_assignees: 1
12+
13+
jobs:
14+
assign:
15+
name: Assign new issue
16+
runs-on: ubuntu-latest
17+
if: contains(github.event.issue.labels.*.name, 'create-repo') && github.event.action == 'opened'
18+
steps:
19+
- name: Generate app token
20+
uses: actions/create-github-app-token@v1
21+
id: authenticate
22+
with:
23+
app-id: ${{ secrets.APP_ID }}
24+
private-key: ${{ secrets.APP_PEM }}
25+
26+
- name: Acknowledge
27+
uses: actions/github-script@v7
28+
env:
29+
reviewer_team: ${{ vars.reviewer_team }}
30+
with:
31+
github-token: ${{ steps.authenticate.outputs.token }}
32+
script: |
33+
const body = `
34+
@${context.actor} : Hey! Let's get this issue assigned to someone from the @${context.repo.owner}/${process.env.reviewer_team} team while we validate the request.
35+
`
36+
37+
github.rest.issues.createComment({
38+
...context.repo,
39+
issue_number: context.issue.number,
40+
body
41+
})
42+
43+
- name: Auto-assign issue
44+
uses: pozil/auto-assign-issue@v2.0.0
45+
with:
46+
repo-token: ${{ steps.authenticate.outputs.token }}
47+
teams: ${{ vars.reviewer_team }}
48+
numOfAssignee: ${{ env.number_of_assignees }}

.github/workflows/ci.yml

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
name: CI
2+
3+
on:
4+
workflow_dispatch:
5+
pull_request:
6+
branches:
7+
- main
8+
paths:
9+
# All tf files
10+
- '**/*.tf'
11+
12+
concurrency:
13+
group: ${{ github.workflow }}-${{ github.ref }}
14+
cancel-in-progress: true
15+
16+
permissions:
17+
contents: read
18+
statuses: write
19+
id-token: write
20+
21+
env:
22+
ARM_CLIENT_ID: ${{ vars.AZURE_CLIENT_ID }}
23+
ARM_TENANT_ID: ${{ vars.AZURE_TENANT_ID }}
24+
GITHUB_TOKEN: ${{ secrets.PAT }}
25+
ARM_USE_OIDC: true
26+
27+
jobs:
28+
CI:
29+
name: CI
30+
runs-on: ubuntu-latest
31+
environment: prod
32+
steps:
33+
- name: Generate app token
34+
uses: actions/create-github-app-token@v1
35+
id: authenticate
36+
with:
37+
app-id: ${{ secrets.APP_ID }}
38+
private-key: ${{ secrets.APP_PEM }}
39+
40+
- uses: actions/checkout@v4
41+
42+
- run: terraform init
43+
44+
- name: Create a speculative plan
45+
id: tfplan
46+
shell: pwsh
47+
run: |
48+
# Create a speculative plan
49+
$plan = terraform plan -lock=false -no-color
50+
Set-Content -Path 'tfplan' -Value $plan -Encoding utf8
51+
52+
terraform plan -lock=false
53+
54+
- name: Write plan in pr comment
55+
uses: actions/github-script@v7
56+
if: github.event_name == 'pull_request'
57+
env:
58+
plan: ${{ steps.tfplan.outputs.plan }}
59+
with:
60+
github-token: ${{ steps.authenticate.outputs.token }}
61+
script: |
62+
const fs = require('fs')
63+
const fileContent = fs.readFileSync('tfplan', 'utf8')
64+
65+
const body = `
66+
Here is the plan for the changes in this PR:
67+
68+
<details>
69+
<summary>Terraform Plan</summary>
70+
71+
\`\`\`terraform
72+
${fileContent}
73+
\`\`\`
74+
75+
`
76+
77+
github.rest.issues.createComment({
78+
...context.repo,
79+
issue_number: context.issue.number,
80+
body
81+
})

.github/workflows/fulfill.yml

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
name: CD
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
branches:
7+
- main
8+
9+
concurrency:
10+
group: ${{ github.workflow }}
11+
12+
permissions:
13+
id-token: write # Needed for OIDC auth in Terraform
14+
15+
env:
16+
ARM_CLIENT_ID: ${{ vars.AZURE_CLIENT_ID }}
17+
ARM_TENANT_ID: ${{ vars.AZURE_TENANT_ID }}
18+
ARM_USE_OIDC: true
19+
20+
jobs:
21+
CD:
22+
name: CD
23+
runs-on: ubuntu-latest
24+
environment: prod
25+
steps:
26+
- name: Generate app token
27+
uses: actions/create-github-app-token@v1
28+
id: authenticate
29+
with:
30+
app-id: ${{ secrets.APP_ID }}
31+
private-key: ${{ secrets.APP_PEM }}
32+
owner: ${{ github.repository_owner }}
33+
34+
- name: Checkout repo
35+
uses: actions/checkout@v4
36+
with:
37+
fetch-depth: 0
38+
token: ${{ steps.authenticate.outputs.token }}
39+
40+
- name: Get PR data
41+
uses: actions/github-script@v7
42+
if: github.event_name == 'push'
43+
id: get_pr_data
44+
with:
45+
github-token: ${{ steps.authenticate.outputs.token }}
46+
script: |
47+
return (
48+
await github.rest.repos.listPullRequestsAssociatedWithCommit({
49+
...context.repo,
50+
commit_sha: context.sha,
51+
})
52+
).data[0]
53+
54+
- name: Get issue number
55+
shell: pwsh
56+
id: get_issue_data
57+
if: ${{ steps.get_pr_data.outputs.result != null }}
58+
env:
59+
PR_DATA: ${{ steps.get_pr_data.outputs.result }}
60+
run: |
61+
$prdata = $env:PR_DATA | ConvertFrom-Json
62+
$prTitle = $prdata.title
63+
64+
# Get the issue number from the PR title
65+
if ($prTitle -match '#(\d+)') {
66+
$issueNumber = $matches[1]
67+
Write-Output "Issue number: $issueNumber"
68+
"issue=$true" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
69+
"issueNumber=$issueNumber" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
70+
} else {
71+
Write-Output "No issue number found in the title."
72+
"issue=$false" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
73+
}
74+
75+
- name: Acknowledge
76+
uses: actions/github-script@v7
77+
if: ${{ steps.get_issue_data.outputs.issue == 'true' }}
78+
env:
79+
issue_number: ${{ steps.get_issue_data.outputs.issueNumber }}
80+
with:
81+
github-token: ${{ steps.authenticate.outputs.token }}
82+
script: |
83+
const body = `
84+
@${context.actor} : All looks good! Merged the request now so we can get the repository created for you.
85+
`
86+
87+
github.rest.issues.createComment({
88+
...context.repo,
89+
issue_number: process.env.issue_number,
90+
body
91+
})
92+
93+
- run: terraform init
94+
95+
- name: Terraform apply
96+
env:
97+
# GITHUB_TOKEN: ${{ steps.authenticate.outputs.token }} # App has issues with GH permissions team lookup and generate repo from template
98+
GITHUB_TOKEN: ${{ secrets.PAT }}
99+
run: terraform apply -auto-approve
100+
101+
- name: Close initial issue
102+
if: ${{ steps.get_issue_data.outputs.issue == 'true' }}
103+
shell: pwsh
104+
env:
105+
GITHUB_ENTERPRISE_TOKEN: ${{ steps.authenticate.outputs.token }}
106+
ISSUE_NUMBER: ${{ steps.get_issue_data.outputs.issueNumber }}
107+
PR_DATA: ${{ steps.get_pr_data.outputs.result }}
108+
run: |
109+
$prdata = $env:PR_DATA | ConvertFrom-Json
110+
$prNumber = $prdata.number
111+
112+
'::group::Authenticate to GitHub'
113+
$env:GITHUB_ENTERPRISE_TOKEN | gh auth login --hostname $env:GH_HOST --with-token
114+
gh auth status
115+
116+
'::group::Close issue'
117+
gh issue close $env:ISSUE_NUMBER --comment "With PR #$prNumber being closed, we can close this issue." --reason completed

0 commit comments

Comments
 (0)