Skip to content
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

build: introduce regal_standalone build flag, use for lint's "fix" hint #1070

Merged
Merged
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
6 changes: 5 additions & 1 deletion .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ builds:
ignore:
- goos: windows
goarch: arm64
tags:
- regal_standalone
ldflags:
- -s -w
- -X github.com/styrainc/regal/pkg/version.Version={{ .Version }}
Expand All @@ -28,6 +30,8 @@ builds:
goarch:
- amd64
- arm64
tags:
- regal_standalone
ldflags:
- -s -w
- -X github.com/styrainc/regal/pkg/version.Version={{ .Version }}
Expand Down Expand Up @@ -68,7 +72,7 @@ checksum:
name_template: "checksums.txt"

snapshot:
name_template: "{{ incpatch .Version }}-next"
version_template: "{{ incpatch .Version }}-next"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

side-track, this was deprecated in some Gorelease version. I don't think we're using snapshot releases, but this will squelch the warning.


changelog:
use: github
Expand Down
120 changes: 55 additions & 65 deletions build/do.rq
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,10 @@ embedded_caps_dir := rq.joinpath([regal_root, "internal", "capabilities", "embed
# placed.
eopa_caps_dir := rq.joinpath([embedded_caps_dir, "eopa"])

main contains do[what] if some what in rq.args()
main contains do[what] if some what in rq.args()

main contains job[what] if some what in rq.args()

main contains job.tasks if {
count(rq.args()) == 0
print("No task(s) provided. Available tasks:")
Expand All @@ -58,7 +60,7 @@ main contains null if {
# METADATA
# title: pull_request
# description: Run all task to verify a pull request
do.pull_request {
do contains "pull_request" if {
some x in ["test", "lint", "e2e", "check_readme"]
github("::group::", x)
job[x]
Expand All @@ -68,7 +70,7 @@ do.pull_request {
# METADATA
# title: tasks
# description: Prints the name of all available tasks
job.tasks {
job contains "tasks" if {
build(false)
some task in tasks
print("-", sprintf("%-20s", [task[0]]), "\t", strings.replace_n({"\n": ""}, task[1]))
Expand All @@ -83,7 +85,7 @@ job.tasks {
# - https://github.com/mvdan/gofumpt
# - https://github.com/golangci/golangci-lint
# - https://github.com/open-policy-agent/opa
job.pr {
job contains "pr" if {
run("go mod tidy")

build(true)
Expand All @@ -102,96 +104,94 @@ job.pr {
# METADATA
# title: test
# description: Run all Regal unit tests (Go and Rego)
job.test {
job contains "test" if {
test
}

# METADATA
# title: fetch
# description: Fetch third-party artifacts, such as capabilities JSON files for engines.
job.fetch {
job contains "fetch" if {
fetch_engine_caps
}


# METADATA
# title: lint
# description: Run `regal lint` on the Regal bundle
job.lint {
job contains "lint" if {
build(true)
lint_ci
}

# METADATA
# title: e2e
# description: Run the Regal end-to-end tests
job.e2e {
job contains "e2e" if {
build(true)
e2e
}

# METADATA
# title: check_readme
# description: Verify that the rules table in the README is up-to-date
job.check_readme {
job contains "check_readme" if {
build(true)
check_readme
}

build(true) {
run("go build")
build(true) if {
run("go build -tags=regal_standalone")
}

build(false) {
build(false) if {
not binary_present
run("go build")
run("go build -tags=regal_standalone")
} else := true

# any binary is good enough when calling `build(false)`, it doesn't need to be
# built freshly
binary_present {
binary_present if {
some f in rq.tree(".", {"maxdepth": 1})
f.base == "regal"
f.is_dir == false
}

test {
test if {
run("go test ./...")
run("go run main.go test bundle")
}

e2e {
e2e if {
run("go test -tags e2e ./e2e")
run("go test -tags integration ./internal/capabilities")
}

lint {
lint if {
run("opa check --strict --capabilities build/capabilities.json bundle")
run("./regal lint --format pretty bundle")
run("markdownlint --config docs/.markdownlint.yaml --ignore docs/CODE_OF_CONDUCT.md README.md docs/")
}

lint_ci {
lint_ci if {
run("opa check --strict --capabilities build/capabilities.json bundle")
run_quiet("./regal lint --format github bundle")
run("markdownlint --config docs/.markdownlint.yaml --ignore docs/CODE_OF_CONDUCT.md README.md docs/")
run("dprint --config build/dprint.json check")
}

check_readme {
check_readme if {
run("./regal table --compare-to-readme bundle")
}

write_readme {
write_readme if {
run("./regal table --write-to-readme bundle")
}

fetch_engine_caps {
fetch_engine_caps if {
fetch_eopa_caps
}

fetch_eopa_caps {

fetch_eopa_caps if {
# git ls-remote --tags output looks like this:
#
# ...
Expand Down Expand Up @@ -222,20 +222,20 @@ fetch_eopa_caps {

print("fetching tags for enterprise-opa repository")

eopa_tags_result := rq.run([
eopa_tags_result := rq.run(
[
"git",
"ls-remote",
"--tags",
"https://github.com/styrainc/enterprise-opa"
], {
"stdout_spec": {
"format": "raw",
"options": {
"raw.fs": "/",
"raw.rs": "[\n\r]"
},
}
}
"https://github.com/styrainc/enterprise-opa",
],
{"stdout_spec": {
"format": "raw",
"options": {
"raw.fs": "/",
"raw.rs": "[\n\r]",
},
}},
)

error_nonzero(eopa_tags_result, "failed to fetch tags from GitHub")
Expand All @@ -245,9 +245,9 @@ fetch_eopa_caps {
# we eliminate them from consideration.

known_bad_tags := {
"v1.15.0", # tag missing capabilities file (misnamed v0.15.0)
"v1.4.1", # tag missing capabilities file
"v1.5.0", # tag missing capabilities file
"v1.15.0", # tag missing capabilities file (misnamed v0.15.0)
"v1.4.1", # tag missing capabilities file
"v1.5.0", # tag missing capabilities file
}

# Note that we use the `not startswith` to explicitly drop any
Expand All @@ -256,8 +256,7 @@ fetch_eopa_caps {
# file locally.

eopa_tags := {
t
|
t |
some r in eopa_tags_result.stdout
t := r[2]
not known_bad_tags[t]
Expand All @@ -268,9 +267,8 @@ fetch_eopa_caps {
# only nonzero size files with JSON extensions. The size check is to
# avoid long-tail edge cases where we crashed after opening the file
# for writing but before committing any content.
eopa_caps_tree := {
p: f
|
eopa_caps_tree := {p:
f |
f := rq.tree(eopa_caps_dir, {})[p]
f.size != 0
f.ext == "json"
Expand All @@ -279,10 +277,7 @@ fetch_eopa_caps {
# Determine which capabilities files are missing, what URL they
# should be fetched from, and where they should end up on disk.
missing_locally := {
{"local": p, "remote": r}

|

{"local": p, "remote": r} |
# construct the local path we expect the caps to exist at
t := eopa_tags[_]
p := rq.joinpath([eopa_caps_dir, sprintf("%s.json", [t])])
Expand All @@ -297,33 +292,30 @@ fetch_eopa_caps {

# Download the capabilities from the constructed URLs.
new_caps := {
{"local": m.local, "content": c}
|
{"local": m.local, "content": c} |
m := missing_locally[_]
print("\tfetching ", m.remote)
resp := http.send({"url": m.remote, "method": "GET"})

{ rq.error(sprintf("non-200 status code '%d' for URL '%s'", [resp.status_code, m.remote])) | resp.status_code != 200 }
{rq.error(sprintf("non-200 status code '%d' for URL '%s'", [resp.status_code, m.remote])) | resp.status_code != 200}

c := resp.raw_body
}

# Commit the retrieved content to disk.
{
rq.write(cap.content, {"format": "raw", "file_path": cap.local})
|
rq.write(cap.content, {"format": "raw", "file_path": cap.local}) |
some cap in new_caps
}
}


fmt_all {
fmt_all if {
gci
gofumpt
opafmt
}

gci {
gci if {
run(concat(" ", [
"gci write",
"-s standard",
Expand All @@ -336,15 +328,15 @@ gci {
]))
}

gofumpt {
gofumpt if {
run("gofumpt -w .")
}

opafmt {
opafmt if {
run("opa fmt --write bundle")
}

golangcilint {
golangcilint if {
run("golangci-lint run ./...")
}

Expand All @@ -354,15 +346,15 @@ tasks := sort([[annotation.title, annotation.description] |
annotation.scope == "rule"
])

run(cmd) {
run(cmd) if {
print(cmd)
args := split(cmd, " ")
out := rq.run(args, {})
{ rq.error(sprintf("\nstdout: %s\nstderr: %s", [out.stdout, out.stderr])) | out.exitcode != 0 }
{rq.error(sprintf("\nstdout: %s\nstderr: %s", [out.stdout, out.stderr])) | out.exitcode != 0}
print(out.stdout)
}

run_quiet(cmd) {
run_quiet(cmd) if {
print(cmd)
args := split(cmd, " ")
out := rq.run(args, {})
Expand All @@ -374,7 +366,7 @@ run_quiet(cmd) {
}
}

github(what, j) {
github(what, j) if {
is_github
print(what, j)
} else := true
Expand All @@ -384,6 +376,4 @@ is_github if rq.env().GITHUB_ACTION
error_nonzero(run_result, message) if {
run_result.exitcode != 0
rq.error(sprintf("%s\nstdout:%s\nstderr:\n%s\n", [message, run_result.stdout, run_result.stderr]))
} else {
true
}
} else = true
24 changes: 24 additions & 0 deletions e2e/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,30 @@ func TestLintNonExistentDir(t *testing.T) {
}
}

func TestLintProposeToRunFix(t *testing.T) {
t.Parallel()
stdout := bytes.Buffer{}
stderr := bytes.Buffer{}

cwd := testutil.Must(os.Getwd())(t)

// using a test rego file that only yields a few violations
err := regal(&stdout, &stderr)("lint", cwd+filepath.FromSlash("/testdata/violations/rule_named_if.rego"))

expectExitCode(t, err, 3, &stdout, &stderr)

if exp, act := "", stderr.String(); exp != act {
t.Errorf("expected stderr %q, got %q", exp, act)
}

act := strings.Split(stdout.String(), "\n")
act = act[len(act)-5:]
exp := []string{"1 file linted. 5 violations found.", "", "Hint: 2/5 violations can be automatically fixed (directory-package-mismatch, use-rego-v1)", " Run regal fix --help for more details.", ""}
if diff := cmp.Diff(act, exp); diff != "" {
t.Errorf("unexpected stdout trailer: (-want, +got):\n%s", diff)
}
}

func TestLintAllViolations(t *testing.T) {
t.Parallel()

Expand Down
8 changes: 8 additions & 0 deletions internal/mode/standalone.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//go:build !regal_standalone

package mode

// Standalone lets us change the output of some commands when Regal
// us used as a binary, as opposed to when it's embedded via its
// Go module.
const Standalone = false
5 changes: 5 additions & 0 deletions internal/mode/standalone_flag.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//go:build regal_standalone

package mode

const Standalone = true
Loading