Skip to content
Closed
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
25 changes: 25 additions & 0 deletions .github/actions/github-actions.check-jsonschema/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ inputs:
target_dir:
required: false
description: "(Optional) The directory to validate. Defaults to `.github/workflows` for `schema_type: workflows` and to `.github/actions` for `schema_type: actions`."
annotations_enabled:
required: false
default: "true"
description: "(Optional) Whether to annotate the failing files in the pull request when the validation fails, by re-running `check-jsonschema` with `--output-format json`. Schema validation reports no line numbers, so the annotation points at the file and names the failing JSON path. Defaults to `true`."

outputs:
skipped:
Expand Down Expand Up @@ -94,6 +98,27 @@ runs:
readarray -t files <<< "$FILES"
check-jsonschema --builtin-schema "$SCHEMA" "${files[@]}"

# GitHub only renders annotations from `::error` lines, so the errors are re-read as JSON. The file names are
# the ones passed in, which are repository-relative already.
- name: Annotate Findings
id: validate-annotate
if: always() && inputs.annotations_enabled == 'true' && steps.validate.outcome == 'failure'
shell: bash
env:
SCHEMA_TYPE: ${{ inputs.schema_type }}
SCHEMA: ${{ steps.target.outputs.schema }}
FILES: ${{ steps.target.outputs.files }}
run: |
# A workflow command takes one line, so newlines and the property separators are percent-encoded the way
# the runner decodes them.
readarray -t files <<< "$FILES"
check-jsonschema --builtin-schema "$SCHEMA" --output-format json "${files[@]}" 2>/dev/null | jq -r --arg type "$SCHEMA_TYPE" '
def esc: gsub("%"; "%25") | gsub("\r"; "%0D") | gsub("\n"; "%0A");
def prop: esc | gsub(":"; "%3A") | gsub(","; "%2C");
(.errors // [])[]
| "::error file=\(.filename),title=\("check-jsonschema · " + $type | prop)::\(.path + ": " + .message | esc)"
' || true

- name: Add Failure Details to Job Summary
id: validate-summary
if: always() && steps.validate.outcome == 'failure'
Expand Down
22 changes: 22 additions & 0 deletions .github/actions/terraform.docs/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ inputs:
args:
required: false
description: "(Optional) Additional arguments to pass to `terraform-docs` (e.g. `--sort-by required`)."
annotations_enabled:
required: false
default: "true"
description: "(Optional) Whether to annotate the out-of-date documentation file in the pull request when the check fails. Only applies with `check: true`. Defaults to `true`."

outputs:
skipped:
Expand Down Expand Up @@ -136,6 +140,24 @@ runs:

terraform-docs "${args[@]}" .

# `terraform-docs` names the stale file (`Error: <path> is out of date`), relative to the module it ran in.
- name: Annotate Findings
id: docs-annotate
if: always() && inputs.annotations_enabled == 'true' && inputs.check == 'true' && steps.docs.outcome == 'failure'
shell: bash
env:
TARGET_DIR: ${{ steps.target.outputs.target_dir }}
LOG_FILE: ${{ steps.docs.outputs.log_file }}
run: |
prefix=""
if [ "$TARGET_DIR" != "." ]; then
prefix="$TARGET_DIR/"
fi

sed -n 's/^Error: \(.*\) is out of date$/\1/p' "$LOG_FILE" | while IFS= read -r file; do
echo "::error file=$prefix$file,title=terraform-docs · Out of date::Regenerate this file with \`terraform-docs\`."
done

- name: Add Failure Details to Job Summary
id: docs-summary
if: always() && steps.docs.outcome == 'failure'
Expand Down
27 changes: 27 additions & 0 deletions .github/actions/terraform.fmt/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ inputs:
required: false
default: "true"
description: "(Optional) Whether to also check subdirectories. Defaults to `true`."
annotations_enabled:
required: false
default: "true"
description: "(Optional) Whether to annotate the unformatted lines in the pull request when `terraform fmt` fails, from the hunks of the diff it printed. Defaults to `true`."

outputs:
skipped:
Expand Down Expand Up @@ -66,6 +70,29 @@ runs:

terraform -chdir="$TARGET_DIR" fmt "${args[@]}"

# The diff already names each file (`+++ new/<file>`) and the first line of every hunk (`@@ -a,b +c,d @@`), so
# the annotations are read from the captured output. Paths are relative to `target_dir`.
- name: Annotate Findings
id: fmt-annotate
if: always() && inputs.annotations_enabled == 'true' && steps.fmt.outcome == 'failure'
shell: bash
env:
TARGET_DIR: ${{ steps.target.outputs.target_dir }}
LOG_FILE: ${{ steps.fmt.outputs.log_file }}
run: |
prefix=""
if [ "$TARGET_DIR" != "." ]; then
prefix="$TARGET_DIR/"
fi

awk -v prefix="$prefix" '
/^\+\+\+ new\// { file = substr($0, 9); next }
/^@@ / && file != "" {
line = $3; sub(/^\+/, "", line); sub(/,.*/, "", line)
printf "::error file=%s%s,line=%s,title=terraform fmt · Not formatted::Run `terraform fmt` to format this file.\n", prefix, file, line
}
' "$LOG_FILE"

- name: Add Failure Details to Job Summary
id: fmt-summary
if: always() && steps.fmt.outcome == 'failure'
Expand Down
81 changes: 81 additions & 0 deletions .github/workflows/test.check-annotations-remaining.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: Test - check annotations remaining


on:
pull_request:
branches:
- "**"


jobs:
test:
name: Test
runs-on: ubuntu-latest

steps:
- name: Checkout
id: checkout
uses: actions/checkout@v7

- name: Set up tools
id: setup-tools
uses: tedilabs/github-actions/.github/actions/mise.setup-tools@main
with:
mise_toml: |
[tools]
terraform = "1.16.3"
terraform-docs = "0.24.0"
"pipx:check-jsonschema" = "latest"

- name: terraform fmt (annotated)
id: fmt
continue-on-error: true
uses: tedilabs/github-actions/.github/actions/terraform.fmt@feat/check-annotations-remaining
with:
target_dir: fixtures/annotations2/tf

- name: terraform-docs (annotated, recursive)
id: docs
continue-on-error: true
uses: tedilabs/github-actions/.github/actions/terraform.docs@feat/check-annotations-remaining
with:
target_dir: fixtures/annotations2/docs
check: "true"
recursive: "true"

- name: check-jsonschema workflows (annotated)
id: cj-workflows
continue-on-error: true
uses: tedilabs/github-actions/.github/actions/github-actions.check-jsonschema@feat/check-annotations-remaining
with:
schema_type: workflows
target_dir: fixtures/annotations2/workflows

- name: check-jsonschema actions (annotated)
id: cj-actions
continue-on-error: true
uses: tedilabs/github-actions/.github/actions/github-actions.check-jsonschema@feat/check-annotations-remaining
with:
schema_type: actions
target_dir: fixtures/annotations2/actions

- name: terraform fmt (annotations disabled, for comparison)
id: fmt-off
continue-on-error: true
uses: tedilabs/github-actions/.github/actions/terraform.fmt@feat/check-annotations-remaining
with:
target_dir: fixtures/annotations2/tf
annotations_enabled: "false"

- name: Outcomes
id: outcomes
env:
FMT: ${{ steps.fmt.outcome }}/${{ steps.fmt.outputs.exitcode }}
DOCS: ${{ steps.docs.outcome }}/${{ steps.docs.outputs.exitcode }}
CJ_WORKFLOWS: ${{ steps.cj-workflows.outcome }}/${{ steps.cj-workflows.outputs.exitcode }}
CJ_ACTIONS: ${{ steps.cj-actions.outcome }}/${{ steps.cj-actions.outputs.exitcode }}
FMT_OFF: ${{ steps.fmt-off.outcome }}/${{ steps.fmt-off.outputs.exitcode }}
run: |
for v in FMT DOCS CJ_WORKFLOWS CJ_ACTIONS FMT_OFF; do printf '%s=<%s>\n' "$v" "${!v}"; done
for v in FMT DOCS CJ_WORKFLOWS CJ_ACTIONS FMT_OFF; do [ "${!v%%/*}" = "failure" ] || { echo "FAIL $v should fail on the fixtures"; exit 1; }; done
echo "All five checks failed as intended."
3 changes: 3 additions & 0 deletions fixtures/annotations2/actions/bad/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name: bad
runs:
using: composite
1 change: 1 addition & 0 deletions fixtures/annotations2/docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# stale
3 changes: 3 additions & 0 deletions fixtures/annotations2/docs/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
variable "name" {
type = string
}
1 change: 1 addition & 0 deletions fixtures/annotations2/docs/modules/a/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# stale
1 change: 1 addition & 0 deletions fixtures/annotations2/docs/modules/a/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
variable "a" {}
7 changes: 7 additions & 0 deletions fixtures/annotations2/tf/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
variable "name" {

Check failure on line 1 in fixtures/annotations2/tf/main.tf

View workflow job for this annotation

GitHub Actions / Test

terraform fmt · Not formatted

Run `terraform fmt` to format this file.
type = string
}

locals {
x = 1
}
3 changes: 3 additions & 0 deletions fixtures/annotations2/tf/sub/out.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
output "x" {

Check failure on line 1 in fixtures/annotations2/tf/sub/out.tf

View workflow job for this annotation

GitHub Actions / Test

terraform fmt · Not formatted

Run `terraform fmt` to format this file.
value = 1
}
6 changes: 6 additions & 0 deletions fixtures/annotations2/workflows/bad.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
name: bad
on: push
jobs:
x:
runs-on: ubuntu-latest
steps: not-a-list
Loading