Skip to content

Commit 032cd8e

Browse files
committed
Updates how interrupts are handled
1 parent 451ec3f commit 032cd8e

File tree

2 files changed

+40
-15
lines changed

2 files changed

+40
-15
lines changed

cli/azd/main.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@ import (
1616
"net/http"
1717
"os"
1818
"os/exec"
19+
"os/signal"
1920
"path/filepath"
2021
"runtime"
2122
"strconv"
2223
"strings"
24+
"syscall"
2325
"time"
2426

2527
azcorelog "github.com/Azure/azure-sdk-for-go/sdk/azcore/log"
@@ -39,6 +41,7 @@ import (
3941

4042
func main() {
4143
ctx := context.Background()
44+
ctx = withInterruptContext(ctx)
4245

4346
restoreColorMode := colorable.EnableColorsStdout(nil)
4447
defer restoreColorMode()
@@ -381,3 +384,24 @@ func startBackgroundUploadProcess() error {
381384
err = cmd.Start()
382385
return err
383386
}
387+
388+
// withInterruptContext creates a new context that is cancelled when the user
389+
// sends an interrupt signal (Ctrl+C) or a SIGTERM signal to the process.
390+
func withInterruptContext(ctx context.Context) context.Context {
391+
ctx, cancel := context.WithCancel(ctx)
392+
393+
signalChan := make(chan os.Signal, 1)
394+
signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM)
395+
go func() {
396+
<-signalChan
397+
log.Println("received interrupt signal, cancelling context")
398+
cancel()
399+
400+
time.Sleep(1 * time.Second)
401+
err := fmt.Errorf("Cancelled by user: %w", ctx.Err())
402+
fmt.Println(output.WithErrorFormat("ERROR: %s", err.Error()))
403+
os.Exit(1)
404+
}()
405+
406+
return ctx
407+
}

cli/azd/pkg/input/console.go

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,8 @@ type AskerConsole struct {
170170
// holds the last 2 bytes written by message or messageUX. This is used to detect when there is already an empty
171171
// line (\n\n)
172172
last2Byte [2]byte
173+
174+
spinnerStopChan chan struct{}
173175
}
174176

175177
type ConsoleOptions struct {
@@ -406,7 +408,20 @@ func (c *AskerConsole) ShowSpinner(ctx context.Context, title string, format Spi
406408
// While it is indeed safe to call Start regardless of whether the spinner is running,
407409
// calling Start may result in an additional line of output being written in non-tty scenarios
408410
_ = c.spinner.Start()
411+
412+
go func() {
413+
c.spinnerStopChan = make(chan struct{}, 1)
414+
415+
select {
416+
case <-ctx.Done():
417+
case <-c.spinnerStopChan:
418+
}
419+
420+
_ = c.spinner.Stop()
421+
return
422+
}()
409423
}
424+
410425
c.spinnerLineMu.Unlock()
411426
}
412427

@@ -468,7 +483,7 @@ func (c *AskerConsole) StopSpinner(ctx context.Context, lastMessage string, form
468483
lastMessage = c.getStopChar(format) + " " + lastMessage
469484
}
470485

471-
_ = c.spinner.Stop()
486+
c.spinnerStopChan <- struct{}{}
472487
if lastMessage != "" {
473488
// Avoid using StopMessage() as it may result in an extra Message line print in non-tty scenarios
474489
fmt.Fprintln(c.writer, lastMessage)
@@ -938,19 +953,6 @@ func watchTerminalResize(c *AskerConsole) {
938953
}
939954
}
940955

941-
func watchTerminalInterrupt(c *AskerConsole) {
942-
signalChan := make(chan os.Signal, 1)
943-
signal.Notify(signalChan, os.Interrupt)
944-
go func() {
945-
<-signalChan
946-
947-
// unhide the cursor if applicable
948-
_ = c.spinner.Stop()
949-
950-
os.Exit(1)
951-
}()
952-
}
953-
954956
// Writers that back the underlying console.
955957
type Writers struct {
956958
// The writer to write output to.
@@ -1015,7 +1017,6 @@ func NewConsole(
10151017
if isTerminal {
10161018
c.consoleWidth = atomic.NewInt32(consoleWidth())
10171019
watchTerminalResize(c)
1018-
watchTerminalInterrupt(c)
10191020
}
10201021

10211022
return c

0 commit comments

Comments
 (0)