diff --git a/.github/labeler.yml b/.github/labeler.yml index 3ff3fe4..c00d8f6 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,8 +1,12 @@ +ci: + - .dependabot/* + - .github/workflows/* + documentation: - docs/**/* - .github/* - ./*.md -ci: - - .dependabot/* - - .github/workflows/* +terraform: + - examples/**/*.tf + - ./*.tf diff --git a/.github/workflows/ok-to-test.yml b/.github/workflows/ok-to-test.yml new file mode 100644 index 0000000..3af34b3 --- /dev/null +++ b/.github/workflows/ok-to-test.yml @@ -0,0 +1,35 @@ +# If someone with write access comments "/ok-to-test" on a pull request, emit a repository_dispatch event +name: ok-to-test + +on: + issue_comment: + types: [created] + +permissions: + pull-requests: write # For doing the emoji reaction on a PR comment + issues: write # For doing the emoji reaction on an issue comment + contents: write # For executing the repository_dispatch event + +jobs: + ok-to-test: + runs-on: ubuntu-latest + permissions: + pull-requests: write + # Only run for PRs, not issue comments + if: ${{ github.event.issue.pull_request }} + steps: + - name: Generate token + id: app-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.PRIVATE_KEY }} + + - name: Slash Command Dispatch + uses: peter-evans/slash-command-dispatch@v3 + with: + token: ${{ steps.app-token.outputs.token }} + reaction-token: ${{ secrets.GITHUB_TOKEN }} + issue-type: pull-request + commands: ok-to-test + permission: write diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml index d714dbe..7378f91 100644 --- a/.github/workflows/pr-labeler.yml +++ b/.github/workflows/pr-labeler.yml @@ -4,9 +4,9 @@ on: pull_request: jobs: - labeler: + pr-labeler: runs-on: ubuntu-latest steps: - - uses: actions/labeler@v2 + - uses: actions/labeler@v4 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/pr-secrets.yml b/.github/workflows/pr-secrets.yml index 42113b6..2b5286f 100644 --- a/.github/workflows/pr-secrets.yml +++ b/.github/workflows/pr-secrets.yml @@ -1,15 +1,15 @@ -name: PR - TruffleHog Secrets +name: Secret scan on: pull_request: jobs: trufflehog: - name: Secrets + name: trufflehog runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: TruffleHog OSS diff --git a/.github/workflows/pr-terraform.yml b/.github/workflows/pr-terraform.yml index 7cfd80e..4453651 100644 --- a/.github/workflows/pr-terraform.yml +++ b/.github/workflows/pr-terraform.yml @@ -1,4 +1,4 @@ -name: PR - Terraform +name: Terraform on: pull_request: @@ -6,29 +6,31 @@ on: - 'examples/**' - 'tests/**' - '**.tf' - -permissions: - id-token: write - pull-requests: write + repository_dispatch: + types: [ ok-to-test-command ] jobs: - terraform: - name: Terraform + # Branch-based pull request + terraform-pr-branch: + name: terraform-pr-branch runs-on: ubuntu-latest + permissions: + id-token: write + pull-requests: write timeout-minutes: 15 env: - AWS_DEFAULT_REGION: eu-central-1 - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} TF_IN_AUTOMATION: true TFDIR: examples + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository steps: - - name: Checkout - uses: actions/checkout@v3 + - name: Branch based PR checkout + uses: actions/checkout@v4 - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v1 + if: startsWith(github.repository, 'ventx/terraform-aws-') + uses: aws-actions/configure-aws-credentials@v4 with: - aws-region: ${{ env.AWS_DEFAULT_REGION }} + aws-region: ${{ secrets.AWS_DEFAULT_REGION }} role-to-assume: ${{ secrets.AWS_GH_OIDC }} - name: Set Versions @@ -37,7 +39,7 @@ jobs: echo "TFVERSION=$TFVER" >> $GITHUB_ENV - name: Setup Terraform - uses: hashicorp/setup-terraform@v1 + uses: hashicorp/setup-terraform@v2 with: terraform_version: ${{ env.TFVERSION }} @@ -46,7 +48,16 @@ jobs: run: terraform fmt -check -recursive continue-on-error: true - - name: Terraform Init + - name: Terraform Init (test) + id: init-test + run: terraform init + + - name: Terraform Test + id: test + run: terraform test -verbose + continue-on-error: true + + - name: Terraform Init (examples) id: init run: terraform -chdir=${{ env.TFDIR }} init @@ -88,3 +99,149 @@ jobs: repo: context.repo.repo, body: output }) + + # User with write access has commented /ok-to-test on a (fork-based) pull request + terraform-pr-fork: + name: terraform-pr-fork + runs-on: ubuntu-latest + permissions: + checks: write + id-token: write + pull-requests: write + timeout-minutes: 15 + env: + TF_IN_AUTOMATION: true + TFDIR: examples + if: | + github.event_name == 'repository_dispatch' && + github.event.client_payload.slash_command.args.named.sha != '' && + contains( + github.event.client_payload.pull_request.head.sha, + github.event.client_payload.slash_command.args.named.sha + ) + steps: + - name: Update skipped check run to in_progress + uses: actions/github-script@v6 + env: + job: ${{ github.job }} + number: ${{ github.event.client_payload.pull_request.number }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const { data: pull } = await github.rest.pulls.get({ + ...context.repo, + pull_number: process.env.number + }); + const ref = pull.head.sha; + const { data: checks } = await github.rest.checks.listForRef({ + ...context.repo, + ref + }); + + // Filter for the check run with a specific name and a 'skipped' conclusion + const check = checks.check_runs.filter(c => c.name === process.env.job && c.conclusion === "skipped"); + + if (check.length > 0) { + console.log(`Skipped check run found with name: ${check[0].name}`); + + // Update the check run to 'in_progress' + const { data: result } = await github.rest.checks.update({ + ...context.repo, + check_run_id: check[0].id, + status: 'in_progress', + }); + + console.log(`Successfully updated check run to 'in_progress'. Name: ${result.name}`); + return result; + } else { + console.log('No skipped check runs found with the specified name.'); + } + + - name: Fork based /ok-to-test checkout + uses: actions/checkout@v4 + with: + ref: 'refs/pull/${{ github.event.client_payload.pull_request.number }}/merge' + + - name: Configure AWS credentials + if: startsWith(github.repository, 'ventx/terraform-aws-') + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-region: ${{ secrets.AWS_DEFAULT_REGION }} + role-to-assume: ${{ secrets.AWS_GH_OIDC }} + + - name: Set Versions + run: | + TFVER=$(grep .tool-versions -e "terraform" | sed "s/terraform \(.*\)/\1/") + echo "TFVERSION=$TFVER" >> $GITHUB_ENV + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v2 + with: + terraform_version: ${{ env.TFVERSION }} + + - name: Terraform Format + id: fmt + run: terraform fmt -check -recursive + continue-on-error: true + + - name: Terraform Init (test) + id: init-test + run: terraform init + + - name: Terraform Test + id: test + run: terraform test -verbose + continue-on-error: true + + - name: Terraform Init (examples) + id: init + run: terraform -chdir=${{ env.TFDIR }} init + + - name: Terraform Validate + id: validate + run: terraform -chdir=${{ env.TFDIR }} validate -no-color + + - name: Terraform Plan + id: plan + run: terraform -chdir=${{ env.TFDIR }} plan -no-color -input=false + continue-on-error: true + + - name: Update Pull Request + uses: actions/github-script@v6 + env: + PLAN: "terraform\n${{ steps.plan.outputs.stdout }}" + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const output = `#### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\` + #### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\` + #### Terraform Plan 📖\`${{ steps.plan.outcome }}\` + #### Terraform Validation 🤖\`${{ steps.validate.outcome }}\` + +
Show Plan + + \`\`\`\n + ${process.env.PLAN} + \`\`\` + +
+ + *Pushed by: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`; + + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: output + }) + + - name: Update check run to completed + uses: LouisBrunner/checks-action@v1.6.2 + id: update-check-run-completed + if: ${{ always() }} + with: + sha: ${{ github.sha }} + token: ${{ secrets.GITHUB_TOKEN }} + name: ${{ github.job }} + status: completed + conclusion: ${{ job.status }} diff --git a/.github/workflows/pr-tflint.yml b/.github/workflows/pr-tflint.yml new file mode 100644 index 0000000..4ee7128 --- /dev/null +++ b/.github/workflows/pr-tflint.yml @@ -0,0 +1,38 @@ +name: TFLint + +on: + pull_request: + paths: + - 'examples/**' + - 'tests/**' + - '**.tf' + +jobs: + tflint: + name: tflint + runs-on: ubuntu-latest + timeout-minutes: 15 + env: + TF_IN_AUTOMATION: true + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set Versions + run: | + TFLINTVER=$(grep .tool-versions -e "tflint" | sed "s/tflint \(.*\)/\1/") + echo "TFLINTVERSION=$TFLINTVER" >> $GITHUB_ENV + + - uses: terraform-linters/setup-tflint@v4 + id: tflintsetup + name: Setup TFLint + with: + tflint_version: v${{ env.TFLINTVERSION }} + + - name: Init TFLint + id: tflintinit + run: tflint --init + + - name: Run TFLint + id: tflint + run: tflint -f compact diff --git a/.github/workflows/pr-tfsec.yml b/.github/workflows/pr-tfsec.yml new file mode 100644 index 0000000..3065015 --- /dev/null +++ b/.github/workflows/pr-tfsec.yml @@ -0,0 +1,25 @@ +name: tfsec + +on: + pull_request: + paths: + - 'examples/**' + - 'tests/**' + - '**.tf' + +jobs: + tfsec: + name: tfsec + runs-on: ubuntu-latest + timeout-minutes: 15 + env: + TF_IN_AUTOMATION: true + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: tfsec + id: tfsec + uses: tfsec/tfsec-pr-commenter-action@main + with: + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 3a37cf6..969c3da 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -6,8 +6,9 @@ on: - main permissions: - id-token: write contents: write + id-token: write + pull-requests: write jobs: release-please: @@ -15,23 +16,39 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 15 env: - AWS_DEFAULT_REGION: eu-central-1 + ARM_CLIENT_ID: "${{ secrets.AZURE_CLIENT_ID }}" + ARM_SKIP_PROVIDER_REGISTRATION: true + ARM_SUBSCRIPTION_ID: "${{ secrets.AZURE_SUBSCRIPTION_ID }}" + ARM_TENANT_ID: "${{ secrets.AZURE_TENANT_ID }}" + ARM_USE_OIDC: true TFDIR: . + TF_VAR_rg_name: github-oidc TF_VAR_subnet_ids: '["test"]' - TF_VAR_workspace_name: rover TFVER: 1.1.2 # Rover uses Terraform 1.1.2, so we need to use the same version for the plan file steps: + - name: Generate token + id: app-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.PRIVATE_KEY }} + - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: main + token: ${{ steps.app-token.outputs.token }} - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v1 + if: startsWith(github.repository, 'ventx/terraform-aws-') + uses: aws-actions/configure-aws-credentials@v4 with: - aws-region: ${{ env.AWS_DEFAULT_REGION }} + aws-region: ${{ secrets.AWS_DEFAULT_REGION }} role-to-assume: ${{ secrets.AWS_GH_OIDC }} - name: Setup Terraform - uses: hashicorp/setup-terraform@v1 + uses: hashicorp/setup-terraform@v2 with: terraform_version: ${{ env.TFVER }} @@ -84,15 +101,26 @@ jobs: terraform: 'true' - name: terraform-docs - uses: terraform-docs/gh-actions@v1.0.0 + uses: terraform-docs/gh-actions@v1 + + - name: Fix .git owner # Fixes: https://github.com/terraform-docs/gh-actions/issues/90#issuecomment-1231312948 + run: sudo chown runner:docker -R .git + + - name: Commit files + run: | + git config --local user.email "github-actions[bot]@users.noreply.github.com" + git config --local user.name "ventx-bot" + git status --porcelain + git add . + git commit -m "[skip ci] Update README.md and terraform-docs" + + - name: Push changes + uses: ad-m/github-push-action@master with: - working-dir: . - output-file: README.md - output-method: inject - git-push: 'true' + github_token: ${{ steps.app-token.outputs.token }} - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: release-please uses: google-github-actions/release-please-action@v3 diff --git a/.github/workflows/test-weekly.yml b/.github/workflows/test-weekly.yml new file mode 100644 index 0000000..14cfa86 --- /dev/null +++ b/.github/workflows/test-weekly.yml @@ -0,0 +1,46 @@ +name: test-weekly + +on: + # cronjob trigger at 03:00 every monday. + schedule: + - cron: "0 3 * * MON" + # manual trigger + workflow_dispatch: + +permissions: + id-token: write + contents: write + +jobs: + test-weekly: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + token: ${{ steps.app-token.outputs.token }} + + - name: Configure AWS credentials + if: ${{ env.AWS_DEFAULT_REGION }} + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-region: ${{ env.AWS_DEFAULT_REGION }} + role-to-assume: ${{ secrets.AWS_GH_OIDC }} + + - name: Set Versions + run: | + TFVER=$(grep .tool-versions -e "terraform" | sed "s/terraform \(.*\)/\1/") + echo "TFVERSION=$TFVER" >> $GITHUB_ENV + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v2 + with: + terraform_version: ${{ env.TFVERSION }} + + - name: Terraform Init + id: init + run: terraform init + + - name: Terraform Test + id: test + run: terraform test -verbose diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0d69f7f..7683194 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 + rev: v4.5.0 hooks: - id: check-json - id: check-merge-conflict @@ -22,7 +22,7 @@ repos: language: system stages: ["commit", "push"] - repo: https://github.com/antonbabenko/pre-commit-terraform - rev: v1.74.1 + rev: v1.83.5 hooks: - id: terraform_fmt - id: terraform_validate diff --git a/Makefile b/Makefile index b5b9cb0..f69ec69 100644 --- a/Makefile +++ b/Makefile @@ -28,10 +28,7 @@ destroy: tf-destroy ## Run terraform destroy. plan: tf-plan ## Run terraform plan. .PHONY: test -test: tf-test ## Run terratest on your code. - -.PHONY: test-fast -test-fast: tf-test-fast ## Run terratest against localstack with 64 parallel requests. +test: tf-test ## Run terraform test on your code. .PHONY: validate validate: tf-validate ## Run terraform validate for some basic HCL validation.