From 09d39ad36d6e34ecf1edca5ee8d4ced0cdf82f72 Mon Sep 17 00:00:00 2001 From: Jared Allard Date: Fri, 10 May 2024 12:42:43 -0700 Subject: [PATCH] feat(updater): add 'checkout' and 'upload_artifact' steps Adds a new `checkout` step intended to replace `git checkout` by actually checking out the correct revision when running commands. Adds a new `upload_artifact` step that uploads an artifact to a package specific prefix. Primarily intended for supporting Go dependency archives, but could also be used for anything. Added `net-vpn/tailscale` using this new functionality. --- .tools/README.md | 2 + .tools/cmd/updater/updater.go | 26 ++- .tools/go.mod | 15 +- .tools/go.sum | 32 +++- .tools/internal/config/config.go | 136 +++------------ .tools/internal/config/packages/packages.go | 155 ++++++++++++++++++ .tools/internal/resolver/apt.go | 4 +- .tools/internal/resolver/git.go | 15 +- .tools/internal/resolver/resolver.go | 11 +- .tools/internal/steps/checkout_step.go | 61 +++++++ .tools/internal/steps/executor.go | 10 +- .tools/internal/steps/steps.go | 53 +++--- .../steps/stepshelpers/stepshelpers.go | 27 +++ .tools/internal/steps/upload_artifact_step.go | 108 ++++++++++++ .updater.yml | 5 + Dockerfile | 5 + net-vpn/tailscale/Manifest | 3 + net-vpn/tailscale/tailscale-1.66.0.ebuild | 60 +++++++ packages.yml | 67 ++++++++ update-base-image.sh | 23 +++ updater.yml | 35 ---- 21 files changed, 663 insertions(+), 190 deletions(-) create mode 100644 .tools/internal/config/packages/packages.go create mode 100644 .tools/internal/steps/checkout_step.go create mode 100644 .tools/internal/steps/upload_artifact_step.go create mode 100644 .updater.yml create mode 100644 net-vpn/tailscale/Manifest create mode 100644 net-vpn/tailscale/tailscale-1.66.0.ebuild create mode 100644 packages.yml create mode 100755 update-base-image.sh delete mode 100644 updater.yml diff --git a/.tools/README.md b/.tools/README.md index 8990149..70e1906 100644 --- a/.tools/README.md +++ b/.tools/README.md @@ -4,6 +4,8 @@ An automated ebuild updating system. ## Usage +**Requirements**: `docker`. + Create an `updater.yml` file in the root of your repository. Create a key for each package that should be managed by the updater. diff --git a/.tools/cmd/updater/updater.go b/.tools/cmd/updater/updater.go index 4e63f5d..a15c1fa 100644 --- a/.tools/cmd/updater/updater.go +++ b/.tools/cmd/updater/updater.go @@ -28,6 +28,7 @@ import ( logger "github.com/charmbracelet/log" "github.com/jaredallard/overlay/.tools/internal/config" + "github.com/jaredallard/overlay/.tools/internal/config/packages" "github.com/jaredallard/overlay/.tools/internal/ebuild" updater "github.com/jaredallard/overlay/.tools/internal/resolver" "github.com/jaredallard/overlay/.tools/internal/steps" @@ -42,8 +43,9 @@ var log = logger.NewWithOptions(os.Stderr, logger.Options{ // rootCmd is the root command used by cobra var rootCmd = &cobra.Command{ - Use: "updater", + Use: "updater ", Short: "updater automatically updates ebuilds", + Args: cobra.MaximumNArgs(1), RunE: entrypoint, SilenceErrors: true, } @@ -88,12 +90,27 @@ func getDefaultSteps() []steps.Step { func entrypoint(cmd *cobra.Command, args []string) error { ctx := cmd.Context() - cfg, err := config.LoadConfig("updater.yml") + cfg, err := config.LoadConfig(".updater.yml") if err != nil { - return fmt.Errorf("failed to load config: %w", err) + cfg = &config.Config{} } - for _, ce := range cfg { + pkgs, err := packages.LoadPackages("packages.yml") + if err != nil { + return fmt.Errorf("failed to load packages: %w", err) + } + + // If we have exactly one argument, we only want to update that + // package. + if len(args) == 1 { + pkgName := args[0] + if _, ok := pkgs[pkgName]; !ok { + return fmt.Errorf("package not found in packages.yml: %s", pkgName) + } + pkgs = packages.List{pkgName: pkgs[pkgName]} + } + + for _, ce := range pkgs { log.With("name", ce.Name).With("resolver", ce.Resolver).Info("checking for updates") ebuildDir := ce.Name @@ -132,6 +149,7 @@ func entrypoint(cmd *cobra.Command, args []string) error { } executor := steps.NewExecutor(log, ceSteps, &steps.ExecutorInput{ + Config: cfg, OriginalEbuild: e, ExistingEbuilds: ebuilds, LatestVersion: latestVersion, diff --git a/.tools/go.mod b/.tools/go.mod index 09ef5ea..85d89e9 100644 --- a/.tools/go.mod +++ b/.tools/go.mod @@ -8,6 +8,7 @@ require ( github.com/docker/docker v25.0.3+incompatible github.com/egym-playground/go-prefix-writer v0.0.0-20180609083313-7326ea162eca github.com/fatih/color v1.15.0 + github.com/minio/minio-go/v7 v7.0.70 github.com/pkg/errors v0.9.1 github.com/spf13/cobra v1.8.0 gopkg.in/yaml.v3 v3.0.1 @@ -23,17 +24,23 @@ require ( github.com/distribution/reference v0.5.0 // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/goccy/go-json v0.10.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/google/go-cmp v0.6.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/klauspost/compress v1.17.6 // indirect + github.com/klauspost/cpuid/v2 v2.2.6 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.18 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/minio/md5-simd v1.1.2 // indirect github.com/moby/term v0.5.0 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/muesli/reflow v0.3.0 // indirect @@ -41,6 +48,7 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect github.com/rivo/uniseg v0.2.0 // indirect + github.com/rs/xid v1.5.0 // indirect github.com/spf13/pflag v1.0.5 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0 // indirect go.opentelemetry.io/otel v1.23.1 // indirect @@ -48,9 +56,12 @@ require ( go.opentelemetry.io/otel/metric v1.23.1 // indirect go.opentelemetry.io/otel/sdk v1.23.1 // indirect go.opentelemetry.io/otel/trace v1.23.1 // indirect - golang.org/x/crypto v0.17.0 // indirect + golang.org/x/crypto v0.21.0 // indirect golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect - golang.org/x/sys v0.16.0 // indirect + golang.org/x/net v0.23.0 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect pault.ag/go/topsort v0.1.1 // indirect ) diff --git a/.tools/go.sum b/.tools/go.sum index 73fcee0..7b3e71b 100644 --- a/.tools/go.sum +++ b/.tools/go.sum @@ -25,6 +25,8 @@ github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= 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/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/egym-playground/go-prefix-writer v0.0.0-20180609083313-7326ea162eca h1:sWNMfkKG8GW1pGUyNlbsWq6f04pFgcsomY+Fly8XdB4= github.com/egym-playground/go-prefix-writer v0.0.0-20180609083313-7326ea162eca/go.mod h1:Ar+qogA+fkjeUR18xJfFzrMSjfs/sCPO+yjVvhXpyEg= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= @@ -38,18 +40,27 @@ github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 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/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 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.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI= +github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= +github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= @@ -61,6 +72,10 @@ github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= +github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= +github.com/minio/minio-go/v7 v7.0.70 h1:1u9NtMgfK1U42kUxcsl5v0yj6TEOPR497OAQxpJnn2g= +github.com/minio/minio-go/v7 v7.0.70/go.mod h1:4yBA8v80xGA30cfM3fz0DKYMXunWl/AV/6tWEs9ryzo= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= @@ -81,6 +96,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= @@ -114,8 +131,8 @@ go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7e golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -124,8 +141,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -135,9 +152,10 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/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.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= @@ -162,6 +180,8 @@ google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7 google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/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.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= diff --git a/.tools/internal/config/config.go b/.tools/internal/config/config.go index b6bfa72..104c9f4 100644 --- a/.tools/internal/config/config.go +++ b/.tools/internal/config/config.go @@ -21,27 +21,32 @@ import ( "fmt" "os" - "github.com/jaredallard/overlay/.tools/internal/steps" "gopkg.in/yaml.v3" ) -// Resolver is the resolver to use to determine if an update is available. -type Resolver string - -// Contains the supported resolvers. -const ( - // GitResolver is the git resolver. - GitResolver Resolver = "git" - - // APTResolver is a version resolver powered by an APT repository. - APTResolver Resolver = "apt" -) - -// Config is the configuration for the updater. -type Config map[string]Ebuild +// Config represents the configuration for the updater itself. +type Config struct { + // StepConfig contains configuration for steps that support + // updater-wide configuration. + StepConfig struct { + // UploadArtifact contains the configuration for where the + // 'upload_artifact' step should upload the artifact to. + UploadArtifact struct { + // Bucket is the S3 bucket to upload the artifact to. + Bucket string `yaml:"bucket"` + + // Host is the host of the S3 bucket. + Host string `yaml:"host"` + + // Prefix is the prefix to use for the artifact when storing it in + // S3. + Prefix string `yaml:"prefix"` + } `yaml:"upload_artifact"` + } `yaml:"step_config"` +} // LoadConfig loads the updater configuration from the provided path. -func LoadConfig(path string) (Config, error) { +func LoadConfig(path string) (*Config, error) { f, err := os.Open(path) if err != nil { return nil, fmt.Errorf("failed to open config file: %w", err) @@ -52,102 +57,5 @@ func LoadConfig(path string) (Config, error) { if err := yaml.NewDecoder(f).Decode(&cfg); err != nil { return nil, fmt.Errorf("failed to decode config: %w", err) } - - return cfg, nil -} - -// Ebuild is an ebuild that should be updated by the updater. -type Ebuild struct { - // Name of the ebuild. This is only set when loaded from the config. - // It is a readonly field. - Name string `yaml:"name,omitempty"` - - // Resolver to use to determine if an update is available. - // Currently only "git" is supported. - Resolver Resolver `yaml:"resolver"` - - // GitOptions is the options for the git resolver. - GitOptions GitOptions `yaml:"options"` - - // APTOptions is the options for the APT resolver. - APTOptions APTOptions `yaml:"options"` - - // Steps are the steps to use to update the ebuild, if not set it - // defaults to a copy the existing ebuild and regenerate the manifest. - Steps steps.Steps `yaml:"steps"` -} - -// UnmarshalYAML unmarshals the ebuild configuration from YAML while -// converting options into the appropriate type for the provided -// resolver. -func (e *Ebuild) UnmarshalYAML(unmarshal func(interface{}) error) error { - var raw struct { - Resolver Resolver `yaml:"resolver"` - Options yaml.Node `yaml:"options"` - Steps steps.Steps - } - - if err := unmarshal(&raw); err != nil { - return err - } - - e.Resolver = raw.Resolver - e.Steps = raw.Steps - - switch e.Resolver { - case GitResolver: - if err := raw.Options.Decode(&e.GitOptions); err != nil { - return fmt.Errorf("failed to decode git options: %w", err) - } - case APTResolver: - if err := raw.Options.Decode(&e.APTOptions); err != nil { - return fmt.Errorf("failed to decode APT options: %w", err) - } - default: - return fmt.Errorf("unsupported resolver: %s", e.Resolver) - } - - return nil -} - -// UnmarshalYAML unmarshals the configuration from YAML while carrying -// over the ebuild name into the ebuild struct. -func (c Config) UnmarshalYAML(unmarshal func(interface{}) error) error { - var ebuilds map[string]Ebuild - - if err := unmarshal(&ebuilds); err != nil { - return err - } - - for name, ebuild := range ebuilds { - ebuild.Name = name - c[name] = ebuild - } - - return nil -} - -// GitOptions is the options for the git resolver. -type GitOptions struct { - // URL is the URL to the git repository. Must be a valid option to - // 'git clone'. - URL string `yaml:"url"` - - // Tags denote if tags should be used as the version source. - Tags bool `yaml:"tags"` -} - -// APTOptions contains the options for the APT resolver. -type APTOptions struct { - // Repository is the URL of the APT repository. Should match the - // following format: - // deb http://archive.ubuntu.com/ubuntu/ focal main - Repository string `yaml:"repository"` - - // Package is the name of the package to watch versions for. - Package string `yaml:"package"` - - // StripRelease is a boolean that denotes if extra release information - // (in the context of a semver) should be stripped. Defaults to true. - StripRelease *bool `yaml:"strip_release"` + return &cfg, nil } diff --git a/.tools/internal/config/packages/packages.go b/.tools/internal/config/packages/packages.go new file mode 100644 index 0000000..f726c67 --- /dev/null +++ b/.tools/internal/config/packages/packages.go @@ -0,0 +1,155 @@ +// Copyright (C) 2024 Jared Allard +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package packages + +import ( + "fmt" + "os" + + "github.com/jaredallard/overlay/.tools/internal/steps" + "gopkg.in/yaml.v3" +) + +// Resolver is the resolver to use to determine if an update is available. +type Resolver string + +// Contains the supported resolvers. +const ( + // GitResolver is the git resolver. + GitResolver Resolver = "git" + + // APTResolver is a version resolver powered by an APT repository. + APTResolver Resolver = "apt" +) + +// List contains all of the packages that should be updated. +type List map[string]Package + +// Package is an package that should be updated by the updater. +type Package struct { + // Name of the ebuild. This is only set when loaded from the config. + // It is a readonly field. + Name string `yaml:"name,omitempty"` + + // Resolver to use to determine if an update is available. + // Currently only "git" is supported. + Resolver Resolver `yaml:"resolver"` + + // GitOptions is the options for the git resolver. + GitOptions GitOptions `yaml:"options"` + + // APTOptions is the options for the APT resolver. + APTOptions APTOptions `yaml:"options"` + + // Steps are the steps to use to update the ebuild, if not set it + // defaults to a copy the existing ebuild and regenerate the manifest. + Steps steps.Steps `yaml:"steps"` +} + +// LoadPackages returns a map of packages from the provided path. +func LoadPackages(path string) (List, error) { + f, err := os.Open(path) + if err != nil { + return nil, fmt.Errorf("failed to open ebuilds file: %w", err) + } + defer f.Close() + + pkgs := make(List) + if err := yaml.NewDecoder(f).Decode(&pkgs); err != nil { + return nil, fmt.Errorf("failed to decode ebuilds: %w", err) + } + + return pkgs, nil +} + +// UnmarshalYAML unmarshals the ebuild configuration from YAML while +// converting options into the appropriate type for the provided +// resolver. +func (p *Package) UnmarshalYAML(unmarshal func(interface{}) error) error { + var raw struct { + Resolver Resolver `yaml:"resolver"` + Options yaml.Node `yaml:"options"` + Steps steps.Steps + } + + if err := unmarshal(&raw); err != nil { + return err + } + + p.Resolver = raw.Resolver + p.Steps = raw.Steps + + switch p.Resolver { + case GitResolver: + if err := raw.Options.Decode(&p.GitOptions); err != nil { + return fmt.Errorf("failed to decode git options: %w", err) + } + case APTResolver: + if err := raw.Options.Decode(&p.APTOptions); err != nil { + return fmt.Errorf("failed to decode APT options: %w", err) + } + default: + return fmt.Errorf("unsupported resolver: %s", p.Resolver) + } + + return nil +} + +// UnmarshalYAML unmarshals the ebuilds from YAML while carrying the +// name from the key into the ebuild name. +func (l List) UnmarshalYAML(unmarshal func(interface{}) error) error { + pkgs := make(map[string]Package) + if err := unmarshal(&pkgs); err != nil { + return err + } + + for name, ebuild := range pkgs { + ebuild.Name = name + l[name] = ebuild + } + + return nil +} + +// GitOptions is the options for the git resolver. +type GitOptions struct { + // URL is the URL to the git repository. Must be a valid option to + // 'git clone'. + URL string `yaml:"url"` + + // Tags denote if tags should be used as the version source. + Tags bool `yaml:"tags"` + + // ConsiderPreReleases denotes if pre-releases should be considered, + // when a tag is used and the version is able to be parsed as a + // semver. Defaults to false. + ConsiderPreReleases bool `yaml:"consider_pre_releases"` +} + +// APTOptions contains the options for the APT resolver. +type APTOptions struct { + // Repository is the URL of the APT repository. Should match the + // following format: + // deb http://archive.ubuntu.com/ubuntu/ focal main + Repository string `yaml:"repository"` + + // Package is the name of the package to watch versions for. + Package string `yaml:"package"` + + // StripRelease is a boolean that denotes if extra release information + // (in the context of a semver) should be stripped. Defaults to true. + StripRelease *bool `yaml:"strip_release"` +} diff --git a/.tools/internal/resolver/apt.go b/.tools/internal/resolver/apt.go index abb13af..65e4adf 100644 --- a/.tools/internal/resolver/apt.go +++ b/.tools/internal/resolver/apt.go @@ -17,13 +17,13 @@ package updater import ( "github.com/blang/semver/v4" - "github.com/jaredallard/overlay/.tools/internal/config" + "github.com/jaredallard/overlay/.tools/internal/config/packages" "github.com/jaredallard/overlay/.tools/internal/resolver/apt" ) // getAPTVersion returns the latest version of an APT package based on // the config provided. -func getAPTVersion(ce *config.Ebuild) (string, error) { +func getAPTVersion(ce *packages.Package) (string, error) { v, err := apt.GetPackageVersion(apt.Lookup{ SourcesEntry: ce.APTOptions.Repository, Package: ce.APTOptions.Package, diff --git a/.tools/internal/resolver/git.go b/.tools/internal/resolver/git.go index 2885190..7674d04 100644 --- a/.tools/internal/resolver/git.go +++ b/.tools/internal/resolver/git.go @@ -23,12 +23,13 @@ import ( "os/exec" "strings" - "github.com/jaredallard/overlay/.tools/internal/config" + "github.com/blang/semver/v4" + "github.com/jaredallard/overlay/.tools/internal/config/packages" ) // getGitVersion returns the latest version available from a git // repository. -func getGitVersion(ce *config.Ebuild) (string, error) { +func getGitVersion(ce *packages.Package) (string, error) { dir, err := os.MkdirTemp("", "updater") if err != nil { return "", fmt.Errorf("failed to create temporary directory: %w", err) @@ -66,6 +67,16 @@ func getGitVersion(ce *config.Ebuild) (string, error) { // Strip the "refs/tags/" prefix. tag := strings.TrimPrefix(fqTag, "refs/tags/") + + // Attempt to parse as a semver, for other options. + if sv, err := semver.ParseTolerant(tag); err == nil { + isPreRelease := len(sv.Pre) > 0 + if isPreRelease && !ce.GitOptions.ConsiderPreReleases { + // Skip the version if we're not considering pre-releases. + continue + } + } + newVersion = tag break } diff --git a/.tools/internal/resolver/resolver.go b/.tools/internal/resolver/resolver.go index b77d8cd..f2a1b93 100644 --- a/.tools/internal/resolver/resolver.go +++ b/.tools/internal/resolver/resolver.go @@ -20,15 +20,16 @@ package updater import ( "fmt" - "github.com/jaredallard/overlay/.tools/internal/config" + "github.com/jaredallard/overlay/.tools/internal/config/packages" ) -// GetLatestVersion returns the latest version available for the given ebuild. -func GetLatestVersion(ce *config.Ebuild) (string, error) { +// GetLatestVersion returns the latest version available for the given +// package. +func GetLatestVersion(ce *packages.Package) (string, error) { switch ce.Resolver { - case config.GitResolver: + case packages.GitResolver: return getGitVersion(ce) - case config.APTResolver: + case packages.APTResolver: return getAPTVersion(ce) case "": return "", fmt.Errorf("no resolver specified") diff --git a/.tools/internal/steps/checkout_step.go b/.tools/internal/steps/checkout_step.go new file mode 100644 index 0000000..ddc2453 --- /dev/null +++ b/.tools/internal/steps/checkout_step.go @@ -0,0 +1,61 @@ +// Copyright (C) 2024 Jared Allard +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package steps + +import ( + "context" + _ "embed" + "fmt" + + "github.com/jaredallard/overlay/.tools/internal/steps/stepshelpers" +) + +// CheckoutStep checks out a repository at the given path. If the Git +// resolver was used, it defaults to the URL provided in the GitOptions. +// Otherwise, it will use the URL provided. +// +// The latest version is used as the revision to checkout. +type CheckoutStep struct { + url string +} + +// NewCheckoutStep creates a new CheckoutStep from the provided input. +func NewCheckoutStep(input any) (StepRunner, error) { + url, ok := input.(string) + if !ok { + return nil, fmt.Errorf("expected string, got %T", input) + } + + return &CheckoutStep{url}, nil +} + +// Run runs the provided command inside of the step runner. +func (c CheckoutStep) Run(ctx context.Context, env Environment) (*StepOutput, error) { + cmds := [][]string{ + {"git", "-c", "init.defaultBranch=main", "init", env.workDir}, + {"git", "remote", "add", "origin", c.url}, + {"git", "-c", "protocol.version=2", "fetch", "origin", "v" + env.in.LatestVersion}, + {"git", "reset", "--hard", "FETCH_HEAD"}, + } + + for _, cmd := range cmds { + if err := stepshelpers.RunCommandInContainer(ctx, env.containerID, cmd...); err != nil { + return nil, fmt.Errorf("failed to run command %v: %w", cmd, err) + } + } + + return nil, nil +} diff --git a/.tools/internal/steps/executor.go b/.tools/internal/steps/executor.go index 920f274..66c771a 100644 --- a/.tools/internal/steps/executor.go +++ b/.tools/internal/steps/executor.go @@ -23,6 +23,7 @@ import ( "strings" logger "github.com/charmbracelet/log" + "github.com/jaredallard/overlay/.tools/internal/config" "github.com/jaredallard/overlay/.tools/internal/ebuild" "github.com/docker/docker/api/types/container" @@ -60,6 +61,10 @@ type Environment struct { // ExecutorInput is input to the executor. This should contain state // that existed before the executor was ran. type ExecutorInput struct { + // Config is the configuration for the updater, which contains + // configuration for some steps. + Config *config.Config + // OriginalEbuild is the original ebuild that can be used for // generating a new one. OriginalEbuild *ebuild.Ebuild @@ -91,7 +96,10 @@ func (e *Executor) Run(ctx context.Context) (*Results, error) { // TODO(jaredallard): Use the Docker API for this, but for now the CLI // is much better. - bid, err := exec.Command("docker", "run", "-d", "--rm", "--entrypoint", "sleep", "ghcr.io/jaredallard/overlay:updater", "infinity").Output() + bid, err := exec.Command( + "docker", "run", "-d", "--rm", "--entrypoint", "sleep", + "ghcr.io/jaredallard/overlay:updater", "infinity", + ).Output() if err != nil { var execErr *exec.ExitError if errors.As(err, &execErr) { diff --git a/.tools/internal/steps/steps.go b/.tools/internal/steps/steps.go index 13b00c0..a51c9e6 100644 --- a/.tools/internal/steps/steps.go +++ b/.tools/internal/steps/steps.go @@ -45,7 +45,7 @@ type Steps []Step // Step encapsulates a step that should be ran. type Step struct { - // Args are the arguments that should be passed to the step. + // Args are the arguments that were provided to the step. Args any // Runner is the runner that runs this step. @@ -55,7 +55,7 @@ type Step struct { // UnmarshalYAML unmarshals the steps from the YAML configuration file // turning them into their type safe representations. func (s *Steps) UnmarshalYAML(node *yaml.Node) error { - var raw []map[string]any + var raw []any if err := node.Decode(&raw); err != nil { return err } @@ -63,32 +63,47 @@ func (s *Steps) UnmarshalYAML(node *yaml.Node) error { // knownSteps map of key values to their respective steps. knownSteps := map[string]func(any) (StepRunner, error){ "command": NewCommandStep, + "checkout": NewCheckoutStep, "ebuild": NewEbuildStep, "original_ebuild": NewOriginalEbuildStep, + "upload_artifact": NewUploadArtifactStep, } for _, rawStep := range raw { - // Find the first key that maps to a known step. - var found bool - for key := range knownSteps { - if _, ok := rawStep[key]; ok { - found = true - - step, err := knownSteps[key](rawStep[key]) - if err != nil { - return fmt.Errorf("failed to create step: %w", err) - } - - *s = append(*s, Step{ - Args: rawStep[key], - Runner: step, - }) + var stepName string + var stepData any + + switch rawStep := rawStep.(type) { + case map[string]any: + // If there's more than one key, fail. + if len(rawStep) != 1 { + return fmt.Errorf("expected one key on step, got %d", len(rawStep)) + } + + // If it's a map, use the first key. + for key, value := range rawStep { + stepName = key + stepData = value break } + case string: + // If it's just a string, then we use it as-is. + stepName = rawStep } - if !found { - return fmt.Errorf("invalid step") + + if _, ok := knownSteps[stepName]; !ok { + return fmt.Errorf("unknown step: %s", stepName) } + + step, err := knownSteps[stepName](stepData) + if err != nil { + return fmt.Errorf("failed to create step: %w", err) + } + + *s = append(*s, Step{ + Args: stepData, + Runner: step, + }) } return nil diff --git a/.tools/internal/steps/stepshelpers/stepshelpers.go b/.tools/internal/steps/stepshelpers/stepshelpers.go index de3527e..27970dc 100644 --- a/.tools/internal/steps/stepshelpers/stepshelpers.go +++ b/.tools/internal/steps/stepshelpers/stepshelpers.go @@ -81,6 +81,33 @@ func ReadFileInContainer(ctx context.Context, containerID, path string) ([]byte, return b, nil } +// StreamFileFromContainer streams a file from a container to the +// provided writer. Because this function streams the file using the +// exec package, the caller must call the returned wait function to wait +// for the command to finish and clean up resources. +// +// The returned int64 is the size of the file being streamed. +func StreamFileFromContainer(ctx context.Context, containerID, path string) (io.Reader, int64, func() error, error) { + cmd := exec.CommandContext(ctx, "docker", "cp", fmt.Sprintf("%s:%s", containerID, path), "-") + stdout, err := cmd.StdoutPipe() + if err != nil { + return nil, 0, nil, fmt.Errorf("failed to create stdout pipe: %w", err) + } + + if err := cmd.Start(); err != nil { + return nil, 0, nil, fmt.Errorf("failed to start command: %w", err) + } + + // Process the output as a tar stream. + t := tar.NewReader(stdout) + th, err := t.Next() + if err != nil { + return nil, 0, nil, fmt.Errorf("failed to read tar: %w", err) + } + + return t, th.Size, cmd.Wait, nil +} + // RunCommandInContainer runs a command inside of a container. func RunCommandInContainer(ctx context.Context, containerID string, origArgs ...string) error { args := []string{"exec", containerID, "bash", "-eo", "pipefail"} diff --git a/.tools/internal/steps/upload_artifact_step.go b/.tools/internal/steps/upload_artifact_step.go new file mode 100644 index 0000000..2d6b5fd --- /dev/null +++ b/.tools/internal/steps/upload_artifact_step.go @@ -0,0 +1,108 @@ +// Copyright (C) 2024 Jared Allard +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package steps + +import ( + "context" + _ "embed" + "fmt" + "path/filepath" + "strings" + + "github.com/jaredallard/overlay/.tools/internal/steps/stepshelpers" + "github.com/minio/minio-go/v7" + "github.com/minio/minio-go/v7/pkg/credentials" +) + +// UploadArtifactStep takes the provided path and uploads it from the +// container to a stable S3 bucket. This is used to store artifacts that +// are required by ebuilds (e.g., Go dependency archives) and retrieve +// them later. +// +// S3 configuration is provided by the environment. The file will be +// stored using the following structure: +// +// /// +type UploadArtifactStep struct { + // path is the path to the artifact. + path string +} + +// NewUploadArtifactStep creates a new UploadArtifactStep from the provided input. +func NewUploadArtifactStep(input any) (StepRunner, error) { + path, ok := input.(string) + if !ok { + return nil, fmt.Errorf("expected string, got %T", input) + } + + return &UploadArtifactStep{path}, nil +} + +// Run runs the provided command inside of the step runner. +func (e UploadArtifactStep) Run(ctx context.Context, env Environment) (*StepOutput, error) { + if !filepath.IsAbs(e.path) { + e.path = filepath.Join(env.workDir, e.path) + } + + // TODO(jaredallard): We should create the client once. + s3Conf := env.in.Config.StepConfig.UploadArtifact + + if s3Conf.Host == "" { + return nil, fmt.Errorf("s3 host was not set in the config") + } + + if s3Conf.Bucket == "" { + return nil, fmt.Errorf("s3 bucket was not set in the config") + } + + // Without a doubt, the worse code I've ever written. + hostWithSchema := strings.TrimPrefix(strings.TrimPrefix(s3Conf.Host, "http://"), "https://") + mc, err := minio.New(hostWithSchema, &minio.Options{ + Creds: credentials.NewEnvAWS(), + Secure: strings.HasPrefix(s3Conf.Host, "https"), + }) + if err != nil { + return nil, fmt.Errorf("failed to create minio client: %w", err) + } + + uploadFileName := filepath.Base(e.path) + + // Example: /net-vpn/tailscale/1.66.1/deps.xz + uploadPath := filepath.Join( + s3Conf.Prefix, env.in.OriginalEbuild.Category, env.in.OriginalEbuild.Name, env.in.LatestVersion, uploadFileName, + ) + + out, size, wait, err := stepshelpers.StreamFileFromContainer(ctx, env.containerID, e.path) + if err != nil { + return nil, fmt.Errorf("failed to stream file from container: %w", err) + } + + env.log.With("path", uploadPath, "size", size).Info("uploading artifact") + inf, err := mc.PutObject(ctx, s3Conf.Bucket, uploadPath, out, size, minio.PutObjectOptions{ + SendContentMd5: true, + }) + if err != nil { + return nil, fmt.Errorf("failed to upload artifact: %w", err) + } + + if err := wait(); err != nil { + return nil, fmt.Errorf("failed to wait for command to finish: %w", err) + } + + env.log.With("size", inf.Size).Info("uploaded artifact") + + return nil, nil +} diff --git a/.updater.yml b/.updater.yml new file mode 100644 index 0000000..cd86fb8 --- /dev/null +++ b/.updater.yml @@ -0,0 +1,5 @@ +step_config: + upload_artifact: + host: https://c41358b3e2e8f5345933f0d433e3abef.r2.cloudflarestorage.com + bucket: gentoo-rgst-io + prefix: updater_artifacts diff --git a/Dockerfile b/Dockerfile index 4ab6f3a..dadb478 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,3 +7,8 @@ RUN emerge-webrsync && \ ACCEPT_KEYWORDS="~amd64 ~arm64" emerge -v app-portage/pycargoebuild && \ emerge -v app-portage/gentoolkit && \ eclean --deep packages && eclean --deep distfiles + +# Install mise for things that might need it. +RUN curl https://mise.run | sh +ENV PATH="/root/.local/bin:/root/.local/share/mise/shims:${PATH}" +RUN set -e; whoami; echo $HOME; mise --version \ No newline at end of file diff --git a/net-vpn/tailscale/Manifest b/net-vpn/tailscale/Manifest new file mode 100644 index 0000000..22d8241 --- /dev/null +++ b/net-vpn/tailscale/Manifest @@ -0,0 +1,3 @@ +DIST deps.tar.xz 395317936 BLAKE2B 50b0e9bb784dd655ac5f2ce7aed0eddbac772511dc9e768c0005ef84b653c58719cabbb1e52c82cc8ecc257eed26cbc976d9df24e30bf6fc5cb871a21767499d SHA512 a9ae271d8272b54a0bfe91bc9d59fa3c2af31fa1565552f5fbd8517e5a7acbcad7fb45bb080a7876e45810394b8ac28d0815b7db3b58ac9e78a0b65d0693d62e +DIST tailscale-1.66.0.tar.gz 2633400 BLAKE2B b089731d5412187d3ef9e3d0f7064143adc6baa8fd70622d2ce0c97d28420da1170d8f388fd4b1617dacd338360669a148f1a0f0f9f90914c77b0b0ff20b2e0e SHA512 5eab341a0d8208bd6915f0dd87d94a22e962527bc0c940b10f84fbcc82cfb9be979441b7367dced40a3adec4a72c0fdb0841db5790e0c24ae67f27f48fe93854 +DIST tailscale-1.66.1.tar.gz 2634211 BLAKE2B cbf90ee36545fa8ba302f872948dc92e735bfe690451428540bee10399d1db1cdbf050949a5f3dbc69a77596b6a6d94724eb43bf9a4ee78a5cee9ada03889a3e SHA512 46e226c651abd5dee248e49fcf40a0cea9de72fe9e330015299acd9ec1fc83e1f192948c26b803f0fe3404558471b374391f3cf9155ecb2257a2563f79f3278b diff --git a/net-vpn/tailscale/tailscale-1.66.0.ebuild b/net-vpn/tailscale/tailscale-1.66.0.ebuild new file mode 100644 index 0000000..5948700 --- /dev/null +++ b/net-vpn/tailscale/tailscale-1.66.0.ebuild @@ -0,0 +1,60 @@ +# Copyright 2020-2024 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 +inherit go-module systemd tmpfiles + +# These settings are obtained by running ./build_dist.sh shellvars in +# the upstream repo. +VERSION_MINOR="60" +VERSION_SHORT="1.60.0" +VERSION_LONG="1.60.0-tf4e3ee53e" +VERSION_GIT_HASH="f4e3ee53ea4605d400df2ef6b6005b026661f96b" + +DESCRIPTION="Tailscale vpn client" +HOMEPAGE="https://tailscale.com" +SRC_URI="https://github.com/tailscale/tailscale/archive/v${PV}.tar.gz -> ${P}.tar.gz" +SRC_URI+=" https://gentoo.rgst.io/updater_artifacts/${CATEGORY}/${PN}/${PV}/deps.tar.xz" + +LICENSE="MIT" +SLOT="0" +KEYWORDS="~amd64 ~arm arm64 ~riscv ~x86" + +RDEPEND="net-firewall/iptables" +BDEPEND=">=dev-lang/go-1.21" + +RESTRICT="test" + +# This translates the build command from upstream's build_dist.sh to an +# ebuild equivalent. +build_dist() { + ego build -tags xversion -ldflags " + -X tailscale.com/version.Long=${VERSION_LONG} + -X tailscale.com/version.Short=${VERSION_SHORT} + -X tailscale.com/version.GitCommit=${VERSION_GIT_HASH}" "$@" +} + +src_compile() { + build_dist ./cmd/tailscale + build_dist ./cmd/tailscaled +} + +src_install() { + dosbin tailscaled + dobin tailscale + + systemd_dounit cmd/tailscaled/tailscaled.service + insinto /etc/default + newins cmd/tailscaled/tailscaled.defaults tailscaled + keepdir /var/lib/${PN} + fperms 0750 /var/lib/${PN} + + newtmpfiles "${FILESDIR}/${PN}.tmpfiles" ${PN}.conf + + newinitd "${FILESDIR}/${PN}d.initd" ${PN} + newconfd "${FILESDIR}/${PN}d.confd" ${PN} +} + +pkg_postinst() { + tmpfiles_process ${PN}.conf +} diff --git a/packages.yml b/packages.yml new file mode 100644 index 0000000..d078cd2 --- /dev/null +++ b/packages.yml @@ -0,0 +1,67 @@ +# Configure automatic updates for ebuilds. +app-admin/chezmoi-bin: + resolver: git + options: + url: https://github.com/twpayne/chezmoi +app-admin/1password: + resolver: apt + options: + repository: "deb https://downloads.1password.com/linux/debian/amd64 stable main" + package: 1password +app-admin/op-cli-bin: + resolver: apt + options: + repository: "deb https://downloads.1password.com/linux/debian/amd64 stable main" + package: 1password-cli +app-arch/7-zip: + resolver: git + options: + url: https://github.com/ip7z/7zip +dev-util/mise: + resolver: git + options: + url: https://github.com/jdx/mise + + # We have to regenerate the ebuild to get new crates and licenses to + # be reflected, so we have to have custom steps. + steps: + - checkout: https://github.com/jdx/mise + - original_ebuild: mise.ebuild + - command: pycargoebuild -i mise.ebuild + - ebuild: mise.ebuild +net-im/armcord: + resolver: git + options: + url: https://github.com/ArmCord/ArmCord +net-vpn/tailscale: + resolver: git + options: + url: https://github.com/tailscale/tailscale + + # We have to generate a Go dependency archive and upload it to a + # stable location, so we do that during this process. + steps: + - checkout: https://github.com/tailscale/tailscale + - original_ebuild: new.ebuild + - command: |- + set -euxo pipefail + + GO_VERSION=$(grep "^go" go.mod | awk '{ print $2 }' | awk -F '.' '{ print $1"."$2}') + mise use -g golang@"${GO_VERSION}" + + # Create the dependency tar. + GOMODCACHE="${PWD}"/go-mod go mod download -modcacherw + tar --create --file deps.tar go-mod + xz --threads 0 deps.tar + + # Get the shell variables and rewrite the ebuild to contain + # them. + eval "$(./build_dist.sh shellvars)" + sed -i 's/VERSION_MINOR=".*"/VERSION_MINOR="'"${VERSION_MINOR}"'"/' new.ebuild + sed -i 's/VERSION_SHORT=".*"/VERSION_SHORT="'"${VERSION_SHORT}"'"/' new.ebuild + sed -i 's/VERSION_LONG=".*"/VERSION_LONG="'"${VERSION_LONG}"'"/' new.ebuild + sed -i 's/VERSION_GIT_HASH=".*"/VERSION_GIT_HASH="'"${VERSION_GIT_HASH}"'"/' new.ebuild + + sed -i 's|dev-lang/golang-.*|dev-lang/golang-${GO_VERSION}|' new.ebuild + - upload_artifact: deps.tar.xz + - ebuild: new.ebuild diff --git a/update-base-image.sh b/update-base-image.sh new file mode 100755 index 0000000..0f91334 --- /dev/null +++ b/update-base-image.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# Rebuilds the base image used by the tools in this repository and +# pushes it upstream. +set -euo pipefail + +# PUSH determines if we should push the image to the remote or not. +PUSH=false +if [[ "${1:-}" == "--push" ]]; then + PUSH=true +fi + +args=( + "--tag" "ghcr.io/jaredallard/overlay:updater" + "$(pwd)" +) + +if [[ "$PUSH" == "true" ]]; then + args+=("--platform" "linux/amd64,linux/arm64" "--push") +else + args+=("--load") +fi + +exec docker buildx build "${args[@]}" diff --git a/updater.yml b/updater.yml deleted file mode 100644 index 50c341f..0000000 --- a/updater.yml +++ /dev/null @@ -1,35 +0,0 @@ -# Configure automatic updates for ebuilds. -app-admin/chezmoi-bin: - resolver: git - options: - url: https://github.com/twpayne/chezmoi -app-admin/1password: - resolver: apt - options: - repository: "deb https://downloads.1password.com/linux/debian/amd64 stable main" - package: 1password -app-admin/op-cli-bin: - resolver: apt - options: - repository: "deb https://downloads.1password.com/linux/debian/amd64 stable main" - package: 1password-cli -app-arch/7-zip: - resolver: git - options: - url: https://github.com/ip7z/7zip -dev-util/mise: - resolver: git - options: - url: https://github.com/jdx/mise - - # We have to regenerate the ebuild to get new crates and licenses to - # be reflected, so we have to have custom steps. - steps: - - command: git clone https://github.com/jdx/mise . - - original_ebuild: mise.ebuild - - command: pycargoebuild -i mise.ebuild - - ebuild: mise.ebuild -net-im/armcord: - resolver: git - options: - url: https://github.com/ArmCord/ArmCord