@@ -10,13 +10,20 @@ import (
1010 composecli "github.com/compose-spec/compose-go/v2/cli"
1111 "github.com/docker/compose/v2/pkg/progress"
1212 "github.com/psviderski/uncloud/internal/cli"
13+ "github.com/psviderski/uncloud/internal/cli/logs"
1314 "github.com/psviderski/uncloud/internal/cli/tui"
15+ "github.com/psviderski/uncloud/pkg/api"
1416 "github.com/psviderski/uncloud/pkg/client"
1517 "github.com/psviderski/uncloud/pkg/client/compose"
1618 "github.com/psviderski/uncloud/pkg/client/deploy"
19+ "github.com/psviderski/uncloud/pkg/client/deploy/operation"
1720 "github.com/spf13/cobra"
1821)
1922
23+ // failedContainerLogsTail is the number of recent log lines to print from a failed container to give the user immediate
24+ // context without requiring a follow-up 'uc logs' invocation.
25+ const failedContainerLogsTail = 10
26+
2027type deployOptions struct {
2128 cli.BuildServicesOptions
2229
@@ -223,10 +230,48 @@ func runDeploy(ctx context.Context, uncli *cli.CLI, opts deployOptions) error {
223230 if deployTarget != "" {
224231 title += " to " + tui .NameStyle .Render (deployTarget )
225232 }
226- return progress .RunWithTitle (ctx , func (ctx context.Context ) error {
233+ err = progress .RunWithTitle (ctx , func (ctx context.Context ) error {
227234 if err := plan .Execute (ctx , clusterClient ); err != nil {
228235 return fmt .Errorf ("deploy services: %w" , err )
229236 }
230237 return nil
231238 }, uncli .ProgressOut (), title )
239+ if err != nil {
240+ fmt .Println ()
241+ if hookErr , ok := errors.AsType [* operation.PreDeployHookError ](err ); ok {
242+ printPreDeployHookLogs (ctx , clusterClient , hookErr )
243+ fmt .Println ()
244+ }
245+ return err
246+ }
247+ return nil
248+ }
249+
250+ // printPreDeployHookLogs fetches the last log lines from the failed pre-deploy hook container and prints them using
251+ // the standard log formatter.
252+ func printPreDeployHookLogs (ctx context.Context , cli * client.Client , hookErr * operation.PreDeployHookError ) {
253+ _ , ch , err := cli .ServiceLogs (ctx , hookErr .ServiceName , api.ServiceLogsOptions {
254+ Containers : []string {hookErr .ContainerID },
255+ Machines : []string {hookErr .MachineName },
256+ Tail : failedContainerLogsTail ,
257+ })
258+ if err != nil {
259+ fmt .Fprintf (os .Stderr , "Failed to fetch pre-deploy hook logs: %v\n " , err )
260+ fmt .Fprintf (os .Stderr , "You can try manually with: uc logs %s\n " , hookErr .ServiceName )
261+ return
262+ }
263+
264+ header := fmt .Sprintf ("Last %d log lines from failed pre-deploy hook:" , failedContainerLogsTail )
265+ fmt .Println (tui .BoldRed .Render (header ))
266+
267+ logsEmpty := true
268+ formatter := logs .NewFormatter ([]string {hookErr .MachineName }, []string {hookErr .ServiceName }, false )
269+ for entry := range ch {
270+ logsEmpty = false
271+ formatter .PrintEntry (entry )
272+ }
273+
274+ if logsEmpty {
275+ fmt .Println ("<no logs available>" )
276+ }
232277}
0 commit comments