hashicorp/terraform

10 workflows · maturity 50% · 6 patterns · GitHub ↗

Security 17.5/100

Practices

✓ Matrix✓ Permissions○ Security scan○ AI review✓ Cache○ Concurrency✓ Reusable workflows

Detected patterns

Security dimensions

permissions
17.5
security scan
0
supply chain
0
secret handling
0
harden runner
0

Workflows (10)

backport perms .github/workflows/backport.yml
Triggers
pull_request_target, pull_request
Runs on
ubuntu-latest
Jobs
backport
Commands
  • backport-assistant backport
View raw YAML
---
name: Backport Assistant Runner

on:
  pull_request_target:
    types:
      - closed
  pull_request:
    types:
      - labeled

permissions:
  contents: write # to push to a new branch
  pull-requests: write # to create a new PR

jobs:
  backport:
    if: github.event.pull_request
    runs-on: ubuntu-latest
    container: hashicorpdev/backport-assistant:0.5.8@sha256:b18bc289f405beb58aa22a1b4b3bdac42394fad602541ed94a0f9c5e3f66f954
    steps:
      - name: Run Backport Assistant
        run: |
          backport-assistant backport
        env:
          BACKPORT_LABEL_REGEXP: "(?P<target>\\d+\\.\\d+)-backport"
          BACKPORT_TARGET_TEMPLATE: "v{{.target}}"
          BACKPORT_CREATE_DRAFT_ALWAYS: true
          GITHUB_TOKEN: ${{ github.token }}
build matrix perms .github/workflows/build.yml
Triggers
workflow_dispatch, push
Runs on
ubuntu-latest, ubuntu-latest, ubuntu-latest, ubuntu-latest, ubuntu-latest, ${{ matrix.runson }}, ubuntu-latest
Jobs
get-product-version, get-go-version, generate-metadata-file, build, package-docker, e2etest-build, e2e-test, e2e-test-exec
Matrix
arch, include, include.cgo-enabled, include.goarch, include.goos, include.runson→ 0, 386, amd64, arm, arm64, darwin, freebsd, linux, macos-latest, openbsd, solaris, ubuntu-latest, windows, windows-latest
Actions
hashicorp/actions-set-product-version, hashicorp/actions-generate-metadata, hashicorp/actions-docker-build, docker/setup-qemu-action
Commands
  • pkg_name=${{ env.PKG_NAME }} echo "pkg-name=${pkg_name}" | tee -a "${GITHUB_OUTPUT}"
  • .github/scripts/get_product_version.sh
  • [ -n "${{steps.get-product-version.outputs.product-version}}" ] echo "::notice title=Terraform CLI Version::${{ steps.get-product-version.outputs.product-version }}"
  • cache_key=e2e-cache-${{ github.sha }} cache_path=internal/command/e2etest/build echo "e2e-cache-key=${cache_key}" | tee -a "${GITHUB_OUTPUT}" echo "e2e-cache-path=${cache_path}" | tee -a "${GITHUB_OUTPUT}"
  • # NOTE: This script reacts to the GOOS, GOARCH, and GO_LDFLAGS # environment variables defined above. The e2e test harness # needs to know the version we're building for so it can verify # that "terraform version" is returning that version number. bash ./internal/command/e2etest/make-archive.sh
  • unzip "${{ needs.e2etest-build.outputs.e2e-cache-path }}/terraform-e2etest_${{ env.os }}_${{ env.arch }}.zip" unzip "./terraform_${{env.version}}_${{ env.os }}_${{ env.arch }}.zip"
  • .github/scripts/e2e_test_linux_darwin.sh
  • e2etest.exe -test.v
View raw YAML
name: build

# If you want to test changes to this file before merging to a main branch,
# push them up to a branch whose name has the prefix "build-workflow-dev/",
# which is a special prefix that triggers this workflow even though it's not
# actually a release branch.

on:
  workflow_dispatch:
  push:
    branches:
      - main
      - 'v[0-9]+.[0-9]+'
      - releng/**
      - tsccr-auto-pinning/**
      - dependabot/**
    tags:
      - 'v[0-9]+.[0-9]+.[0-9]+*'

env:
  PKG_NAME: "terraform"

permissions:
  contents: read
  statuses: write

jobs:
  get-product-version:
    name: "Determine intended Terraform version"
    runs-on: ubuntu-latest
    outputs:
      product-version: ${{ steps.get-product-version.outputs.product-version }}
      product-version-base: ${{ steps.get-product-version.outputs.base-product-version }}
      product-version-pre: ${{ steps.get-product-version.outputs.prerelease-product-version }}
      experiments: ${{ steps.get-ldflags.outputs.experiments }}
      go-ldflags: ${{ steps.get-ldflags.outputs.go-ldflags }}
      pkg-name: ${{ steps.get-pkg-name.outputs.pkg-name }}

    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
      - name: Get Package Name
        id: get-pkg-name
        run: |
          pkg_name=${{ env.PKG_NAME }}
          echo "pkg-name=${pkg_name}" | tee -a "${GITHUB_OUTPUT}"
      - name: Decide version number
        id: get-product-version
        uses: hashicorp/actions-set-product-version@2ec1b51402b3070bccf7ca95306afbd039e574ff # v2.0.1
        with:
          checkout: false
      - name: Determine experiments
        id: get-ldflags
        env:
          RAW_VERSION: ${{ steps.get-product-version.outputs.product-version }}
        shell: bash
        run: .github/scripts/get_product_version.sh
      - name: Report chosen version number
        run: |
          [ -n "${{steps.get-product-version.outputs.product-version}}" ]
          echo "::notice title=Terraform CLI Version::${{ steps.get-product-version.outputs.product-version }}"

  get-go-version:
    name: "Determine Go toolchain version"
    runs-on: ubuntu-latest
    outputs:
      go-version: ${{ steps.get-go-version.outputs.version }}

    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
      - name: Determine Go version
        id: get-go-version
        uses: ./.github/actions/go-version

  generate-metadata-file:
    name: "Generate release metadata"
    runs-on: ubuntu-latest
    needs: get-product-version
    outputs:
      filepath: ${{ steps.generate-metadata-file.outputs.filepath }}

    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
      - name: Generate package metadata
        id: generate-metadata-file
        uses: hashicorp/actions-generate-metadata@f1d852525201cb7bbbf031dd2e985fb4c22307fc # v1.1.3
        with:
          version: ${{ needs.get-product-version.outputs.product-version }}
          product: ${{ env.PKG_NAME }}

      - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
        with:
          name: metadata.json
          path: ${{ steps.generate-metadata-file.outputs.filepath }}

  build:
    name: Build for ${{ matrix.goos }}_${{ matrix.goarch }}
    needs:
      - get-product-version
      - get-go-version
    uses: ./.github/workflows/build-terraform-cli.yml
    with:
      goarch: ${{ matrix.goarch }}
      goos: ${{ matrix.goos }}
      go-version: ${{ needs.get-go-version.outputs.go-version }}
      package-name: ${{ needs.get-product-version.outputs.pkg-name }}
      product-version: ${{ needs.get-product-version.outputs.product-version }}
      ld-flags: ${{ needs.get-product-version.outputs.go-ldflags }}
      cgo-enabled: ${{ matrix.cgo-enabled }}
      runson: ${{ matrix.runson }}
    secrets: inherit
    strategy:
      matrix:
        include:
          - {goos: "freebsd", goarch: "386", runson: "ubuntu-latest", cgo-enabled: "0"}
          - {goos: "freebsd", goarch: "amd64", runson: "ubuntu-latest", cgo-enabled: "0"}
          - {goos: "freebsd", goarch: "arm", runson: "ubuntu-latest", cgo-enabled: "0"}
          - {goos: "linux", goarch: "386", runson: "ubuntu-latest", cgo-enabled: "0"}
          - {goos: "linux", goarch: "amd64", runson: "ubuntu-latest", cgo-enabled: "0"}
          - {goos: "linux", goarch: "arm", runson: "ubuntu-latest", cgo-enabled: "0"}
          - {goos: "linux", goarch: "arm64", runson: "ubuntu-latest", cgo-enabled: "0"}
          - {goos: "openbsd", goarch: "386", runson: "ubuntu-latest", cgo-enabled: "0"}
          - {goos: "openbsd", goarch: "amd64", runson: "ubuntu-latest", cgo-enabled: "0"}
          - {goos: "solaris", goarch: "amd64", runson: "ubuntu-latest", cgo-enabled: "0"}
          - {goos: "windows", goarch: "386", runson: "ubuntu-latest", cgo-enabled: "0"}
          - {goos: "windows", goarch: "amd64", runson: "ubuntu-latest", cgo-enabled: "0"}
          - {goos: "windows", goarch: "arm64", runson: "ubuntu-latest", cgo-enabled: "0"}
          - {goos: "darwin", goarch: "amd64", runson: "ubuntu-latest", cgo-enabled: "0"}
          - {goos: "darwin", goarch: "arm64", runson: "ubuntu-latest", cgo-enabled: "0"}
      fail-fast: false

  package-docker:
    name: Build Docker image for linux_${{ matrix.arch }}
    runs-on: ubuntu-latest
    needs:
      - get-product-version
      - build
    strategy:
      matrix:
        arch: ["amd64", "386", "arm", "arm64"]
      fail-fast: false
    env:
      repo: "terraform"
      version: ${{needs.get-product-version.outputs.product-version}}
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
      - name: Build Docker images
        uses: hashicorp/actions-docker-build@200254326a30d7b747745592f8f4d226bbe4abe4 # v2.2.0
        with:
          pkg_name: "terraform_${{env.version}}"
          version: ${{env.version}}
          bin_name: terraform
          target: default
          arch: ${{matrix.arch}}
          dockerfile: build.Dockerfile
          smoke_test: .github/scripts/verify_docker v${{ env.version }}
          tags: |
            docker.io/hashicorp/${{env.repo}}:${{env.version}}
            public.ecr.aws/hashicorp/${{env.repo}}:${{env.version}}

  e2etest-build:
    name: Build e2etest for ${{ matrix.goos }}_${{ matrix.goarch }}
    runs-on: ubuntu-latest
    outputs:
      e2e-cache-key: ${{ steps.set-cache-values.outputs.e2e-cache-key }}
      e2e-cache-path: ${{ steps.set-cache-values.outputs.e2e-cache-path }}
    needs:
      - get-product-version
      - get-go-version
    strategy:
      matrix:
        include:
          - {goos: "darwin", goarch: "amd64"}
          - {goos: "darwin", goarch: "arm64"}
          - {goos: "windows", goarch: "amd64"}
          - {goos: "windows", goarch: "386"}
          - {goos: "linux", goarch: "386"}
          - {goos: "linux", goarch: "amd64"}
          - {goos: "linux", goarch: "arm"}
          - {goos: "linux", goarch: "arm64"}
      fail-fast: false

    env:
      build_script: ./internal/command/e2etest/make-archive.sh

    steps:
      - name: Set Cache Values
        id: set-cache-values
        run: |
          cache_key=e2e-cache-${{ github.sha }}
          cache_path=internal/command/e2etest/build
          echo "e2e-cache-key=${cache_key}" | tee -a "${GITHUB_OUTPUT}"
          echo "e2e-cache-path=${cache_path}" | tee -a "${GITHUB_OUTPUT}"
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Install Go toolchain
        uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
        with:
          go-version: ${{ needs.get-go-version.outputs.go-version }}

      - name: Build test harness package
        env:
          GOOS: ${{ matrix.goos }}
          GOARCH: ${{ matrix.goarch }}
          GO_LDFLAGS: ${{ needs.get-product-version.outputs.go-ldflags }}
        run: |
          # NOTE: This script reacts to the GOOS, GOARCH, and GO_LDFLAGS
          # environment variables defined above. The e2e test harness
          # needs to know the version we're building for so it can verify
          # that "terraform version" is returning that version number.
          bash ./internal/command/e2etest/make-archive.sh

      - name: Save test harness to cache
        uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
        with:
          path: ${{ steps.set-cache-values.outputs.e2e-cache-path }}
          key: ${{ steps.set-cache-values.outputs.e2e-cache-key }}_${{ matrix.goos }}_${{ matrix.goarch }}

  e2e-test:
    name: Run e2e test for ${{ matrix.goos }}_${{ matrix.goarch }}
    runs-on: ${{ matrix.runson }}
    needs:
      - get-product-version
      - build
      - e2etest-build
    strategy:
      matrix:
        include:
          - { runson: ubuntu-latest, goos: linux, goarch: "amd64" }
          - { runson: ubuntu-latest, goos: linux, goarch: "386" }
          - { runson: ubuntu-latest, goos: linux, goarch: "arm" }
          - { runson: ubuntu-latest, goos: linux, goarch: "arm64" }
          - { runson: macos-latest, goos: darwin, goarch: "amd64" }
          - { runson: windows-latest, goos: windows, goarch: "amd64" }
          - { runson: windows-latest, goos: windows, goarch: "386" }
      fail-fast: false

    env:
      os: ${{ matrix.goos }}
      arch: ${{ matrix.goarch }}
      version: ${{needs.get-product-version.outputs.product-version}}

    steps:
      # NOTE: This intentionally _does not_ check out the source code
      # for the commit/tag we're building, because by now we should
      # have everything we need in the combination of CLI release package
      # and e2etest package for this platform. (This helps ensure that we're
      # really testing the release package and not inadvertently testing a
      # fresh build from source.)
      - name: Checkout repo
        if: ${{ (matrix.goos == 'linux') || (matrix.goos == 'darwin') }}
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
      - name: "Restore cache"
        uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
        id: e2etestpkg
        with:
          path: ${{ needs.e2etest-build.outputs.e2e-cache-path }}
          key: ${{ needs.e2etest-build.outputs.e2e-cache-key }}_${{ matrix.goos }}_${{ matrix.goarch }}
          fail-on-cache-miss: true
          enableCrossOsArchive: true
      - name: "Download Terraform CLI package"
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        id: clipkg
        with:
          name: terraform_${{env.version}}_${{ env.os }}_${{ env.arch }}.zip
          path: .
      - name: Extract packages
        if: ${{ matrix.goos == 'windows' }}
        run: |
          unzip "${{ needs.e2etest-build.outputs.e2e-cache-path }}/terraform-e2etest_${{ env.os }}_${{ env.arch }}.zip"
          unzip "./terraform_${{env.version}}_${{ env.os }}_${{ env.arch }}.zip"
      - name: Set up QEMU
        uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
        if: ${{ contains(matrix.goarch, 'arm') }}
        with:
          platforms: all
      - name: Run E2E Tests (Darwin & Linux)
        id: get-product-version
        shell: bash
        if: ${{ (matrix.goos == 'linux') || (matrix.goos == 'darwin') }}
        env:
          e2e_cache_path: ${{ needs.e2etest-build.outputs.e2e-cache-path }}
        run: .github/scripts/e2e_test_linux_darwin.sh
      - name: Run E2E Tests (Windows)
        if: ${{ matrix.goos == 'windows' }}
        env:
          TF_ACC: 1
        shell: cmd
        run: e2etest.exe -test.v


  e2e-test-exec:
    name: Run terraform-exec test for linux amd64
    runs-on: ubuntu-latest
    needs:
      - get-product-version
      - get-go-version
      - build

    env:
      os: ${{ matrix.goos }}
      arch: ${{ matrix.goarch }}
      version: ${{needs.get-product-version.outputs.product-version}}

    steps:
      - name: Install Go toolchain
        uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
        with:
          go-version: ${{ needs.get-go-version.outputs.go-version }}
      - name: Download Terraform CLI package
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        id: clipkg
        with:
          name: terraform_${{ env.version }}_linux_amd64.zip
          path: .
      - name: Checkout terraform-exec repo
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          repository: hashicorp/terraform-exec
          path: terraform-exec
      - name: Run terraform-exec end-to-end tests
        run: |
          FULL_RELEASE_VERSION="${{ env.version }}"
          unzip terraform_${FULL_RELEASE_VERSION}_linux_amd64.zip
          export TFEXEC_E2ETEST_TERRAFORM_PATH="$(pwd)/terraform"
          cd terraform-exec
          go test -race -timeout=30m -v ./tfexec/internal/e2etest
build-terraform-cli .github/workflows/build-terraform-cli.yml
Triggers
workflow_call
Runs on
${{ inputs.runson }}
Jobs
build
Actions
hashicorp/actions-go-build, hashicorp/actions-packaging-linux
Commands
  • mkdir -p "$LICENSE_DIR" && cp LICENSE "$LICENSE_DIR/LICENSE.txt"
  • echo "RPM_PACKAGE=$(basename out/*.rpm)" >> $GITHUB_ENV echo "DEB_PACKAGE=$(basename out/*.deb)" >> $GITHUB_ENV
View raw YAML
---
name: build_terraform

# This workflow is intended to be called by the build workflow. The crt make
# targets that are utilized automatically determine build metadata and
# handle building and packing Terraform.

on:
  workflow_call:
    inputs:
      cgo-enabled:
        type: string
        required: true
      goos:
        required: true
        type: string
      goarch:
        required: true
        type: string
      go-version:
        type: string
      package-name:
        type: string
        default: terraform
      product-version:
        type: string
        required: true
      ld-flags:
        type: string
        required: true
      runson:
        type: string
        required: true

jobs:
  build:
    runs-on: ${{ inputs.runson }}
    name: Terraform ${{ inputs.goos }} ${{ inputs.goarch }} v${{ inputs.product-version }}
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
      - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
        with:
          go-version: ${{ inputs.go-version }}
      - name: Build Terraform
        env:
          GOOS: ${{ inputs.goos }}
          GOARCH: ${{ inputs.goarch }}
          GO_LDFLAGS: ${{ inputs.ld-flags }}
          ACTIONSOS: ${{ inputs.runson }}
          CGO_ENABLED: ${{ inputs.cgo-enabled }}
        uses: hashicorp/actions-go-build@b9e2cfba3013adccdc112b01cba922d83c78fac5 # v1.1.1
        with:
          product_name: ${{ inputs.package-name }}
          product_version: ${{ inputs.product-version }}
          go_version: ${{ inputs.go-version }}
          os: ${{ inputs.goos }}
          arch: ${{ inputs.goarch }}
          reproducible: nope
          instructions: |-
            go build -ldflags "${{ inputs.ld-flags }}" -o "$BIN_PATH" -trimpath -buildvcs=false
            cp LICENSE "$TARGET_DIR/LICENSE.txt"
      - name: Copy license file to config_dir
        if: ${{ inputs.goos == 'linux' }}
        env:
          LICENSE_DIR: ".release/linux/package/usr/share/doc/${{ inputs.package-name }}"
        run: |
          mkdir -p "$LICENSE_DIR" && cp LICENSE "$LICENSE_DIR/LICENSE.txt"
      - if: ${{ inputs.goos == 'linux' }}
        uses: hashicorp/actions-packaging-linux@129994a18b8e7dc106937edf859fddd97af66365 # v1.10
        with:
          name: "terraform"
          description: "Terraform enables you to safely and predictably create, change, and improve infrastructure. It is a tool that codifies APIs into declarative configuration files that can be shared amongst team members, treated as code, edited, reviewed, and versioned."
          arch: ${{ inputs.goarch }}
          version: ${{ inputs.product-version }}
          maintainer: "HashiCorp"
          homepage: "https://terraform.io/"
          license: "BUSL-1.1"
          binary: "dist/terraform"
          deb_depends: "git"
          rpm_depends: "git"
          config_dir: ".release/linux/package/"
      - if: ${{ inputs.goos == 'linux' }}
        name: Determine package file names
        run: |
          echo "RPM_PACKAGE=$(basename out/*.rpm)" >> $GITHUB_ENV
          echo "DEB_PACKAGE=$(basename out/*.deb)" >> $GITHUB_ENV
      - if: ${{ inputs.goos == 'linux' }}
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
        with:
          name: ${{ env.RPM_PACKAGE }}
          path: out/${{ env.RPM_PACKAGE }}
          if-no-files-found: error
      - if: ${{ inputs.goos == 'linux' }}
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
        with:
          name: ${{ env.DEB_PACKAGE }}
          path: out/${{ env.DEB_PACKAGE }}
          if-no-files-found: error
changelog perms .github/workflows/changelog.yml
Triggers
pull_request_target
Runs on
ubuntu-latest
Jobs
check-changelog-entry
Actions
dorny/paths-filter, miniscruff/changie-action
View raw YAML
# This workflow makes sure contributors don't forget to add a changelog entry or explicitly opt-out of it.
#
# Do not extend this workflow to include checking out the code (e.g. for building and testing purposes) while the pull_request_target trigger is used.
# Instead, see use of workflow_run in https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/

name: Changelog

on:
    # The pull_request_target trigger event allows PRs raised from forks to have write permissions and access secrets.
    # We uses it in this workflow to enable writing comments to the PR.
    pull_request_target:
        types:
            - opened
            - ready_for_review
            - reopened
            - synchronize
            - labeled
            - unlabeled

# This workflow runs for not-yet-reviewed external contributions.
# Following a pull_request_target trigger the workflow would have write permissions,
# so we intentionally restrict the permissions to only include write access on pull-requests.
permissions:
    contents: read
    pull-requests: write

jobs:
    check-changelog-entry:
        if: ${{ !contains(github.event.pull_request.labels.*.name, 'skip-changelog-check') }}
        name: "Check Changelog Entry"
        runs-on: ubuntu-latest
        concurrency:
            group: changelog-${{ github.head_ref }}
            cancel-in-progress: true

        steps:
            - name: "Changed files"
              uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
              id: changelog
              with:
                  filters: |
                      changes:
                          - '.changes/*/*.yaml'
                      changelog:
                        - 'CHANGELOG.md'
                      version:
                        - 'version/VERSION'
                  list-files: json

            - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
              with:
                  sparse-checkout: |
                      version/VERSION
                      .changie.yaml
                      .changes/
                  sparse-checkout-cone-mode: false
                  ref: ${{ github.ref }} # Ref refers to the target branch of this PR

            - name: "Check for changelog entry"
              uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
              with:
                  script: |
                      const fs = require("fs");
                      async function createOrUpdateChangelogComment(commentDetails, deleteComment) {
                          const commentStart = "## Changelog Warning"
                          
                          const body = commentStart + "\n\n" + commentDetails;
                          const { number: issue_number } = context.issue;
                          const { owner, repo } = context.repo;
                          
                          // List all comments
                          const allComments = (await github.rest.issues.listComments({
                              issue_number,
                              owner,
                              repo,
                          })).data;

                          const existingComment = allComments.find(c => c.body.startsWith(commentStart));
                          const comment_id = existingComment?.id;
                          
                          if (deleteComment) {
                              if (existingComment) {
                                  await github.rest.issues.deleteComment({
                                      owner,
                                      repo,
                                      comment_id,
                                  });
                              }
                              return;
                          }

                          core.setFailed(commentDetails);

                          if (existingComment) {
                              await github.rest.issues.updateComment({
                                  owner,
                                  repo,
                                  comment_id,
                                  body,
                              });
                          } else {
                              await github.rest.issues.createComment({
                                  owner,
                                  repo,
                                  issue_number,
                                  body,
                              });
                          }
                      }

                      const changesPresent = ${{steps.changelog.outputs.changes}};
                      const changedChangesFiles = ${{steps.changelog.outputs.changes_files}};
                      const changelogChangesPresent = ${{steps.changelog.outputs.changelog}};
                      const versionChangesPresent = ${{steps.changelog.outputs.version}};

                      const prLabels = await github.rest.issues.listLabelsOnIssue({
                          issue_number: context.issue.number,
                          owner: context.repo.owner,
                          repo: context.repo.repo
                      });
                      const backportLabels = prLabels.data.filter(label => label.name.endsWith("-backport"));
                      const backportVersions = backportLabels.map(label => label.name.split("-")[0]);

                      const currentVersionFile = fs.readFileSync("./version/VERSION", "utf-8");
                      const currentVersionParts = currentVersionFile.split(".");
                      currentVersionParts.pop();
                      const currentVersion = currentVersionParts.join(".");

                      const allVersions = [currentVersion, ...backportVersions]
                      allVersions.sort((a, b) => {
                        const as = a.split(".").map(Number);
                        const bs = b.split(".").map(Number);

                        if (as[0] !== bs[0]) {
                          return as[0] - bs[0];
                        }
                        
                        if (as[1] !== bs[1]) {
                          return as[1] - bs[1];
                        }
                      });

                      const noChangelogNeededLabel = prLabels.data.find(label => label.name === 'no-changelog-needed');
                      const dependenciesLabel = prLabels.data.find(label => label.name === 'dependencies');

                      // We want to prohibit contributors from directly changing the CHANGELOG.md, it's 
                      // generated so all changes will be lost during the release process.
                      // Therefore we only allow the changelog to change together with the version.
                      // In very rare cases where we generate changes in the changelog without changing the 
                      // version we will just ignore this failing check.
                      if (changelogChangesPresent && !versionChangesPresent) {
                          await createOrUpdateChangelogComment("Please don't edit the CHANGELOG.md manually. We use changie to control the Changelog generation, please use `npx changie new` to create a new changelog entry.");
                          return;
                      }

                      if (dependenciesLabel) {
                          return;
                      }

                      if (noChangelogNeededLabel) {
                          if (changesPresent) {
                              await createOrUpdateChangelogComment("Please remove either the 'no-changelog-needed' label or the changelog entry from this PR.");
                              return;
                          }
                          
                          // Nothing to complain about, so delete any existing comment
                          await createOrUpdateChangelogComment("", true);
                          return;
                      }

                      // We only want to have a changelog entry for the oldest version this PR will
                      // land in.
                      const onlyExpectedChangeVersion = allVersions[0]
                      const missingChangelogEntry = !changedChangesFiles.some(filePath => filePath.includes("/v"+onlyExpectedChangeVersion+"/"))
                      const unexpectedChangelogEntry = changedChangesFiles.filter(filePath => !filePath.includes("/v"+onlyExpectedChangeVersion+"/"))


                      if (missingChangelogEntry) {
                          await createOrUpdateChangelogComment(`Currently this PR would target a v${onlyExpectedChangeVersion} release. Please add a changelog entry for in the .changes/v${onlyExpectedChangeVersion} folder, or discuss which release you'd like to target with your reviewer. If you believe this change does not need a changelog entry, please add the 'no-changelog-needed' label.`);
                          return;
                      }

                      if (unexpectedChangelogEntry.length > 0) {
                          await createOrUpdateChangelogComment(`Please remove the changelog entry for the following paths: ${unexpectedChangelogEntry.join(", ")}. If you believe this change does not need a changelog entry, please add the 'no-changelog-needed' label.`);
                          return;
                      }

                      // Nothing to complain about, so delete any existing comment
                      await createOrUpdateChangelogComment("", true);

            - name: Validate changie fragment is valid
              uses: miniscruff/changie-action@5036dffa79ffc007110dc7f75eca7ef72780e147 # v2.1.0
              with:
                  version: latest
                  args: merge -u "." --dry-run
checks perms .github/workflows/checks.yml
Triggers
pull_request, push
Runs on
ubuntu-latest, ubuntu-latest, ubuntu-latest, ubuntu-latest
Jobs
unit-tests, race-tests, e2e-tests, consistency-checks
Commands
  • # We run tests for all packages from all modules in this repository. for dir in $(go list -m -f '{{.Dir}}' github.com/hashicorp/terraform/...); do (cd $dir && go test -cover "./...") done
  • go test -race ./internal/terraform ./internal/command ./internal/states
  • TF_ACC=1 go test -v ./internal/command/e2etest
  • make syncdeps CHANGED="$(git status --porcelain)" if [[ -n "$CHANGED" ]]; then git diff echo >&2 "ERROR: go.mod/go.sum files are not up-to-date. Run 'make syncdeps' and then commit the updated files." echo >&2 $'Affected files:\n'"$CHANGED" exit 1 fi
  • make fmtcheck importscheck vetcheck copyright generate staticcheck exhaustive protobuf CHANGED="$(git status --porcelain)" if [[ -n "$CHANGED" ]]; then git diff echo >&2 "ERROR: Generated files are inconsistent. Run 'make generate' and 'make protobuf' locally and then commit the updated files." echo >&2 $'Affected files:\n'"$CHANGED" exit 1 fi
View raw YAML
# This workflow is a collection of "quick checks" that should be reasonable
# to run for any new commit to this repository in principle.
#
# The main purpose of this workflow is to represent checks that we want to
# run prior to reviewing and merging a pull request. We should therefore aim
# for these checks to complete in no more than a few minutes in the common
# case.
#
# The build.yml workflow includes some additional checks we run only for
# already-merged changes to release branches and tags, as a compromise to
# keep the PR feedback relatively fast. The intent is that checks.yml should
# catch most problems but that build.yml might occasionally by the one to catch
# more esoteric situations, such as architecture-specific or OS-specific
# misbehavior.

name: Quick Checks

on:
  pull_request:
    types:
      - opened
      - ready_for_review
      - reopened
      - synchronize
  push:
    branches:
      - "*"
    tags:
      - "v[0-9]+.[0-9]+.[0-9]+*"

# This workflow runs for not-yet-reviewed external contributions and so it
# intentionally has no write access and only limited read access to the
# repository.
permissions:
  contents: read

jobs:
  unit-tests:
    name: "Unit Tests"
    runs-on: ubuntu-latest

    steps:
      - name: "Fetch source code"
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Determine Go version
        id: go
        uses: ./.github/actions/go-version

      - name: Install Go toolchain
        uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
        with:
          go-version: ${{ steps.go.outputs.version }}
          cache-dependency-path: go.sum

      - name: "Unit tests"
        run: |
          # We run tests for all packages from all modules in this repository.
          for dir in $(go list -m -f '{{.Dir}}' github.com/hashicorp/terraform/...); do
              (cd $dir && go test -cover "./...")
          done

  race-tests:
    name: "Race Tests"
    runs-on: ubuntu-latest

    steps:
      - name: "Fetch source code"
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Determine Go version
        id: go
        uses: ./.github/actions/go-version

      - name: Install Go toolchain
        uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
        with:
          go-version: ${{ steps.go.outputs.version }}
          cache-dependency-path: go.sum

      # The race detector add significant time to the unit tests, so only run
      # it for select packages.
      - name: "Race detector"
        run: |
          go test -race ./internal/terraform ./internal/command ./internal/states

  e2e-tests:
    # This is an intentionally-limited form of our E2E test run which only
    # covers Terraform running on Linux. The build.yml workflow runs these
    # tests across various other platforms in order to catch the rare exception
    # that might leak through this.
    name: "End-to-end Tests"
    runs-on: ubuntu-latest

    steps:
      - name: "Fetch source code"
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Determine Go version
        id: go
        uses: ./.github/actions/go-version

      - name: Install Go toolchain
        uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
        with:
          go-version: ${{ steps.go.outputs.version }}
          cache-dependency-path: go.sum

      - name: "End-to-end tests"
        run: |
          TF_ACC=1 go test -v ./internal/command/e2etest

  consistency-checks:
    name: "Code Consistency Checks"
    runs-on: ubuntu-latest

    steps:
      - name: "Fetch source code"
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          fetch-depth: 0 # We need to do comparisons against the main branch.

      - name: Determine Go version
        id: go
        uses: ./.github/actions/go-version

      - name: Install Go toolchain
        uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
        with:
          go-version: ${{ steps.go.outputs.version }}
          cache-dependency-path: go.sum

      - name: "go.mod and go.sum consistency check"
        run: |
          make syncdeps
          CHANGED="$(git status --porcelain)"
          if [[ -n "$CHANGED" ]]; then
            git diff
            echo >&2 "ERROR: go.mod/go.sum files are not up-to-date. Run 'make syncdeps' and then commit the updated files."
            echo >&2 $'Affected files:\n'"$CHANGED"
            exit 1
          fi

      - name: Cache protobuf tools
        uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
        with:
          path: "tools/protobuf-compile/.workdir"
          key: protobuf-tools-${{ hashFiles('tools/protobuf-compile/protobuf-compile.go') }}
          restore-keys: |
            protobuf-tools-

      - name: "Code consistency checks"
        run: |
          make fmtcheck importscheck vetcheck copyright generate staticcheck exhaustive protobuf
          CHANGED="$(git status --porcelain)"
          if [[ -n "$CHANGED" ]]; then
            git diff
            echo >&2 "ERROR: Generated files are inconsistent. Run 'make generate' and 'make protobuf' locally and then commit the updated files."
            echo >&2 $'Affected files:\n'"$CHANGED"
            exit 1
          fi
equivalence-test-diff perms .github/workflows/equivalence-test-diff.yml
Triggers
pull_request
Runs on
ubuntu-latest
Jobs
equivalence-test-diff
Commands
  • ./.github/scripts/equivalence-test.sh download_equivalence_test_binary \ 0.5.0 \ ./bin/equivalence-tests \ linux \ amd64
  • ./.github/scripts/equivalence-test.sh build_terraform_binary ./bin/terraform
  • ./bin/equivalence-tests diff \ --tests=testing/equivalence-tests/tests \ --goldens=testing/equivalence-tests/outputs \ --binary=$(pwd)/bin/terraform echo "exit-code=$?" >> "${GITHUB_OUTPUT}"
  • gh pr comment ${{ github.event.pull_request.number }} \ --body "The equivalence tests failed. Please investigate [here](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})." exit 1 # fail the job
  • gh pr comment ${{ github.event.pull_request.number }} \ --body "The equivalence tests will be updated. Please verify the changes [here](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})."
View raw YAML
name: equivalence-test-diff

on:
  pull_request:
    types:
      - opened
      - synchronize
      - ready_for_review
      - reopened

permissions:
  contents: read
  pull-requests: write

jobs:
  equivalence-test-diff:
    name: "Equivalence Test Diff"
    runs-on: ubuntu-latest

    steps:
      - name: Fetch source code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Determine Go version
        id: go
        uses: ./.github/actions/go-version

      - name: Install Go toolchain
        uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
        with:
          go-version: ${{ steps.go.outputs.version }}
          cache-dependency-path: go.sum

      - name: Download testing framework
        shell: bash
        run: |
          ./.github/scripts/equivalence-test.sh download_equivalence_test_binary \
            0.5.0 \
            ./bin/equivalence-tests \
            linux \
            amd64

      - name: Build terraform
        shell: bash
        run: ./.github/scripts/equivalence-test.sh build_terraform_binary ./bin/terraform

      - name: Run equivalence tests
        id: equivalence-tests
        shell: bash {0} # we want to capture the exit code
        run: |
          ./bin/equivalence-tests diff \
              --tests=testing/equivalence-tests/tests \
              --goldens=testing/equivalence-tests/outputs \
              --binary=$(pwd)/bin/terraform
          echo "exit-code=$?" >> "${GITHUB_OUTPUT}"

      - name: Equivalence tests failed
        if: steps.equivalence-tests.outputs.exit-code == 1 # 1 is the exit code for failure
        shell: bash
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          gh pr comment ${{ github.event.pull_request.number }} \
            --body "The equivalence tests failed. Please investigate [here](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})."
          exit 1 # fail the job

      - name: Equivalence tests changed
        if: steps.equivalence-tests.outputs.exit-code == 2 # 2 is the exit code for changed
        shell: bash
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          gh pr comment ${{ github.event.pull_request.number }} \
            --body "The equivalence tests will be updated. Please verify the changes [here](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})."
equivalence-test-manual-update perms .github/workflows/equivalence-test-manual-update.yml
Triggers
workflow_dispatch
Runs on
ubuntu-latest
Jobs
run-equivalence-tests
View raw YAML
name: equivalence-tests-manual

on:
  workflow_dispatch:
    inputs:
      target-branch:
        type: string
        description: "Which branch should be updated?"
        required: true
      new-branch:
        type: string
        description: "Name of new branch to be created for the review."
        required: true
      equivalence-test-version:
        type: string
        description: 'Equivalence testing framework version to use (no v prefix, eg. 0.5.0).'
        default: "0.5.0"
        required: true

permissions:
  contents: write
  pull-requests: write

jobs:
  run-equivalence-tests:
    name: "Run equivalence tests"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          ref: ${{ inputs.target-branch }}

      - name: Determine Go version
        id: go
        uses: ./.github/actions/go-version

      - name: Install Go toolchain
        uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
        with:
          go-version: ${{ steps.go.outputs.version }}
          cache-dependency-path: go.sum

      - uses: ./.github/actions/equivalence-test
        with:
          target-equivalence-test-version: ${{ inputs.equivalence-test-version }}
          target-os: linux
          target-arch: amd64
          current-branch: ${{ inputs.target-branch }}
          new-branch: ${{ inputs.new-branch }}
          reviewers: ${{ github.actor }}
          message: "Update equivalence test golden files."
          github-token: ${{ github.token }}
equivalence-test-update perms .github/workflows/equivalence-test-update.yml
Triggers
pull_request_target
Runs on
ubuntu-latest, ubuntu-latest
Jobs
check, run-equivalence-tests
Commands
  • merged='${{ github.event.pull_request.merged }}' target_branch='${{ github.event.pull_request.base.ref }}' targets_release_branch=false if [ "$target_branch" == "main" ]; then targets_release_branch=true elif [ "$target_branch" =~ ^v[0-9]+\.[0-9]+$ ]; then targets_release_branch=true fi should_run=false if [ "$merged" == "true" ] && [ "$targets_release_branch" == "true" ]; then should_run=true fi echo "should_run=$should_run" >> ${GITHUB_OUTPUT}
View raw YAML
name: equivalence-test-update

on:
  pull_request_target:
    types: [ closed ]

permissions:
  contents: write
  pull-requests: write

jobs:
  check:
    name: "Should run equivalence tests?"
    runs-on: ubuntu-latest
    outputs:
        should_run: ${{ steps.target_branch.outputs.should_run }}
    steps:
      - name: target_branch
        id: target_branch
        run: |
          merged='${{ github.event.pull_request.merged }}'
          target_branch='${{ github.event.pull_request.base.ref }}'

          targets_release_branch=false
          if [ "$target_branch" == "main" ]; then
            targets_release_branch=true
          elif [ "$target_branch" =~ ^v[0-9]+\.[0-9]+$ ]; then
            targets_release_branch=true
          fi

          should_run=false
          if [ "$merged" == "true" ] && [ "$targets_release_branch" == "true" ]; then
            should_run=true
          fi

          echo "should_run=$should_run" >> ${GITHUB_OUTPUT}
  run-equivalence-tests:
    name: "Run equivalence tests"
    needs:
      - check
    if: needs.check.outputs.should_run == 'true'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          ref: ${{ inputs.target-branch }}

      - name: Determine Go version
        id: go
        uses: ./.github/actions/go-version

      - name: Install Go toolchain
        uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
        with:
          go-version: ${{ steps.go.outputs.version }}
          cache-dependency-path: go.sum

      - uses: ./.github/actions/equivalence-test
        with:
          target-equivalence-test-version: 0.5.0
          target-os: linux
          target-arch: amd64
          current-branch: ${{ github.event.pull_request.base.ref }}
          new-branch: equivalence-testing/${{ github.event.pull_request.head.ref }}
          reviewers: ${{ github.event.pull_request.merged_by.login }}
          message: "Update equivalence test golden files after ${{ github.event.pull_request.html_url }}."
          github-token: ${{ github.token }}
issue-comment-created .github/workflows/issue-comment-created.yml
Triggers
issue_comment
Runs on
ubuntu-latest
Jobs
issue_comment_triage
Actions
actions-ecosystem/action-remove-labels
View raw YAML
name: Issue Comment Created Triage

on:
  issue_comment:
    types: [created]

jobs:
  issue_comment_triage:
    runs-on: ubuntu-latest
    steps:
      - uses: actions-ecosystem/action-remove-labels@2ce5d41b4b6aa8503e285553f75ed56e0a40bae0 # v1.3.0
        with:
          labels: |
            stale
            waiting-reply
lock .github/workflows/lock.yml
Triggers
schedule
Runs on
ubuntu-latest
Jobs
lock
Actions
dessant/lock-threads
View raw YAML
name: 'Lock Threads'

on:
  schedule:
    - cron: '50 1 * * *'

jobs:
  lock:
    runs-on: ubuntu-latest
    steps:
      - uses: dessant/lock-threads@7266a7ce5c1df01b1c6db85bf8cd86c737dadbe7 # v6.0.0
        with:
          process-only: 'issues, prs'
          github-token: ${{ github.token }}
          issue-comment: >
            I'm going to lock this issue because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues.

            If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.
          issue-inactive-days: '30'
          pr-comment: >
            I'm going to lock this pull request because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active contributions.

            If you have found a problem that seems related to this change, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.
          pr-inactive-days: '30'