-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add source action to explore compiler stages (#1096)
This PR integrates @srenatus excellent [opa-explorer](https://github.com/srenatus/opa-explorer/) with Regal, where we now start a web server along with the language server to host the explorer. Users will now see a "Source Action" in the context menu of any given Rego file, and when clicked will bring them to the explorer view for that file. Since a command for opening web pages client side is AFAIK only available in VS Code, this feature is sadly limited to that editor currently. Perhaps we can figure out some way to have the link show in Zed, similar to what we did for linter docs? For now, we're only loading a single file into the explorer for compilation. We could perhaps consider loading all modules in the workspace too, as otherwise compilation will always fail on e.g. unknown functions from outside the policy. Signed-off-by: Anders Eknert <anders@styra.com>
- Loading branch information
1 parent
b567b5d
commit da50d28
Showing
11 changed files
with
404 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
package explorer | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
|
||
"github.com/anderseknert/roast/pkg/encoding" | ||
|
||
"github.com/open-policy-agent/opa/ast" | ||
"github.com/open-policy-agent/opa/bundle" | ||
"github.com/open-policy-agent/opa/compile" | ||
"github.com/open-policy-agent/opa/ir" | ||
|
||
compile2 "github.com/styrainc/regal/internal/compile" | ||
) | ||
|
||
type CompileResult struct { | ||
Stage string | ||
Result *ast.Module | ||
Error string | ||
} | ||
|
||
type stage struct{ name, metricName string } | ||
|
||
// NOTE(sr): copied from 0.68.0. | ||
// | ||
//nolint:gochecknoglobals | ||
var stages []stage = []stage{ | ||
{"ResolveRefs", "compile_stage_resolve_refs"}, | ||
{"InitLocalVarGen", "compile_stage_init_local_var_gen"}, | ||
{"RewriteRuleHeadRefs", "compile_stage_rewrite_rule_head_refs"}, | ||
{"CheckKeywordOverrides", "compile_stage_check_keyword_overrides"}, | ||
{"CheckDuplicateImports", "compile_stage_check_duplicate_imports"}, | ||
{"RemoveImports", "compile_stage_remove_imports"}, | ||
{"SetModuleTree", "compile_stage_set_module_tree"}, | ||
{"SetRuleTree", "compile_stage_set_rule_tree"}, | ||
{"RewriteLocalVars", "compile_stage_rewrite_local_vars"}, | ||
{"CheckVoidCalls", "compile_stage_check_void_calls"}, | ||
{"RewritePrintCalls", "compile_stage_rewrite_print_calls"}, | ||
{"RewriteExprTerms", "compile_stage_rewrite_expr_terms"}, | ||
{"ParseMetadataBlocks", "compile_stage_parse_metadata_blocks"}, | ||
{"SetAnnotationSet", "compile_stage_set_annotationset"}, | ||
{"RewriteRegoMetadataCalls", "compile_stage_rewrite_rego_metadata_calls"}, | ||
{"SetGraph", "compile_stage_set_graph"}, | ||
{"RewriteComprehensionTerms", "compile_stage_rewrite_comprehension_terms"}, | ||
{"RewriteRefsInHead", "compile_stage_rewrite_refs_in_head"}, | ||
{"RewriteWithValues", "compile_stage_rewrite_with_values"}, | ||
{"CheckRuleConflicts", "compile_stage_check_rule_conflicts"}, | ||
{"CheckUndefinedFuncs", "compile_stage_check_undefined_funcs"}, | ||
{"CheckSafetyRuleHeads", "compile_stage_check_safety_rule_heads"}, | ||
{"CheckSafetyRuleBodies", "compile_stage_check_safety_rule_bodies"}, | ||
{"RewriteEquals", "compile_stage_rewrite_equals"}, | ||
{"RewriteDynamicTerms", "compile_stage_rewrite_dynamic_terms"}, | ||
{"RewriteTestRulesForTracing", "compile_stage_rewrite_test_rules_for_tracing"}, // must run after RewriteDynamicTerms | ||
{"CheckRecursion", "compile_stage_check_recursion"}, | ||
{"CheckTypes", "compile_stage_check_types"}, | ||
{"CheckUnsafeBuiltins", "compile_state_check_unsafe_builtins"}, | ||
{"CheckDeprecatedBuiltins", "compile_state_check_deprecated_builtins"}, | ||
{"BuildRuleIndices", "compile_stage_rebuild_indices"}, | ||
{"BuildComprehensionIndices", "compile_stage_rebuild_comprehension_indices"}, | ||
{"BuildRequiredCapabilities", "compile_stage_build_required_capabilities"}, | ||
} | ||
|
||
func CompilerStages(path, rego string, useStrict, useAnno, usePrint bool) []CompileResult { | ||
c := compile2.NewCompilerWithRegalBuiltins(). | ||
WithStrict(useStrict). | ||
WithEnablePrintStatements(usePrint). | ||
WithUseTypeCheckAnnotations(useAnno) | ||
|
||
result := make([]CompileResult, 0, len(stages)+1) | ||
result = append(result, CompileResult{ | ||
Stage: "ParseModule", | ||
}) | ||
|
||
mod, err := ast.ParseModuleWithOpts(path, rego, ast.ParserOptions{ProcessAnnotation: useAnno}) | ||
if err != nil { | ||
result[0].Error = err.Error() | ||
|
||
return result | ||
} | ||
|
||
result[0].Result = mod | ||
|
||
for i := range stages { | ||
stage := stages[i] | ||
c = c.WithStageAfter(stage.name, | ||
ast.CompilerStageDefinition{ | ||
Name: stage.name + "Record", | ||
MetricName: stage.metricName + "_record", | ||
Stage: func(c0 *ast.Compiler) *ast.Error { | ||
result = append(result, CompileResult{ | ||
Stage: stage.name, | ||
Result: getOne(c0.Modules), | ||
}) | ||
|
||
return nil | ||
}, | ||
}) | ||
} | ||
|
||
c.Compile(map[string]*ast.Module{ | ||
path: mod, | ||
}) | ||
|
||
if len(c.Errors) > 0 { | ||
// stage after the last than ran successfully | ||
stage := stages[len(result)-1] | ||
result = append(result, CompileResult{ | ||
Stage: stage.name + ": Failure", | ||
Error: c.Errors.Error(), | ||
}) | ||
} | ||
|
||
return result | ||
} | ||
|
||
func getOne(mods map[string]*ast.Module) *ast.Module { | ||
for _, m := range mods { | ||
return m.Copy() | ||
} | ||
|
||
panic("unreachable") | ||
} | ||
|
||
func Plan(ctx context.Context, path, rego string, usePrint bool) (string, error) { | ||
mod, err := ast.ParseModuleWithOpts(path, rego, ast.ParserOptions{ProcessAnnotation: true}) | ||
if err != nil { | ||
return "", err //nolint:wrapcheck | ||
} | ||
|
||
b := &bundle.Bundle{ | ||
Modules: []bundle.ModuleFile{ | ||
{ | ||
URL: "/url", | ||
Path: path, | ||
Raw: []byte(rego), | ||
Parsed: mod, | ||
}, | ||
}, | ||
} | ||
|
||
compiler := compile.New(). | ||
WithTarget(compile.TargetPlan). | ||
WithBundle(b). | ||
WithRegoAnnotationEntrypoints(true). | ||
WithEnablePrintStatements(usePrint) | ||
if err := compiler.Build(ctx); err != nil { | ||
return "", err //nolint:wrapcheck | ||
} | ||
|
||
var policy ir.Policy | ||
|
||
if err := encoding.JSON().Unmarshal(compiler.Bundle().PlanModules[0].Raw, &policy); err != nil { | ||
return "", err //nolint:wrapcheck | ||
} | ||
|
||
buf := bytes.Buffer{} | ||
if err := ir.Pretty(&buf, &policy); err != nil { | ||
return "", err //nolint:wrapcheck | ||
} | ||
|
||
return buf.String(), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<meta charset="utf-8"/> | ||
<link rel="stylesheet" href="/assets/missing.min.css"> | ||
<script src="/assets/htmx-1.8.4.min.js"></script> | ||
</head> | ||
<body> | ||
<main class="crowded"> | ||
<form> | ||
<div class="f-row"> | ||
<textarea name="code" | ||
style="height: 300px" | ||
hx-post="/?tmpl=output" | ||
hx-target="#output" | ||
hx-trigger="keyup changed delay:200ms" | ||
class="flex-grow:1 monospace" | ||
>{{ .Code }}</textarea> | ||
</div> | ||
</form> | ||
<section id="output"> | ||
{{ block "output" . }} | ||
{{ range .Result }} | ||
<details class={{ .Class }} {{ if .Show }}open{{ end }}> | ||
<summary>{{ .Stage }}</summary> | ||
<pre><code>{{ .Output }}</code></pre> | ||
</details> | ||
{{ end }} | ||
{{ end }} | ||
</section> | ||
</main> | ||
</body> | ||
</html> |
Large diffs are not rendered by default.
Oops, something went wrong.
Oops, something went wrong.