Conversation
|
Warning Review limit reachedNext included review available in 59 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (47)
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. Comment |
There was a problem hiding this comment.
9 issues found and verified against the latest diff
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/ko-kr-lang.json">
<violation number="1" location="src/portal/src/i18n/lang/ko-kr-lang.json:58">
P2: The new regex-filter keys added to the Korean locale carry English values, so Korean UI users will see English text for the pattern-engine select and the regex tooltips while every other string in this file is Korean. Translate the six added keys (NAME_FILTER_REGEX, TAG_FILTER_REGEX, LABEL_FILTER_REGEX at lines 58-60 and PATTERN_ENGINE, ENGINE_DOUBLESTAR, ENGINE_REGEX at lines 627-629) instead of dropping in the en-US strings.</violation>
</file>
<file name="src/pkg/reg/adapter/gitlab/adapter.go">
<violation number="1" location="src/pkg/reg/adapter/gitlab/adapter.go:244">
P2: When a GitLab policy contains an invalid regex that bypassed write-time validation, `Matcher.Match` returns an error but this loop discards it and continues. Return the matcher error through `existPatterns` and `FetchArtifacts` (and do the same for tag matching) so replication fails visibly instead of silently omitting content.</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:58">
P3: The Simplified Chinese locale file now serves these new strings in English (NAME_FILTER_REGEX, TAG_FILTER_REGEX, LABEL_FILTER_REGEX, PATTERN_ENGINE, ENGINE_DOUBLESTAR, ENGINE_REGEX), so Chinese users will see English tooltips and selectors. Translate the new values to Simplified Chinese, mirroring de-de-lang.json which already localized the same keys.</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:58">
P3: The new UI strings added to the Traditional Chinese locale file are in English, not translated to zh-TW. This file is otherwise fully localized (the adjacent NAME_FILTER/TAG_FILTER/LABEL_FILTER at lines 55-57 are Chinese), so Traditional Chinese users will see untranslated English text for the new RegEx pattern controls and tooltips: NAME_FILTER_REGEX (58), TAG_FILTER_REGEX (59), LABEL_FILTER_REGEX (60), PATTERN_ENGINE (627), plus the REPLICATION section copies at 1352-1354. Translate these keys (PATTERN_ENGINE → "模式引擎"; ENGINE_DOUBLESTAR/ENGINE_REGEX can stay as-is) and provide zh-TW tooltip text for the three _REGEX keys.</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:58">
P2: The new regex-filter i18n keys (NAME_FILTER_REGEX, TAG_FILTER_REGEX, LABEL_FILTER_REGEX, PATTERN_ENGINE, ENGINE_DOUBLESTAR, ENGINE_REGEX) were added to the pt-br file with English values. Every surrounding string in this Portuguese file is translated, so pt-BR users will see English tooltips and the "Pattern engine" label for the new replication feature. Translate these values to Portuguese (e.g. NAME_FILTER_REGEX: "Filtrar o nome do recurso com uma expressão regular que deve corresponder ao nome completo.", PATTERN_ENGINE: "Mecanismo de padrão").</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:58">
P3: The newly added user-facing strings (NAME_FILTER_REGEX, TAG_FILTER_REGEX, LABEL_FILTER_REGEX, PATTERN_ENGINE, ENGINE_DOUBLESTAR, ENGINE_REGEX) are left untranslated in English in the Turkish locale, even though the surrounding TOOLTIP/REPLICATION keys are translated into Turkish. Non-Turkish users will see these controls and tooltips in English, which is inconsistent with the rest of the dialog (e.g. NAME_FILTER/TAG_FILTER right above are Turkish). The German locale in this same PR (de-de-lang.json:58,627-629) did translate the same keys, so translating them here is expected.</violation>
</file>
<file name="src/portal/src/i18n/lang/es-es-lang.json">
<violation number="1" location="src/portal/src/i18n/lang/es-es-lang.json:58">
P3: The new keys added to the Spanish file (es-es) are untranslated English strings, so Spanish-language portal users will see English for the regex filter tooltips (NAME_FILTER_REGEX, TAG_FILTER_REGEX, LABEL_FILTER_REGEX) and the pattern-engine dropdown (PATTERN_ENGINE, ENGINE_DOUBLESTAR, ENGINE_REGEX). The neighboring NAME_FILTER/TAG_FILTER keys are Spanish, and the German file de-de-lang.json:58-60 receives German text for these same keys, so Spanish translations are expected here. Provide Spanish translations for all six added values.</violation>
</file>
<file name="src/pkg/reg/model/policy.go">
<violation number="1" location="src/pkg/reg/model/policy.go:94">
P2: When a label filter is loaded from a stored policy, parseFilters rewrites Value to []string, but Filter.Validate's label branch asserts f.Value.([]any). A kind=regex label filter configured at request time validates (JSON unmarshals to []any), but once re-loaded from storage and re-validated it fails the type assertion before the new regex validation runs. Normalize label Value to one type across validation and the parse/round-trip path (or assert []string in Validate), and add a stored->validated round-trip test for a regex label filter.</violation>
</file>
<file name="api/v2.0/swagger.yaml">
<violation number="1" location="api/v2.0/swagger.yaml:7751">
P3: The new `kind` field is documented as accepting exactly `doublestar` or `regex`, but the spec defines no `enum`. Backend validation in `model.Filter.Validate` rejects other values case-sensitively at write time, so downstream consumers (harbor-cli, terraform provider, SDKs generated from this spec) get no client-side constraint and can easily send a value that the server silently 400s. Add the enum to make the allowed set explicit and self-documenting.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| "NAME_FILTER": "리소스 이름을 필터링합니다. 모두 일치시키려면 비워두거나 '**'을 입력하세요. 'library/**'는 'library' 아래의 리소스만 일치합니다. 더 많은 패턴에 대해서는 사용자 가이드를 참조하세요.", | ||
| "TAG_FILTER": "리소스의 태그/버전 부분을 필터링합니다. 모두 일치시키려면 비워 두거나 '**'를 사용하세요. '1.0*'은 '1.0'으로 시작하는 태그에만 일치합니다. 더 많은 패턴에 대해서는 사용자 가이드를 참조하세요.", | ||
| "LABEL_FILTER": "라벨에 따라 리소스를 필터링합니다.", | ||
| "NAME_FILTER_REGEX": "Filter the name of the resource with a regular expression that has to match the full name. Leave empty to match all. 'library/.*' only matches resources under 'library'.", |
There was a problem hiding this comment.
P2: The new regex-filter keys added to the Korean locale carry English values, so Korean UI users will see English text for the pattern-engine select and the regex tooltips while every other string in this file is Korean. Translate the six added keys (NAME_FILTER_REGEX, TAG_FILTER_REGEX, LABEL_FILTER_REGEX at lines 58-60 and PATTERN_ENGINE, ENGINE_DOUBLESTAR, ENGINE_REGEX at lines 627-629) instead of dropping in the en-US strings.
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 58:
<comment>The new regex-filter keys added to the Korean locale carry English values, so Korean UI users will see English text for the pattern-engine select and the regex tooltips while every other string in this file is Korean. Translate the six added keys (NAME_FILTER_REGEX, TAG_FILTER_REGEX, LABEL_FILTER_REGEX at lines 58-60 and PATTERN_ENGINE, ENGINE_DOUBLESTAR, ENGINE_REGEX at lines 627-629) instead of dropping in the en-US strings.</comment>
<file context>
@@ -55,6 +55,9 @@
"NAME_FILTER": "리소스 이름을 필터링합니다. 모두 일치시키려면 비워두거나 '**'을 입력하세요. 'library/**'는 'library' 아래의 리소스만 일치합니다. 더 많은 패턴에 대해서는 사용자 가이드를 참조하세요.",
"TAG_FILTER": "리소스의 태그/버전 부분을 필터링합니다. 모두 일치시키려면 비워 두거나 '**'를 사용하세요. '1.0*'은 '1.0'으로 시작하는 태그에만 일치합니다. 더 많은 패턴에 대해서는 사용자 가이드를 참조하세요.",
"LABEL_FILTER": "라벨에 따라 리소스를 필터링합니다.",
+ "NAME_FILTER_REGEX": "Filter the name of the resource with a regular expression that has to match the full name. Leave empty to match all. 'library/.*' only matches resources under 'library'.",
+ "TAG_FILTER_REGEX": "Filter the tag/version part of the resources with a regular expression that has to match the full tag. Leave empty to match all. 'v\\d+\\.\\d+' only matches tags such as 'v1.0'. Untagged artifacts never match a regular expression.",
+ "LABEL_FILTER_REGEX": "Filter the resources according to labels, every expression has to match a full label name.",
</file context>
There was a problem hiding this comment.
Not changing this, deliberately. German was hand-translated by a German speaker on the team; the other locales carry the English string rather than machine translations nobody here can review. These files are maintained by their language communities upstream, and an unreviewed translation is harder to correct later than an obvious English placeholder, because it looks finished. The key is in place, so a translator only has to fill in the value.
Side note: the tooltip text this thread quotes has since changed. TAG_FILTER_REGEX no longer says untagged artifacts never match a regular expression, because that behaviour was corrected: an untagged artifact is kept exactly when the pattern matches the empty tag, the same rule doublestar follows.
| if ok, _ := util.Match(strings.ToLower(pathPattern), strings.ToLower(path)); ok { | ||
| if len(matchers) > 0 { | ||
| for _, matcher := range matchers { | ||
| if ok, _ := matcher.Match(path); ok { |
There was a problem hiding this comment.
P2: When a GitLab policy contains an invalid regex that bypassed write-time validation, Matcher.Match returns an error but this loop discards it and continues. Return the matcher error through existPatterns and FetchArtifacts (and do the same for tag matching) so replication fails visibly instead of silently omitting content.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/pkg/reg/adapter/gitlab/adapter.go, line 244:
<comment>When a GitLab policy contains an invalid regex that bypassed write-time validation, `Matcher.Match` returns an error but this loop discards it and continues. Return the matcher error through `existPatterns` and `FetchArtifacts` (and do the same for tag matching) so replication fails visibly instead of silently omitting content.</comment>
<file context>
@@ -222,11 +237,11 @@ func (a *adapter) getProjectsByPattern(pattern string) ([]*Project, error) {
- if ok, _ := util.Match(strings.ToLower(pathPattern), strings.ToLower(path)); ok {
+ if len(matchers) > 0 {
+ for _, matcher := range matchers {
+ if ok, _ := matcher.Match(path); ok {
correct = true
break
</file context>
There was a problem hiding this comment.
Leaving this as it is, for now. Discarding the match error is the shape the code already had: the previous line was if ok, _ := util.Match(...) and existPatterns has always swallowed it, so this PR is not introducing the behaviour, only carrying it. It is also unreachable through the API, since a pattern that fails to compile is rejected when the policy is saved, and a stored policy cannot acquire an invalid pattern afterwards.
Threading the error out of existPatterns and the tag loop is worth doing, but it changes the signature of a function on the fetch path for every kind, which belongs in its own change rather than in a feature PR where it would be indistinguishable from the feature. Noted as a follow-up.
| "NAME_FILTER": "Filtrar por nome de recurso. Deixe vazio ou use '**' para ver todos. A expressão 'library/**' seleciona recursos dentro de 'library'. Para mais detalhes, confira a documentação.", | ||
| "TAG_FILTER": "Filtrar por tag de cada recurso. Deixe vazio ou use '**' para ver todas. A expressão '1.0*' seleciona todas as tags que começam com 1.0. Para mais detalhes, confira a documentação.", | ||
| "LABEL_FILTER": "Filtrar por marcadores.", | ||
| "NAME_FILTER_REGEX": "Filter the name of the resource with a regular expression that has to match the full name. Leave empty to match all. 'library/.*' only matches resources under 'library'.", |
There was a problem hiding this comment.
P2: The new regex-filter i18n keys (NAME_FILTER_REGEX, TAG_FILTER_REGEX, LABEL_FILTER_REGEX, PATTERN_ENGINE, ENGINE_DOUBLESTAR, ENGINE_REGEX) were added to the pt-br file with English values. Every surrounding string in this Portuguese file is translated, so pt-BR users will see English tooltips and the "Pattern engine" label for the new replication feature. Translate these values to Portuguese (e.g. NAME_FILTER_REGEX: "Filtrar o nome do recurso com uma expressão regular que deve corresponder ao nome completo.", PATTERN_ENGINE: "Mecanismo de padrão").
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 58:
<comment>The new regex-filter i18n keys (NAME_FILTER_REGEX, TAG_FILTER_REGEX, LABEL_FILTER_REGEX, PATTERN_ENGINE, ENGINE_DOUBLESTAR, ENGINE_REGEX) were added to the pt-br file with English values. Every surrounding string in this Portuguese file is translated, so pt-BR users will see English tooltips and the "Pattern engine" label for the new replication feature. Translate these values to Portuguese (e.g. NAME_FILTER_REGEX: "Filtrar o nome do recurso com uma expressão regular que deve corresponder ao nome completo.", PATTERN_ENGINE: "Mecanismo de padrão").</comment>
<file context>
@@ -55,6 +55,9 @@
"NAME_FILTER": "Filtrar por nome de recurso. Deixe vazio ou use '**' para ver todos. A expressão 'library/**' seleciona recursos dentro de 'library'. Para mais detalhes, confira a documentação.",
"TAG_FILTER": "Filtrar por tag de cada recurso. Deixe vazio ou use '**' para ver todas. A expressão '1.0*' seleciona todas as tags que começam com 1.0. Para mais detalhes, confira a documentação.",
"LABEL_FILTER": "Filtrar por marcadores.",
+ "NAME_FILTER_REGEX": "Filter the name of the resource with a regular expression that has to match the full name. Leave empty to match all. 'library/.*' only matches resources under 'library'.",
+ "TAG_FILTER_REGEX": "Filter the tag/version part of the resources with a regular expression that has to match the full tag. Leave empty to match all. 'v\\d+\\.\\d+' only matches tags such as 'v1.0'. Untagged artifacts never match a regular expression.",
+ "LABEL_FILTER_REGEX": "Filter the resources according to labels, every expression has to match a full label name.",
</file context>
There was a problem hiding this comment.
Not changing this, deliberately. German was hand-translated by a German speaker on the team; the other locales carry the English string rather than machine translations nobody here can review. These files are maintained by their language communities upstream, and an unreviewed translation is harder to correct later than an obvious English placeholder, because it looks finished. The key is in place, so a translator only has to fill in the value.
Side note: the tooltip text this thread quotes has since changed. TAG_FILTER_REGEX no longer says untagged artifacts never match a regular expression, because that behaviour was corrected: an untagged artifact is kept exactly when the pattern matches the empty tag, the same rule doublestar follows.
| } | ||
| for _, label := range labels { | ||
| _, ok := label.(string) | ||
| value, ok := label.(string) |
There was a problem hiding this comment.
P2: When a label filter is loaded from a stored policy, parseFilters rewrites Value to []string, but Filter.Validate's label branch asserts f.Value.([]any). A kind=regex label filter configured at request time validates (JSON unmarshals to []any), but once re-loaded from storage and re-validated it fails the type assertion before the new regex validation runs. Normalize label Value to one type across validation and the parse/round-trip path (or assert []string in Validate), and add a stored->validated round-trip test for a regex label filter.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/pkg/reg/model/policy.go, line 94:
<comment>When a label filter is loaded from a stored policy, parseFilters rewrites Value to []string, but Filter.Validate's label branch asserts f.Value.([]any). A kind=regex label filter configured at request time validates (JSON unmarshals to []any), but once re-loaded from storage and re-validated it fails the type assertion before the new regex validation runs. Normalize label Value to one type across validation and the parse/round-trip path (or assert []string in Validate), and add a stored->validated round-trip test for a regex label filter.</comment>
<file context>
@@ -54,25 +70,35 @@ func (f *Filter) Validate() error {
}
for _, label := range labels {
- _, ok := label.(string)
+ value, ok := label.(string)
if !ok {
return errors.New(nil).WithCode(errors.BadRequestCode).
</file context>
There was a problem hiding this comment.
Not changing this. The type split is real but pre-existing: Validate's label branch has always asserted []any, because it only ever runs on API input, where the value arrives from JSON. parseFilters rewrites the value to []string on the way out of storage, and nothing re-validates a policy loaded from storage, so the assertion this describes is never reached.
The regex validation sits behind the same assertion and inherits exactly the same reachability, so it adds no new exposure. Normalising the two representations would mean touching Validate, parseFilters and every caller that consumes Filter.Value, which is a refactor of the filter model rather than part of adding a kind.
| "NAME_FILTER": "篩選資源名稱。不填寫、或使用「**」可選取所有資源。「library/**」僅選取「library」下的資源。更多模式請參考使用者手冊。", | ||
| "TAG_FILTER": "篩選資源的標籤/版本。不填寫、或使用「**」可選取所有標籤/版本。「1.0*」僅選取以「1.0」開頭的標籤。更多模式請參考使用者手冊。", | ||
| "LABEL_FILTER": "根據標籤篩選資源。", | ||
| "NAME_FILTER_REGEX": "Filter the name of the resource with a regular expression that has to match the full name. Leave empty to match all. 'library/.*' only matches resources under 'library'.", |
There was a problem hiding this comment.
P3: The new UI strings added to the Traditional Chinese locale file are in English, not translated to zh-TW. This file is otherwise fully localized (the adjacent NAME_FILTER/TAG_FILTER/LABEL_FILTER at lines 55-57 are Chinese), so Traditional Chinese users will see untranslated English text for the new RegEx pattern controls and tooltips: NAME_FILTER_REGEX (58), TAG_FILTER_REGEX (59), LABEL_FILTER_REGEX (60), PATTERN_ENGINE (627), plus the REPLICATION section copies at 1352-1354. Translate these keys (PATTERN_ENGINE → "模式引擎"; ENGINE_DOUBLESTAR/ENGINE_REGEX can stay as-is) and provide zh-TW tooltip text for the three _REGEX keys.
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 58:
<comment>The new UI strings added to the Traditional Chinese locale file are in English, not translated to zh-TW. This file is otherwise fully localized (the adjacent NAME_FILTER/TAG_FILTER/LABEL_FILTER at lines 55-57 are Chinese), so Traditional Chinese users will see untranslated English text for the new RegEx pattern controls and tooltips: NAME_FILTER_REGEX (58), TAG_FILTER_REGEX (59), LABEL_FILTER_REGEX (60), PATTERN_ENGINE (627), plus the REPLICATION section copies at 1352-1354. Translate these keys (PATTERN_ENGINE → "模式引擎"; ENGINE_DOUBLESTAR/ENGINE_REGEX can stay as-is) and provide zh-TW tooltip text for the three _REGEX keys.</comment>
<file context>
@@ -55,6 +55,9 @@
"NAME_FILTER": "篩選資源名稱。不填寫、或使用「**」可選取所有資源。「library/**」僅選取「library」下的資源。更多模式請參考使用者手冊。",
"TAG_FILTER": "篩選資源的標籤/版本。不填寫、或使用「**」可選取所有標籤/版本。「1.0*」僅選取以「1.0」開頭的標籤。更多模式請參考使用者手冊。",
"LABEL_FILTER": "根據標籤篩選資源。",
+ "NAME_FILTER_REGEX": "Filter the name of the resource with a regular expression that has to match the full name. Leave empty to match all. 'library/.*' only matches resources under 'library'.",
+ "TAG_FILTER_REGEX": "Filter the tag/version part of the resources with a regular expression that has to match the full tag. Leave empty to match all. 'v\\d+\\.\\d+' only matches tags such as 'v1.0'. Untagged artifacts never match a regular expression.",
+ "LABEL_FILTER_REGEX": "Filter the resources according to labels, every expression has to match a full label name.",
</file context>
There was a problem hiding this comment.
Not changing this, deliberately. German was hand-translated by a German speaker on the team; the other locales carry the English string rather than machine translations nobody here can review. These files are maintained by their language communities upstream, and an unreviewed translation is harder to correct later than an obvious English placeholder, because it looks finished. The key is in place, so a translator only has to fill in the value.
Side note: the tooltip text this thread quotes has since changed. TAG_FILTER_REGEX no longer says untagged artifacts never match a regular expression, because that behaviour was corrected: an untagged artifact is kept exactly when the pattern matches the empty tag, the same rule doublestar follows.
| "NAME_FILTER": "Kaynağın adını filtreleyin. Boş bırakın veya hepsine uyacak şekilde '**' kullanın. 'library / **' sadece 'library' altındaki kaynaklarla eşleşiyor. Daha fazla desen için lütfen kullanım kılavuzuna bakın.", | ||
| "TAG_FILTER": "Kaynakların etiket / sürüm bölümünü filtreleyin. Boş bırakın veya hepsine uyacak şekilde '**' kullanın. '1.0 *' sadece '1.0' ile başlayan etiketlerle eşleşir. Daha fazla desen için lütfen kullanım kılavuzuna bakın.", | ||
| "LABEL_FILTER": "Kaynakları etiketlere göre filtreleyin.", | ||
| "NAME_FILTER_REGEX": "Filter the name of the resource with a regular expression that has to match the full name. Leave empty to match all. 'library/.*' only matches resources under 'library'.", |
There was a problem hiding this comment.
P3: The newly added user-facing strings (NAME_FILTER_REGEX, TAG_FILTER_REGEX, LABEL_FILTER_REGEX, PATTERN_ENGINE, ENGINE_DOUBLESTAR, ENGINE_REGEX) are left untranslated in English in the Turkish locale, even though the surrounding TOOLTIP/REPLICATION keys are translated into Turkish. Non-Turkish users will see these controls and tooltips in English, which is inconsistent with the rest of the dialog (e.g. NAME_FILTER/TAG_FILTER right above are Turkish). The German locale in this same PR (de-de-lang.json:58,627-629) did translate the same keys, so translating them here is expected.
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 58:
<comment>The newly added user-facing strings (NAME_FILTER_REGEX, TAG_FILTER_REGEX, LABEL_FILTER_REGEX, PATTERN_ENGINE, ENGINE_DOUBLESTAR, ENGINE_REGEX) are left untranslated in English in the Turkish locale, even though the surrounding TOOLTIP/REPLICATION keys are translated into Turkish. Non-Turkish users will see these controls and tooltips in English, which is inconsistent with the rest of the dialog (e.g. NAME_FILTER/TAG_FILTER right above are Turkish). The German locale in this same PR (de-de-lang.json:58,627-629) did translate the same keys, so translating them here is expected.</comment>
<file context>
@@ -55,6 +55,9 @@
"NAME_FILTER": "Kaynağın adını filtreleyin. Boş bırakın veya hepsine uyacak şekilde '**' kullanın. 'library / **' sadece 'library' altındaki kaynaklarla eşleşiyor. Daha fazla desen için lütfen kullanım kılavuzuna bakın.",
"TAG_FILTER": "Kaynakların etiket / sürüm bölümünü filtreleyin. Boş bırakın veya hepsine uyacak şekilde '**' kullanın. '1.0 *' sadece '1.0' ile başlayan etiketlerle eşleşir. Daha fazla desen için lütfen kullanım kılavuzuna bakın.",
"LABEL_FILTER": "Kaynakları etiketlere göre filtreleyin.",
+ "NAME_FILTER_REGEX": "Filter the name of the resource with a regular expression that has to match the full name. Leave empty to match all. 'library/.*' only matches resources under 'library'.",
+ "TAG_FILTER_REGEX": "Filter the tag/version part of the resources with a regular expression that has to match the full tag. Leave empty to match all. 'v\\d+\\.\\d+' only matches tags such as 'v1.0'. Untagged artifacts never match a regular expression.",
+ "LABEL_FILTER_REGEX": "Filter the resources according to labels, every expression has to match a full label name.",
</file context>
There was a problem hiding this comment.
Not changing this, deliberately. German was hand-translated by a German speaker on the team; the other locales carry the English string rather than machine translations nobody here can review. These files are maintained by their language communities upstream, and an unreviewed translation is harder to correct later than an obvious English placeholder, because it looks finished. The key is in place, so a translator only has to fill in the value.
Side note: the tooltip text this thread quotes has since changed. TAG_FILTER_REGEX no longer says untagged artifacts never match a regular expression, because that behaviour was corrected: an untagged artifact is kept exactly when the pattern matches the empty tag, the same rule doublestar follows.
| "NAME_FILTER": "Filtrar el nombre del recurso. Dejar vacio o usar '**' para todos. 'library/**' solo busca recursos en 'library'. Para más patrones, por favor refierase a la guía de usuario.", | ||
| "TAG_FILTER": "Filtar parte tag/version de los recursos. Dejar vacio o usar '**' para todos. '1.0*' solo busca recursos con tags que comienzan con '1.0'. Para más patrones, por favor refierase a la guía de usuario.", | ||
| "LABEL_FILTER": "Filtrar los recursos por labels.", | ||
| "NAME_FILTER_REGEX": "Filter the name of the resource with a regular expression that has to match the full name. Leave empty to match all. 'library/.*' only matches resources under 'library'.", |
There was a problem hiding this comment.
P3: The new keys added to the Spanish file (es-es) are untranslated English strings, so Spanish-language portal users will see English for the regex filter tooltips (NAME_FILTER_REGEX, TAG_FILTER_REGEX, LABEL_FILTER_REGEX) and the pattern-engine dropdown (PATTERN_ENGINE, ENGINE_DOUBLESTAR, ENGINE_REGEX). The neighboring NAME_FILTER/TAG_FILTER keys are Spanish, and the German file de-de-lang.json:58-60 receives German text for these same keys, so Spanish translations are expected here. Provide Spanish translations for all six added values.
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 58:
<comment>The new keys added to the Spanish file (es-es) are untranslated English strings, so Spanish-language portal users will see English for the regex filter tooltips (NAME_FILTER_REGEX, TAG_FILTER_REGEX, LABEL_FILTER_REGEX) and the pattern-engine dropdown (PATTERN_ENGINE, ENGINE_DOUBLESTAR, ENGINE_REGEX). The neighboring NAME_FILTER/TAG_FILTER keys are Spanish, and the German file de-de-lang.json:58-60 receives German text for these same keys, so Spanish translations are expected here. Provide Spanish translations for all six added values.</comment>
<file context>
@@ -55,6 +55,9 @@
"NAME_FILTER": "Filtrar el nombre del recurso. Dejar vacio o usar '**' para todos. 'library/**' solo busca recursos en 'library'. Para más patrones, por favor refierase a la guía de usuario.",
"TAG_FILTER": "Filtar parte tag/version de los recursos. Dejar vacio o usar '**' para todos. '1.0*' solo busca recursos con tags que comienzan con '1.0'. Para más patrones, por favor refierase a la guía de usuario.",
"LABEL_FILTER": "Filtrar los recursos por labels.",
+ "NAME_FILTER_REGEX": "Filter the name of the resource with a regular expression that has to match the full name. Leave empty to match all. 'library/.*' only matches resources under 'library'.",
+ "TAG_FILTER_REGEX": "Filter the tag/version part of the resources with a regular expression that has to match the full tag. Leave empty to match all. 'v\\d+\\.\\d+' only matches tags such as 'v1.0'. Untagged artifacts never match a regular expression.",
+ "LABEL_FILTER_REGEX": "Filter the resources according to labels, every expression has to match a full label name.",
</file context>
There was a problem hiding this comment.
Not changing this, deliberately. German was hand-translated by a German speaker on the team; the other locales carry the English string rather than machine translations nobody here can review. These files are maintained by their language communities upstream, and an unreviewed translation is harder to correct later than an obvious English placeholder, because it looks finished. The key is in place, so a translator only has to fill in the value.
Side note: the tooltip text this thread quotes has since changed. TAG_FILTER_REGEX no longer says untagged artifacts never match a regular expression, because that behaviour was corrected: an untagged artifact is kept exactly when the pattern matches the empty tag, the same rule doublestar follows.
| decoration: | ||
| type: string | ||
| description: 'matches or excludes the result' | ||
| kind: |
There was a problem hiding this comment.
P3: The new kind field is documented as accepting exactly doublestar or regex, but the spec defines no enum. Backend validation in model.Filter.Validate rejects other values case-sensitively at write time, so downstream consumers (harbor-cli, terraform provider, SDKs generated from this spec) get no client-side constraint and can easily send a value that the server silently 400s. Add the enum to make the allowed set explicit and self-documenting.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At api/v2.0/swagger.yaml, line 7751:
<comment>The new `kind` field is documented as accepting exactly `doublestar` or `regex`, but the spec defines no `enum`. Backend validation in `model.Filter.Validate` rejects other values case-sensitively at write time, so downstream consumers (harbor-cli, terraform provider, SDKs generated from this spec) get no client-side constraint and can easily send a value that the server silently 400s. Add the enum to make the allowed set explicit and self-documenting.</comment>
<file context>
@@ -7748,6 +7748,12 @@ definitions:
decoration:
type: string
description: 'matches or excludes the result'
+ kind:
+ type: string
+ description: |-
</file context>
There was a problem hiding this comment.
Deliberate. An enum in the spec makes every future kind a breaking change for generated clients: an SDK built against today's spec rejects a value a newer server accepts, and the failure surfaces in the client rather than at the API boundary that owns the rule. The allowed values are named in the description, and the server validates them, returning 400 with the offending value.
This also matches the neighbouring feature: the retention and immutability selector kind is free-form in the same spec, so adding an enum on only the replication filter would make the two inconsistent.
There was a problem hiding this comment.
1 existing issue remains and 1 new issue found across 25 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/i18n/lang/fr-fr-lang.json">
<violation number="1" location="src/portal/src/i18n/lang/fr-fr-lang.json:59">
P3: The change to the French locale update leaves the TAG_FILTER_REGEX tooltip in English instead of translating it, so French-portal users still see an English description. The rest of this file is translated, and the PR's own de-de-lang.json translates this same key, so keeping fr-fr in English is an inconsistency. Translate the added sentence, e.g. "Un artefact sans tag n'est conservé que si l'expression correspond au tag vide, comme '.*' le fait."</violation>
</file>
Requires human review: Auto-approval blocked because this review re-detected 3 unresolved issues already reported by Cubic.
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
| "TAG_FILTER": "Filtrer la partie tag/version des ressources. Laissez vide ou utilisez '**' pour tout inclure. '1.0*' n'inclut que les ressources qui commencent par '1.0'. Pour plus de patterns, référez-vous au guide d'utilisation.", | ||
| "LABEL_FILTER": "Filtrer les ressources en fonction des labels.", | ||
| "NAME_FILTER_REGEX": "Filter the name of the resource with a regular expression that has to match the full name. Leave empty to match all. 'library/.*' only matches resources under 'library'.", | ||
| "TAG_FILTER_REGEX": "Filter the tag/version part of the resources with a regular expression that has to match the full tag. Leave empty to match all. 'v\\d+\\.\\d+' only matches tags such as 'v1.0'. An untagged artifact is kept only when the expression matches the empty tag, as '.*' does.", |
There was a problem hiding this comment.
P3: The change to the French locale update leaves the TAG_FILTER_REGEX tooltip in English instead of translating it, so French-portal users still see an English description. The rest of this file is translated, and the PR's own de-de-lang.json translates this same key, so keeping fr-fr in English is an inconsistency. Translate the added sentence, e.g. "Un artefact sans tag n'est conservé que si l'expression correspond au tag vide, comme '.*' le fait."
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 59:
<comment>The change to the French locale update leaves the TAG_FILTER_REGEX tooltip in English instead of translating it, so French-portal users still see an English description. The rest of this file is translated, and the PR's own de-de-lang.json translates this same key, so keeping fr-fr in English is an inconsistency. Translate the added sentence, e.g. "Un artefact sans tag n'est conservé que si l'expression correspond au tag vide, comme '.*' le fait."</comment>
<file context>
@@ -56,7 +56,7 @@
"LABEL_FILTER": "Filtrer les ressources en fonction des labels.",
"NAME_FILTER_REGEX": "Filter the name of the resource with a regular expression that has to match the full name. Leave empty to match all. 'library/.*' only matches resources under 'library'.",
- "TAG_FILTER_REGEX": "Filter the tag/version part of the resources with a regular expression that has to match the full tag. Leave empty to match all. 'v\\d+\\.\\d+' only matches tags such as 'v1.0'. Untagged artifacts never match a regular expression.",
+ "TAG_FILTER_REGEX": "Filter the tag/version part of the resources with a regular expression that has to match the full tag. Leave empty to match all. 'v\\d+\\.\\d+' only matches tags such as 'v1.0'. An untagged artifact is kept only when the expression matches the empty tag, as '.*' does.",
"LABEL_FILTER_REGEX": "Filter the resources according to labels, every expression has to match a full label name.",
"RESOURCE_FILTER": "Filtrer le type de ressources.",
</file context>
| "TAG_FILTER_REGEX": "Filter the tag/version part of the resources with a regular expression that has to match the full tag. Leave empty to match all. 'v\\d+\\.\\d+' only matches tags such as 'v1.0'. An untagged artifact is kept only when the expression matches the empty tag, as '.*' does.", | |
| "TAG_FILTER_REGEX": "Filtrer la partie tag/version des ressources avec une expression régulière qui doit correspondre à tout le tag. Laissez vide pour tout inclure. 'v\\d+\\.\\d+' ne correspond qu'aux tags comme 'v1.0'. Un artefact sans tag n'est conservé que si l'expression correspond au tag vide, comme '.*' le fait.", |
There was a problem hiding this comment.
Not changing this, deliberately. German was hand-translated by a German speaker on the team; the other locales carry the English string rather than machine translations nobody here can review. These files are maintained by their language communities upstream, and an unreviewed translation is harder to correct later than an obvious English placeholder, because it looks finished. The key is in place, so a translator only has to fill in the value.
Side note: the tooltip text this thread quotes has since changed. TAG_FILTER_REGEX no longer says untagged artifacts never match a regular expression, because that behaviour was corrected: an untagged artifact is kept exactly when the pattern matches the empty tag, the same rule doublestar follows.
5412926 to
01307f5
Compare
Adds an optional "kind" to the name, tag and label filters of a replication policy. It selects the pattern engine for the filter value: "doublestar", the default and the only behavior so far, or "regex", the anchored full string matching of the selector added in phase 1. The compile and anchor step is the one from src/lib/selector/selectors/regexp, so a pattern that would escape the anchoring, such as `foo)|(?:bar`, is rejected here as well, at write time. util.Matcher carries the kind to every place a filter pattern is evaluated, compiling a regex once per filter instead of once per candidate. The adapters that reverse a glob into an explicit repository or namespace list (IsSpecificPath, IsSpecificPathComponent) fall back to listing the catalog for a regex, since the glob syntax says nothing about a regular expression: doing otherwise would silently take "lib(a|b)" for a literal namespace. Two deliberate asymmetries with doublestar: an untagged artifact is never matched by running the expression against the empty string, and the gitlab adapter, which compares paths case insensitively, leaves a regex unfolded because lowercasing rewrites classes such as \D or [A-Z]. The stored filters already carry a "kind" field, which names the pre-1.10 repository/tag/label filter on entries without a type, so parseFilters only reads it as a pattern engine on typed entries. Signed-off-by: Vadim Bauer <vb@container-registry.com>
Each pattern filter of a replication rule (name, tag, label) gets a Doublestar / RegEx select next to its value, following the retention and immutability dialogs of phase 1. The resource filter picks a type rather than matching a pattern, so it has no engine. The engine is left out of the payload while it is the default, which keeps the policies of existing rules untouched until someone switches one to RegEx. The info tooltip of a filter follows the selected engine. The dialog grows to the large modal size: the filter row now holds four controls and no longer fits the medium one. Signed-off-by: Vadim Bauer <vb@container-registry.com>
src/lib/pattern already holds the kind literals and the doublestar/regex matching of the proxy cache repository filter (goharbor/harbor#23527), so it is where the engine belongs rather than in a selector package the replication filters would have to reach into. CompileRegex and ValidateRegex are the phase-1 compile and anchor step, cap included, moved verbatim: a pattern is compiled bare before it is wrapped, so that one which would escape the anchoring is rejected instead of silently matching on a prefix. Matcher adds the caching the selector had, so a filter applied to thousands of candidates compiles once, and makes it available to any caller that carries a kind. The regex selector of phase 1 now delegates: its Kind, MaxPatternLength, Compile and Validate are aliases of the shared ones, and it holds a Matcher instead of its own sync.Once. The replication filters and the adapters take the same Matcher, so src/pkg/reg no longer defines a second one. Match, ValidateKind and ValidateRepositoryFilter keep their current behavior and the proxy cache middleware is untouched: moving that path onto the cached Matcher, and onto a single doublestar behavior, is a follow-up. Signed-off-by: Vadim Bauer <vb@container-registry.com>
A replication tag filter has no untagged flag: an artifact without tags is kept exactly when the pattern matches the empty tag, which is why "**" keeps it and "v*" drops it. An anchored regex gives the same rule for free, ".*" matches the empty string and "v.*" does not, so the special case that refused to evaluate a regex against "" only made the two engines disagree on the same policy. The rule belongs to the retention and immutability selectors, where an untagged flag governs and running the expression against "" would bypass it. Nothing here does. Signed-off-by: Vadim Bauer <vb@container-registry.com>
The clrModalSize="lg" added with the engine select was inert: a rule in this component pinned div.modal-dialog to 32rem through ::ng-deep, which outranks Clarity's .modal-dialog.modal-lg on specificity. The dialog kept rendering at its old width and the three pattern filter inputs were clipped, the tag and label ones down to a barely usable sliver. The override governed nothing else. This component holds a single clr-modal and no child inside it renders a dialog of its own, so removing the rule lets the declared size apply and leaves other components untouched. The row width also had to grow. Clarity sets a 20px root, so the rem based widths of the row are a quarter wider than they look: the widest row now measures 531px against the 430px the row was given, which clipped its own content whatever the dialog did. 560px clears the content and still leaves about 60px inside the space the large modal offers next to the label column. Signed-off-by: Vadim Bauer <vb@container-registry.com>
The tag and label value inputs were 6rem, which fits a glob like "1.0*" but shows only a fragment of an expression: "^v\d+\.\d+\.\d+$" is already wider than the field. Both grow to 10rem, 200px at Clarity's 20px root, which holds that expression at less than two thirds of the field. The row grows with them. Those two rows carry a decoration select the name row does not, so their content reaches 611px, against the 642px the large modal leaves beside the label column; 620px covers the content and keeps the row inside the dialog. Matching the name field's 12rem would not fit, the row would want 651px. The class is defined in this component and Angular scopes it here, so the export-cve dialog that uses the same class name is unaffected. Signed-off-by: Vadim Bauer <vb@container-registry.com>
Four findings from the review on #753. An explicit kind of "doublestar" was echoed back in the response while the identical filter created without a kind had the field omitted, so a client that spells out the default saw a shape it never sent and read it as drift. Both spellings now collapse to the same absent field. Matcher.Match fell back to doublestar for any kind it did not recognise. No stored filter can carry another kind today, between write-time validation and the guard on the legacy kind field, but a pattern written for some other engine would have selected the wrong repositories rather than failing, so an unknown kind is now an error. The round-trip test asserted the kind survived storage but never the pattern itself, which is the part whose corruption would quietly change what a policy selects. TestCompileIsCached no longer tested caching once the compiled expression moved into the matcher: the selector never reassigns that pointer, so the assertion held whatever the cache did. The behaviour is covered by TestMatcherCompilesOnce in lib/pattern, which can see the compiled expression, so the test is dropped rather than left as a tautology. Signed-off-by: Vadim Bauer <vb@container-registry.com>
eb088b0 to
06a99e9
Compare
The filter value input stripped every whitespace character on each keystroke. That is right for a doublestar pattern, where whitespace carries no meaning and the strip forgives a pasted list like "a, b", but a space is a literal character in a regex: "^(v1|v2) rc$" and "[a-z ]+" lost the space as the user typed it, leaving a pattern that silently matched something else. The strip now applies to a row only while that row is on doublestar, matching the fix made for the retention and immutability dialogs. Signed-off-by: Vadim Bauer <vb@container-registry.com>
…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>
|
One process thing that I think matters more than anything in the diff: CI is not running on this PR. Every substantive workflow in So the green tick here means spellcheck passed. Roughly two-thirds of the stack's Go code has never been compiled or tested by CI, and this is the largest diff in the stack (47 files) touching the subsystem with the least unit coverage of real registry behaviour. Each phase needs re-targeting to On the codeThe engine work looks solid. The one thing I'd want documented before merge
Smaller
Not testedI did not run the Go suites on this head. Given CI runs none of them either, the most valuable next step is: re-target to Not a 2.15.9 candidate — feature, plus an adapter-wide signature refactor. |
PR 2 of the stack, base is phase 1 (#752). Proposal: goharbor/community#298.
Phase 1 added the
regexselector kind to retention and immutability. This phase brings the same engine to the replication filters.What changes
A replication filter gains an optional
kind, valid on thename,tagandlabelfilters and rejected on theresourceone, exactly likedecoration:{ "type": "name", "value": "library/(app|api)-.*", "kind": "regex" }kindis empty ordoublestarfor everything that exists today, so no stored policy changes meaning.One engine, in
src/lib/patternsrc/lib/patternalready held the kind literals and the doublestar/regex matching of the proxy cache repository filter (goharbor/harbor#23527), so the shared engine goes there rather than in a selector package the replication filters would have to reach into. This PR addsCompileRegex,ValidateRegexand a cachedMatchernext to it:CompileRegexis phase 1's compile-and-anchor step, cap included, moved verbatim. The pattern is compiled bare before it is wrapped, so one that would escape the anchoring is rejected rather than silently matching on a prefix:foo)|(?:baris invalid on its own, yet wrapping it produces a valid unanchored alternation.Matchercarries the caching the selector had — a filter applied to thousands of candidates compiles once, not once per candidate — and makes it available to any caller that has a kind.Kind,MaxPatternLength,CompileandValidateare aliases of the shared ones and it holds aMatcherinstead of its ownsync.Once.src/pkg/reguses the sameMatcher, so there is no second implementation anywhere.Match,ValidateKindandValidateRepositoryFilterkeep their behavior and the proxy cache middleware is untouched. Two differences remain between them and the new helpers, both left for the follow-up that moves the proxy onto the cachedMatcher:Matchcompiles twice per call and has no length cap, and it trims and pre-validates its doublestar patterns while the selectors and replication filters do not.Wiring
Matcherreaches every place a filter pattern is evaluated (pkg/reg/filter, and the aliacr, dockerhub, gitlab, tencentcr and volcenginecr adapters).util.IsSpecificPathForKind/IsSpecificPathComponentForKindrefuse to reverse aregexpattern into an explicit repository or namespace list. Glob syntax says nothing about a regular expression, so without this an adapter would takelib(a|b)for a literal namespace and fetch the wrong thing. All theIsSpecificPath*callers were converted, not only the ones that also callMatch: native, jfrog, githubcr and harbor/base derive repository or project lists the same way. githubcr only supports specific repository names, so a regex name filter is refused there.kindfield: on a pre-1.10 entry, one without atype, it names the old repository/tag/label filter.parseFilterstherefore reads it as a pattern engine only on typed entries.Untagged artifacts
A replication tag filter has no untagged flag, so the rule is the one doublestar already follows: an artifact without tags is kept exactly when the pattern matches the empty tag.
**keeps it,v*drops it, and an anchored regex behaves identically for free —.*matches the empty string,v.*does not. The engines agree on the same policy, which is what the proposal promises; the paired cases are inartifact_test.go.(The "never run the expression against an empty string" rule belongs to the retention and immutability selectors of phase 1, where an untagged flag governs and evaluating the pattern against
""would bypass it. Nothing in replication has that flag.)One asymmetry that stays: gitlab case folding
The gitlab adapter compares repository paths and tags case insensitively by lowercasing both sides. Lowercasing a regex would rewrite
\Dinto\dand[A-Z]into[a-z], so only doublestar patterns are folded.(?i)is available for the regex case.Portal
Each pattern filter row gets a Doublestar / RegEx select, i18n keys in all ten languages (German for
de-de, English elsewhere), and an info tooltip that follows the selected engine. The engine is left out of the payload while it is the default, so switching nothing changes nothing. The dialog moves to the large modal size: the filter row now carries four controls and no longer fits the old one. That needed two things beyond theclrModalSizebinding, both found by measuring a live render: a 2021 rule in this component pinneddiv.modal-dialogto 32rem through::ng-deepand outranked Clarity's.modal-lgon specificity, so the declared size was inert; and Clarity sets a 20px root, which makes the row's rem-based controls a quarter wider than they read, so the widest row measures 531px against the 430px the row had been given. The override is gone (this component has a singleclr-modaland nothing inside it renders a dialog of its own).The tag and label value inputs also grow from 6rem to 10rem: 6rem fits a glob like
1.0*but shows only a fragment of^v\d+\.\d+\.\d+$, which is the point of the feature. Those two rows carry a decoration select the name row does not, so their content reaches 611px against the 642px the large modal leaves beside the label column, and the row is 620px. Matching the name field's 12rem would not fit — the row would want 651px. The class is defined in this component and Angular scopes it here, so the export-cve dialog that uses the same class name is untouched.The value input strips whitespace only while a row is on doublestar: whitespace carries no meaning in a glob and the strip forgives a pasted
a, b, but a space is a literal character in a regex, so^(v1|v2) rc$and[a-z ]+were being corrupted as the user typed. This matches the same fix in the retention and immutability dialogs on phase 1.Known and inherited:
.filterSelectis a fixed pixel width, as it has been since 2021. Below roughly an 800px viewport the Clarity modal goes fluid while the row does not, so it would clip — the same failure the previous 430px had, at a different breakpoint. Making the row fluid is a change to a shared dialog that this stack does not carry.Follow-up, deliberately not in this PR
regexp.Regexp.LiteralPrefix()could recover a literal prefix from a regex (library/.*→library/) and give the adapters back a narrowing hint, the wayIsSpecificPathdoes for globs. Today a regex name filter costs a full catalog walk, which is the same cost as a**glob, so this is an optimization rather than a correctness gap. It needs its own design and its own tests.Testing
go test ./lib/pattern/... ./lib/selector/... ./pkg/reg/... ./pkg/retention/... ./pkg/immutable/... ./controller/replication/... ./controller/immutable/... ./server/v2.0/handler/— pass, so the retention, immutability and proxy paths still behave after the refactorgolangci-lint run ./lib/pattern/... ./lib/selector/... ./pkg/reg/... ./controller/replication/... ./server/v2.0/handler/...— 0 issuestask test:lint:api— 0 errors (286 pre-existing warnings)ng build --aot,ng lintclean,ng test --include='**/replication/**/*.spec.ts'— 16 pass,bun run i18n:checkadds no missing key (23 pre-existing gaps inru-ru, unchanged)src/lib/pattern/regex_test.gocovers the shared engine: anchoring, the anchoring-escape pattern, the 512 character cap counted in characters rather than bytes, an inline flag staying scoped to the wrapped group, compile-once caching and the cached compile error. The rest coverFilter.Validate(kind combinations,resource+ kind rejected, the anchoring-escape pattern, the 512 character cap), the repository and artifact filters underregex(anchoring, excludes, untagged in both engines, labels), a gitlab fetch driven by regex name and tag filters,IsSpecificPath*refusing a regex, the handler conversion, and the round trip ofkindthrough the stored policy including the legacy-kindcase.src/server/v2.0/handler/replication_test.gowas already unformatted onmain; since this PR adds a case to it, the file is gofmt'd here, which is most of its diff.Release Notes
Replication filters can now be written as regular expressions. Each name, tag and label filter of a replication rule carries a pattern engine, Doublestar (the default, unchanged) or RegEx, selectable in the rule dialog and set through the
kindfield of the API. A regular expression has to match the whole repository name, tag or label, and is validated when the rule is saved.