Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .codespellrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[codespell]
skip = template_helpers_test.go
72 changes: 72 additions & 0 deletions .github/workflows/base-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
name: Base Tests

on:
workflow_call:

jobs:
test:
name: Test (${{ matrix.os }})
runs-on: ${{ matrix.os }}-latest
strategy:
fail-fast: false
matrix:
os: [ubuntu, macos]

steps:
- name: Checkout code
uses: actions/checkout@v5

- name: Use mise to install dependencies
uses: jdx/mise-action@v2
with:
version: 2025.8.16
experimental: true
env:
# Adding token here to reduce the likelihood of hitting rate limit issues.
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- id: go-cache-paths
run: |
echo "go-build=$(go env GOCACHE)" >> "$GITHUB_OUTPUT"
echo "go-mod=$(go env GOMODCACHE)" >> "$GITHUB_OUTPUT"
shell: bash

- name: Go Build Cache
uses: actions/cache@v4
with:
path: ${{ steps.go-cache-paths.outputs.go-build }}
key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum') }}-${{ matrix.os }}-amd64

- name: Go Mod Cache
uses: actions/cache@v4
with:
path: ${{ steps.go-cache-paths.outputs.go-mod }}
key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }}-${{ matrix.os }}-amd64

- name: Install go-junit-report
run: go install github.com/jstemmer/go-junit-report/v2@latest

- name: Run Tests
id: run-tests
run: |
set -o pipefail
go test -v ./... -timeout 45m | tee >(go-junit-report -set-exit-code > result.xml)
shell: bash
env:
# Adding token here to reduce the likelihood of hitting rate limit issues.
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Upload Report (${{ matrix.os }})
uses: actions/upload-artifact@v4
with:
name: test-report-${{ matrix.os }}
path: result.xml

- name: Display Test Results (${{ matrix.os }})
uses: mikepenz/action-junit-report@v5
if: always()
with:
report_paths: result.xml
detailed_summary: 'true'
include_time_in_summary: 'true'
group_suite: 'true'
67 changes: 67 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: Build

on:
workflow_call:

jobs:
build:
name: Build (${{ matrix.os }}/${{ matrix.arch }})
runs-on: ubuntu-latest
strategy:
matrix:
include:
- os: darwin
arch: amd64
- os: darwin
arch: arm64
- os: linux
arch: "386"
- os: linux
arch: amd64
- os: linux
arch: arm64
- os: windows
arch: "386"
- os: windows
arch: amd64

steps:
- name: Checkout code
uses: actions/checkout@v5

- name: Use mise to install dependencies
uses: jdx/mise-action@v2
with:
version: 2025.8.16
experimental: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- id: go-cache-paths
run: |
echo "go-build=$(go env GOCACHE)" >> "$GITHUB_OUTPUT"

- name: Go Build Cache
uses: actions/cache@v4
with:
path: ${{ steps.go-cache-paths.outputs.go-build }}
key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum') }}-${{ matrix.os }}-${{ matrix.arch }}

- name: Build Boilerplate
env:
GOOS: ${{ matrix.os }}
GOARCH: ${{ matrix.arch }}
run: |
OUTPUT="bin/boilerplate_${GOOS}_${GOARCH}"
if [[ "${GOOS}" == "windows" ]]; then
OUTPUT="${OUTPUT}.exe"
fi
go build -o "${OUTPUT}" \
-ldflags "-s -w -X github.com/gruntwork-io/go-commons/version.Version=${GITHUB_REF_NAME} -extldflags '-static'" \
.

- name: Upload Build Artifact
uses: actions/upload-artifact@v4
with:
name: boilerplate_${{ matrix.os }}_${{ matrix.arch }}
path: bin/boilerplate_${{ matrix.os }}_${{ matrix.arch }}*
26 changes: 26 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: CI

on:
push:

jobs:
lint:
uses: ./.github/workflows/lint.yml
secrets: inherit

codespell:
uses: ./.github/workflows/codespell.yml
secrets: inherit

base_tests:
needs: [lint, codespell]
uses: ./.github/workflows/base-test.yml
permissions:
contents: read
checks: write
secrets: inherit

build:
needs: [lint, codespell]
uses: ./.github/workflows/build.yml
secrets: inherit
36 changes: 36 additions & 0 deletions .github/workflows/codespell.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Codespell

on:
workflow_call:

jobs:
codespell:
name: Check Spelling
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v5

- name: Create default packages file
run: |
cat <<EOF > .mise-python-default-packages
codespell==2.4.0
EOF

echo "MISE_PYTHON_DEFAULT_PACKAGES_FILE=.mise-python-default-packages" >> "$GITHUB_ENV"

- name: Use mise to install dependencies
uses: jdx/mise-action@v2
with:
version: 2025.8.16
experimental: true
mise_toml: |
[tools]
python = "3.13.3"
env:
# Adding token here to reduce the likelihood of hitting rate limit issues.
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Run codespell
run: codespell .
53 changes: 53 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: Lint

on:
workflow_call:

jobs:
lint:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v5

- id: go-version
run: |
go version

- name: Set up mise
uses: jdx/mise-action@v2
with:
version: 2025.8.16
experimental: true
env:
# Adding token here to reduce the likelihood of hitting rate limit issues.
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- id: go-cache-paths
run: |
echo "go-build=$(go env GOCACHE)" >> "$GITHUB_OUTPUT"
echo "go-mod=$(go env GOMODCACHE)" >> "$GITHUB_OUTPUT"

# TODO: Make this less brittle.
echo "golanci-lint-cache=/home/runner/.cache/golangci-lint" >> "$GITHUB_OUTPUT"

- name: Go Build Cache
uses: actions/cache@v4
with:
path: ${{ steps.go-cache-paths.outputs.go-build }}
key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum') }}-linux-amd64

- name: Go Mod Cache
uses: actions/cache@v4
with:
path: ${{ steps.go-cache-paths.outputs.go-mod }}
key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }}-linux-amd64

- name: golangci-lint Cache
uses: actions/cache@v4
with:
path: ${{ steps.go-cache-paths.outputs.golanci-lint-cache }}
key: ${{ runner.os }}-golangci-lint-${{ hashFiles('**/go.sum') }}-linux-amd64

- name: Lint
run: make lint
7 changes: 5 additions & 2 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# This file is generated using `make update-local-lint` to track the linting used in Terragrunt. Do not edit manually.
version: "2"
run:
go: "1.24"
go: "1.25"
issues-exit-code: 1
tests: true
output:
Expand Down Expand Up @@ -54,7 +54,7 @@ linters:
- unparam
- usetesting
- wastedassign
- wsl
- wsl_v5
- zerologlint
disable:
- depguard
Expand Down Expand Up @@ -103,6 +103,9 @@ linters:
- -ST1001
unparam:
check-exported: false
wsl_v5:
allow-whole-block: false
branch-max-lines: 2
exclusions:
generated: lax
rules:
Expand Down
41 changes: 38 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ You can find older versions on the [Releases Page](https://github.com/gruntwork-
When you run Boilerplate, it performs the following steps:

1. Read the `boilerplate.yml` file in the folder specified by the `--template-url` option to find all defined
varaibles.
variables.
1. Gather values for the variables from any `--var` and `--var-file` options that were passed in and prompting the user
for the rest (unless the `--non-interactive` flag is specified).
1. Copy each file from `--template-url` to `--output-folder`, running each non-binary file through the Go
Expand Down Expand Up @@ -289,7 +289,7 @@ variables:
description: Enter the welcome text used by the website dependency

- name: ShowLogo
description: Should the webiste show the logo (true or false)?
description: Should the website show the logo (true or false)?
type: bool
default: true

Expand Down Expand Up @@ -688,7 +688,7 @@ Here's an example prompt for a variable with validations that shows how invalid

![Example Boilerplate real-time validation](./docs/bp-validation.png)

Here's an example demonstating how to specify validations when defining your variables:
Here's an example demonstrating how to specify validations when defining your variables:

```yaml
variables:
Expand Down Expand Up @@ -1151,6 +1151,41 @@ variables:

With this variable, you can have a template file named `{{ .RootTerragruntFileName }}` which will generate a file named according to the value of RootTerragruntFileName.

## Running tests

To run all tests:

```bash
go test ./...
```

This will run all tests except AWS-dependent integration tests, which are excluded by default since they require AWS credentials.

### Running AWS-dependent tests

Some integration tests require AWS credentials and are tagged with the `aws` build tag. These tests are also prefixed with `TestAWS` for easy targeting. To run these tests:

1. Set up AWS credentials (e.g., using AWS CLI or environment variables):

```bash
export AWS_ACCESS_KEY_ID=your_access_key
export AWS_SECRET_ACCESS_KEY=your_secret_key
```

2. Run tests with the `aws` build tag enabled:

```bash
go test -tags=aws ./...
```

Or target AWS tests specifically by name:

```bash
go test -tags=aws -run '^TestAWS' ./...
```

These AWS tests validate Terragrunt configurations by running `terragrunt validate-all`, which requires valid AWS credentials to access AWS provider APIs.

## Alternative project generators

Before creating Boilerplate, we tried a number of other project generators, but none of them met all of our
Expand Down
5 changes: 4 additions & 1 deletion cli/boilerplate_cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
package cli

import (
"context"
"fmt"

"github.com/gruntwork-io/go-commons/entrypoint"
Expand Down Expand Up @@ -120,8 +121,10 @@ func runApp(cliContext *cli.Context) error {
return err
}

ctx := context.Background()

// The root boilerplate.yml is not itself a dependency, so we pass an empty Dependency.
emptyDep := variables.Dependency{}

return templates.ProcessTemplate(opts, opts, emptyDep)
return templates.ProcessTemplateWithContext(ctx, opts, opts, emptyDep)
}
Loading