grept
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.
Installation
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.
How to use
grept
supports two commands: plan
and apply
.
Plan Command
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.
Apply Command
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:
An example:
grept apply git::https://github.com/lonegunmanb/grept-example-config.git//mit-example
You can check Terraform Module sources document for more details.
Example
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
}
Functions
All built-in functions provided by HashiCorp Packer are available.
All toxxx
functions provided by Terraform 1.5.7
are supported:
tostring
tonumber
tobool
toset
tolist
tomap
We've provided the following new functions:
env
: To read environment variable, like env("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.
Blocks
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
Rule blocks define the rules that should be enforced in the repository.
Data Blocks
Data blocks define the data that should be collected from the repository.
Fix Blocks
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.
Locals
You can define and use locals
block in grept
just like Terraform.
for_each
support
You can use for_each
like Terraform. for_each
is a meta-argument, it can be used with:
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
}
precondition
support
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
}
Contributing
Contributions to grept
are welcome! Please submit a pull request or issue on the grept GitHub page.
License
grept
is released under the MIT license. For more information, see LICENSE.