hashicorp/terraform
10 workflows · maturity 50% · 6 patterns · GitHub ↗
Practices
✓ Matrix✓ Permissions○ Security scan○ AI review✓ Cache○ Concurrency✓ Reusable workflows
Detected patterns
Security dimensions
Workflows (10)
backport perms .github/workflows/backport.yml
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
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
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
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
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
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
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
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
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
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'