feat(packaging): add OCI image creation as package task

This commit is contained in:
Peter 2023-04-04 18:19:58 +02:00
parent 72b85dcace
commit a859b564a9
Signed by: prskr
GPG key ID: C1DB5D2E8DB512F9
35 changed files with 1277 additions and 231 deletions

View file

@ -11,8 +11,10 @@ build "go_build" "build_linux" {
goarch = each.key
flags = [
"-v",
"-trimpath",
"-a",
"-installsuffix=cgo"
]
ldflags = [

View file

@ -11,3 +11,37 @@ package "archive" "linux_amd64" {
builds.build_linux_amd64.id = "dist/linux_amd64"
}
}*/
package "container_image" "buildr_image" {
base_image = "gcr.io/distroless/static"
image_name = "code.icb4dc0.de/buildr/buildr"
platform = "linux/amd64"
entrypoint = "/usr/local/bin/buildr"
tags = [
"latest"
]
publish_to_daemon = true
publish_to_registry = true
registry_auth "code.icb4dc0.de" {
auth = base64encode(format("prskr:%s", from_vault("gitea_token")))
}
input_mapping = {
"${buildr.repo.root}" = "."
"${builds.build_linux["amd64"].out_dir}" = "out/"
}
content = {
"out/buildr" = "/usr/local/bin/buildr",
"README.md" = "/usr/local/man/README.md"
}
depends_on = [
builds.build_linux["amd64"].id
]
}

View file

@ -1 +1 @@
{"github_token":"m1m7a4MCEAvPwwMo:AAAAAAAAAAA=:bHra57FHFNJHqsFIN2JygV3hB70rqa3SMvXSaGR69IbIZ0/mcE5bmQS/HRmdqJVYZPmgI5/VNaw="}
{"gitea_token":"0tfC3x/sBkcCUw//:AAAAAAAAAAA=:XQ7xJNmvJgYB1Wq8v7Np9sf1AHCWowbs4usvGYSBAQLqaweLAbXLJCTtZGLjzAV7H+AtMhjD5D8=","github_token":"92+1SdEnrAtwZ2BD:AAAAAAAAAAA=:EcEIm6qUpZqCSPUe0ss48WO5+rwfJQPQ2oV63bPnmIU1Dh2i88CAOkUPMWtW5iGztv22+TSPewA="}

29
go.mod
View file

@ -3,11 +3,14 @@ module code.icb4dc0.de/buildr/buildr
go 1.20
require (
github.com/docker/docker v23.0.1+incompatible
github.com/google/go-containerregistry v0.14.0
github.com/google/go-github/v50 v50.1.0
github.com/google/uuid v1.3.0
github.com/hanwen/go-fuse/v2 v2.2.0
github.com/hashicorp/hcl/v2 v2.16.2
github.com/jinzhu/copier v0.3.5
github.com/opencontainers/image-spec v1.1.0-rc2
github.com/spf13/cobra v1.6.1
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.15.0
@ -21,30 +24,46 @@ require (
replace golang.org/x/net => golang.org/x/net v0.8.0
require (
github.com/Microsoft/go-winio v0.6.0 // indirect
github.com/agext/levenshtein v1.2.1 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/cli v23.0.1+incompatible // indirect
github.com/docker/distribution v2.8.1+incompatible // indirect
github.com/docker/docker-credential-helpers v0.7.0 // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/klauspost/compress v1.16.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/spf13/afero v1.9.3 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
golang.org/x/net v0.6.0 // indirect
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect
github.com/vbatts/tar-split v0.11.2 // indirect
golang.org/x/mod v0.9.0 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/oauth2 v0.6.0 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/text v0.8.0 // indirect
golang.org/x/tools v0.7.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.28.1 // indirect
google.golang.org/protobuf v1.29.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

69
go.sum
View file

@ -36,8 +36,11 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8=
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
@ -50,10 +53,25 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k=
github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/cli v23.0.1+incompatible h1:LRyWITpGzl2C9e9uGxzisptnxAn1zfZKXy13Ul2Q5oM=
github.com/docker/cli v23.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v23.0.1+incompatible h1:vjgvJZxprTTE1A37nm+CLNAdwu6xZekyoiVlUZEINcY=
github.com/docker/docker v23.0.1+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/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@ -67,6 +85,8 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@ -93,8 +113,8 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@ -109,6 +129,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
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.14.0 h1:z58vMqHxuwvAsVwvKEkmVBz2TlgBgH5k6koEXBtlYkw=
github.com/google/go-containerregistry v0.14.0/go.mod h1:aiJ2fp/SXvkWgmYHioXnbMdlgB8eXiiYOY55gfN91Wk=
github.com/google/go-github/v50 v50.1.0 h1:hMUpkZjklC5GJ+c3GquSqOP/T4BNsB7XohaPhtMOzRk=
github.com/google/go-github/v50 v50.1.0/go.mod h1:Ev4Tre8QoKiolvbpOSG3FIi4Mlon3S2Nt9W5JYqKiwA=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
@ -143,13 +165,17 @@ github.com/hashicorp/hcl/v2 v2.16.2 h1:mpkHZh/Tv+xet3sy3F9Ld4FyI2tUpWe9x3XtPx9f1
github.com/hashicorp/hcl/v2 v2.16.2/go.mod h1:JRmR89jycNkrrqnMmvPDMd56n1rQJ2Q6KocSLCMCXng=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg=
github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
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.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4=
github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
@ -160,12 +186,21 @@ github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3v
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297 h1:yH0SvLzcbZxcJXho2yh7CqdENGMQe73Cw3woZBpPli0=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
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-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034=
github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ=
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@ -173,8 +208,13 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk=
github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
@ -200,6 +240,9 @@ github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKs
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME=
github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@ -258,6 +301,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/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.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
golang.org/x/mod v0.9.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/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -269,8 +314,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 h1:nt+Q6cXKz4MosCSpnbMtqiQ8Oz0pxTef2B4Vca2lvfk=
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw=
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -294,6 +339,7 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -316,6 +362,7 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -335,6 +382,7 @@ golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@ -372,6 +420,7 @@ golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
@ -380,10 +429,13 @@ golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
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.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
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=
@ -479,11 +531,11 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.29.0 h1:44S3JjaKmLEE4YIkjzexaP+NzZsudE3Zin5Njn/pYX0=
google.golang.org/protobuf v1.29.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
@ -491,6 +543,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

30
internal/cmd/args.go Normal file
View file

@ -0,0 +1,30 @@
package cmd
import (
"strings"
"github.com/spf13/cobra"
"golang.org/x/exp/maps"
"code.icb4dc0.de/buildr/buildr/modules"
)
func validArgsFor(moduleType modules.ModuleType) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
tasks := maps.Keys(repo.ModulesByType(moduleType))
filtered := make([]string, 0, len(tasks))
for i := range tasks {
if strings.HasPrefix(tasks[i], toComplete) {
filtered = append(filtered, tasks[i])
}
}
return filtered, cobra.ShellCompDirectiveNoFileComp
}
}

View file

@ -2,37 +2,19 @@ package cmd
import (
"fmt"
"strings"
"github.com/spf13/cobra"
"golang.org/x/exp/maps"
"code.icb4dc0.de/buildr/buildr/modules"
)
var buildCmd = &cobra.Command{
Use: "build",
RunE: runBuild,
Args: cobra.ExactArgs(1),
SilenceUsage: true,
SilenceErrors: true,
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
tasks := maps.Keys(repo.Builds())
filtered := make([]string, 0, len(tasks))
for i := range tasks {
if strings.HasPrefix(tasks[i], toComplete) {
filtered = append(filtered, tasks[i])
}
}
return filtered, cobra.ShellCompDirectiveNoFileComp
},
Use: "build",
RunE: runBuild,
Args: cobra.ExactArgs(1),
SilenceUsage: true,
SilenceErrors: true,
ValidArgsFunction: validArgsFor(modules.ModuleTypeBuild),
}
func runBuild(cmd *cobra.Command, args []string) error {

31
internal/cmd/package.go Normal file
View file

@ -0,0 +1,31 @@
package cmd
import (
"fmt"
"github.com/spf13/cobra"
"code.icb4dc0.de/buildr/buildr/modules"
)
var packageCmd = &cobra.Command{
Use: "package",
Args: cobra.ExactArgs(1),
SilenceUsage: true,
SilenceErrors: true,
RunE: runPackage,
ValidArgsFunction: validArgsFor(modules.ModuleTypePackage),
}
func runPackage(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("no package specified")
}
plan, err := modules.NewExecutionPlanFor(modules.ModuleTypePackage, args[0], repo)
if err != nil {
return err
}
return plan.Execute(cmd.Context(), repo.Buildr)
}

View file

@ -16,6 +16,7 @@ import (
"code.icb4dc0.de/buildr/buildr/modules"
"code.icb4dc0.de/buildr/buildr/modules/build"
"code.icb4dc0.de/buildr/buildr/modules/buildr"
"code.icb4dc0.de/buildr/buildr/modules/packaging"
"code.icb4dc0.de/buildr/buildr/modules/task"
"code.icb4dc0.de/buildr/buildr/modules/tool"
)
@ -38,7 +39,7 @@ var (
)
func init() {
rootCmd.AddCommand(toolCmd, taskCmd, buildCmd, vaultCmd)
rootCmd.AddCommand(toolCmd, taskCmd, buildCmd, packageCmd, vaultCmd)
rootCmd.PersistentFlags().StringVar(&buildRDir, "buildR-dir", ".buildr", "Directory where to look for BuildR files - if relative path will be relavtive to repository root (where the next .git directory resides")
rootCmd.PersistentFlags().String("vault-passphrase", "", "Password for vault - has precedence over vault-passphrase-file")
rootCmd.PersistentFlags().String("vault-passphrase-file", "", "File containing the vault passphrase")
@ -93,11 +94,12 @@ func initBuildR(cmd *cobra.Command, _ []string) error {
return err
}
registry := modules.NewRegistry()
build.RegisterTypes(registry)
tool.RegisterTypes(registry)
task.RegisterTypes(registry)
registry := modules.NewRegistry(
build.Registration,
tool.Registration,
task.Registration,
packaging.Registration,
)
buildrInstance, err = rawSpec.Config.Buildr(buildRDir, repoRoot)
if err != nil {

View file

@ -2,37 +2,19 @@ package cmd
import (
"fmt"
"strings"
"github.com/spf13/cobra"
"golang.org/x/exp/maps"
"code.icb4dc0.de/buildr/buildr/modules"
)
var taskCmd = &cobra.Command{
Use: "task",
RunE: runTask,
Args: cobra.ExactArgs(1),
SilenceUsage: true,
SilenceErrors: true,
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
tasks := maps.Keys(repo.Tasks())
filtered := make([]string, 0, len(tasks))
for i := range tasks {
if strings.HasPrefix(tasks[i], toComplete) {
filtered = append(filtered, tasks[i])
}
}
return filtered, cobra.ShellCompDirectiveNoFileComp
},
Use: "task",
RunE: runTask,
Args: cobra.ExactArgs(1),
SilenceUsage: true,
SilenceErrors: true,
ValidArgsFunction: validArgsFor(modules.ModuleTypeTask),
}
func runTask(cmd *cobra.Command, args []string) error {

View file

@ -2,37 +2,19 @@ package cmd
import (
"fmt"
"strings"
"github.com/spf13/cobra"
"golang.org/x/exp/maps"
"code.icb4dc0.de/buildr/buildr/modules"
)
var toolCmd = &cobra.Command{
Use: "tool",
RunE: runTool,
Args: cobra.ExactArgs(1),
SilenceUsage: true,
SilenceErrors: true,
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
tasks := maps.Keys(repo.Tools())
filtered := make([]string, 0, len(tasks))
for i := range tasks {
if strings.HasPrefix(tasks[i], toComplete) {
filtered = append(filtered, tasks[i])
}
}
return filtered, cobra.ShellCompDirectiveNoFileComp
},
Use: "tool",
RunE: runTool,
Args: cobra.ExactArgs(1),
SilenceUsage: true,
SilenceErrors: true,
ValidArgsFunction: validArgsFor(modules.ModuleTypeTool),
}
func runTool(cmd *cobra.Command, args []string) error {

View file

@ -1,31 +0,0 @@
package parsing
import (
"github.com/hashicorp/hcl/v2"
"code.icb4dc0.de/buildr/buildr/modules"
)
var _ modules.Block = (*BuildBlock)(nil)
type BuildBlock struct {
BuildType string `hcl:",label"`
BuildName string `hcl:",label"`
Remain hcl.Body `hcl:",remain"`
}
func (b BuildBlock) Type() modules.ModuleType {
return modules.ModuleTypeBuild
}
func (b BuildBlock) Name() string {
return b.BuildName
}
func (b BuildBlock) Body() hcl.Body {
return b.Remain
}
func (b BuildBlock) Module() string {
return b.BuildType
}

View file

@ -92,9 +92,9 @@ func prepareBlockForParsing(block modules.Block, evalCtx *hcl.EvalContext, build
gb := GenericBlock{
blockType: block.Type(),
moduleName: block.Module(),
name: blockName,
body: initBlock(blockCopy, block.Type(), blockName, buildrInstance.Config.OutDirectory),
ModuleName: block.Module(),
BlockName: blockName,
BlockBody: initBlock(blockCopy, block.Type(), blockName, buildrInstance.Config.OutDirectory),
}
result.Specs = append(result.Specs, parsingSpecOf(gb, vals))

View file

@ -8,15 +8,29 @@ import (
var _ modules.Block = (*GenericBlock)(nil)
type GenericBlocks []GenericBlock
func (b GenericBlocks) Blocks() []GenericBlock {
return b
}
func (b GenericBlocks) WithType(blockType modules.ModuleType) GenericBlocks {
for i := range b {
b[i].blockType = blockType
}
return b
}
type GenericBlock struct {
blockType modules.ModuleType
moduleName string
name string
body hcl.Body
ModuleName string `hcl:",label"`
BlockName string `hcl:",label"`
BlockBody hcl.Body `hcl:",remain"`
}
func (g GenericBlock) Module() string {
return g.moduleName
return g.ModuleName
}
func (g GenericBlock) Type() modules.ModuleType {
@ -24,9 +38,9 @@ func (g GenericBlock) Type() modules.ModuleType {
}
func (g GenericBlock) Name() string {
return g.name
return g.BlockName
}
func (g GenericBlock) Body() hcl.Body {
return g.body
return g.BlockBody
}

View file

@ -12,11 +12,12 @@ import (
)
type RawSpec struct {
Config BuildrConfig `hcl:"buildr,block"`
Locals []LocalsBlock `hcl:"locals,block"`
Tools []ToolBlock `hcl:"tool,block"`
Tasks []TaskBlock `hcl:"task,block"`
Builds []BuildBlock `hcl:"build,block"`
Config BuildrConfig `hcl:"buildr,block"`
Locals []LocalsBlock `hcl:"locals,block"`
Tools GenericBlocks `hcl:"tool,block"`
Tasks GenericBlocks `hcl:"task,block"`
Builds GenericBlocks `hcl:"build,block"`
Packages GenericBlocks `hcl:"package,block"`
}
func (s RawSpec) Repository(ctx context.Context, b buildr.Buildr, svc *services.Collection) (repo *modules.Repository, err error) {
@ -36,19 +37,25 @@ func (s RawSpec) Repository(ctx context.Context, b buildr.Buildr, svc *services.
parsingSpecs := make([]blockParsingSpec, 0, len(s.Tools)+len(s.Tasks)+len(s.Builds))
if specs, err := s.buildParsingInventory(toBlocks(s.Tools), "tools", evalCtx, b); err != nil {
if specs, err := s.buildParsingInventory(toBlocks(s.Tools.WithType(modules.ModuleTypeTool)), evalCtx, b); err != nil {
return nil, err
} else {
parsingSpecs = append(parsingSpecs, specs...)
}
if specs, err := s.buildParsingInventory(toBlocks(s.Tasks), "tasks", evalCtx, b); err != nil {
if specs, err := s.buildParsingInventory(toBlocks(s.Tasks.WithType(modules.ModuleTypeTask)), evalCtx, b); err != nil {
return nil, err
} else {
parsingSpecs = append(parsingSpecs, specs...)
}
if specs, err := s.buildParsingInventory(toBlocks(s.Builds), "builds", evalCtx, b); err != nil {
if specs, err := s.buildParsingInventory(toBlocks(s.Builds.WithType(modules.ModuleTypeBuild)), evalCtx, b); err != nil {
return nil, err
} else {
parsingSpecs = append(parsingSpecs, specs...)
}
if specs, err := s.buildParsingInventory(toBlocks(s.Packages.WithType(modules.ModuleTypePackage)), evalCtx, b); err != nil {
return nil, err
} else {
parsingSpecs = append(parsingSpecs, specs...)
@ -65,10 +72,16 @@ func (s RawSpec) Repository(ctx context.Context, b buildr.Buildr, svc *services.
func (s RawSpec) buildParsingInventory(
blocks []modules.Block,
groupName string,
evalCtx *hcl.EvalContext,
buildrInstance buildr.Buildr,
) ([]blockParsingSpec, error) {
if len(blocks) == 0 {
return nil, nil
}
groupName := blocks[0].Type().GroupName()
blockGroupRepresentation := make(map[string]cty.Value, len(blocks))
blockGroupParsingSpecs := make([]blockParsingSpec, 0)

View file

@ -1,31 +0,0 @@
package parsing
import (
"github.com/hashicorp/hcl/v2"
"code.icb4dc0.de/buildr/buildr/modules"
)
var _ modules.Block = (*TaskBlock)(nil)
type TaskBlock struct {
TaskType string `hcl:",label"`
TaskName string `hcl:",label"`
Remain hcl.Body `hcl:",remain"`
}
func (b TaskBlock) Type() modules.ModuleType {
return modules.ModuleTypeTask
}
func (b TaskBlock) Name() string {
return b.TaskName
}
func (b TaskBlock) Body() hcl.Body {
return b.Remain
}
func (b TaskBlock) Module() string {
return b.TaskType
}

View file

@ -1,31 +0,0 @@
package parsing
import (
"github.com/hashicorp/hcl/v2"
"code.icb4dc0.de/buildr/buildr/modules"
)
var _ modules.Block = (*ToolBlock)(nil)
type ToolBlock struct {
ToolType string `hcl:",label"`
ToolName string `hcl:",label"`
Remain hcl.Body `hcl:",remain"`
}
func (b ToolBlock) Type() modules.ModuleType {
return modules.ModuleTypeTool
}
func (b ToolBlock) Name() string {
return b.ToolName
}
func (b ToolBlock) Body() hcl.Body {
return b.Remain
}
func (b ToolBlock) Module() string {
return b.ToolType
}

View file

@ -1,12 +1,11 @@
package modules
type BaseModule struct {
Id string `hcl:"id,optional"`
OutputDir string `hcl:"out_dir,optional"`
Name string `hcl:"name,optional"`
RepoIsolation bool `hcl:"repo_isolation,optional"`
Deps []string `hcl:"depends_on,optional"`
InputMapping map[string]string `hcl:"input_mapping,optional"`
Id string `hcl:"id,optional"`
OutputDir string `hcl:"out_dir,optional"`
Name string `hcl:"name,optional"`
Deps []string `hcl:"depends_on,optional"`
InputMapping map[string]string `hcl:"input_mapping,optional"`
}
func (m BaseModule) ModuleName() string {

View file

@ -21,19 +21,15 @@ type GoBuild struct {
Main string `hcl:"main"`
GoOS string `hcl:"goos"`
GoArch string `hcl:"goarch"`
Flags []string `hcl:"flags"`
LdFlags []string `hcl:"ldflags"`
Env map[string]string `hcl:"env"`
Flags []string `hcl:"flags,optional"`
LdFlags []string `hcl:"ldflags,optional"`
Env map[string]string `hcl:"env,optional"`
}
func (g GoBuild) Type() modules.ModuleType {
return modules.ModuleTypeBuild
}
func (g GoBuild) ModuleName() string {
return g.Name
}
func (g GoBuild) Execute(ctx modules.ExecutionContext) (err error) {
logger := ctx.Logger()
@ -69,10 +65,14 @@ func (g GoBuild) Execute(ctx modules.ExecutionContext) (err error) {
return err
}
cmd.AddEnv(map[string]string{
"GOARCH": g.GoArch,
"GOOS": g.GoOS,
})
if g.Env == nil {
g.Env = make(map[string]string, 2)
}
g.Env["GOARCH"] = g.GoArch
g.Env["GOOS"] = g.GoOS
cmd.AddEnv(g.Env)
defer func() {
err = errors.Join(err, cmd.Close())

View file

@ -8,7 +8,7 @@ import (
"code.icb4dc0.de/buildr/buildr/modules"
)
func RegisterTypes(registry *modules.TypeRegistry) {
var Registration = modules.RegistrationFunc(func(registry *modules.TypeRegistry) {
registry.RegisterModule(modules.ModuleTypeBuild, "go_build", modules.ModuleFactoryFunc(func(block modules.Block, hclCtx *hcl.EvalContext) (modules.Module, error) {
b := new(GoBuild)
diags := gohcl.DecodeBody(block.Body(), hclCtx, b)
@ -19,4 +19,4 @@ func RegisterTypes(registry *modules.TypeRegistry) {
return b, nil
}))
}
})

View file

@ -0,0 +1,25 @@
package strings
import (
"encoding/base64"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/function"
)
func Base64Encode() function.Function {
return function.New(&function.Spec{
Description: "Encode a string as base64",
Params: []function.Parameter{
{
Name: "input",
Description: "input string to be encoded as base64",
Type: cty.String,
},
},
Type: function.StaticReturnType(cty.String),
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
return cty.StringVal(base64.StdEncoding.EncodeToString([]byte(args[0].AsString()))), nil
},
})
}

View file

@ -0,0 +1,61 @@
package strings
import (
"fmt"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/function"
)
func Format() function.Function {
return function.New(&function.Spec{
Description: "Format a string",
Params: []function.Parameter{
{
Name: "format",
Description: "format for the resulting string",
Type: cty.String,
},
},
VarParam: &function.Parameter{
Name: "args",
Description: "variadic args for format",
Type: cty.DynamicPseudoType,
},
Type: function.StaticReturnType(cty.String),
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
format := args[0].AsString()
formatArgs := make([]any, 0, len(args))
if len(args) > 1 {
for i := 1; i < len(args); i++ {
if unboxed := unbox(args[i]); unboxed != nil {
formatArgs = append(formatArgs, unboxed)
}
}
}
return cty.StringVal(fmt.Sprintf(format, formatArgs...)), nil
},
})
}
func unbox(in cty.Value) any {
switch in.Type() {
case cty.String:
return in.AsString()
case cty.Number:
bigFloat := in.AsBigFloat()
if bigFloat.IsInt() {
i, _ := bigFloat.Int64()
return i
} else {
f, _ := bigFloat.Float64()
return f
}
case cty.Bool:
return in.True()
default:
return nil
}
}

View file

@ -4,4 +4,6 @@ import "github.com/hashicorp/hcl/v2"
func RegisterInContext(evalCtx *hcl.EvalContext) {
evalCtx.Functions["trim"] = Trim()
evalCtx.Functions["format"] = Format()
evalCtx.Functions["base64encode"] = Base64Encode()
}

View file

@ -0,0 +1,94 @@
package ociimg
import (
"context"
"errors"
"fmt"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/daemon"
"golang.org/x/exp/slog"
)
func newContainerDaemon(imageName string, logger *slog.Logger, tags []string, cli daemon.Client) containerDaemon {
return containerDaemon{
imageName: imageName,
logger: logger,
client: cli,
tags: tags,
}
}
type containerDaemon struct {
imageName string
logger *slog.Logger
client daemon.Client
tags []string
}
func (d containerDaemon) Publish(ctx context.Context, result imageOrIndex) (name.Reference, error) {
if len(d.tags) == 0 {
return nil, errors.New("at least one tag must be specified")
}
var img v1.Image
switch i := result.(type) {
case v1.Image:
img = i
case v1.ImageIndex:
im, err := i.IndexManifest()
if err != nil {
return nil, err
}
if len(im.Manifests) < 1 {
return nil, errors.New("index is empty")
}
img, err = i.Image(im.Manifests[0].Digest)
if err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("failed to interpret %s result as image: %v", d.imageName, result)
}
defaultTag, err := name.NewTag(fmt.Sprintf("%s:%s", d.imageName, d.tags[0]))
if err != nil {
return nil, err
}
d.logger.Info("Loading", slog.Any("defaultTag", defaultTag))
if resp, err := daemon.Write(defaultTag, img, d.getOpts(ctx)...); err != nil {
d.logger.Debug("daemon.Write", slog.String("response", resp))
return nil, err
}
d.logger.Info("Loaded", slog.Any("defaultTag", defaultTag))
for _, tagName := range d.tags[1:] {
d.logger.Info("Adding tag", slog.String("tag", tagName))
tag, err := name.NewTag(fmt.Sprintf("%s:%s", d.imageName, tagName))
if err != nil {
return nil, err
}
if err := daemon.Tag(defaultTag, tag, d.getOpts(ctx)...); err != nil {
return nil, err
}
d.logger.Info("Added tag", slog.String("tag", tagName))
}
return &defaultTag, nil
}
func (d containerDaemon) getOpts(ctx context.Context) []daemon.Option {
return []daemon.Option{
daemon.WithContext(ctx),
daemon.WithClient(d.client),
}
}

View file

@ -0,0 +1,47 @@
package ociimg
import (
"context"
"fmt"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/google/go-containerregistry/pkg/v1/types"
)
func imageFromIndex(index v1.ImageIndex, platform *v1.Platform) (v1.Image, v1.Descriptor, error) {
im, err := index.IndexManifest()
if err != nil {
return nil, v1.Descriptor{}, err
}
for i := range im.Manifests {
manifest := im.Manifests[i]
if manifest.MediaType != types.OCIManifestSchema1 && im.MediaType != types.DockerManifestSchema2 {
return nil, v1.Descriptor{}, fmt.Errorf("%q has unexpected mediaType %q in base for %q", manifest.Digest, manifest.MediaType, platform)
}
if manifest.Platform.Satisfies(*platform) {
img, err := index.Image(manifest.Digest)
return img, manifest, err
}
}
return nil, v1.Descriptor{}, fmt.Errorf("index does not contain image satisfying the requested platoform %q", platform)
}
func fetch(ctx context.Context, ref name.Reference) (imageOrIndex, error) {
ropt := []remote.Option{
remote.WithUserAgent(userAgent),
remote.WithContext(ctx),
}
desc, err := remote.Get(ref, ropt...)
if err != nil {
return nil, err
}
if desc.MediaType.IsIndex() {
return desc.ImageIndex()
}
return desc.Image()
}

View file

@ -0,0 +1,318 @@
package ociimg
import (
"archive/tar"
"context"
"fmt"
"io"
"os"
"sync"
"time"
"github.com/docker/docker/client"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/empty"
"github.com/google/go-containerregistry/pkg/v1/mutate"
"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"
"golang.org/x/exp/slog"
"code.icb4dc0.de/buildr/buildr/modules"
)
var (
_ modules.Module = (*ContainerImage)(nil)
defaultCreationTime = time.Unix(0, 0)
)
type ContainerImage struct {
modules.BaseModule `hcl:",remain"`
BaseImage string `hcl:"base_image"`
ImageName string `hcl:"image_name"`
Tags []string `hcl:"tags"`
Platform string `hcl:"platform"`
Entrypoint *string `hcl:"entrypoint,optional"`
Command []string `hcl:"command,optional"`
Content map[string]string `hcl:"content"`
PublishToDaemon bool `hcl:"publish_to_daemon,optional"`
PublishToRegistry bool `hcl:"publish_to_registry,optional"`
RegistryCredentials []RegistryAuth `hcl:"registry_auth,block"`
cache sync.Map
publishers []Publisher
}
func (o *ContainerImage) Type() modules.ModuleType {
return modules.ModuleTypePackage
}
func (o *ContainerImage) Execute(ctx modules.ExecutionContext) error {
logger := ctx.Logger()
if err := o.initPublishers(logger); err != nil {
return err
}
baseRef, base, err := o.getBaseImage(ctx, logger, o.BaseImage)
if err != nil {
return err
}
mt, err := base.MediaType()
if err != nil {
return err
}
// Annotate the base image we pass to the build function with
// annotations indicating the digest (and possibly tag) of the
// base image. This will be inherited by the image produced.
if mt != types.DockerManifestList {
baseDigest, err := base.Digest()
if err != nil {
return err
}
anns := map[string]string{
specsv1.AnnotationBaseImageDigest: baseDigest.String(),
specsv1.AnnotationBaseImageName: baseRef.Name(),
}
base = mutate.Annotations(base, anns).(imageOrIndex)
}
platform, err := v1.ParsePlatform(o.Platform)
if err != nil {
return err
}
var buildResult imageOrIndex
switch mt {
case types.OCIImageIndex, types.DockerManifestList:
baseIdx, ok := base.(v1.ImageIndex)
if !ok {
return fmt.Errorf("failed to interpret base as index: %v", base)
}
im, err := baseIdx.IndexManifest()
if err != nil {
return err
}
img, desc, err := imageFromIndex(baseIdx, platform)
if err != nil {
return err
}
img = mutate.Annotations(img, map[string]string{
specsv1.AnnotationBaseImageDigest: desc.Digest.String(),
specsv1.AnnotationBaseImageName: baseRef.Name(),
}).(v1.Image)
builtImage, err := o.buildImage(ctx, img, platform)
if err != nil {
return err
}
adds := mutate.IndexAddendum{
Add: builtImage,
Descriptor: v1.Descriptor{
URLs: desc.URLs,
MediaType: desc.MediaType,
Annotations: desc.Annotations,
Platform: desc.Platform,
},
}
buildResult = mutate.AppendManifests(
mutate.Annotations(
mutate.IndexMediaType(empty.Index, mt),
im.Annotations,
).(v1.ImageIndex),
adds,
)
case types.OCIManifestSchema1, types.DockerManifestSchema2:
baseImage, ok := base.(v1.Image)
if !ok {
return fmt.Errorf("failed to interpret base as image: %v", base)
}
buildResult, err = o.buildImage(ctx, baseImage, platform)
if err != nil {
return err
}
default:
return nil
}
for i := range o.publishers {
if _, err := o.publishers[i].Publish(ctx, buildResult); err != nil {
return err
}
}
return nil
}
func (o *ContainerImage) initPublishers(logger *slog.Logger) error {
if o.PublishToDaemon {
cli, err := client.NewClientWithOpts(
client.WithHostFromEnv(),
client.WithTLSClientConfigFromEnv(),
client.WithVersionFromEnv(),
)
if err != nil {
return err
}
o.publishers = append(o.publishers, newContainerDaemon(o.ImageName, logger, o.Tags, cli))
}
if o.PublishToRegistry {
opts := []registryPublisherOption{
withTags(o.Tags...),
}
if o.RegistryCredentials != nil {
opts = append(opts, withKeyChain(authn.NewMultiKeychain(authn.DefaultKeychain, KeyChain(o.RegistryCredentials))))
}
o.publishers = append(o.publishers, newRegistryPublisher(o.ImageName, logger, opts...))
}
return nil
}
func (o *ContainerImage) buildImage(ctx modules.ExecutionContext, base v1.Image, platform *v1.Platform) (v1.Image, error) {
// Layers should be typed to match the underlying image, since some
// registries reject mixed-type layers.
var layerMediaType types.MediaType
mt, err := base.MediaType()
if err != nil {
return nil, err
}
switch mt {
case types.OCIManifestSchema1:
layerMediaType = types.OCILayer
case types.DockerManifestSchema2:
layerMediaType = types.DockerLayer
}
if err := o.platformMatches(platform); err != nil {
return nil, err
}
contentTemp, err := os.CreateTemp(os.TempDir(), "buildr-oci-image-")
if err != nil {
return nil, err
}
defer func() {
tempFilePath := contentTemp.Name()
_ = contentTemp.Close()
_ = os.Remove(tempFilePath)
}()
if err := o.tarFiles(contentTemp, ctx.Buildr().Repo.Root); err != nil {
return nil, err
}
layer, err := tarball.LayerFromOpener(func() (io.ReadCloser, error) {
if _, err := contentTemp.Seek(0, 0); err != nil {
return nil, err
}
return io.NopCloser(contentTemp), nil
}, tarball.WithCompressedCaching, tarball.WithMediaType(layerMediaType))
if err != nil {
return nil, err
}
withContent, err := mutate.Append(base, mutate.Addendum{
Layer: layer,
History: v1.History{
Author: "buildr",
CreatedBy: fmt.Sprintf("oci_image %s", o.Name),
Created: v1.Time{Time: defaultCreationTime},
},
})
if err != nil {
return nil, err
}
cfg, err := withContent.ConfigFile()
if err != nil {
return nil, err
}
cfg = cfg.DeepCopy()
if o.Entrypoint != nil {
cfg.Config.Entrypoint = []string{*o.Entrypoint}
}
cfg.Config.Cmd = o.Command
cfg.Author = "buildr"
if cfg.Config.Labels == nil {
cfg.Config.Labels = map[string]string{}
}
return mutate.ConfigFile(withContent, cfg)
}
func (o *ContainerImage) tarFiles(outWriter io.Writer, cwd string) error {
tw := tar.NewWriter(outWriter)
defer tw.Close()
tree, err := buildDirFromContent(cwd, o.Content)
if err != nil {
return err
}
return tree.writeToTar(tw, "/")
}
func (o *ContainerImage) platformMatches(platform *v1.Platform) error {
parsed, err := v1.ParsePlatform(o.Platform)
if err != nil {
return err
}
if parsed.Satisfies(*platform) {
return nil
}
return fmt.Errorf("platform %s does not satisfy %s", platform.String(), o.Platform)
}
func (o *ContainerImage) getBaseImage(ctx context.Context, logger *slog.Logger, imageRef string) (name.Reference, imageOrIndex, error) {
ref, err := name.ParseReference(imageRef, name.WithDefaultRegistry("docker.io"))
if err != nil {
return nil, nil, fmt.Errorf("parsing base image (%q): %w", imageRef, err)
}
if v, ok := o.cache.Load(ref.String()); ok {
return ref, v.(imageOrIndex), nil
}
result, err := fetch(ctx, ref)
if err != nil {
return ref, result, err
}
if dig, ok := ref.(name.Digest); ok {
logger.Info("Using base", slog.String("base_image", fmt.Sprintf("%s@%s", ref, dig)))
} else {
dig, err := result.Digest()
if err != nil {
return ref, result, err
}
logger.Info("Using base", slog.String("base_image", fmt.Sprintf("%s@%s", ref, dig)))
}
o.cache.Store(ref.String(), result)
return ref, result, nil
}

View file

@ -0,0 +1,24 @@
package ociimg
import (
"context"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/types"
)
const (
latestTag = "latest"
userAgent = "buildr"
)
type imageOrIndex interface {
RawManifest() ([]byte, error)
MediaType() (types.MediaType, error)
Digest() (v1.Hash, error)
}
type Publisher interface {
Publish(ctx context.Context, result imageOrIndex) (name.Reference, error)
}

View file

@ -0,0 +1,56 @@
package ociimg
import "github.com/google/go-containerregistry/pkg/authn"
var (
_ authn.Keychain = (*BuildrKeyChain)(nil)
_ authn.Authenticator = (*AuthConfig)(nil)
)
type RegistryAuth struct {
Registry string `hcl:",label"`
AuthConfig `hcl:",remain"`
}
func KeyChain(creds []RegistryAuth) BuildrKeyChain {
chain := make(BuildrKeyChain)
for i := range creds {
c := creds[i]
chain[c.Registry] = c.AuthConfig
}
return chain
}
type BuildrKeyChain map[string]AuthConfig
func (b BuildrKeyChain) Resolve(resource authn.Resource) (authn.Authenticator, error) {
if cfg, ok := b[resource.RegistryStr()]; !ok {
return authn.Anonymous, nil
} else {
return cfg, nil
}
}
type AuthConfig struct {
Username string `hcl:"username,optional"`
Password string `hcl:"password,optional"`
Auth string `hcl:"auth,optional"`
// IdentityToken is used to authenticate the user and get
// an access token for the registry.
IdentityToken string `hcl:"identitytoken,optional"`
// RegistryToken is a bearer token to be sent to a registry
RegistryToken string `hcl:"registrytoken,optional"`
}
func (a AuthConfig) Authorization() (*authn.AuthConfig, error) {
return &authn.AuthConfig{
Username: a.Username,
Password: a.Password,
Auth: a.Auth,
IdentityToken: a.IdentityToken,
RegistryToken: a.RegistryToken,
}, nil
}

View file

@ -0,0 +1,154 @@
package ociimg
import (
"context"
"fmt"
"net/http"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
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"
"golang.org/x/exp/slog"
)
type registryPublisherOption func(p *registryPublisher)
func withTags(tags ...string) registryPublisherOption {
return func(p *registryPublisher) {
tagsSet := make(map[string]bool)
for i := range p.tags {
tagsSet[p.tags[i]] = true
}
for i := range tags {
tagsSet[tags[i]] = true
}
p.tags = maps.Keys(tagsSet)
}
}
func withTransport(t http.RoundTripper) registryPublisherOption {
return func(p *registryPublisher) {
p.t = t
}
}
func withInsecure(insecure bool) registryPublisherOption {
return func(p *registryPublisher) {
p.insecure = insecure
}
}
func withKeyChain(kc authn.Keychain) registryPublisherOption {
return func(p *registryPublisher) {
p.keychain = kc
}
}
func newRegistryPublisher(
imageName string,
logger *slog.Logger,
opts ...registryPublisherOption,
) registryPublisher {
p := registryPublisher{
imageName: imageName,
logger: logger,
tags: []string{latestTag},
t: http.DefaultTransport,
keychain: authn.DefaultKeychain,
}
for i := range opts {
opts[i](&p)
}
return p
}
type registryPublisher struct {
imageName string
logger *slog.Logger
tags []string
t http.RoundTripper
keychain authn.Keychain
insecure bool
}
func (p registryPublisher) Publish(ctx context.Context, result imageOrIndex) (name.Reference, error) {
ro := []remote.Option{
remote.WithAuthFromKeychain(p.keychain),
remote.WithTransport(p.t),
remote.WithContext(ctx),
remote.WithUserAgent(userAgent),
}
var no []name.Option
if p.insecure {
no = append(no, name.Insecure)
}
for i, tagName := range p.tags {
tag, err := name.NewTag(fmt.Sprintf("%s:%s", p.imageName, tagName), no...)
if err != nil {
return nil, err
}
if i == 0 {
p.logger.Debug("Publishing", slog.String("tag", tag.String()))
if err := pushResult(tag, result, ro); err != nil {
return nil, err
}
} else {
p.logger.Debug("Tagging", slog.String("tag", tag.String()))
if err := remote.Tag(tag, result, ro...); err != nil {
return nil, err
}
}
}
h, err := result.Digest()
if err != nil {
return nil, err
}
ref := fmt.Sprintf("%s@%s", p.imageName, h)
if len(p.tags) == 1 && p.tags[0] != latestTag {
// If a single tag is explicitly set (not latest), then this
// is probably a release, so include the tag in the reference.
ref = fmt.Sprintf("%s:%s@%s", p.imageName, p.tags[0], h)
}
dig, err := name.NewDigest(ref)
if err != nil {
return nil, err
}
p.logger.Debug("Published", slog.String("digest", dig.String()))
return &dig, nil
}
func pushResult(tag name.Tag, br imageOrIndex, opt []remote.Option) error {
mt, err := br.MediaType()
if err != nil {
return err
}
switch mt {
case types.OCIImageIndex, types.DockerManifestList:
idx, ok := br.(v1.ImageIndex)
if !ok {
return fmt.Errorf("failed to interpret result as index: %v", br)
}
return remote.WriteIndex(tag, idx, opt...)
case types.OCIManifestSchema1, types.DockerManifestSchema2:
img, ok := br.(v1.Image)
if !ok {
return fmt.Errorf("failed to interpret result as image: %v", br)
}
return remote.Write(tag, img, opt...)
default:
return fmt.Errorf("result image media type: %s", mt)
}
}

View file

@ -0,0 +1,169 @@
package ociimg
import (
"archive/tar"
"errors"
"io"
"io/fs"
"os"
"path"
"path/filepath"
"strings"
)
func buildDirFromContent(cwd string, content map[string]string) (*dirNode, error) {
rootNode := newDirNode()
for source, target := range content {
if !filepath.IsAbs(source) {
source = filepath.Join(cwd, source)
}
info, err := os.Stat(source)
if err != nil {
return nil, err
}
if info.IsDir() {
walkErr := filepath.WalkDir(source, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
rootNode.addDir(path)
} else {
rootNode.addFile(path, "")
}
return nil
})
if walkErr != nil {
return nil, walkErr
}
}
if strings.HasSuffix(target, "/") {
_, fileName := filepath.Split(source)
target = filepath.Join(target, fileName)
}
rootNode.addFile(source, target)
}
return rootNode, nil
}
type fileToTar struct {
sourcePath string
targetFileName string
}
func newDirNode() *dirNode {
return &dirNode{
children: make(map[string]*dirNode),
}
}
type dirNode struct {
children map[string]*dirNode
files []fileToTar
}
func (n *dirNode) addDir(dirPath string) {
current := n
for _, segment := range strings.Split(dirPath, "/") {
if child, ok := n.children[segment]; ok {
current = child
} else {
newNode := newDirNode()
current.children[segment] = newNode
current = newNode
}
}
}
func (n *dirNode) addFile(sourcePath, targetPath string) {
dirPath, fileName := filepath.Split(targetPath)
current := n
for _, segment := range strings.Split(dirPath, "/") {
if segment == "" {
continue
}
if child, ok := current.children[segment]; ok {
current = child
} else {
newNode := &dirNode{
children: make(map[string]*dirNode),
}
current.children[segment] = newNode
current = newNode
}
}
current.files = append(current.files, fileToTar{
sourcePath: sourcePath,
targetFileName: fileName,
})
}
func (n *dirNode) writeToTar(writer *tar.Writer, parent string) error {
for segment, child := range n.children {
segmentPath := filepath.Join(parent, segment)
header := &tar.Header{
Name: segmentPath,
Typeflag: tar.TypeDir,
// Use a fixed Mode, so that this isn't sensitive to the directory and umask
// under which it was created. Additionally, windows can only set 0222,
// 0444, or 0666, none of which are executable.
Mode: 0555,
ModTime: defaultCreationTime,
}
if err := writer.WriteHeader(header); err != nil {
return err
}
if err := child.writeToTar(writer, segmentPath); err != nil {
return err
}
}
for i := range n.files {
fileSpec := n.files[i]
info, err := os.Stat(fileSpec.sourcePath)
if err != nil {
return err
}
f, err := os.Open(fileSpec.sourcePath)
if err != nil {
return err
}
header := &tar.Header{
Name: path.Join(parent, fileSpec.targetFileName),
Size: info.Size(),
Typeflag: tar.TypeReg,
// Use a fixed Mode, so that this isn't sensitive to the directory and umask
// under which it was created. Additionally, windows can only set 0222,
// 0444, or 0666, none of which are executable.
Mode: 0o555,
ModTime: defaultCreationTime,
}
if err = writer.WriteHeader(header); err != nil {
return errors.Join(err, f.Close())
}
if _, err = io.Copy(writer, f); err != nil {
return errors.Join(err, f.Close())
}
if err = f.Close(); err != nil {
return err
}
}
return nil
}

View file

@ -0,0 +1,23 @@
package packaging
import (
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/gohcl"
"code.icb4dc0.de/buildr/buildr/internal/logging"
"code.icb4dc0.de/buildr/buildr/modules"
"code.icb4dc0.de/buildr/buildr/modules/packaging/ociimg"
)
var Registration = modules.RegistrationFunc(func(registry *modules.TypeRegistry) {
registry.RegisterModule(modules.ModuleTypePackage, "container_image", modules.ModuleFactoryFunc(func(block modules.Block, hclCtx *hcl.EvalContext) (modules.Module, error) {
b := new(ociimg.ContainerImage)
diags := gohcl.DecodeBody(block.Body(), hclCtx, b)
if diags.HasErrors() {
logging.Diagnostics(diags, nil)
return nil, diags
}
return b, nil
}))
})

View file

@ -12,10 +12,26 @@ func (f ModuleFactoryFunc) Create(block Block, hclCtx *hcl.EvalContext) (Module,
return f(block, hclCtx)
}
func NewRegistry() *TypeRegistry {
return &TypeRegistry{
type Registration interface {
RegisterAt(registry *TypeRegistry)
}
type RegistrationFunc func(registry *TypeRegistry)
func (f RegistrationFunc) RegisterAt(registry *TypeRegistry) {
f(registry)
}
func NewRegistry(registrations ...Registration) *TypeRegistry {
r := &TypeRegistry{
registrations: make(map[moduleSpec]ModuleFactory),
}
for i := range registrations {
registrations[i].RegisterAt(r)
}
return r
}
type TypeRegistry struct {

View file

@ -8,7 +8,7 @@ import (
"code.icb4dc0.de/buildr/buildr/modules"
)
func RegisterTypes(registry *modules.TypeRegistry) {
var Registration = modules.RegistrationFunc(func(registry *modules.TypeRegistry) {
registry.RegisterModule("task", "script", modules.ModuleFactoryFunc(func(block modules.Block, hclCtx *hcl.EvalContext) (modules.Module, error) {
b := new(ScriptTask)
diags := gohcl.DecodeBody(block.Body(), hclCtx, b)
@ -22,4 +22,4 @@ func RegisterTypes(registry *modules.TypeRegistry) {
return b, nil
}))
}
})

View file

@ -8,7 +8,7 @@ import (
"code.icb4dc0.de/buildr/buildr/modules"
)
func RegisterTypes(registry *modules.TypeRegistry) {
var Registration = modules.RegistrationFunc(func(registry *modules.TypeRegistry) {
registry.RegisterModule("tool", "go_tool", modules.ModuleFactoryFunc(func(block modules.Block, hclCtx *hcl.EvalContext) (modules.Module, error) {
b := new(GoTool)
diags := gohcl.DecodeBody(block.Body(), hclCtx, b)
@ -19,4 +19,4 @@ func RegisterTypes(registry *modules.TypeRegistry) {
return b, nil
}))
}
})

View file

@ -1,13 +1,20 @@
package modules
import "fmt"
type ModuleType string
func (t ModuleType) String() string {
return string(t)
}
func (t ModuleType) GroupName() string {
return fmt.Sprintf("%ss", t)
}
const (
ModuleTypeTool ModuleType = "tool"
ModuleTypeTask ModuleType = "task"
ModuleTypeBuild ModuleType = "build"
ModuleTypeTool ModuleType = "tool"
ModuleTypeTask ModuleType = "task"
ModuleTypeBuild ModuleType = "build"
ModuleTypePackage ModuleType = "package"
)