Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package regal for openSUSE:Factory checked in at 2026-01-14 16:23:13 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/regal (Old) and /work/SRC/openSUSE:Factory/.regal.new.1928 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "regal" Wed Jan 14 16:23:13 2026 rev:9 rq:1327144 version:0.38.1 Changes: -------- --- /work/SRC/openSUSE:Factory/regal/regal.changes 2026-01-12 10:32:58.958291293 +0100 +++ /work/SRC/openSUSE:Factory/.regal.new.1928/regal.changes 2026-01-14 16:23:42.082467774 +0100 @@ -1,0 +2,45 @@ +Wed Jan 14 07:28:39 UTC 2026 - Johannes Kastl <[email protected]> + +- Update to version 0.38.1: + This patch release fixes several bugs including some found in the + recent v0.38.0 release, as well some other improvements. + * Bug Fixes + - Fix for prefer-equals-comparison fixer failing to parse + policies with multiple "=" in expressions (#1824, fixes #1818 + reported by @gusega) + - Fix for incorrect fixable violation count display in lint + output (#1825, fixes #1813 reported by @gusega) + - Fix for false positive in prefer-equals-comparison rule with + comprehension term vars (#1828, fixes #1826 reported by + @SeanLedford) + - bundle: Surface configured rule notices as messages in lint + output (#1827, fixes #1795 reported by @ghmer) + * Improvements + - Code action for prefer-equals-comparison fixer now available + in the language server (#1810) + - New option for prefer-value-in-head rule to count + interpolated strings as scalars (#1817) + - Minor performance improvement for any_set_item, used for + selecting items from sets (#1815) + * Changelog + - f1c5f74: bundle: Improve performance of any_set_item (#1815) + (@charlieegan3) + - 9300420: Implemented Code action for prefer-equals-comparison + fixer (#1810) (@SeanLedford) + - 28121f0: build(deps): bump github/codeql-action in the + dependencies group (#1823) (@dependabot[bot]) + - 54cdcb6: Add prefer-value-in-head option to count + interpolated string as scalar (#1817) (@anderseknert) + - e68184d: Fix false positivie in prefer-equals-comparison + (#1828) (@anderseknert) + - 593e221: fix: Prefer-Equals-Comparison Edge Case With + Multiple "=" (#1824) (@SeanLedford) + - 3dc28bb: bundle: Surface configured rule notices (#1827) + (@charlieegan3) + - aac9618: Updated lint reporter to track fixable violation + count separate from fixable violation Set (#1825) + (@SeanLedford) + - fd6dd4d: Updated example for prefer-equals-comparison edge + case to not include false positive (#1829) (@SeanLedford) + +------------------------------------------------------------------- Old: ---- regal-0.38.0.obscpio New: ---- regal-0.38.1.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ regal.spec ++++++ --- /var/tmp/diff_new_pack.xxQNB4/_old 2026-01-14 16:23:43.114510633 +0100 +++ /var/tmp/diff_new_pack.xxQNB4/_new 2026-01-14 16:23:43.114510633 +0100 @@ -17,7 +17,7 @@ Name: regal -Version: 0.38.0 +Version: 0.38.1 Release: 0 Summary: Linter and language server for Rego License: Apache-2.0 ++++++ _service ++++++ --- /var/tmp/diff_new_pack.xxQNB4/_old 2026-01-14 16:23:43.146511962 +0100 +++ /var/tmp/diff_new_pack.xxQNB4/_new 2026-01-14 16:23:43.150512128 +0100 @@ -3,8 +3,8 @@ <param name="url">https://github.com/open-policy-agent/regal</param> <param name="scm">git</param> <param name="exclude">.git</param> - <param name="revision">v0.38.0</param> - <param name="match-tag">v0.38.0</param> + <param name="revision">v0.38.1</param> + <param name="match-tag">v*.*.*</param> <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v(.*)</param> <param name="changesgenerate">enable</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.xxQNB4/_old 2026-01-14 16:23:43.182513457 +0100 +++ /var/tmp/diff_new_pack.xxQNB4/_new 2026-01-14 16:23:43.186513622 +0100 @@ -3,6 +3,6 @@ <param name="url">https://github.com/StyraInc/regal</param> <param name="changesrevision">92d1ba9bffa4eae59ae848714097e4125c82ff93</param></service><service name="tar_scm"> <param name="url">https://github.com/open-policy-agent/regal</param> - <param name="changesrevision">a9843ab9bb96a8f2852e6d328f5a9a6ff8b4e333</param></service></servicedata> + <param name="changesrevision">fd6dd4d160ef7eafee9d5d606f2e8536ce889bb0</param></service></servicedata> (No newline at EOF) ++++++ regal-0.38.0.obscpio -> regal-0.38.1.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regal-0.38.0/.regal/config.yaml new/regal-0.38.1/.regal/config.yaml --- old/regal-0.38.0/.regal/config.yaml 2026-01-08 09:14:06.000000000 +0100 +++ new/regal-0.38.1/.regal/config.yaml 2026-01-13 16:27:23.000000000 +0100 @@ -27,6 +27,7 @@ prefer-value-in-head: level: error only-scalars: true + include-interpolated: true style: line-length: level: error diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regal-0.38.0/bundle/regal/config/config.rego new/regal-0.38.1/bundle/regal/config/config.rego --- old/regal-0.38.0/bundle/regal/config/config.rego 2026-01-08 09:14:06.000000000 +0100 +++ new/regal-0.38.1/bundle/regal/config/config.rego 2026-01-13 16:27:23.000000000 +0100 @@ -28,6 +28,10 @@ # description: the default configuration with user config merged on top (if provided) merged_config := data.internal.combined_config +# METADATA +# description: the config the user manually configured without defaults +user_config := data.internal.user_config + # ensure that config.rules can be referenced in tests even without mocking default rules := {} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regal-0.38.0/bundle/regal/lsp/codeaction/codeaction.rego new/regal-0.38.1/bundle/regal/lsp/codeaction/codeaction.rego --- old/regal-0.38.0/bundle/regal/lsp/codeaction/codeaction.rego 2026-01-08 09:14:06.000000000 +0100 +++ new/regal-0.38.1/bundle/regal/lsp/codeaction/codeaction.rego 2026-01-13 16:27:23.000000000 +0100 @@ -129,6 +129,7 @@ "Move file so that directory structure mirrors package path", ["target", "diagnostic"], ], + "prefer-equals-comparison": ["Replace = with == in comparison", ["target", "diagnostic"]], } # METADATA diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regal-0.38.0/bundle/regal/main/main.rego new/regal-0.38.1/bundle/regal/main/main.rego --- old/regal-0.38.0/bundle/regal/main/main.rego 2026-01-08 09:14:06.000000000 +0100 +++ new/regal-0.38.1/bundle/regal/main/main.rego 2026-01-13 16:27:23.000000000 +0100 @@ -10,11 +10,20 @@ import data.regal.ast import data.regal.config +import data.regal.notices import data.regal.util # METADATA # description: set of all notices returned from linter rules -lint.notices contains _grouped_notices[_][_][_] if "lint" in input.regal.operations +lint.notices contains notice if { + "lint" in input.regal.operations + + some category, title + _rules_to_run[category][title] + + rule_notices := notices.promoted_notices[category][title] + some notice in rule_notices +} # METADATA # description: map of all ignore directives encountered when linting @@ -57,13 +66,6 @@ not config.excluded_file(category, title, relative_filename) } -_grouped_notices[category][title] contains notice if { - some category, title - _rules_to_run[category][title] - - some notice in data.regal.rules[category][title].notices -} - # METADATA # title: report # description: | @@ -94,7 +96,7 @@ some category, title _rules_to_run[category][title] - count(object.get(_grouped_notices, [category, title], [])) == 0 + count(object.get(notices.promoted_notices, [category, title], [])) == 0 some violation in data.regal.rules[category][title].report diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regal-0.38.0/bundle/regal/main/main_test.rego new/regal-0.38.1/bundle/regal/main/main_test.rego --- old/regal-0.38.0/bundle/regal/main/main_test.rego 2026-01-08 09:14:06.000000000 +0100 +++ new/regal-0.38.1/bundle/regal/main/main_test.rego 2026-01-13 16:27:23.000000000 +0100 @@ -358,22 +358,6 @@ rules_to_run == {"testing": {"test"}} } -test_notices if { - notice := { - "category": "idiomatic", - "description": "here's a notice", - "level": "notice", - "title": "testme notice", - "severity": "none", - } - - notices := main.lint.notices with main._rules_to_run as {"idiomatic": {"testme"}} - with data.regal.rules.idiomatic.testme.notices as {notice} # regal ignore:unresolved-reference - with input.regal.operations as ["lint"] - - notices == {notice} -} - test_main_fail_when_input_not_object if { violation := { "category": "error", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regal-0.38.0/bundle/regal/notices/notices.rego new/regal-0.38.1/bundle/regal/notices/notices.rego --- old/regal-0.38.0/bundle/regal/notices/notices.rego 1970-01-01 01:00:00.000000000 +0100 +++ new/regal-0.38.1/bundle/regal/notices/notices.rego 2026-01-13 16:27:23.000000000 +0100 @@ -0,0 +1,46 @@ +# METADATA +# description: | +# package with functionality for post-processing notices +# to ensure they are presented correctly as errors when relevant. +package regal.notices + +import data.regal.config + +# METADATA +# scope: rule +# description: | +# promoted_notices maps notices from rules, potentially changing their severity +# based on user configuration +promoted_notices[category][title] contains original_notice if { + some category, title + notices := data.regal.rules[category][title].notices + + some original_notice in notices + + not config.user_config.rules[category][title] +} + +promoted_notices[category][title] contains notice if { + some category, title + notices := data.regal.rules[category][title].notices + + some notice in notices + + rule_config := config.user_config.rules[category][title] + object.get(rule_config, "level", "") == "ignore" +} + +promoted_notices[category][title] contains notice if { + some category, title + notices := data.regal.rules[category][title].notices + + some original_notice in notices + + rule_config := config.user_config.rules[category][title] + object.get(rule_config, "level", "") != "ignore" + + # Use configured level as severity, or default to "error" + new_severity := object.get(rule_config, "level", "error") + + notice := object.union(original_notice, {"severity": new_severity}) +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regal-0.38.0/bundle/regal/notices/notices_test.rego new/regal-0.38.1/bundle/regal/notices/notices_test.rego --- old/regal-0.38.0/bundle/regal/notices/notices_test.rego 1970-01-01 01:00:00.000000000 +0100 +++ new/regal-0.38.1/bundle/regal/notices/notices_test.rego 2026-01-13 16:27:23.000000000 +0100 @@ -0,0 +1,72 @@ +package regal.notices_test + +import data.regal.notices + +test_promoted_notices_with_level_error if { + result := notices.promoted_notices with data.regal.rules.imports["use-rego-v1"].notices as {_example_notice} + with data.internal.user_config as {"rules": {"imports": {"use-rego-v1": {"level": "error"}}}} + + # With user config level set to error, the severity should be promoted to error + result.imports["use-rego-v1"] == {object.union(_example_notice, {"severity": "error"})} +} + +test_promoted_notices_with_level_ignore if { + result := notices.promoted_notices with data.regal.rules.imports["use-rego-v1"].notices as {_example_notice} + with data.internal.user_config as {"rules": {"imports": {"use-rego-v1": {"level": "ignore"}}}} + + # With user config level set to ignore, the severity should stay none + result.imports["use-rego-v1"] == {_example_notice} +} + +test_promoted_notices_with_level_warning if { + result := notices.promoted_notices with data.regal.rules.imports["use-rego-v1"].notices as {_example_notice} + with data.internal.user_config as {"rules": {"imports": {"use-rego-v1": {"level": "warning"}}}} + + # With user config level set to warning, the severity should be promoted to warning + result.imports["use-rego-v1"] == {object.union(_example_notice, {"severity": "warning"})} +} + +test_promoted_notices_configured_without_level if { + # Rule is configured but level field is not present + result := notices.promoted_notices with data.regal.rules.imports["use-rego-v1"].notices as {_example_notice} + with data.internal.user_config as {"rules": {"imports": {"use-rego-v1": {}}}} + + # When configured without level, should default to error + result.imports["use-rego-v1"] == {object.union(_example_notice, {"severity": "error"})} +} + +test_promoted_notices_mixed_configured_and_unconfigured if { + notice_configured_rule := { + "category": "imports", + "description": "Configured rule", + "level": "notice", + "title": "use-rego-v1", + "severity": "none", + } + + notice_unconfigured_rule := { + "category": "bugs", + "description": "Unconfigured rule", + "level": "notice", + "title": "deprecated-builtin", + "severity": "none", + } + + result := notices.promoted_notices with data.regal.rules.imports["use-rego-v1"].notices as {notice_configured_rule} + with data.regal.rules.bugs["deprecated-builtin"].notices as {notice_unconfigured_rule} + with data.internal.user_config as {"rules": {"imports": {"use-rego-v1": {"level": "error"}}}} + + # Configured rule should have severity promoted to error + result.imports["use-rego-v1"] == {object.union(notice_configured_rule, {"severity": "error"})} + + # Unconfigured rule should keep original severity: none + result.bugs["deprecated-builtin"] == {notice_unconfigured_rule} +} + +_example_notice := { + "category": "imports", + "description": "Test rule description", + "level": "notice", + "title": "use-rego-v1", + "severity": "none", +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regal-0.38.0/bundle/regal/rules/custom/prefer-value-in-head/prefer_value_in_head.rego new/regal-0.38.1/bundle/regal/rules/custom/prefer-value-in-head/prefer_value_in_head.rego --- old/regal-0.38.0/bundle/regal/rules/custom/prefer-value-in-head/prefer_value_in_head.rego 2026-01-08 09:14:06.000000000 +0100 +++ new/regal-0.38.1/bundle/regal/rules/custom/prefer-value-in-head/prefer_value_in_head.rego 2026-01-13 16:27:23.000000000 +0100 @@ -17,7 +17,7 @@ terms[1].type == "var" terms[1].value == var - not _scalar_fail(terms[2].type, ast.scalar_types) + not _scalar_fail(terms[2].type, _scalar_types) not _excepted_var_name(var) violation := result.fail(rego.metadata.chain(), result.location(terms[2])) @@ -36,3 +36,6 @@ } _excepted_var_name(name) if name in config.rules.custom["prefer-value-in-head"]["except-var-names"] + +_scalar_types contains type if some type in ast.scalar_types +_scalar_types contains "templatestring" if config.rules.custom["prefer-value-in-head"]["include-interpolated"] == true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regal-0.38.0/bundle/regal/rules/custom/prefer-value-in-head/prefer_value_in_head_test.rego new/regal-0.38.1/bundle/regal/rules/custom/prefer-value-in-head/prefer_value_in_head_test.rego --- old/regal-0.38.0/bundle/regal/rules/custom/prefer-value-in-head/prefer_value_in_head_test.rego 2026-01-08 09:14:06.000000000 +0100 +++ new/regal-0.38.1/bundle/regal/rules/custom/prefer-value-in-head/prefer_value_in_head_test.rego 2026-01-13 16:27:23.000000000 +0100 @@ -80,6 +80,51 @@ r == set() } +test_fail_value_could_be_in_head_templatestring if { + r := rule.report with input as ast.policy(`value := x if { + input.x + x := $"{input.y}" + }`) + + r == expected_with_location({ + "col": 8, + "row": 5, + "end": { + "col": 20, + "row": 5, + }, + "text": "\t\tx := $\"{input.y}\"", + }) +} + +test_success_only_scalar_no_include_interpolated if { + r := rule.report with input as ast.policy(`value := x if { + input.x + x := $"{input.y}" + }`) + with config.rules as {"custom": {"prefer-value-in-head": {"only-scalars": true}}} + + r == set() +} + +test_fail_value_could_be_in_head_only_scalars_with_include_interpolated if { + r := rule.report with input as ast.policy(`value := x if { + input.x + x := $"{input.y}" + }`) + with config.rules as {"custom": {"prefer-value-in-head": {"only-scalars": true, "include-interpolated": true}}} + + r == expected_with_location({ + "col": 8, + "row": 5, + "end": { + "col": 20, + "row": 5, + }, + "text": "\t\tx := $\"{input.y}\"", + }) +} + test_fail_value_could_be_in_head_and_is_a_scalar if { module := ast.policy(`value := x if { input.x diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regal-0.38.0/bundle/regal/rules/idiomatic/prefer-equals-comparison/prefer_equals_comparison.rego new/regal-0.38.1/bundle/regal/rules/idiomatic/prefer-equals-comparison/prefer_equals_comparison.rego --- old/regal-0.38.0/bundle/regal/rules/idiomatic/prefer-equals-comparison/prefer_equals_comparison.rego 2026-01-08 09:14:06.000000000 +0100 +++ new/regal-0.38.1/bundle/regal/rules/idiomatic/prefer-equals-comparison/prefer_equals_comparison.rego 2026-01-13 16:27:23.000000000 +0100 @@ -4,6 +4,7 @@ import data.regal.ast import data.regal.result +import data.regal.util report contains violation if { some rule_index, expr @@ -26,5 +27,33 @@ _unassignable(term, rule_index) if { term.type == "var" - not ast.is_output_var(input.rules[to_number(rule_index)], term) + ri := to_number(rule_index) + not ast.is_output_var(input.rules[ri], term) + not _is_declared_comp_term(term, rule_index) +} + +_is_declared_comp_term(term, rule_index) if { + some comp_term in _comprehension_term_vars_in_scope(rule_index, term.location) + comp_term.type == "var" + comp_term.value == term.value +} + +_comprehension_term_vars_in_scope(rule_index, location) := {node | + loc := util.to_location_object(location) + + some comp in ast.found.comprehensions[rule_index] + util.contains_location(util.to_location_object(comp.location), loc) + + fields := { + "arraycomprehension": ["term"], + "objectcomprehension": ["key", "value"], + "setcomprehension": ["term"], + }[comp.type] + + some field in fields + term := comp.value[field] + + walk(term, [_, node]) + + node.type == "var" } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regal-0.38.0/bundle/regal/rules/idiomatic/prefer-equals-comparison/prefer_equals_comparison_test.rego new/regal-0.38.1/bundle/regal/rules/idiomatic/prefer-equals-comparison/prefer_equals_comparison_test.rego --- old/regal-0.38.0/bundle/regal/rules/idiomatic/prefer-equals-comparison/prefer_equals_comparison_test.rego 2026-01-08 09:14:06.000000000 +0100 +++ new/regal-0.38.1/bundle/regal/rules/idiomatic/prefer-equals-comparison/prefer_equals_comparison_test.rego 2026-01-13 16:27:23.000000000 +0100 @@ -117,3 +117,39 @@ r == set() } + +test_success_not_impossible_assignment_set_comprehension_term if { + r := rule.report with input as ast.policy(`r := {x | x = y[_].z}`) + + r == set() +} + +test_success_not_impossible_assignment_set_comprehension_term_complex if { + r := rule.report with input as ast.policy(`r := {{x} | x = y[_].z}`) + + r == set() +} + +test_success_not_impossible_assignment_array_comprehension_term if { + r := rule.report with input as ast.policy(`r := [x | x = y[_].z]`) + + r == set() +} + +test_success_not_impossible_assignment_array_comprehension_term_complex if { + r := rule.report with input as ast.policy(`r := [[x] | x = y[_].z]`) + + r == set() +} + +test_success_not_impossible_assignment_object_comprehension_term if { + r := rule.report with input as ast.policy(`r := {k: v | k = y[v].k}`) + + r == set() +} + +test_success_not_impossible_assignment_object_comprehension_term_complex if { + r := rule.report with input as ast.policy(`r := {{k}: {v} | k = y[v].k}`) + + r == set() +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regal-0.38.0/bundle/regal/rules/imports/unresolved-reference/unresolved_reference.rego new/regal-0.38.1/bundle/regal/rules/imports/unresolved-reference/unresolved_reference.rego --- old/regal-0.38.0/bundle/regal/rules/imports/unresolved-reference/unresolved_reference.rego 2026-01-08 09:14:06.000000000 +0100 +++ new/regal-0.38.1/bundle/regal/rules/imports/unresolved-reference/unresolved_reference.rego 2026-01-13 16:27:23.000000000 +0100 @@ -143,5 +143,5 @@ from_col := substring(text, col - 1, -1) ref_text := substring(from_col, 0, indexof(from_col, " ")) - end_col := to_number(vals[1]) + count(ref_text) + end_col := col + count(ref_text) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regal-0.38.0/bundle/regal/util/util.rego new/regal-0.38.1/bundle/regal/util/util.rego --- old/regal-0.38.0/bundle/regal/util/util.rego 2026-01-08 09:14:06.000000000 +0100 +++ new/regal-0.38.1/bundle/regal/util/util.rego 2026-01-13 16:27:23.000000000 +0100 @@ -169,9 +169,14 @@ some item in s } +# @anderseknert looked into the different implementations and sort was still the fastest +# | Implementation | ns/op | B/op | allocs/op | +# | [x | some x in s][0] | 12,496 | 9,103 | 184 | +# | max(s) | 9,671 | 8,049 | 150 | +# | sort(s)[0] | 9,184 | 7,816 | 133 | # METADATA # description: returns any item of a set -any_set_item(s) := [x | some x in s][0] # this is convoluted.. but can't think of a better way +any_set_item(s) := sort(s)[0] # METADATA # description: returns last index of item, or undefined (*not* -1) if missing @@ -223,3 +228,23 @@ # METADATA # description: creates a string where s is repeated n times repeat(s, n) := replace(sprintf("%-*s", [n, " "]), " ", s) + +# METADATA +# description: true if location 'sub' is fully contained within location 'sup' +contains_location(sup, sub) if { + sup.row < sub.row + sup.end.row > sub.end.row +} else if { + sup.row == sub.row + sup.col <= sub.col + sup.end.row > sub.end.row +} else if { + sup.row < sub.row + sup.end.row == sub.end.row + sup.end.col >= sub.end.col +} else if { + sup.row == sub.row + sup.col <= sub.col + sup.end.row == sub.end.row + sup.end.col >= sub.end.col +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regal-0.38.0/docs/rules/custom/prefer-value-in-head.md new/regal-0.38.1/docs/rules/custom/prefer-value-in-head.md --- old/regal-0.38.0/docs/rules/custom/prefer-value-in-head.md 2026-01-08 09:14:06.000000000 +0100 +++ new/regal-0.38.1/docs/rules/custom/prefer-value-in-head.md 2026-01-13 16:27:23.000000000 +0100 @@ -5,6 +5,7 @@ **Category**: Custom **Avoid** + ```rego package policy @@ -20,6 +21,7 @@ ``` **Prefer** + ```rego package policy @@ -58,6 +60,9 @@ } ``` +The `include-interpolated` configuration option may be used to count interpolated strings as a scalar (string) values, +which will have Regal recommend moving them to the head even when `only-scalars` is set to `true`. + ## Configuration Options This linter rule provides the following configuration options: @@ -74,10 +79,13 @@ # whether to only suggest moving scalar values (strings, numbers, booleans, null) # to the head, and not expressions or functions only-scalars: false + # when set to true, counts interpolated strings as a scalar value, and will suggest + # moving them to the head even when `only-scalars` is true + include-interpolated: false # variable names to exempt from the rule (by default, none) except-var-names: - - report - - violation + - report + - violation ``` ## Related Resources diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regal-0.38.0/internal/lsp/eval.go new/regal-0.38.1/internal/lsp/eval.go --- old/regal-0.38.0/internal/lsp/eval.go 2026-01-08 09:14:06.000000000 +0100 +++ new/regal-0.38.1/internal/lsp/eval.go 2026-01-13 16:27:23.000000000 +0100 @@ -119,6 +119,11 @@ evalConfig = *cfg } + userConfigMap := map[string]any{} + if cfg != nil { + userConfigMap = config.ToMap(*cfg) + } + internalBundle := &bundle.Bundle{ Manifest: bundle.Manifest{ Roots: &[]string{"internal"}, @@ -127,6 +132,7 @@ Data: map[string]any{ "internal": map[string]any{ "combined_config": config.ToMap(evalConfig), + "user_config": userConfigMap, "capabilities": caps, }, }, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regal-0.38.0/internal/lsp/eval_test.go new/regal-0.38.1/internal/lsp/eval_test.go --- old/regal-0.38.0/internal/lsp/eval_test.go 2026-01-08 09:14:06.000000000 +0100 +++ new/regal-0.38.1/internal/lsp/eval_test.go 2026-01-13 16:27:23.000000000 +0100 @@ -76,7 +76,7 @@ val := testutil.MustBe[[]any](t, res.Value) act := util.Sorted(testutil.Must(util.AnySliceTo[string](val))(t)) - if exp := []string{"capabilities", "combined_config"}; !slices.Equal(exp, act) { + if exp := []string{"capabilities", "combined_config", "user_config"}; !slices.Equal(exp, act) { t.Fatalf("expected %v, got %v", exp, act) } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regal-0.38.0/internal/lsp/server.go new/regal-0.38.1/internal/lsp/server.go --- old/regal-0.38.0/internal/lsp/server.go 2026-01-08 09:14:06.000000000 +0100 +++ new/regal-0.38.1/internal/lsp/server.go 2026-01-13 16:27:23.000000000 +0100 @@ -83,11 +83,12 @@ regalEvalUseAsInputComment = regexp.MustCompile(`^\s*regal eval:\s*use-as-input`) validPathComponentPattern = regexp.MustCompile(`^\w+[\w\-]*\w+$`) - fixFmt = &fixes.Fmt{OPAFmtOpts: format.Opts{}} - fixUseRegoV1 = &fixes.Fmt{OPAFmtOpts: format.Opts{RegoVersion: ast.RegoV0CompatV1}} - fixUseAssignmentOperator = &fixes.UseAssignmentOperator{} - fixNoWhitespaceComment = &fixes.NoWhitespaceComment{} - fixNonRawRegexPattern = &fixes.NonRawRegexPattern{} + fixFmt = &fixes.Fmt{OPAFmtOpts: format.Opts{}} + fixUseRegoV1 = &fixes.Fmt{OPAFmtOpts: format.Opts{RegoVersion: ast.RegoV0CompatV1}} + fixUseAssignmentOperator = &fixes.UseAssignmentOperator{} + fixNoWhitespaceComment = &fixes.NoWhitespaceComment{} + fixNonRawRegexPattern = &fixes.NonRawRegexPattern{} + fixPreferEqualsComparison = &fixes.PreferEqualsComparison{} ) type LanguageServerOptions struct { @@ -598,6 +599,8 @@ fixed, editParams, err = l.fixEditParams("Format comment to have leading whitespace", fixNoWhitespaceComment, args) case "regal.fix.non-raw-regex-pattern": fixed, editParams, err = l.fixEditParams("Replace \" with ` in regex pattern", fixNonRawRegexPattern, args) + case "regal.fix.prefer-equals-comparison": + fixed, editParams, err = l.fixEditParams("Replace = with == in comparison", fixPreferEqualsComparison, args) case "regal.fix.directory-package-mismatch": params, err := l.fixRenameParams("Rename file to match package path", args.Target) if err != nil { @@ -1890,6 +1893,7 @@ "regal.fix.no-whitespace-comment", "regal.fix.directory-package-mismatch", "regal.fix.non-raw-regex-pattern", + "regal.fix.prefer-equals-comparison", "regal.config.disable-rule", }, }, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regal-0.38.0/pkg/fixer/fixes/preferequalscomparison.go new/regal-0.38.1/pkg/fixer/fixes/preferequalscomparison.go --- old/regal-0.38.0/pkg/fixer/fixes/preferequalscomparison.go 2026-01-08 09:14:06.000000000 +0100 +++ new/regal-0.38.1/pkg/fixer/fixes/preferequalscomparison.go 2026-01-13 16:27:23.000000000 +0100 @@ -25,13 +25,17 @@ continue } - eqIndex := strings.Index(line, "=") + targetedLocation := line[loc.Column-1:] + + targetedEqIndex := strings.Index(targetedLocation, "=") // unification operator not found, skipping - if eqIndex == -1 { + if targetedEqIndex == -1 { continue } + eqIndex := targetedEqIndex + loc.Column - 1 + lines[loc.Row-1] = line[0:eqIndex] + "=" + line[eqIndex:] fixed = true } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regal-0.38.0/pkg/fixer/fixes/preferequalscomparison_test.go new/regal-0.38.1/pkg/fixer/fixes/preferequalscomparison_test.go --- old/regal-0.38.0/pkg/fixer/fixes/preferequalscomparison_test.go 2026-01-08 09:14:06.000000000 +0100 +++ new/regal-0.38.1/pkg/fixer/fixes/preferequalscomparison_test.go 2026-01-13 16:27:23.000000000 +0100 @@ -124,6 +124,21 @@ fixExpected: true, runtimeOptions: &RuntimeOptions{Locations: []report.Location{{Row: 5, Column: 2}, {Row: 6, Column: 2}}}, }, + "multiple = signs": { + fc: &FixCandidate{ + Filename: "test.rego", + Contents: `package test + +is_get := true if input.request.method = "GET" +`, + }, + contentAfterFix: `package test + +is_get := true if input.request.method == "GET" +`, + fixExpected: true, + runtimeOptions: &RuntimeOptions{Locations: []report.Location{{Row: 3, Column: 40}}}, + }, } for testName, tc := range testCases { t.Run(testName, func(t *testing.T) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regal-0.38.0/pkg/linter/linter.go new/regal-0.38.1/pkg/linter/linter.go --- old/regal-0.38.0/pkg/linter/linter.go 2026-01-08 09:14:06.000000000 +0100 +++ new/regal-0.38.1/pkg/linter/linter.go 2026-01-13 16:27:23.000000000 +0100 @@ -648,6 +648,11 @@ "ignore_files": util.NilSliceToEmpty(l.ignoreFiles), } + userConfigMap := map[string]any{} + if l.userConfig != nil { + userConfigMap = config.ToMap(*l.userConfig) + } + return &bundle.Bundle{ Manifest: bundle.Manifest{ Roots: &[]string{"internal", "eval"}, @@ -659,6 +664,7 @@ }, "internal": map[string]any{ "combined_config": config.ToMap(conf), + "user_config": userConfigMap, "capabilities": rio.ToMap(config.CapabilitiesForThisVersion()), "path_prefix": l.pathPrefix, }, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regal-0.38.0/pkg/reporter/reporter.go new/regal-0.38.1/pkg/reporter/reporter.go --- old/regal-0.38.0/pkg/reporter/reporter.go 2026-01-08 09:14:06.000000000 +0100 +++ new/regal-0.38.1/pkg/reporter/reporter.go 2026-01-13 16:27:23.000000000 +0100 @@ -103,7 +103,6 @@ // Publish prints a pretty report to the configured output. func (tr PrettyReporter) Publish(_ context.Context, r report.Report) error { table := buildPrettyViolationsTable(r.Violations) - numsWarning, numsError := 0, 0 for i := range r.Violations { @@ -162,10 +161,13 @@ f := fixer.NewFixer().RegisterFixes(fixes.NewDefaultFixes()...) fixableViolations := util.NewSet[string]() + fixableCount := 0 for i := range r.Violations { if fix, ok := f.GetFixForName(r.Violations[i].Title); ok { fixableViolations.Add(fix.Name()) + + fixableCount++ } } @@ -177,7 +179,7 @@ Hint: %d/%d violations can be automatically fixed (%s) Run regal fix --help for more details. `, - fixableViolations.Size(), + fixableCount, r.Summary.NumViolations, strings.Join(violationKeys, ", "), ) ++++++ regal.obsinfo ++++++ --- /var/tmp/diff_new_pack.xxQNB4/_old 2026-01-14 16:23:44.378563126 +0100 +++ /var/tmp/diff_new_pack.xxQNB4/_new 2026-01-14 16:23:44.382563292 +0100 @@ -1,5 +1,5 @@ name: regal -version: 0.38.0 -mtime: 1767860046 -commit: a9843ab9bb96a8f2852e6d328f5a9a6ff8b4e333 +version: 0.38.1 +mtime: 1768318043 +commit: fd6dd4d160ef7eafee9d5d606f2e8536ce889bb0 ++++++ vendor.tar.gz ++++++ /work/SRC/openSUSE:Factory/regal/vendor.tar.gz /work/SRC/openSUSE:Factory/.regal.new.1928/vendor.tar.gz differ: char 13, line 1
