Skip to content

Commit f4d7cd6

Browse files
fix: show git repository cloning progress
1 parent 5b44d6a commit f4d7cd6

File tree

15 files changed

+141
-68
lines changed

15 files changed

+141
-68
lines changed

cmd/agent/container/setup.go

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -174,16 +174,23 @@ func (cmd *SetupContainerCmd) Run(ctx context.Context) error {
174174
}
175175

176176
if b, err := workspaceInfo.PullFromInsideContainer.Bool(); err == nil && b {
177-
if err := agent.CloneRepositoryForWorkspace(ctx,
178-
&workspaceInfo.Source,
179-
&workspaceInfo.Agent,
180-
setupInfo.SubstitutionContext.ContainerWorkspaceFolder,
181-
"",
182-
workspaceInfo.CLIOptions,
183-
true,
184-
logger,
185-
); err != nil {
186-
return err
177+
// check if workspace folder exists and is a git repository.
178+
// skip cloning if it does
179+
_, err := os.Stat(filepath.Join(setupInfo.SubstitutionContext.ContainerWorkspaceFolder, ".git"))
180+
if err == nil && !workspaceInfo.CLIOptions.Recreate {
181+
logger.Debugf("Workspace repository already checked out %s, skipping clone", setupInfo.SubstitutionContext.ContainerWorkspaceFolder)
182+
} else {
183+
if err := agent.CloneRepositoryForWorkspace(ctx,
184+
&workspaceInfo.Source,
185+
&workspaceInfo.Agent,
186+
setupInfo.SubstitutionContext.ContainerWorkspaceFolder,
187+
"",
188+
workspaceInfo.CLIOptions,
189+
true,
190+
logger,
191+
); err != nil {
192+
return err
193+
}
187194
}
188195
}
189196

cmd/helper/get_workspace_config.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,10 @@ func (cmd *GetWorkspaceConfigCommand) Run(ctx context.Context, devPodConfig *con
6767
if cmd.GlobalFlags.Debug {
6868
level = logrus.DebugLevel
6969
}
70-
logger := log.NewStdoutLogger(os.Stdin, nil, nil, level)
70+
var logger log.Logger = log.NewStdoutLogger(os.Stdin, os.Stdout, os.Stderr, level)
71+
if os.Getenv("DEVPOD_UI") == "true" {
72+
logger = log.Discard
73+
}
7174
logger.Debugf("Resolving devcontainer config for source: %s", rawSource)
7275

7376
ctx, cancel := context.WithTimeout(context.Background(), cmd.timeout)
@@ -102,7 +105,7 @@ func (cmd *GetWorkspaceConfigCommand) Run(ctx context.Context, devPodConfig *con
102105
if err != nil {
103106
return err
104107
}
105-
log.Default.Done(string(out))
108+
fmt.Println(string(out))
106109
}
107110
defer close(done)
108111

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ require (
3434
github.com/loft-sh/analytics-client v0.0.0-20240219162240-2f4c64b2494e
3535
github.com/loft-sh/api/v4 v4.3.0-devpod.alpha.25
3636
github.com/loft-sh/apiserver v0.0.0-20250206205835-422f1d472459
37-
github.com/loft-sh/log v0.0.0-20240219160058-26d83ffb46ac
37+
github.com/loft-sh/log v0.0.0-20250409101748-50124f882858
3838
github.com/loft-sh/programming-language-detection v0.0.5
3939
github.com/loft-sh/ssh v0.0.5
4040
github.com/mattn/go-isatty v0.0.20

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,8 @@ github.com/loft-sh/apiserver v0.0.0-20250206205835-422f1d472459 h1:6SrgBtT1S9ANs
432432
github.com/loft-sh/apiserver v0.0.0-20250206205835-422f1d472459/go.mod h1:rung3jsKjaVAtykQN0vWmFHhx2A/umpRyAae8BJVSeE=
433433
github.com/loft-sh/log v0.0.0-20240219160058-26d83ffb46ac h1:Gz/7Lb7WgdgIv+KJz87ORA1zvQW52tUqKPGyunlp4dQ=
434434
github.com/loft-sh/log v0.0.0-20240219160058-26d83ffb46ac/go.mod h1:YImeRjXH34Yf5E79T7UHBQpDZl9fIaaFRgyZ/bkY+UQ=
435+
github.com/loft-sh/log v0.0.0-20250409101748-50124f882858 h1:MKlp3CvzXqd+a2plptpE/bKcVMb8t/7BrEuy6Yqmnaw=
436+
github.com/loft-sh/log v0.0.0-20250409101748-50124f882858/go.mod h1:YImeRjXH34Yf5E79T7UHBQpDZl9fIaaFRgyZ/bkY+UQ=
435437
github.com/loft-sh/programming-language-detection v0.0.5 h1:XiWlxtrf4t6Z7SQiob0JMKaCeMHCP3kWhB80wLt+EMY=
436438
github.com/loft-sh/programming-language-detection v0.0.5/go.mod h1:QGPQGKr9q1+rQS4OyisS5CPGY1a76SdNaZuk9oy+2cE=
437439
github.com/loft-sh/ssh v0.0.5 h1:CmLfBrbekAZmYhpS+urhqmUZW1XU9kUo2bi4lJiUFH8=

main.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package main
22

3-
import (
4-
"github.com/loft-sh/devpod/cmd"
5-
)
3+
import "github.com/loft-sh/devpod/cmd"
64

75
func main() {
86
cmd.Execute()

pkg/agent/tunnelserver/logger.go

Lines changed: 62 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"io"
77
"os"
8+
"time"
89

910
"github.com/go-logr/logr"
1011
"github.com/loft-sh/devpod/pkg/agent/tunnel"
@@ -20,112 +21,136 @@ func NewTunnelLogger(ctx context.Context, client tunnel.TunnelClient, debug bool
2021
level = logrus.DebugLevel
2122
}
2223

23-
return &tunnelLogger{ctx: ctx, client: client, level: level}
24+
logger := &tunnelLogger{
25+
ctx: ctx,
26+
client: client,
27+
level: level,
28+
logChan: make(chan *tunnel.LogMessage, 1000), // Buffer size of 1000 messages
29+
}
30+
31+
go logger.worker()
32+
33+
return logger
2434
}
2535

2636
type tunnelLogger struct {
27-
ctx context.Context
28-
level logrus.Level
29-
client tunnel.TunnelClient
37+
ctx context.Context
38+
level logrus.Level
39+
client tunnel.TunnelClient
40+
logChan chan *tunnel.LogMessage
41+
}
42+
43+
func (s *tunnelLogger) worker() {
44+
for {
45+
select {
46+
case msg := <-s.logChan:
47+
ctx, cancel := context.WithTimeout(s.ctx, 5*time.Second)
48+
_, _ = s.client.Log(ctx, msg)
49+
// ignore error since we can't use the logger itself
50+
cancel()
51+
case <-s.ctx.Done():
52+
return
53+
}
54+
}
3055
}
3156

3257
func (s *tunnelLogger) Debug(args ...interface{}) {
3358
if s.level < logrus.DebugLevel {
3459
return
3560
}
3661

37-
_, _ = s.client.Log(s.ctx, &tunnel.LogMessage{
62+
s.logChan <- &tunnel.LogMessage{
3863
LogLevel: tunnel.LogLevel_DEBUG,
3964
Message: fmt.Sprintln(args...),
40-
})
65+
}
4166
}
4267

4368
func (s *tunnelLogger) Debugf(format string, args ...interface{}) {
4469
if s.level < logrus.DebugLevel {
4570
return
4671
}
4772

48-
_, _ = s.client.Log(s.ctx, &tunnel.LogMessage{
73+
s.logChan <- &tunnel.LogMessage{
4974
LogLevel: tunnel.LogLevel_DEBUG,
5075
Message: fmt.Sprintf(format, args...) + "\n",
51-
})
76+
}
5277
}
5378

5479
func (s *tunnelLogger) Info(args ...interface{}) {
5580
if s.level < logrus.InfoLevel {
5681
return
5782
}
5883

59-
_, _ = s.client.Log(s.ctx, &tunnel.LogMessage{
84+
s.logChan <- &tunnel.LogMessage{
6085
LogLevel: tunnel.LogLevel_INFO,
6186
Message: fmt.Sprintln(args...),
62-
})
87+
}
6388
}
6489

6590
func (s *tunnelLogger) Infof(format string, args ...interface{}) {
6691
if s.level < logrus.InfoLevel {
6792
return
6893
}
6994

70-
_, _ = s.client.Log(s.ctx, &tunnel.LogMessage{
95+
s.logChan <- &tunnel.LogMessage{
7196
LogLevel: tunnel.LogLevel_INFO,
7297
Message: fmt.Sprintf(format, args...) + "\n",
73-
})
98+
}
7499
}
75100

76101
func (s *tunnelLogger) Warn(args ...interface{}) {
77102
if s.level < logrus.WarnLevel {
78103
return
79104
}
80105

81-
_, _ = s.client.Log(s.ctx, &tunnel.LogMessage{
106+
s.logChan <- &tunnel.LogMessage{
82107
LogLevel: tunnel.LogLevel_WARNING,
83108
Message: fmt.Sprintln(args...),
84-
})
109+
}
85110
}
86111

87112
func (s *tunnelLogger) Warnf(format string, args ...interface{}) {
88113
if s.level < logrus.WarnLevel {
89114
return
90115
}
91116

92-
_, _ = s.client.Log(s.ctx, &tunnel.LogMessage{
117+
s.logChan <- &tunnel.LogMessage{
93118
LogLevel: tunnel.LogLevel_WARNING,
94119
Message: fmt.Sprintf(format, args...) + "\n",
95-
})
120+
}
96121
}
97122

98123
func (s *tunnelLogger) Error(args ...interface{}) {
99124
if s.level < logrus.ErrorLevel {
100125
return
101126
}
102127

103-
_, _ = s.client.Log(s.ctx, &tunnel.LogMessage{
128+
s.logChan <- &tunnel.LogMessage{
104129
LogLevel: tunnel.LogLevel_ERROR,
105130
Message: fmt.Sprintln(args...),
106-
})
131+
}
107132
}
108133

109134
func (s *tunnelLogger) Errorf(format string, args ...interface{}) {
110135
if s.level < logrus.ErrorLevel {
111136
return
112137
}
113138

114-
_, _ = s.client.Log(s.ctx, &tunnel.LogMessage{
139+
s.logChan <- &tunnel.LogMessage{
115140
LogLevel: tunnel.LogLevel_ERROR,
116141
Message: fmt.Sprintf(format, args...) + "\n",
117-
})
142+
}
118143
}
119144

120145
func (s *tunnelLogger) Fatal(args ...interface{}) {
121146
if s.level < logrus.FatalLevel {
122147
return
123148
}
124149

125-
_, _ = s.client.Log(s.ctx, &tunnel.LogMessage{
150+
s.logChan <- &tunnel.LogMessage{
126151
LogLevel: tunnel.LogLevel_ERROR,
127152
Message: fmt.Sprintln(args...),
128-
})
153+
}
129154

130155
os.Exit(1)
131156
}
@@ -135,10 +160,10 @@ func (s *tunnelLogger) Fatalf(format string, args ...interface{}) {
135160
return
136161
}
137162

138-
_, _ = s.client.Log(s.ctx, &tunnel.LogMessage{
163+
s.logChan <- &tunnel.LogMessage{
139164
LogLevel: tunnel.LogLevel_ERROR,
140165
Message: fmt.Sprintf(format, args...) + "\n",
141-
})
166+
}
142167

143168
os.Exit(1)
144169
}
@@ -148,21 +173,21 @@ func (s *tunnelLogger) Done(args ...interface{}) {
148173
return
149174
}
150175

151-
_, _ = s.client.Log(s.ctx, &tunnel.LogMessage{
176+
s.logChan <- &tunnel.LogMessage{
152177
LogLevel: tunnel.LogLevel_DONE,
153178
Message: fmt.Sprintln(args...),
154-
})
179+
}
155180
}
156181

157182
func (s *tunnelLogger) Donef(format string, args ...interface{}) {
158183
if s.level < logrus.InfoLevel {
159184
return
160185
}
161186

162-
_, _ = s.client.Log(s.ctx, &tunnel.LogMessage{
187+
s.logChan <- &tunnel.LogMessage{
163188
LogLevel: tunnel.LogLevel_DONE,
164189
Message: fmt.Sprintf(format, args...) + "\n",
165-
})
190+
}
166191
}
167192

168193
func (s *tunnelLogger) Print(level logrus.Level, args ...interface{}) {
@@ -240,6 +265,15 @@ func (s *tunnelLogger) WriteString(level logrus.Level, message string) {
240265
s.Print(level, message)
241266
}
242267

268+
func (s *tunnelLogger) WriteLevel(level logrus.Level, message []byte) (int, error) {
269+
if s.level < level {
270+
return 0, nil
271+
}
272+
273+
s.Print(level, string(message))
274+
return len(message), nil
275+
}
276+
243277
func (s *tunnelLogger) Question(params *survey.QuestionOptions) (string, error) {
244278
return "", fmt.Errorf("not supported")
245279
}

pkg/devcontainer/setup.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func (r *runner) setupContainer(
8080
Agent: r.WorkspaceConfig.Agent,
8181
ContentFolder: r.WorkspaceConfig.ContentFolder,
8282
}
83-
if crane.ShouldUse(&r.WorkspaceConfig.CLIOptions) {
83+
if crane.ShouldUse(&r.WorkspaceConfig.CLIOptions) && r.WorkspaceConfig.Workspace.Source.GitRepository != "" {
8484
workspaceConfig.PullFromInsideContainer = "true"
8585
}
8686
// compress container workspace info

pkg/git/clone.go

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
package git
22

33
import (
4-
"bytes"
54
"context"
65
"fmt"
7-
"io"
86

97
"github.com/loft-sh/log"
108
"github.com/sirupsen/logrus"
@@ -105,6 +103,15 @@ func (c *cloner) initialArgs() []string {
105103
return []string{"clone"}
106104
}
107105

106+
type progressWriter struct {
107+
level logrus.Level
108+
log log.Logger
109+
}
110+
111+
func (w *progressWriter) Write(p []byte) (n int, err error) {
112+
return w.log.WriteLevel(w.level, p)
113+
}
114+
108115
func (c *cloner) Clone(ctx context.Context, repository string, targetDir string, extraArgs, extraEnv []string, log log.Logger) error {
109116
args := c.initialArgs()
110117
args = append(args, extraArgs...)
@@ -114,28 +121,12 @@ func (c *cloner) Clone(ctx context.Context, repository string, targetDir string,
114121
}
115122

116123
func run(ctx context.Context, args []string, extraEnv []string, log log.Logger) error {
117-
var buf bytes.Buffer
118-
119124
args = append(args, "--progress")
120125

126+
w := &progressWriter{log: log, level: logrus.InfoLevel}
121127
gitCommand := CommandContext(ctx, extraEnv, args...)
122-
gitCommand.Stdout = &buf
123-
gitCommand.Stderr = &buf
124-
125-
// git always prints progress output to stderr,
126-
// we need to check the exit code to decide where the logs should go
127-
if err := gitCommand.Run(); err != nil {
128-
// report as error
129-
if _, err2 := io.Copy(log.Writer(logrus.ErrorLevel, false), &buf); err2 != nil {
130-
return err2
131-
}
132-
return err
133-
}
134-
135-
// report as debug
136-
if _, err := io.Copy(log.Writer(logrus.DebugLevel, false), &buf); err != nil {
137-
return err
138-
}
128+
gitCommand.Stdout = w
129+
gitCommand.Stderr = w
139130

140-
return nil
131+
return gitCommand.Run()
141132
}

pkg/log/log.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,14 @@ func (c *CombinedLogger) WriteString(level logrus.Level, message string) {
147147
})
148148
}
149149

150+
func (c *CombinedLogger) WriteLevel(level logrus.Level, message []byte) (int, error) {
151+
c.log(level, func(logger logLib.Logger) {
152+
_, _ = logger.WriteLevel(level, message)
153+
})
154+
155+
return len(message), nil
156+
}
157+
150158
func (c *CombinedLogger) Question(params *survey.QuestionOptions) (string, error) {
151159
return "", errors.New("questions in combined logger not supported")
152160
}

vendor/github.com/loft-sh/log/.devcontainer.json

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)