4
4
"bufio"
5
5
"bytes"
6
6
"context"
7
- "crypto/x509"
8
7
"encoding/base64"
9
8
"encoding/json"
10
9
"errors"
@@ -41,7 +40,6 @@ import (
41
40
_ "github.com/distribution/distribution/v3/registry/storage/driver/filesystem"
42
41
"github.com/docker/cli/cli/config/configfile"
43
42
"github.com/fatih/color"
44
- "github.com/go-git/go-git/v5/plumbing/transport"
45
43
v1 "github.com/google/go-containerregistry/pkg/v1"
46
44
"github.com/google/go-containerregistry/pkg/v1/remote"
47
45
"github.com/kballard/go-shellquote"
@@ -88,23 +86,6 @@ func Run(ctx context.Context, opts options.Options) error {
88
86
89
87
opts .Logger (log .LevelInfo , "%s - Build development environments from repositories in a container" , newColor (color .Bold ).Sprintf ("envbuilder" ))
90
88
91
- var caBundle []byte
92
- if opts .SSLCertBase64 != "" {
93
- certPool , err := x509 .SystemCertPool ()
94
- if err != nil {
95
- return xerrors .Errorf ("get global system cert pool: %w" , err )
96
- }
97
- data , err := base64 .StdEncoding .DecodeString (opts .SSLCertBase64 )
98
- if err != nil {
99
- return xerrors .Errorf ("base64 decode ssl cert: %w" , err )
100
- }
101
- ok := certPool .AppendCertsFromPEM (data )
102
- if ! ok {
103
- return xerrors .Errorf ("failed to append the ssl cert to the global pool: %s" , data )
104
- }
105
- caBundle = data
106
- }
107
-
108
89
if opts .DockerConfigBase64 != "" {
109
90
decoded , err := base64 .StdEncoding .DecodeString (opts .DockerConfigBase64 )
110
91
if err != nil {
@@ -125,51 +106,23 @@ func Run(ctx context.Context, opts options.Options) error {
125
106
}
126
107
}
127
108
109
+ buildTimeWorkspaceFolder := opts .WorkspaceFolder
128
110
var fallbackErr error
129
111
var cloned bool
130
112
if opts .GitURL != "" {
113
+ cloneOpts , err := git .CloneOptionsFromOptions (opts )
114
+ if err != nil {
115
+ return fmt .Errorf ("git clone options: %w" , err )
116
+ }
117
+
131
118
endStage := startStage ("📦 Cloning %s to %s..." ,
132
119
newColor (color .FgCyan ).Sprintf (opts .GitURL ),
133
- newColor (color .FgCyan ).Sprintf (opts . WorkspaceFolder ),
120
+ newColor (color .FgCyan ).Sprintf (cloneOpts . Path ),
134
121
)
135
122
136
- reader , writer := io .Pipe ()
137
- defer reader .Close ()
138
- defer writer .Close ()
139
- go func () {
140
- data := make ([]byte , 4096 )
141
- for {
142
- read , err := reader .Read (data )
143
- if err != nil {
144
- return
145
- }
146
- content := data [:read ]
147
- for _ , line := range strings .Split (string (content ), "\r " ) {
148
- if line == "" {
149
- continue
150
- }
151
- opts .Logger (log .LevelInfo , "#1: %s" , strings .TrimSpace (line ))
152
- }
153
- }
154
- }()
155
-
156
- cloneOpts := git.CloneRepoOptions {
157
- Path : opts .WorkspaceFolder ,
158
- Storage : opts .Filesystem ,
159
- Insecure : opts .Insecure ,
160
- Progress : writer ,
161
- SingleBranch : opts .GitCloneSingleBranch ,
162
- Depth : int (opts .GitCloneDepth ),
163
- CABundle : caBundle ,
164
- }
165
-
166
- cloneOpts .RepoAuth = git .SetupRepoAuth (& opts )
167
- if opts .GitHTTPProxyURL != "" {
168
- cloneOpts .ProxyOptions = transport.ProxyOptions {
169
- URL : opts .GitHTTPProxyURL ,
170
- }
171
- }
172
- cloneOpts .RepoURL = opts .GitURL
123
+ w := git .Logger (func (line string ) { opts .Logger (log .LevelInfo , "#%d: %s" , stageNumber , line ) })
124
+ defer w .Close ()
125
+ cloneOpts .Progress = w
173
126
174
127
cloned , fallbackErr = git .CloneRepo (ctx , cloneOpts )
175
128
if fallbackErr == nil {
@@ -182,6 +135,37 @@ func Run(ctx context.Context, opts options.Options) error {
182
135
opts .Logger (log .LevelError , "Failed to clone repository: %s" , fallbackErr .Error ())
183
136
opts .Logger (log .LevelError , "Falling back to the default image..." )
184
137
}
138
+
139
+ // The repo wasn't cloned (i.e. may not be up-to-date with remote), so
140
+ // we need to clone it to get the right build context. This is only
141
+ // necessary when the repo is in remote build mode. If the repo is in
142
+ // local build mode, the build context is already available on the host
143
+ // filesystem.
144
+ if fallbackErr == nil && ! cloned && opts .RepoBuildMode == options .RepoBuildModeRemote {
145
+ cloneOpts , err := git .CloneOptionsFromOptions (opts )
146
+ if err != nil {
147
+ return fmt .Errorf ("git clone options: %w" , err )
148
+ }
149
+ cloneOpts .Path = constants .MagicRemoteRepoDir
150
+
151
+ endStage := startStage ("📦 Remote build mode enabled, cloning %s to %s for build context..." ,
152
+ newColor (color .FgCyan ).Sprintf (opts .GitURL ),
153
+ newColor (color .FgCyan ).Sprintf (cloneOpts .Path ),
154
+ )
155
+
156
+ w := git .Logger (func (line string ) { opts .Logger (log .LevelInfo , "#%d: %s" , stageNumber , line ) })
157
+ defer w .Close ()
158
+ cloneOpts .Progress = w
159
+
160
+ fallbackErr = git .ShallowCloneRepo (ctx , cloneOpts )
161
+ if fallbackErr == nil {
162
+ endStage ("📦 Cloned repository!" )
163
+ buildTimeWorkspaceFolder = cloneOpts .Path
164
+ } else {
165
+ opts .Logger (log .LevelError , "Failed to clone repository for remote repo mode: %s" , err .Error ())
166
+ opts .Logger (log .LevelError , "Falling back to the default image..." )
167
+ }
168
+ }
185
169
}
186
170
187
171
defaultBuildParams := func () (* devcontainer.Compiled , error ) {
@@ -222,7 +206,7 @@ func Run(ctx context.Context, opts options.Options) error {
222
206
// devcontainer is a standard, so it's reasonable to be the default.
223
207
var devcontainerDir string
224
208
var err error
225
- devcontainerPath , devcontainerDir , err = findDevcontainerJSON (opts )
209
+ devcontainerPath , devcontainerDir , err = findDevcontainerJSON (buildTimeWorkspaceFolder , opts )
226
210
if err != nil {
227
211
opts .Logger (log .LevelError , "Failed to locate devcontainer.json: %s" , err .Error ())
228
212
opts .Logger (log .LevelError , "Falling back to the default image..." )
@@ -614,10 +598,10 @@ ENTRYPOINT [%q]`, exePath, exePath, exePath)
614
598
if err != nil {
615
599
return fmt .Errorf ("unmarshal metadata: %w" , err )
616
600
}
617
- opts .Logger (log .LevelInfo , "#3 : 👀 Found devcontainer.json label metadata in image..." )
601
+ opts .Logger (log .LevelInfo , "#%d : 👀 Found devcontainer.json label metadata in image..." , stageNumber )
618
602
for _ , container := range devContainer {
619
603
if container .RemoteUser != "" {
620
- opts .Logger (log .LevelInfo , "#3 : 🧑 Updating the user to %q!" , container .RemoteUser )
604
+ opts .Logger (log .LevelInfo , "#%d : 🧑 Updating the user to %q!" , stageNumber , container .RemoteUser )
621
605
622
606
configFile .Config .User = container .RemoteUser
623
607
}
@@ -724,7 +708,7 @@ ENTRYPOINT [%q]`, exePath, exePath, exePath)
724
708
username = buildParams .User
725
709
}
726
710
if username == "" {
727
- opts .Logger (log .LevelWarn , "#3 : no user specified, using root" )
711
+ opts .Logger (log .LevelWarn , "#%d : no user specified, using root" , stageNumber )
728
712
}
729
713
730
714
userInfo , err := getUser (username )
@@ -1030,7 +1014,11 @@ func newColor(value ...color.Attribute) *color.Color {
1030
1014
return c
1031
1015
}
1032
1016
1033
- func findDevcontainerJSON (options options.Options ) (string , string , error ) {
1017
+ func findDevcontainerJSON (workspaceFolder string , options options.Options ) (string , string , error ) {
1018
+ if workspaceFolder == "" {
1019
+ workspaceFolder = options .WorkspaceFolder
1020
+ }
1021
+
1034
1022
// 0. Check if custom devcontainer directory or path is provided.
1035
1023
if options .DevcontainerDir != "" || options .DevcontainerJSONPath != "" {
1036
1024
devcontainerDir := options .DevcontainerDir
@@ -1040,7 +1028,7 @@ func findDevcontainerJSON(options options.Options) (string, string, error) {
1040
1028
1041
1029
// If `devcontainerDir` is not an absolute path, assume it is relative to the workspace folder.
1042
1030
if ! filepath .IsAbs (devcontainerDir ) {
1043
- devcontainerDir = filepath .Join (options . WorkspaceFolder , devcontainerDir )
1031
+ devcontainerDir = filepath .Join (workspaceFolder , devcontainerDir )
1044
1032
}
1045
1033
1046
1034
// An absolute location always takes a precedence.
@@ -1059,20 +1047,20 @@ func findDevcontainerJSON(options options.Options) (string, string, error) {
1059
1047
return devcontainerPath , devcontainerDir , nil
1060
1048
}
1061
1049
1062
- // 1. Check `options.WorkspaceFolder `/.devcontainer/devcontainer.json.
1063
- location := filepath .Join (options . WorkspaceFolder , ".devcontainer" , "devcontainer.json" )
1050
+ // 1. Check `workspaceFolder `/.devcontainer/devcontainer.json.
1051
+ location := filepath .Join (workspaceFolder , ".devcontainer" , "devcontainer.json" )
1064
1052
if _ , err := options .Filesystem .Stat (location ); err == nil {
1065
1053
return location , filepath .Dir (location ), nil
1066
1054
}
1067
1055
1068
- // 2. Check `options.WorkspaceFolder `/devcontainer.json.
1069
- location = filepath .Join (options . WorkspaceFolder , "devcontainer.json" )
1056
+ // 2. Check `workspaceFolder `/devcontainer.json.
1057
+ location = filepath .Join (workspaceFolder , "devcontainer.json" )
1070
1058
if _ , err := options .Filesystem .Stat (location ); err == nil {
1071
1059
return location , filepath .Dir (location ), nil
1072
1060
}
1073
1061
1074
- // 3. Check every folder: `options.WorkspaceFolder `/.devcontainer/<folder>/devcontainer.json.
1075
- devcontainerDir := filepath .Join (options . WorkspaceFolder , ".devcontainer" )
1062
+ // 3. Check every folder: `workspaceFolder `/.devcontainer/<folder>/devcontainer.json.
1063
+ devcontainerDir := filepath .Join (workspaceFolder , ".devcontainer" )
1076
1064
1077
1065
fileInfos , err := options .Filesystem .ReadDir (devcontainerDir )
1078
1066
if err != nil {
0 commit comments