From 6a4107bc3c30cff5d2ac29186125bdb4d42227fb Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 4 Aug 2024 15:35:10 +0200 Subject: [PATCH 1/6] starting point --- .github/ISSUE_TEMPLATE/Create-Repo.yml | 81 ++++++ .github/workflows/Linter.yml | 28 ++ .github/workflows/assign.yml | 48 ++++ .github/workflows/ci.yml | 81 ++++++ .github/workflows/fullfill.yml | 117 ++++++++ .github/workflows/validate.yml | 355 +++++++++++++++++++++++++ 6 files changed, 710 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/Create-Repo.yml create mode 100644 .github/workflows/Linter.yml create mode 100644 .github/workflows/assign.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/fullfill.yml create mode 100644 .github/workflows/validate.yml diff --git a/.github/ISSUE_TEMPLATE/Create-Repo.yml b/.github/ISSUE_TEMPLATE/Create-Repo.yml new file mode 100644 index 0000000..67f3802 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Create-Repo.yml @@ -0,0 +1,81 @@ +# GitHub Enterprise Documentation: +# - https://github.com/andyfeller/issueops-createrepo +# - https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms +# - https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema +# +name: Create Repository +description: Request creation of GitHub Repository +labels: + - create-repo +body: + - type: markdown + attributes: + value: | + Need a new repository? + + Fill out this form to get it started. + More info about the repository product, please check [GitHub Repository](). + - type: dropdown + id: organization + attributes: + label: Organization + description: Select an organization to create the repo. + options: + - dnb-acme + - dnb-tooling + default: 0 + validations: + required: true + - type: input + id: name + attributes: + label: Name + description: Great repository names are short and memorable. + validations: + required: true + - type: input + id: description + attributes: + label: Description + - type: dropdown + id: visibility + attributes: + label: Visibility + description: | + - **Internal**: Enterprise members can see this repository. You choose who can commit. + - **Private**: You choose who can see and commit to this repository. + options: + - Internal + - Private + default: 0 + validations: + required: true + - type: textarea + id: private-visibility-justification + attributes: + label: Private visibility justification + description: | + Understanding the business case for `Private` visibility instead of `Internal`. + - type: dropdown + id: type + attributes: + label: Type + description: Select a type. For more info about the repo types, please check [Repository Archetypes](). + options: + - Action + - FunctionApp + - PSModule + - Workflow + - '-----------' + - Bare + - Template + default: 0 + validations: + required: true + - type: input + id: business-application-id + attributes: + label: Business Application ID + description: The id used to link the business application to a CI in the CMBD. Must start with 'BS' or 'BSN'. + validations: + required: true diff --git a/.github/workflows/Linter.yml b/.github/workflows/Linter.yml new file mode 100644 index 0000000..66d3ef4 --- /dev/null +++ b/.github/workflows/Linter.yml @@ -0,0 +1,28 @@ +name: Linter + +run-name: "Linter - [${{ github.event.pull_request.title }} #${{ github.event.pull_request.number }}] by @${{ github.actor }}" + +on: [pull_request] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + statuses: write + +jobs: + Lint: + name: Lint code base + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Lint code base + uses: super-linter/super-linter/slim@latest + env: + GITHUB_TOKEN: ${{ github.token }} diff --git a/.github/workflows/assign.yml b/.github/workflows/assign.yml new file mode 100644 index 0000000..f1ac27d --- /dev/null +++ b/.github/workflows/assign.yml @@ -0,0 +1,48 @@ +name: Create Repo - Assign +on: + issues: + types: + - opened + +permissions: + contents: read + +env: + number_of_assignees: 1 + +jobs: + assign: + name: Assign new issue + runs-on: ubuntu-latest + if: contains(github.event.issue.labels.*.name, 'create-repo') && github.event.action == 'opened' + steps: + - name: Generate app token + uses: actions/create-github-app-token@v1 + id: authenticate + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PEM }} + + - name: Acknowledge + uses: actions/github-script@v7 + env: + reviewer_team: ${{ vars.reviewer_team }} + with: + github-token: ${{ steps.authenticate.outputs.token }} + script: | + const body = ` + @${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. + ` + + github.rest.issues.createComment({ + ...context.repo, + issue_number: context.issue.number, + body + }) + + - name: Auto-assign issue + uses: pozil/auto-assign-issue@v2.0.0 + with: + repo-token: ${{ steps.authenticate.outputs.token }} + teams: ${{ vars.reviewer_team }} + numOfAssignee: ${{ env.number_of_assignees }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ea7188e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,81 @@ +name: CI + +on: + workflow_dispatch: + pull_request: + branches: + - main + paths: + # All tf files + - '**/*.tf' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + statuses: write + id-token: write + +env: + ARM_CLIENT_ID: ${{ vars.AZURE_CLIENT_ID }} + ARM_TENANT_ID: ${{ vars.AZURE_TENANT_ID }} + GITHUB_TOKEN: ${{ secrets.PAT }} + ARM_USE_OIDC: true + +jobs: + CI: + name: CI + runs-on: ubuntu-latest + environment: prod + steps: + - name: Generate app token + uses: actions/create-github-app-token@v1 + id: authenticate + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PEM }} + + - uses: actions/checkout@v4 + + - run: terraform init + + - name: Create a speculative plan + id: tfplan + shell: pwsh + run: | + # Create a speculative plan + $plan = terraform plan -lock=false -no-color + Set-Content -Path 'tfplan' -Value $plan -Encoding utf8 + + terraform plan -lock=false + + - name: Write plan in pr comment + uses: actions/github-script@v7 + if: github.event_name == 'pull_request' + env: + plan: ${{ steps.tfplan.outputs.plan }} + with: + github-token: ${{ steps.authenticate.outputs.token }} + script: | + const fs = require('fs') + const fileContent = fs.readFileSync('tfplan', 'utf8') + + const body = ` + Here is the plan for the changes in this PR: + +
+ Terraform Plan + + \`\`\`terraform + ${fileContent} + \`\`\` + + ` + + github.rest.issues.createComment({ + ...context.repo, + issue_number: context.issue.number, + body + }) diff --git a/.github/workflows/fullfill.yml b/.github/workflows/fullfill.yml new file mode 100644 index 0000000..461eef5 --- /dev/null +++ b/.github/workflows/fullfill.yml @@ -0,0 +1,117 @@ +name: CD + +on: + workflow_dispatch: + push: + branches: + - main + +concurrency: + group: ${{ github.workflow }} + +permissions: + id-token: write # Needed for OIDC auth in Terraform + +env: + ARM_CLIENT_ID: ${{ vars.AZURE_CLIENT_ID }} + ARM_TENANT_ID: ${{ vars.AZURE_TENANT_ID }} + ARM_USE_OIDC: true + +jobs: + CD: + name: CD + runs-on: ubuntu-latest + environment: prod + steps: + - name: Generate app token + uses: actions/create-github-app-token@v1 + id: authenticate + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PEM }} + owner: ${{ github.repository_owner }} + + - name: Checkout repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ steps.authenticate.outputs.token }} + + - name: Get PR data + uses: actions/github-script@v7 + if: github.event_name == 'push' + id: get_pr_data + with: + github-token: ${{ steps.authenticate.outputs.token }} + script: | + return ( + await github.rest.repos.listPullRequestsAssociatedWithCommit({ + ... context.repo, + commit_sha: context.sha, + }) + ).data[0] + + - name: Get issue number + shell: pwsh + id: get_issue_data + if: ${{ steps.get_pr_data.outputs.result != null }} + env: + PR_DATA: ${{ steps.get_pr_data.outputs.result }} + run: | + $prdata = $env:PR_DATA | ConvertFrom-Json + $prTitle = $prdata.title + + # Get the issue number from the PR title + if ($prTitle -match '#(\d+)') { + $issueNumber = $matches[1] + Write-Output "Issue number: $issueNumber" + "issue=$true" | Out-File -FilePath $env:GITHUB_OUTPUT -Append + "issueNumber=$issueNumber" | Out-File -FilePath $env:GITHUB_OUTPUT -Append + } else { + Write-Output "No issue number found in the title." + "issue=$false" | Out-File -FilePath $env:GITHUB_OUTPUT -Append + } + + - name: Acknowledge + uses: actions/github-script@v7 + if: ${{ steps.get_issue_data.outputs.issue == 'true' }} + env: + issue_number: ${{ steps.get_issue_data.outputs.issueNumber }} + with: + github-token: ${{ steps.authenticate.outputs.token }} + script: | + const body = ` + @${context.actor} : All looks good! Merged the request now so we can get the repository created for you. + ` + + github.rest.issues.createComment({ + ...context.repo, + issue_number: process.env.issue_number, + body + }) + + - run: terraform init + + - name: Terraform apply + env: + # GITHUB_TOKEN: ${{ steps.authenticate.outputs.token }} # App has issues with GH permissions team lookup and generate repo from templateæ + GITHUB_TOKEN: ${{ secrets.PAT }} + run: terraform apply -auto-approve + + - name: Close initial issue + if: ${{ steps.get_issue_data.outputs.issue == 'true' }} + shell: pwsh + env: + GITHUB_ENTERPRISE_TOKEN: ${{ steps.authenticate.outputs.token }} + ISSUE_NUMBER: ${{ steps.get_issue_data.outputs.issueNumber }} + PR_DATA: ${{ steps.get_pr_data.outputs.result }} + run: | + $prdata = $env:PR_DATA | ConvertFrom-Json + $prNmber = $prdata.number + + '::group::Authenticate to GitHub' + $env:GITHUB_ENTERPRISE_TOKEN | gh auth login --hostname $env:GH_HOST --with-token + gh auth status + + '::group::Close issue' + gh issue close $env:ISSUE_NUMBER --comment "With PR #$prNmber being closed, we can close this issue." --reason completed diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 0000000..0e796e3 --- /dev/null +++ b/.github/workflows/validate.yml @@ -0,0 +1,355 @@ +name: Create Repo - Validate +on: + issues: + types: + - opened + - edited + +permissions: {} + +jobs: + validate: + name: Validate issue + runs-on: ubuntu-latest + if: contains(github.event.issue.labels.*.name, 'create-repo') && github.event.issue.state == 'open' + steps: + - name: Generate app token + uses: actions/create-github-app-token@v1 + id: authenticate + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PEM }} + + - name: Handle generate app installation token failure + if: ${{ failure() }} + uses: actions/github-script@v7 + with: + script: | + const body = ` + @${context.actor} : Unfortunately, it appears something went wrong in generating app installation token granting. + ` + + github.rest.issues.createComment({ + ...context.repo, + issue_number: context.issue.number, + body + }) + + - name: Retrieve GitHub App User ID + id: app + uses: actions/github-script@v7 + env: + appSlug: ${{ steps.authenticate.outputs.app-slug }} + with: + github-token: ${{ steps.authenticate.outputs.token }} + script: | + const username = `${process.env.appSlug}[bot]` + console.log(`Username: ${username}`) + + const app = (await github.rest.users.getByUsername({ + username: `${username}` + })).data + + console.log(JSON.stringify(app)) + + return app + + - name: Application data + shell: pwsh + env: + DATA: ${{ steps.app.outputs.result }} + run: | + $data = $env:DATA | ConvertFrom-Json + Write-Verbose ($data | Out-String) -Verbose + + - name: Checkout repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ steps.authenticate.outputs.token }} + + - name: Parse issue form + id: parse + uses: zentered/issue-forms-body-parser@v2.2.0 + + - name: Print parsed data + shell: pwsh + env: + DATA: ${{ steps.parse.outputs.data }} + run: | + $data = $env:DATA | ConvertFrom-Json + Write-Verbose ($data | Out-String) -Verbose + + - name: Record issue form results + if: false + uses: actions/github-script@v7 + env: + ISSUE_FORM_JSON: ${{ steps.parse.outputs.data }} + with: + github-token: ${{ steps.authenticate.outputs.token }} + script: | + const issueForm = JSON.parse(process.env.ISSUE_FORM_JSON) + const jsonPretty = JSON.stringify(issueForm, null, 2) + + const body = ` + Nice! Let's validate the issue form so we can get this thing done! + +
+ Issue Form Payload + + \`\`\`json + ${jsonPretty} + \`\`\` +
+ ` + + github.rest.issues.createComment({ + ...context.repo, + issue_number: context.issue.number, + body + }) + + - name: Verify issue form results + id: inputs + uses: actions/github-script@v7 + env: + ISSUE_FORM_JSON: ${{ steps.parse.outputs.data }} + VALIDATE_FILE: ./scripts/validate.js + with: + github-token: ${{ steps.authenticate.outputs.token }} + script: | + const validate = require(process.env.VALIDATE_FILE) + await validate({github, context, core}) + + - name: Handle verify issue form results failure + if: ${{ failure() }} + uses: actions/github-script@v7 + env: + ERRORS: ${{ steps.inputs.outputs.errors }} + with: + github-token: ${{ steps.authenticate.outputs.token }} + script: | + const errors = JSON.parse(process.env.ERRORS) + const errorsPretty = errors.map(error => `1. ${error}`).join("\r\n") + const body = ` + @${context.actor} : Unfortunately, it appears one or more validation issues arose or something went wrong. + + ${errorsPretty} + ` + + github.rest.issues.createComment({ + ...context.repo, + issue_number: context.issue.number, + body + }) + + - name: Configure git + shell: pwsh + env: + APP_USER_OBJECT: ${{ steps.app.outputs.result }} + run: | + $app = ($env:APP_USER_OBJECT | ConvertFrom-Json) + git config --global user.name "$($app.login)" + git config --global user.email "$($app.id)+$($app.login)@users.noreply.github.com" + git config --global --list + + - name: Build terraform file + id: build + shell: pwsh + env: + GITHUB_TOKEN: ${{ steps.authenticate.outputs.token }} + ISSUE_FORM_JSON: ${{ steps.parse.outputs.data }} + ISSUE_NUMBER: ${{ github.event.issue.number }} + run: | + $branchName = "create-repo-$env:ISSUE_NUMBER" + Write-Host "::group::Create branch [$branchName]" + git fetch + git checkout -b $branchName + $upstreamBranches = git branch -r + Write-Verbose 'Upstream branches' -Verbose + Write-Verbose ($upstreamBranches | Out-String) -Verbose + $branchExists = ($upstreamBranches | ForEach-Object { $_ -Match "origin/$branchName" }) -contains $true + if ($branchExists) { + Write-Verbose "Branch [$branchName] exists on remote, pulling changes." -Verbose + git pull origin $branchName --rebase + } + + Write-Host '::group::Process data from issue' + $repository = $env:ISSUE_FORM_JSON | ConvertFrom-Json -AsHashtable + Write-Verbose ($repository | Out-String) -Verbose + + Write-Host '::group::Build replacement table' + $type = [string]($repository.type.text).ToLower() + + $replacements = @{ + name = $repository.name.text + organization = $repository.organization.text + description = $repository.description.text + visibility = $repository.visibility.text + type = $type + business_application_id = $repository['business-application-id'].text + } + Write-Verbose ($replacements | Out-String) -Verbose + + Write-Host '::group::Build Terraform file' + $filePath = Join-Path -Path $PWD -ChildPath "$($repository.name.text).tf" + $repoFileTemplate = Join-Path -Path 'template' -ChildPath "$type.txt" + $content = Get-Content -Path $repoFileTemplate -Raw + foreach ($item in $replacements.Keys) { + $value = $replacements[$item] + $content = $content.Replace("{{ $item }}", $value) + } + $content | Set-Content -Path $filePath -Force + + Write-Host "::group::File content [$filePath]" + Write-Verbose (Get-Content -Path $filePath -Raw) -Verbose + "file-path=$filePath" | Out-File -FilePath $env:GITHUB_OUTPUT -Append + + Write-Host '::group::Add files' + git add . + git status + Write-Host '::group::Commit changes' + git commit -m "Automated commit" + git status + Write-Host '::group::Push changes' + git push --set-upstream origin $branchName + git status + + Get-ChildItem env: | Format-Table -AutoSize + + - name: PR changes + id: pr + shell: pwsh + env: + GITHUB_ENTERPRISE_TOKEN: ${{ steps.authenticate.outputs.token }} + BRANCH_NAME: create-repo-${{ github.event.issue.number }} + ISSUE_NUMBER: ${{ github.event.issue.number }} + run: | + '::group::Authenticate to GitHub' + $env:GITHUB_ENTERPRISE_TOKEN | gh auth login --hostname $env:GH_HOST --with-token + gh auth status + + '::group::Ensure PR exists' + $pr = gh pr list --state open --base main --head create-repo-${env:ISSUE_NUMBER} --json 'number,title,baseRefName,headRefName' | ConvertFrom-Json + if (-not $pr) { + Write-Host "No PR exists for issue [$env:ISSUE_NUMBER], creation one." + gh pr create --title "Request #$env:ISSUE_NUMBER" --body "Based on request #$env:ISSUE_NUMBER" --base main --head $env:BRANCH_NAME + } + $pr = gh pr list --state open --base main --head create-repo-${env:ISSUE_NUMBER} --json 'number,title,baseRefName,headRefName' | ConvertFrom-Json + if (-not $pr) { + Write-Error "Failed to create PR for issue [$env:ISSUE_NUMBER]" + exit 1 + } + $pr | Select-Object number,title,baseRefName,headRefName | Format-Table -AutoSize + $prNumber = $pr.number + + if (-not $prNumber) { + Write-Error "Failed to create PR for issue [$env:ISSUE_NUMBER]" + exit 1 + } + + "::group::Output PR number [$prNumber] to 'prNumber'" + "prNumber=$prNumber" | Out-File -FilePath $env:GITHUB_OUTPUT -Append + + '::group::Ensure label is added to PR' + gh pr edit $prNumber --add-label create-repo --repo $env:GITHUB_REPOSITORY + + '::group::Ensure actor is assigned to the PR' + gh pr edit $prNumber --add-assignee $env:GITHUB_ACTOR --repo $env:GITHUB_REPOSITORY + + '::group::Ensure PR has auto-merge enabled' + $email = git config --global user.email + # gh pr merge $prNumber --auto --delete-branch --merge --repo $env:GITHUB_REPOSITORY + # gh pr merge $prNumber --auto --delete-branch --squash --repo $env:GITHUB_REPOSITORY --author-email $email + gh pr merge $prNumber --auto --delete-branch --squash --repo $env:GITHUB_REPOSITORY + + - name: Generate app token + uses: actions/create-github-app-token@v1 + id: authenticate_approver + with: + app-id: ${{ secrets.APPROVER_APP_ID }} + private-key: ${{ secrets.APPROVER_APP_PEM }} + + - name: Retrieve GitHub App User ID + id: app_approver + uses: actions/github-script@v7 + env: + appSlug: ${{ steps.authenticate_approver.outputs.app-slug }} + with: + github-token: ${{ steps.authenticate_approver.outputs.token }} + script: | + const username = `${process.env.appSlug}[bot]` + console.log(`Username: ${username}`) + + const app = (await github.rest.users.getByUsername({ + username: `${username}` + })).data + + console.log(JSON.stringify(app)) + + return app + + - name: Application data + shell: pwsh + env: + DATA: ${{ steps.app_approver.outputs.result }} + run: | + $data = $env:DATA | ConvertFrom-Json + Write-Verbose ($data | Out-String) -Verbose + + - name: Approve PR changes + id: pr_approve + shell: pwsh + env: + GITHUB_ENTERPRISE_TOKEN: ${{ steps.authenticate_approver.outputs.token }} + GH_HOST: dnb.ghe.com + PR_NUMBER: ${{ steps.pr.outputs.prNumber }} + APP_USER_OBJECT: ${{ steps.app_approver.outputs.result }} + run: | + '::group::Authenticate to GitHub' + $env:GITHUB_ENTERPRISE_TOKEN | gh auth login --hostname $env:GH_HOST --with-token + gh auth status + + '::group::Configure git' + $app = $env:APP_USER_OBJECT | ConvertFrom-Json + git config --global user.name "$($app.login)" + git config --global user.email "$($app.id)+$($app.login)@users.noreply.github.com" + git config --global --list + + $prNumber = $env:PR_NUMBER + '::group::Ensure PR is approved' + gh pr review $prNumber --approve --repo $env:GITHUB_REPOSITORY + + - name: Confirm issue + uses: actions/github-script@v7 + env: + reviewer_team: ${{ vars.reviewer_team }} + pr_number: ${{ steps.pr.outputs.prNumber }} + with: + github-token: ${{ steps.authenticate.outputs.token }} + script: | + const fs = require('fs') + const filePath = '${{ steps.build.outputs.file-path }}' + const fileName = filePath.split('/').pop() + const fileContent = fs.readFileSync(filePath, 'utf8') + + const body = ` + @${context.actor} : Everything looks good on the surface! We have opened a PR for the repository. + + #${process.env.pr_number} + +
+ Terraform file + + From: [${fileName}] + \`\`\`terraform + ${fileContent} + \`\`\` +
+ ` + + github.rest.issues.createComment({ + ...context.repo, + issue_number: context.issue.number, + body + }) From 0516a667fe89f26621f63a121f6cd484d205f789 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 4 Aug 2024 16:04:30 +0200 Subject: [PATCH 2/6] Fist version --- .github/ISSUE_TEMPLATE/Create-Repo.yml | 81 -------------------------- .github/ISSUE_TEMPLATE/New-Project.yml | 59 +++++++++++++++++++ 2 files changed, 59 insertions(+), 81 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/Create-Repo.yml create mode 100644 .github/ISSUE_TEMPLATE/New-Project.yml diff --git a/.github/ISSUE_TEMPLATE/Create-Repo.yml b/.github/ISSUE_TEMPLATE/Create-Repo.yml deleted file mode 100644 index 67f3802..0000000 --- a/.github/ISSUE_TEMPLATE/Create-Repo.yml +++ /dev/null @@ -1,81 +0,0 @@ -# GitHub Enterprise Documentation: -# - https://github.com/andyfeller/issueops-createrepo -# - https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms -# - https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema -# -name: Create Repository -description: Request creation of GitHub Repository -labels: - - create-repo -body: - - type: markdown - attributes: - value: | - Need a new repository? - - Fill out this form to get it started. - More info about the repository product, please check [GitHub Repository](). - - type: dropdown - id: organization - attributes: - label: Organization - description: Select an organization to create the repo. - options: - - dnb-acme - - dnb-tooling - default: 0 - validations: - required: true - - type: input - id: name - attributes: - label: Name - description: Great repository names are short and memorable. - validations: - required: true - - type: input - id: description - attributes: - label: Description - - type: dropdown - id: visibility - attributes: - label: Visibility - description: | - - **Internal**: Enterprise members can see this repository. You choose who can commit. - - **Private**: You choose who can see and commit to this repository. - options: - - Internal - - Private - default: 0 - validations: - required: true - - type: textarea - id: private-visibility-justification - attributes: - label: Private visibility justification - description: | - Understanding the business case for `Private` visibility instead of `Internal`. - - type: dropdown - id: type - attributes: - label: Type - description: Select a type. For more info about the repo types, please check [Repository Archetypes](). - options: - - Action - - FunctionApp - - PSModule - - Workflow - - '-----------' - - Bare - - Template - default: 0 - validations: - required: true - - type: input - id: business-application-id - attributes: - label: Business Application ID - description: The id used to link the business application to a CI in the CMBD. Must start with 'BS' or 'BSN'. - validations: - required: true diff --git a/.github/ISSUE_TEMPLATE/New-Project.yml b/.github/ISSUE_TEMPLATE/New-Project.yml new file mode 100644 index 0000000..2d20b85 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/New-Project.yml @@ -0,0 +1,59 @@ +# GitHub Enterprise Documentation: +# - https://github.com/andyfeller/issueops-createrepo +# - https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms +# - https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema +# +name: New-Project +description: | + Request creation of a project in the MSX organization. + + A project consists of the following: + - Repository based on a template. +# - Channel in Discord. + +labels: + - New-Project + +body: + - type: dropdown + id: type + attributes: + label: Type + description: Select a type or project. For more info about the project types, please check [Project types](https://psmodule.io/Types). + options: + - Action + - FunctionApp + - PSModule + - Workflow + - '-----------' + - Bare + - Template + default: 0 + validations: + required: true + - type: input + id: name + attributes: + label: Name + description: The name of the project. This will be the repo name. + validations: + required: true + - type: input + id: description + attributes: + label: Description + - type: dropdown + id: visibility + attributes: + label: Visibility + description: | + - **Public**: Anyone on the internet can see this repository. You choose who can commit. + - **Internal**: Enterprise members can see this repository. You can choose who can commit. + - **Private**: You choose who can see and commit to this repository. + options: + - Internal + - Private + - Public + default: 0 + validations: + required: true From 562c7aa4aafcbef60025f861e5e7ecf25f74e3ba Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 5 Oct 2025 15:49:18 +0200 Subject: [PATCH 3/6] Add new project issue template and auto-assign workflow --- .github/workflows/validate.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 0e796e3..68594e4 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -302,7 +302,7 @@ jobs: shell: pwsh env: GITHUB_ENTERPRISE_TOKEN: ${{ steps.authenticate_approver.outputs.token }} - GH_HOST: dnb.ghe.com + GH_HOST: msx.ghe.com PR_NUMBER: ${{ steps.pr.outputs.prNumber }} APP_USER_OBJECT: ${{ steps.app_approver.outputs.result }} run: | From 54cbfe05d7930ce7d8ffd3d49e718b0e69612b24 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 5 Oct 2025 15:51:12 +0200 Subject: [PATCH 4/6] Add fullfill.yml workflow for continuous deployment --- .../workflows/{fullfill.yml => fulfill.yml} | 234 +++++++++--------- 1 file changed, 117 insertions(+), 117 deletions(-) rename .github/workflows/{fullfill.yml => fulfill.yml} (94%) diff --git a/.github/workflows/fullfill.yml b/.github/workflows/fulfill.yml similarity index 94% rename from .github/workflows/fullfill.yml rename to .github/workflows/fulfill.yml index 461eef5..42200b3 100644 --- a/.github/workflows/fullfill.yml +++ b/.github/workflows/fulfill.yml @@ -1,117 +1,117 @@ -name: CD - -on: - workflow_dispatch: - push: - branches: - - main - -concurrency: - group: ${{ github.workflow }} - -permissions: - id-token: write # Needed for OIDC auth in Terraform - -env: - ARM_CLIENT_ID: ${{ vars.AZURE_CLIENT_ID }} - ARM_TENANT_ID: ${{ vars.AZURE_TENANT_ID }} - ARM_USE_OIDC: true - -jobs: - CD: - name: CD - runs-on: ubuntu-latest - environment: prod - steps: - - name: Generate app token - uses: actions/create-github-app-token@v1 - id: authenticate - with: - app-id: ${{ secrets.APP_ID }} - private-key: ${{ secrets.APP_PEM }} - owner: ${{ github.repository_owner }} - - - name: Checkout repo - uses: actions/checkout@v4 - with: - fetch-depth: 0 - token: ${{ steps.authenticate.outputs.token }} - - - name: Get PR data - uses: actions/github-script@v7 - if: github.event_name == 'push' - id: get_pr_data - with: - github-token: ${{ steps.authenticate.outputs.token }} - script: | - return ( - await github.rest.repos.listPullRequestsAssociatedWithCommit({ - ... context.repo, - commit_sha: context.sha, - }) - ).data[0] - - - name: Get issue number - shell: pwsh - id: get_issue_data - if: ${{ steps.get_pr_data.outputs.result != null }} - env: - PR_DATA: ${{ steps.get_pr_data.outputs.result }} - run: | - $prdata = $env:PR_DATA | ConvertFrom-Json - $prTitle = $prdata.title - - # Get the issue number from the PR title - if ($prTitle -match '#(\d+)') { - $issueNumber = $matches[1] - Write-Output "Issue number: $issueNumber" - "issue=$true" | Out-File -FilePath $env:GITHUB_OUTPUT -Append - "issueNumber=$issueNumber" | Out-File -FilePath $env:GITHUB_OUTPUT -Append - } else { - Write-Output "No issue number found in the title." - "issue=$false" | Out-File -FilePath $env:GITHUB_OUTPUT -Append - } - - - name: Acknowledge - uses: actions/github-script@v7 - if: ${{ steps.get_issue_data.outputs.issue == 'true' }} - env: - issue_number: ${{ steps.get_issue_data.outputs.issueNumber }} - with: - github-token: ${{ steps.authenticate.outputs.token }} - script: | - const body = ` - @${context.actor} : All looks good! Merged the request now so we can get the repository created for you. - ` - - github.rest.issues.createComment({ - ...context.repo, - issue_number: process.env.issue_number, - body - }) - - - run: terraform init - - - name: Terraform apply - env: - # GITHUB_TOKEN: ${{ steps.authenticate.outputs.token }} # App has issues with GH permissions team lookup and generate repo from templateæ - GITHUB_TOKEN: ${{ secrets.PAT }} - run: terraform apply -auto-approve - - - name: Close initial issue - if: ${{ steps.get_issue_data.outputs.issue == 'true' }} - shell: pwsh - env: - GITHUB_ENTERPRISE_TOKEN: ${{ steps.authenticate.outputs.token }} - ISSUE_NUMBER: ${{ steps.get_issue_data.outputs.issueNumber }} - PR_DATA: ${{ steps.get_pr_data.outputs.result }} - run: | - $prdata = $env:PR_DATA | ConvertFrom-Json - $prNmber = $prdata.number - - '::group::Authenticate to GitHub' - $env:GITHUB_ENTERPRISE_TOKEN | gh auth login --hostname $env:GH_HOST --with-token - gh auth status - - '::group::Close issue' - gh issue close $env:ISSUE_NUMBER --comment "With PR #$prNmber being closed, we can close this issue." --reason completed +name: CD + +on: + workflow_dispatch: + push: + branches: + - main + +concurrency: + group: ${{ github.workflow }} + +permissions: + id-token: write # Needed for OIDC auth in Terraform + +env: + ARM_CLIENT_ID: ${{ vars.AZURE_CLIENT_ID }} + ARM_TENANT_ID: ${{ vars.AZURE_TENANT_ID }} + ARM_USE_OIDC: true + +jobs: + CD: + name: CD + runs-on: ubuntu-latest + environment: prod + steps: + - name: Generate app token + uses: actions/create-github-app-token@v1 + id: authenticate + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PEM }} + owner: ${{ github.repository_owner }} + + - name: Checkout repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ steps.authenticate.outputs.token }} + + - name: Get PR data + uses: actions/github-script@v7 + if: github.event_name == 'push' + id: get_pr_data + with: + github-token: ${{ steps.authenticate.outputs.token }} + script: | + return ( + await github.rest.repos.listPullRequestsAssociatedWithCommit({ + ... context.repo, + commit_sha: context.sha, + }) + ).data[0] + + - name: Get issue number + shell: pwsh + id: get_issue_data + if: ${{ steps.get_pr_data.outputs.result != null }} + env: + PR_DATA: ${{ steps.get_pr_data.outputs.result }} + run: | + $prdata = $env:PR_DATA | ConvertFrom-Json + $prTitle = $prdata.title + + # Get the issue number from the PR title + if ($prTitle -match '#(\d+)') { + $issueNumber = $matches[1] + Write-Output "Issue number: $issueNumber" + "issue=$true" | Out-File -FilePath $env:GITHUB_OUTPUT -Append + "issueNumber=$issueNumber" | Out-File -FilePath $env:GITHUB_OUTPUT -Append + } else { + Write-Output "No issue number found in the title." + "issue=$false" | Out-File -FilePath $env:GITHUB_OUTPUT -Append + } + + - name: Acknowledge + uses: actions/github-script@v7 + if: ${{ steps.get_issue_data.outputs.issue == 'true' }} + env: + issue_number: ${{ steps.get_issue_data.outputs.issueNumber }} + with: + github-token: ${{ steps.authenticate.outputs.token }} + script: | + const body = ` + @${context.actor} : All looks good! Merged the request now so we can get the repository created for you. + ` + + github.rest.issues.createComment({ + ...context.repo, + issue_number: process.env.issue_number, + body + }) + + - run: terraform init + + - name: Terraform apply + env: + # GITHUB_TOKEN: ${{ steps.authenticate.outputs.token }} # App has issues with GH permissions team lookup and generate repo from templateæ + GITHUB_TOKEN: ${{ secrets.PAT }} + run: terraform apply -auto-approve + + - name: Close initial issue + if: ${{ steps.get_issue_data.outputs.issue == 'true' }} + shell: pwsh + env: + GITHUB_ENTERPRISE_TOKEN: ${{ steps.authenticate.outputs.token }} + ISSUE_NUMBER: ${{ steps.get_issue_data.outputs.issueNumber }} + PR_DATA: ${{ steps.get_pr_data.outputs.result }} + run: | + $prdata = $env:PR_DATA | ConvertFrom-Json + $prNumber = $prdata.number + + '::group::Authenticate to GitHub' + $env:GITHUB_ENTERPRISE_TOKEN | gh auth login --hostname $env:GH_HOST --with-token + gh auth status + + '::group::Close issue' + gh issue close $env:ISSUE_NUMBER --comment "With PR #$prNumber being closed, we can close this issue." --reason completed From 058daaf64dd0a75d4b0e1a86cab09d3fab83628d Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 5 Oct 2025 15:53:04 +0200 Subject: [PATCH 5/6] Update .github/workflows/fulfill.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .github/workflows/fulfill.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/fulfill.yml b/.github/workflows/fulfill.yml index 42200b3..3c3b849 100644 --- a/.github/workflows/fulfill.yml +++ b/.github/workflows/fulfill.yml @@ -46,7 +46,7 @@ jobs: script: | return ( await github.rest.repos.listPullRequestsAssociatedWithCommit({ - ... context.repo, + ...context.repo, commit_sha: context.sha, }) ).data[0] From 47c4675098b3360cd9984f55d98cf6c86f38d877 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 5 Oct 2025 15:53:09 +0200 Subject: [PATCH 6/6] Update .github/workflows/fulfill.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .github/workflows/fulfill.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/fulfill.yml b/.github/workflows/fulfill.yml index 3c3b849..b7b23ef 100644 --- a/.github/workflows/fulfill.yml +++ b/.github/workflows/fulfill.yml @@ -94,7 +94,7 @@ jobs: - name: Terraform apply env: - # GITHUB_TOKEN: ${{ steps.authenticate.outputs.token }} # App has issues with GH permissions team lookup and generate repo from templateæ + # GITHUB_TOKEN: ${{ steps.authenticate.outputs.token }} # App has issues with GH permissions team lookup and generate repo from template GITHUB_TOKEN: ${{ secrets.PAT }} run: terraform apply -auto-approve