mirror of
https://github.com/open-gitagent/langship.sh.git
synced 2026-08-03 07:21:04 +02:00
feat: add PushExecutor for OCI image mirroring
- Implement PushExecutor to copy OCI images from one registry to another. - Add PushForm to the node form for configuring push parameters. - Update node catalog to include the new Push node type. - Modify registry registration to include PushExecutor. - Update go.sum with necessary dependency versions.
This commit is contained in:
@@ -1,16 +1,17 @@
|
||||
module github.com/lyzrai/flow
|
||||
|
||||
go 1.25
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
github.com/docker/cli v27.4.0-rc.2+incompatible
|
||||
github.com/docker/cli v29.4.0+incompatible
|
||||
github.com/dop251/goja v0.0.0-20260311135729-065cd970411c
|
||||
github.com/google/go-containerregistry v0.21.5
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/minio/minio-go/v7 v7.1.0
|
||||
github.com/moby/buildkit v0.18.2
|
||||
github.com/restatedev/sdk-go v0.23.0
|
||||
go.mongodb.org/mongo-driver/v2 v2.6.0
|
||||
golang.org/x/sync v0.19.0
|
||||
golang.org/x/sync v0.20.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -26,11 +27,12 @@ require (
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/platforms v0.2.1 // indirect
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.18.2 // indirect
|
||||
github.com/containerd/ttrpc v1.2.7 // indirect
|
||||
github.com/containerd/typeurl/v2 v2.2.3 // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/dlclark/regexp2 v1.11.4 // indirect
|
||||
github.com/docker/docker-credential-helpers v0.8.2 // indirect
|
||||
github.com/docker/docker-credential-helpers v0.9.3 // 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
|
||||
@@ -51,12 +53,13 @@ require (
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/in-toto/in-toto-golang v0.5.0 // indirect
|
||||
github.com/invopop/jsonschema v0.13.0 // indirect
|
||||
github.com/klauspost/compress v1.18.2 // indirect
|
||||
github.com/klauspost/compress v1.18.5 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.11 // indirect
|
||||
github.com/klauspost/crc32 v1.3.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/minio/crc64nvme v1.1.1 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/locker v1.0.1 // indirect
|
||||
github.com/moby/patternmatcher v0.6.0 // indirect
|
||||
@@ -71,13 +74,14 @@ require (
|
||||
github.com/rs/xid v1.6.0 // indirect
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.4.0 // indirect
|
||||
github.com/shibumi/go-pathspec v1.3.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/sirupsen/logrus v1.9.4 // indirect
|
||||
github.com/tetratelabs/wazero v1.9.0 // indirect
|
||||
github.com/tinylib/msgp v1.6.1 // indirect
|
||||
github.com/tonistiigi/fsutil v0.0.0-20241121093142-31cf1f437184 // indirect
|
||||
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4 // indirect
|
||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
|
||||
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab // indirect
|
||||
github.com/vbatts/tar-split v0.12.2 // indirect
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
github.com/xdg-go/scram v1.2.0 // indirect
|
||||
@@ -87,7 +91,7 @@ require (
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.46.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
|
||||
go.opentelemetry.io/otel v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
||||
@@ -97,7 +101,7 @@ require (
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/crypto v0.46.0 // indirect
|
||||
golang.org/x/net v0.48.0 // indirect
|
||||
golang.org/x/sys v0.39.0 // indirect
|
||||
golang.org/x/sys v0.43.0 // indirect
|
||||
golang.org/x/text v0.32.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect
|
||||
|
||||
@@ -47,9 +47,8 @@ github.com/containerd/nydus-snapshotter v0.14.0 h1:6/eAi6d7MjaeLLuMO8Udfe5GVsDud
|
||||
github.com/containerd/nydus-snapshotter v0.14.0/go.mod h1:TT4jv2SnIDxEBu4H2YOvWQHPOap031ydTaHTuvc5VQk=
|
||||
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
|
||||
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
|
||||
github.com/containerd/stargz-snapshotter v0.15.1 h1:fpsP4kf/Z4n2EYnU0WT8ZCE3eiKDwikDhL6VwxIlgeA=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.15.1 h1:eXJjw9RbkLFgioVaTG+G/ZW/0kEe2oEKCdS/ZxIyoCU=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.15.1/go.mod h1:gr2RNwukQ/S9Nv33Lt6UC7xEx58C+LHRdoqbEKjz1Kk=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.18.2 h1:yXkZFYIzz3eoLwlTUZKz2iQ4MrckBxJjkmD16ynUTrw=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.18.2/go.mod h1:XyVU5tcJ3PRpkA9XS2T5us6Eg35yM0214Y+wvrZTBrY=
|
||||
github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRqQ=
|
||||
github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
|
||||
github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40=
|
||||
@@ -61,12 +60,12 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
|
||||
github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/docker/cli v27.4.0-rc.2+incompatible h1:A0GZwegDlt2wdt3tpmrUzkVOZmbhvd7i05wPSf7Oo74=
|
||||
github.com/docker/cli v27.4.0-rc.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli v29.4.0+incompatible h1:+IjXULMetlvWJiuSI0Nbor36lcJ5BTcVpUmB21KBoVM=
|
||||
github.com/docker/cli v29.4.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/docker v28.5.1+incompatible h1:Bm8DchhSD2J6PsFzxC35TZo4TLGR2PdW/E69rU45NhM=
|
||||
github.com/docker/docker v28.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
|
||||
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
|
||||
github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=
|
||||
github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo=
|
||||
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
|
||||
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
|
||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8=
|
||||
@@ -102,6 +101,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/go-containerregistry v0.21.5 h1:KTJG9Pn/jC0VdZR6ctV3/jcN+q6/Iqlx0sTVz3ywZlM=
|
||||
github.com/google/go-containerregistry v0.21.5/go.mod h1:ySvMuiWg+dOsRW0Hw8GYwfMwBlNRTmpYBFJPlkco5zU=
|
||||
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
|
||||
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
@@ -124,8 +125,8 @@ github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uO
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
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.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
|
||||
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||
github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
|
||||
github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.11 h1:0OwqZRYI2rFrjS4kvkDnqJkKHdHaRnCm68/DY4OxRzU=
|
||||
github.com/klauspost/cpuid/v2 v2.2.11/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
@@ -143,6 +144,8 @@ 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.1.0 h1:QEt5IStDpxgGjEdtOgpiZ5QhmSl3ax7qy61vi2SwHO8=
|
||||
github.com/minio/minio-go/v7 v7.1.0/go.mod h1:Dm7WS1AgLmBa0NcQD6SeJnJf+K/EUW3GR7Ks6olB3OA=
|
||||
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/moby/buildkit v0.18.2 h1:l86uBvxh4ntNoUUg3Y0eGTbKg1PbUh6tawJ4Xt75SpQ=
|
||||
github.com/moby/buildkit v0.18.2/go.mod h1:vCR5CX8NGsPTthTg681+9kdmfvkvqJBXEv71GZe5msU=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
@@ -197,13 +200,12 @@ github.com/secure-systems-lab/go-securesystemslib v0.4.0 h1:b23VGrQhTA8cN2CbBw7/
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.4.0/go.mod h1:FGBZgq2tXWICsxWQW1msNf49F0Pf2Op5Htayx335Qbs=
|
||||
github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI=
|
||||
github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
|
||||
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
|
||||
github.com/spdx/tools-golang v0.5.3 h1:ialnHeEYUC4+hkm5vJm4qz2x+oEJbS0mAMFrNXdQraY=
|
||||
github.com/spdx/tools-golang v0.5.3/go.mod h1:/ETOahiAo96Ob0/RAIBmFZw6XN0yTnyr/uFZm2NTMhI=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
|
||||
@@ -218,8 +220,8 @@ github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/
|
||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea/go.mod h1:WPnis/6cRcDZSUvVmezrxJPkiO87ThFYsoUiMwWNDJk=
|
||||
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab h1:H6aJ0yKQ0gF49Qb2z5hI1UHxSQt4JMyxebFR15KnApw=
|
||||
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab/go.mod h1:ulncasL3N9uLrVann0m+CDlJKWsIAP34MPcOJF6VRvc=
|
||||
github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts=
|
||||
github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk=
|
||||
github.com/vbatts/tar-split v0.12.2 h1:w/Y6tjxpeiFMR47yzZPlPj/FcPLpXbTUi/9H7d3CPa4=
|
||||
github.com/vbatts/tar-split v0.12.2/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||
@@ -247,8 +249,8 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.4
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.46.1 h1:gbhw/u49SS3gkPWiYweQNJGm/uJN5GkI/FrosxSHT7A=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.46.1/go.mod h1:GnOaBaFQ2we3b9AGWJpsBa7v1S5RlQzlC3O7dRMxZhM=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
|
||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
|
||||
@@ -276,8 +278,8 @@ golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/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.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
|
||||
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
|
||||
golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
|
||||
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
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=
|
||||
@@ -286,25 +288,24 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
||||
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
|
||||
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=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/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-20220520151302-bc2c85ada10a/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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
|
||||
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
|
||||
@@ -343,7 +344,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
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.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
package executors
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-containerregistry/pkg/authn"
|
||||
"github.com/google/go-containerregistry/pkg/crane"
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
|
||||
"github.com/lyzrai/flow/pkg/engine"
|
||||
"github.com/lyzrai/flow/pkg/models"
|
||||
)
|
||||
|
||||
// PushExecutor copies an OCI image from one registry to another. Source
|
||||
// defaults to the local registry image produced by an upstream Build node
|
||||
// (read from inputs[0][0].__build.image) but can be overridden by the
|
||||
// srcImage parameter. Destination is constructed from targetRegistry +
|
||||
// targetImage + tag. Optional username/password authenticate the push.
|
||||
//
|
||||
// Implementation uses go-containerregistry's crane.Copy — same primitive
|
||||
// the `crane` CLI / ko / skopeo-Go-callers use. No docker daemon required;
|
||||
// works against any OCI v2 registry. The local registry (registry:5000) is
|
||||
// reached via plain HTTP because we mark it insecure when constructing the
|
||||
// reference.
|
||||
//
|
||||
// Parameters:
|
||||
// - srcImage string override source image ref (optional)
|
||||
// - targetRegistry string e.g. "ghcr.io"
|
||||
// - targetImage string e.g. "org/agent"
|
||||
// - tag string defaults to upstream __build.commit then "latest"
|
||||
// - username string optional
|
||||
// - password string optional
|
||||
// - srcInsecure bool treat src registry as plain HTTP (default true; the local one is)
|
||||
// - dstInsecure bool treat dst registry as plain HTTP (default false)
|
||||
type PushExecutor struct{}
|
||||
|
||||
func (e *PushExecutor) Execute(ctx context.Context, node models.NodeDef, inputs [][]models.Item, _ *engine.ExecutionContext) (map[int][]models.Item, error) {
|
||||
logger := engine.NodeLoggerFromContext(ctx)
|
||||
|
||||
// Source: explicit param wins, otherwise upstream __build.image.
|
||||
srcImage := strParam(node.Parameters, "srcImage", "")
|
||||
if srcImage == "" {
|
||||
srcImage = imageFromBuildOutput(inputs)
|
||||
}
|
||||
if srcImage == "" {
|
||||
return nil, errors.New("push: no source image (set srcImage or wire a Build node upstream)")
|
||||
}
|
||||
|
||||
// Destination.
|
||||
dstRegistry := strings.TrimRight(strings.TrimSpace(strParam(node.Parameters, "targetRegistry", "")), "/")
|
||||
dstImage := strings.TrimSpace(strParam(node.Parameters, "targetImage", ""))
|
||||
if dstRegistry == "" || dstImage == "" {
|
||||
return nil, errors.New("push: targetRegistry and targetImage are required")
|
||||
}
|
||||
tag := strings.TrimSpace(strParam(node.Parameters, "tag", ""))
|
||||
if tag == "" {
|
||||
tag = strFirst(commitFromBuildOutput(inputs), "latest")
|
||||
}
|
||||
dstRef := fmt.Sprintf("%s/%s:%s", dstRegistry, dstImage, tag)
|
||||
|
||||
username := strParam(node.Parameters, "username", "")
|
||||
password := strParam(node.Parameters, "password", "")
|
||||
|
||||
srcInsecure := boolParam(node.Parameters, "srcInsecure", true)
|
||||
dstInsecure := boolParam(node.Parameters, "dstInsecure", false)
|
||||
|
||||
logger.Log(fmt.Sprintf("copying %s -> %s", srcImage, dstRef))
|
||||
|
||||
// Build the crane options. We layer:
|
||||
// - context (for cancellation)
|
||||
// - per-side insecure flag (lets us read from local registry:5000 over HTTP)
|
||||
// - keychain for auth (only used by the destination side; explicit
|
||||
// username/password takes precedence via WithAuth)
|
||||
srcOpts := []crane.Option{crane.WithContext(ctx)}
|
||||
dstOpts := []crane.Option{crane.WithContext(ctx)}
|
||||
if srcInsecure {
|
||||
srcOpts = append(srcOpts, crane.Insecure)
|
||||
}
|
||||
if dstInsecure {
|
||||
dstOpts = append(dstOpts, crane.Insecure)
|
||||
}
|
||||
if username != "" || password != "" {
|
||||
dstOpts = append(dstOpts, crane.WithAuth(&authn.Basic{
|
||||
Username: username,
|
||||
Password: password,
|
||||
}))
|
||||
} else {
|
||||
// Falls back to docker config / GHCR anonymous / etc.
|
||||
dstOpts = append(dstOpts, crane.WithAuthFromKeychain(authn.DefaultKeychain))
|
||||
}
|
||||
|
||||
// Validate refs early so we surface a clear error.
|
||||
if _, err := name.ParseReference(srcImage); err != nil {
|
||||
return nil, fmt.Errorf("push: invalid src %q: %w", srcImage, err)
|
||||
}
|
||||
if _, err := name.ParseReference(dstRef); err != nil {
|
||||
return nil, fmt.Errorf("push: invalid dst %q: %w", dstRef, err)
|
||||
}
|
||||
|
||||
// crane.Copy doesn't accept per-side options — we emulate by pulling
|
||||
// the image with src options and pushing it with dst options.
|
||||
logger.Log("pulling source manifest…")
|
||||
img, err := crane.Pull(srcImage, srcOpts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("push: pull %s: %w", srcImage, err)
|
||||
}
|
||||
|
||||
logger.Log("pushing to destination…")
|
||||
started := time.Now()
|
||||
if err := crane.Push(img, dstRef, dstOpts...); err != nil {
|
||||
return nil, fmt.Errorf("push: push %s: %w", dstRef, err)
|
||||
}
|
||||
logger.Log(fmt.Sprintf("done in %s", time.Since(started).Round(100*time.Millisecond)))
|
||||
|
||||
digest, derr := img.Digest()
|
||||
digestStr := ""
|
||||
if derr == nil {
|
||||
digestStr = digest.String()
|
||||
}
|
||||
|
||||
// Pass-through items + a __push summary so downstream nodes can read it.
|
||||
out := make([]models.Item, 0)
|
||||
for _, in := range inputs {
|
||||
for _, item := range in {
|
||||
ci := copyItem(item)
|
||||
ci["__push"] = map[string]any{
|
||||
"src": srcImage,
|
||||
"dst": dstRef,
|
||||
"digest": digestStr,
|
||||
"finished_at": time.Now().UTC(),
|
||||
}
|
||||
out = append(out, ci)
|
||||
}
|
||||
}
|
||||
if len(out) == 0 {
|
||||
out = append(out, models.Item{
|
||||
"__push": map[string]any{
|
||||
"src": srcImage,
|
||||
"dst": dstRef,
|
||||
"digest": digestStr,
|
||||
"finished_at": time.Now().UTC(),
|
||||
},
|
||||
})
|
||||
}
|
||||
return map[int][]models.Item{0: out}, nil
|
||||
}
|
||||
|
||||
// imageFromBuildOutput walks input items for an upstream Build node's
|
||||
// `__build.image` field. Returns "" if not present.
|
||||
func imageFromBuildOutput(inputs [][]models.Item) string {
|
||||
for _, in := range inputs {
|
||||
for _, it := range in {
|
||||
if b, ok := it["__build"].(map[string]any); ok {
|
||||
if s, ok := b["image"].(string); ok && s != "" {
|
||||
return s
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// commitFromBuildOutput pulls the commit SHA from an upstream Build node so
|
||||
// Push can default the destination tag to that SHA.
|
||||
func commitFromBuildOutput(inputs [][]models.Item) string {
|
||||
for _, in := range inputs {
|
||||
for _, it := range in {
|
||||
if b, ok := it["__build"].(map[string]any); ok {
|
||||
if s, ok := b["commit"].(string); ok && s != "" {
|
||||
return s
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// boolParam reads a bool with a default. Accepts native bool, "true"/"false"
|
||||
// strings, and 0/1 numerics for forgiveness on the wire.
|
||||
func boolParam(p map[string]any, key string, def bool) bool {
|
||||
if p == nil {
|
||||
return def
|
||||
}
|
||||
switch v := p[key].(type) {
|
||||
case bool:
|
||||
return v
|
||||
case string:
|
||||
s := strings.ToLower(strings.TrimSpace(v))
|
||||
if s == "true" || s == "1" || s == "yes" {
|
||||
return true
|
||||
}
|
||||
if s == "false" || s == "0" || s == "no" {
|
||||
return false
|
||||
}
|
||||
case float64:
|
||||
return v != 0
|
||||
}
|
||||
return def
|
||||
}
|
||||
@@ -56,8 +56,9 @@ func RegisterAll(deps ...RegistryDeps) {
|
||||
// Human-in-the-loop
|
||||
Register("flow-nodes-base.waitForApproval", &ApprovalExecutor{})
|
||||
|
||||
// CI/CD primitives — Build is real; the rest are stubs for now.
|
||||
// CI/CD primitives — Build + Push are real; the rest are stubs for now.
|
||||
Register("flow-nodes-base.build", &BuildExecutor{Agents: d.Agents})
|
||||
Register("flow-nodes-base.push", &PushExecutor{})
|
||||
Register("flow-nodes-base.test", &TestExecutor{})
|
||||
Register("flow-nodes-base.eval", &EvalExecutor{})
|
||||
Register("flow-nodes-base.policy", &PolicyExecutor{})
|
||||
|
||||
@@ -31,6 +31,8 @@ export function NodeForm({ node, onChange }: NodeFormProps) {
|
||||
return <PolicyForm node={node} onChange={onChange} />;
|
||||
case "flow-nodes-base.waitForApproval":
|
||||
return <ApprovalForm node={node} onChange={onChange} />;
|
||||
case "flow-nodes-base.push":
|
||||
return <PushForm node={node} onChange={onChange} />;
|
||||
case "flow-nodes-base.deploy":
|
||||
return <DeployForm node={node} onChange={onChange} />;
|
||||
case "flow-nodes-base.promote":
|
||||
@@ -48,6 +50,7 @@ export function hasTypedForm(type: string): boolean {
|
||||
return [
|
||||
"flow-nodes-base.trigger",
|
||||
"flow-nodes-base.build",
|
||||
"flow-nodes-base.push",
|
||||
"flow-nodes-base.test",
|
||||
"flow-nodes-base.eval",
|
||||
"flow-nodes-base.policy",
|
||||
@@ -82,6 +85,12 @@ function getStringArray(node: PipelineNode, key: string): string[] {
|
||||
return Array.isArray(v) ? v.filter((x): x is string => typeof x === "string") : [];
|
||||
}
|
||||
|
||||
function getBool(node: PipelineNode, key: string, fallback: boolean): boolean {
|
||||
const v = node.parameters?.[key];
|
||||
if (typeof v === "boolean") return v;
|
||||
return fallback;
|
||||
}
|
||||
|
||||
// --- forms ----------------------------------------------------------------
|
||||
|
||||
function TriggerForm({ node, onChange }: NodeFormProps) {
|
||||
@@ -474,6 +483,141 @@ function ApprovalForm({ node, onChange }: NodeFormProps) {
|
||||
);
|
||||
}
|
||||
|
||||
function PushForm({ node, onChange }: NodeFormProps) {
|
||||
const srcImage = getString(node, "srcImage", "");
|
||||
const targetRegistry = getString(node, "targetRegistry", "ghcr.io");
|
||||
const targetImage = getString(node, "targetImage", "");
|
||||
const tag = getString(node, "tag", "");
|
||||
const username = getString(node, "username", "");
|
||||
const password = getString(node, "password", "");
|
||||
const srcInsecure = getBool(node, "srcInsecure", true);
|
||||
const dstInsecure = getBool(node, "dstInsecure", false);
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="push-src">Source image (optional)</Label>
|
||||
<Input
|
||||
id="push-src"
|
||||
value={srcImage}
|
||||
onChange={(e) => onChange(setParam(node, "srcImage", e.target.value))}
|
||||
placeholder="registry:5000/owner/agent:sha (defaults to upstream Build output)"
|
||||
className="font-mono text-xs"
|
||||
/>
|
||||
<p className="text-[11px] text-muted-foreground">
|
||||
Leave blank to use the upstream Build node’s{" "}
|
||||
<code className="font-mono">__build.image</code>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="push-reg">Target registry</Label>
|
||||
<Input
|
||||
id="push-reg"
|
||||
value={targetRegistry}
|
||||
onChange={(e) =>
|
||||
onChange(setParam(node, "targetRegistry", e.target.value))
|
||||
}
|
||||
placeholder="ghcr.io"
|
||||
className="font-mono text-xs"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="push-tag">Tag</Label>
|
||||
<Input
|
||||
id="push-tag"
|
||||
value={tag}
|
||||
onChange={(e) => onChange(setParam(node, "tag", e.target.value))}
|
||||
placeholder="defaults to commit SHA, then 'latest'"
|
||||
className="font-mono text-xs"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="push-img">Target image</Label>
|
||||
<Input
|
||||
id="push-img"
|
||||
value={targetImage}
|
||||
onChange={(e) => onChange(setParam(node, "targetImage", e.target.value))}
|
||||
placeholder="org/agent"
|
||||
className="font-mono text-xs"
|
||||
/>
|
||||
<p className="text-[11px] text-muted-foreground">
|
||||
Final ref: <code className="font-mono">{targetRegistry || "<registry>"}/{targetImage || "<image>"}:{tag || "<tag>"}</code>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="push-user">Username</Label>
|
||||
<Input
|
||||
id="push-user"
|
||||
value={username}
|
||||
onChange={(e) => onChange(setParam(node, "username", e.target.value))}
|
||||
placeholder="(empty = anonymous / docker config)"
|
||||
className="font-mono text-xs"
|
||||
autoComplete="off"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="push-pass">Password / token</Label>
|
||||
<Input
|
||||
id="push-pass"
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => onChange(setParam(node, "password", e.target.value))}
|
||||
placeholder="ghp_… or registry password"
|
||||
className="font-mono text-xs"
|
||||
autoComplete="new-password"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rounded-md border bg-muted/20 p-2 text-[11px]">
|
||||
<div className="mb-1 font-medium uppercase tracking-wider text-muted-foreground">
|
||||
Insecure transport
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
id="push-src-insecure"
|
||||
type="checkbox"
|
||||
checked={srcInsecure}
|
||||
onChange={(e) =>
|
||||
onChange(setParam(node, "srcInsecure", e.target.checked))
|
||||
}
|
||||
className="size-3.5"
|
||||
/>
|
||||
<Label htmlFor="push-src-insecure" className="text-[11px]">
|
||||
Source allows HTTP (default; the local{" "}
|
||||
<code className="font-mono">registry:5000</code> serves plain HTTP)
|
||||
</Label>
|
||||
</div>
|
||||
<div className="mt-1 flex items-center gap-2">
|
||||
<input
|
||||
id="push-dst-insecure"
|
||||
type="checkbox"
|
||||
checked={dstInsecure}
|
||||
onChange={(e) =>
|
||||
onChange(setParam(node, "dstInsecure", e.target.checked))
|
||||
}
|
||||
className="size-3.5"
|
||||
/>
|
||||
<Label htmlFor="push-dst-insecure" className="text-[11px]">
|
||||
Destination allows HTTP (off — public registries are HTTPS)
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-[11px] text-muted-foreground">
|
||||
Image is pulled from the source over OCI v2 and pushed to the target;
|
||||
no docker daemon needed. For GHCR, the password is a PAT with{" "}
|
||||
<code className="font-mono">write:packages</code>.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function DeployForm({ node, onChange }: NodeFormProps) {
|
||||
const runtime = getString(node, "runtime", "kubernetes");
|
||||
const env = getString(node, "env", "dev");
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
Undo2,
|
||||
CircleSlash,
|
||||
Settings2,
|
||||
UploadCloud,
|
||||
} from "lucide-react";
|
||||
|
||||
export type CatalogEntry = {
|
||||
@@ -132,6 +133,26 @@ export const CATALOG: CatalogEntry[] = [
|
||||
},
|
||||
group: "gate",
|
||||
},
|
||||
{
|
||||
type: "flow-nodes-base.push",
|
||||
label: "Push",
|
||||
description:
|
||||
"Mirror the locally-built image to an external registry (GHCR, Docker Hub, ECR, etc.).",
|
||||
icon: UploadCloud,
|
||||
color: "bg-cyan-500",
|
||||
outputs: 1,
|
||||
defaults: {
|
||||
// srcImage left empty — defaults to upstream __build.image
|
||||
targetRegistry: "ghcr.io",
|
||||
targetImage: "",
|
||||
tag: "",
|
||||
username: "",
|
||||
password: "",
|
||||
srcInsecure: true,
|
||||
dstInsecure: false,
|
||||
},
|
||||
group: "deploy",
|
||||
},
|
||||
{
|
||||
type: "flow-nodes-base.deploy",
|
||||
label: "Deploy",
|
||||
|
||||
Reference in New Issue
Block a user