Skip to content

Commit 81eed2f

Browse files
committed
feat: adds command for scanning for deployment files
1 parent 65e4cf6 commit 81eed2f

File tree

7 files changed

+284
-17
lines changed

7 files changed

+284
-17
lines changed

.github/workflows/deploy.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ jobs:
3737
repository: ${{ inputs.deployment_repo }}
3838
token: ${{ secrets.token }}
3939
- name: Install updater CLI
40-
uses: input-output-hk/catalyst-ci/actions/install@allow-multiple-install
40+
uses: input-output-hk/catalyst-ci/actions/install@master
4141
with:
4242
asset: updater
4343
- name: Test
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
overrides:
2+
- app: jormungandr
3+
path: exporter.image.tag
4+
value: GITHUB_SHA

tools/updater/cmd/main.go

+65-14
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ package main
33
// cspell: words afero alecthomas cuelang cuecontext cuectx existingfile mheers Timoni nolint
44

55
import (
6+
"encoding/json"
7+
"fmt"
68
"os"
9+
"strings"
710

811
"cuelang.org/go/cue"
912
"cuelang.org/go/cue/cuecontext"
@@ -15,34 +18,82 @@ import (
1518
)
1619

1720
var cli struct {
21+
Scan scanCmd `cmd:"" help:"Scans a directory for deployment files."`
22+
Update updateCmd `cmd:"" help:"Overrides a target path in a CUE file with the given value."`
23+
}
24+
25+
type scanCmd struct {
26+
Path string `arg:"" help:"The path to scan for deployment files." type:"existingdir" required:"true"`
27+
Template []string `short:"t" help:"A key/value pair used to override constant values in deployment configurations."`
28+
}
29+
30+
func (c *scanCmd) Run() error {
31+
files, err := pkg.ScanForDeploymentFiles(c.Path, afero.NewOsFs())
32+
if err != nil {
33+
return err
34+
}
35+
36+
overrides := []pkg.OverrideConfig{}
37+
for _, file := range files {
38+
overrides = append(overrides, file.Overrides...)
39+
}
40+
41+
for _, template := range c.Template {
42+
pair := strings.Split(template, "=")
43+
pkg.ApplyTemplateValue(overrides, pair[0], pair[1])
44+
}
45+
46+
output, err := json.Marshal(overrides)
47+
if err != nil {
48+
return fmt.Errorf("failed to marshal overrides: %v", err)
49+
}
50+
51+
fmt.Print(string(output))
52+
53+
return nil
54+
}
55+
56+
type updateCmd struct {
1857
BundleFile string `type:"existingfile" short:"b" help:"Path to the Timoni bundle file to modify." required:"true"`
1958
Path string `arg:"" help:"A dot separated path to the value to override (must already exist)."`
2059
Value string `arg:"" help:"The value to override the value at the path with."`
2160
}
2261

23-
func main() {
24-
ctx := kong.Parse(&cli,
25-
kong.Name("updater"),
26-
kong.Description("A helper tool for modifying CUE files to override arbitrary values. Useful for updating Timoni bundles."))
27-
62+
func (c *updateCmd) Run() error {
2863
cuectx := cuecontext.New()
29-
v, err := pkg.ReadFile(cuectx, cli.BundleFile, afero.NewOsFs())
30-
ctx.FatalIfErrorf(err)
64+
v, err := pkg.ReadFile(cuectx, c.BundleFile, afero.NewOsFs())
65+
if err != nil {
66+
return err
67+
}
3168

32-
if !v.LookupPath(cue.ParsePath(cli.Path)).Exists() {
33-
ctx.Fatalf("path %q does not exist", cli.Path)
69+
if !v.LookupPath(cue.ParsePath(c.Path)).Exists() {
70+
return fmt.Errorf("path %q does not exist", c.Path)
3471
}
3572

36-
v, err = ch.Replace(v, cli.Path, cli.Value)
37-
ctx.FatalIfErrorf(err)
73+
v, err = ch.Replace(v, c.Path, c.Value)
74+
if err != nil {
75+
return err
76+
}
3877

3978
node := v.Syntax(cue.Final(), cue.Concrete(true), cue.Docs(true))
4079
src, err := format.Node(node)
41-
ctx.FatalIfErrorf(err)
80+
if err != nil {
81+
return err
82+
}
4283

43-
if err := os.WriteFile(cli.BundleFile, src, 0644); err != nil { //nolint:gosec
44-
ctx.Fatalf("failed to write file %q: %v", cli.BundleFile, err)
84+
if err := os.WriteFile(c.BundleFile, src, 0644); err != nil { //nolint:gosec
85+
return fmt.Errorf("failed to write file %q: %v", c.BundleFile, err)
4586
}
4687

88+
return nil
89+
}
90+
91+
func main() {
92+
ctx := kong.Parse(&cli,
93+
kong.Name("updater"),
94+
kong.Description("A helper tool for modifying CUE files to override arbitrary values. Useful for updating Timoni bundles."))
95+
96+
err := ctx.Run()
97+
ctx.FatalIfErrorf(err)
4798
os.Exit(0)
4899
}

tools/updater/go.mod

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
module github.com/input-output-hk/catalyst-ci/tools/updater
22

3-
go 1.20
3+
go 1.21
4+
5+
toolchain go1.21.5
46

57
require (
68
cuelang.org/go v0.7.0
@@ -9,6 +11,7 @@ require (
911
github.com/onsi/ginkgo/v2 v2.9.2
1012
github.com/onsi/gomega v1.27.5
1113
github.com/spf13/afero v1.11.0
14+
gopkg.in/yaml.v3 v3.0.1
1215
)
1316

1417
require (
@@ -26,5 +29,4 @@ require (
2629
golang.org/x/sys v0.15.0 // indirect
2730
golang.org/x/text v0.14.0 // indirect
2831
golang.org/x/tools v0.16.1 // indirect
29-
gopkg.in/yaml.v3 v3.0.1 // indirect
3032
)

tools/updater/go.sum

+19
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
cuelabs.dev/go/oci/ociregistry v0.0.0-20231103182354-93e78c079a13 h1:zkiIe8AxZ/kDjqQN+mDKc5BxoVJOqioSdqApjc+eB1I=
2+
cuelabs.dev/go/oci/ociregistry v0.0.0-20231103182354-93e78c079a13/go.mod h1:XGKYSMtsJWfqQYPwq51ZygxAPqpEUj/9bdg16iDPTAA=
23
cuelang.org/go v0.7.0 h1:gMztinxuKfJwMIxtboFsNc6s8AxwJGgsJV+3CuLffHI=
34
cuelang.org/go v0.7.0/go.mod h1:ix+3dM/bSpdG9xg6qpCgnJnpeLtciZu+O/rDbywoMII=
45
github.com/alecthomas/assert/v2 v2.1.0 h1:tbredtNcQnoSd3QBhQWI7QZ3XHOVkw1Moklp2ojoH/0=
6+
github.com/alecthomas/assert/v2 v2.1.0/go.mod h1:b/+1DI2Q6NckYi+3mXyH3wFb8qG37K/DuK80n7WefXA=
57
github.com/alecthomas/kong v0.8.1 h1:acZdn3m4lLRobeh3Zi2S2EpnXTd1mOL6U7xVml+vfkY=
68
github.com/alecthomas/kong v0.8.1/go.mod h1:n1iCIO2xS46oE8ZfYCNDqdR0b0wZNrXAIAqro/2132U=
79
github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE=
10+
github.com/alecthomas/repr v0.1.0/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
811
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
912
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
1013
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@@ -14,50 +17,64 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
1417
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
1518
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1619
github.com/emicklei/proto v1.10.0 h1:pDGyFRVV5RvV+nkBK9iy3q67FBy9Xa7vwrOTE+g5aGw=
20+
github.com/emicklei/proto v1.10.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
1721
github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U=
1822
github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
1923
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
2024
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
2125
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
26+
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
2227
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
2328
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
2429
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
30+
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
2531
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
2632
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
2733
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
2834
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
2935
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
3036
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
3137
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
38+
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
3239
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
3340
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
41+
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
3442
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
43+
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
3544
github.com/kubevela/workflow v0.6.0 h1:fYXviOYD5zqHs3J61tNbM4HZ85EcZlPm7Fyz8Q5o9Fk=
3645
github.com/kubevela/workflow v0.6.0/go.mod h1:sjLcYqKHKeCQ+w77gijoNILwIShJKnCU+e3q7ETtZGI=
3746
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
47+
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
3848
github.com/mheers/cue-helper v0.0.0-20231214081257-a325036f9a0d h1:JZsFf1WWJjBq7swih1HeWbmnh5uKZ2Ol2HSZ8o6kcK8=
3949
github.com/mheers/cue-helper v0.0.0-20231214081257-a325036f9a0d/go.mod h1:TvPPtiz/noXLe+NDP3XJLg8Y0ROt3m6rVlqUYlLuba0=
4050
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
51+
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
4152
github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de h1:D5x39vF5KCwKQaw+OC9ZPiLVHXz3UFw2+psEX+gYcto=
4253
github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de/go.mod h1:kJun4WP5gFuHZgRjZUWWuH1DTxCtxbHDOIJsudS8jzY=
4354
github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU=
4455
github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts=
4556
github.com/onsi/gomega v1.27.5 h1:T/X6I0RNFw/kTqgfkZPcQ5KU6vCnWNBGdtrIx2dpGeQ=
4657
github.com/onsi/gomega v1.27.5/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
4758
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
59+
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
4860
github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0=
61+
github.com/opencontainers/image-spec v1.1.0-rc4/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
4962
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
5063
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
5164
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
5265
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
5366
github.com/protocolbuffers/txtpbfmt v0.0.0-20230328191034-3462fbc510c0 h1:sadMIsgmHpEOGbUs6VtHBXRR1OHevnj7hLx9ZcdNGW4=
67+
github.com/protocolbuffers/txtpbfmt v0.0.0-20230328191034-3462fbc510c0/go.mod h1:jgxiZysxFPM+iWKwQwPR+y+Jvo54ARd4EisXxKYpB5c=
5468
github.com/rogpeppe/go-internal v1.11.1-0.20231026093722-fa6a31e0812c h1:fPpdjePK1atuOg28PXfNSqgwf9I/qD1Hlo39JFwKBXk=
69+
github.com/rogpeppe/go-internal v1.11.1-0.20231026093722-fa6a31e0812c/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
5570
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
5671
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
5772
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
5873
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
5974
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
75+
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
6076
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
77+
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
6178
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
6279
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
6380
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -68,8 +85,10 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
6885
golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA=
6986
golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
7087
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
88+
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
7189
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
7290
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
91+
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
7392
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
7493
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
7594
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

tools/updater/pkg/deployment.go

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package pkg
2+
3+
import (
4+
"fmt"
5+
"os"
6+
7+
"github.com/spf13/afero"
8+
"gopkg.in/yaml.v3"
9+
)
10+
11+
// Filename is the static name of a deployment configuration file.
12+
const Filename = "deployment.yml"
13+
14+
// DeploymentFile represents a deployment configuration file.
15+
type DeploymentFile struct {
16+
Overrides []OverrideConfig `json:"overrides" yaml:"overrides"`
17+
}
18+
19+
// OverrideConfig represents configuration for overriding a value in a CUE file.
20+
type OverrideConfig struct {
21+
App string `json:"app" yaml:"app"`
22+
Path string `json:"path" yaml:"path"`
23+
Value string `json:"value" yaml:"value"`
24+
}
25+
26+
// ScanForDeploymentFiles scans a directory for deployment configuration files.
27+
func ScanForDeploymentFiles(dir string, fs afero.Fs) ([]DeploymentFile, error) {
28+
29+
files := []DeploymentFile{}
30+
err := afero.Walk(fs, dir, func(path string, info os.FileInfo, err error) error {
31+
if err != nil {
32+
return err
33+
}
34+
35+
if info.IsDir() {
36+
return nil
37+
}
38+
39+
if info.Name() == Filename {
40+
contents, err := afero.ReadFile(fs, path)
41+
if err != nil {
42+
return fmt.Errorf("failed to read deployment file at %q: %v", path, err)
43+
}
44+
45+
deploymentFile := DeploymentFile{}
46+
if err := yaml.Unmarshal(contents, &deploymentFile); err != nil {
47+
// If we can't unmarshal the file, we assume it's not a deployment file and just log a warning.
48+
fmt.Fprintf(os.Stderr, "warning: failed to parse deployment file %q: %v", path, err)
49+
return nil
50+
}
51+
52+
files = append(files, deploymentFile)
53+
}
54+
55+
return nil
56+
})
57+
58+
if err != nil {
59+
return nil, err
60+
}
61+
62+
return files, nil
63+
}
64+
65+
// ApplyTemplateValue applies a template value to a list of overrides.
66+
func ApplyTemplateValue(overrides []OverrideConfig, key, value string) {
67+
for i, override := range overrides {
68+
if override.Value == key {
69+
overrides[i].Value = value
70+
}
71+
}
72+
}

0 commit comments

Comments
 (0)