Skip to content

Commit 72a8c3f

Browse files
committed
erge branch 'master' of https://github.com/jr-k/d4s
2 parents 9d34a4e + 1a922cf commit 72a8c3f

3 files changed

Lines changed: 156 additions & 40 deletions

File tree

internal/dao/compose/compose.go

Lines changed: 80 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -184,34 +184,6 @@ func (m *Manager) Stop(projectName string) error {
184184
return nil
185185
}
186186

187-
func (m *Manager) Restart(projectName string) error {
188-
// Find all containers with this project name
189-
args := filters.NewArgs()
190-
args.Add("label", fmt.Sprintf("com.docker.compose.project=%s", projectName))
191-
192-
containers, err := m.cli.ContainerList(m.ctx, container.ListOptions{Filters: args, All: true})
193-
if err != nil {
194-
return err
195-
}
196-
197-
if len(containers) == 0 {
198-
return fmt.Errorf("no containers found for project %s", projectName)
199-
}
200-
201-
timeout := 10
202-
var errs []string
203-
for _, c := range containers {
204-
if err := m.cli.ContainerRestart(m.ctx, c.ID, container.StopOptions{Timeout: &timeout}); err != nil {
205-
errs = append(errs, fmt.Sprintf("%s: %v", c.Names[0], err))
206-
}
207-
}
208-
209-
if len(errs) > 0 {
210-
return fmt.Errorf("errors restarting containers: %s", strings.Join(errs, "; "))
211-
}
212-
return nil
213-
}
214-
215187
func (m *Manager) GetConfig(projectName string) (string, error) {
216188
// Find one container to get config path
217189
args := filters.NewArgs()
@@ -253,6 +225,34 @@ func (m *Manager) GetConfig(projectName string) (string, error) {
253225
return sb.String(), nil
254226
}
255227

228+
func (m *Manager) getConfigPaths(projectName string) ([]string, error) {
229+
args := filters.NewArgs()
230+
args.Add("label", fmt.Sprintf("com.docker.compose.project=%s", projectName))
231+
232+
containers, err := m.cli.ContainerList(m.ctx, container.ListOptions{Filters: args, All: true, Limit: 1})
233+
if err != nil {
234+
return nil, err
235+
}
236+
237+
if len(containers) == 0 {
238+
return nil, fmt.Errorf("project '%s' not found or has no active containers to read configuration from", projectName)
239+
}
240+
241+
configFiles := containers[0].Labels["com.docker.compose.project.config_files"]
242+
if configFiles == "" {
243+
return nil, fmt.Errorf("no config files label found for project '%s'", projectName)
244+
}
245+
246+
var paths []string
247+
for f := range strings.SplitSeq(configFiles, ",") {
248+
path := strings.TrimSpace(f)
249+
if path != "" {
250+
paths = append(paths, path)
251+
}
252+
}
253+
return paths, nil
254+
}
255+
256256
func (m *Manager) Logs(projectName string, since string, tail string, timestamps bool) (io.ReadCloser, error) {
257257
args := []string{"compose", "-p", projectName, "logs", "-f"}
258258
if tail != "" && tail != "all" {
@@ -282,6 +282,58 @@ func (m *Manager) Logs(projectName string, since string, tail string, timestamps
282282
return &cmdReadCloser{pipe: stdout, cmd: cmd}, nil
283283
}
284284

285+
func (m *Manager) Down(projectName string) error {
286+
var args []string
287+
args = append(args, "compose", "-p", projectName, "down")
288+
289+
cmd := exec.Command("docker", args...)
290+
output, err := cmd.CombinedOutput()
291+
if err != nil {
292+
return fmt.Errorf("error running docker compose down: %v, output: %s", err, string(output))
293+
}
294+
return nil
295+
}
296+
297+
func (m *Manager) Up(projectName string) error {
298+
paths, err := m.getConfigPaths(projectName)
299+
if err != nil {
300+
return fmt.Errorf("failed to up project: %v", err)
301+
}
302+
303+
args := []string{"compose", "-p", projectName}
304+
for _, path := range paths {
305+
args = append(args, "-f", path)
306+
}
307+
args = append(args, "up", "-d", "--force-recreate")
308+
309+
cmd := exec.Command("docker", args...)
310+
output, err := cmd.CombinedOutput()
311+
if err != nil {
312+
return fmt.Errorf("error running docker compose up: %v\nOutput: %s", err, string(output))
313+
}
314+
return nil
315+
}
316+
317+
func (m *Manager) Build(projectName string) error {
318+
paths, err := m.getConfigPaths(projectName)
319+
if err != nil {
320+
return fmt.Errorf("failed to build project: %v", err)
321+
}
322+
323+
args := []string{"compose", "-p", projectName}
324+
for _, path := range paths {
325+
args = append(args, "-f", path)
326+
}
327+
args = append(args, "up", "-d", "--build")
328+
329+
cmd := exec.Command("docker", args...)
330+
output, err := cmd.CombinedOutput()
331+
if err != nil {
332+
return fmt.Errorf("error running docker compose up --build: %v\nOutput: %s", err, string(output))
333+
}
334+
return nil
335+
}
336+
285337
type cmdReadCloser struct {
286338
pipe io.ReadCloser
287339
cmd *exec.Cmd
@@ -297,4 +349,3 @@ func (c *cmdReadCloser) Close() error {
297349
}
298350
return c.pipe.Close()
299351
}
300-

internal/dao/docker.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -551,8 +551,16 @@ func (d *DockerClient) StopComposeProject(projectName string) error {
551551
return d.Compose.Stop(projectName)
552552
}
553553

554-
func (d *DockerClient) RestartComposeProject(projectName string) error {
555-
return d.Compose.Restart(projectName)
554+
func (d *DockerClient) UpComposeProject(projectName string) error {
555+
return d.Compose.Up(projectName)
556+
}
557+
558+
func (d *DockerClient) BuildComposeProject(projectName string) error {
559+
return d.Compose.Build(projectName)
560+
}
561+
562+
func (d *DockerClient) DownComposeProject(projectName string) error {
563+
return d.Compose.Down(projectName)
556564
}
557565

558566
func (d *DockerClient) GetComposeConfig(projectName string) (string, error) {

internal/ui/views/compose/compose.go

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/jr-k/d4s/internal/ui/common"
1414
"github.com/jr-k/d4s/internal/ui/components/inspect"
1515
"github.com/jr-k/d4s/internal/ui/components/view"
16+
"github.com/jr-k/d4s/internal/ui/dialogs"
1617
"github.com/jr-k/d4s/internal/ui/styles"
1718
)
1819

@@ -82,6 +83,7 @@ func GetShortcuts() []string {
8283
common.FormatSCHeader("d", "Describe"),
8384
common.FormatSCHeader("e", "Edit"),
8485
common.FormatSCHeader("r", "(Re)Start"),
86+
common.FormatSCHeader("b", "Build"),
8587
common.FormatSCHeader("ctrl-d", "Delete"),
8688
common.FormatSCHeader("ctrl-k", "Stop"),
8789
}
@@ -93,6 +95,10 @@ func InputHandler(v *view.ResourceView, event *tcell.EventKey) *tcell.EventKey {
9395
StopAction(app, v)
9496
return nil
9597
}
98+
if event.Key() == tcell.KeyCtrlD {
99+
DeleteAction(app, v)
100+
return nil
101+
}
96102
switch event.Rune() {
97103
case 'l':
98104
Logs(app, v)
@@ -104,7 +110,10 @@ func InputHandler(v *view.ResourceView, event *tcell.EventKey) *tcell.EventKey {
104110
EditAction(app, v)
105111
return nil
106112
case 'r':
107-
RestartAction(app, v)
113+
UpAction(app, v)
114+
return nil
115+
case 'b':
116+
BuildAction(app, v)
108117
return nil
109118
}
110119

@@ -152,12 +161,6 @@ func NavigateToContainers(app common.AppController, v *view.ResourceView) {
152161
}
153162
}
154163

155-
func RestartAction(app common.AppController, v *view.ResourceView) {
156-
app.PerformAction(func(id string) error {
157-
return app.GetDocker().RestartComposeProject(id)
158-
}, "restarting", styles.ColorStatusOrange)
159-
}
160-
161164
func StopAction(app common.AppController, v *view.ResourceView) {
162165
app.PerformAction(func(id string) error {
163166
return app.GetDocker().StopComposeProject(id)
@@ -226,10 +229,64 @@ func EditAction(app common.AppController, v *view.ResourceView) {
226229
}
227230
}
228231

229-
func Restart(app common.AppController, id string) error {
230-
return app.GetDocker().RestartComposeProject(id)
232+
func UpAction(app common.AppController, v *view.ResourceView) {
233+
app.PerformAction(func(id string) error {
234+
return app.GetDocker().UpComposeProject(id)
235+
}, "restarting", styles.ColorStatusYellow)
236+
}
237+
238+
func BuildAction(app common.AppController, v *view.ResourceView) {
239+
app.PerformAction(func(id string) error {
240+
return app.GetDocker().BuildComposeProject(id)
241+
}, "building", styles.ColorStatusYellow)
242+
}
243+
244+
func DeleteAction(app common.AppController, v *view.ResourceView) {
245+
ids, err := v.GetSelectedIDs()
246+
if err != nil || len(ids) == 0 {
247+
return
248+
}
249+
250+
label := ids[0]
251+
if len(ids) > 1 {
252+
label = fmt.Sprintf("%d items", len(ids))
253+
}
254+
255+
// Stop background refresh to prevent UI flickering during confirmation/deletion
256+
app.StopAutoRefresh()
257+
app.SetPaused(true)
258+
259+
dialogs.ShowConfirmation(app, "DELETE", label, func(force bool) {
260+
// Ensure we always resume UI refresh cycles when the action completes or cancels
261+
defer func() {
262+
app.SetPaused(false)
263+
app.StartAutoRefresh()
264+
265+
// Force UI sync to clean up any leftover artifacts from the dialog
266+
if app.GetScreen() != nil {
267+
app.GetScreen().Sync()
268+
}
269+
}()
270+
271+
// Define the multi-item deletion task
272+
batchDeleteAction := func(_ string) error {
273+
for _, id := range ids {
274+
if err := Remove(id, force, app); err != nil {
275+
return err // Halts and displays error if one fails
276+
}
277+
}
278+
return nil
279+
}
280+
281+
// Perform the action with the consistent styling
282+
app.PerformAction(batchDeleteAction, "deleting", styles.ColorStatusRed)
283+
})
231284
}
232285

233286
func Stop(app common.AppController, id string) error {
234287
return app.GetDocker().StopComposeProject(id)
235288
}
289+
290+
func Remove(id string, force bool, app common.AppController) error {
291+
return app.GetDocker().DownComposeProject(id)
292+
}

0 commit comments

Comments
 (0)