Skip to content

Commit 22c9cd0

Browse files
author
Ren Goto (@ren510dev)
authored
fix: regex based tag replacement scheme (pipe-cd#5730)
* fix: regex based tag replacement scheme Signed-off-by: GotoRen <r0719en@pluslab.org> * fix: nits comment Signed-off-by: GotoRen <r0719en@pluslab.org> * fix: copy changes into main implementation Signed-off-by: GotoRen <r0719en@pluslab.org> --------- Signed-off-by: GotoRen <r0719en@pluslab.org>
1 parent b6c89b1 commit 22c9cd0

9 files changed

Lines changed: 123 additions & 23 deletions

File tree

docs/content/en/docs-dev/user-guide/configuration-reference.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ One of `yamlField` or `regex` is required.
209209
|-|-|-|-|
210210
| file | string | The relative path from the repository root to the file to be updated. | Yes |
211211
| yamlField | string | The yaml path to the field to be updated. It requires to start with `$` which represents the root element. e.g. `$.foo.bar[0].baz`. | No |
212-
| regex | string | The regex string that specify what should be replaced. The only first capturing group enclosed by `()` will be replaced with the new value. e.g. `host.xz/foo/bar:(v[0-9].[0-9].[0-9])` | No |
212+
| regex | string | The regex string that specify what should be replaced. The only first capturing group enclosed by `()` will be replaced with the new value. e.g. `host.xz/foo/bar:(v[0-9].[0-9].[0-9])`, `host.xz/foo/bar:([0-9a-z]+)` | No |
213213

214214
## CommitMatcher
215215

pkg/app/piped/eventwatcher/eventwatcher.go

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
package eventwatcher
1919

2020
import (
21+
"bytes"
2122
"context"
2223
"errors"
2324
"fmt"
@@ -789,9 +790,17 @@ func convertStr(value interface{}) (out string, err error) {
789790
return
790791
}
791792

792-
// modifyText returns a new text replacing all matches of the given regex with the newValue.
793-
// The only first capturing group enclosed by `()` will be replaced.
794-
// True as a second returned value means it's already up-to-date.
793+
// modifyText returns a modified text of the file contents by replacing the first capturing group
794+
// of all matches of the provided regular expression with the specified newValue.
795+
// The replacement is applied only to the part of each match that corresponds to the first capturing
796+
// group (the portion enclosed in the first pair of parentheses `()`).
797+
//
798+
// It returns the updated content, a boolean indicating whether the file was already up-to-date,
799+
// and an error if any issue occurs during reading, regular expression parsing, or matching.
800+
//
801+
// If no matches are found, an error is returned.
802+
// If all first capturing groups already equal newValue, the original content is returned,
803+
// the boolean is true, and no changes are applied.
795804
func modifyText(path, regexText, newValue string) ([]byte, bool, error) {
796805
content, err := os.ReadFile(path)
797806
if err != nil {
@@ -819,17 +828,32 @@ func modifyText(path, regexText, newValue string) ([]byte, bool, error) {
819828
if firstGroup == "" {
820829
return nil, false, fmt.Errorf("capturing group not found in the given regex")
821830
}
822-
subRegex, err := pool.Get(firstGroup)
823-
if err != nil {
824-
return nil, false, fmt.Errorf("failed to compile the first capturing group: %w", err)
825-
}
826831

827832
var touched, outDated bool
828833
newText := regex.ReplaceAllFunc(content, func(match []byte) []byte {
829834
touched = true
830-
outDated = string(subRegex.Find(match)) != newValue
831-
// Return text replacing the only first capturing group with the newValue.
832-
return subRegex.ReplaceAll(match, []byte(newValue))
835+
submatches := regex.FindSubmatchIndex(match)
836+
837+
if len(submatches) < 4 {
838+
return match
839+
}
840+
841+
groupStart, groupEnd := submatches[2], submatches[3]
842+
843+
// no update on the value
844+
if string(match[groupStart:groupEnd]) == newValue {
845+
return match
846+
}
847+
848+
outDated = true
849+
850+
var buf bytes.Buffer
851+
852+
buf.Write(match[:groupStart])
853+
buf.WriteString(newValue)
854+
buf.Write(match[groupEnd:])
855+
856+
return buf.Bytes()
833857
})
834858
if !touched {
835859
return nil, false, fmt.Errorf("the content of %s doesn't match %s", path, regexText)

pkg/app/piped/eventwatcher/eventwatcher_test.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ spec:
221221
wantErr: false,
222222
},
223223
{
224-
name: "replace text",
224+
name: "replace text for semantic version",
225225
path: "testdata/kustomization.yaml",
226226
regex: "newTag: (v[0-9].[0-9].[0-9])",
227227
newValue: "v0.2.0",
@@ -232,6 +232,26 @@ spec:
232232
wantUpToDate: false,
233233
wantErr: false,
234234
},
235+
{
236+
name: "replace text for non-semantic version (commit hash)",
237+
path: "testdata/kustomization2.yaml",
238+
regex: "newTag: ([0-9a-z]+)",
239+
newValue: "cba4321",
240+
want: []byte(`images:
241+
- name: gcr.io/pipecd/foo
242+
newTag: cba4321
243+
`),
244+
},
245+
{
246+
name: "replace text for non-semantic version (sha256)",
247+
path: "testdata/kustomization3.yaml",
248+
regex: "newTag: sha256:([0-9a-z]+)",
249+
newValue: "cba4321",
250+
want: []byte(`images:
251+
- name: gcr.io/pipecd/foo
252+
newTag: sha256:cba4321
253+
`),
254+
},
235255
}
236256
for _, tc := range testcases {
237257
t.Run(tc.name, func(t *testing.T) {
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
images:
2+
- name: gcr.io/pipecd/foo
3+
newTag: abc1234
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
images:
2+
- name: gcr.io/pipecd/foo
3+
newTag: sha256:abc1234

pkg/app/pipedv1/eventwatcher/eventwatcher.go

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
package eventwatcher
1919

2020
import (
21+
"bytes"
2122
"context"
2223
"errors"
2324
"fmt"
@@ -718,9 +719,17 @@ func convertStr(value interface{}) (out string, err error) {
718719
return
719720
}
720721

721-
// modifyText returns a new text replacing all matches of the given regex with the newValue.
722-
// The only first capturing group enclosed by `()` will be replaced.
723-
// True as a second returned value means it's already up-to-date.
722+
// modifyText returns a modified text of the file contents by replacing the first capturing group
723+
// of all matches of the provided regular expression with the specified newValue.
724+
// The replacement is applied only to the part of each match that corresponds to the first capturing
725+
// group (the portion enclosed in the first pair of parentheses `()`).
726+
//
727+
// It returns the updated content, a boolean indicating whether the file was already up-to-date,
728+
// and an error if any issue occurs during reading, regular expression parsing, or matching.
729+
//
730+
// If no matches are found, an error is returned.
731+
// If all first capturing groups already equal newValue, the original content is returned,
732+
// the boolean is true, and no changes are applied.
724733
func modifyText(path, regexText, newValue string) ([]byte, bool, error) {
725734
content, err := os.ReadFile(path)
726735
if err != nil {
@@ -748,17 +757,32 @@ func modifyText(path, regexText, newValue string) ([]byte, bool, error) {
748757
if firstGroup == "" {
749758
return nil, false, fmt.Errorf("capturing group not found in the given regex")
750759
}
751-
subRegex, err := pool.Get(firstGroup)
752-
if err != nil {
753-
return nil, false, fmt.Errorf("failed to compile the first capturing group: %w", err)
754-
}
755760

756761
var touched, outDated bool
757762
newText := regex.ReplaceAllFunc(content, func(match []byte) []byte {
758763
touched = true
759-
outDated = string(subRegex.Find(match)) != newValue
760-
// Return text replacing the only first capturing group with the newValue.
761-
return subRegex.ReplaceAll(match, []byte(newValue))
764+
submatches := regex.FindSubmatchIndex(match)
765+
766+
if len(submatches) < 4 {
767+
return match
768+
}
769+
770+
groupStart, groupEnd := submatches[2], submatches[3]
771+
772+
// no update on the value
773+
if string(match[groupStart:groupEnd]) == newValue {
774+
return match
775+
}
776+
777+
outDated = true
778+
779+
var buf bytes.Buffer
780+
781+
buf.Write(match[:groupStart])
782+
buf.WriteString(newValue)
783+
buf.Write(match[groupEnd:])
784+
785+
return buf.Bytes()
762786
})
763787
if !touched {
764788
return nil, false, fmt.Errorf("the content of %s doesn't match %s", path, regexText)

pkg/app/pipedv1/eventwatcher/eventwatcher_test.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ spec:
221221
wantErr: false,
222222
},
223223
{
224-
name: "replace text",
224+
name: "replace text for semantic version",
225225
path: "testdata/kustomization.yaml",
226226
regex: "newTag: (v[0-9].[0-9].[0-9])",
227227
newValue: "v0.2.0",
@@ -232,6 +232,26 @@ spec:
232232
wantUpToDate: false,
233233
wantErr: false,
234234
},
235+
{
236+
name: "replace text for non-semantic version (commit hash)",
237+
path: "testdata/kustomization2.yaml",
238+
regex: "newTag: ([0-9a-z]+)",
239+
newValue: "cba4321",
240+
want: []byte(`images:
241+
- name: gcr.io/pipecd/foo
242+
newTag: cba4321
243+
`),
244+
},
245+
{
246+
name: "replace text for non-semantic version (sha256)",
247+
path: "testdata/kustomization3.yaml",
248+
regex: "newTag: sha256:([0-9a-z]+)",
249+
newValue: "cba4321",
250+
want: []byte(`images:
251+
- name: gcr.io/pipecd/foo
252+
newTag: sha256:cba4321
253+
`),
254+
},
235255
}
236256
for _, tc := range testcases {
237257
t.Run(tc.name, func(t *testing.T) {
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
images:
2+
- name: gcr.io/pipecd/foo
3+
newTag: abc1234
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
images:
2+
- name: gcr.io/pipecd/foo
3+
newTag: sha256:abc1234

0 commit comments

Comments
 (0)