From 3c1352fb09e90de2086311efbcbef97d46e9c65c Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Mon, 9 Dec 2024 15:46:21 +0000 Subject: [PATCH 1/9] chore(README.md): add demo gif (#433) --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 09dfebbb..15726b4e 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,10 @@ # Envbuilder +![envbuilder](https://github.com/user-attachments/assets/0a49f5cd-2040-4a07-84ba-8b765b954e57) + +_(Video created using [asciinema](https://github.com/asciinema/asciinema) and [agg](https://github.com/asciinema/agg))_ + Build development environments from a Dockerfile on Docker, Kubernetes, and OpenShift. Allow developers to modify their environment in a tight feedback loop. - Supports [`devcontainer.json`](https://containers.dev/) and `Dockerfile` From 440ac5c906925e29ca55a512ed3468d3bd4e9c49 Mon Sep 17 00:00:00 2001 From: Alexander Zubarev Date: Thu, 12 Dec 2024 16:38:41 +0200 Subject: [PATCH 2/9] fix: use Lchown to prevent erroring on broken symlinks (#434) --- envbuilder.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/envbuilder.go b/envbuilder.go index ea1031ce..ad2c84ff 100644 --- a/envbuilder.go +++ b/envbuilder.go @@ -815,7 +815,7 @@ func run(ctx context.Context, opts options.Options, execArgs *execArgsInfo) erro if err != nil { return err } - return os.Chown(path, execArgs.UserInfo.uid, execArgs.UserInfo.gid) + return os.Lchown(path, execArgs.UserInfo.uid, execArgs.UserInfo.gid) }); chownErr != nil { opts.Logger(log.LevelError, "chown %q: %s", execArgs.UserInfo.user.HomeDir, chownErr.Error()) endStage("⚠️ Failed to the ownership of the workspace, you may need to fix this manually!") @@ -832,7 +832,7 @@ func run(ctx context.Context, opts options.Options, execArgs *execArgsInfo) erro if err != nil { return err } - return os.Chown(path, execArgs.UserInfo.uid, execArgs.UserInfo.gid) + return os.Lchown(path, execArgs.UserInfo.uid, execArgs.UserInfo.gid) }); chownErr != nil { opts.Logger(log.LevelError, "chown %q: %s", execArgs.UserInfo.user.HomeDir, chownErr.Error()) endStage("⚠️ Failed to update ownership of %s, you may need to fix this manually!", execArgs.UserInfo.user.HomeDir) From 1ab3f6977b1c98e3115deeb7f3c6fdf4e2669163 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Thu, 19 Dec 2024 10:29:08 +0000 Subject: [PATCH 3/9] chore(integration): test with a locally-hosted devcontainer feature (#438) Co-authored-by: Mathias Fredriksson --- integration/integration_test.go | 84 +++++++++++++++++++++------------ 1 file changed, 55 insertions(+), 29 deletions(-) diff --git a/integration/integration_test.go b/integration/integration_test.go index 6e9d2383..26e01951 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -1545,6 +1545,23 @@ COPY %s .`, testImageAlpine, inclFile) func TestPushImage(t *testing.T) { t.Parallel() + // Write a test feature to an in-memory registry. + testFeature := registrytest.WriteContainer(t, registrytest.New(t), "features/test-feature:latest", features.TarLayerMediaType, map[string]any{ + "install.sh": `#!/bin/sh + echo "${MESSAGE}" > /root/message.txt`, + "devcontainer-feature.json": features.Spec{ + ID: "test-feature", + Name: "test feature", + Version: "v0.0.1", + Options: map[string]features.Option{ + "message": { + Type: "string", + Default: "hello world", + }, + }, + }, + }) + t.Run("CacheWithoutPush", func(t *testing.T) { t.Parallel() @@ -1557,12 +1574,15 @@ WORKDIR $WORKDIR ENV FOO=bar RUN echo $FOO > /root/foo.txt RUN date --utc > /root/date.txt`, testImageAlpine), - ".devcontainer/devcontainer.json": `{ + ".devcontainer/devcontainer.json": fmt.Sprintf(`{ "name": "Test", "build": { "dockerfile": "Dockerfile" }, - }`, + "features": { + %q: {} + } + }`, testFeature), }, }) @@ -1603,7 +1623,7 @@ RUN date --utc > /root/date.txt`, testImageAlpine), envbuilderEnv("CACHE_REPO", testRepo), envbuilderEnv("GET_CACHED_IMAGE", "1"), }}) - require.ErrorContains(t, err, "uncached COPY command is not supported in cache probe mode") + require.Regexp(t, `uncached.*command.*is not supported in cache probe mode`, err.Error()) }) t.Run("CacheAndPush", func(t *testing.T) { @@ -1612,21 +1632,27 @@ RUN date --utc > /root/date.txt`, testImageAlpine), ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) + // Given: a git repository with a devcontainer.json that references the + // feature srv := gittest.CreateGitServer(t, gittest.Options{ Files: map[string]string{ ".devcontainer/Dockerfile": fmt.Sprintf(`FROM %s -USER root -ARG WORKDIR=/ -WORKDIR $WORKDIR -ENV FOO=bar -RUN echo $FOO > /root/foo.txt -RUN date --utc > /root/date.txt`, testImageAlpine), - ".devcontainer/devcontainer.json": `{ - "name": "Test", - "build": { - "dockerfile": "Dockerfile" - }, - }`, + USER root + ARG WORKDIR=/ + WORKDIR $WORKDIR + ENV FOO=bar + RUN echo $FOO > /root/foo.txt + RUN date --utc > /root/date.txt`, testImageAlpine), + ".devcontainer/devcontainer.json": fmt.Sprintf(` + { + "name": "Test", + "build": { + "dockerfile": "Dockerfile" + }, + "features": { + %q: {} + } + }`, testFeature), }, }) @@ -1671,6 +1697,9 @@ RUN date --utc > /root/date.txt`, testImageAlpine), require.Regexp(t, `(?s)^USAGE:\s+envbuilder`, strings.TrimSpace(out)) out = execContainer(t, ctr.ID, "cat /root/date.txt") require.NotEmpty(t, strings.TrimSpace(out)) + // Then: the feature install script was run + out = execContainer(t, ctr.ID, "cat /root/message.txt") + require.Equal(t, "hello world", strings.TrimSpace(out)) }) t.Run("CacheAndPushDevcontainerOnly", func(t *testing.T) { @@ -1702,7 +1731,7 @@ RUN date --utc > /root/date.txt`, testImageAlpine), _, err = runEnvbuilder(t, runOpts{env: append(opts, envbuilderEnv("GET_CACHED_IMAGE", "1"), )}) - require.ErrorContains(t, err, "error probing build cache: uncached COPY command") + require.Regexp(t, "error probing build cache: uncached.*command.*is not supported in cache probe mode", err.Error()) // Then: it should fail to build the image and nothing should be pushed _, err = remote.Image(ref) require.ErrorContains(t, err, "NAME_UNKNOWN", "expected image to not be present before build + push") @@ -2293,7 +2322,7 @@ RUN date --utc > /root/date.txt`, testImageAlpine), require.NoError(t, err) }) - t.Run("CacheAndPushDevcontainerFeatures", func(t *testing.T) { + t.Run("CacheAndPushDevcontainerFeaturesOverrideOption", func(t *testing.T) { t.Parallel() ctx, cancel := context.WithCancel(context.Background()) @@ -2301,18 +2330,15 @@ RUN date --utc > /root/date.txt`, testImageAlpine), srv := gittest.CreateGitServer(t, gittest.Options{ Files: map[string]string{ - // NOTE(mafredri): We can't cache the feature in our local - // registry because the image media type is incompatible. ".devcontainer/devcontainer.json": fmt.Sprintf(` -{ - "image": %q, - "features": { - "ghcr.io/devcontainers/feature-starter/color:1": { - "favorite": "green" - } - } -} -`, testImageUbuntu), + { + "image": %q, + "features": { + %q: { + "message": "my favorite color is green" + } + } + }`, testImageUbuntu, testFeature), }, }) @@ -2343,7 +2369,7 @@ RUN date --utc > /root/date.txt`, testImageAlpine), ctr := startContainerFromRef(ctx, t, cli, cachedRef) // Check that the feature is present in the image. - out := execContainer(t, ctr.ID, "/usr/local/bin/color") + out := execContainer(t, ctr.ID, "cat /root/message.txt") require.Contains(t, strings.TrimSpace(out), "my favorite color is green") }) From 2c0c991d4a03714e6cd44cde26b88ce6510d8190 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Thu, 19 Dec 2024 18:07:52 +0000 Subject: [PATCH 4/9] refactor(testutil): refactor registrytest (#439) --- devcontainer/devcontainer_test.go | 4 +-- integration/integration_test.go | 11 ++------ testutil/registrytest/registrytest.go | 39 ++++++++++++++++----------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/devcontainer/devcontainer_test.go b/devcontainer/devcontainer_test.go index 4a475682..d304e763 100644 --- a/devcontainer/devcontainer_test.go +++ b/devcontainer/devcontainer_test.go @@ -231,7 +231,7 @@ func TestUserFrom(t *testing.T) { }}) require.NoError(t, err) - parsed, err := url.Parse(registry) + parsed, err := url.Parse("http://" + registry) require.NoError(t, err) parsed.Path = "coder/test:latest" ref, err := name.ParseReference(strings.TrimPrefix(parsed.String(), "http://")) @@ -306,7 +306,7 @@ func TestUserFrom(t *testing.T) { }, }}) require.NoError(t, err) - parsed, err := url.Parse(registry) + parsed, err := url.Parse("http://" + registry) require.NoError(t, err) parsed.Path = "coder/test:" + tag ref, err := name.ParseReference(strings.TrimPrefix(parsed.String(), "http://")) diff --git a/integration/integration_test.go b/integration/integration_test.go index 26e01951..913ab567 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -47,7 +47,6 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/name" - "github.com/google/go-containerregistry/pkg/registry" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/google/go-containerregistry/pkg/v1/remote/transport" @@ -2507,14 +2506,8 @@ type setupInMemoryRegistryOpts struct { func setupInMemoryRegistry(t *testing.T, opts setupInMemoryRegistryOpts) string { t.Helper() - tempDir := t.TempDir() - regHandler := registry.New(registry.WithBlobHandler(registry.NewDiskBlobHandler(tempDir))) - authHandler := mwtest.BasicAuthMW(opts.Username, opts.Password)(regHandler) - regSrv := httptest.NewServer(authHandler) - t.Cleanup(func() { regSrv.Close() }) - regSrvURL, err := url.Parse(regSrv.URL) - require.NoError(t, err) - return fmt.Sprintf("localhost:%s", regSrvURL.Port()) + regSrv := registrytest.New(t, mwtest.BasicAuthMW(opts.Username, opts.Password)) + return regSrv } // TestMain runs before all tests to build the envbuilder image. diff --git a/testutil/registrytest/registrytest.go b/testutil/registrytest/registrytest.go index 033fd75b..632c1836 100644 --- a/testutil/registrytest/registrytest.go +++ b/testutil/registrytest/registrytest.go @@ -3,45 +3,46 @@ package registrytest import ( "archive/tar" "bytes" - "context" "crypto" "encoding/hex" "encoding/json" + "fmt" "io" + "net/http" "net/http/httptest" "net/url" "strings" "testing" "time" - "github.com/distribution/distribution/v3/configuration" - "github.com/distribution/distribution/v3/registry/handlers" "github.com/google/go-containerregistry/pkg/name" + "github.com/google/go-containerregistry/pkg/registry" 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/partial" "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/google/go-containerregistry/pkg/v1/types" - "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" // needed by the registry _ "github.com/distribution/distribution/v3/registry/storage/driver/inmemory" ) -// New creates a new registry server that discards all logs. -func New(t *testing.T) string { - cfg := &configuration.Configuration{ - Storage: configuration.Storage{ - "inmemory": configuration.Parameters{}, - }, +// New starts a new Docker registry listening on localhost. +// It will automatically shut down when the test finishes. +// It will store data in memory. +func New(t testing.TB, mws ...func(http.Handler) http.Handler) string { + t.Helper() + regHandler := registry.New(registry.WithBlobHandler(registry.NewInMemoryBlobHandler())) + for _, mw := range mws { + regHandler = mw(regHandler) } - logrus.SetOutput(io.Discard) - app := handlers.NewApp(context.Background(), cfg) - srv := httptest.NewServer(app) - t.Cleanup(srv.Close) - return srv.URL + regSrv := httptest.NewServer(regHandler) + t.Cleanup(func() { regSrv.Close() }) + regSrvURL, err := url.Parse(regSrv.URL) + require.NoError(t, err) + return fmt.Sprintf("localhost:%s", regSrvURL.Port()) } // WriteContainer uploads a container to the registry server. @@ -96,11 +97,17 @@ func WriteContainer(t *testing.T, serverURL, containerRef, mediaType string, fil }) require.NoError(t, err) + // url.Parse will interpret localhost:12345 as scheme localhost and host 12345 + // so we need to add a scheme to the URL + if !strings.HasPrefix(serverURL, "http://") { + serverURL = "http://" + serverURL + } parsed, err := url.Parse(serverURL) require.NoError(t, err) parsed.Path = containerRef + parsedStr := parsed.String() - ref, err := name.ParseReference(strings.TrimPrefix(parsed.String(), "http://")) + ref, err := name.ParseReference(strings.TrimPrefix(parsedStr, "http://")) require.NoError(t, err) err = remote.Write(ref, image) From ebe6ddc452c6f492e36cc051ef3b20bce6cf25d0 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Tue, 7 Jan 2025 12:24:45 +0000 Subject: [PATCH 5/9] chore: update go-git to v5.13.1 (#443) --- go.mod | 29 +++++++++++++------------- go.sum | 64 +++++++++++++++++++++++++++++----------------------------- 2 files changed, 46 insertions(+), 47 deletions(-) diff --git a/go.mod b/go.mod index f1fe25d1..fdd498f6 100644 --- a/go.mod +++ b/go.mod @@ -22,9 +22,9 @@ require ( github.com/docker/cli v27.2.1+incompatible github.com/docker/docker v27.3.1+incompatible github.com/fatih/color v1.17.0 - github.com/gliderlabs/ssh v0.3.7 - github.com/go-git/go-billy/v5 v5.5.0 - github.com/go-git/go-git/v5 v5.12.0 + github.com/gliderlabs/ssh v0.3.8 + github.com/go-git/go-billy/v5 v5.6.1 + github.com/go-git/go-git/v5 v5.13.1 github.com/google/go-cmp v0.6.0 github.com/google/go-containerregistry v0.20.1 github.com/google/uuid v1.6.0 @@ -36,12 +36,12 @@ require ( github.com/prometheus/procfs v0.15.1 github.com/sirupsen/logrus v1.9.3 github.com/skeema/knownhosts v1.3.0 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.10.0 github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a go.uber.org/mock v0.4.0 - golang.org/x/crypto v0.29.0 + golang.org/x/crypto v0.31.0 golang.org/x/mod v0.21.0 - golang.org/x/sync v0.9.0 + golang.org/x/sync v0.10.0 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 ) @@ -71,7 +71,7 @@ require ( github.com/DataDog/sketches-go v1.4.2 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/hcsshim v0.11.7 // indirect - github.com/ProtonMail/go-crypto v1.1.0-alpha.2 // indirect + github.com/ProtonMail/go-crypto v1.1.3 // indirect github.com/agext/levenshtein v1.2.3 // indirect github.com/akutz/memconn v0.1.0 // indirect github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 // indirect @@ -116,7 +116,7 @@ require ( github.com/containerd/typeurl/v2 v2.2.0 // indirect github.com/coreos/go-iptables v0.6.0 // indirect github.com/coreos/go-oidc/v3 v3.10.0 // indirect - github.com/cyphar/filepath-securejoin v0.2.4 // indirect + github.com/cyphar/filepath-securejoin v0.3.6 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect @@ -230,7 +230,6 @@ require ( github.com/richardartoul/molecule v1.0.1-0.20221107223329-32cfee06a052 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect - github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/secure-systems-lab/go-securesystemslib v0.7.0 // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect @@ -271,14 +270,14 @@ require ( go.uber.org/atomic v1.11.0 // indirect go4.org/mem v0.0.0-20220726221520-4f986261bf13 // indirect go4.org/netipx v0.0.0-20230728180743-ad4cb58a6516 // indirect - golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect - golang.org/x/net v0.26.0 // indirect + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect + golang.org/x/net v0.33.0 // indirect golang.org/x/oauth2 v0.20.0 // indirect - golang.org/x/sys v0.27.0 // indirect - golang.org/x/term v0.26.0 // indirect - golang.org/x/text v0.20.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/term v0.27.0 // indirect + golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.22.0 // indirect + golang.org/x/tools v0.23.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wireguard/windows v0.5.3 // indirect google.golang.org/appengine v1.6.8 // indirect diff --git a/go.sum b/go.sum index 10106a44..98bcdd19 100644 --- a/go.sum +++ b/go.sum @@ -70,8 +70,8 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Microsoft/hcsshim v0.11.7 h1:vl/nj3Bar/CvJSYo7gIQPyRWc9f3c6IeSNavBTSZNZQ= github.com/Microsoft/hcsshim v0.11.7/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU= -github.com/ProtonMail/go-crypto v1.1.0-alpha.2 h1:bkyFVUP+ROOARdgCiJzNQo2V2kiB97LyUpzH9P6Hrlg= -github.com/ProtonMail/go-crypto v1.1.0-alpha.2/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk= +github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/akutz/memconn v0.1.0 h1:NawI0TORU4hcOMsMr11g7vwlCdkYeLKXBcxWu2W/P8A= @@ -215,8 +215,8 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0= github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= -github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= +github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM= +github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -255,8 +255,8 @@ github.com/ePirat/docker-credential-gitlabci v1.0.0 h1:YRkUSvkON6rT88vtscClAmPEY github.com/ePirat/docker-credential-gitlabci v1.0.0/go.mod h1:Ptmh+D0lzBQtgb6+QHjXl9HqOn3T1P8fKUHldiSQQGA= github.com/ebitengine/purego v0.6.0-alpha.5 h1:EYID3JOAdmQ4SNZYJHu9V6IqOeRQDBYxqKAg9PyoHFY= github.com/ebitengine/purego v0.6.0-alpha.5/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ= -github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= -github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= +github.com/elazarl/goproxy v1.2.3 h1:xwIyKHbaP5yfT6O9KIeYJR5549MXRQkoQMRXGztz8YQ= +github.com/elazarl/goproxy v1.2.3/go.mod h1:YfEbZtqP4AetfO6d40vWchF3znWX7C7Vd6ZMfdL8z64= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -283,18 +283,18 @@ github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/github/fakeca v0.1.0 h1:Km/MVOFvclqxPM9dZBC4+QE564nU4gz4iZ0D9pMw28I= github.com/github/fakeca v0.1.0/go.mod h1:+bormgoGMMuamOscx7N91aOuUST7wdaJ2rNjeohylyo= -github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE= -github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8= +github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= +github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk= github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= -github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= +github.com/go-git/go-billy/v5 v5.6.1 h1:u+dcrgaguSSkbjzHwelEjc0Yj300NUevrrPphk/SoRA= +github.com/go-git/go-billy/v5 v5.6.1/go.mod h1:0AsLr1z2+Uksi4NlElmMblP5rPcDZNRCD8ujZCRR2BE= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys= -github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY= +github.com/go-git/go-git/v5 v5.13.1 h1:DAQ9APonnlvSWpvolXWIuV6Q6zXy2wHbN4cVlNR5Q+M= +github.com/go-git/go-git/v5 v5.13.1/go.mod h1:qryJB4cSBoq3FRoBRf5A77joojuBcmPJ0qu3XXXVixc= github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U= github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -600,8 +600,8 @@ github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce h1:RPclfga2SEJmgMmz2k github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce/go.mod h1:uFMI8w+ref4v2r9jz+c9i1IfIttS/OkmLfrk1jne5hs= github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= -github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= 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 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= @@ -710,8 +710,8 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/swaggest/assertjson v1.9.0 h1:dKu0BfJkIxv/xe//mkCrK5yZbs79jL7OVf9Ija7o2xQ= github.com/swaggest/assertjson v1.9.0/go.mod h1:b+ZKX2VRiUjxfUIal0HDN85W0nHPAYUbYH5WkkSsFsU= github.com/tailscale/certstore v0.1.1-0.20220316223106-78d6e1c49d8d h1:K3j02b5j2Iw1xoggN9B2DIEkhWGheqFOeDkdJdBrJI8= @@ -838,11 +838,11 @@ golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= -golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY= -golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -873,8 +873,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= @@ -887,8 +887,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -927,15 +927,15 @@ golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.1-0.20230131160137-e7d7f63158de/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.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= -golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -945,8 +945,8 @@ golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= @@ -962,8 +962,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= -golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= -golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= +golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= +golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= 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= From d045c1ce27a5995ac60409bd48ad8e90861b6bfd Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Tue, 7 Jan 2025 14:15:02 +0000 Subject: [PATCH 6/9] chore(docs): add example for ENVBUILDER_GIT_SSH_PRIVATE_KEY_BASE64 (#442) --- docs/git-auth.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/git-auth.md b/docs/git-auth.md index 5f0acb0b..e2b8cd28 100644 --- a/docs/git-auth.md +++ b/docs/git-auth.md @@ -49,6 +49,18 @@ envbuilder will assume SSH authentication. You have the following options: ghcr.io/coder/envbuilder ``` + Alternatively, you can set `ENVBUILDER_GIT_SSH_PRIVATE_KEY_BASE64` to the + base64-encoded content of your private key. Example: + + ```bash + docker run -it --rm \ + -v /tmp/envbuilder:/workspaces \ + -e ENVBUILDER_GIT_URL=git@example.com:path/to/private/repo.git \ + -e ENVBUILDER_INIT_SCRIPT=bash \ + -e ENVBUILDER_GIT_SSH_PRIVATE_KEY_BASE64=$(base64 < ~/.ssh/id_ed25519) \ + ghcr.io/coder/envbuilder + ``` + 1. Agent-based authentication: set `SSH_AUTH_SOCK` and mount in your agent socket, for example: ```bash From 6f12850edf9a5551bc8ffdb3ff5b0a2d357cbe94 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Wed, 5 Feb 2025 20:53:35 +0000 Subject: [PATCH 7/9] chore(README.md): add example for specifying branch (#451) --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 15726b4e..d508f00c 100644 --- a/README.md +++ b/README.md @@ -19,10 +19,11 @@ Build development environments from a Dockerfile on Docker, Kubernetes, and Open ## Getting Started -The easiest way to get started is by running the `envbuilder` Docker container that clones a repository, builds the image from a Dockerfile, and runs the `$ENVBUILDER_INIT_SCRIPT` in the freshly built container. - -> **Note**: The `/tmp/envbuilder` directory persists demo data between commands. You can choose a different directory if needed. +The easiest way to get started is by running the `envbuilder` Docker container that clones a repository specified by `ENVBUILDER_GIT_URL`, builds the image from a Dockerfile or `devcontainer.json`, and runs the `$ENVBUILDER_INIT_SCRIPT` in the freshly built container. +> **Tips**: +> - The `/tmp/envbuilder` directory persists demo data between commands. You can choose a different directory if needed. +> - To clone a different branch, you append it to `ENVBUILDER_GIT_URL` in the form `#refs/heads/my-branch`. For example: `https://github.com/coder/envbuilder-starter-devcontainer#refs/heads/boring-prompt`. ```bash docker run -it --rm -v /tmp/envbuilder:/workspaces From 71555b8d8341364a2e09e8b43776f4f76394b485 Mon Sep 17 00:00:00 2001 From: CH-Chang <56328238+CH-Chang@users.noreply.github.com> Date: Thu, 13 Mar 2025 19:12:16 +0800 Subject: [PATCH 8/9] feat: add git clone with thinpack option to support self hosted azure devops (#446) --- docs/env-variables.md | 1 + git/git.go | 16 ++++++++++++++-- options/options.go | 11 +++++++++++ options/options_test.go | 8 ++++++++ options/testdata/options.golden | 5 +++++ 5 files changed, 39 insertions(+), 2 deletions(-) diff --git a/docs/env-variables.md b/docs/env-variables.md index 861f31b0..e6fa7ca5 100644 --- a/docs/env-variables.md +++ b/docs/env-variables.md @@ -27,6 +27,7 @@ | `--git-url` | `ENVBUILDER_GIT_URL` | | The URL of a Git repository containing a Devcontainer or Docker image to clone. This is optional. | | `--git-clone-depth` | `ENVBUILDER_GIT_CLONE_DEPTH` | | The depth to use when cloning the Git repository. | | `--git-clone-single-branch` | `ENVBUILDER_GIT_CLONE_SINGLE_BRANCH` | | Clone only a single branch of the Git repository. | +| `--git-clone-thinpack` | `ENVBUILDER_GIT_CLONE_THINPACK` | `true` | Git clone with thin pack compatibility enabled, ensuring that even when thin pack compatibility is activated,it will not be turned on for the domain dev.zaure.com. | | `--git-username` | `ENVBUILDER_GIT_USERNAME` | | The username to use for Git authentication. This is optional. | | `--git-password` | `ENVBUILDER_GIT_PASSWORD` | | The password to use for Git authentication. This is optional. | | `--git-ssh-private-key-path` | `ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH` | | Path to an SSH private key to be used for Git authentication. If this is set, then GIT_SSH_PRIVATE_KEY_BASE64 cannot be set. | diff --git a/git/git.go b/git/git.go index f37b9682..efcffa91 100644 --- a/git/git.go +++ b/git/git.go @@ -37,6 +37,7 @@ type CloneRepoOptions struct { Progress sideband.Progress Insecure bool SingleBranch bool + ThinPack bool Depth int CABundle []byte ProxyOptions transport.ProxyOptions @@ -53,7 +54,13 @@ func CloneRepo(ctx context.Context, logf func(string, ...any), opts CloneRepoOpt return false, fmt.Errorf("parse url %q: %w", opts.RepoURL, err) } logf("Parsed Git URL as %q", parsed.Redacted()) - if parsed.Hostname() == "dev.azure.com" { + + thinPack := true + + if !opts.ThinPack { + thinPack = false + logf("ThinPack options is false, Marking thin-pack as unsupported") + } else if parsed.Hostname() == "dev.azure.com" { // Azure DevOps requires capabilities multi_ack / multi_ack_detailed, // which are not fully implemented and by default are included in // transport.UnsupportedCapabilities. @@ -71,10 +78,14 @@ func CloneRepo(ctx context.Context, logf func(string, ...any), opts CloneRepoOpt // // This is knowingly not safe to call in parallel, but it seemed // like the least-janky place to add a super janky hack. + thinPack = false + logf("Workaround for Azure DevOps: marking thin-pack as unsupported") + } + + if !thinPack { transport.UnsupportedCapabilities = []capability.Capability{ capability.ThinPack, } - logf("Workaround for Azure DevOps: marking thin-pack as unsupported") } err = opts.Storage.MkdirAll(opts.Path, 0o755) @@ -347,6 +358,7 @@ func CloneOptionsFromOptions(logf func(string, ...any), options options.Options) Storage: options.Filesystem, Insecure: options.Insecure, SingleBranch: options.GitCloneSingleBranch, + ThinPack: options.GitCloneThinPack, Depth: int(options.GitCloneDepth), CABundle: caBundle, } diff --git a/options/options.go b/options/options.go index c2b6efe6..8cdf723a 100644 --- a/options/options.go +++ b/options/options.go @@ -106,6 +106,8 @@ type Options struct { GitCloneDepth int64 // GitCloneSingleBranch clone only a single branch of the Git repository. GitCloneSingleBranch bool + // GitCloneThinPack clone with thin pack compabilities. This is optional. + GitCloneThinPack bool // GitUsername is the username to use for Git authentication. This is // optional. GitUsername string @@ -375,6 +377,15 @@ func (o *Options) CLI() serpent.OptionSet { Value: serpent.BoolOf(&o.GitCloneSingleBranch), Description: "Clone only a single branch of the Git repository.", }, + { + Flag: "git-clone-thinpack", + Env: WithEnvPrefix("GIT_CLONE_THINPACK"), + Value: serpent.BoolOf(&o.GitCloneThinPack), + Default: "true", + Description: "Git clone with thin pack compatibility enabled, " + + "ensuring that even when thin pack compatibility is activated," + + "it will not be turned on for the domain dev.zaure.com.", + }, { Flag: "git-username", Env: WithEnvPrefix("GIT_USERNAME"), diff --git a/options/options_test.go b/options/options_test.go index bf7a216c..ed5dcd3c 100644 --- a/options/options_test.go +++ b/options/options_test.go @@ -38,31 +38,39 @@ func TestEnvOptionParsing(t *testing.T) { t.Run("lowercase", func(t *testing.T) { t.Setenv(options.WithEnvPrefix("SKIP_REBUILD"), "true") t.Setenv(options.WithEnvPrefix("GIT_CLONE_SINGLE_BRANCH"), "false") + t.Setenv(options.WithEnvPrefix("GIT_CLONE_THINPACK"), "false") o := runCLI() require.True(t, o.SkipRebuild) require.False(t, o.GitCloneSingleBranch) + require.False(t, o.GitCloneThinPack) }) t.Run("uppercase", func(t *testing.T) { t.Setenv(options.WithEnvPrefix("SKIP_REBUILD"), "TRUE") t.Setenv(options.WithEnvPrefix("GIT_CLONE_SINGLE_BRANCH"), "FALSE") + t.Setenv(options.WithEnvPrefix("GIT_CLONE_THINPACK"), "FALSE") o := runCLI() require.True(t, o.SkipRebuild) require.False(t, o.GitCloneSingleBranch) + require.False(t, o.GitCloneThinPack) }) t.Run("numeric", func(t *testing.T) { t.Setenv(options.WithEnvPrefix("SKIP_REBUILD"), "1") t.Setenv(options.WithEnvPrefix("GIT_CLONE_SINGLE_BRANCH"), "0") + t.Setenv(options.WithEnvPrefix("GIT_CLONE_THINPACK"), "0") o := runCLI() require.True(t, o.SkipRebuild) require.False(t, o.GitCloneSingleBranch) + require.False(t, o.GitCloneThinPack) }) t.Run("empty", func(t *testing.T) { t.Setenv(options.WithEnvPrefix("GIT_CLONE_SINGLE_BRANCH"), "") + t.Setenv(options.WithEnvPrefix("GIT_CLONE_THINPACK"), "") o := runCLI() require.False(t, o.GitCloneSingleBranch) + require.True(t, o.GitCloneThinPack) }) }) } diff --git a/options/testdata/options.golden b/options/testdata/options.golden index 6a8145ad..92a85232 100644 --- a/options/testdata/options.golden +++ b/options/testdata/options.golden @@ -99,6 +99,11 @@ OPTIONS: --git-clone-single-branch bool, $ENVBUILDER_GIT_CLONE_SINGLE_BRANCH Clone only a single branch of the Git repository. + --git-clone-thinpack bool, $ENVBUILDER_GIT_CLONE_THINPACK (default: true) + Git clone with thin pack compatibility enabled, ensuring that even + when thin pack compatibility is activated,it will not be turned on for + the domain dev.zaure.com. + --git-http-proxy-url string, $ENVBUILDER_GIT_HTTP_PROXY_URL The URL for the HTTP proxy. This is optional. From 7eabaa4d876c3ce54f7271b6e68fea37b6719c66 Mon Sep 17 00:00:00 2001 From: Sas Swart Date: Mon, 17 Mar 2025 14:26:16 +0200 Subject: [PATCH 9/9] chore: clarify ssl proxy documentation (#448) --- docs/proxy.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/proxy.md b/docs/proxy.md index a377463d..65c36040 100644 --- a/docs/proxy.md +++ b/docs/proxy.md @@ -13,8 +13,8 @@ To configure Envbuilder to route HTTP traffic for git and the container registry Once traffic is routed to the proxy, you will need to install the proxy's CA certificate into Envbuilder. To do this, you can do one of the following: * Set `ENVBUILDER_SSL_CERT_BASE64=...` to the base64 encoded value of your proxy's CA certificate. This will only apply to Envbuilder. Other TLS connections within your container will not be aware of this certificate. -* Mount the certificate file into the Envbuilder container and then set `ENVBUILDER_SSL_CERT_FILE=/path/to/cert.pem`. -* Mount a directory containing all relevant CA certificates into the Envbuilder container and then set `ENVBUILDER_SSL_CERT_DIR=/path/to/certs/`. +* Mount the certificate file into the Envbuilder container and then set `SSL_CERT_FILE=/path/to/cert.pem`. +* Mount a directory containing all relevant CA certificates into the Envbuilder container and then set `SSL_CERT_DIR=/path/to/certs/`. ## Demonstration Envbuilder clones a repository that contains your `devcontainer.json` and optional `Dockerfile` so that it can build your container. If the clone is done using HTTPS, then TLS verification must succeed or be disabled. If a transparent HTTPS proxy is present, TLS verification will fail unless Envbuilder trusts the proxy’s certificate. Therefore, we need to configure Envbuilder to trust your proxy.