Skip to content

Commit

Permalink
Add YQ-evaluating remediator
Browse files Browse the repository at this point in the history
In order to be able to change YAML files such as github workflows safely and
with minimal amount of changes, we need to add a new remediation
function in addition to the put-a-content-somewhere and call-frizbee
ones we have now.

I chose to add one based on `libyq` which does a decent job at retaining
comments and general YAML structure.

Fixes: #4815
  • Loading branch information
jhrozek committed Oct 28, 2024
1 parent 2e41532 commit c20ed0f
Show file tree
Hide file tree
Showing 7 changed files with 578 additions and 0 deletions.
10 changes: 10 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ require (
github.com/itchyny/gojq v0.12.16
github.com/lib/pq v1.10.9
github.com/microcosm-cc/bluemonday v1.0.27
github.com/mikefarah/yq/v4 v4.44.3
github.com/motemen/go-loghttp v0.0.0-20231107055348-29ae44b293f4
github.com/nats-io/nats-server/v2 v2.10.22
github.com/nats-io/nats.go v1.37.0
Expand Down Expand Up @@ -89,6 +90,7 @@ require (
google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38
google.golang.org/grpc v1.67.1
google.golang.org/protobuf v1.35.1
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/apimachinery v0.31.2
Expand All @@ -105,7 +107,9 @@ require (
github.com/Masterminds/squirrel v1.5.4 // indirect
github.com/MicahParks/keyfunc/v2 v2.1.0 // indirect
github.com/Microsoft/hcsshim v0.12.8 // indirect
github.com/a8m/envsubst v1.4.2 // indirect
github.com/alecthomas/chroma/v2 v2.14.0 // indirect
github.com/alecthomas/participle/v2 v2.1.1 // indirect
github.com/anchore/go-struct-converter v0.0.0-20240925125616-a0883641c664 // indirect
github.com/anderseknert/roast v0.4.2 // indirect
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
Expand Down Expand Up @@ -139,19 +143,23 @@ require (
github.com/coreos/go-semver v0.3.1 // indirect
github.com/deckarep/golang-set/v2 v2.6.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/dlclark/regexp2 v1.11.4 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-events v0.0.0-20241017185736-969db071c880 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/elliotchance/orderedmap v1.6.0 // indirect
github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/erikvarga/go-rpmdb v0.0.0-20240208180226-b97e041ef9af // indirect
github.com/fatih/color v1.17.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-chi/chi/v5 v5.1.0 // indirect
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/goccy/go-yaml v1.12.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/google/go-github/v61 v61.0.0 // indirect
Expand All @@ -165,6 +173,7 @@ require (
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx/v5 v5.7.0 // indirect
github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/jinzhu/copier v0.4.0 // indirect
github.com/jon-whit/go-grpc-prometheus v1.4.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/karlseguin/ccache/v3 v3.0.5 // indirect
Expand Down Expand Up @@ -227,6 +236,7 @@ require (
github.com/theupdateframework/go-tuf/v2 v2.0.2 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/yuin/goldmark-emoji v1.0.4 // indirect
github.com/yuin/gopher-lua v1.1.1 // indirect
github.com/zitadel/logging v0.6.1 // indirect
github.com/zitadel/schema v1.3.0 // indirect
go.etcd.io/bbolt v1.3.11 // indirect
Expand Down
20 changes: 20 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,16 @@ github.com/ThreeDotsLabs/watermill v1.3.7 h1:NV0PSTmuACVEOV4dMxRnmGXrmbz8U83LENO
github.com/ThreeDotsLabs/watermill v1.3.7/go.mod h1:lBnrLbxOjeMRgcJbv+UiZr8Ylz8RkJ4m6i/VN/Nk+to=
github.com/ThreeDotsLabs/watermill-sql/v3 v3.1.0 h1:g4uE5Nm3Z6LVB3m+uMgHlN4ne4bDpwf3RJmXYRgMv94=
github.com/ThreeDotsLabs/watermill-sql/v3 v3.1.0/go.mod h1:G8/otZYWLTCeYL2Ww3ujQ7gQ/3+jw5Bj0UtyKn7bBjA=
github.com/a8m/envsubst v1.4.2 h1:4yWIHXOLEJHQEFd4UjrWDrYeYlV7ncFWJOCBRLOZHQg=
github.com/a8m/envsubst v1.4.2/go.mod h1:MVUTQNGQ3tsjOOtKCNd+fl8RzhsXcDvvAEzkhGtlsbY=
github.com/agnivade/levenshtein v1.2.0 h1:U9L4IOT0Y3i0TIlUIDJ7rVUziKi/zPbrJGaFrtYH3SY=
github.com/agnivade/levenshtein v1.2.0/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU=
github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE=
github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E=
github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I=
github.com/alecthomas/participle/v2 v2.1.1 h1:hrjKESvSqGHzRb4yW1ciisFJ4p3MGYih6icjJvbsmV8=
github.com/alecthomas/participle/v2 v2.1.1/go.mod h1:Y1+hAs8DHPmc3YUFzqllV+eSQ9ljPTk0ZkPMtEdAx2c=
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
Expand Down Expand Up @@ -310,6 +314,8 @@ github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 h1:ge14PCmCvPjpMQM
github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc=
github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 h1:lxmTCgmHE1GUYL7P0MlNa00M67axePTq+9nBSGddR8I=
github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7/go.mod h1:GvWntX9qiTlOud0WkQ6ewFm0LPy5JUR1Xo0Ngbd1w6Y=
github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
Expand All @@ -332,6 +338,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
github.com/elliotchance/orderedmap v1.6.0 h1:xjn+kbbKXeDq6v9RVE+WYwRbYfAZKvlWfcJNxM8pvEw=
github.com/elliotchance/orderedmap v1.6.0/go.mod h1:wsDwEaX5jEoyhbs7x93zk2H/qv0zwuhg4inXhDkYqys=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
Expand Down Expand Up @@ -446,6 +454,8 @@ github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.12.0 h1:/1WHjnMsI1dlIBQutrvSMGZRQufVO3asrHfTwfACoPM=
github.com/goccy/go-yaml v1.12.0/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU=
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
Expand Down Expand Up @@ -656,6 +666,8 @@ github.com/jellydator/ttlcache/v3 v3.3.0 h1:BdoC9cE81qXfrxeb9eoJi9dWrdhSuwXMAnHT
github.com/jellydator/ttlcache/v3 v3.3.0/go.mod h1:bj2/e0l4jRnQdrnSTaGTsh4GSXvMjQcy41i7th0GVGw=
github.com/jeremija/gosubmit v0.2.7 h1:At0OhGCFGPXyjPYAsCchoBUhE099pcBXmsb4iZqROIc=
github.com/jeremija/gosubmit v0.2.7/go.mod h1:Ui+HS073lCFREXBbdfrJzMB57OI/bdxTiLtrDHHhFPI=
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmhodges/clock v1.2.0 h1:eq4kys+NI0PLngzaHEe7AmPT90XMGIEySD1JfV1PDIs=
Expand Down Expand Up @@ -745,6 +757,8 @@ github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwX
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs=
github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ=
github.com/mikefarah/yq/v4 v4.44.3 h1:3zxHntH67maSHr6ynCjM44htw7LZNINmTzYn3tM2t+I=
github.com/mikefarah/yq/v4 v4.44.3/go.mod h1:1pm9sJoyZLDql3OqgklvRCkD0XIIHMZV38jKZgAuxwY=
github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q=
github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
Expand Down Expand Up @@ -864,6 +878,8 @@ github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
Expand Down Expand Up @@ -1077,6 +1093,8 @@ github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark-emoji v1.0.4 h1:vCwMkPZSNefSUnOW2ZKRUjBSD5Ok3W78IXhGxxAEF90=
github.com/yuin/goldmark-emoji v1.0.4/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U=
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
github.com/zalando/go-keyring v0.2.3 h1:v9CUu9phlABObO4LPWycf+zwMG7nlbb3t/B5wa97yms=
github.com/zalando/go-keyring v0.2.3/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk=
github.com/zitadel/logging v0.6.1 h1:Vyzk1rl9Kq9RCevcpX6ujUaTYFX43aa4LkvV1TvUk+Y=
Expand Down Expand Up @@ -1480,6 +1498,8 @@ gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 h1:6D+BvnJ/j6e222UW8s2qTSe3wGBtvo0MbVQG/c5k8RE=
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473/go.mod h1:N1eN2tsCx0Ydtgjl4cqmbRCsY4/+z4cYDeqwZTk6zog=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ const (
frizbeePrBody = `This PR replaces tags with sha`
frizbeePrBodyWithExcludes = `This PR replaces tags with sha`

yqCommitTitle = "Replace dangerous PR events"
yqPrBody = `This PR replaces dangerous PR events`

actionWithTags = `
on:
workflow_call:
Expand Down Expand Up @@ -132,6 +135,14 @@ func frizbeePrRemWithExcludes(e []string) *pb.RuleType_Definition_Remediate_Pull
}
}

func yqPrRem() *pb.RuleType_Definition_Remediate_PullRequestRemediation {
return &pb.RuleType_Definition_Remediate_PullRequestRemediation{
Method: "minder.yq.evaluate",
Title: yqCommitTitle,
Body: yqPrBody,
}
}

type remediateArgs struct {
remAction models.ActionOpt
ent protoreflect.ProtoMessage
Expand Down Expand Up @@ -569,6 +580,30 @@ func TestPullRequestRemediate(t *testing.T) {
expectedErr: errors.ErrActionPending,
expectedMetadata: json.RawMessage(`{"pr_number":44}`),
},
{
name: "replace PR events with YQ",
newRemArgs: &newPullRequestRemediateArgs{
prRem: yqPrRem(),
actionType: TestActionTypeValid,
},
repoSetup: defaultMockRepoSetup,
mockSetup: func(t *testing.T, mockGitHub *mockghclient.MockGitHub) {
t.Helper()

happyPathMockSetup(mockGitHub)

mockGitHub.EXPECT().
CreatePullRequest(
gomock.Any(),
repoOwner, repoName,
yqCommitTitle, yqPrBody,
refFromBranch(branchBaseName(yqCommitTitle)), dflBranchTo).
Return(&github.PullRequest{Number: github.Int(45)}, nil)
},
remArgs: createTestRemArgs(),
expectedErr: errors.ErrActionPending,
expectedMetadata: json.RawMessage(`{"pr_number":45}`),
},
}

for _, tt := range tests {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ const (
minderContentModification = "minder.content"
// minderFrizbeeTagResolve replaces a github action tag with the appropriate checksum
minderFrizbeeTagResolve = "minder.actions.replace_tags_with_sha"
// minderYQEvaluate evaluates a yq expression
minderYQEvaluate = "minder.yq.evaluate"

// ContentBytesLimit is the maximum number of bytes for the content
ContentBytesLimit = 5120
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ func (mr modificationRegistry) register(name string, constructor modificationCon
func (mr modificationRegistry) registerBuiltIn() {
mr.register(minderContentModification, newContentModification)
mr.register(minderFrizbeeTagResolve, newFrizbeeTagResolveModification)
mr.register(minderYQEvaluate, newYqExecute)
}

func (mr modificationRegistry) getModification(
Expand Down
150 changes: 150 additions & 0 deletions internal/engine/actions/remediate/pull_request/types_yq.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// SPDX-FileCopyrightText: Copyright 2023 The Minder Authors
// SPDX-License-Identifier: Apache-2.0

package pull_request

import (
"bytes"
"context"
"encoding/json"
"fmt"

"github.com/go-git/go-billy/v5/util"
"github.com/go-git/go-git/v5/plumbing/filemode"
"github.com/mikefarah/yq/v4/pkg/yqlib"
"github.com/rs/zerolog"
gologging "gopkg.in/op/go-logging.v1"

"github.com/mindersec/minder/internal/engine/interfaces"
)

// unfortunately yqlib does seem to be using global variables...
func init() {
// setting the log level to critical pretty much silences the logging
gologging.SetLevel(gologging.CRITICAL, yqLibModule)
yqlib.InitExpressionParser()
}

type patternType string

const (
patternTypeGlob patternType = "glob"
)

const (
yqLibModule = "yq-lib"
)

var _ fsModifier = (*yqExecute)(nil)

type yqExecuteConfig struct {
Expression string `json:"expression"`
Patterns []struct {
Pattern string `json:"pattern"`
Type string `json:"type"`
}
}

type yqExecute struct {
fsChangeSet

config yqExecuteConfig
}

var _ modificationConstructor = newYqExecute

func newYqExecute(
params *modificationConstructorParams,
) (fsModifier, error) {

confMap := make(map[string]any)
if params.prCfg.GetParams() != nil {
confMap = params.prCfg.Params.AsMap()
}

rawConfig, err := json.Marshal(confMap)
if err != nil {
return nil, fmt.Errorf("cannot marshal config")
}

var conf yqExecuteConfig
err = json.Unmarshal(rawConfig, &conf)
if err != nil {
return nil, fmt.Errorf("cannot marshal config")
}

return &yqExecute{
fsChangeSet: fsChangeSet{
fs: params.bfs,
},

config: conf,
}, nil
}

func (yq *yqExecute) createFsModEntries(ctx context.Context, _ interfaces.ActionsParams) error {
matchingFiles := make([]string, 0)
for _, pattern := range yq.config.Patterns {
if pattern.Type != string(patternTypeGlob) {
zerolog.Ctx(ctx).
Warn().
Str("pattern.Type", pattern.Type).
Msg("unsupported pattern type")
continue
}

patternMatches, err := util.Glob(yq.fs, pattern.Pattern)
if err != nil {
return fmt.Errorf("cannot get matching files: %w", err)
}
matchingFiles = append(matchingFiles, patternMatches...)
}

for _, file := range matchingFiles {
newContent, err := yq.executeYq(file, yq.config.Expression)
if err != nil {
return fmt.Errorf("cannot execute yq: %w", err)
}
yq.entries = append(yq.entries, &fsEntry{
Path: file,
Content: newContent,
Mode: filemode.Regular.String(),
})
}

return nil
}

func (yq *yqExecute) modifyFs() ([]*fsEntry, error) {
err := yq.fsChangeSet.writeEntries()
if err != nil {
return nil, fmt.Errorf("cannot write entries: %w", err)
}
return yq.entries, nil
}

func (yq *yqExecute) executeYq(filename, expression string) (string, error) {
file, err := yq.fs.Open(filename)
if err != nil {
return "", fmt.Errorf("cannot read file: %w", err)
}

out := new(bytes.Buffer)
encoder := yqlib.NewYamlEncoder(yqlib.NewDefaultYamlPreferences())
printer := yqlib.NewPrinter(encoder, yqlib.NewSinglePrinterWriter(out))

expParser := yqlib.ExpressionParser
expressionNode, err := expParser.ParseExpression(expression)
if err != nil {
return "", fmt.Errorf("cannot parse expression: %w", err)
}

decoder := yqlib.NewYamlDecoder(yqlib.NewDefaultYamlPreferences())
parser := yqlib.NewStreamEvaluator()
_, err = parser.Evaluate(filename, file, expressionNode, printer, decoder)
if err != nil {
return "", fmt.Errorf("cannot evaluate expression: %w", err)
}

return out.String(), nil
}
Loading

0 comments on commit c20ed0f

Please sign in to comment.