Skip to content

feat: Regex filters for P2P preheat (phase 3) - #754

Open
Vad1mo wants to merge 1 commit into
feat/regex-filters-phase2from
feat/regex-preheat-phase3
Open

feat: Regex filters for P2P preheat (phase 3)#754
Vad1mo wants to merge 1 commit into
feat/regex-filters-phase2from
feat/regex-preheat-phase3

Conversation

@Vad1mo

@Vad1mo Vad1mo commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

PR 3 of the stack (base #753, proposal goharbor/community#298): the regex engine comes to the P2P preheat policy filters. Review only the top commit.

What's in here

  • Preheat repository and tag filters gain an optional kind (doublestar default | regex), reusing lib/pattern's literals and the shared compile/anchor/cap — no new engine, no new literal. A kind on the label, signature or vulnerability filters is rejected rather than silently ignored.
  • Write-time validation in ValidatePreheatPolicy (already called on create and update): unknown kind, invalid or over-long pattern → 400. buildFilter validates again as a runtime backstop for policies stored by older versions.
  • No swagger change: PreheatPolicy.filters is an opaque JSON string end to end; the field is additive, and stored policies without kind decode and behave identically.
  • Portal: engine select per pattern input, regex patterns stored verbatim (no doublestar brace-wrapping), dialog widened to fit the extra control, i18n in all 10 locales.

Testing

Unit coverage for buildFilter under regex, the validation table (including the anchoring-escape pattern), stored-policy backward compatibility, and the controller 400 paths; portal build, lint and specs green.

Known, out of scope: a pre-existing editPolicy brace-unwrapping bug that predates the stack (noted, untouched).

Release Notes

P2P preheat policies can now filter repositories and tags with regular expressions: pick the pattern engine next to each filter in the policy dialog, or send "kind": "regex" through the API. Expressions must match the whole repository name or tag and are validated when the policy is saved. Existing policies keep using Doublestar and are unchanged.

@coderabbitai

coderabbitai Bot commented Aug 27, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8350f212-0e7b-499c-b7a1-83f20878e933

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

9 issues found across 22 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/portal/src/i18n/lang/es-es-lang.json">

<violation number="1" location="src/portal/src/i18n/lang/es-es-lang.json:1665">
P2: The new P2P preheat regex keys added to the Spanish locale (es-es-lang.json) are untranslated English strings, while the rest of this file is translated Spanish. Spanish users will see English text ("Pattern engine", "Enter a regular expression...") for these new UI elements. Translate them, as was done for the German locale (de-de-lang.json uses "Muster-Engine" and "Regulärer Ausdruck...").</violation>
</file>

<file name="src/portal/src/i18n/lang/pt-br-lang.json">

<violation number="1" location="src/portal/src/i18n/lang/pt-br-lang.json:1668">
P2: The four new P2P policy keys are added to the pt-br locale file in English, but every other key in this section is Portuguese. Portuguese users will see English "Pattern engine" and the full English regex helper sentence in the P2P policy UI. The German locale translates these same keys, so this should be translated too. Keep ENGINE_DOUBLESTAR/ENGINE_REGEX as-is (they are enum/proper-noun labels) but translate PATTERN_ENGINE and REGEX_TAG_SEPARATOR.</violation>
</file>

<file name="src/portal/src/i18n/lang/fr-fr-lang.json">

<violation number="1" location="src/portal/src/i18n/lang/fr-fr-lang.json:1661">
P2: The new P2P preheat keys were added to the French locale with untranslated English values, while the rest of this section is properly localized to French. French UI users will see "Pattern engine" and the English "Enter a regular expression that matches the full tag..." string. Translate these values (e.g. "Moteur de modèle" for PATTERN_ENGINE and a French equivalent for REGEX_TAG_SEPARATOR), keeping the regex example and its escaping intact.</violation>
</file>

<file name="src/portal/src/i18n/lang/ko-kr-lang.json">

<violation number="1" location="src/portal/src/i18n/lang/ko-kr-lang.json:1659">
P2: The four new keys added to the Korean locale file carry untranslated English values ("Pattern engine", "Enter a regular expression that matches the full tag..."). Every surrounding key in ko-kr-lang.json is Korean, so Korean users will see this new preheat pattern-engine UI in English instead of their locale. Translate the values to Korean, as de-de already did for these keys ("Muster-Engine"). The "Doublestar"/"RegEx" engine-name values may reasonably stay as-is since they are product names, but the label and helper text should be localized.</violation>
</file>

<file name="src/portal/src/i18n/lang/ru-ru-lang.json">

<violation number="1" location="src/portal/src/i18n/lang/ru-ru-lang.json:2896">
P3: The new PREHEAT strings in the Russian locale are shipped untranslated ("Pattern engine", "RegEx", and the REGEX_TAG_SEPARATOR help text are English), so Russian users see English text for the new pattern-engine controls. Other locales in this stack follow the same untranslated pattern, so this may be intentional pending translation, but the strings should be localized in Russian for the feature to read consistently.</violation>
</file>

<file name="src/portal/src/i18n/lang/zh-tw-lang.json">

<violation number="1" location="src/portal/src/i18n/lang/zh-tw-lang.json:1661">
P3: The new strings added to the Traditional Chinese locale are English: "Pattern engine", "RegEx", and "Enter a regular expression that matches the full tag, for example v\d+\.\d+\.\d+". Traditional Chinese users of the P2P preheat policy editor will see these untranslated. Provide Traditional Chinese translations for PATTERN_ENGINE and REGEX_TAG_SEPARATOR (ENGINE_DOUBLESTAR/ENGINE_REGEX may stay as-is if kept untranslated elsewhere).</violation>
</file>

<file name="src/portal/src/i18n/lang/zh-cn-lang.json">

<violation number="1" location="src/portal/src/i18n/lang/zh-cn-lang.json:1661">
P2: The four new P2P_POLICY keys added to zh-cn-lang.json carry English values instead of Chinese. This file is the Simplified Chinese locale, and every surrounding key (标准, 定时任务, 使用逗号分割, etc.) is translated. These keys are rendered by add-p2p-policy.component.html (P2P_PROVIDER.PATTERN_ENGINE at lines 189/267, P2P_PROVIDER.REGEX_TAG_SEPARATOR at line 312), so Chinese-locale users will see untranslated English text ("Pattern engine", "RegEx", and the English regex help string) in the P2P policy editor. Translate the four values to Chinese.</violation>
</file>

<file name="src/portal/src/i18n/lang/tr-tr-lang.json">

<violation number="1" location="src/portal/src/i18n/lang/tr-tr-lang.json:1659">
P3: The newly added keys are left in English in this Turkish locale file. Translate PATTERN_ENGINE and REGEX_TAG_SEPARATOR to Turkish for users of this locale.</violation>
</file>

<file name="src/pkg/p2p/preheat/models/policy/policy.go">

<violation number="1" location="src/pkg/p2p/preheat/models/policy/policy.go:115">
P2: A policy stored with kind=regex carries a regex value inside the opaque FiltersStr, but any consumer that does not know the `kind` key (an older Harbor after downgrade, or harbor-cli/terraform reading/round-tripping the filters JSON) drops it and silently reinterprets the regex as a doublestar glob, changing which artifacts match with no error. The value is only routed by buildFilter's `f.Kind == FilterKindRegex` check, so there is no boundary enforcement. Consider documenting the downgrade behavior, or at minimum flagging in release notes that downgrading reinterprets regex filters.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment on lines +1665 to +1668
"PATTERN_ENGINE": "Pattern engine",
"ENGINE_DOUBLESTAR": "Doublestar",
"ENGINE_REGEX": "RegEx",
"REGEX_TAG_SEPARATOR": "Enter a regular expression that matches the full tag, for example v\\d+\\.\\d+\\.\\d+",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: The new P2P preheat regex keys added to the Spanish locale (es-es-lang.json) are untranslated English strings, while the rest of this file is translated Spanish. Spanish users will see English text ("Pattern engine", "Enter a regular expression...") for these new UI elements. Translate them, as was done for the German locale (de-de-lang.json uses "Muster-Engine" and "Regulärer Ausdruck...").

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/portal/src/i18n/lang/es-es-lang.json, line 1665:

<comment>The new P2P preheat regex keys added to the Spanish locale (es-es-lang.json) are untranslated English strings, while the rest of this file is translated Spanish. Spanish users will see English text ("Pattern engine", "Enter a regular expression...") for these new UI elements. Translate them, as was done for the German locale (de-de-lang.json uses "Muster-Engine" and "Regulärer Ausdruck...").</comment>

<file context>
@@ -1662,6 +1662,10 @@
         "UPDATED_SUCCESSFULLY": "Política actualizada exitosamente",
         "EXECUTIONS": "Ejecuciones",
         "TAG_SEPARATOR": "Introduzca varias etiquetas separadas por comas, etiqueta* o **",
+        "PATTERN_ENGINE": "Pattern engine",
+        "ENGINE_DOUBLESTAR": "Doublestar",
+        "ENGINE_REGEX": "RegEx",
</file context>
Suggested change
"PATTERN_ENGINE": "Pattern engine",
"ENGINE_DOUBLESTAR": "Doublestar",
"ENGINE_REGEX": "RegEx",
"REGEX_TAG_SEPARATOR": "Enter a regular expression that matches the full tag, for example v\\d+\\.\\d+\\.\\d+",
"PATTERN_ENGINE": "Motor de patrones",
"ENGINE_DOUBLESTAR": "Doublestar",
"ENGINE_REGEX": "RegEx",
"REGEX_TAG_SEPARATOR": "Introduzca una expresión regular que coincida con la etiqueta completa, por ejemplo v\\d+\\.\\d+\\.\\d+",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not changing this, by policy for the whole stack. German is hand-translated because it is reviewed in-house; every other locale carries the English string rather than an unreviewed machine translation. The locale files belong to their language communities upstream, and a wrong translation of a filter tooltip is worse than an English one a translator can still find and replace. The same applies to the sibling threads on the other locale files.

Comment on lines +1668 to +1671
"PATTERN_ENGINE": "Pattern engine",
"ENGINE_DOUBLESTAR": "Doublestar",
"ENGINE_REGEX": "RegEx",
"REGEX_TAG_SEPARATOR": "Enter a regular expression that matches the full tag, for example v\\d+\\.\\d+\\.\\d+",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: The four new P2P policy keys are added to the pt-br locale file in English, but every other key in this section is Portuguese. Portuguese users will see English "Pattern engine" and the full English regex helper sentence in the P2P policy UI. The German locale translates these same keys, so this should be translated too. Keep ENGINE_DOUBLESTAR/ENGINE_REGEX as-is (they are enum/proper-noun labels) but translate PATTERN_ENGINE and REGEX_TAG_SEPARATOR.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/portal/src/i18n/lang/pt-br-lang.json, line 1668:

<comment>The four new P2P policy keys are added to the pt-br locale file in English, but every other key in this section is Portuguese. Portuguese users will see English "Pattern engine" and the full English regex helper sentence in the P2P policy UI. The German locale translates these same keys, so this should be translated too. Keep ENGINE_DOUBLESTAR/ENGINE_REGEX as-is (they are enum/proper-noun labels) but translate PATTERN_ENGINE and REGEX_TAG_SEPARATOR.</comment>

<file context>
@@ -1665,6 +1665,10 @@
         "UPDATED_SUCCESSFULLY": "Política atualizada com sucesso",
         "EXECUTIONS": "Execuções",
         "TAG_SEPARATOR": "Lista de tags separadas por vírgula: minhatag,tag* ou **",
+        "PATTERN_ENGINE": "Pattern engine",
+        "ENGINE_DOUBLESTAR": "Doublestar",
+        "ENGINE_REGEX": "RegEx",
</file context>
Suggested change
"PATTERN_ENGINE": "Pattern engine",
"ENGINE_DOUBLESTAR": "Doublestar",
"ENGINE_REGEX": "RegEx",
"REGEX_TAG_SEPARATOR": "Enter a regular expression that matches the full tag, for example v\\d+\\.\\d+\\.\\d+",
"PATTERN_ENGINE": "Mecanismo de padrão",
"ENGINE_DOUBLESTAR": "Doublestar",
"ENGINE_REGEX": "RegEx",
"REGEX_TAG_SEPARATOR": "Digite uma expressão regular que corresponda à tag completa, por exemplo v\\d+\\.\\d+\\.\\d+",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not changing this, by policy for the whole stack. German is hand-translated because it is reviewed in-house; every other locale carries the English string rather than an unreviewed machine translation. The locale files belong to their language communities upstream, and a wrong translation of a filter tooltip is worse than an English one a translator can still find and replace. The same applies to the sibling threads on the other locale files.

Comment on lines +1661 to +1664
"PATTERN_ENGINE": "Pattern engine",
"ENGINE_DOUBLESTAR": "Doublestar",
"ENGINE_REGEX": "RegEx",
"REGEX_TAG_SEPARATOR": "Enter a regular expression that matches the full tag, for example v\\d+\\.\\d+\\.\\d+",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: The new P2P preheat keys were added to the French locale with untranslated English values, while the rest of this section is properly localized to French. French UI users will see "Pattern engine" and the English "Enter a regular expression that matches the full tag..." string. Translate these values (e.g. "Moteur de modèle" for PATTERN_ENGINE and a French equivalent for REGEX_TAG_SEPARATOR), keeping the regex example and its escaping intact.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/portal/src/i18n/lang/fr-fr-lang.json, line 1661:

<comment>The new P2P preheat keys were added to the French locale with untranslated English values, while the rest of this section is properly localized to French. French UI users will see "Pattern engine" and the English "Enter a regular expression that matches the full tag..." string. Translate these values (e.g. "Moteur de modèle" for PATTERN_ENGINE and a French equivalent for REGEX_TAG_SEPARATOR), keeping the regex example and its escaping intact.</comment>

<file context>
@@ -1658,6 +1658,10 @@
         "UPDATED_SUCCESSFULLY": "Stratégie mise à jour avec succès",
         "EXECUTIONS": "Exécutions",
         "TAG_SEPARATOR": "Entrez plusieurs tags,tag*,ou **, séparés par des virgules",
+        "PATTERN_ENGINE": "Pattern engine",
+        "ENGINE_DOUBLESTAR": "Doublestar",
+        "ENGINE_REGEX": "RegEx",
</file context>
Suggested change
"PATTERN_ENGINE": "Pattern engine",
"ENGINE_DOUBLESTAR": "Doublestar",
"ENGINE_REGEX": "RegEx",
"REGEX_TAG_SEPARATOR": "Enter a regular expression that matches the full tag, for example v\\d+\\.\\d+\\.\\d+",
"PATTERN_ENGINE": "Moteur de modèle",
"ENGINE_DOUBLESTAR": "Doublestar",
"ENGINE_REGEX": "RegEx",
"REGEX_TAG_SEPARATOR": "Saisissez une expression régulière qui correspond à l'intégralité du tag, par exemple v\\d+\\.\\d+\\.\\d+",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not changing this, by policy for the whole stack. German is hand-translated because it is reviewed in-house; every other locale carries the English string rather than an unreviewed machine translation. The locale files belong to their language communities upstream, and a wrong translation of a filter tooltip is worse than an English one a translator can still find and replace. The same applies to the sibling threads on the other locale files.

"UPDATED_SUCCESSFULLY": "정책이 성공적으로 업데이트됐습니다",
"EXECUTIONS": "실행",
"TAG_SEPARATOR": "쉼표로 구분된 태그를 여러 개 입력하세요(태그* 또는 **).",
"PATTERN_ENGINE": "Pattern engine",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: The four new keys added to the Korean locale file carry untranslated English values ("Pattern engine", "Enter a regular expression that matches the full tag..."). Every surrounding key in ko-kr-lang.json is Korean, so Korean users will see this new preheat pattern-engine UI in English instead of their locale. Translate the values to Korean, as de-de already did for these keys ("Muster-Engine"). The "Doublestar"/"RegEx" engine-name values may reasonably stay as-is since they are product names, but the label and helper text should be localized.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/portal/src/i18n/lang/ko-kr-lang.json, line 1659:

<comment>The four new keys added to the Korean locale file carry untranslated English values ("Pattern engine", "Enter a regular expression that matches the full tag..."). Every surrounding key in ko-kr-lang.json is Korean, so Korean users will see this new preheat pattern-engine UI in English instead of their locale. Translate the values to Korean, as de-de already did for these keys ("Muster-Engine"). The "Doublestar"/"RegEx" engine-name values may reasonably stay as-is since they are product names, but the label and helper text should be localized.</comment>

<file context>
@@ -1656,6 +1656,10 @@
         "UPDATED_SUCCESSFULLY": "정책이 성공적으로 업데이트됐습니다",
         "EXECUTIONS": "실행",
         "TAG_SEPARATOR": "쉼표로 구분된 태그를 여러 개 입력하세요(태그* 또는 **).",
+        "PATTERN_ENGINE": "Pattern engine",
+        "ENGINE_DOUBLESTAR": "Doublestar",
+        "ENGINE_REGEX": "RegEx",
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not changing this, by policy for the whole stack. German is hand-translated because it is reviewed in-house; every other locale carries the English string rather than an unreviewed machine translation. The locale files belong to their language communities upstream, and a wrong translation of a filter tooltip is worse than an English one a translator can still find and replace. The same applies to the sibling threads on the other locale files.

"UPDATED_SUCCESSFULLY": "更新策略成功",
"EXECUTIONS": "执行记录",
"TAG_SEPARATOR": "使用逗号分割 tags,tag* 和 **",
"PATTERN_ENGINE": "Pattern engine",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: The four new P2P_POLICY keys added to zh-cn-lang.json carry English values instead of Chinese. This file is the Simplified Chinese locale, and every surrounding key (标准, 定时任务, 使用逗号分割, etc.) is translated. These keys are rendered by add-p2p-policy.component.html (P2P_PROVIDER.PATTERN_ENGINE at lines 189/267, P2P_PROVIDER.REGEX_TAG_SEPARATOR at line 312), so Chinese-locale users will see untranslated English text ("Pattern engine", "RegEx", and the English regex help string) in the P2P policy editor. Translate the four values to Chinese.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/portal/src/i18n/lang/zh-cn-lang.json, line 1661:

<comment>The four new P2P_POLICY keys added to zh-cn-lang.json carry English values instead of Chinese. This file is the Simplified Chinese locale, and every surrounding key (标准, 定时任务, 使用逗号分割, etc.) is translated. These keys are rendered by add-p2p-policy.component.html (P2P_PROVIDER.PATTERN_ENGINE at lines 189/267, P2P_PROVIDER.REGEX_TAG_SEPARATOR at line 312), so Chinese-locale users will see untranslated English text ("Pattern engine", "RegEx", and the English regex help string) in the P2P policy editor. Translate the four values to Chinese.</comment>

<file context>
@@ -1658,6 +1658,10 @@
         "UPDATED_SUCCESSFULLY": "更新策略成功",
         "EXECUTIONS": "执行记录",
         "TAG_SEPARATOR": "使用逗号分割 tags,tag* 和 **",
+        "PATTERN_ENGINE": "Pattern engine",
+        "ENGINE_DOUBLESTAR": "Doublestar",
+        "ENGINE_REGEX": "RegEx",
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not changing this, by policy for the whole stack. German is hand-translated because it is reviewed in-house; every other locale carries the English string rather than an unreviewed machine translation. The locale files belong to their language communities upstream, and a wrong translation of a filter tooltip is worse than an English one a translator can still find and replace. The same applies to the sibling threads on the other locale files.

Comment thread src/portal/src/app/base/project/p2p-provider/policy/policy.component.ts Outdated
Value any `json:"value"`
// Kind selects the pattern engine used for Value, empty means FilterKindDoublestar.
// Only the repository and tag filters carry a pattern, the others are exact or numeric.
Kind string `json:"kind,omitempty"`

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: A policy stored with kind=regex carries a regex value inside the opaque FiltersStr, but any consumer that does not know the kind key (an older Harbor after downgrade, or harbor-cli/terraform reading/round-tripping the filters JSON) drops it and silently reinterprets the regex as a doublestar glob, changing which artifacts match with no error. The value is only routed by buildFilter's f.Kind == FilterKindRegex check, so there is no boundary enforcement. Consider documenting the downgrade behavior, or at minimum flagging in release notes that downgrading reinterprets regex filters.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/pkg/p2p/preheat/models/policy/policy.go, line 115:

<comment>A policy stored with kind=regex carries a regex value inside the opaque FiltersStr, but any consumer that does not know the `kind` key (an older Harbor after downgrade, or harbor-cli/terraform reading/round-tripping the filters JSON) drops it and silently reinterprets the regex as a doublestar glob, changing which artifacts match with no error. The value is only routed by buildFilter's `f.Kind == FilterKindRegex` check, so there is no boundary enforcement. Consider documenting the downgrade behavior, or at minimum flagging in release notes that downgrading reinterprets regex filters.</comment>

<file context>
@@ -103,6 +110,50 @@ type FilterType = string
 	Value any        `json:"value"`
+	// Kind selects the pattern engine used for Value, empty means FilterKindDoublestar.
+	// Only the repository and tag filters carry a pattern, the others are exact or numeric.
+	Kind string `json:"kind,omitempty"`
+}
+
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Known and deliberate, no change. The downgrade behavior is documented in the compatibility section of the proposal this stack implements (goharbor/community#298), which covers every filter that gained a kind, not just preheat. Repeating it as a per-PR release-note line would duplicate that and would say it in the one place a downgrading operator is least likely to read.

Enforcing it at the boundary is not possible either: the filters travel as an opaque JSON string end to end, so an older Harbor decodes them with a struct that has no kind field, and nothing this version writes can make that decode fail.

"UPDATED_SUCCESSFULLY": "Политика успешно обновлена",
"EXECUTIONS": "Выполнения",
"TAG_SEPARATOR": "Введите несколько тегов через запятую, tag*, или **",
"PATTERN_ENGINE": "Pattern engine",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: The new PREHEAT strings in the Russian locale are shipped untranslated ("Pattern engine", "RegEx", and the REGEX_TAG_SEPARATOR help text are English), so Russian users see English text for the new pattern-engine controls. Other locales in this stack follow the same untranslated pattern, so this may be intentional pending translation, but the strings should be localized in Russian for the feature to read consistently.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/portal/src/i18n/lang/ru-ru-lang.json, line 2896:

<comment>The new PREHEAT strings in the Russian locale are shipped untranslated ("Pattern engine", "RegEx", and the REGEX_TAG_SEPARATOR help text are English), so Russian users see English text for the new pattern-engine controls. Other locales in this stack follow the same untranslated pattern, so this may be intentional pending translation, but the strings should be localized in Russian for the feature to read consistently.</comment>

<file context>
@@ -2893,6 +2893,10 @@
     "UPDATED_SUCCESSFULLY": "Политика успешно обновлена",
     "EXECUTIONS": "Выполнения",
     "TAG_SEPARATOR": "Введите несколько тегов через запятую, tag*, или **",
+    "PATTERN_ENGINE": "Pattern engine",
+    "ENGINE_DOUBLESTAR": "Doublestar",
+    "ENGINE_REGEX": "RegEx",
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not changing this, by policy for the whole stack. German is hand-translated because it is reviewed in-house; every other locale carries the English string rather than an unreviewed machine translation. The locale files belong to their language communities upstream, and a wrong translation of a filter tooltip is worse than an English one a translator can still find and replace. The same applies to the sibling threads on the other locale files.

"UPDATED_SUCCESSFULLY": "原則更新成功。",
"EXECUTIONS": "執行項",
"TAG_SEPARATOR": "輸入多個以逗號分隔的標籤,例如 tag、tag* 或 **",
"PATTERN_ENGINE": "Pattern engine",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: The new strings added to the Traditional Chinese locale are English: "Pattern engine", "RegEx", and "Enter a regular expression that matches the full tag, for example v\d+.\d+.\d+". Traditional Chinese users of the P2P preheat policy editor will see these untranslated. Provide Traditional Chinese translations for PATTERN_ENGINE and REGEX_TAG_SEPARATOR (ENGINE_DOUBLESTAR/ENGINE_REGEX may stay as-is if kept untranslated elsewhere).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/portal/src/i18n/lang/zh-tw-lang.json, line 1661:

<comment>The new strings added to the Traditional Chinese locale are English: "Pattern engine", "RegEx", and "Enter a regular expression that matches the full tag, for example v\d+\.\d+\.\d+". Traditional Chinese users of the P2P preheat policy editor will see these untranslated. Provide Traditional Chinese translations for PATTERN_ENGINE and REGEX_TAG_SEPARATOR (ENGINE_DOUBLESTAR/ENGINE_REGEX may stay as-is if kept untranslated elsewhere).</comment>

<file context>
@@ -1658,6 +1658,10 @@
         "UPDATED_SUCCESSFULLY": "原則更新成功。",
         "EXECUTIONS": "執行項",
         "TAG_SEPARATOR": "輸入多個以逗號分隔的標籤,例如 tag、tag* 或 **",
+        "PATTERN_ENGINE": "Pattern engine",
+        "ENGINE_DOUBLESTAR": "Doublestar",
+        "ENGINE_REGEX": "RegEx",
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not changing this, by policy for the whole stack. German is hand-translated because it is reviewed in-house; every other locale carries the English string rather than an unreviewed machine translation. The locale files belong to their language communities upstream, and a wrong translation of a filter tooltip is worse than an English one a translator can still find and replace. The same applies to the sibling threads on the other locale files.

"UPDATED_SUCCESSFULLY": "Updated policy successfully",
"EXECUTIONS": "Executions",
"TAG_SEPARATOR": "Enter multiple comma separated tags,tag*,or **",
"PATTERN_ENGINE": "Pattern engine",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: The newly added keys are left in English in this Turkish locale file. Translate PATTERN_ENGINE and REGEX_TAG_SEPARATOR to Turkish for users of this locale.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/portal/src/i18n/lang/tr-tr-lang.json, line 1659:

<comment>The newly added keys are left in English in this Turkish locale file. Translate PATTERN_ENGINE and REGEX_TAG_SEPARATOR to Turkish for users of this locale.</comment>

<file context>
@@ -1656,6 +1656,10 @@
         "UPDATED_SUCCESSFULLY": "Updated policy successfully",
         "EXECUTIONS": "Executions",
         "TAG_SEPARATOR": "Enter multiple comma separated tags,tag*,or **",
+        "PATTERN_ENGINE": "Pattern engine",
+        "ENGINE_DOUBLESTAR": "Doublestar",
+        "ENGINE_REGEX": "RegEx",
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not changing this, by policy for the whole stack. German is hand-translated because it is reviewed in-house; every other locale carries the English string rather than an unreviewed machine translation. The locale files belong to their language communities upstream, and a wrong translation of a filter tooltip is worse than an English one a translator can still find and replace. The same applies to the sibling threads on the other locale files.

@Vad1mo
Vad1mo force-pushed the feat/regex-filters-phase2 branch from 5412926 to 01307f5 Compare August 27, 2026 18:11
@Vad1mo
Vad1mo force-pushed the feat/regex-preheat-phase3 branch from 8f819fc to 670f25f Compare August 27, 2026 18:14

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

2 existing issues remain and 1 new issue found across 22 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/portal/src/app/base/project/p2p-provider/add-p2p-policy/add-p2p-policy.component.ts">

<violation number="1" location="src/portal/src/app/base/project/p2p-provider/add-p2p-policy/add-p2p-policy.component.ts:293">
P2: When an edited policy contains a future or otherwise unsupported pattern kind, this branch drops it and treats the filter as doublestar. Preserve non-default kinds and their raw values, or block saving unsupported kinds, instead of converting them whenever the user edits an unrelated field; apply the same handling to the tag branch.</violation>
</file>

Requires human review: Auto-approval blocked because this review re-detected 8 unresolved issues already reported by Cubic.

Re-trigger cubic

value: wrapPattern(this.repos, this.reposKind),
};
// doublestar is the default engine, sending it would only add noise to the policy
if (this.reposKind === PatternKind.REGEX) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: When an edited policy contains a future or otherwise unsupported pattern kind, this branch drops it and treats the filter as doublestar. Preserve non-default kinds and their raw values, or block saving unsupported kinds, instead of converting them whenever the user edits an unrelated field; apply the same handling to the tag branch.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/portal/src/app/base/project/p2p-provider/add-p2p-policy/add-p2p-policy.component.ts, line 293:

<comment>When an edited policy contains a future or otherwise unsupported pattern kind, this branch drops it and treats the filter as doublestar. Preserve non-default kinds and their raw values, or block saving unsupported kinds, instead of converting them whenever the user edits an unrelated field; apply the same handling to the tag branch.</comment>

<file context>
@@ -273,32 +285,25 @@ export class AddP2pPolicyComponent implements OnInit, OnDestroy {
+                value: wrapPattern(this.repos, this.reposKind),
+            };
+            // doublestar is the default engine, sending it would only add noise to the policy
+            if (this.reposKind === PatternKind.REGEX) {
+                repoFilter.kind = this.reposKind;
             }
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not changing this: the case cannot occur in stored data of this version. The only kinds that exist are doublestar and regex, and the server rejects anything else at save time with a 400, both on create and on update, so no policy in this schema can carry an unknown kind for the dialog to preserve. Preserving or blocking a kind that no writer can produce is speculative complexity in the edit path.

If a later version adds a kind, the portal that ships with it will know it. The lib/pattern Matcher on this branch already errors on an unrecognised kind at match time rather than falling back to doublestar, so the runtime side is fail-closed rather than silently reinterpreting.

@Vad1mo
Vad1mo force-pushed the feat/regex-preheat-phase3 branch from 670f25f to 293cdff Compare August 27, 2026 18:40

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

2 existing issues remain and 1 new issue found across 22 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/pkg/p2p/preheat/policy/filter.go">

<violation number="1" location="src/pkg/p2p/preheat/policy/filter.go:158">
P3: `buildFilter` now calls `f.Validate()` on every filter build, and for a regex kind that recompiles the expression (`ValidateRegex` -> `CompileRegex`, which itself compiles twice) on every enforcer run. The `regexpselector` matcher then compiles the same expression a third time on first use. This is redundant compile work on the preheat execution path with no new safety (write-time validation already covers valid API-created policies; legacy stored policies have empty Kind and return nil immediately). Consider skipping Validate on the runtime path, or having buildFilter rely on the matcher's own cached compile error, so valid policies are not compiled twice.</violation>
</file>

Requires human review: Auto-approval blocked because this review re-detected 9 unresolved issues already reported by Cubic.
Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread src/controller/p2p/preheat/controller_test.go

// Backstop for the write time validation: a policy stored by an older version, or
// written past the API, still has to fail here instead of building a broken selector.
if err := f.Validate(); err != nil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: buildFilter now calls f.Validate() on every filter build, and for a regex kind that recompiles the expression (ValidateRegex -> CompileRegex, which itself compiles twice) on every enforcer run. The regexpselector matcher then compiles the same expression a third time on first use. This is redundant compile work on the preheat execution path with no new safety (write-time validation already covers valid API-created policies; legacy stored policies have empty Kind and return nil immediately). Consider skipping Validate on the runtime path, or having buildFilter rely on the matcher's own cached compile error, so valid policies are not compiled twice.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/pkg/p2p/preheat/policy/filter.go, line 158:

<comment>`buildFilter` now calls `f.Validate()` on every filter build, and for a regex kind that recompiles the expression (`ValidateRegex` -> `CompileRegex`, which itself compiles twice) on every enforcer run. The `regexpselector` matcher then compiles the same expression a third time on first use. This is redundant compile work on the preheat execution path with no new safety (write-time validation already covers valid API-created policies; legacy stored policies have empty Kind and return nil immediately). Consider skipping Validate on the runtime path, or having buildFilter rely on the matcher's own cached compile error, so valid policies are not compiled twice.</comment>

<file context>
@@ -152,6 +153,12 @@ func buildFilter(f *policy.Filter) (selector.Selector, error) {
 
+	// Backstop for the write time validation: a policy stored by an older version, or
+	// written past the API, still has to fail here instead of building a broken selector.
+	if err := f.Validate(); err != nil {
+		return nil, err
+	}
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Deliberate, no change. The Validate call in buildFilter is a backstop for the two paths that do not go through write-time validation: a policy stored before this version existed, and anything written past the API. Runtime keeping its own check is the point of it.

The cost is not on the hot path either. buildFilter runs once per filter per enforcer run, not per candidate — the compiled expression then lives in the selector's Matcher and is reused across every artifact the run evaluates. So this is one extra regexp.Compile of a pattern capped at 512 characters per policy execution.

The preheat policy filters bypassed the selector registry: buildFilter reached
straight for the doublestar selector, so a repository or tag pattern could only
be a glob, while the retention, immutability and replication filters can already
be regular expressions.

A preheat filter now carries an optional "kind", the pattern engine of its
value, with the same literals the other filters use. The field is additive and
omitted when it is the doublestar default, so a policy stored before this keeps
decoding and behaving exactly as it did.

Only the repository and tag filters take a kind. The label filter matches label
names exactly, and signature and vulnerability carry a bool and an int, so a
kind there is rejected rather than silently ignored.

The policy schema validates the filters at write time, which is the create and
update path of the controller, and returns a 400 for an unknown kind, a kind on
a filter type that has no pattern, or an expression that does not compile. The
filter builder keeps its own check as a backstop for a policy that was stored by
an older version or written past the API.

The engine is picked per pattern input in the preheat policy dialog, next to the
repositories and the tags, and the payload carries the kind only when it is not
the default. A regex is stored verbatim: the portal wraps a comma separated
doublestar list in braces, which a regex uses for quantifiers.

Signed-off-by: Vadim Bauer <vb@container-registry.com>
@Vad1mo
Vad1mo force-pushed the feat/regex-preheat-phase3 branch from b2792ba to 705bb11 Compare August 27, 2026 20:49
Vad1mo added a commit that referenced this pull request Sep 10, 2026
…810)

Preview artifacts now follow what a pull request actually changes,
including across a GitHub stack: the seven images when image inputs
change, the chart when chart inputs change, always from the top of a
stack. Same change as container-registry/harbor-scanner-trivy#92,
applied to the inline seven-image workflow, plus the chart preview this
repo did not have.

## Problem

On a native stack the trigger's `paths` filter saw only each PR's own
slice: the top PR, the only one worth deploying, got no images when its
slice was docs-only, while every lower PR rebuilt all fourteen platform
images on each `gh stack push`. The chart had no preview at all; a chart
change could only be tried after a release.

## How it worked before

`pr-ci.yml` filtered with `on.pull_request.paths`, evaluated against the
PR's own diff; `build`, `merge`, `sign` and `pr-comment` each repeated
the same `if:` and ran for every position of a stack.

## How it works now

- A workflow publishes when the PR's diff against `main` touches one of
its inputs. The check is a job, not a `paths` trigger filter, so every
PR gets a status and the diff is the real one.
- Stacked PRs (`gh stack`): only the top PR publishes; its head is the
whole stack, so its ordinary `pr-<N>` artifacts are the stack artifacts.
Lower PRs are skipped at the gate. Plain PRs behave as before.
- The workflows also subscribe to the `stacked` activity type: GitHub
opens the PRs of a stack before it links them, so `opened` never carries
`pull_request.stack`; the link does. Verified on trivy stack #96:
linking fired both previews on the top with no push (gate logged
`event=stacked stack=96`), the bottom was skipped.
- Image and chart are independent: a chart-only PR gets a chart, an
app-only PR gets an image, both when both change, nothing when neither
does.
- `pr-ci.yml`: the 15-entry allowlist moves into a `changes` job; the
four jobs depend on it. The comment lists the stack members when
stacked.
- New `pr-chart.yml`: `<chart version>-pr.<N>` into
`PR_REGISTRY_PROJECT` with the `PR_REGISTRY_*` credentials, subcharts
from `Chart.lock`, Artifact Hub annotation against the release project
as on release, signed, sticky comment with the install command.

`build.yml` and `test.yml` are untouched: native stacks already trigger
them as if targeting `main`. Manually chained PRs such as #753 -> #754
are not a stack to GitHub; their bottom PR gets ordinary previews, the
ones above match no `branches` filter. `gh stack init` on those branches
is the fix.

## Verification

- `actionlint` and `zizmor` findings identical to `main` (the
`ubuntu-26.04` label and SC2086 notes are pre-existing); comment scripts
pass `node --check`.
- This PR changes `pr-ci.yml` and adds `pr-chart.yml`, so its own runs
exercise both plain paths: gate, 14 builds, 7 merges, sign, comment;
chart packaged, pushed, signed, commented.
- The stack path was verified on harbor-scanner-trivy with a 2-PR stack
(docs-only top built, bottom skipped, restack rebuilt only the top).


## Preview comments

Each preview comment names the commit it was built from, lists the
inputs that triggered the build, links the sibling preview comment when
one exists, and is rewritten with an outdated notice when a build is
skipped, fails, or the pull request is not the top of its stack. The
notice is delimited by markers, so a second one replaces the first and a
later successful build clears it. Ported from
container-registry/harbor-scanner-trivy#101.

---------

Signed-off-by: Vadim Bauer <vb@container-registry.com>
@bupd bupd added prio/P2 No deadline; merges when the queue is clear state/blocked Waiting on another PR, a decision, or an external answer stacked Based on another open PR, not on main. Merge bottom-up labels Sep 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component/portal prio/P2 No deadline; merges when the queue is clear stacked Based on another open PR, not on main. Merge bottom-up state/blocked Waiting on another PR, a decision, or an external answer tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants