feat: implement new and man for plugin modules
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing

- use extracted shared libraries
This commit is contained in:
Peter 2023-08-23 20:14:02 +02:00
parent f2ac6e1549
commit e60726ef9e
Signed by: prskr
GPG key ID: C1DB5D2E8DB512F9
71 changed files with 845 additions and 1954 deletions

View file

@ -4,7 +4,7 @@ buildr {
logs_dir = ".buildr/logs"
plugin "golang" {
url = "file:///home/baez/sources/Gitea/buildr/golang-plugin/golang.wasm"
checksum = "8c339abb97dd2caa8cb219b371563273e414ae9bb362d74b01b3d19dcc46f6ab"
url = "https://code.icb4dc0.de/api/packages/buildr/generic/golang_plugin/0.0.1/golang.wasm"
checksum = "2a183eb9c2c55b8ab678e9f176c60a477a2d0a67e3e8d25f42bc2113e0cdab8e"
}
}

View file

@ -3,22 +3,20 @@ kind: pipeline
type: docker
name: default
platform:
os: linux
arch: arm64
trigger:
event:
- push
- pull_request
- tag
refs:
include:
- refs/heads/main
- refs/pull/**
- refs/tags/**
steps:
- name: Generate protobuf files
image: bufbuild/buf
commands:
- buf generate
- name: Go generate
image: docker.io/golang:1.20-alpine
depends_on:
- Generate protobuf files
image: docker.io/golang:1.21-alpine
commands:
- go generate ./...
volumes:
@ -26,21 +24,22 @@ steps:
path: /go
- name: Validate
image: docker.io/golang:1.20-alpine
image: docker.io/golang:1.21-alpine
network_mode: host
privileged: true
environment:
GO111MODULE: "on"
GOPRIVATE: "code.icb4dc0.de/buildr/*"
CGO_ENABLED: "0"
DOCKER_HOST: tcp://localhost:2375
BUILDR_VAULT_PASSPHRASE:
from_secret: vault_password
depends_on:
- Generate protobuf files
- Go generate
commands:
- apk add -U --no-cache fuse
- CGO_ENABLED=0 go install -a -installsuffix=cgo -trimpath -ldflags="-s -w" .
- buildr --execution.log-to-stderr task buf_generate
- go install -trimpath -ldflags="-s -w" .
- buildr plugins update
- buildr --execution.log-to-stderr task go_generate
- buildr --execution.log-to-stderr task go_fmt
- buildr --execution.log-to-stderr task golangci_lint
@ -57,6 +56,10 @@ kind: pipeline
type: docker
name: housekeeping
platform:
os: linux
arch: arm64
trigger:
event:
- cron
@ -64,13 +67,8 @@ trigger:
- housekeeping
steps:
- name: Generate protobuf files
image: bufbuild/buf
commands:
- buf generate
- name: Go generate
image: docker.io/golang:1.20-alpine
image: docker.io/golang:1.21-alpine
commands:
- go generate ./...
volumes:
@ -96,4 +94,4 @@ steps:
volumes:
- name: go-cache
temp: { }
temp: { }

27
.editorconfig Normal file
View file

@ -0,0 +1,27 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
tab_width = 4
indent_style = space
insert_final_newline = false
max_line_length = 120
trim_trailing_whitespace = true
[*.go]
indent_style = tab
ij_smart_tabs = true
ij_go_GROUP_CURRENT_PROJECT_IMPORTS = true
ij_go_group_stdlib_imports = true
ij_go_import_sorting = goimports
ij_go_local_group_mode = project
ij_go_move_all_imports_in_one_declaration = true
ij_go_move_all_stdlib_imports_in_one_group = true
ij_go_remove_redundant_import_aliases = true
[*.{yml,yaml}]
indent_size = 2
tab_width = 2
insert_final_newline = true

View file

@ -1,7 +1,7 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="new plugin" type="GoApplicationRunConfiguration" factoryName="Go Application">
<module name="buildr" />
<working_directory value="$PROJECT_DIR$" />
<working_directory value="$PROJECT_DIR$/buildr" />
<parameters value="new task hello_world asdf" />
<EXTENSION ID="net.ashald.envfile">
<option name="IS_ENABLED" value="false" />

View file

@ -1,7 +1,7 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="task go_fmt" type="GoApplicationRunConfiguration" factoryName="Go Application">
<module name="buildr" />
<working_directory value="$PROJECT_DIR$" />
<working_directory value="$PROJECT_DIR$/buildr" />
<parameters value="--pprof.cpu-profile --pprof.out-file cpu.profile task go_fmt" />
<EXTENSION ID="net.ashald.envfile">
<option name="IS_ENABLED" value="false" />

22
go.mod
View file

@ -3,27 +3,28 @@ module code.icb4dc0.de/buildr/buildr
go 1.21
require (
code.icb4dc0.de/buildr/api v0.0.0-20230817151157-dbc0adad8f8b
code.icb4dc0.de/buildr/api v0.0.0-20230823165833-52d0bd9e9741
code.icb4dc0.de/buildr/common v0.0.0-20230823164419-5a757074eaa3
code.icb4dc0.de/prskr/go-pwgen v0.0.0-20230427131724-8ef26fd9749e
entgo.io/ent v0.12.3
github.com/Masterminds/sprig/v3 v3.2.3
github.com/charmbracelet/bubbles v0.16.1
github.com/charmbracelet/bubbletea v0.24.2
github.com/charmbracelet/glamour v0.6.0
github.com/charmbracelet/lipgloss v0.7.1
github.com/charmbracelet/lipgloss v0.8.0
github.com/docker/docker v24.0.5+incompatible
github.com/docker/go-connections v0.4.0
github.com/fsnotify/fsnotify v1.6.0
github.com/go-git/go-git/v5 v5.8.1
github.com/google/go-containerregistry v0.16.1
github.com/google/go-github/v53 v53.2.0
github.com/google/uuid v1.3.0
github.com/google/uuid v1.3.1
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/hanwen/go-fuse/v2 v2.3.0
github.com/hashicorp/go-getter v1.7.2
github.com/hashicorp/hcl/v2 v2.17.0
github.com/jinzhu/copier v0.3.5
github.com/jinzhu/copier v0.4.0
github.com/klauspost/compress v1.16.7
github.com/klauspost/pgzip v1.2.6
github.com/muesli/termenv v0.15.2
@ -34,7 +35,6 @@ require (
github.com/tetratelabs/wazero v1.4.0
github.com/zclconf/go-cty v1.13.2
golang.org/x/crypto v0.12.0
golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb
golang.org/x/sync v0.3.0
google.golang.org/grpc v1.57.0
google.golang.org/protobuf v1.31.0
@ -60,7 +60,7 @@ require (
github.com/agext/levenshtein v1.2.3 // indirect
github.com/alecthomas/chroma v0.10.0 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/aws/aws-sdk-go v1.44.325 // indirect
github.com/aws/aws-sdk-go v1.44.329 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
@ -142,13 +142,13 @@ require (
golang.org/x/term v0.11.0 // indirect
golang.org/x/text v0.12.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.12.0 // indirect
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/api v0.137.0 // indirect
google.golang.org/api v0.138.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230815205213-6bfd019c3878 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230815205213-6bfd019c3878 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230815205213-6bfd019c3878 // indirect
google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
lukechampine.com/uint128 v1.3.0 // indirect

157
go.sum
View file

@ -1,5 +1,3 @@
ariga.io/atlas v0.12.0 h1:jDfjxT3ppKhzqLS26lZv9ni7p9TVNrhy7SQquaF7bPs=
ariga.io/atlas v0.12.0/go.mod h1:+TR129FJZ5Lvzms6dvCeGWh1yR6hMvmXBhug4hrNIGk=
ariga.io/atlas v0.13.1 h1:oSkEYgI3qUnQZ6b6+teAEiIuizjBvkZ4YDbz0XWfCdQ=
ariga.io/atlas v0.13.1/go.mod h1:+TR129FJZ5Lvzms6dvCeGWh1yR6hMvmXBhug4hrNIGk=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
@ -34,8 +32,6 @@ cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w9
cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc=
cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU=
cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA=
cloud.google.com/go v0.110.2 h1:sdFPBr6xG9/wkBbfhmUz/JmZC7X6LavQgcrVINrKiVA=
cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw=
cloud.google.com/go v0.110.7 h1:rJyC7nWRg2jWGZ4wSJ5nY65GTdYJkg0cd/uXb+ACI6o=
cloud.google.com/go v0.110.7/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI=
cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw=
@ -74,8 +70,6 @@ cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz
cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU=
cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U=
cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU=
cloud.google.com/go/compute v1.20.0 h1:cUOcywWuowO9It2i1KX1lIb0HH7gLv6nENKuZGnlcSo=
cloud.google.com/go/compute v1.20.0/go.mod h1:kn5BhC++qUWR/AM3Dn21myV7QbgqejW04cAOrtppaQI=
cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY=
cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
@ -117,8 +111,6 @@ cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y97
cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc=
cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc=
cloud.google.com/go/iam v1.1.0 h1:67gSqaPukx7O8WLLHMa0PNs3EBGd2eE4d+psbO/CO94=
cloud.google.com/go/iam v1.1.0/go.mod h1:nxdHjaKfCr7fNYx/HJMM8LgiMugmveWlkatear5gVyk=
cloud.google.com/go/iam v1.1.2 h1:gacbrBdWcoVmGLozRuStX45YKvJtzIjJdAolzUs1sm4=
cloud.google.com/go/iam v1.1.2/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU=
cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic=
@ -181,8 +173,6 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc=
cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s=
cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM=
cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E=
cloud.google.com/go/storage v1.32.0 h1:5w6DxEGOnktmJHarxAOUywxVW9lbNWIzlzzUltG/3+o=
cloud.google.com/go/storage v1.32.0/go.mod h1:Hhh/dogNRGca7IWv1RC2YqEn0c0G77ctA/OxflYkiD8=
cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw=
@ -196,10 +186,10 @@ cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xX
cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg=
cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0=
cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M=
code.icb4dc0.de/buildr/api v0.0.0-20230816155529-748bd2ef327b h1:S7h+UltAjzuBQMlIkRk765DS5+Mq6G/nHWxOG7do/9U=
code.icb4dc0.de/buildr/api v0.0.0-20230816155529-748bd2ef327b/go.mod h1:iI2z7GsD9jIb1Cnsn6Nu4Iu/f5iPDAJ7WkGefsZosfw=
code.icb4dc0.de/buildr/api v0.0.0-20230817151157-dbc0adad8f8b h1:pZDHPJCMRw3UM1Jd0y7XnPZaDOzZ5du17EvW09mgb4g=
code.icb4dc0.de/buildr/api v0.0.0-20230817151157-dbc0adad8f8b/go.mod h1:Bxt+fw/9hH7/WESz3asYBIWPr81UlUMEleXJGTqX6ys=
code.icb4dc0.de/buildr/api v0.0.0-20230823165833-52d0bd9e9741 h1:uG2ECI+OT5oA5x2+G7upBhwKsYBS49xBIX+qlZrs9gw=
code.icb4dc0.de/buildr/api v0.0.0-20230823165833-52d0bd9e9741/go.mod h1:eCeIvmMgYp/04nar7GnBDORMdO4jHYQl/RMiNJhyGf8=
code.icb4dc0.de/buildr/common v0.0.0-20230823164419-5a757074eaa3 h1:jWQCCE56vhfNoOuJj4JlCEcfXUB/40iR8L59j5xeh+s=
code.icb4dc0.de/buildr/common v0.0.0-20230823164419-5a757074eaa3/go.mod h1:u0PEZjq0l3ICCUgARZObM6Hc0ZlqkcjK0PBEozhAdNQ=
code.icb4dc0.de/prskr/go-pwgen v0.0.0-20230427131724-8ef26fd9749e h1:N+3hdfeHRf/ndLjiZoet6h+9eZ2Zr5O/Ia5G7b3oQXU=
code.icb4dc0.de/prskr/go-pwgen v0.0.0-20230427131724-8ef26fd9749e/go.mod h1:1MCCxqZsOgfQzV8AR2ZAVRI36MEGF+teFium6Pf2HHU=
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
@ -210,13 +200,11 @@ entgo.io/ent v0.12.3/go.mod h1:AigGGx+tbrBBYHAzGOg8ND661E5cxx1Uiu5o/otJ6Yg=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
@ -226,8 +214,6 @@ github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/ProtonMail/go-crypto v0.0.0-20230528122434-6f98819771a1 h1:JMDGhoQvXNTqH6Y3MC0IUw6tcZvaUdujNqzK2HYWZc8=
github.com/ProtonMail/go-crypto v0.0.0-20230528122434-6f98819771a1/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 h1:KLq8BE0KwCL+mmXnjLWEAOYO+2l2AE4YMmqG1ZpZHBs=
github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ=
@ -244,12 +230,8 @@ github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkE
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
github.com/aws/aws-sdk-go v1.44.280 h1:UYl/yxhDxP8naok6ftWyQ9/9ZzNwjC9dvEs/j8BkGhw=
github.com/aws/aws-sdk-go v1.44.280/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aws/aws-sdk-go v1.44.324 h1:/uja9PtgeeqrZCPOJTenjMLNpciIMuzaRKooq+erG4A=
github.com/aws/aws-sdk-go v1.44.324/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aws/aws-sdk-go v1.44.325 h1:jF/L99fJSq/BfiLmUOflO/aM+LwcqBm0Fe/qTK5xxuI=
github.com/aws/aws-sdk-go v1.44.325/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aws/aws-sdk-go v1.44.329 h1:Rqy+wYI8h+iq+FphR59KKTsHR1Lz7YiwRqFzWa7xoYU=
github.com/aws/aws-sdk-go v1.44.329/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
@ -262,7 +244,6 @@ github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1U
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
@ -273,8 +254,8 @@ github.com/charmbracelet/bubbletea v0.24.2 h1:uaQIKx9Ai6Gdh5zpTbGiWpytMU+CfsPp06
github.com/charmbracelet/bubbletea v0.24.2/go.mod h1:XdrNrV4J8GiyshTtx3DNuYkR1FDaJmO3l2nejekbsgg=
github.com/charmbracelet/glamour v0.6.0 h1:wi8fse3Y7nfcabbbDuwolqTqMQPMnVPeZhDM273bISc=
github.com/charmbracelet/glamour v0.6.0/go.mod h1:taqWV4swIMMbWALc0m7AfE9JkPSU8om2538k9ITBxOc=
github.com/charmbracelet/lipgloss v0.7.1 h1:17WMwi7N1b1rVWOjMT+rCh7sQkvDU75B2hbZpc5Kc1E=
github.com/charmbracelet/lipgloss v0.7.1/go.mod h1:yG0k3giv8Qj8edTCbbg6AlQ5e8KNWpFujkNawKNhE2c=
github.com/charmbracelet/lipgloss v0.8.0 h1:IS00fk4XAHcf8uZKc3eHeMUTCxUH6NkaTrdyCQk84RU=
github.com/charmbracelet/lipgloss v0.8.0/go.mod h1:p4eYUZZJ/0oXTuCQKFF8mqyKCz0ja6y+7DniDDw5KKU=
github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
@ -303,18 +284,12 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/docker/cli v24.0.2+incompatible h1:QdqR7znue1mtkXIJ+ruQMGQhpw2JzMJLRXp6zpzF6tM=
github.com/docker/cli v24.0.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli v24.0.5+incompatible h1:WeBimjvS0eKdH4Ygx+ihVq1Q++xg36M/rMi4aXAvodc=
github.com/docker/cli v24.0.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v24.0.2+incompatible h1:eATx+oLz9WdNVkQrr0qjQ8HvRJ4bOOxfzEo8R+dA3cg=
github.com/docker/docker v24.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3smi0k/jho4HLeY=
github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
github.com/docker/docker-credential-helpers v0.8.0 h1:YQFtbBQb4VrpoPxhFuzEBPQ9E16qz5SpHLS+uswaCp8=
github.com/docker/docker-credential-helpers v0.8.0/go.mod h1:UGFXcuoQ5TxPiB54nHOZ32AWRqQdECoh/Mg0AlEYb40=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
@ -338,6 +313,8 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
@ -349,8 +326,6 @@ github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8
github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo=
github.com/go-git/go-git/v5 v5.7.0 h1:t9AudWVLmqzlo+4bqdf7GY+46SUuRsx59SboFxkq2aE=
github.com/go-git/go-git/v5 v5.7.0/go.mod h1:coJHKEOk5kUClpsNlXrUvPrDxY3w3gjHvhcZd8Fodw8=
github.com/go-git/go-git/v5 v5.8.1 h1:Zo79E4p7TRk0xoRgMq0RShiTHGKcKI4+DI6BfJc/Q+A=
github.com/go-git/go-git/v5 v5.8.1/go.mod h1:FHFuoD6yGz5OSKEBK+aWN9Oah0q54Jxl0abmj6GnqAo=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
@ -418,12 +393,8 @@ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-containerregistry v0.15.2 h1:MMkSh+tjSdnmJZO7ljvEqV1DjfekB6VUEAZgy3a+TQE=
github.com/google/go-containerregistry v0.15.2/go.mod h1:wWK+LnOv4jXMM23IT/F1wdYftGWGr47Is8CG+pmHK1Q=
github.com/google/go-containerregistry v0.16.1 h1:rUEt426sR6nyrL3gt+18ibRcvYpKYdpsa5ZW7MA08dQ=
github.com/google/go-containerregistry v0.16.1/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ=
github.com/google/go-github/v53 v53.0.0 h1:T1RyHbSnpHYnoF0ZYKiIPSgPtuJ8G6vgc0MKodXsQDQ=
github.com/google/go-github/v53 v53.0.0/go.mod h1:XhFRObz+m/l+UCm9b7KSIC3lT3NWSXGt7mOsAWEloao=
github.com/google/go-github/v53 v53.2.0 h1:wvz3FyF53v4BK+AsnvCmeNhf8AkTaeh2SoYu/XUvTtI=
github.com/google/go-github/v53 v53.2.0/go.mod h1:XhFRObz+m/l+UCm9b7KSIC3lT3NWSXGt7mOsAWEloao=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
@ -452,19 +423,16 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc=
github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
github.com/google/s2a-go v0.1.5 h1:8IYp3w9nysqv3JH+NJgXJzGbDHzLOTj43BmSkp+O7qg=
github.com/google/s2a-go v0.1.5/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg=
github.com/googleapis/enterprise-certificate-proxy v0.2.4 h1:uGy6JWR/uMIILU8wbf+OkstIrNiMjGpEIyhx8f6W7s4=
github.com/googleapis/enterprise-certificate-proxy v0.2.4/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
github.com/googleapis/enterprise-certificate-proxy v0.2.5 h1:UR4rDjcgpgEnqpIEvkiqTYKBCKLNmlge2eVjoZfySzM=
github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
@ -476,8 +444,6 @@ github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99
github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=
github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo=
github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY=
github.com/googleapis/gax-go/v2 v2.10.0 h1:ebSgKfMxynOdxw8QQuFOKMgomqeLGPqNLQox2bo42zg=
github.com/googleapis/gax-go/v2 v2.10.0/go.mod h1:4UOEnMCrxsSqQ940WnTiD6qJ63le2ev3xfyagutxiPw=
github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas=
github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU=
github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
@ -492,8 +458,6 @@ github.com/hanwen/go-fuse/v2 v2.3.0 h1:t5ivNIH2PK+zw4OBul/iJjsoG9K6kXo4nMDoBpciC
github.com/hanwen/go-fuse/v2 v2.3.0/go.mod h1:xKwi1cF7nXAOBCXujD5ie0ZKsxc8GGSA1rlMJc+8IJs=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-getter v1.7.1 h1:SWiSWN/42qdpR0MdhaOc/bLR48PLuP1ZQtYLRlM69uY=
github.com/hashicorp/go-getter v1.7.1/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744=
github.com/hashicorp/go-getter v1.7.2 h1:uJDtyXwEfalmp1PqdxuhZqrNkUyClZAhVeZYTArbqkg=
github.com/hashicorp/go-getter v1.7.2/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744=
github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=
@ -504,7 +468,6 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl/v2 v2.17.0 h1:z1XvSUyXd1HP10U4lrLg5e0JMVz6CPaJvAgxM0KNZVY=
github.com/hashicorp/hcl/v2 v2.17.0/go.mod h1:gJyW2PTShkJqQBKpAmPO3yxMxIuoXkOF2TpqXzrQyx4=
github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4=
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU=
github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
@ -517,8 +480,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg=
github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
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/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
@ -532,8 +495,6 @@ github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI=
github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
@ -564,7 +525,6 @@ github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+Ei
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
@ -573,11 +533,8 @@ github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM=
github.com/microcosm-cc/bluemonday v1.0.24 h1:NGQoPtwGVcbGkKfvyYk1yRqknzBuoMiUrO6R7uFTPlw=
github.com/microcosm-cc/bluemonday v1.0.24/go.mod h1:ArQySAMps0790cHSkdPEJ7bGkF2VePWH773hsJNSHf8=
github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg=
github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE=
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
@ -587,7 +544,6 @@ github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJ
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
@ -604,8 +560,6 @@ github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIf
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc=
github.com/muesli/termenv v0.15.1 h1:UzuTb/+hhlBugQz28rpzey4ZuKcZ03MeKsoG7IJZIxs=
github.com/muesli/termenv v0.15.1/go.mod h1:HeAQPTzpfs016yGtA4g00CsdYnVLJvxsS4ANqrZs2sQ=
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
@ -613,8 +567,6 @@ github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8=
github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0=
github.com/opencontainers/image-spec v1.1.0-rc4/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
@ -625,8 +577,6 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI=
github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@ -634,11 +584,8 @@ github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUo
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=
github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI=
github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
@ -652,21 +599,16 @@ github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncj
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/skeema/knownhosts v1.1.1 h1:MTk78x9FPgDFVFkDLTrsnnfCJl7g1C/nnKvePgrIngE=
github.com/skeema/knownhosts v1.1.1/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo=
github.com/skeema/knownhosts v1.2.0 h1:h9r9cf0+u7wSE+M183ZtMGgOJKiL96brpaz5ekfJCpM=
github.com/skeema/knownhosts v1.2.0/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
@ -689,16 +631,11 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tetratelabs/wazero v1.2.0 h1:I/8LMf4YkCZ3r2XaL9whhA0VMyAvF6QE+O7rco0DCeQ=
github.com/tetratelabs/wazero v1.2.0/go.mod h1:wYx2gNRg8/WihJfSDxA1TIL8H+GkfLYm+bIfbblu9VQ=
github.com/tetratelabs/wazero v1.4.0 h1:9/MirYvmkJ/zSUOygKY/ia3t+e+RqIZXKbylIby1WYk=
github.com/tetratelabs/wazero v1.4.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A=
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8=
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8=
github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RVck=
github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY=
github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts=
github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
@ -711,13 +648,8 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.5.2/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU=
github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.5.5 h1:IJznPe8wOzfIKETmMkd06F8nXkmlhaHqFRM9l1hAGsU=
github.com/yuin/goldmark v1.5.5/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.5.6 h1:COmQAWTCcGetChm3Ig7G/t8AFAN00t+o8Mt4cf7JpwA=
github.com/yuin/goldmark v1.5.6/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os=
github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ=
github.com/yuin/goldmark-emoji v1.0.2 h1:c/RgTShNgHTtc6xdz2KKI74jJr6rWi7FPgnP9GAsO5s=
github.com/yuin/goldmark-emoji v1.0.2/go.mod h1:RhP/RWpexdp+KHs7ghKnifRoIs/Bq4nDS7tRbCkOwKY=
@ -746,8 +678,6 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -760,9 +690,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 h1:EDuYyU/MkFXllv9QF9819VlI9a4tzGuCbhG0ExK9o1U=
golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb h1:mIKbk8weKhSeLH2GmUTrvx8CjkyJmnU1wFmg59CUjFA=
golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@ -790,11 +717,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -822,8 +746,6 @@ golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri
golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A=
golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8=
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU=
golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -841,8 +763,6 @@ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -908,22 +828,17 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0=
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -938,8 +853,6 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -1003,10 +916,8 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM=
golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss=
golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 h1:Vve/L0v7CXXuxUmaMGIEK/dEeq7uiqb5qBgQrZzIE7E=
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -1064,10 +975,8 @@ google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ
google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70=
google.golang.org/api v0.126.0 h1:q4GJq+cAdMAC7XP7njvQ4tvohGLiSlytuL4BQxbIZ+o=
google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw=
google.golang.org/api v0.137.0 h1:QrKX6uNvzJLr0Fd3vWVqcyrcmFoYi036VUAsZbiF4+s=
google.golang.org/api v0.137.0/go.mod h1:4xyob8CxC+0GChNBvEUAk8VBKNvYOTWM9T3v3UfRxuY=
google.golang.org/api v0.138.0 h1:K/tVp05MxNVbHShRw9m7e9VJGdagNeTdMzqPH7AUqr0=
google.golang.org/api v0.138.0/go.mod h1:4xyob8CxC+0GChNBvEUAk8VBKNvYOTWM9T3v3UfRxuY=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@ -1178,18 +1087,12 @@ google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqw
google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM=
google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM=
google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s=
google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc h1:8DyZCyvI8mE1IdLy/60bS+52xfymkE72wv1asokgtao=
google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64=
google.golang.org/genproto v0.0.0-20230815205213-6bfd019c3878 h1:Iveh6tGCJkHAjJgEqUQYGDGgbwmhjoAOz8kO/ajxefY=
google.golang.org/genproto v0.0.0-20230815205213-6bfd019c3878/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4=
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM=
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
google.golang.org/genproto/googleapis/api v0.0.0-20230815205213-6bfd019c3878 h1:WGq4lvB/mlicysM/dUT3SBvijH4D3sm/Ny1A4wmt2CI=
google.golang.org/genproto/googleapis/api v0.0.0-20230815205213-6bfd019c3878/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230815205213-6bfd019c3878 h1:lv6/DhyiFFGsmzxbsUUTOkN29II+zeWHxvT8Lpdxsv0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230815205213-6bfd019c3878/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M=
google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d h1:VBu5YqKPv6XiJ199exd8Br+Aetz+o08F+PLMnwJQHAY=
google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4=
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d h1:DoPTO70H+bcDXcd39vOqb2viZxgqeBeSGtZ55yZU4/Q=
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@ -1225,8 +1128,6 @@ google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu
google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag=
google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw=
google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
@ -1279,8 +1180,6 @@ lukechampine.com/uint128 v1.3.0 h1:cDdUVfRwDUDovz610ABgFD17nXD4/uDgVHl2sC3+sbo=
lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
modernc.org/cc/v3 v3.41.0 h1:QoR1Sn3YWlmA1T4vLaKZfawdVtSiGx8H+cEojbC7v1Q=
modernc.org/cc/v3 v3.41.0/go.mod h1:Ni4zjJYJ04CDOhG7dn640WGfwBzfE0ecX8TyMB0Fv0Y=
modernc.org/ccgo/v3 v3.16.14 h1:af6KNtFgsVmnDYrWk3PQCS9XT6BXe7o3ZFJKkIKvXNQ=
modernc.org/ccgo/v3 v3.16.14/go.mod h1:mPDSujUIaTNWQSG4eqKw+atqLOEbma6Ncsa94WbC9zo=
modernc.org/ccgo/v3 v3.16.15 h1:KbDR3ZAVU+wiLyMESPtbtE/Add4elztFyfsWoNTgxS0=
modernc.org/ccgo/v3 v3.16.15/go.mod h1:yT7B+/E2m43tmMOT51GMoM98/MtHIcQQSleGnddkUNI=
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
@ -1289,22 +1188,14 @@ modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/libc v1.24.1 h1:uvJSeCKL/AgzBo2yYIPPTy82v21KgGnizcGYfBHaNuM=
modernc.org/libc v1.24.1/go.mod h1:FmfO1RLrU3MHJfyi9eYYmZBfi/R+tqZ6+hQ3yQQUkak=
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.6.0 h1:i6mzavxrE9a30whzMfwf7XWVODx2r5OYXvU46cirX7o=
modernc.org/memory v1.6.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/memory v1.7.0 h1:2pXdbgdP5hIyDp2JqIwkHNZ1sAjEbh8GnRpcqFWBf7E=
modernc.org/memory v1.7.0/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM=
modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
modernc.org/sqlite v1.25.0 h1:AFweiwPNd/b3BoKnBOfFm+Y260guGMF+0UFk0savqeA=
modernc.org/sqlite v1.25.0/go.mod h1:FL3pVXie73rg3Rii6V/u5BoHlSoyeZeIgKZEgHARyCU=
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY=

View file

@ -97,7 +97,7 @@ type KnownModulesArgProvider interface {
}
type ManCommander interface {
DisplayModuleManual(pager Pager, cat modules.Category, name string) error
DisplayModuleManual(ctx context.Context, pager Pager, cat modules.Category, name string) error
DisplayModulesManual(pager Pager) error
}

View file

@ -194,17 +194,9 @@ func (a *App) Init(ctx context.Context) (err error) {
return err
}
var db *state.DB
if db, err = a.appCfg.InitState(ctx); err != nil {
return err
}
a.Collection, err = services.NewCollection(
services.WithTypeRegistry(registry),
services.WithIgnorer(ignorer),
services.WithDB(db),
services.WithStateStore(db.State),
services.WithCache(state.NewStateCache(a.appCfg.Cache.TTL, db.State)),
services.WithDockerClientFromEnv(ctx),
)
@ -281,15 +273,22 @@ func (a *App) RunModule(ctx context.Context, cat modules.Category, name string)
})
}
func (a *App) initBasic(context.Context) (err error) {
func (a *App) initBasic(ctx context.Context) (err error) {
var v *vault.Vault
if v, err = a.appCfg.InitVault(); err != nil {
return err
}
var db *state.DB
if db, err = a.appCfg.InitState(ctx); err != nil {
return err
}
return a.Collection.With(
services.WithVault(v),
services.WithDB(db),
services.WithStateStore(db.State),
services.WithCache(state.NewStateCache(a.appCfg.Cache.TTL, db.State)),
)
}
@ -318,7 +317,7 @@ func (a *App) initBuildRConfig(ctx context.Context) error {
_ = a.Collection.With(services.WithDiagnosticsWriter(parser.DiagsWriter()))
if err := buildrCfg.SetupDirectories(a.appCfg.BuildRDirectory, a.appCfg.RepoRoot); err != nil {
if err := buildrCfg.SetupDirectories(a.appCfg.BuildRDirectory, a.appCfg.RepoRoot, a.appCfg.Execution.CleanDirectories); err != nil {
return err
}

View file

@ -1,14 +1,16 @@
package cmd
import (
"errors"
"context"
"fmt"
"io"
"io/fs"
"log/slog"
"text/template"
"github.com/Masterminds/sprig/v3"
"log/slog"
v1 "code.icb4dc0.de/buildr/buildr/internal/rpc/v1"
"code.icb4dc0.de/buildr/buildr/internal/tmpl"
"code.icb4dc0.de/buildr/buildr/internal/services"
@ -48,7 +50,7 @@ type ManApp struct {
serviceAccess ManAppServiceAccess
}
func (m ManApp) DisplayModuleManual(pager Pager, cat modules.Category, name string) error {
func (m ManApp) DisplayModuleManual(ctx context.Context, pager Pager, cat modules.Category, name string) error {
mod, err := m.serviceAccess.TypeRegistry().Create(cat, name)
if err != nil {
return err
@ -58,14 +60,14 @@ func (m ManApp) DisplayModuleManual(pager Pager, cat modules.Category, name stri
if h, ok := mod.Unwrap().(modules.Helper); !ok {
slog.Info(
"Module has no help",
slog.String("category", cat.String()),
slog.String("category", v1.CategoryName(cat)),
slog.String("name", name),
)
return nil
} else if he, ok := h.Help(); ok {
} else if he, err := h.Help(ctx); err == nil {
help = he
} else {
return errors.New("module has no help")
return fmt.Errorf("failed to get module help: %w", err)
}
if err := m.templates.ExecuteTemplate(pager, "single-module-man.tmpl.md", help); err != nil {

View file

@ -2,11 +2,10 @@ package cmd
import (
"context"
"log/slog"
"code.icb4dc0.de/buildr/buildr/internal/rpc"
"code.icb4dc0.de/buildr/buildr/internal/services"
"log/slog"
)
var _ ServerCommander = (*ServerApp)(nil)

View file

@ -5,13 +5,12 @@ import (
"errors"
"fmt"
"io"
"log/slog"
"os"
"code.icb4dc0.de/prskr/go-pwgen"
"code.icb4dc0.de/buildr/buildr/internal/services"
"log/slog"
)
const defaultPassphraseFileMode os.FileMode = 0o600

View file

@ -39,7 +39,10 @@ type AppConfig struct {
State struct{ FilePath string }
Profiling profiling.Config
Cache struct{ TTL time.Duration }
Execution struct{ LogToStderr bool }
Execution struct {
LogToStderr bool
CleanDirectories bool
}
}
func (c *AppConfig) CollectRepoDetails(vcsInfo any) (repoDetails repo.Repo, err error) {
@ -100,6 +103,7 @@ func (c *AppConfig) InitPaths(from string) (err error) {
return nil
}
//nolint:nilnil // if state is empty we don't care
func (c *AppConfig) InitState(ctx context.Context) (*state.DB, error) {
if stateFilePath := c.State.FilePath; stateFilePath != "" {
return state.NewDB(ctx, c.State.FilePath)
@ -174,6 +178,13 @@ func (c *AppConfig) Flags() *flag.FlagSet {
"If output of commands should be piped to stdout",
)
flags.BoolVar(
&c.Execution.CleanDirectories,
"execution.clean-directories",
config.EnvOr("BUILDR_EXECUTION_CLEAN_DIRECTORIES", strconv.ParseBool, false),
"If ephemeral directories (currently out and logs) should be cleaned",
)
flags.StringVar(
&c.State.FilePath,
"state.file-path",

View file

@ -75,6 +75,7 @@ func (m *ManConfig) Pager(ctx context.Context, title string) (Pager, error) {
func ModuleManCommand(
category modules.Category,
cmder ManCommander,
initializer LevelInitializer,
argsProvider KnownModulesArgProvider,
manCfg *ManConfig,
) *cobra.Command {
@ -86,12 +87,16 @@ func ModuleManCommand(
Args: cobra.ExactArgs(1),
ValidArgsFunction: argsProvider.ValidModulesArgs,
RunE: func(cmd *cobra.Command, args []string) (err error) {
p, err := manCfg.Pager(cmd.Context(), fmt.Sprintf("Manual - %s/%s", category.String(), args[0]))
if err := initializer.InitAt(cmd.Context(), InitLevelBuildRConfig); err != nil {
return err
}
p, err := manCfg.Pager(cmd.Context(), fmt.Sprintf("Manual - %s/%s", v1.CategoryName(category), args[0]))
if err != nil {
return err
}
return cmder.DisplayModuleManual(p, category, args[0])
return cmder.DisplayModuleManual(cmd.Context(), p, category, args[0])
},
}
}
@ -108,6 +113,10 @@ func ManCmd(
SilenceUsage: true,
SilenceErrors: true,
RunE: func(cmd *cobra.Command, args []string) error {
if err := initializer.InitAt(cmd.Context(), InitLevelBuildRConfig); err != nil {
return err
}
p, err := manCfg.Pager(cmd.Context(), "Manual - modules")
if err != nil {
return err
@ -120,6 +129,7 @@ func ManCmd(
return ModuleManCommand(
c,
cmder,
initializer,
ModulesArgsProviderFor(initializer, registryAcc, c),
&manCfg,
)

View file

@ -6,12 +6,15 @@ import (
"github.com/spf13/cobra"
)
func PluginListCommand(cmder PluginCommander) *cobra.Command {
func PluginListCommand(cmder PluginCommander, initializer LevelInitializer) *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Short: "List plugins",
Aliases: []string{"ls", "dir"},
RunE: func(cmd *cobra.Command, args []string) error {
if err := initializer.InitAt(cmd.Context(), InitLevelBasic); err != nil {
return err
}
return cmder.ListPlugins(cmd.Context(), os.Stdout)
},
}
@ -42,7 +45,7 @@ func PluginsCommand(cmder PluginCommander, initializer LevelInitializer) *cobra.
SilenceErrors: true,
}
cmd.AddCommand(PluginListCommand(cmder), PluginUpdateCommand(cmder, initializer))
cmd.AddCommand(PluginListCommand(cmder, initializer), PluginUpdateCommand(cmder, initializer))
return cmd
}

View file

@ -1,10 +1,10 @@
package cmd
import (
"log/slog"
"runtime"
"github.com/spf13/cobra"
"log/slog"
)
var CurrentVersion = "development"

View file

@ -9,9 +9,9 @@ import (
const defaultDirectoryPermissions = 0o755
type PluginReference struct {
Checksum *string `hcl:"checksum,optional"`
Name string `hcl:",label"`
URL string `hcl:"url"`
Checksum *string `hcl:"checksum,optional"`
}
type Buildr struct {
@ -36,7 +36,7 @@ func (c Buildr) PluginURLs() (pluginUrls []*url.URL, err error) {
return pluginUrls, nil
}
func (c *Buildr) SetupDirectories(buildRDir, repoRoot string) error {
func (c *Buildr) SetupDirectories(buildRDir, repoRoot string, cleanDirectories bool) error {
if ucd, err := os.UserCacheDir(); err != nil {
return err
} else {
@ -63,7 +63,7 @@ func (c *Buildr) SetupDirectories(buildRDir, repoRoot string) error {
c.OutDirectory = filepath.Join(repoRoot, c.OutDirectory)
}
if err := createCleanDir(c.OutDirectory, true); err != nil {
if err := createCleanDir(c.OutDirectory, cleanDirectories); err != nil {
return err
}
@ -73,7 +73,7 @@ func (c *Buildr) SetupDirectories(buildRDir, repoRoot string) error {
c.LogsDirectory = filepath.Join(repoRoot, c.LogsDirectory)
}
if err := createCleanDir(c.LogsDirectory, true); err != nil {
if err := createCleanDir(c.LogsDirectory, cleanDirectories); err != nil {
return err
}

View file

@ -30,8 +30,9 @@ const defaultGRPCDialTimeout = 10 * time.Second
var (
_ Shutdowner = (*Orchestrator)(nil)
architectureMapping = map[string]string{
"amd64": "amd64",
"x86_64": "amd64",
"amd64": "amd64",
"x86_64": "amd64",
"aarch64": "arm64",
}
)
@ -93,29 +94,9 @@ func (o *Orchestrator) BuildRContainer(ctx context.Context, spec *BuildRContaine
_, buildrExecutableName := filepath.Split(buildrExecutablePath)
containerRepoRoot := path.Join("/", "work", spec.ID)
conSpec := ContainerSpec{
Image: spec.Image,
User: spec.User,
Privileged: spec.Privileged,
Capabilities: spec.Capabilities,
ExposedPorts: []string{"3000/tcp"},
Env: map[string]string{
"BUILDR_GRPC_SERVE_ADDRESS": "0.0.0.0:3000",
"BUILDR_REPO_ROOT": containerRepoRoot,
"BUILDR_STATE_FILE_PATH": "/tmp/buildr.state",
},
Entrypoint: []string{
path.Join("/opt/buildr/bin", buildrExecutableName),
},
Cmd: []string{
"serve",
"api",
},
Mounts: spec.Mounts,
}
containerRepoRoot, conSpec := spec.containerSpec(buildrExecutableName)
if err := o.createContainer(containerSetupCtx, &conSpec, con); err != nil {
if err := o.createContainer(containerSetupCtx, conSpec, con); err != nil {
return nil, nil, err
}

View file

@ -4,12 +4,12 @@ import (
"context"
"errors"
"fmt"
"log/slog"
"time"
"github.com/docker/docker/client"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"log/slog"
)
const containerAPICallingThreshold = 100 * time.Millisecond

View file

@ -1,6 +1,10 @@
package containers
import "github.com/docker/docker/api/types/mount"
import (
"path"
"github.com/docker/docker/api/types/mount"
)
type Capabilities struct {
Add []string
@ -21,6 +25,32 @@ type BuildRContainerSpec struct {
Privileged bool
}
func (s *BuildRContainerSpec) containerSpec(buildrExecutableName string) (containerRepoRoot string, spec *ContainerSpec) {
containerRepoRoot = path.Join("/", "work", s.ID)
spec = &ContainerSpec{
Image: s.Image,
User: s.User,
Privileged: s.Privileged,
Capabilities: s.Capabilities,
ExposedPorts: []string{"3000/tcp"},
Env: map[string]string{
"BUILDR_GRPC_SERVE_ADDRESS": "0.0.0.0:3000",
"BUILDR_REPO_ROOT": containerRepoRoot,
"BUILDR_STATE_FILE_PATH": "/tmp/buildr.state",
},
Entrypoint: []string{
path.Join("/opt/buildr/bin", buildrExecutableName),
},
Cmd: []string{
"serve",
"api",
},
Mounts: s.Mounts,
}
return
}
type ContainerSpec struct {
Env map[string]string
Image string

View file

@ -13,13 +13,16 @@ import (
"sync"
"time"
"code.icb4dc0.de/buildr/common/protocol"
"code.icb4dc0.de/buildr/buildr/internal/errs"
"code.icb4dc0.de/buildr/buildr/internal/protocol"
v1 "code.icb4dc0.de/buildr/buildr/internal/rpc/v1"
"code.icb4dc0.de/buildr/buildr/modules/state"
"github.com/klauspost/compress/s2"
rpcv1 "code.icb4dc0.de/buildr/api/generated/rpc/v1"
"code.icb4dc0.de/buildr/buildr/internal/containers"
"code.icb4dc0.de/buildr/buildr/internal/execution"
"code.icb4dc0.de/buildr/buildr/internal/ioutils"
@ -65,7 +68,7 @@ func (c *containerTask) doExecute(ctx context.Context, spec execution.Spec) (err
Default().
With(
slog.String("module_name", c.moduleWithMeta.Name()),
slog.String("module_type", c.moduleWithMeta.Category().String()),
slog.String("module_type", v1.CategoryName(c.moduleWithMeta.Category())),
)
if c.moduleWithMeta.ShouldSkip() {

View file

@ -3,11 +3,10 @@ package local
import (
"context"
"io"
"log/slog"
"code.icb4dc0.de/buildr/buildr/modules/state"
"log/slog"
"code.icb4dc0.de/buildr/buildr/internal/logging"
"code.icb4dc0.de/buildr/buildr/modules"
)

View file

@ -9,6 +9,7 @@ import (
"path/filepath"
"sync"
v1 "code.icb4dc0.de/buildr/buildr/internal/rpc/v1"
"code.icb4dc0.de/buildr/buildr/modules/state"
"golang.org/x/sync/errgroup"
@ -79,7 +80,7 @@ func (t *localTask) doExecute(ctx context.Context, spec execution.Spec) error {
}
if err := t.module.Execute(execCtx); err != nil {
return fmt.Errorf("failed to execute module %s/%s: %w", t.module.Category().String(), t.module.Name(), err)
return fmt.Errorf("failed to execute module %s/%s: %w", v1.CategoryName(t.module.Category()), t.module.Name(), err)
}
return nil
@ -149,7 +150,6 @@ func (t *localTask) executeIsolated(ctx context.Context, spec execution.Spec) er
workDir,
outDir,
)
if err != nil {
return err
}
@ -159,7 +159,7 @@ func (t *localTask) executeIsolated(ctx context.Context, spec execution.Spec) er
}()
if err = t.module.Execute(execCtx); err != nil {
return fmt.Errorf("failed to execute module %s/%s: %w", t.module.Category().String(), t.module.Name(), err)
return fmt.Errorf("failed to execute module %s/%s: %w", v1.CategoryName(t.module.Category()), t.module.Name(), err)
}
return nil
@ -186,7 +186,7 @@ func (t *localTask) executionContextFor(
WithLoggerFactory(func() *slog.Logger {
return slog.Default().With(
slog.String("module_name", m.Name()),
slog.String("module_type", m.Category().String()),
slog.String("module_type", v1.CategoryName(m.Category())),
)
}),
)

View file

@ -4,6 +4,7 @@ import (
"context"
"fmt"
v1 "code.icb4dc0.de/buildr/buildr/internal/rpc/v1"
"code.icb4dc0.de/buildr/buildr/modules"
)
@ -15,7 +16,7 @@ func NewPlanFor(
) (*Plan, error) {
target := repo.Module(moduleType, moduleName)
if target == nil {
return nil, fmt.Errorf("%w: %s/%s", modules.ErrNoSuchModule, moduleType, moduleName)
return nil, fmt.Errorf("%w: %s/%s", modules.ErrNoSuchModule, v1.CategoryName(moduleType), moduleName)
}
entryPoint, err := factory.TaskForModule(target.ID(), repo)

View file

@ -123,8 +123,8 @@ func parsingSpecOf(modType modules.Category, block GenericBlock, additionalVaria
}
type blockParsingSpec struct {
Block GenericBlock
AdditionalParsingVariables map[string]cty.Value
Block GenericBlock
Type modules.Category
}

View file

@ -9,13 +9,13 @@ import (
"github.com/zclconf/go-cty/cty"
)
type mappingCfg struct {
type MappingCfg struct {
MapAsObject bool
StructAsMap bool
}
//nolint:gocyclo // expected to be complex as entrypoint
func mapToCtyVal(val any, cfg mappingCfg) (cty.Value, error) {
func MapToCtyVal(val any, cfg MappingCfg) (cty.Value, error) {
if val == nil {
return cty.Value{}, nil
}
@ -53,7 +53,7 @@ func mapToCtyVal(val any, cfg mappingCfg) (cty.Value, error) {
case reflect.Float32, reflect.Float64:
return cty.NumberFloatVal(value.Float()), nil
case reflect.Pointer:
return mapToCtyVal(reflect.ValueOf(val).Elem().Interface(), cfg)
return MapToCtyVal(reflect.ValueOf(val).Elem().Interface(), cfg)
case reflect.Map:
return mapMap(t, value, cfg)
case reflect.Slice, reflect.Array:
@ -66,7 +66,7 @@ func mapToCtyVal(val any, cfg mappingCfg) (cty.Value, error) {
out := make([]cty.Value, 0, length)
for i := 0; i < length; i++ {
if mapped, err := mapToCtyVal(value.Index(i).Interface(), cfg); err != nil {
if mapped, err := MapToCtyVal(value.Index(i).Interface(), cfg); err != nil {
return cty.Value{}, err
} else {
out = append(out, mapped)
@ -77,18 +77,18 @@ func mapToCtyVal(val any, cfg mappingCfg) (cty.Value, error) {
case reflect.Struct:
return mapStruct(t, value, cfg)
default:
return cty.Value{}, fmt.Errorf("unmapped type %s", t.Kind())
return cty.NilVal, fmt.Errorf("unmapped type %s", t.Kind())
}
}
func mapStruct(t reflect.Type, val reflect.Value, cfg mappingCfg) (cty.Value, error) {
func mapStruct(t reflect.Type, val reflect.Value, cfg MappingCfg) (cty.Value, error) {
numField := t.NumField()
out := make(map[string]cty.Value, numField)
for i := 0; i < numField; i++ {
field := t.Field(i)
if field.IsExported() && !isNil(val.Field(i)) {
if mapped, err := mapToCtyVal(val.Field(i).Interface(), cfg); err != nil {
if mapped, err := MapToCtyVal(val.Field(i).Interface(), cfg); err != nil {
return cty.Value{}, err
} else if field.Anonymous && mapped.CanIterateElements() {
for key, val := range mapped.AsValueMap() {
@ -114,7 +114,7 @@ func mapStruct(t reflect.Type, val reflect.Value, cfg mappingCfg) (cty.Value, er
return cty.ObjectVal(out), nil
}
func mapMap(t reflect.Type, val reflect.Value, cfg mappingCfg) (cty.Value, error) {
func mapMap(t reflect.Type, val reflect.Value, cfg MappingCfg) (cty.Value, error) {
if val.Len() == 0 {
return cty.MapValEmpty(mapType(t.Elem())), nil
}
@ -126,7 +126,7 @@ func mapMap(t reflect.Type, val reflect.Value, cfg mappingCfg) (cty.Value, error
iter := val.MapRange()
for iter.Next() {
if mapped, err := mapToCtyVal(iter.Value().Interface(), cfg); err != nil {
if mapped, err := MapToCtyVal(iter.Value().Interface(), cfg); err != nil {
return cty.Value{}, err
} else {
out[iter.Key().String()] = mapped

View file

@ -6,6 +6,8 @@ import (
"reflect"
"github.com/hashicorp/hcl/v2/hclwrite"
"code.icb4dc0.de/buildr/common/hcl"
)
func (w *Writer[T]) marshalInto(val reflect.Value, block *hclwrite.Block) error {
@ -52,8 +54,8 @@ func (w *Writer[T]) marshalField(structField reflect.StructField, fieldVal refle
return nil
}
marshalCfg, err := marshalConfigOf(structField)
if err != nil && !errors.Is(err, errNoHclTag) {
marshalCfg, err := hcl.ExtractFieldMeta(structField, "hcl", "hcl_write")
if err != nil && !errors.Is(err, hcl.ErrNoHclTag) {
return err
}
@ -61,7 +63,7 @@ func (w *Writer[T]) marshalField(structField reflect.StructField, fieldVal refle
return nil
}
handleStruct := func(val reflect.Value, t elementType, name string) error {
handleStruct := func(val reflect.Value, t hcl.ElementType, name string) error {
var (
targetBlock *hclwrite.Block
needsAppending bool
@ -69,10 +71,10 @@ func (w *Writer[T]) marshalField(structField reflect.StructField, fieldVal refle
//nolint:exhaustive // not necessary here
switch t {
case elementTypeBlock:
case hcl.ElementTypeBlock:
targetBlock = hclwrite.NewBlock(name, discoverLabels(val.Type()))
needsAppending = true
case elementTypeRemain:
case hcl.ElementTypeRemain:
targetBlock = block
default:
return fmt.Errorf("undefined block creation behavior for type %s", marshalCfg.Type)
@ -92,22 +94,22 @@ func (w *Writer[T]) marshalField(structField reflect.StructField, fieldVal refle
switch fieldVal.Kind() {
case reflect.Map:
if isPrimitiveType(fieldVal.Type().Elem()) {
if v, err := mapToCtyVal(fieldVal, mappingCfg{}); err != nil {
if v, err := MapToCtyVal(fieldVal, MappingCfg{}); err != nil {
return err
} else {
block.Body().SetAttributeValue(marshalCfg.Name, v)
}
}
case reflect.Slice, reflect.Array:
if isPrimitiveType(fieldVal.Type().Elem()) || marshalCfg.Type == elementTypeAttribute {
if v, err := mapToCtyVal(fieldVal, mappingCfg{}); err != nil {
if isPrimitiveType(fieldVal.Type().Elem()) || marshalCfg.Type == hcl.ElementTypeAttribute {
if v, err := MapToCtyVal(fieldVal, MappingCfg{}); err != nil {
return err
} else {
block.Body().SetAttributeValue(marshalCfg.Name, v)
}
} else {
for i := 0; i < fieldVal.Len(); i++ {
if err := handleStruct(fieldVal.Index(i), elementTypeBlock, marshalCfg.Name); err != nil {
if err := handleStruct(fieldVal.Index(i), hcl.ElementTypeBlock, marshalCfg.Name); err != nil {
return err
}
}
@ -119,18 +121,18 @@ func (w *Writer[T]) marshalField(structField reflect.StructField, fieldVal refle
return w.marshalField(structField, fieldVal.Elem(), block)
case reflect.Struct:
if marshalCfg.Type != elementTypeBlock && marshalCfg.Type != elementTypeRemain {
if marshalCfg.Type != hcl.ElementTypeBlock && marshalCfg.Type != hcl.ElementTypeRemain {
return fmt.Errorf("field %s is a struct but not marked as block", structField.Name)
}
return handleStruct(fieldVal, marshalCfg.Type, marshalCfg.Name)
default:
if val, err := mapToCtyVal(fieldVal, mappingCfg{}); err == nil {
if val, err := MapToCtyVal(fieldVal, MappingCfg{}); err == nil {
//nolint:exhaustive // not necessary here
switch marshalCfg.Type {
case elementTypeAttribute:
case hcl.ElementTypeAttribute:
block.Body().SetAttributeValue(marshalCfg.Name, val)
case elementTypeLabel:
case hcl.ElementTypeLabel:
block.SetLabels(append(block.Labels(), val.AsString()))
}
}
@ -148,12 +150,12 @@ func discoverLabels(t reflect.Type) []string {
continue
}
marshalCfg, err := marshalConfigOf(structField)
if err != nil && !errors.Is(err, errNoHclTag) {
marshalCfg, err := hcl.ExtractFieldMeta(structField, "hcl", "hcl_write")
if err != nil && !errors.Is(err, hcl.ErrNoHclTag) {
continue
}
if marshalCfg.Type == elementTypeLabel {
if marshalCfg.Type == hcl.ElementTypeLabel {
labels = append(labels, marshalCfg.Name)
}
}

View file

@ -1,71 +0,0 @@
package hcl
import (
"errors"
"fmt"
"reflect"
"strings"
)
const (
elementTypeAttribute elementType = "attr"
elementTypeBlock elementType = "block"
elementTypeLabel elementType = "label"
elementTypeRemain elementType = "remain"
)
var errNoHclTag = errors.New("no hcl tag")
type elementType string
func marshalConfigOf(t reflect.StructField) (marshalConfig, error) {
hclTag, present := t.Tag.Lookup("hcl")
if !present {
return marshalConfig{}, errNoHclTag
}
cfg := marshalConfig{
Name: t.Name,
Type: elementTypeAttribute,
}
if hclWriteTag, present := t.Tag.Lookup("hcl_write"); present && hclWriteTag == "-" {
cfg.Ignore = true
return cfg, nil
}
split := strings.Split(hclTag, ",")
if len(split) > 0 && split[0] != "" {
cfg.Name = split[0]
}
if len(split) > 1 {
switch split[1] {
case "":
fallthrough
case "attr":
cfg.Type = elementTypeAttribute
case "optional":
cfg.Optional = true
case "label":
cfg.Type = elementTypeLabel
case "block":
cfg.Type = elementTypeBlock
case "remain":
cfg.Type = elementTypeRemain
default:
return marshalConfig{}, fmt.Errorf("unknown element type %q", split[1])
}
}
return cfg, nil
}
type marshalConfig struct {
Name string
Type elementType
Optional bool
Ignore bool
}

View file

@ -6,6 +6,7 @@ import (
"github.com/hashicorp/hcl/v2/hclwrite"
v1 "code.icb4dc0.de/buildr/buildr/internal/rpc/v1"
"code.icb4dc0.de/buildr/buildr/modules"
)
@ -39,7 +40,7 @@ type Writer[T modules.ModuleWithMeta] struct {
func (w *Writer[T]) Write(in T) error {
f := hclwrite.NewEmptyFile()
block := hclwrite.NewBlock(in.Category().String(), []string{in.Type(), in.Name()})
block := hclwrite.NewBlock(v1.CategoryName(in.Category()), []string{in.Type(), in.Name()})
if err := w.marshalInto(reflect.ValueOf(in), block); err != nil {
return err
}

View file

@ -1,52 +0,0 @@
package logging
import (
"context"
"strings"
"github.com/hashicorp/hcl/v2"
"log/slog"
)
func Diagnostics(diag hcl.Diagnostics, logger *slog.Logger) {
if !diag.HasErrors() {
return
}
if logger == nil {
logger = slog.Default()
}
for _, d := range diag {
var level slog.Level
switch d.Severity {
case hcl.DiagInvalid, hcl.DiagError:
level = slog.LevelError
case hcl.DiagWarning:
level = slog.LevelWarn
default:
level = slog.LevelInfo
}
logArgs := []any{
slog.String("detail", strings.ReplaceAll(d.Detail, "\"", "'")),
}
if d.Context != nil {
logArgs = append(logArgs,
slog.String("filename", d.Context.Filename),
slog.Int("start_line", d.Context.Start.Line),
slog.Int("start_column", d.Context.Start.Line),
slog.Int("end_line", d.Context.End.Line),
slog.Int("end_column", d.Context.End.Line),
)
}
logger.Log(
context.Background(),
level,
d.Summary,
logArgs...,
)
}
}

View file

@ -2,9 +2,8 @@ package logging
import (
"flag"
"os"
"log/slog"
"os"
)
func NewConfig() Config {

View file

@ -12,7 +12,6 @@ import (
"net/url"
"os"
"path/filepath"
"sync"
"code.icb4dc0.de/buildr/buildr/internal/config"
"code.icb4dc0.de/buildr/buildr/internal/slices"
@ -26,12 +25,12 @@ import (
"code.icb4dc0.de/buildr/buildr/modules/plugin"
rpcv1 "code.icb4dc0.de/buildr/api/generated/rpc/v1"
"code.icb4dc0.de/buildr/buildr/modules/state"
)
type Manager struct {
Downloader Downloader
lock sync.RWMutex
plugins state.Plugins
cacheDir string
}
@ -59,13 +58,25 @@ func (p *Manager) Register(ctx context.Context, registry *modules.TypeRegistry)
for _, module := range knownModules {
module := module
registry.RegisterModule(modules.ModuleFactoryFunc(func() modules.ModuleWithMeta {
return &modules.Metadata[plugin.Module]{
Module: plugin.Module{
PluginPayload: &plugin.PayloadFile{Path: registeredPlugin.LocalPath},
var (
defaultSpec = new(rpcv1.ModuleSpec)
mod = plugin.Module{
PluginPayload: &plugin.PayloadFile{
Path: registeredPlugin.LocalPath,
Checksum: registeredPlugin.Hash,
},
PluginCategory: module.Category,
PluginType: module.Type,
ModuleSpec: make(map[string]any),
},
}
)
if err := defaultSpec.UnmarshalVT(module.DefaultSpec); err == nil {
mod.SetModuleSpec(defaultSpec)
}
return &modules.Metadata[plugin.Module]{
Module: mod,
}
}))
}
@ -108,7 +119,7 @@ func (p *Manager) UpdatePlugins(ctx context.Context, refs ...config.PluginRefere
} else if inventory, err := plugin.DiscoverInventory(ctx, payload); err != nil {
return err
} else {
pluginModules := slices.Map(inventory.Modules, func(in *rpcv1.ModuleReference) state.PluginModule {
pluginModules := slices.Map(inventory.Specs, func(in *rpcv1.PluginInventory_InventorySpec) state.PluginModule {
return moduleReferenceToPluginModule(pluginID, in)
})
if err := p.plugins.UpsertModules(ctx, pluginModules); err != nil {
@ -209,17 +220,20 @@ func (p *Manager) validateKnownPlugin(knownPlugin state.Plugin, ref config.Plugi
return false, nil
}
func validateHashForFile(filePath string, hash []byte) (computedHash []byte, equal bool, err error) {
func validateHashForFile(filePath string, expectedHash []byte) (computedHash []byte, equal bool, err error) {
computedHash, _, err = hashFile(filePath, sha256.New())
if err != nil {
return nil, false, err
}
if !bytes.Equal(hash, computedHash) {
if !bytes.Equal(expectedHash, computedHash) {
return nil, false, nil
}
return computedHash, true, nil
}
func hashFile(filePath string, h hash.Hash) (hash []byte, payload []byte, err error) {
func hashFile(filePath string, h hash.Hash) (computedHash, payload []byte, err error) {
f, err := os.Open(filePath)
if err != nil {
return nil, nil, err
@ -239,10 +253,11 @@ func hashFile(filePath string, h hash.Hash) (hash []byte, payload []byte, err er
return h.Sum(nil), buf.Bytes(), nil
}
func moduleReferenceToPluginModule(pluginID uuid.UUID, in *rpcv1.ModuleReference) state.PluginModule {
func moduleReferenceToPluginModule(pluginID uuid.UUID, in *rpcv1.PluginInventory_InventorySpec) state.PluginModule {
return state.PluginModule{
PluginID: pluginID,
Type: in.ModuleType,
Category: in.ModuleCategory,
PluginID: pluginID,
Type: in.ModuleRef.ModuleType,
Category: in.ModuleRef.ModuleCategory,
DefaultSpec: in.EmptySpec,
}
}

View file

@ -1,63 +0,0 @@
package plugins_test
import (
"errors"
"testing"
"code.icb4dc0.de/buildr/buildr/modules/plugin"
)
func TestPluginModule_UnmarshalJSON(t *testing.T) {
t.Parallel()
type args struct {
data []byte
}
tests := []struct {
want func(m *plugin.Module) error
name string
args args
wantErr bool
}{
{
name: "Empty json",
args: args{
data: []byte(`{}`),
},
wantErr: false,
},
{
name: "Set type and category",
args: args{
data: []byte(`{"plugin_type": "test", "plugin_category": "task"}`),
},
want: func(m *plugin.Module) error {
if m.PluginCategory == "" {
return errors.New("category is empty")
}
if m.PluginType == "" {
return errors.New("type is empty")
}
return nil
},
wantErr: false,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
var m plugin.Module
if err := m.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr {
t.Errorf("UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
}
if tt.want != nil {
if err := tt.want(&m); err != nil {
t.Error(err)
}
}
})
}
}

View file

@ -1,11 +0,0 @@
package protocol
import rpcv1 "code.icb4dc0.de/buildr/api/generated/rpc/v1"
type SpecUnmarshaler interface {
UnmarshalModuleSpec(val *rpcv1.ModuleSpec) error
}
type SpecValueUnmarshaler interface {
UnmarshalSpecValue(val *rpcv1.ModuleSpec_Value) error
}

View file

@ -1,238 +0,0 @@
package protocol
import (
"errors"
"fmt"
"reflect"
"strings"
"golang.org/x/exp/constraints"
rpcv1 "code.icb4dc0.de/buildr/api/generated/rpc/v1"
)
var ErrExpectedStruct = errors.New("expected struct")
func Marshal(in any) (*rpcv1.ModuleSpec, error) {
cfg := marshalConfig{
TagName: "hcl",
}
val := reflect.ValueOf(in)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
if val.Kind() != reflect.Struct {
return nil, fmt.Errorf("%w: got %T", ErrExpectedStruct, in)
}
return cfg.marshal(val)
}
type marshalConfig struct {
TagName string
}
func (cfg marshalConfig) marshal(in reflect.Value) (*rpcv1.ModuleSpec, error) {
numField := in.NumField()
out := &rpcv1.ModuleSpec{
Values: make(map[string]*rpcv1.ModuleSpec_Value, numField),
}
// TODO consider tag for field name
inputType := in.Type()
for i := 0; i < numField; i++ {
structField := inputType.Field(i)
if !structField.IsExported() {
continue
}
fieldName := cfg.fieldName(structField)
out.Values[fieldName] = cfg.mapReflectValueToSpecValue(in.Field(i))
}
return out, nil
}
func (cfg marshalConfig) mapReflectValueToSpecValue(in reflect.Value) *rpcv1.ModuleSpec_Value {
switch in.Kind() {
case reflect.Map:
return cfg.mapValue(in)
case reflect.Bool:
return boolValue(in.Bool())
case reflect.String:
return stringValue(in.String())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return intValue(in.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return intValue(in.Uint())
case reflect.Float32, reflect.Float64:
return floatValue(in.Float())
case reflect.Struct:
return cfg.structValue(in)
case reflect.Slice, reflect.Array:
switch in.Type().Elem().Kind() {
case reflect.Bool:
return boolSliceValue(in)
case reflect.String:
return stringSliceValue(in)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return intSliceValue(in, reflect.Value.Int)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return intSliceValue(in, func(v reflect.Value) int64 {
return int64(v.Uint())
})
case reflect.Float32, reflect.Float64:
return floatSliceValue(in)
default:
return nil
}
default:
return nil
}
}
func (cfg marshalConfig) fieldName(field reflect.StructField) string {
tagVal, ok := field.Tag.Lookup(cfg.TagName)
if !ok {
return field.Name
}
split := strings.Split(tagVal, ",")
switch {
case len(split) == 0:
fallthrough
case split[0] == "":
return strings.ToLower(field.Name)
default:
return strings.ToLower(split[0])
}
}
func (cfg marshalConfig) mapValue(in reflect.Value) *rpcv1.ModuleSpec_Value {
result := &rpcv1.ModuleSpec_Value{
Type: rpcv1.ModuleSpec_ValueTypeMap,
ComplexValue: make(map[string]*rpcv1.ModuleSpec_Value, in.Len()),
}
iter := in.MapRange()
for iter.Next() {
result.ComplexValue[iter.Key().String()] = cfg.mapReflectValueToSpecValue(iter.Value())
}
return result
}
func (cfg marshalConfig) structValue(in reflect.Value) *rpcv1.ModuleSpec_Value {
inputType := in.Type()
numFields := inputType.NumField()
result := &rpcv1.ModuleSpec_Value{
Type: rpcv1.ModuleSpec_ValueTypeObject,
ComplexValue: make(map[string]*rpcv1.ModuleSpec_Value, numFields),
}
for i := 0; i < numFields; i++ {
structField := inputType.Field(i)
if !structField.IsExported() {
continue
}
result.ComplexValue[structField.Name] = cfg.mapReflectValueToSpecValue(in.Field(i))
}
return result
}
func boolValue(in bool) *rpcv1.ModuleSpec_Value {
return &rpcv1.ModuleSpec_Value{
Type: rpcv1.ModuleSpec_ValueTypeSingle,
SingleValue: &rpcv1.ModuleSpec_Value_BoolValue{
BoolValue: in,
},
}
}
func boolSliceValue(val reflect.Value) *rpcv1.ModuleSpec_Value {
result := &rpcv1.ModuleSpec_Value{
Type: rpcv1.ModuleSpec_ValueTypeBoolSlice,
BoolValues: make([]bool, 0, val.Len()),
}
for i := 0; i < val.Len(); i++ {
result.BoolValues = append(result.BoolValues, val.Index(i).Bool())
}
return result
}
func intValue[T constraints.Integer](in T) *rpcv1.ModuleSpec_Value {
return &rpcv1.ModuleSpec_Value{
Type: rpcv1.ModuleSpec_ValueTypeSingle,
SingleValue: &rpcv1.ModuleSpec_Value_IntValue{
IntValue: int64(in),
},
}
}
func intSliceValue(val reflect.Value, selector func(v reflect.Value) int64) *rpcv1.ModuleSpec_Value {
result := &rpcv1.ModuleSpec_Value{
Type: rpcv1.ModuleSpec_ValueTypeIntSlice,
IntValues: make([]int64, 0, val.Len()),
}
for i := 0; i < val.Len(); i++ {
result.IntValues = append(result.IntValues, selector(val.Index(i)))
}
return result
}
func floatValue[T constraints.Float](in T) *rpcv1.ModuleSpec_Value {
return &rpcv1.ModuleSpec_Value{
Type: rpcv1.ModuleSpec_ValueTypeSingle,
SingleValue: &rpcv1.ModuleSpec_Value_DoubleValue{
DoubleValue: float64(in),
},
}
}
func floatSliceValue(val reflect.Value) *rpcv1.ModuleSpec_Value {
result := &rpcv1.ModuleSpec_Value{
Type: rpcv1.ModuleSpec_ValueTypeDoubleSlice,
DoubleValues: make([]float64, 0, val.Len()),
}
for i := 0; i < val.Len(); i++ {
result.DoubleValues = append(result.DoubleValues, val.Index(i).Float())
}
return result
}
func stringValue(in string) *rpcv1.ModuleSpec_Value {
return &rpcv1.ModuleSpec_Value{
Type: rpcv1.ModuleSpec_ValueTypeSingle,
SingleValue: &rpcv1.ModuleSpec_Value_StringValue{
StringValue: in,
},
}
}
func stringSliceValue(val reflect.Value) *rpcv1.ModuleSpec_Value {
result := &rpcv1.ModuleSpec_Value{
Type: rpcv1.ModuleSpec_ValueTypeStringSlice,
StringValues: make([]string, 0, val.Len()),
}
for i := 0; i < val.Len(); i++ {
result.StringValues = append(result.StringValues, val.Index(i).String())
}
return result
}

View file

@ -1,220 +0,0 @@
package protocol_test
import (
"testing"
rpcv1 "code.icb4dc0.de/buildr/api/generated/rpc/v1"
"code.icb4dc0.de/buildr/buildr/internal/protocol"
)
func TestMarshal_Bool_Success(t *testing.T) {
input := struct {
IsDeleted bool
}{
IsDeleted: true,
}
spec, err := protocol.Marshal(input)
if err != nil {
t.Fatal(err)
}
nameVal, ok := spec.Values["IsDeleted"]
if !ok {
t.Fatal("IsDeleted not found")
}
if nameVal.Type != rpcv1.ModuleSpec_ValueTypeSingle {
t.Fatalf("Expected single value type, got %v", nameVal.Type)
}
if s, ok := nameVal.SingleValue.(*rpcv1.ModuleSpec_Value_BoolValue); !ok {
t.Fatalf("Expected string value, got %v", nameVal.SingleValue)
} else if !s.BoolValue {
t.Errorf("Expected bool value to be true, got %t", s.BoolValue)
}
}
func TestMarshal_BoolSlice_Success(t *testing.T) {
input := struct {
IsDeleted []bool
}{
IsDeleted: []bool{true},
}
spec, err := protocol.Marshal(input)
if err != nil {
t.Fatal(err)
}
nameVal, ok := spec.Values["IsDeleted"]
if !ok {
t.Fatal("IsDeleted not found")
}
if nameVal.Type != rpcv1.ModuleSpec_ValueTypeBoolSlice {
t.Fatalf("Expected bool slice value type, got %v", nameVal.Type)
}
if len(nameVal.BoolValues) < 1 {
t.Fatalf("Expected at least one bool value, got %d", len(nameVal.BoolValues))
}
if !nameVal.BoolValues[0] {
t.Errorf("Expected bool value to be true, got %t", nameVal.BoolValues[0])
}
}
func TestMarshal_Int_Success(t *testing.T) {
input := struct {
Age int
}{
Age: 42,
}
spec, err := protocol.Marshal(input)
if err != nil {
t.Fatal(err)
}
nameVal, ok := spec.Values["Age"]
if !ok {
t.Fatal("Age not found")
}
if nameVal.Type != rpcv1.ModuleSpec_ValueTypeSingle {
t.Fatalf("Expected single value type, got %v", nameVal.Type)
}
if s, ok := nameVal.SingleValue.(*rpcv1.ModuleSpec_Value_IntValue); !ok {
t.Fatalf("Expected string value, got %v", nameVal.SingleValue)
} else if s.IntValue != 42 {
t.Errorf("Expected int value to be 42, got %d", s.IntValue)
}
}
func TestMarshal_IntSlice_Success(t *testing.T) {
input := struct {
Ages []int
}{
Ages: []int{42},
}
spec, err := protocol.Marshal(input)
if err != nil {
t.Fatal(err)
}
nameVal, ok := spec.Values["Ages"]
if !ok {
t.Fatal("Ages not found")
}
if nameVal.Type != rpcv1.ModuleSpec_ValueTypeIntSlice {
t.Fatalf("Expected int slice value type, got %v", nameVal.Type)
}
if len(nameVal.IntValues) < 1 {
t.Fatalf("Expected at least one bool value, got %d", len(nameVal.BoolValues))
}
if nameVal.IntValues[0] != 42 {
t.Errorf("Expected int value to be 52, got %d", nameVal.IntValues[0])
}
}
func TestMarshal_StringField_Success(t *testing.T) {
input := struct {
Name string
}{
Name: "John Doe",
}
spec, err := protocol.Marshal(input)
if err != nil {
t.Fatal(err)
}
nameVal, ok := spec.Values["Name"]
if !ok {
t.Fatal("Name not found")
}
if nameVal.Type != rpcv1.ModuleSpec_ValueTypeSingle {
t.Fatalf("Expected single value type, got %v", nameVal.Type)
}
if s, ok := nameVal.SingleValue.(*rpcv1.ModuleSpec_Value_StringValue); !ok {
t.Fatalf("Expected string value, got %v", nameVal.SingleValue)
} else if s.StringValue != "John Doe" {
t.Errorf("Expected string value to be John Doe, got %s", s.StringValue)
}
}
func TestMarshal_Float64_Success(t *testing.T) {
input := struct {
Pi float64
}{
Pi: 3.14,
}
spec, err := protocol.Marshal(input)
if err != nil {
t.Fatal(err)
}
nameVal, ok := spec.Values["Pi"]
if !ok {
t.Fatal("Pi not found")
}
if nameVal.Type != rpcv1.ModuleSpec_ValueTypeSingle {
t.Fatalf("Expected single value type, got %v", nameVal.Type)
}
if s, ok := nameVal.SingleValue.(*rpcv1.ModuleSpec_Value_DoubleValue); !ok {
t.Fatalf("Expected double value, got %v", nameVal.SingleValue)
} else if s.DoubleValue-3.14 > 0.000001 {
t.Errorf("Expected double value to be 3.14, got %f", s.DoubleValue)
}
}
func TestMarshal_NestedStruct_Success(t *testing.T) {
type Address struct {
City string
}
input := struct {
Address Address
}{
Address: Address{
City: "New York",
},
}
spec, err := protocol.Marshal(input)
if err != nil {
t.Fatal(err)
}
addressVal, ok := spec.Values["Address"]
if !ok {
t.Fatal("Address not found")
}
if addressVal.Type != rpcv1.ModuleSpec_ValueTypeObject {
t.Fatalf("Expected object value type, got %v", addressVal.Type)
}
cityVal, ok := addressVal.ComplexValue["City"]
if !ok {
t.Fatal("City not found")
}
if cityVal.Type != rpcv1.ModuleSpec_ValueTypeSingle {
t.Fatalf("Expected single value type, got %v", cityVal.Type)
}
if cityVal.SingleValue.(*rpcv1.ModuleSpec_Value_StringValue).StringValue != "New York" {
t.Errorf("Expected string value to be New York, got %s", cityVal.SingleValue.(*rpcv1.ModuleSpec_Value_StringValue).StringValue)
}
}

View file

@ -1,195 +0,0 @@
package protocol
import (
"errors"
"fmt"
"reflect"
"strings"
"golang.org/x/exp/constraints"
rpcv1 "code.icb4dc0.de/buildr/api/generated/rpc/v1"
)
var (
ErrUnmatchingType = errors.New("field type does not match wire value")
)
func Unmarshal(input *rpcv1.ModuleSpec, into any) error {
if u, ok := into.(SpecUnmarshaler); ok {
return u.UnmarshalModuleSpec(input)
}
val := reflect.ValueOf(into)
switch val.Kind() {
case reflect.Ptr:
default:
return errors.New("into value must be a pointer")
}
return unmarshal(input.Values, val.Elem(), reflect.TypeOf(into).Elem())
}
func unmarshal(input map[string]*rpcv1.ModuleSpec_Value, into reflect.Value, intoType reflect.Type) error {
for i := 0; i < intoType.NumField(); i++ {
tf := intoType.Field(i)
if !tf.IsExported() {
continue
}
val, ok := input[strings.ToLower(tf.Name)]
if !ok {
continue
}
if into.Type().Kind() == reflect.Pointer {
if u, ok := into.Elem().Field(i).Interface().(SpecValueUnmarshaler); ok {
if err := u.UnmarshalSpecValue(val); err != nil {
return err
}
continue
}
} else {
if u, ok := into.Field(i).Interface().(SpecValueUnmarshaler); ok {
if err := u.UnmarshalSpecValue(val); err != nil {
return err
}
continue
}
}
if mapped, err := mapSpecValueTo(val, tf.Type); err != nil {
return err
} else if into.Type().Kind() == reflect.Pointer {
into.Elem().Field(i).Set(mapped)
} else {
into.Field(i).Set(mapped)
}
}
return nil
}
func mapSpecValueTo(val *rpcv1.ModuleSpec_Value, targetType reflect.Type) (reflect.Value, error) {
switch val.Type {
case rpcv1.ModuleSpec_ValueTypeUnknown:
return reflect.Value{}, fmt.Errorf("%w: expected %s", ErrUnmatchingType, targetType.String())
case rpcv1.ModuleSpec_ValueTypeObject:
if targetType.Kind() == reflect.Struct {
structVal := reflect.New(targetType)
if err := unmarshal(val.ComplexValue, structVal, targetType); err != nil {
return reflect.Value{}, err
} else {
return structVal.Elem(), nil
}
} else if targetType.Kind() == reflect.Pointer && targetType.Elem().Kind() == reflect.Struct {
structVal := reflect.New(targetType.Elem())
if err := unmarshal(val.ComplexValue, structVal, targetType.Elem()); err != nil {
return reflect.Value{}, err
} else {
return structVal, nil
}
} else {
return reflect.Value{}, fmt.Errorf("%w: expected struct, got %s", ErrUnmatchingType, val.Type)
}
case rpcv1.ModuleSpec_ValueTypeMap:
if targetType.Kind() != reflect.Map {
return reflect.Value{}, fmt.Errorf("%w: expected map, got %v", ErrUnmatchingType, targetType)
}
mapVal := reflect.MakeMap(targetType)
for k, v := range val.ComplexValue {
if mappedVal, err := mapSpecValueTo(v, targetType.Elem()); err != nil {
return reflect.Value{}, err
} else {
mapVal.SetMapIndex(reflect.ValueOf(k), mappedVal)
}
}
return mapVal, nil
case rpcv1.ModuleSpec_ValueTypeSingle:
switch sv := val.SingleValue.(type) {
case *rpcv1.ModuleSpec_Value_BoolValue:
if targetType.Kind() != reflect.Bool {
return reflect.Value{}, fmt.Errorf("%w: expected bool, got %v", ErrUnmatchingType, targetType)
}
return reflect.ValueOf(sv.BoolValue), nil
case *rpcv1.ModuleSpec_Value_StringValue:
if targetType.Kind() != reflect.String {
return reflect.Value{}, fmt.Errorf("%w: expected string, got %v", ErrUnmatchingType, targetType)
}
return reflect.ValueOf(sv.StringValue), nil
case *rpcv1.ModuleSpec_Value_IntValue:
if targetType.Kind() != reflect.Int {
return reflect.Value{}, fmt.Errorf("%w: expected int, got %v", ErrUnmatchingType, targetType)
}
return reflect.ValueOf(int(sv.IntValue)), nil
case *rpcv1.ModuleSpec_Value_DoubleValue:
if targetType.Kind() != reflect.Float64 {
return reflect.Value{}, fmt.Errorf("%w: expected float64, got %v", ErrUnmatchingType, targetType)
}
return reflect.ValueOf(sv.DoubleValue), nil
}
case rpcv1.ModuleSpec_ValueTypeBoolSlice:
if targetType.Kind() != reflect.Slice {
return reflect.Value{}, fmt.Errorf("%w: expected slice, got %v", ErrUnmatchingType, targetType)
} else if targetType.Elem().Kind() != reflect.Bool {
return reflect.Value{}, fmt.Errorf("%w: expected bool, got %v", ErrUnmatchingType, targetType.Elem())
}
return reflect.ValueOf(val.BoolValues), nil
case rpcv1.ModuleSpec_ValueTypeStringSlice:
if targetType.Kind() != reflect.Slice {
return reflect.Value{}, fmt.Errorf("%w: expected slice, got %v", ErrUnmatchingType, targetType)
} else if targetType.Elem().Kind() != reflect.String {
return reflect.Value{}, fmt.Errorf("%w: expected string, got %v", ErrUnmatchingType, targetType.Elem())
}
return reflect.ValueOf(val.StringValues), nil
case rpcv1.ModuleSpec_ValueTypeIntSlice:
if targetType.Kind() != reflect.Slice {
return reflect.Value{}, fmt.Errorf("%w: expected slice, got %v", ErrUnmatchingType, targetType)
}
switch targetType.Elem().Kind() {
case reflect.Int:
return reflect.ValueOf(convertNumericSlice[int64, int](val.IntValues)), nil
case reflect.Int8:
return reflect.ValueOf(convertNumericSlice[int64, int8](val.IntValues)), nil
case reflect.Int16:
return reflect.ValueOf(convertNumericSlice[int64, int16](val.IntValues)), nil
case reflect.Int32:
return reflect.ValueOf(convertNumericSlice[int64, int32](val.IntValues)), nil
case reflect.Int64:
return reflect.ValueOf(val.IntValues), nil
default:
return reflect.Value{}, fmt.Errorf("%w: expected int, got %v", ErrUnmatchingType, targetType.Elem())
}
case rpcv1.ModuleSpec_ValueTypeDoubleSlice:
if targetType.Kind() != reflect.Slice {
return reflect.Value{}, fmt.Errorf("%w: expected slice, got %v", ErrUnmatchingType, targetType)
}
switch targetType.Elem().Kind() {
case reflect.Float32:
return reflect.ValueOf(convertNumericSlice[float64, float32](val.DoubleValues)), nil
case reflect.Float64:
return reflect.ValueOf(val.DoubleValues), nil
default:
return reflect.Value{}, fmt.Errorf("%w: expected int, got %v", ErrUnmatchingType, targetType.Elem())
}
}
return reflect.Value{}, nil
}
type numeric interface {
constraints.Integer | constraints.Float
}
func convertNumericSlice[TIn numeric, TOut numeric](input []TIn) []TOut {
output := make([]TOut, len(input))
for i, v := range input {
output[i] = TOut(v)
}
return output
}

View file

@ -1,515 +0,0 @@
package protocol_test
import (
"testing"
rpcv1 "code.icb4dc0.de/buildr/api/generated/rpc/v1"
"code.icb4dc0.de/buildr/buildr/internal/protocol"
)
func TestUnmarshal_Bool_Success(t *testing.T) {
target := struct {
Delete bool
}{}
spec := &rpcv1.ModuleSpec{
Values: map[string]*rpcv1.ModuleSpec_Value{
"delete": {
Type: rpcv1.ModuleSpec_ValueTypeSingle,
SingleValue: &rpcv1.ModuleSpec_Value_BoolValue{
BoolValue: true,
},
},
},
}
if err := protocol.Unmarshal(spec, &target); err != nil {
t.Errorf("Failed to unmarshal: %v", err)
}
if !target.Delete {
t.Errorf("Expected Delete to be true")
}
}
func TestUnmarshal_Bool_Err(t *testing.T) {
target := struct {
Delete string
}{}
spec := &rpcv1.ModuleSpec{
Values: map[string]*rpcv1.ModuleSpec_Value{
"delete": {
Type: rpcv1.ModuleSpec_ValueTypeSingle,
SingleValue: &rpcv1.ModuleSpec_Value_BoolValue{
BoolValue: true,
},
},
},
}
if err := protocol.Unmarshal(spec, &target); err == nil {
t.Errorf("Expected error")
} else {
t.Log(err.Error())
}
}
func TestUnmarshal_Bool_Slice_Success(t *testing.T) {
target := struct {
Delete []bool
}{}
spec := &rpcv1.ModuleSpec{
Values: map[string]*rpcv1.ModuleSpec_Value{
"delete": {
Type: rpcv1.ModuleSpec_ValueTypeBoolSlice,
BoolValues: []bool{true},
},
},
}
if err := protocol.Unmarshal(spec, &target); err != nil {
t.Errorf("Failed to unmarshal: %v", err)
}
if len(target.Delete) < 1 {
t.Errorf("Expected Delete to have at least one element")
} else if !target.Delete[0] {
t.Errorf("Expected Delete[0] to be true")
}
}
func TestUnmarshal_Bool_Slice_Err(t *testing.T) {
target := struct {
Delete []string
}{}
spec := &rpcv1.ModuleSpec{
Values: map[string]*rpcv1.ModuleSpec_Value{
"delete": {
Type: rpcv1.ModuleSpec_ValueTypeBoolSlice,
BoolValues: []bool{true},
},
},
}
if err := protocol.Unmarshal(spec, &target); err == nil {
t.Errorf("Expected error")
} else {
t.Log(err.Error())
}
}
func TestUnmarshal_String_Success(t *testing.T) {
target := struct {
Name string
}{}
spec := &rpcv1.ModuleSpec{
Values: map[string]*rpcv1.ModuleSpec_Value{
"name": {
Type: rpcv1.ModuleSpec_ValueTypeSingle,
SingleValue: &rpcv1.ModuleSpec_Value_StringValue{
StringValue: "Ted",
},
},
},
}
if err := protocol.Unmarshal(spec, &target); err != nil {
t.Errorf("Failed to unmarshal: %v", err)
}
if target.Name != "Ted" {
t.Errorf("Expected Name to be 'Ted'")
}
}
func TestUnmarshal_String_Err(t *testing.T) {
target := struct {
Name int
}{}
spec := &rpcv1.ModuleSpec{
Values: map[string]*rpcv1.ModuleSpec_Value{
"name": {
Type: rpcv1.ModuleSpec_ValueTypeSingle,
SingleValue: &rpcv1.ModuleSpec_Value_StringValue{
StringValue: "Ted",
},
},
},
}
if err := protocol.Unmarshal(spec, &target); err == nil {
t.Errorf("Expected error")
} else {
t.Log(err.Error())
}
}
func TestUnmarshal_String_Slice_Success(t *testing.T) {
target := struct {
Names []string
}{}
spec := &rpcv1.ModuleSpec{
Values: map[string]*rpcv1.ModuleSpec_Value{
"names": {
Type: rpcv1.ModuleSpec_ValueTypeStringSlice,
StringValues: []string{"Ted"},
},
},
}
if err := protocol.Unmarshal(spec, &target); err != nil {
t.Errorf("Failed to unmarshal: %v", err)
}
if len(target.Names) < 1 {
t.Errorf("Expected Names to have at least one element")
} else if target.Names[0] != "Ted" {
t.Errorf("Expected Names[0] to be 'Ted'")
}
}
func TestUnmarshal_String_Slice_Err(t *testing.T) {
target := struct {
Names []int
}{}
spec := &rpcv1.ModuleSpec{
Values: map[string]*rpcv1.ModuleSpec_Value{
"names": {
Type: rpcv1.ModuleSpec_ValueTypeStringSlice,
StringValues: []string{"Ted"},
},
},
}
if err := protocol.Unmarshal(spec, &target); err == nil {
t.Errorf("Expected error")
} else {
t.Log(err.Error())
}
}
func TestUnmarshal_Int_Success(t *testing.T) {
target := struct {
Age int
}{}
spec := &rpcv1.ModuleSpec{
Values: map[string]*rpcv1.ModuleSpec_Value{
"age": {
Type: rpcv1.ModuleSpec_ValueTypeSingle,
SingleValue: &rpcv1.ModuleSpec_Value_IntValue{
IntValue: 42,
},
},
},
}
if err := protocol.Unmarshal(spec, &target); err != nil {
t.Errorf("Failed to unmarshal: %v", err)
}
if target.Age != 42 {
t.Errorf("Expected Age to be 42")
}
}
func TestUnmarshal_Int_Err(t *testing.T) {
target := struct {
Age string
}{}
spec := &rpcv1.ModuleSpec{
Values: map[string]*rpcv1.ModuleSpec_Value{
"age": {
Type: rpcv1.ModuleSpec_ValueTypeSingle,
SingleValue: &rpcv1.ModuleSpec_Value_IntValue{
IntValue: 42,
},
},
},
}
if err := protocol.Unmarshal(spec, &target); err == nil {
t.Errorf("Expected error")
} else {
t.Log(err.Error())
}
}
func TestUnmarshal_Int_Slice_Success(t *testing.T) {
target := struct {
Ages []int
}{}
spec := &rpcv1.ModuleSpec{
Values: map[string]*rpcv1.ModuleSpec_Value{
"ages": {
Type: rpcv1.ModuleSpec_ValueTypeIntSlice,
IntValues: []int64{42},
},
},
}
if err := protocol.Unmarshal(spec, &target); err != nil {
t.Errorf("Failed to unmarshal: %v", err)
}
if len(target.Ages) < 1 {
t.Errorf("Expected Ages to have at least one element")
} else if target.Ages[0] != 42 {
t.Errorf("Expected Ages[0] to be 42")
}
}
func TestUnmarshal_Int_Slice_Err(t *testing.T) {
target := struct {
Ages []string
}{}
spec := &rpcv1.ModuleSpec{
Values: map[string]*rpcv1.ModuleSpec_Value{
"ages": {
Type: rpcv1.ModuleSpec_ValueTypeIntSlice,
IntValues: []int64{42},
},
},
}
if err := protocol.Unmarshal(spec, &target); err == nil {
t.Errorf("Expected error")
} else {
t.Log(err.Error())
}
}
func TestUnmarshal_Double_Success(t *testing.T) {
target := struct {
Pi float64
}{}
spec := &rpcv1.ModuleSpec{
Values: map[string]*rpcv1.ModuleSpec_Value{
"Pi": {
Type: rpcv1.ModuleSpec_ValueTypeSingle,
SingleValue: &rpcv1.ModuleSpec_Value_DoubleValue{
DoubleValue: 3.14,
},
},
},
}
if err := protocol.Unmarshal(spec, &target); err != nil {
t.Errorf("Failed to unmarshal: %v", err)
}
if target.Pi-3.14 > 0.0000001 {
t.Errorf("Expected Pi to be 3.14")
}
}
func TestUnmarshal_Double_Err(t *testing.T) {
target := struct {
Pi string
}{}
spec := &rpcv1.ModuleSpec{
Values: map[string]*rpcv1.ModuleSpec_Value{
"pi": {
Type: rpcv1.ModuleSpec_ValueTypeSingle,
SingleValue: &rpcv1.ModuleSpec_Value_DoubleValue{
DoubleValue: 3.14,
},
},
},
}
if err := protocol.Unmarshal(spec, &target); err == nil {
t.Errorf("Expected error")
} else {
t.Log(err.Error())
}
}
func TestUnmarshal_Double_Slice_Success(t *testing.T) {
target := struct {
Pis []float64
}{}
spec := &rpcv1.ModuleSpec{
Values: map[string]*rpcv1.ModuleSpec_Value{
"pis": {
Type: rpcv1.ModuleSpec_ValueTypeDoubleSlice,
DoubleValues: []float64{3.14},
},
},
}
if err := protocol.Unmarshal(spec, &target); err != nil {
t.Errorf("Failed to unmarshal: %v", err)
}
if len(target.Pis) < 1 {
t.Errorf("Expected Pis to have at least one element")
} else if target.Pis[0]-3.14 > 0.0000001 {
t.Errorf("Expected Pis[0] to be 3.14")
}
}
func TestUnmarshal_Double_Slice_Err(t *testing.T) {
target := struct {
Pis []string
}{}
spec := &rpcv1.ModuleSpec{
Values: map[string]*rpcv1.ModuleSpec_Value{
"pis": {
Type: rpcv1.ModuleSpec_ValueTypeDoubleSlice,
DoubleValues: []float64{3.14},
},
},
}
if err := protocol.Unmarshal(spec, &target); err == nil {
t.Errorf("Expected error")
} else {
t.Log(err.Error())
}
}
func TestUnmarshal_NestedStruct_Success(t *testing.T) {
target := struct {
Address struct {
City string
}
}{}
spec := &rpcv1.ModuleSpec{
Values: map[string]*rpcv1.ModuleSpec_Value{
"address": {
Type: rpcv1.ModuleSpec_ValueTypeObject,
ComplexValue: map[string]*rpcv1.ModuleSpec_Value{
"city": {
Type: rpcv1.ModuleSpec_ValueTypeSingle,
SingleValue: &rpcv1.ModuleSpec_Value_StringValue{
StringValue: "New York",
},
},
},
},
},
}
if err := protocol.Unmarshal(spec, &target); err != nil {
t.Errorf("Failed to unmarshal: %v", err)
}
if target.Address.City != "New York" {
t.Errorf("Expected City to be 'New York'")
}
}
func TestUnmarshal_NestedStructPointer_Success(t *testing.T) {
target := struct {
Address *struct {
City string
}
}{}
spec := &rpcv1.ModuleSpec{
Values: map[string]*rpcv1.ModuleSpec_Value{
"address": {
Type: rpcv1.ModuleSpec_ValueTypeObject,
ComplexValue: map[string]*rpcv1.ModuleSpec_Value{
"city": {
Type: rpcv1.ModuleSpec_ValueTypeSingle,
SingleValue: &rpcv1.ModuleSpec_Value_StringValue{
StringValue: "New York",
},
},
},
},
},
}
if err := protocol.Unmarshal(spec, &target); err != nil {
t.Errorf("Failed to unmarshal: %v", err)
}
if target.Address.City != "New York" {
t.Errorf("Expected City to be 'New York'")
}
}
func TestUnmarshal_Map_Success(t *testing.T) {
target := struct {
Values map[string]string
}{}
spec := &rpcv1.ModuleSpec{
Values: map[string]*rpcv1.ModuleSpec_Value{
"values": {
Type: rpcv1.ModuleSpec_ValueTypeMap,
ComplexValue: map[string]*rpcv1.ModuleSpec_Value{
"City": {
Type: rpcv1.ModuleSpec_ValueTypeSingle,
SingleValue: &rpcv1.ModuleSpec_Value_StringValue{
StringValue: "New York",
},
},
},
},
},
}
if err := protocol.Unmarshal(spec, &target); err != nil {
t.Errorf("Failed to unmarshal: %v", err)
}
if target.Values["City"] != "New York" {
t.Errorf("Expected City to be 'New York'")
}
}
func TestUnmarshal_NestedMap_Success(t *testing.T) {
target := struct {
City struct {
Labels map[string]string
}
}{}
spec := &rpcv1.ModuleSpec{
Values: map[string]*rpcv1.ModuleSpec_Value{
"city": {
Type: rpcv1.ModuleSpec_ValueTypeObject,
ComplexValue: map[string]*rpcv1.ModuleSpec_Value{
"labels": {
Type: rpcv1.ModuleSpec_ValueTypeMap,
ComplexValue: map[string]*rpcv1.ModuleSpec_Value{
"Region": {
Type: rpcv1.ModuleSpec_ValueTypeSingle,
SingleValue: &rpcv1.ModuleSpec_Value_StringValue{
StringValue: "west",
},
},
},
},
},
},
},
}
if err := protocol.Unmarshal(spec, &target); err != nil {
t.Errorf("Failed to unmarshal: %v", err)
}
if target.City.Labels["Region"] != "west" {
t.Errorf("Expected 'Region' to be 'west'")
}
}

View file

@ -1,6 +1,7 @@
package rpc
import (
"log/slog"
"net"
"sync"
"time"
@ -10,9 +11,9 @@ import (
prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
v1Health "google.golang.org/grpc/health/grpc_health_v1"
"google.golang.org/grpc/reflection"
"log/slog"
rpcv1 "code.icb4dc0.de/buildr/api/generated/rpc/v1"
"code.icb4dc0.de/buildr/buildr/internal/rpc/middleware"
v1 "code.icb4dc0.de/buildr/buildr/internal/rpc/v1"
"code.icb4dc0.de/buildr/buildr/internal/services"

View file

@ -3,13 +3,13 @@ package v1
import (
"context"
"io"
"log/slog"
"os"
"code.icb4dc0.de/buildr/buildr/modules/state"
"log/slog"
rpcv1 "code.icb4dc0.de/buildr/api/generated/rpc/v1"
"code.icb4dc0.de/buildr/buildr/modules"
)

View file

@ -9,8 +9,9 @@ import (
"time"
rpcv1 "code.icb4dc0.de/buildr/api/generated/rpc/v1"
"code.icb4dc0.de/buildr/common/protocol"
"code.icb4dc0.de/buildr/buildr/internal/archive"
"code.icb4dc0.de/buildr/buildr/internal/protocol"
"code.icb4dc0.de/buildr/buildr/modules"
"google.golang.org/grpc/codes"
@ -83,7 +84,7 @@ func (e *RemoteTaskExecutor) Execute(
result := new(rpcv1.TaskResult)
if err != nil {
result.Error = fmt.Sprintf("failed to execute module %s/%s: %s", mod.Category().String(), mod.Name(), err.Error())
result.Error = fmt.Sprintf("failed to execute module %s/%s: %s", CategoryName(mod.Category()), mod.Name(), err.Error())
}
e.logger.Debug("Waiting for file change events to propagate before canceling watcher")

View file

@ -5,14 +5,15 @@ import (
"errors"
"fmt"
"io"
"log/slog"
rpcv1 "code.icb4dc0.de/buildr/api/generated/rpc/v1"
"code.icb4dc0.de/buildr/buildr/internal/errs"
"code.icb4dc0.de/buildr/buildr/modules"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"log/slog"
)
var (

View file

@ -5,11 +5,11 @@ import (
"fmt"
"io"
"io/fs"
"log/slog"
"os"
"path/filepath"
"github.com/fsnotify/fsnotify"
"log/slog"
"code.icb4dc0.de/buildr/buildr/internal/archive"
"code.icb4dc0.de/buildr/buildr/internal/ignore"

View file

@ -2,10 +2,10 @@ package v1
import (
"context"
"log/slog"
"time"
v1 "google.golang.org/grpc/health/grpc_health_v1"
"log/slog"
)
const healthCheckInterval = 100 * time.Millisecond

View file

@ -5,12 +5,12 @@ import (
"errors"
"fmt"
"io"
"log/slog"
"sync"
rpcv1 "code.icb4dc0.de/buildr/api/generated/rpc/v1"
"github.com/google/uuid"
"log/slog"
)
var (

View file

@ -3,13 +3,13 @@ package v1
import (
"context"
"errors"
"log/slog"
rpcv1 "code.icb4dc0.de/buildr/api/generated/rpc/v1"
"code.icb4dc0.de/buildr/buildr/internal/errs"
"code.icb4dc0.de/buildr/buildr/modules"
"code.icb4dc0.de/buildr/buildr/modules/state"
"log/slog"
)
func newRemoteStateClient(
@ -29,8 +29,8 @@ func newRemoteStateClient(
type RemoteStateClient struct {
logger *slog.Logger
requestClient *RequestResponseClient
cat modules.Category
name string
cat modules.Category
}
func (c *RemoteStateClient) GetState(ctx context.Context, key string) ([]byte, state.Metadata, error) {

View file

@ -1,9 +1,10 @@
package services
import (
"code.icb4dc0.de/buildr/buildr/modules/state"
"github.com/docker/docker/client"
"code.icb4dc0.de/buildr/buildr/modules/state"
"code.icb4dc0.de/buildr/buildr/internal/ignore"
"code.icb4dc0.de/buildr/buildr/internal/vault"
"code.icb4dc0.de/buildr/buildr/modules"

View file

@ -48,7 +48,7 @@ func WithIgnorer(ignorer *ignore.Ignorer) CollectionOption {
func WithDB(db *state.DB) CollectionOption {
return collectionOptionFunc(func(svc *Collection) error {
svc.stateDb = db
svc.stateDB = db
return nil
})
}
@ -109,7 +109,7 @@ type Collection struct {
dockerClient *client.Client
ignorer *ignore.Ignorer
stateStore state.Store
stateDb *state.DB
stateDB *state.DB
cache state.Cache
diagsWriter hcl.DiagnosticWriter
}
@ -153,7 +153,7 @@ func (c *Collection) DiagsWriter() hcl.DiagnosticWriter {
}
func (c *Collection) PluginsRepo() state.Plugins {
return c.stateDb.Plugins
return c.stateDB.Plugins
}
func (c *Collection) Close() error {
@ -162,8 +162,8 @@ func (c *Collection) Close() error {
err = c.dockerClient.Close()
}
if c.stateDb != nil {
err = errors.Join(c.stateDb.Close())
if c.stateDB != nil {
err = errors.Join(c.stateDB.Close())
}
return err

View file

@ -3,11 +3,10 @@ package main
import (
"context"
"errors"
"log/slog"
"os"
"os/signal"
"log/slog"
"code.icb4dc0.de/buildr/buildr/internal/cmd"
"code.icb4dc0.de/buildr/buildr/internal/errs"
)

View file

@ -3,11 +3,11 @@ package modules
import (
"context"
"io"
"log/slog"
"code.icb4dc0.de/buildr/buildr/modules/state"
"github.com/hashicorp/hcl/v2"
"log/slog"
)
type ExecutionContext interface {
@ -48,7 +48,7 @@ type Initializer interface {
}
type Helper interface {
Help() (help Help, okay bool)
Help(ctx context.Context) (help Help, err error)
}
type Module interface {

View file

@ -10,7 +10,7 @@ import (
"github.com/hashicorp/hcl/v2/gohcl"
rpcv1 "code.icb4dc0.de/buildr/api/generated/rpc/v1"
"code.icb4dc0.de/buildr/buildr/internal/protocol"
"code.icb4dc0.de/buildr/common/protocol"
)
var (

View file

@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"io/fs"
"log/slog"
"os"
"path"
"path/filepath"
@ -12,7 +13,6 @@ import (
"code.icb4dc0.de/buildr/buildr/modules"
"github.com/klauspost/compress/zip"
"log/slog"
)
var _ modules.Module = (*ZipArchive)(nil)

View file

@ -4,11 +4,11 @@ import (
"context"
"errors"
"fmt"
"log/slog"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/daemon"
"log/slog"
)
func newContainerDaemon(imageName string, logger *slog.Logger, tags []string, cli daemon.Client) containerDaemon {

View file

@ -5,6 +5,7 @@ import (
"context"
"fmt"
"io"
"log/slog"
"os"
"sync"
"time"
@ -18,7 +19,6 @@ import (
"github.com/google/go-containerregistry/pkg/v1/tarball"
"github.com/google/go-containerregistry/pkg/v1/types"
specsv1 "github.com/opencontainers/image-spec/specs-go/v1"
"log/slog"
"code.icb4dc0.de/buildr/buildr/modules"
)

View file

@ -11,7 +11,8 @@ import (
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/google/go-containerregistry/pkg/v1/types"
"golang.org/x/exp/maps"
"code.icb4dc0.de/buildr/buildr/internal/maps"
)
type registryPublisherOption func(p *registryPublisher)

View file

@ -0,0 +1,187 @@
package plugin
import (
"bytes"
"context"
"errors"
"log/slog"
"os/exec"
"time"
"github.com/tetratelabs/wazero/api"
"google.golang.org/protobuf/proto"
rpcv1 "code.icb4dc0.de/buildr/api/generated/rpc/v1"
"code.icb4dc0.de/buildr/buildr/internal/sh"
"code.icb4dc0.de/buildr/buildr/modules"
)
type wasiPluginExecContext struct {
ctx modules.ExecutionContext
memMgr *memoryManager
}
func (c *wasiPluginExecContext) lookPath(ctx context.Context, m api.Module, offset, byteCount uint32) uint64 {
buf, ok := m.Memory().Read(offset, byteCount)
if !ok {
return 0
}
lookupPathReq := new(rpcv1.LookupPathRequest)
if err := proto.Unmarshal(buf, lookupPathReq); err != nil {
c.ctx.Logger().Error("failed to unmarshal LookupPathRequest", slog.String("err", err.Error()))
return 0
}
if lookupPathReq.Command == "" {
c.ctx.Logger().Error("cannot lookup empty command")
return 0
}
foundPath, err := exec.LookPath(lookupPathReq.Command)
lookupPathResp := &rpcv1.LookupPathResponse{
Path: foundPath,
}
if err != nil {
lookupPathResp.Error = err.Error()
}
if ptrSize, err := c.memMgr.WriteMessage(ctx, m, lookupPathResp); err != nil {
c.ctx.Logger().Error("Failed to write message", slog.String("err", err.Error()))
return 0
} else {
return ptrSize
}
}
func (c *wasiPluginExecContext) exec(ctx context.Context, m api.Module, offset, byteCount uint32) uint64 {
buf, ok := m.Memory().Read(offset, byteCount)
if !ok {
return 0
}
execReq := new(rpcv1.ProcessStartRequest)
if err := proto.Unmarshal(buf, execReq); err != nil {
c.ctx.Logger().Error("failed to unmarshal ProcessStartRequest", slog.String("err", err.Error()))
return 0
}
execResp := new(rpcv1.ProcessStartResponse)
//nolint:contextcheck // that ain't a context.Context
cmd, err := sh.PrepareCommand(c.ctx, execReq.Command, execReq.Args...)
if err != nil {
c.ctx.Logger().Error("failed to prepare command", slog.String("err", err.Error()))
return 0
}
cmd.AddEnv(execReq.Environment)
if len(execReq.Stdin) > 0 {
cmd.SetStdIn(bytes.NewReader(execReq.Stdin))
}
if len(execReq.WorkingDirectory) > 0 {
cmd.SetWorkingDir(execReq.WorkingDirectory)
}
if err := cmd.Run(); err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
execResp.ExitCode = int32(exitErr.ExitCode())
execResp.Stderr = exitErr.Stderr
} else {
execResp.ExitCode = 1
}
execResp.Error = err.Error()
}
if ptrSize, err := c.memMgr.WriteMessage(ctx, m, execResp); err != nil {
c.ctx.Logger().Error("Failed to write message", slog.String("err", err.Error()))
return 0
} else {
return ptrSize
}
}
func (c *wasiPluginExecContext) log(ctx context.Context, m api.Module, offset, byteCount uint32) {
buf, ok := m.Memory().Read(offset, byteCount)
if !ok {
return
}
logger := c.ctx.Logger()
taskLog := new(rpcv1.TaskLog)
if err := proto.Unmarshal(buf, taskLog); err != nil {
logger.Warn("failed to unmarshal task log", slog.String("err", err.Error()))
return
}
rec := slog.NewRecord(time.UnixMicro(taskLog.Time), slog.Level(taskLog.Level), taskLog.Message, 0)
for i := range taskLog.Attributes {
attr := taskLog.Attributes[i]
rec.AddAttrs(slog.String(attr.Key, attr.Value))
}
_ = logger.Handler().Handle(ctx, rec)
}
func (c *wasiPluginExecContext) getState(ctx context.Context, m api.Module, offset, byteCount uint32) uint64 {
buf, ok := m.Memory().Read(offset, byteCount)
if !ok {
return 0
}
getStateReq := new(rpcv1.GetStateRequest)
if err := proto.Unmarshal(buf, getStateReq); err != nil {
c.ctx.Logger().Error("failed to unmarshal getStateRequest", slog.String("err", err.Error()))
return 0
}
resp := new(rpcv1.GetStateResponse)
val, _, err := c.ctx.GetState(ctx, string(getStateReq.Key))
if err != nil {
c.ctx.Logger().Error("failed to get state", slog.String("err", err.Error()))
return 0
}
resp.Data = val
if ptrSize, err := c.memMgr.WriteMessage(ctx, m, resp); err != nil {
c.ctx.Logger().Error("Failed to write message", slog.String("err", err.Error()))
return 0
} else {
return ptrSize
}
}
func (c *wasiPluginExecContext) setState(ctx context.Context, m api.Module, offset, byteCount uint32) (result uint64) {
buf, ok := m.Memory().Read(offset, byteCount)
if !ok {
return 0
}
setState := new(rpcv1.SetState)
if err := proto.Unmarshal(buf, setState); err != nil {
c.ctx.Logger().Error("failed to unmarshal SetState", slog.String("err", err.Error()))
return 0
}
var resp rpcv1.Result
if err := c.ctx.SetState(ctx, string(setState.Key), setState.Data); err != nil {
c.ctx.Logger().Error("failed to set state", slog.String("err", err.Error()))
resp.Error = err.Error()
} else {
resp.Success = true
}
if ptrSize, err := c.memMgr.WriteMessage(ctx, m, &resp); err != nil {
c.ctx.Logger().Error("Failed to write message", slog.String("err", err.Error()))
return 0
} else {
return ptrSize
}
}

202
modules/plugin/help.go Normal file
View file

@ -0,0 +1,202 @@
package plugin
import (
"context"
"errors"
"fmt"
"io"
"time"
"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
"google.golang.org/protobuf/proto"
rpcv1 "code.icb4dc0.de/buildr/api/generated/rpc/v1"
"code.icb4dc0.de/buildr/buildr/modules"
)
//nolint:funlen // not much value in splitting hereG22
func (m Module) Help(ctx context.Context) (help modules.Help, err error) {
runtime := m.prepareWASIRuntime(ctx)
defer func() {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
_ = runtime.Close(ctx)
}()
_, err = runtime.NewHostModuleBuilder("buildr").
NewFunctionBuilder().WithFunc(dummyWithoutResult).Export("log_msg").
NewFunctionBuilder().WithFunc(dummyWithResult).Export("get_state").
NewFunctionBuilder().WithFunc(dummyWithResult).Export("set_state").
NewFunctionBuilder().WithFunc(dummyWithResult).Export("exec").
NewFunctionBuilder().WithFunc(dummyWithResult).Export("lookPath").
Instantiate(ctx)
if err != nil {
return modules.Help{}, err
}
closer, err := wasi_snapshot_preview1.Instantiate(ctx, runtime)
if err != nil {
return modules.Help{}, err
}
defer func() {
if closeErr := closer.Close(context.Background()); closeErr != nil {
err = errors.Join(err, fmt.Errorf("failed to close WASI runtime: %w", closeErr))
}
}()
moduleConfig := wazero.NewModuleConfig().
WithStdout(io.Discard).
WithStderr(io.Discard)
pluginPayload, err := m.PluginPayload.Bytes()
if err != nil {
return modules.Help{}, err
}
mod, err := runtime.InstantiateWithConfig(ctx, pluginPayload, moduleConfig)
if err != nil {
return modules.Help{}, err
}
memMgr := newMemoryManager(mod)
helpRequest := &rpcv1.HelpRequest{
ModuleReference: &rpcv1.ModuleReference{
ModuleCategory: m.PluginCategory,
ModuleType: m.PluginType,
},
}
data, err := proto.Marshal(helpRequest)
if err != nil {
return modules.Help{}, err
}
helpFunc := mod.ExportedFunction("help")
var helpResult rpcv1.HelpResponse
err = memMgr.WithMem(ctx, uint64(len(data)), func(ptr uint64) error {
if !mod.Memory().Write(uint32(ptr), data) {
return errors.New("failed to write to memory")
}
result, err := helpFunc.Call(ctx, ptr, uint64(len(data)))
if err != nil {
return fmt.Errorf("failed to run WASI module: %w", err)
}
resultPtr, resultSize := uint32(result[0]>>defaultIntegerSize), uint32(result[0])
if ptr == 0 {
return errors.New("failed to get inventory - 0 pointer")
}
defer func() {
err = errors.Join(err, memMgr.Deallocate(ctx, ptr))
}()
data, ok := mod.Memory().Read(resultPtr, resultSize)
if !ok {
return errors.New("failed to get inventory")
}
if err = proto.Unmarshal(data, &helpResult); err != nil {
return fmt.Errorf("failed to unmarshal inventory: %w", err)
}
return nil
})
if err != nil {
return modules.Help{}, err
}
moduleHelp := modules.Help{
Name: helpResult.Name,
Description: helpResult.Description,
Examples: m.mapToExamples(helpResult.Examples),
}
return moduleHelp, nil
}
func (m Module) mapToExamples(raw []*rpcv1.TaskExample) []modules.Example {
examples := make([]modules.Example, 0, len(raw))
for _, example := range raw {
spec := &modules.Metadata[Module]{
Module: Module{
PluginCategory: m.Category(),
PluginType: m.Type(),
PluginPayload: m.PluginPayload,
modSpec: example.TaskSpec.ModuleSpec,
},
ModuleName: example.TaskSpec.ModuleName,
OutputDir: example.TaskSpec.OutputDir,
}
if example.TaskSpec.Container != nil {
spec.Container = &modules.ContainerSpec{
Image: example.TaskSpec.Container.Image,
User: example.TaskSpec.Container.User,
BindMounts: nil,
Privileged: example.TaskSpec.Container.Privileged,
}
if example.TaskSpec.Container.Capabilities != nil {
spec.Container.Capabilities = &modules.ContainerCapabilities{
Add: example.TaskSpec.Container.Capabilities.Add,
Drop: example.TaskSpec.Container.Capabilities.Drop,
}
}
if example.TaskSpec.Container.VolumeMounts != nil {
spec.Container.VolumeMounts = make([]modules.ContainerVolumeMount, 0, len(example.TaskSpec.Container.VolumeMounts))
for _, v := range example.TaskSpec.Container.VolumeMounts {
spec.Container.VolumeMounts = append(spec.Container.VolumeMounts, modules.ContainerVolumeMount{
Target: v.Target,
Name: v.Name,
ReadOnly: v.ReadOnly,
NoCopy: v.NoCopy,
})
}
}
if example.TaskSpec.Container.TmpfsMounts != nil {
spec.Container.TmpfsMounts = make([]modules.ContainerTmpfsMount, 0, len(example.TaskSpec.Container.TmpfsMounts))
for _, v := range example.TaskSpec.Container.TmpfsMounts {
spec.Container.TmpfsMounts = append(spec.Container.TmpfsMounts, modules.ContainerTmpfsMount{
Target: v.Target,
ReadOnly: v.ReadOnly,
Size: v.Size,
})
}
}
if example.TaskSpec.Container.BindMounts != nil {
spec.Container.BindMounts = make([]modules.ContainerBindMount, 0, len(example.TaskSpec.Container.BindMounts))
for _, v := range example.TaskSpec.Container.BindMounts {
spec.Container.BindMounts = append(spec.Container.BindMounts, modules.ContainerBindMount{
Source: v.Source,
Target: v.Target,
})
}
}
}
examples = append(examples, modules.Example{
Name: example.Name,
Description: example.Description,
Spec: spec,
})
}
return examples
}

90
modules/plugin/marshal.go Normal file
View file

@ -0,0 +1,90 @@
package plugin
import (
"errors"
"fmt"
"github.com/hashicorp/hcl/v2/hclwrite"
"github.com/zclconf/go-cty/cty"
rpcv1 "code.icb4dc0.de/buildr/api/generated/rpc/v1"
"code.icb4dc0.de/buildr/buildr/internal/hcl"
)
func (m Module) MarshalHCL(block *hclwrite.Block) error {
for s, value := range m.modSpec.Values {
switch value.Kind {
case rpcv1.ModuleSpec_ValueKindUnknown, rpcv1.ModuleSpec_ValueKindAttribute:
if mapped, err := mapSpecValueToCty(value); err != nil {
return err
} else {
block.Body().SetAttributeValue(s, mapped)
}
case rpcv1.ModuleSpec_ValueKindBlock:
case rpcv1.ModuleSpec_ValueKindLabel:
if value.Type != rpcv1.ModuleSpec_ValueTypeSingle {
return errors.New("only single values are supported as labels")
}
if value.SingleValue == nil {
return errors.New("single value is nil")
}
if s, ok := value.SingleValue.(*rpcv1.ModuleSpec_Value_StringValue); !ok {
return errors.New("single value is not a string - only strings are supported as labels")
} else {
block.SetLabels(append(block.Labels(), s.StringValue))
}
}
}
return nil
}
//nolint:gocyclo // cannot be less complex
func mapSpecValueToCty(value *rpcv1.ModuleSpec_Value) (cty.Value, error) {
switch value.Type {
case rpcv1.ModuleSpec_ValueTypeUnknown:
return cty.Value{}, errors.New("cannot map unknown value type")
case rpcv1.ModuleSpec_ValueTypeSingle:
switch v := value.SingleValue.(type) {
case *rpcv1.ModuleSpec_Value_BoolValue:
return cty.BoolVal(v.BoolValue), nil
case *rpcv1.ModuleSpec_Value_StringValue:
return cty.StringVal(v.StringValue), nil
case *rpcv1.ModuleSpec_Value_IntValue:
return cty.NumberIntVal(v.IntValue), nil
case *rpcv1.ModuleSpec_Value_DoubleValue:
return cty.NumberFloatVal(v.DoubleValue), nil
default:
return cty.NilVal, fmt.Errorf("unsupported value type %q", value.Type)
}
case rpcv1.ModuleSpec_ValueTypeStringSlice:
return hcl.MapToCtyVal(value.StringValues, hcl.MappingCfg{})
case rpcv1.ModuleSpec_ValueTypeIntSlice:
return hcl.MapToCtyVal(value.IntValues, hcl.MappingCfg{})
case rpcv1.ModuleSpec_ValueTypeDoubleSlice:
return hcl.MapToCtyVal(value.DoubleValues, hcl.MappingCfg{})
case rpcv1.ModuleSpec_ValueTypeBoolSlice:
return hcl.MapToCtyVal(value.BoolValues, hcl.MappingCfg{})
case rpcv1.ModuleSpec_ValueTypeObject:
return cty.Value{}, errors.New("object type are right now not supported (yet)")
case rpcv1.ModuleSpec_ValueTypeMap:
if len(value.ComplexValue) == 0 {
return cty.MapValEmpty(cty.String), nil
}
mappedVal := make(map[string]cty.Value, len(value.ComplexValue))
for k, v := range value.ComplexValue {
if mapped, err := mapSpecValueToCty(v); err != nil {
return cty.Value{}, err
} else {
mappedVal[k] = mapped
}
}
return cty.MapVal(mappedVal), nil
default:
return cty.NilVal, fmt.Errorf("unsupported value type %q", value.Type)
}
}

View file

@ -1,30 +1,17 @@
package plugin
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"log/slog"
"os/exec"
"time"
"code.icb4dc0.de/buildr/buildr/internal/sh"
"code.icb4dc0.de/buildr/buildr/internal/ioutils"
"code.icb4dc0.de/buildr/buildr/internal/hcl"
"code.icb4dc0.de/buildr/buildr/modules"
"github.com/hashicorp/hcl/v2/hclwrite"
"github.com/google/uuid"
hcl2 "github.com/hashicorp/hcl/v2"
"github.com/klauspost/compress/s2"
"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
"google.golang.org/protobuf/proto"
@ -32,45 +19,27 @@ import (
)
var (
_ modules.Module = (*Module)(nil)
_ hcl.Marshaler = (*Module)(nil)
_ json.Marshaler = (*Module)(nil)
_ json.Unmarshaler = (*Module)(nil)
_ modules.Module = (*Module)(nil)
_ hcl.Marshaler = (*Module)(nil)
_ modules.Helper = (*Module)(nil)
_ modules.Initializer = (*Module)(nil)
)
type jsonPluginModule struct {
ModuleSpec *rpcv1.ModuleSpec `json:"module_spec"`
PluginCategory modules.Category `json:"plugin_category"`
PluginType string `json:"plugin_type"`
PluginPayload []byte `json:"plugin_payload"`
}
type Module struct {
ModuleSpec map[string]any `hcl:",remain"`
PluginCategory modules.Category
PluginType string
PluginPayload PayloadReader
ModuleSpec map[string]any `hcl:",remain"`
modSpec *rpcv1.ModuleSpec
}
func (m Module) MarshalHCL(*hclwrite.Block) error {
return errors.New("plugin modules cannot be marshaled to HCL right now")
PluginType string
PluginCategory modules.Category
}
func (m Module) Execute(ctx modules.ExecutionContext) (err error) {
runtimeConfig := wazero.NewRuntimeConfig().
WithCloseOnContextDone(true)
runtime := m.prepareWASIRuntime(ctx)
pluginpayload, err := m.PluginPayload.Bytes()
if err != nil {
return err
}
r := wazero.NewRuntimeWithConfig(ctx, runtimeConfig)
defer func() {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
if closeErr := r.Close(ctx); closeErr != nil {
if closeErr := runtime.Close(ctx); closeErr != nil {
err = errors.Join(err, fmt.Errorf("failed to close runtime: %w", closeErr))
}
}()
@ -79,7 +48,7 @@ func (m Module) Execute(ctx modules.ExecutionContext) (err error) {
ctx: ctx,
}
_, err = r.NewHostModuleBuilder("buildr").
_, err = runtime.NewHostModuleBuilder("buildr").
NewFunctionBuilder().WithFunc(wasiCtx.log).Export("log_msg").
NewFunctionBuilder().WithFunc(wasiCtx.getState).Export("get_state").
NewFunctionBuilder().WithFunc(wasiCtx.setState).Export("set_state").
@ -91,7 +60,7 @@ func (m Module) Execute(ctx modules.ExecutionContext) (err error) {
return fmt.Errorf("failed to instantiate host module: %w", err)
}
closer, err := wasi_snapshot_preview1.Instantiate(ctx, r)
closer, err := wasi_snapshot_preview1.Instantiate(ctx, runtime)
if err != nil {
return fmt.Errorf("failed to instantiate WASI infrastructure: %w", err)
}
@ -107,7 +76,12 @@ func (m Module) Execute(ctx modules.ExecutionContext) (err error) {
WithStderr(ctx.StdErr()).
WithFS(newMultiDirFS(ctx.WorkingDir(), ctx.OutDir()))
mod, err := r.InstantiateWithConfig(ctx, pluginpayload, moduleConfig)
pluginPayload, err := m.PluginPayload.Bytes()
if err != nil {
return err
}
mod, err := runtime.InstantiateWithConfig(ctx, pluginPayload, moduleConfig)
if err != nil {
return fmt.Errorf("failed to instantiate WASI module: %w", err)
}
@ -129,6 +103,7 @@ func (m Module) Execute(ctx modules.ExecutionContext) (err error) {
Root: ctx.WorkingDir(),
},
OutDir: ctx.OutDir(),
BinDir: ctx.BinariesDir(),
},
Spec: m.modSpec,
}
@ -165,6 +140,10 @@ func (m Module) Type() string {
return m.PluginType
}
func (m *Module) SetModuleSpec(spec *rpcv1.ModuleSpec) {
m.modSpec = spec
}
func (m Module) Init(hclCtx *hcl2.EvalContext) (modules.Module, error) {
m.modSpec = &rpcv1.ModuleSpec{
Category: m.PluginCategory,
@ -173,8 +152,7 @@ func (m Module) Init(hclCtx *hcl2.EvalContext) (modules.Module, error) {
}
for k, v := range m.ModuleSpec {
switch a := v.(type) {
case *hcl2.Attribute:
if a, ok := v.(*hcl2.Attribute); ok {
val, err := a.Expr.Value(hclCtx)
if err != nil {
return nil, err
@ -186,214 +164,12 @@ func (m Module) Init(hclCtx *hcl2.EvalContext) (modules.Module, error) {
return m, nil
}
func (m *Module) UnmarshalJSON(data []byte) (err error) {
var jpm jsonPluginModule
func (Module) prepareWASIRuntime(ctx context.Context) wazero.Runtime {
runtimeConfig := wazero.
NewRuntimeConfig().
WithCloseOnContextDone(true)
if err := json.Unmarshal(data, &jpm); err != nil {
return err
}
r := wazero.NewRuntimeWithConfig(ctx, runtimeConfig)
reader := s2.NewReader(bytes.NewReader(jpm.PluginPayload))
if plainData, err := io.ReadAll(reader); err != nil {
return err
} else {
m.PluginPayload = MemoryPayload(plainData)
}
m.PluginCategory = jpm.PluginCategory
m.PluginType = jpm.PluginType
m.modSpec = jpm.ModuleSpec
return nil
}
func (m Module) MarshalJSON() ([]byte, error) {
buf := bytes.NewBuffer(nil)
writer := s2.NewWriter(buf)
if _, err := ioutils.CopyWithPooledBuffer(writer, m.PluginPayload.Reader()); err != nil {
return nil, err
}
if err := writer.Close(); err != nil {
return nil, err
}
jpm := jsonPluginModule{
PluginCategory: m.PluginCategory,
PluginType: m.PluginType,
PluginPayload: buf.Bytes(),
ModuleSpec: m.modSpec,
}
return json.Marshal(jpm)
}
type wasiPluginExecContext struct {
ctx modules.ExecutionContext
memMgr *memoryManager
}
func (c *wasiPluginExecContext) lookPath(ctx context.Context, m api.Module, offset, byteCount uint32) uint64 {
buf, ok := m.Memory().Read(offset, byteCount)
if !ok {
return 0
}
lookupPathReq := new(rpcv1.LookupPathRequest)
if err := proto.Unmarshal(buf, lookupPathReq); err != nil {
c.ctx.Logger().Error("failed to unmarshal LookupPathRequest", slog.String("err", err.Error()))
return 0
}
if lookupPathReq.Command == "" {
c.ctx.Logger().Error("cannot lookup empty command")
return 0
}
foundPath, err := exec.LookPath(lookupPathReq.Command)
lookupPathResp := &rpcv1.LookupPathResponse{
Path: foundPath,
}
if err != nil {
lookupPathResp.Error = err.Error()
}
if ptrSize, err := c.memMgr.WriteMessage(ctx, m, lookupPathResp); err != nil {
c.ctx.Logger().Error("Failed to write message", slog.String("err", err.Error()))
return 0
} else {
return ptrSize
}
}
func (c *wasiPluginExecContext) exec(ctx context.Context, m api.Module, offset, byteCount uint32) uint64 {
buf, ok := m.Memory().Read(offset, byteCount)
if !ok {
return 0
}
execReq := new(rpcv1.ProcessStartRequest)
if err := proto.Unmarshal(buf, execReq); err != nil {
c.ctx.Logger().Error("failed to unmarshal ProcessStartRequest", slog.String("err", err.Error()))
return 0
}
execResp := new(rpcv1.ProcessStartResponse)
cmd, err := sh.PrepareCommand(c.ctx, execReq.Command, execReq.Args...)
if err != nil {
c.ctx.Logger().Error("failed to prepare command", slog.String("err", err.Error()))
return 0
}
cmd.AddEnv(execReq.Environment)
if len(execReq.Stdin) > 0 {
cmd.SetStdIn(bytes.NewReader(execReq.Stdin))
}
if len(execReq.WorkingDirectory) > 0 {
cmd.SetWorkingDir(execReq.WorkingDirectory)
}
if err := cmd.Run(); err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
execResp.ExitCode = int32(exitErr.ExitCode())
execResp.Stderr = exitErr.Stderr
} else {
execResp.ExitCode = 1
}
execResp.Error = err.Error()
}
if ptrSize, err := c.memMgr.WriteMessage(ctx, m, execResp); err != nil {
c.ctx.Logger().Error("Failed to write message", slog.String("err", err.Error()))
return 0
} else {
return ptrSize
}
}
func (c *wasiPluginExecContext) log(ctx context.Context, m api.Module, offset, byteCount uint32) {
buf, ok := m.Memory().Read(offset, byteCount)
if !ok {
return
}
logger := c.ctx.Logger()
taskLog := new(rpcv1.TaskLog)
if err := proto.Unmarshal(buf, taskLog); err != nil {
logger.Warn("failed to unmarshal task log", slog.String("err", err.Error()))
return
}
rec := slog.NewRecord(time.UnixMicro(taskLog.Time), slog.Level(taskLog.Level), taskLog.Message, 0)
for i := range taskLog.Attributes {
attr := taskLog.Attributes[i]
rec.AddAttrs(slog.String(attr.Key, attr.Value))
}
_ = logger.Handler().Handle(ctx, rec)
}
func (c *wasiPluginExecContext) getState(ctx context.Context, m api.Module, offset, byteCount uint32) uint64 {
buf, ok := m.Memory().Read(offset, byteCount)
if !ok {
return 0
}
getStateReq := new(rpcv1.GetStateRequest)
if err := proto.Unmarshal(buf, getStateReq); err != nil {
c.ctx.Logger().Error("failed to unmarshal getStateRequest", slog.String("err", err.Error()))
return 0
}
resp := new(rpcv1.GetStateResponse)
val, _, err := c.ctx.GetState(ctx, string(getStateReq.Key))
if err != nil {
c.ctx.Logger().Error("failed to get state", slog.String("err", err.Error()))
return 0
}
resp.Data = val
if ptrSize, err := c.memMgr.WriteMessage(ctx, m, resp); err != nil {
c.ctx.Logger().Error("Failed to write message", slog.String("err", err.Error()))
return 0
} else {
return ptrSize
}
}
func (c *wasiPluginExecContext) setState(ctx context.Context, m api.Module, offset, byteCount uint32) (result uint64) {
buf, ok := m.Memory().Read(offset, byteCount)
if !ok {
return 0
}
setState := new(rpcv1.SetState)
if err := proto.Unmarshal(buf, setState); err != nil {
c.ctx.Logger().Error("failed to unmarshal SetState", slog.String("err", err.Error()))
return 0
}
var resp rpcv1.Result
if err := c.ctx.SetState(ctx, string(setState.Key), setState.Data); err != nil {
c.ctx.Logger().Error("failed to set state", slog.String("err", err.Error()))
resp.Error = err.Error()
} else {
resp.Success = true
}
if ptrSize, err := c.memMgr.WriteMessage(ctx, m, &resp); err != nil {
c.ctx.Logger().Error("Failed to write message", slog.String("err", err.Error()))
return 0
} else {
return ptrSize
}
return r
}

View file

@ -2,6 +2,8 @@ package plugin
import (
"bytes"
"crypto/sha256"
"fmt"
"io"
"os"
"sync"
@ -28,9 +30,10 @@ func (m MemoryPayload) Bytes() ([]byte, error) {
}
type PayloadFile struct {
readOnce sync.Once
payload []byte
Path string
payload []byte
Checksum []byte
readOnce sync.Once
}
func (f *PayloadFile) Reader() io.Reader {
@ -48,6 +51,16 @@ func (f *PayloadFile) Bytes() ([]byte, error) {
func (f *PayloadFile) readPayloadFile() (err error) {
f.readOnce.Do(func() {
f.payload, err = os.ReadFile(f.Path)
if err != nil {
return
}
hash := sha256.New()
_, _ = hash.Write(f.payload)
actualHash := hash.Sum(nil)
if !bytes.Equal(f.Checksum, actualHash) {
err = fmt.Errorf("plugin checksum mismatch: expected %x, got %x", f.Checksum, actualHash)
}
})
return
}

View file

@ -32,6 +32,7 @@ func unwrap(val cty.Value) *rpcv1.ModuleSpec_Value {
},
}
case val.Type().IsTupleType():
//nolint:gocritic // eventually, there will be more cases
switch {
case val.Type().TupleElementTypes()[0].Equals(cty.String):
specVal := &rpcv1.ModuleSpec_Value{
@ -48,6 +49,7 @@ func unwrap(val cty.Value) *rpcv1.ModuleSpec_Value {
Type: rpcv1.ModuleSpec_ValueTypeMap,
ComplexValue: make(map[string]*rpcv1.ModuleSpec_Value, len(valueMap)),
}
//nolint:gocritic // eventually, there will be more cases
switch {
case val.Type().MapElementType().Equals(cty.String):
for k, v := range valueMap {

View file

@ -82,10 +82,10 @@ func (r *TypeRegistry) CreateFromHCL(
unwrapped := mod.Unwrap()
if initializer, ok := unwrapped.(Initializer); ok {
if m, err := initializer.Init(hclCtx); err != nil {
if initialized, err := initializer.Init(hclCtx); err != nil {
return nil, err
} else {
mod.SetModule(m)
mod.SetModule(initialized)
}
}

View file

@ -70,6 +70,6 @@ func specOf(typeName Category, moduleName string) moduleSpec {
}
type moduleSpec struct {
TypeName Category
ModuleName string
TypeName Category
}

View file

@ -48,6 +48,6 @@ type Plugins interface {
List(ctx context.Context) ([]Plugin, error)
Remove(ctx context.Context, id uuid.UUID) error
ModulesForPlugin(ctx context.Context, id uuid.UUID) ([]PluginModule, error)
UpsertPlugin(ctx context.Context, plugin Plugin) (pluginId uuid.UUID, err error)
UpsertPlugin(ctx context.Context, plugin Plugin) (pluginID uuid.UUID, err error)
UpsertModules(ctx context.Context, modules []PluginModule) error
}

View file

@ -4,6 +4,7 @@ import (
"net/url"
rpcv1 "code.icb4dc0.de/buildr/api/generated/rpc/v1"
"code.icb4dc0.de/buildr/buildr/modules/state/ent"
"github.com/google/uuid"
@ -18,10 +19,11 @@ type Plugin struct {
}
type PluginModule struct {
ID *uuid.UUID
PluginID uuid.UUID
Type string
Category rpcv1.Category
ID *uuid.UUID
Type string
DefaultSpec []byte
Category rpcv1.Category
PluginID uuid.UUID
}
func pluginFromEntity(p *ent.Plugin) (Plugin, error) {

View file

@ -4,6 +4,7 @@ import (
"context"
rpcv1 "code.icb4dc0.de/buildr/api/generated/rpc/v1"
"code.icb4dc0.de/buildr/buildr/modules/state/ent"
"code.icb4dc0.de/buildr/buildr/modules/state/ent/plugin"
"code.icb4dc0.de/buildr/buildr/modules/state/ent/pluginmodule"
@ -38,11 +39,13 @@ func (e EntPluginsRepo) ModulesForPlugin(ctx context.Context, id uuid.UUID) ([]P
result := make([]PluginModule, 0, len(modules))
for _, module := range modules {
module := module
result = append(result, PluginModule{
ID: &module.ID,
PluginID: id,
Type: module.Type,
Category: rpcv1.Category(module.Category),
ID: &module.ID,
PluginID: id,
Type: module.Type,
Category: rpcv1.Category(module.Category),
DefaultSpec: module.DefaultSpec,
})
}
@ -67,14 +70,14 @@ func (e EntPluginsRepo) List(ctx context.Context) ([]Plugin, error) {
return plugins, nil
}
func (e EntPluginsRepo) UpsertPlugin(ctx context.Context, plugin Plugin) (pluginId uuid.UUID, err error) {
func (e EntPluginsRepo) UpsertPlugin(ctx context.Context, p Plugin) (pluginID uuid.UUID, err error) {
return e.client.Plugin.
Create().
SetNillableID(plugin.ID).
SetName(plugin.Name).
SetLocalPath(plugin.LocalPath).
SetURL(plugin.URL.String()).
SetHash(plugin.Hash).
SetNillableID(p.ID).
SetName(p.Name).
SetLocalPath(p.LocalPath).
SetURL(p.URL.String()).
SetHash(p.Hash).
OnConflict().
UpdateNewValues().
ID(ctx)
@ -88,6 +91,7 @@ func (e EntPluginsRepo) UpsertModules(ctx context.Context, modules []PluginModul
SetPluginID(module.PluginID).
SetType(module.Type).
SetCategory(int(module.Category)).
SetDefaultSpec(module.DefaultSpec).
OnConflict().
UpdateNewValues().
Exec(ctx)

View file

@ -24,6 +24,8 @@ func (PluginModule) Fields() []ent.Field {
MinLen(1),
field.Int("category").
Min(1),
field.Bytes("default_spec").
NotEmpty(),
}
}

View file

@ -4,13 +4,12 @@ import (
"errors"
"fmt"
"io"
"log/slog"
"os"
"os/exec"
"path/filepath"
"strings"
"log/slog"
"code.icb4dc0.de/buildr/buildr/internal/sh"
"code.icb4dc0.de/buildr/buildr/modules"
)

View file

@ -1,10 +1,12 @@
package task
import (
"context"
"code.icb4dc0.de/buildr/buildr/modules"
)
func (s ScriptTask) Help() (help modules.Help, okay bool) {
func (s ScriptTask) Help(context.Context) (help modules.Help, err error) {
return modules.Help{
Name: "Script - run single commands or complete scripts",
Description: `This module helps to run arbitrary commands or complete scripts.
@ -93,5 +95,5 @@ like changing the current working directory.`,
},
},
},
}, true
}, nil
}

View file

@ -16,8 +16,12 @@ func TestExamples(t *testing.T) {
if err != nil {
t.Fatal(err)
}
s := new(task.ScriptTask)
for _, e := range s.Help().Examples {
help, err := new(task.ScriptTask).Help(context.Background())
if err != nil {
t.Fatal(err)
}
for _, e := range help.Examples {
e := e
t.Run(e.Name, func(t *testing.T) {
t.Parallel()

View file

@ -3,11 +3,10 @@ package modules
import (
"context"
"io"
"log/slog"
"testing"
"code.icb4dc0.de/buildr/buildr/modules/state"
"log/slog"
)
var _ ExecutionContext = (*TestExecutionContext)(nil)