-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add hook for provider version checking #51
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,3 +3,5 @@ | |
|
||
# Crash log files | ||
crash.log | ||
|
||
.vscode/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
# Provider Pinned Versions Hook | ||
|
||
This hook validates that all providers under the `required_providers` block of a Terraform configuration file have a pinned version. | ||
|
||
## Usage | ||
|
||
### cli | ||
|
||
```bash | ||
pre-commit run provider-pinned-versions --all-files | ||
``` | ||
|
||
### pre-commit config | ||
|
||
```yaml | ||
repos: | ||
- repo: https://github.com/open-turo/standards-terraform | ||
hooks: | ||
- id: provider-pinned-versions | ||
files: ^terraform.tf$ | ||
``` | ||
|
||
## Examples of supported configurations | ||
|
||
### Standard pinned version | ||
|
||
```hcl | ||
terraform { | ||
required_providers { | ||
aws = { | ||
source = "hashicorp/aws" | ||
version = "3.40.0" | ||
} | ||
} | ||
} | ||
``` | ||
|
||
### Pre-release version | ||
|
||
```hcl | ||
terraform { | ||
required_providers { | ||
aws = { | ||
source = "hashicorp/aws" | ||
version = "3.41.0-beta1" | ||
} | ||
} | ||
} | ||
``` | ||
|
||
## Examples of unsupported configurations | ||
|
||
### Version constraints | ||
|
||
If the version contains any of the following constraint characters, `!~><`, the hook will fail. | ||
|
||
```hcl | ||
terraform { | ||
required_providers { | ||
aws = { | ||
source = "hashicorp/aws" | ||
version = ">= 3.40.0" | ||
} | ||
} | ||
} | ||
``` | ||
|
||
```hcl | ||
terraform { | ||
required_providers { | ||
aws = { | ||
source = "hashicorp/aws" | ||
version = "~> 3.40.0" | ||
} | ||
} | ||
} | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
#!/bin/bash -e | ||
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | ||
AWK_FILE="${SCRIPT_DIR}/required_providers.awk" | ||
|
||
check_files() { | ||
has_error=0 | ||
for file in "$@"; do | ||
awk -f "$AWK_FILE" "$file" || has_error=1 | ||
done | ||
return $has_error | ||
} | ||
|
||
check_files "$@" | ||
exit $has_error |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
terraform { | ||
required_version = ">= 1.0.0, < 2.0.0" | ||
|
||
required_providers { | ||
aws = { | ||
source = "hashicorp/aws" | ||
version = "~>5.78" | ||
} | ||
newrelic = { | ||
source = "newrelic/newrelic" | ||
version = ">=2, <3" | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
terraform { | ||
required_version = ">= 1.0.0, < 2.0.0" | ||
|
||
required_providers { | ||
aws = { | ||
source = "hashicorp/aws" | ||
version = "5.78.0" | ||
} | ||
newrelic = { | ||
source = "newrelic/newrelic" | ||
version = ">=2, <3" | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
terraform { | ||
required_version = ">= 1.0.0, < 2.0.0" | ||
|
||
required_providers { | ||
aws = { | ||
source = "hashicorp/aws" | ||
version = "5.78.0" | ||
} | ||
cyral = { | ||
source = "cyralinc/cyral" | ||
version = "4.14.1" | ||
} | ||
mysql = { | ||
source = "petoju/mysql" | ||
version = "3.0.67" | ||
} | ||
newrelic = { | ||
source = "newrelic/newrelic" | ||
version = "3.52.1" | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
terraform { | ||
required_version = ">= 1.0.0, < 2.0.0" | ||
|
||
required_providers { | ||
aws = { source = "hashicorp/aws", version = "5.78.0" } | ||
cyral = { source = "cyralinc/cyral", version = "4.14.1" } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
terraform { | ||
required_version = ">= 1.0.0, < 2.0.0" | ||
|
||
required_providers { | ||
aws = { | ||
source = "hashicorp/aws" | ||
version = "5.78.0-beta-some-branch-name" | ||
} | ||
newrelic = { | ||
source = "newrelic/newrelic" | ||
version = "3.52.1" | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
terraform { | ||
required_version = ">= 1.0.0, < 2.0.0" | ||
|
||
required_providers { | ||
aws = { | ||
version = "5.78.0" | ||
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
Sorry, something went wrong. |
||
source = "hashicorp/aws" | ||
} | ||
cyral = { | ||
source = "cyralinc/cyral" | ||
version = "4.14.1" | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
#! /bin/awk | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AWK is fine, but I wonder if it would be easier if we used python (since it's available already as pre-commit uses python)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought of it as well but I thought we wouldn't have access to it since we're removing the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can leave that in... and/or just assume that python is present. |
||
|
||
BEGIN { | ||
in_required_providers = 0; | ||
brace_count = 0; | ||
version_prefix_regex = ".*version[[:space:]]+=[[:space:]]+"; | ||
provider_prefix_regex = "[a-z_-]+[[:space:]]+=[[:space:]]+\{"; | ||
version_constraints_regex = "[!~><]+"; | ||
} | ||
|
||
{ | ||
if ($0 ~ /required_providers/) { | ||
in_required_providers = 1; | ||
} | ||
|
||
# If inside "required_providers" block, count braces | ||
if (in_required_providers) { | ||
brace_count += gsub(/{/, "{"); | ||
brace_count -= gsub(/}/, "}"); | ||
|
||
# If brace count returns to 0, exit the block | ||
if (brace_count == 0) { | ||
in_required_providers = 0; | ||
next; | ||
} | ||
|
||
if ($0 ~ provider_prefix_regex) { | ||
provider = $1; | ||
} | ||
|
||
# If the line doesn't contain a version, skip | ||
if (!($0 ~ version_prefix_regex)) { | ||
next; | ||
} else { | ||
# If the line contains a version, remove the prefix | ||
gsub(version_prefix_regex, "", $0); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would have used match with capture groups but it wasn't working on macos. I think it's a gawk specific function and depends on the os. |
||
if ($0 ~ version_constraints_regex) { | ||
# remove trailing curly brace if any (happens when version is inlined) | ||
gsub(/}/, "", $0); | ||
error[++i] = "ERROR: '" provider "' version not in pinned format: " $0 " in file " FILENAME; | ||
} | ||
} | ||
} | ||
} | ||
|
||
END { | ||
if (length(error) > 0) { | ||
for (j = 1; j <= i; j++) { | ||
print error[j] | ||
} | ||
exit 1; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
#!/bin/bash -e | ||
|
||
# get the directory of the script | ||
script_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" | ||
|
||
echo "testing: $script_directory" | ||
|
||
# shellcheck disable=SC2207 | ||
files=($(find "$script_directory"/fixtures -type f -name "pass_*.tf")) | ||
for file in "${files[@]}"; do | ||
echo "testing: check $file" | ||
"$script_directory/check" "$file" | ||
done | ||
|
||
# shellcheck disable=SC2207 | ||
files=($(find "$script_directory"/fixtures -type f -name "fail_*.tf")) | ||
for file in "${files[@]}"; do | ||
echo "testing: check $file" | ||
echo " expecting error" | ||
if "$script_directory/check" "$file"; then | ||
echo "ERROR: should have failed" | ||
exit 1 | ||
fi | ||
done | ||
|
||
echo "testing: PASS" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add to the readme this hook and how to use it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like we don't have any docs about hooks