grept stands for GoREPositorylinTer.
grept is a powerful, extensible linting tool for repositories. Inspired by RepoLinter, grept is designed to ensure that your repositories follow certain predefined standards. It parses and evaluates configuration files, generates plans based on the specified configuration, and applies the plans. This makes it an excellent tool for maintaining consistency and quality in your codebase.
grept is written in Golang, using cobra for command-line interface and afero for file system abstraction. This makes it both highly extensible and testable.
You can install grept directly using the go install command:
go install github.com/Azure/grept@latest
Please make sure you have Go installed and your GOPATH properly set up.
grept supports two commands: plan and apply.
The plan command generates a plan based on the specified configuration.
grept plan [path-to-config-folder]
Replace [path-to-config-folder] with your configuration files folder's path. The plan command will parse all files with suffix *.grept.hcl and generate a plan. If all rule checks are successful, it will print "All rule checks successful, nothing to do." Otherwise, it will print the plan.
Omitting [path-to-config-folder] will use the current folder instead.
The apply command applies the plan generated by the plan command.
grept apply [path-to-config-folder]Replace [path-to-config-folder] with your configuration files folder's path. The apply command will apply the fixes to the issues found by the plan command. If all fixes are applied successfully, it will print "Plan applied successfully."
Omitting [path-to-config-folder] will use the current folder instead.
You can use the -a or --auto flag to apply fixes without confirmation.
grept apply -a [path-to-config-folder]
The config folder path support multiple different types:
- Local paths
- Terraform Registry
- GitHub
- Bitbucket
- Generic Git, Mercurial repositories
- HTTP URLs
- S3 buckets
- GCS buckets
An example:
grept apply git::https://github.com/lonegunmanb/grept-example-config.git//mit-exampleYou can check Terraform Module sources document for more details.
The following example config file would ensure that your repository contains a MIT license file:
data http mit_license {
url = "https://raw.githubusercontent.com/Azure/terraform-verified-module/main/LICENSE"
}
rule file_hash license {
glob = "LICENSE"
hash = sha1(data.http.mit_license.response_body)
}
fix local_file license {
rule_ids = [rule.file_hash.license.id]
paths = [rule.file_hash.glob]
content = data.http.mit_license.response_body
}All built-in functions provided by HashiCorp Packer are available.
All toxxx functions provided by Terraform 1.5.7 are supported:
tostringtonumbertobooltosettolisttomap
We've provided the following new functions:
env: To read environment variable, likeenv("GITHUB_REPOSITORY").compliment: Return the compliment of multiple lists.yaml2json: Convert yaml to corresponding json string.
It seems like go-cty-yaml has a bug, so please do not use yamldecode function to parse and check Github action yaml files. I added this yaml2json function so you can convert yaml string to json first, then use jsondecode function to unmarshal it and check whether the github action file meets your requirement.
You can find detailed explanations about different components of the grept tool.
The documents are organized by categories: rule blocks, data blocks, and fix blocks.
Rule blocks define the rules that should be enforced in the repository.
Data blocks define the data that should be collected from the repository.
Fix blocks define the actions that should be taken when a rule fails.
For each block type, you can find detailed information about the block's attributes, exported attributes, and usage examples.
You can define and use locals block in grept just like Terraform.
You can use for_each like Terraform. for_each is a meta-argument, it can be used with:
datarulefix
The for_each meta-argument accepts a map or a set of strings, and creates an instance for each item in that map or set. Each instance has a distinct block associated with it, and each is separately planed and applied.
locals {
items = toset(["item1", "item2", "item3"])
}
data "http" echo {
for_each = local.items
url = "http://foo"
request_body = jsonencode({
query = each.value
})
}
rule "must_be_true" sample {
for_each = local.items
condition = each.value != data.http.echo[each.value]response_body
}
fix "local_file" hello_world{
for_each = local.items
rule_ids = [rule.must_be_true.sample[each.value].id]
paths = [each.value]
content = each.value
}The precondition block is used to specify a condition that must be met before a block is evaluated. This is useful for adding checks that prevent the rule from running when certain conditions are not met.
Here is an example of a precondition block that checks if the GITHUB_TOKEN environment variable exists:
rule "must_be_true" "check_env" {
precondition {
condition = env("GITHUB_TOKEN") != ""
error_message = "GITHUB_TOKEN environment variable must be set"
}
condition = true
}Contributions to grept are welcome! Please submit a pull request or issue on the grept GitHub page.
grept is released under the MIT license. For more information, see LICENSE.