From 5b5c80364f252c31fb17fc297676baf1f19ab392 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Jul 2026 12:03:04 +0000 Subject: [PATCH 1/8] Initial plan From 40e400c823d0a522f33fcd5a13aaf9d2cc202b39 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Jul 2026 12:19:37 +0000 Subject: [PATCH 2/8] Honor NO_COLOR via colorprofile writers Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/cli/compile_schedule_calendar.go | 2 +- pkg/console/banner.go | 6 +-- pkg/console/colorprofile_writer.go | 18 +++++++++ pkg/console/colorprofile_writer_test.go | 23 +++++++++++ pkg/console/colorprofile_writer_wasm.go | 16 ++++++++ pkg/console/confirm.go | 9 +++-- pkg/console/console.go | 14 +++---- pkg/console/list.go | 10 ++--- pkg/console/spinner.go | 16 ++++---- pkg/console/terminal.go | 16 ++++---- pkg/console/verbose.go | 3 +- pkg/logger/logger.go | 51 +++++++++++++++++-------- pkg/logger/logger_test.go | 14 +++++++ pkg/styles/theme.go | 3 ++ 14 files changed, 147 insertions(+), 54 deletions(-) create mode 100644 pkg/console/colorprofile_writer.go create mode 100644 pkg/console/colorprofile_writer_test.go create mode 100644 pkg/console/colorprofile_writer_wasm.go diff --git a/pkg/cli/compile_schedule_calendar.go b/pkg/cli/compile_schedule_calendar.go index a31d0c76c04..49626c74fa2 100644 --- a/pkg/cli/compile_schedule_calendar.go +++ b/pkg/cli/compile_schedule_calendar.go @@ -200,7 +200,7 @@ func intensityChar(count int) string { func intensityStyle(count int, isTerminal bool) lipgloss.Style { if !isTerminal { // Keep glyph rendering unchanged while preventing ANSI escapes in piped output. - return lipgloss.Style{} + return lipgloss.NewStyle() } switch { diff --git a/pkg/console/banner.go b/pkg/console/banner.go index 0dc89a386ea..07a48e811d2 100644 --- a/pkg/console/banner.go +++ b/pkg/console/banner.go @@ -5,7 +5,6 @@ package console import ( _ "embed" "fmt" - "os" "strings" lipgloss "charm.land/lipgloss/v2" @@ -31,6 +30,7 @@ func FormatBanner() string { // PrintBanner prints the ASCII logo to stderr with purple GitHub color theme. // This is used by the --banner flag to display the logo at the start of command execution. func PrintBanner() { - fmt.Fprintln(os.Stderr, FormatBanner()) - fmt.Fprintln(os.Stderr) + out := stderrWriter() + fmt.Fprintln(out, FormatBanner()) + fmt.Fprintln(out) } diff --git a/pkg/console/colorprofile_writer.go b/pkg/console/colorprofile_writer.go new file mode 100644 index 00000000000..8cfbbb2b2bc --- /dev/null +++ b/pkg/console/colorprofile_writer.go @@ -0,0 +1,18 @@ +//go:build !js && !wasm + +package console + +import ( + "io" + "os" + + "github.com/charmbracelet/colorprofile" +) + +func newColorProfileWriter(w io.Writer, environ []string) io.Writer { + return colorprofile.NewWriter(w, environ) +} + +func stderrWriter() io.Writer { + return newColorProfileWriter(os.Stderr, os.Environ()) +} diff --git a/pkg/console/colorprofile_writer_test.go b/pkg/console/colorprofile_writer_test.go new file mode 100644 index 00000000000..3911ec3602f --- /dev/null +++ b/pkg/console/colorprofile_writer_test.go @@ -0,0 +1,23 @@ +//go:build !integration && !js && !wasm + +package console + +import ( + "bytes" + "fmt" + "strings" + "testing" +) + +func TestColorProfileWriterStripsANSIWithNoColor(t *testing.T) { + var buf bytes.Buffer + w := newColorProfileWriter(&buf, []string{"NO_COLOR=1", "TERM=xterm-256color"}) + + if _, err := fmt.Fprint(w, "\x1b[38;2;255;0;0mhello\x1b[0m"); err != nil { + t.Fatalf("write failed: %v", err) + } + + if strings.Contains(buf.String(), "\x1b[") { + t.Fatalf("expected ANSI-free output with NO_COLOR, got %q", buf.String()) + } +} diff --git a/pkg/console/colorprofile_writer_wasm.go b/pkg/console/colorprofile_writer_wasm.go new file mode 100644 index 00000000000..136e3e7c139 --- /dev/null +++ b/pkg/console/colorprofile_writer_wasm.go @@ -0,0 +1,16 @@ +//go:build js || wasm + +package console + +import ( + "io" + "os" +) + +func newColorProfileWriter(w io.Writer, _ []string) io.Writer { + return w +} + +func stderrWriter() io.Writer { + return os.Stderr +} diff --git a/pkg/console/confirm.go b/pkg/console/confirm.go index a24d4106f8b..ccb25ec37b6 100644 --- a/pkg/console/confirm.go +++ b/pkg/console/confirm.go @@ -51,11 +51,12 @@ func ConfirmAction(title, affirmative, negative string) (bool, error) { // showTextConfirm displays a non-interactive confirmation prompt for non-TTY environments func showTextConfirm(title, affirmative, negative string, reader io.Reader) (bool, error) { confirmLog.Printf("Showing text confirm: title=%s", title) + out := stderrWriter() - fmt.Fprintf(os.Stderr, "\n%s\n\n", title) - fmt.Fprintf(os.Stderr, " 1) %s\n", affirmative) - fmt.Fprintf(os.Stderr, " 2) %s\n", negative) - fmt.Fprintf(os.Stderr, "\nEnter y/yes/1 to confirm, n/no/2 to cancel: ") + fmt.Fprintf(out, "\n%s\n\n", title) + fmt.Fprintf(out, " 1) %s\n", affirmative) + fmt.Fprintf(out, " 2) %s\n", negative) + fmt.Fprintf(out, "\nEnter y/yes/1 to confirm, n/no/2 to cancel: ") var input string _, err := fmt.Fscan(reader, &input) diff --git a/pkg/console/console.go b/pkg/console/console.go index cb5dfa156a5..40ef11db79d 100644 --- a/pkg/console/console.go +++ b/pkg/console/console.go @@ -5,7 +5,6 @@ package console import ( "errors" "fmt" - "os" "strconv" "strings" @@ -449,16 +448,17 @@ func RenderInfoSection(content string) []string { // RenderComposedSections composes and outputs a slice of sections to stderr func RenderComposedSections(sections []string) { + out := stderrWriter() if tty.IsStderrTerminal() { plan := lipgloss.JoinVertical(lipgloss.Left, sections...) - fmt.Fprintln(os.Stderr, "") - fmt.Fprintln(os.Stderr, plan) - fmt.Fprintln(os.Stderr, "") + fmt.Fprintln(out, "") + fmt.Fprintln(out, plan) + fmt.Fprintln(out, "") } else { - fmt.Fprintln(os.Stderr, "") + fmt.Fprintln(out, "") for _, section := range sections { - fmt.Fprintln(os.Stderr, section) + fmt.Fprintln(out, section) } - fmt.Fprintln(os.Stderr, "") + fmt.Fprintln(out, "") } } diff --git a/pkg/console/list.go b/pkg/console/list.go index b09498c5ea3..5b0b383c225 100644 --- a/pkg/console/list.go +++ b/pkg/console/list.go @@ -5,7 +5,6 @@ package console import ( "errors" "fmt" - "os" "charm.land/huh/v2" "github.com/github/gh-aw/pkg/logger" @@ -65,15 +64,16 @@ func ShowInteractiveList(title string, items []ListItem) (string, error) { // showTextList displays a non-interactive numbered list for non-TTY environments func showTextList(title string, items []ListItem) (string, error) { listLog.Printf("Showing text list: title=%s, items=%d", title, len(items)) + out := stderrWriter() - fmt.Fprintf(os.Stderr, "\n%s\n\n", title) + fmt.Fprintf(out, "\n%s\n\n", title) for i, item := range items { - fmt.Fprintf(os.Stderr, " %d) %s\n", i+1, item.title) + fmt.Fprintf(out, " %d) %s\n", i+1, item.title) if item.description != "" { - fmt.Fprintf(os.Stderr, " %s\n", item.description) + fmt.Fprintf(out, " %s\n", item.description) } } - fmt.Fprintf(os.Stderr, "\nSelect (1-%d): ", len(items)) + fmt.Fprintf(out, "\nSelect (1-%d): ", len(items)) var choice int _, err := fmt.Scanf("%d", &choice) diff --git a/pkg/console/spinner.go b/pkg/console/spinner.go index 0480c941a2c..fb1256ee752 100644 --- a/pkg/console/spinner.go +++ b/pkg/console/spinner.go @@ -40,7 +40,7 @@ package console import ( "fmt" - "os" + "io" "sync" "charm.land/bubbles/v2/spinner" @@ -60,7 +60,7 @@ type updateMessageMsg string type spinnerModel struct { spinner spinner.Model message string - output *os.File + output io.Writer } func (m spinnerModel) Init() tea.Cmd { return m.spinner.Tick } @@ -110,13 +110,13 @@ func NewSpinner(message string) *SpinnerWrapper { model := spinnerModel{ spinner: spinner.New(spinner.WithSpinner(spinner.MiniDot), spinner.WithStyle(styles.Info)), message: message, - output: os.Stderr, + output: stderrWriter(), } // tea.WithInput(nil) disables stdin reading so the spinner does not consume key // events that should be handled by subsequent interactive forms (e.g. huh.Select). // Ctrl+C is still handled via OS signal delivery (SIGINT), which bubbletea // processes independently of the input reader. - s.program = tea.NewProgram(model, tea.WithOutput(os.Stderr), tea.WithoutRenderer(), tea.WithInput(nil)) + s.program = tea.NewProgram(model, tea.WithOutput(stderrWriter()), tea.WithoutRenderer(), tea.WithInput(nil)) } return s } @@ -170,7 +170,7 @@ func (s *SpinnerWrapper) Stop() { spinnerLog.Print("Stopping spinner") s.program.Quit() s.wg.Wait() // Wait for the goroutine to complete - fmt.Fprintf(os.Stderr, "%s%s", ansiCarriageReturn, ansiClearLine) + fmt.Fprintf(stderrWriter(), "%s%s", ansiCarriageReturn, ansiClearLine) } } } @@ -189,14 +189,14 @@ func (s *SpinnerWrapper) StopWithMessage(msg string) { if wasRunning { s.program.Quit() s.wg.Wait() // Wait for the goroutine to complete - fmt.Fprintf(os.Stderr, "%s%s%s\n", ansiCarriageReturn, ansiClearLine, msg) + fmt.Fprintf(stderrWriter(), "%s%s%s\n", ansiCarriageReturn, ansiClearLine, msg) } else { // Still print the message even if spinner wasn't running - fmt.Fprintf(os.Stderr, "%s\n", msg) + fmt.Fprintf(stderrWriter(), "%s\n", msg) } } else if msg != "" { // If spinner is disabled, still print the message for user feedback - fmt.Fprintf(os.Stderr, "%s\n", msg) + fmt.Fprintf(stderrWriter(), "%s\n", msg) } } diff --git a/pkg/console/terminal.go b/pkg/console/terminal.go index 1cee1d06f73..bff9002efcf 100644 --- a/pkg/console/terminal.go +++ b/pkg/console/terminal.go @@ -2,7 +2,6 @@ package console import ( "fmt" - "os" "github.com/github/gh-aw/pkg/styles" "github.com/github/gh-aw/pkg/tty" @@ -24,7 +23,7 @@ const ( // Uses ANSI escape codes for cross-platform compatibility func ClearScreen() { if tty.IsStderrTerminal() { - fmt.Fprint(os.Stderr, ansiClearScreen) + fmt.Fprint(stderrWriter(), ansiClearScreen) } } @@ -32,7 +31,7 @@ func ClearScreen() { // Uses ANSI escape codes: \r moves cursor to start, \033[K clears to end of line func ClearLine() { if tty.IsStderrTerminal() { - fmt.Fprintf(os.Stderr, "%s%s", ansiCarriageReturn, ansiClearLine) + fmt.Fprintf(stderrWriter(), "%s%s", ansiCarriageReturn, ansiClearLine) } } @@ -44,9 +43,10 @@ func ShowWelcomeBanner(description string) { if tty.IsStderrTerminal() { header = styles.Header.Render(header) } - fmt.Fprintln(os.Stderr, "") - fmt.Fprintln(os.Stderr, header) - fmt.Fprintln(os.Stderr, "") - fmt.Fprintln(os.Stderr, description) - fmt.Fprintln(os.Stderr, "") + out := stderrWriter() + fmt.Fprintln(out, "") + fmt.Fprintln(out, header) + fmt.Fprintln(out, "") + fmt.Fprintln(out, description) + fmt.Fprintln(out, "") } diff --git a/pkg/console/verbose.go b/pkg/console/verbose.go index 75c6dfb99a8..30fdff7f2d9 100644 --- a/pkg/console/verbose.go +++ b/pkg/console/verbose.go @@ -2,13 +2,12 @@ package console import ( "fmt" - "os" ) // LogVerbose outputs a verbose message to stderr only when verbose mode is enabled. // This is a convenience helper to avoid repetitive if-verbose checks throughout the codebase. func LogVerbose(verbose bool, message string) { if verbose { - fmt.Fprintln(os.Stderr, FormatVerboseMessage(message)) + fmt.Fprintln(stderrWriter(), FormatVerboseMessage(message)) } } diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go index 569a3b09366..8f85882637c 100644 --- a/pkg/logger/logger.go +++ b/pkg/logger/logger.go @@ -3,12 +3,15 @@ package logger import ( "fmt" "hash/fnv" + "image/color" + "io" "os" "strings" "sync" "time" lipgloss "charm.land/lipgloss/v2" + "github.com/charmbracelet/colorprofile" "github.com/github/gh-aw/pkg/styles" "github.com/github/gh-aw/pkg/timeutil" ) @@ -30,23 +33,39 @@ var ( // DEBUG_COLORS environment variable to control color output. debugColors = os.Getenv("DEBUG_COLORS") != "0" //nolint:osgetenvlibrary - // Color palette for namespace coloring, using adaptive styles. - colorPalette = []lipgloss.Style{ - lipgloss.NewStyle().Foreground(styles.ColorInfo), - lipgloss.NewStyle().Foreground(styles.ColorSuccess), - lipgloss.NewStyle().Foreground(styles.ColorWarning), - lipgloss.NewStyle().Foreground(styles.ColorPurple), - lipgloss.NewStyle().Foreground(styles.ColorYellow), - lipgloss.NewStyle().Foreground(styles.ColorError), - lipgloss.NewStyle().Foreground(styles.ColorComment), - lipgloss.NewStyle().Foreground(styles.ColorForeground), - lipgloss.NewStyle().Foreground(styles.ColorBorder), - lipgloss.NewStyle().Foreground(styles.ColorInfo), - lipgloss.NewStyle().Foreground(styles.ColorSuccess), - lipgloss.NewStyle().Foreground(styles.ColorPurple), + basePaletteColors = []color.Color{ + styles.ColorInfo, + styles.ColorSuccess, + styles.ColorWarning, + styles.ColorPurple, + styles.ColorYellow, + styles.ColorError, + styles.ColorComment, + styles.ColorForeground, + styles.ColorBorder, } + + // Color palette for namespace coloring, using adaptive styles. + colorPalette = buildColorPalette() ) +func buildColorPalette() []lipgloss.Style { + order := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 3} + palette := make([]lipgloss.Style, 0, len(order)) + for _, idx := range order { + palette = append(palette, lipgloss.NewStyle().Foreground(basePaletteColors[idx])) + } + return palette +} + +func newColorProfileWriter(w io.Writer, environ []string) io.Writer { + return colorprofile.NewWriter(w, environ) +} + +func stderrWriter() io.Writer { + return newColorProfileWriter(os.Stderr, os.Environ()) +} + // initDebugEnv resolves the effective debug pattern. // If DEBUG is set, it takes precedence. Otherwise, if ACTIONS_RUNNER_DEBUG=true, // all loggers are enabled (equivalent to DEBUG=*). @@ -115,7 +134,7 @@ func (l *Logger) Printf(format string, args ...any) { diff := l.tickTime() message := fmt.Sprintf(format, args...) - lipgloss.Fprintf(os.Stderr, "%s %s +%s\n", l.label, message, timeutil.FormatDuration(diff)) + lipgloss.Fprintf(stderrWriter(), "%s %s +%s\n", l.label, message, timeutil.FormatDuration(diff)) } // Print prints a message if the logger is enabled. @@ -128,7 +147,7 @@ func (l *Logger) Print(args ...any) { diff := l.tickTime() message := fmt.Sprint(args...) - lipgloss.Fprintf(os.Stderr, "%s %s +%s\n", l.label, message, timeutil.FormatDuration(diff)) + lipgloss.Fprintf(stderrWriter(), "%s %s +%s\n", l.label, message, timeutil.FormatDuration(diff)) } func (l *Logger) tickTime() time.Duration { diff --git a/pkg/logger/logger_test.go b/pkg/logger/logger_test.go index 3a6f1d47386..561904ba253 100644 --- a/pkg/logger/logger_test.go +++ b/pkg/logger/logger_test.go @@ -4,6 +4,7 @@ package logger import ( "bytes" + "fmt" "os" "strings" "testing" @@ -322,6 +323,19 @@ func TestNoColorEnvironment(t *testing.T) { } } +func TestColorProfileWriterStripsANSIWithNoColor(t *testing.T) { + var buf bytes.Buffer + w := newColorProfileWriter(&buf, []string{"NO_COLOR=1", "TERM=xterm-256color"}) + + if _, err := fmt.Fprint(w, "\x1b[38;2;255;0;0mhello\x1b[0m"); err != nil { + t.Fatalf("write failed: %v", err) + } + + if strings.Contains(buf.String(), "\x1b[") { + t.Fatalf("expected NO_COLOR writer output to be ANSI-free, got %q", buf.String()) + } +} + func TestInitDebugEnv(t *testing.T) { tests := []struct { name string diff --git a/pkg/styles/theme.go b/pkg/styles/theme.go index 6e21b17e4fd..875c3321a32 100644 --- a/pkg/styles/theme.go +++ b/pkg/styles/theme.go @@ -66,6 +66,9 @@ var hasDarkBackground = true // adaptiveColor selects between a light and a dark color variant based on the // terminal background detected at startup. +// Use this for shared package-level colors that should follow gh-aw's startup +// probe; for per-render selection in one-off UI code, prefer lipgloss.LightDark +// (see huh_theme.go). type adaptiveColor struct { Light color.Color Dark color.Color From 51915fa01edacbadbe9ad06a34e434d6c2a18624 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 2 Jul 2026 12:37:07 +0000 Subject: [PATCH 3/8] docs(adr): add draft ADR-42945 for colorprofile.Writer routing --- ...s-v2-output-through-colorprofile-writer.md | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 docs/adr/42945-route-lipgloss-v2-output-through-colorprofile-writer.md diff --git a/docs/adr/42945-route-lipgloss-v2-output-through-colorprofile-writer.md b/docs/adr/42945-route-lipgloss-v2-output-through-colorprofile-writer.md new file mode 100644 index 00000000000..e0415d244d3 --- /dev/null +++ b/docs/adr/42945-route-lipgloss-v2-output-through-colorprofile-writer.md @@ -0,0 +1,44 @@ +# ADR-42945: Route Lip Gloss v2 Output Through colorprofile.Writer + +**Date**: 2026-07-02 +**Status**: Draft +**Deciders**: Unknown + +--- + +### Context + +Lip Gloss v2 removed automatic terminal capability detection that was present in v1. As a result, styled output paths in `pkg/console` and `pkg/logger` began emitting raw ANSI escape codes regardless of `NO_COLOR`, `COLORTERM`, or terminal type — including piped output and limited-color terminals. This violates the [NO_COLOR](https://no-color.org) convention and can corrupt output on terminals that do not support truecolor. The codebase has multiple write sites that emit directly to `os.Stderr`, which bypasses any capability probing entirely. + +### Decision + +We will route all styled stderr output through a `colorprofile.Writer` (from `github.com/charmbracelet/colorprofile`) wrapper rather than writing to `os.Stderr` directly. A shared factory function `stderrWriter()` is introduced in `pkg/console` (with a passthrough stub for wasm builds) so all output paths consistently consult the environment for `NO_COLOR`, `COLORTERM`, and terminal capability before rendering ANSI sequences. + +### Alternatives Considered + +#### Alternative 1: Manual NO_COLOR guard at each write site + +Each styled `fmt.Fprintf(os.Stderr, ...)` call could be preceded by an `if os.Getenv("NO_COLOR") == ""` check. This is the lowest-effort change but requires repetitive, error-prone guards scattered across every output site, with no coverage for terminal capability degradation (e.g., downgrading truecolor to 256-color on constrained terminals). It does not scale as new write sites are added. + +#### Alternative 2: Configure a global Lip Gloss renderer with the correct profile + +Lip Gloss v2 exposes a `Renderer` that can be constructed with a specific `colorprofile.Profile`. A single global renderer could be initialized once at startup and used for all style rendering. This would centralize capability probing but requires replacing every `lipgloss.NewStyle()` call with renderer-scoped style construction, a larger refactor surface. It also does not address `lipgloss.Fprintf` calls that accept an `io.Writer` rather than using a renderer. + +### Consequences + +#### Positive +- `NO_COLOR` is correctly honored across all `pkg/console` and `pkg/logger` output paths without per-call guards. +- Terminal capability is degraded appropriately (e.g., truecolor → 256-color → plain) based on the detected environment at write time. +- Wasm builds retain their original `os.Stderr` passthrough via a build-tagged stub, preserving existing behavior on that platform. + +#### Negative +- `stderrWriter()` calls `colorprofile.NewWriter(os.Stderr, os.Environ())` on every invocation, allocating a new writer wrapper per write site call rather than reusing a singleton. This introduces minor per-call overhead that could be avoided with a cached writer. +- The `newColorProfileWriter`/`stderrWriter` helper functions are duplicated between `pkg/console` and `pkg/logger`, creating two independent copies that could drift in behavior. [TODO: verify] whether consolidating into a shared package is feasible. + +#### Neutral +- Existing tests for `NO_COLOR` behavior in `pkg/logger` (`TestNoColorEnvironment`) continue to pass; new regression tests confirm ANSI stripping via the colorprofile writer path. +- The `pkg/styles/theme.go` comment update clarifying `adaptiveColor` vs `lipgloss.LightDark` usage is documentation-only and carries no runtime impact. + +--- + +*ADR created by [adr-writer agent]. Review and finalize before changing status from Draft to Accepted.* From 91dde54414e39937bf481f5bd690e4061cea5620 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Jul 2026 13:28:20 +0000 Subject: [PATCH 4/8] plan: mirror console build-tag pattern in pkg/logger Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 71fc695ea64..d96ee591154 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( charm.land/bubbletea/v2 v2.0.7 charm.land/huh/v2 v2.0.3 charm.land/lipgloss/v2 v2.0.4 + github.com/charmbracelet/colorprofile v0.4.3 github.com/charmbracelet/x/exp/golden v0.0.0-20260622092256-25656177ba8e github.com/charmbracelet/x/term v0.2.2 github.com/cli/go-gh/v2 v2.13.0 @@ -43,7 +44,6 @@ require ( github.com/catppuccin/go v0.3.0 // indirect github.com/ccojocar/zxcvbn-go v1.0.4 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/charmbracelet/colorprofile v0.4.3 // indirect github.com/charmbracelet/harmonica v0.2.0 // indirect github.com/charmbracelet/ultraviolet v0.0.0-20260525132238-948f4557a654 // indirect github.com/charmbracelet/x/ansi v0.11.7 // indirect From a19d6b74d46496253003e0ee32c81fdfcec21316 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Jul 2026 13:32:55 +0000 Subject: [PATCH 5/8] refactor(logger): split colorprofile writer into build-tagged files Mirror the pkg/console pattern: move newColorProfileWriter/stderrWriter into colorprofile_writer.go (!js && !wasm) and colorprofile_writer_wasm.go (js || wasm) so the colorprofile import is excluded from js/wasm builds. Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- pkg/logger/colorprofile_writer.go | 18 ++++++++++++++++++ pkg/logger/colorprofile_writer_wasm.go | 16 ++++++++++++++++ pkg/logger/logger.go | 10 ---------- 3 files changed, 34 insertions(+), 10 deletions(-) create mode 100644 pkg/logger/colorprofile_writer.go create mode 100644 pkg/logger/colorprofile_writer_wasm.go diff --git a/pkg/logger/colorprofile_writer.go b/pkg/logger/colorprofile_writer.go new file mode 100644 index 00000000000..192110c6722 --- /dev/null +++ b/pkg/logger/colorprofile_writer.go @@ -0,0 +1,18 @@ +//go:build !js && !wasm + +package logger + +import ( + "io" + "os" + + "github.com/charmbracelet/colorprofile" +) + +func newColorProfileWriter(w io.Writer, environ []string) io.Writer { + return colorprofile.NewWriter(w, environ) +} + +func stderrWriter() io.Writer { + return newColorProfileWriter(os.Stderr, os.Environ()) +} diff --git a/pkg/logger/colorprofile_writer_wasm.go b/pkg/logger/colorprofile_writer_wasm.go new file mode 100644 index 00000000000..55ed3ba2c0f --- /dev/null +++ b/pkg/logger/colorprofile_writer_wasm.go @@ -0,0 +1,16 @@ +//go:build js || wasm + +package logger + +import ( + "io" + "os" +) + +func newColorProfileWriter(w io.Writer, _ []string) io.Writer { + return w +} + +func stderrWriter() io.Writer { + return os.Stderr +} diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go index 8f85882637c..7a693752873 100644 --- a/pkg/logger/logger.go +++ b/pkg/logger/logger.go @@ -4,14 +4,12 @@ import ( "fmt" "hash/fnv" "image/color" - "io" "os" "strings" "sync" "time" lipgloss "charm.land/lipgloss/v2" - "github.com/charmbracelet/colorprofile" "github.com/github/gh-aw/pkg/styles" "github.com/github/gh-aw/pkg/timeutil" ) @@ -58,14 +56,6 @@ func buildColorPalette() []lipgloss.Style { return palette } -func newColorProfileWriter(w io.Writer, environ []string) io.Writer { - return colorprofile.NewWriter(w, environ) -} - -func stderrWriter() io.Writer { - return newColorProfileWriter(os.Stderr, os.Environ()) -} - // initDebugEnv resolves the effective debug pattern. // If DEBUG is set, it takes precedence. Otherwise, if ACTIONS_RUNNER_DEBUG=true, // all loggers are enabled (equivalent to DEBUG=*). From 38d3cf036425fcb57e3d5596de1c47cb73f4f313 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Jul 2026 14:27:14 +0000 Subject: [PATCH 6/8] chore: plan spinner writer sharing, logger bounds guard, duplicate test removal Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- .github/workflows/plan.lock.yml | 204 +++++++++++++++++++++----------- 1 file changed, 134 insertions(+), 70 deletions(-) diff --git a/.github/workflows/plan.lock.yml b/.github/workflows/plan.lock.yml index 3ace36363bc..e0660b08e36 100644 --- a/.github/workflows/plan.lock.yml +++ b/.github/workflows/plan.lock.yml @@ -1,6 +1,6 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"7126ed9768737822881bf5a8530f13e7eb276788a5b053d9d038c214905bc50e","body_hash":"f3f7c30eedd5c2709fc93cda56721f423fea8d76201e4ca6a0b6358da4619769","compiler_version":"v0.81.6","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.65","copilot-sdk":"1.0.4"}} -# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"v0.81.6","version":"v0.81.6"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.11","digest":"sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11","digest":"sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.11"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.11","digest":"sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.30","digest":"sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} -# This file was automatically generated by gh-aw (v0.81.6). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"7126ed9768737822881bf5a8530f13e7eb276788a5b053d9d038c214905bc50e","body_hash":"f3f7c30eedd5c2709fc93cda56721f423fea8d76201e4ca6a0b6358da4619769","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.67","copilot-sdk":"1.0.4"}} +# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.20"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.20"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.20"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.20"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.32","digest":"sha256:63e46b56dfd70895a701b6fc6dd0189e11e2d875f327f1781e81b31848735477","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.32@sha256:63e46b56dfd70895a701b6fc6dd0189e11e2d875f327f1781e81b31848735477"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.5.0","digest":"sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4","pinned_image":"ghcr.io/github/github-mcp-server:v1.5.0@sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4"}]} +# This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # # ___ _ _ # / _ \ | | (_) @@ -40,7 +40,6 @@ # - GITHUB_TOKEN # # Custom actions used: -# - actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 # - actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 # - actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 # - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 @@ -48,16 +47,15 @@ # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 # - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 # - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 -# - github/gh-aw-actions/setup@v0.81.6 # # Container images used: -# - ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7 -# - ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d -# - ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.11 -# - ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d -# - ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be +# - ghcr.io/github/gh-aw-firewall/agent:0.27.20 +# - ghcr.io/github/gh-aw-firewall/api-proxy:0.27.20 +# - ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.20 +# - ghcr.io/github/gh-aw-firewall/squid:0.27.20 +# - ghcr.io/github/gh-aw-mcpg:v0.3.32@sha256:63e46b56dfd70895a701b6fc6dd0189e11e2d875f327f1781e81b31848735477 # - ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b -# - ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036 +# - ghcr.io/github/github-mcp-server:v1.5.0@sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4 name: "Plan Command" on: @@ -120,9 +118,17 @@ jobs: text: ${{ steps.sanitized.outputs.text }} title: ${{ steps.sanitized.outputs.title }} steps: + - name: Checkout actions folder + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: github/gh-aw + sparse-checkout: | + actions + clean: false + persist-credentials: false - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@v0.81.6 + uses: ./actions/setup with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} @@ -132,8 +138,8 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Plan Command" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/plan.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.65" - GH_AW_INFO_AWF_VERSION: "v0.27.11" + GH_AW_INFO_VERSION: "1.0.67" + GH_AW_INFO_AWF_VERSION: "v0.27.20" GH_AW_INFO_ENGINE_ID: "copilot" - name: Mask OTLP telemetry headers run: bash "${RUNNER_TEMP}/gh-aw/actions/mask_otlp_headers.sh" @@ -143,16 +149,15 @@ jobs: GH_AW_INFO_ENGINE_ID: "copilot" GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI" GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }} - GH_AW_INFO_VERSION: "1.0.65" - GH_AW_INFO_AGENT_VERSION: "1.0.65" - GH_AW_INFO_CLI_VERSION: "v0.81.6" + GH_AW_INFO_VERSION: "1.0.67" + GH_AW_INFO_AGENT_VERSION: "1.0.67" GH_AW_INFO_WORKFLOW_NAME: "Plan Command" GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" GH_AW_INFO_STAGED: "false" GH_AW_INFO_ALLOWED_DOMAINS: '["*.grafana.net","*.sentry.io","defaults"]' GH_AW_INFO_FIREWALL_ENABLED: "true" - GH_AW_INFO_AWF_VERSION: "v0.27.11" + GH_AW_INFO_AWF_VERSION: "v0.27.20" GH_AW_INFO_AWMG_VERSION: "" GH_AW_INFO_FIREWALL_TYPE: "squid" GH_AW_INFO_FRONTMATTER_EMOJI: "📋" @@ -168,7 +173,7 @@ jobs: id: restore-daily-aic-cache if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} continue-on-error: true - uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: key: agentic-workflow-usage-plan-${{ github.run_id }} restore-keys: agentic-workflow-usage-plan- @@ -233,6 +238,7 @@ jobs: sparse-checkout: | .github .agents + actions/setup .antigravity .claude .codex @@ -260,16 +266,6 @@ jobs: setupGlobals(core, github, context, exec, io, getOctokit); const { main } = require('${{ runner.temp }}/gh-aw/actions/check_workflow_timestamp_api.cjs'); await main(); - - name: Check compile-agentic version - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - env: - GH_AW_COMPILED_VERSION: "v0.81.6" - with: - script: | - const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/check_version_updates.cjs'); - await main(); - name: Compute current body text id: sanitized uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -525,6 +521,7 @@ jobs: checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }} has_patch: ${{ steps.collect_output.outputs.has_patch }} + http_400_response_error: ${{ steps.detect-agent-errors.outputs.http_400_response_error || 'false' }} inference_access_error: ${{ steps.detect-agent-errors.outputs.inference_access_error || 'false' }} mcp_policy_error: ${{ steps.detect-agent-errors.outputs.mcp_policy_error || 'false' }} model: ${{ needs.activation.outputs.model }} @@ -536,9 +533,17 @@ jobs: setup-trace-id: ${{ steps.setup.outputs.trace-id }} unknown_model_ai_credits: ${{ steps.parse-mcp-gateway.outputs.unknown_model_ai_credits || 'false' }} steps: + - name: Checkout actions folder + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: github/gh-aw + sparse-checkout: | + actions + clean: false + persist-credentials: false - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@v0.81.6 + uses: ./actions/setup with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} @@ -547,8 +552,8 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Plan Command" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/plan.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.65" - GH_AW_INFO_AWF_VERSION: "v0.27.11" + GH_AW_INFO_VERSION: "1.0.67" + GH_AW_INFO_AWF_VERSION: "v0.27.20" GH_AW_INFO_ENGINE_ID: "copilot" - name: Set runtime paths id: set-runtime-paths @@ -591,11 +596,11 @@ jobs: const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs'); await main(); - name: Install GitHub Copilot CLI - run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.65 + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.67 env: GH_HOST: github.com - name: Install AWF binary - run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.27.11 --rootless + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.27.20 --rootless - name: Install GitHub Copilot SDK (Node.js) run: cd "${GITHUB_WORKSPACE}" && npm install --ignore-scripts --no-save @github/copilot-sdk@1.0.4 - name: Parse integrity filter lists @@ -626,7 +631,7 @@ jobs: GH_AW_SKILL_DIR: ".github/skills" run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_skills.sh" - name: Download container images - run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7 ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.11 ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036 + run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.27.20 ghcr.io/github/gh-aw-firewall/api-proxy:0.27.20 ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.20 ghcr.io/github/gh-aw-firewall/squid:0.27.20 ghcr.io/github/gh-aw-mcpg:v0.3.32@sha256:63e46b56dfd70895a701b6fc6dd0189e11e2d875f327f1781e81b31848735477 ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b ghcr.io/github/github-mcp-server:v1.5.0@sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4 - name: Generate Safe Outputs Config run: | mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" @@ -826,7 +831,7 @@ jobs: * ) DOCKER_SOCK_PATH=/var/run/docker.sock ;; esac DOCKER_SOCK_GID=$(stat -c '%g' "$DOCKER_SOCK_PATH" 2>/dev/null || echo '0') - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network bridge -p 127.0.0.1:'"${MCP_GATEWAY_PORT}"':'"${MCP_GATEWAY_PORT}"' --name awmg-mcpg --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e RUNNER_TEMP -e GITHUB_AW_OTEL_TRACE_ID -e GITHUB_AW_OTEL_PARENT_SPAN_ID -e OTEL_EXPORTER_OTLP_HEADERS -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw -v '"${RUNNER_TEMP}"'/gh-aw/safeoutputs:'"${RUNNER_TEMP}"'/gh-aw/safeoutputs:rw ghcr.io/github/gh-aw-mcpg:v0.3.30' + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network bridge -p 127.0.0.1:'"${MCP_GATEWAY_PORT}"':'"${MCP_GATEWAY_PORT}"' --name awmg-mcpg --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e RUNNER_TEMP -e GITHUB_AW_OTEL_TRACE_ID -e GITHUB_AW_OTEL_PARENT_SPAN_ID -e OTEL_EXPORTER_OTLP_HEADERS -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw -v '"${RUNNER_TEMP}"'/gh-aw/safeoutputs:'"${RUNNER_TEMP}"'/gh-aw/safeoutputs:rw ghcr.io/github/gh-aw-mcpg:v0.3.32' mkdir -p "$HOME/.copilot" GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) @@ -903,9 +908,15 @@ jobs: env: GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_API_URL: ${{ github.api_url }} + GH_HOST: ${{ env.GH_HOST }} + GITHUB_HOST: ${{ env.GITHUB_HOST }} + GITHUB_ENTERPRISE_HOST: ${{ env.GITHUB_ENTERPRISE_HOST }} + GITHUB_GRAPHQL_URL: ${{ env.GITHUB_GRAPHQL_URL }} + GITHUB_COPILOT_BASE_URL: ${{ env.GITHUB_COPILOT_BASE_URL }} GH_AW_NETWORK_ISOLATION: 'true' CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"none","repos":"all"}}' - CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.3.30' + CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.3.32' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh" - name: Execute GitHub Copilot CLI @@ -926,7 +937,7 @@ jobs: export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK" (umask 177 && touch /tmp/gh-aw/agent-stdio.log) GH_AW_MAX_AI_CREDITS="${GH_AW_MAX_AI_CREDITS:-1000}" - printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.11/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"*.grafana.net\",\"*.sentry.io\",\"api.business.githubcopilot.com\",\"api.enterprise.githubcopilot.com\",\"api.github.com\",\"api.githubcopilot.com\",\"api.individual.githubcopilot.com\",\"api.snapcraft.io\",\"archive.ubuntu.com\",\"azure.archive.ubuntu.com\",\"crl.geotrust.com\",\"crl.globalsign.com\",\"crl.identrust.com\",\"crl.sectigo.com\",\"crl.thawte.com\",\"crl.usertrust.com\",\"crl.verisign.com\",\"crl3.digicert.com\",\"crl4.digicert.com\",\"crls.ssl.com\",\"github.com\",\"host.docker.internal\",\"json-schema.org\",\"json.schemastore.org\",\"keyserver.ubuntu.com\",\"ocsp.digicert.com\",\"ocsp.geotrust.com\",\"ocsp.globalsign.com\",\"ocsp.identrust.com\",\"ocsp.sectigo.com\",\"ocsp.ssl.com\",\"ocsp.thawte.com\",\"ocsp.usertrust.com\",\"ocsp.verisign.com\",\"packagecloud.io\",\"packages.cloud.google.com\",\"packages.microsoft.com\",\"ppa.launchpad.net\",\"raw.githubusercontent.com\",\"registry.npmjs.org\",\"s.symcb.com\",\"s.symcd.com\",\"security.ubuntu.com\",\"telemetry.enterprise.githubcopilot.com\",\"ts-crl.ws.symantec.com\",\"ts-ocsp.ws.symantec.com\",\"www.googleapis.com\"],\"isolation\":true,\"topologyAttach\":[\"awmg-mcpg\",\"awmg-cli-proxy\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5,\"models\":{\"agent\":[\"sonnet-6x\",\"gpt-5.5\",\"gpt-5.4\",\"gpt-5.3\",\"gemini-pro\",\"any\"],\"antigravity\":[\"copilot/antigravity*\",\"google/antigravity*\",\"gemini/antigravity*\"],\"any\":[\"copilot/*\",\"anthropic/*\",\"openai/*\",\"google/*\",\"gemini/*\"],\"claude\":[\"agent\"],\"codex\":[\"agent\"],\"coding\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\",\"gpt-5-codex\"],\"computer-use\":[\"copilot/*computer-use*\",\"google/*computer-use*\",\"gemini/*computer-use*\",\"openai/*computer-use*\"],\"copilot\":[\"agent\"],\"deep-research\":[\"copilot/deep-research*\",\"copilot/o3-deep-research*\",\"copilot/o4-mini-deep-research*\",\"google/deep-research*\",\"gemini/deep-research*\",\"openai/o3-deep-research*\",\"openai/o4-mini-deep-research*\"],\"gemini\":[\"agent\"],\"gemini-3-flash\":[\"copilot/gemini-3*flash*\",\"google/gemini-3*flash*\",\"gemini/gemini-3*flash*\"],\"gemini-3-pro\":[\"copilot/gemini-3*pro*\",\"google/gemini-3*pro*\",\"google/nano-banana*\",\"gemini/gemini-3*pro*\"],\"gemini-3.1-flash\":[\"copilot/gemini-3.1*flash*\",\"google/gemini-3.1*flash*\",\"gemini/gemini-3.1*flash*\"],\"gemini-3.1-pro\":[\"copilot/gemini-3.1*pro*\",\"google/gemini-3.1*pro*\",\"gemini/gemini-3.1*pro*\"],\"gemini-3.5-flash\":[\"copilot/gemini-3.5*flash*\",\"google/gemini-3.5*flash*\",\"gemini/gemini-3.5*flash*\"],\"gemini-flash\":[\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"],\"gemini-flash-lite\":[\"copilot/gemini-*flash*lite*\",\"google/gemini-*flash*lite*\",\"gemini/gemini-*flash*lite*\"],\"gemini-pro\":[\"copilot/gemini-*pro*\",\"google/gemini-*pro*\",\"gemini/gemini-*pro*\"],\"gemma\":[\"copilot/gemma*\",\"google/gemma*\",\"gemini/gemma*\"],\"gpt-5\":[\"copilot/gpt-5*\",\"openai/gpt-5*\"],\"gpt-5-codex\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\"],\"gpt-5-mini\":[\"copilot/gpt-5*mini*\",\"openai/gpt-5*mini*\"],\"gpt-5-nano\":[\"copilot/gpt-5*nano*\",\"openai/gpt-5*nano*\"],\"gpt-5-pro\":[\"copilot/gpt-5*pro*\",\"openai/gpt-5*pro*\"],\"gpt-5.1\":[\"copilot/gpt-5.1*\",\"openai/gpt-5.1*\"],\"gpt-5.2\":[\"copilot/gpt-5.2*\",\"openai/gpt-5.2*\"],\"gpt-5.3\":[\"copilot/gpt-5.3*\",\"openai/gpt-5.3*\"],\"gpt-5.4\":[\"copilot/gpt-5.4*\",\"openai/gpt-5.4*\"],\"gpt-5.5\":[\"copilot/gpt-5.5*\",\"openai/gpt-5.5*\"],\"haiku\":[\"copilot/*haiku*\",\"anthropic/*haiku*\"],\"image-generation\":[\"copilot/gpt-image*\",\"openai/gpt-image*\",\"openai/chatgpt-image*\",\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"google/imagen*\"],\"large\":[\"sonnet\",\"gpt-5-pro\",\"gpt-5\",\"gemini-pro\"],\"mai-code\":[\"copilot/MAI-Code*\",\"copilot/mai-code*\",\"openai/MAI-Code*\"],\"mini\":[\"haiku\",\"gpt-5-mini\",\"gpt-5-nano\",\"gemini-flash-lite\"],\"nano-banana\":[\"copilot/nano-banana*\",\"google/nano-banana*\",\"gemini/nano-banana*\"],\"opus\":[\"copilot/*opus*\",\"anthropic/*opus*\"],\"opusplan\":[\"opus?effort=high\"],\"reasoning\":[\"copilot/o1*\",\"copilot/o3*\",\"copilot/o4*\",\"openai/o1*\",\"openai/o3*\",\"openai/o4*\"],\"robotics\":[\"copilot/*robotics*\",\"google/*robotics*\",\"gemini/*robotics*\"],\"small\":[\"mini\"],\"small-agent\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash\"],\"sonnet\":[\"copilot/*sonnet*\",\"anthropic/*sonnet*\"],\"sonnet-6x\":[\"copilot/*sonnet-4.5*\",\"copilot/*sonnet-4.6*\",\"copilot/*sonnet-4-5-*\",\"anthropic/*sonnet-4-5-*\",\"copilot/*sonnet-4-6*\",\"anthropic/*sonnet-4-6*\"],\"summarization\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash-lite\",\"mini\"],\"vision\":[\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"]}},\"container\":{\"imageTag\":\"0.27.11,squid=sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d,agent=sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7,api-proxy=sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json" + printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.20/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"*.grafana.net\",\"*.sentry.io\",\"api.business.githubcopilot.com\",\"api.enterprise.githubcopilot.com\",\"api.github.com\",\"api.githubcopilot.com\",\"api.individual.githubcopilot.com\",\"api.snapcraft.io\",\"archive.ubuntu.com\",\"azure.archive.ubuntu.com\",\"crl.geotrust.com\",\"crl.globalsign.com\",\"crl.identrust.com\",\"crl.sectigo.com\",\"crl.thawte.com\",\"crl.usertrust.com\",\"crl.verisign.com\",\"crl3.digicert.com\",\"crl4.digicert.com\",\"crls.ssl.com\",\"github.com\",\"host.docker.internal\",\"json-schema.org\",\"json.schemastore.org\",\"keyserver.ubuntu.com\",\"ocsp.digicert.com\",\"ocsp.geotrust.com\",\"ocsp.globalsign.com\",\"ocsp.identrust.com\",\"ocsp.sectigo.com\",\"ocsp.ssl.com\",\"ocsp.thawte.com\",\"ocsp.usertrust.com\",\"ocsp.verisign.com\",\"packagecloud.io\",\"packages.cloud.google.com\",\"packages.microsoft.com\",\"ppa.launchpad.net\",\"raw.githubusercontent.com\",\"registry.npmjs.org\",\"s.symcb.com\",\"s.symcd.com\",\"security.ubuntu.com\",\"telemetry.enterprise.githubcopilot.com\",\"ts-crl.ws.symantec.com\",\"ts-ocsp.ws.symantec.com\",\"www.googleapis.com\"],\"isolation\":true,\"topologyAttach\":[\"awmg-mcpg\",\"awmg-cli-proxy\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5,\"models\":{\"agent\":[\"sonnet-6x\",\"gpt-5.5\",\"gpt-5.4\",\"gpt-5.3\",\"gemini-pro\",\"any\"],\"antigravity\":[\"copilot/antigravity*\",\"google/antigravity*\",\"gemini/antigravity*\"],\"any\":[\"copilot/*\",\"anthropic/*\",\"openai/*\",\"google/*\",\"gemini/*\"],\"claude\":[\"agent\"],\"codex\":[\"agent\"],\"coding\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\",\"gpt-5-codex\"],\"computer-use\":[\"copilot/*computer-use*\",\"google/*computer-use*\",\"gemini/*computer-use*\",\"openai/*computer-use*\"],\"copilot\":[\"agent\"],\"deep-research\":[\"copilot/deep-research*\",\"copilot/o3-deep-research*\",\"copilot/o4-mini-deep-research*\",\"google/deep-research*\",\"gemini/deep-research*\",\"openai/o3-deep-research*\",\"openai/o4-mini-deep-research*\"],\"fable\":[\"copilot/*fable*\",\"anthropic/*fable*\"],\"gemini\":[\"agent\"],\"gemini-3-flash\":[\"copilot/gemini-3*flash*\",\"google/gemini-3*flash*\",\"gemini/gemini-3*flash*\"],\"gemini-3-pro\":[\"copilot/gemini-3*pro*\",\"google/gemini-3*pro*\",\"google/nano-banana*\",\"gemini/gemini-3*pro*\"],\"gemini-3.1-flash\":[\"copilot/gemini-3.1*flash*\",\"google/gemini-3.1*flash*\",\"gemini/gemini-3.1*flash*\"],\"gemini-3.1-pro\":[\"copilot/gemini-3.1*pro*\",\"google/gemini-3.1*pro*\",\"gemini/gemini-3.1*pro*\"],\"gemini-3.5-flash\":[\"copilot/gemini-3.5*flash*\",\"google/gemini-3.5*flash*\",\"gemini/gemini-3.5*flash*\"],\"gemini-flash\":[\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"],\"gemini-flash-lite\":[\"copilot/gemini-*flash*lite*\",\"google/gemini-*flash*lite*\",\"gemini/gemini-*flash*lite*\"],\"gemini-omni\":[\"copilot/gemini-omni*\",\"google/gemini-omni*\",\"gemini/gemini-omni*\"],\"gemini-pro\":[\"copilot/gemini-*pro*\",\"google/gemini-*pro*\",\"gemini/gemini-*pro*\"],\"gemma\":[\"copilot/gemma*\",\"google/gemma*\",\"gemini/gemma*\"],\"gpt-5\":[\"copilot/gpt-5*\",\"openai/gpt-5*\"],\"gpt-5-codex\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\"],\"gpt-5-mini\":[\"copilot/gpt-5*mini*\",\"openai/gpt-5*mini*\"],\"gpt-5-nano\":[\"copilot/gpt-5*nano*\",\"openai/gpt-5*nano*\"],\"gpt-5-pro\":[\"copilot/gpt-5*pro*\",\"openai/gpt-5*pro*\"],\"gpt-5.1\":[\"copilot/gpt-5.1*\",\"openai/gpt-5.1*\"],\"gpt-5.2\":[\"copilot/gpt-5.2*\",\"openai/gpt-5.2*\"],\"gpt-5.3\":[\"copilot/gpt-5.3*\",\"openai/gpt-5.3*\"],\"gpt-5.4\":[\"copilot/gpt-5.4*\",\"openai/gpt-5.4*\"],\"gpt-5.5\":[\"copilot/gpt-5.5*\",\"openai/gpt-5.5*\"],\"gpt-5.6\":[\"copilot/gpt-5.6*\",\"openai/gpt-5.6*\"],\"haiku\":[\"copilot/*haiku*\",\"anthropic/*haiku*\"],\"image-generation\":[\"copilot/gpt-image*\",\"openai/gpt-image*\",\"openai/chatgpt-image*\",\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"google/imagen*\"],\"large\":[\"fable\",\"sonnet\",\"gpt-5-pro\",\"gpt-5\",\"gemini-pro\"],\"mai-code\":[\"copilot/MAI-Code*\",\"copilot/mai-code*\",\"openai/MAI-Code*\"],\"mini\":[\"haiku\",\"gpt-5-mini\",\"gpt-5-nano\",\"gemini-flash-lite\"],\"nano-banana\":[\"copilot/nano-banana*\",\"google/nano-banana*\",\"gemini/nano-banana*\"],\"opus\":[\"copilot/*opus*\",\"anthropic/*opus*\"],\"opusplan\":[\"opus?effort=high\"],\"reasoning\":[\"copilot/o1*\",\"copilot/o3*\",\"copilot/o4*\",\"openai/o1*\",\"openai/o3*\",\"openai/o4*\"],\"robotics\":[\"copilot/*robotics*\",\"google/*robotics*\",\"gemini/*robotics*\"],\"small\":[\"mini\"],\"small-agent\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash\"],\"sonnet\":[\"copilot/*sonnet*\",\"anthropic/*sonnet*\"],\"sonnet-6x\":[\"copilot/*sonnet-4.5*\",\"copilot/*sonnet-4.6*\",\"copilot/*sonnet-4-5-*\",\"anthropic/*sonnet-4-5-*\",\"copilot/*sonnet-4-6*\",\"anthropic/*sonnet-4-6*\"],\"summarization\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash-lite\",\"mini\"],\"vision\":[\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"]}},\"container\":{\"imageTag\":\"0.27.20\"},\"logging\":{\"proxyLogsDir\":\"/tmp/gh-aw/sandbox/firewall/logs\",\"auditDir\":\"/tmp/gh-aw/sandbox/firewall/audit\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json" cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json export GH_AW_MODELS_JSON_PATH="/tmp/gh-aw/models.json" GH_AW_DOCKER_HOST="" @@ -935,8 +946,8 @@ jobs: fi GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="" if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then - GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw" - GH_AW_CHROOT_BINARIES_SOURCE_PATH=/tmp/gh-aw GH_AW_CHROOT_IDENTITY_HOME=/tmp/gh-aw/home node "${RUNNER_TEMP}/gh-aw/actions/patch_awf_chroot_config.cjs" + GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix ${RUNNER_TEMP}/gh-aw" + GH_AW_CHROOT_BINARIES_SOURCE_PATH="${RUNNER_TEMP}/gh-aw" GH_AW_CHROOT_IDENTITY_HOME="${RUNNER_TEMP}/gh-aw/home" node "${RUNNER_TEMP}/gh-aw/actions/patch_awf_chroot_config.cjs" fi GH_AW_TOOL_CACHE_MOUNT="" GH_AW_TOOL_CACHE="${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}" @@ -945,8 +956,8 @@ jobs: GH_AW_TOOL_CACHE_MOUNT="$GH_AW_TOOL_CACHE:$GH_AW_TOOL_CACHE:ro" fi fi - # shellcheck disable=SC1003,SC2086 - awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GH_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --skip-pull --difc-proxy-host awmg-cli-proxy:18443 --difc-proxy-ca-cert /tmp/gh-aw/difc-proxy-tls/ca.crt \ + # shellcheck disable=SC1003,SC2016,SC2086 + awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GH_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --skip-pull --difc-proxy-host awmg-cli-proxy:18443 --difc-proxy-ca-cert /tmp/gh-aw/difc-proxy-tls/ca.crt \ -- /bin/bash -c 'set +o histexpand; export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && : "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; export PATH="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_WORKSPACE_NODE_MODULES="${GITHUB_WORKSPACE:-$PWD}/node_modules"; if [ -d "$GH_AW_WORKSPACE_NODE_MODULES" ]; then export NODE_PATH="${GH_AW_WORKSPACE_NODE_MODULES}${NODE_PATH:+:${NODE_PATH}}"; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs "$GH_AW_NODE_EXEC" "${RUNNER_TEMP}/gh-aw/actions/copilot_sdk_driver.cjs" /usr/local/bin/copilot' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log env: AWF_REFLECT_ENABLED: 1 @@ -965,7 +976,7 @@ jobs: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} GH_AW_TIMEOUT_MINUTES: 10 - GH_AW_VERSION: v0.81.6 + GH_AW_VERSION: dev GH_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || github.token }} GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true @@ -1176,9 +1187,17 @@ jobs: tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} total_count: ${{ steps.missing_tool.outputs.total_count }} steps: + - name: Checkout actions folder + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: github/gh-aw + sparse-checkout: | + actions + clean: false + persist-credentials: false - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@v0.81.6 + uses: ./actions/setup with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} @@ -1187,8 +1206,8 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Plan Command" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/plan.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.65" - GH_AW_INFO_AWF_VERSION: "v0.27.11" + GH_AW_INFO_VERSION: "1.0.67" + GH_AW_INFO_AWF_VERSION: "v0.27.20" GH_AW_INFO_ENGINE_ID: "copilot" - name: Download agent output artifact id: download-agent-output @@ -1228,7 +1247,7 @@ jobs: [ -f /tmp/gh-aw/usage/agent/token_usage.jsonl ] || : > /tmp/gh-aw/usage/agent/token_usage.jsonl [ -f /tmp/gh-aw/usage/detection/token_usage.jsonl ] || : > /tmp/gh-aw/usage/detection/token_usage.jsonl mkdir -p /tmp/gh-aw/usage/activity - node ${{ runner.temp }}/gh-aw/actions/generate_usage_activity_summary.cjs + node "${RUNNER_TEMP}/gh-aw/actions/generate_usage_activity_summary.cjs" find /tmp/gh-aw/usage -type f -print | sort - name: Upload usage artifact if: always() @@ -1380,6 +1399,7 @@ jobs: GH_AW_MCP_POLICY_ERROR: ${{ needs.agent.outputs.mcp_policy_error }} GH_AW_AGENTIC_ENGINE_TIMEOUT: ${{ needs.agent.outputs.agentic_engine_timeout }} GH_AW_MODEL_NOT_SUPPORTED_ERROR: ${{ needs.agent.outputs.model_not_supported_error }} + GH_AW_HTTP_400_RESPONSE_ERROR: ${{ needs.agent.outputs.http_400_response_error }} GH_AW_ENGINE_API_HOSTS: "api.enterprise.githubcopilot.com,api.githubcopilot.com,api.business.githubcopilot.com,api.individual.githubcopilot.com" GH_AW_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }} GH_AW_STALE_LOCK_FILE_FAILED: ${{ needs.activation.outputs.stale_lock_file_failed }} @@ -1435,9 +1455,17 @@ jobs: detection_reason: ${{ steps.detection_conclusion.outputs.reason }} detection_success: ${{ steps.detection_conclusion.outputs.success }} steps: + - name: Checkout actions folder + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: github/gh-aw + sparse-checkout: | + actions + clean: false + persist-credentials: false - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@v0.81.6 + uses: ./actions/setup with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} @@ -1446,8 +1474,8 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Plan Command" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/plan.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.65" - GH_AW_INFO_AWF_VERSION: "v0.27.11" + GH_AW_INFO_VERSION: "1.0.67" + GH_AW_INFO_AWF_VERSION: "v0.27.20" GH_AW_INFO_ENGINE_ID: "copilot" - name: Download agent output artifact id: download-agent-output @@ -1480,7 +1508,7 @@ jobs: rm -rf /tmp/gh-aw/sandbox/firewall/logs rm -rf /tmp/gh-aw/sandbox/firewall/audit - name: Download container images - run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7 ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d + run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.27.20 ghcr.io/github/gh-aw-firewall/api-proxy:0.27.20 ghcr.io/github/gh-aw-firewall/squid:0.27.20 - name: Check if detection needed id: detection_guard if: always() @@ -1543,11 +1571,11 @@ jobs: node-version: '24' package-manager-cache: false - name: Install GitHub Copilot CLI - run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.65 + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.67 env: GH_HOST: github.com - name: Install AWF binary - run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.27.11 + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.27.20 - name: Execute GitHub Copilot CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' continue-on-error: true @@ -1567,7 +1595,7 @@ jobs: export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK" (umask 177 && touch /tmp/gh-aw/threat-detection/detection.log) GH_AW_MAX_AI_CREDITS="${GH_AW_MAX_AI_CREDITS:-400}" - printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.11/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"api.business.githubcopilot.com\",\"api.enterprise.githubcopilot.com\",\"api.github.com\",\"api.githubcopilot.com\",\"api.individual.githubcopilot.com\",\"github.com\",\"host.docker.internal\",\"registry.npmjs.org\",\"telemetry.enterprise.githubcopilot.com\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5},\"container\":{\"imageTag\":\"0.27.11,squid=sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d,agent=sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7,api-proxy=sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json" + printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.20/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"api.business.githubcopilot.com\",\"api.enterprise.githubcopilot.com\",\"api.github.com\",\"api.githubcopilot.com\",\"api.individual.githubcopilot.com\",\"github.com\",\"host.docker.internal\",\"registry.npmjs.org\",\"telemetry.enterprise.githubcopilot.com\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5},\"container\":{\"imageTag\":\"0.27.20\"},\"logging\":{\"proxyLogsDir\":\"/tmp/gh-aw/sandbox/firewall/logs\",\"auditDir\":\"/tmp/gh-aw/sandbox/firewall/audit\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json" cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json export GH_AW_MODELS_JSON_PATH="/tmp/gh-aw/models.json" GH_AW_DOCKER_HOST="" @@ -1576,10 +1604,10 @@ jobs: fi GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="" if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then - GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw" - _GH_AW_CHROOT_JSON=$(jq -c --arg src /tmp/gh-aw --arg user "$(id -un)" --argjson uid "$(id -u)" --argjson gid "$(id -g)" --arg home /tmp/gh-aw/home '.chroot={"binariesSourcePath":$src,"identity":{"user":$user,"uid":$uid,"gid":$gid,"home":$home}}' "${RUNNER_TEMP}/gh-aw/awf-config.json") || { echo "chroot config patch failed" >&2; exit 1; } + GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix ${RUNNER_TEMP}/gh-aw" + _GH_AW_CHROOT_JSON=$(jq -c --arg src "${RUNNER_TEMP}/gh-aw" --arg user "$(id -un)" --argjson uid "$(id -u)" --argjson gid "$(id -g)" --arg home "${RUNNER_TEMP}/gh-aw/home" '.chroot={"binariesSourcePath":$src,"identity":{"user":$user,"uid":$uid,"gid":$gid,"home":$home}}' "${RUNNER_TEMP}/gh-aw/awf-config.json") || { echo "chroot config patch failed" >&2; exit 1; } + printf '%s\n' "$_GH_AW_CHROOT_JSON" > "${RUNNER_TEMP}/gh-aw/awf-config.json" printf '%s\n' "$_GH_AW_CHROOT_JSON" > "${RUNNER_TEMP}/gh-aw/awf-config.json" - printf '%s\n' "$_GH_AW_CHROOT_JSON" > "/tmp/gh-aw/awf-config.json" fi GH_AW_TOOL_CACHE_MOUNT="" GH_AW_TOOL_CACHE="${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}" @@ -1588,8 +1616,8 @@ jobs: GH_AW_TOOL_CACHE_MOUNT="$GH_AW_TOOL_CACHE:$GH_AW_TOOL_CACHE:ro" fi fi - # shellcheck disable=SC1003,SC2086 - sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \ + # shellcheck disable=SC1003,SC2016,SC2086 + sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --log-level info --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \ -- /bin/bash -c 'set +o histexpand; : "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; export PATH="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log env: AWF_REFLECT_ENABLED: 1 @@ -1603,7 +1631,7 @@ jobs: GH_AW_PHASE: detection GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_TIMEOUT_MINUTES: 20 - GH_AW_VERSION: v0.81.6 + GH_AW_VERSION: dev GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows @@ -1673,6 +1701,8 @@ jobs: pre_activation: runs-on: ubuntu-slim + permissions: + contents: read env: GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} outputs: @@ -1682,17 +1712,25 @@ jobs: setup-span-id: ${{ steps.setup.outputs.span-id }} setup-trace-id: ${{ steps.setup.outputs.trace-id }} steps: + - name: Checkout actions folder + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: github/gh-aw + sparse-checkout: | + actions + clean: false + persist-credentials: false - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@v0.81.6 + uses: ./actions/setup with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} env: GH_AW_SETUP_WORKFLOW_NAME: "Plan Command" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/plan.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.65" - GH_AW_INFO_AWF_VERSION: "v0.27.11" + GH_AW_INFO_VERSION: "1.0.67" + GH_AW_INFO_AWF_VERSION: "v0.27.20" GH_AW_INFO_ENGINE_ID: "copilot" - name: Check team membership for command workflow id: check_membership @@ -1727,9 +1765,17 @@ jobs: env: GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} steps: + - name: Checkout actions folder + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: github/gh-aw + sparse-checkout: | + actions + clean: false + persist-credentials: false - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@v0.81.6 + uses: ./actions/setup with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} @@ -1738,8 +1784,8 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Plan Command" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/plan.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.65" - GH_AW_INFO_AWF_VERSION: "v0.27.11" + GH_AW_INFO_VERSION: "1.0.67" + GH_AW_INFO_AWF_VERSION: "v0.27.20" GH_AW_INFO_ENGINE_ID: "copilot" - name: Checkout repository uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 @@ -1774,6 +1820,16 @@ jobs: setupGlobals(core, github, context, exec, io, getOctokit); const { main } = require('${{ runner.temp }}/gh-aw/actions/push_experiment_state.cjs'); await main(); + - name: Restore actions folder + if: always() + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: github/gh-aw + sparse-checkout: | + actions/setup + sparse-checkout-cone-mode: true + clean: false + persist-credentials: false safe_outputs: needs: @@ -1798,7 +1854,7 @@ jobs: GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }} GH_AW_ENGINE_ID: "copilot" GH_AW_ENGINE_MODEL: ${{ needs.agent.outputs.model }} - GH_AW_ENGINE_VERSION: "1.0.65" + GH_AW_ENGINE_VERSION: "1.0.67" GH_AW_PROJECT_UTC: "-08:00" GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }} @@ -1816,9 +1872,17 @@ jobs: process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: + - name: Checkout actions folder + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: github/gh-aw + sparse-checkout: | + actions + clean: false + persist-credentials: false - name: Setup Scripts id: setup - uses: github/gh-aw-actions/setup@v0.81.6 + uses: ./actions/setup with: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} @@ -1827,8 +1891,8 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Plan Command" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/plan.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.65" - GH_AW_INFO_AWF_VERSION: "v0.27.11" + GH_AW_INFO_VERSION: "1.0.67" + GH_AW_INFO_AWF_VERSION: "v0.27.20" GH_AW_INFO_ENGINE_ID: "copilot" - name: Mask OTLP telemetry headers run: bash "${RUNNER_TEMP}/gh-aw/actions/mask_otlp_headers.sh" From d29af847bf62353f3cad43b7c7a73fde1148bce7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Jul 2026 14:31:47 +0000 Subject: [PATCH 7/8] fix(console/logger): share single colorprofile writer in spinner, add bounds guard, remove duplicate test - spinner.go: capture stderrWriter() once in NewSpinner, store on SpinnerWrapper.out, share with spinnerModel.output, tea.WithOutput, and Stop/StopWithMessage calls - logger.go: add explicit panic in buildColorPalette if an order index exceeds len(basePaletteColors), making the invariant visible at startup - logger_test.go: remove TestColorProfileWriterStripsANSIWithNoColor (duplicate of the same test in pkg/console/colorprofile_writer_test.go) Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- pkg/console/spinner.go | 16 +++++++++------- pkg/logger/logger.go | 3 +++ pkg/logger/logger_test.go | 14 -------------- 3 files changed, 12 insertions(+), 21 deletions(-) diff --git a/pkg/console/spinner.go b/pkg/console/spinner.go index fb1256ee752..443fe737165 100644 --- a/pkg/console/spinner.go +++ b/pkg/console/spinner.go @@ -91,6 +91,7 @@ func (m spinnerModel) render() { // SpinnerWrapper wraps the spinner functionality with TTY detection and Bubble Tea program type SpinnerWrapper struct { program *tea.Program + out io.Writer enabled bool running bool mu sync.Mutex @@ -104,19 +105,20 @@ func NewSpinner(message string) *SpinnerWrapper { isAccessible := IsAccessibleMode() enabled := isTTY && !isAccessible spinnerLog.Printf("Creating spinner: message=%q, tty=%t, accessible=%t, enabled=%t", message, isTTY, isAccessible, enabled) - s := &SpinnerWrapper{enabled: enabled} + out := stderrWriter() + s := &SpinnerWrapper{enabled: enabled, out: out} if enabled { model := spinnerModel{ spinner: spinner.New(spinner.WithSpinner(spinner.MiniDot), spinner.WithStyle(styles.Info)), message: message, - output: stderrWriter(), + output: out, } // tea.WithInput(nil) disables stdin reading so the spinner does not consume key // events that should be handled by subsequent interactive forms (e.g. huh.Select). // Ctrl+C is still handled via OS signal delivery (SIGINT), which bubbletea // processes independently of the input reader. - s.program = tea.NewProgram(model, tea.WithOutput(stderrWriter()), tea.WithoutRenderer(), tea.WithInput(nil)) + s.program = tea.NewProgram(model, tea.WithOutput(out), tea.WithoutRenderer(), tea.WithInput(nil)) } return s } @@ -170,7 +172,7 @@ func (s *SpinnerWrapper) Stop() { spinnerLog.Print("Stopping spinner") s.program.Quit() s.wg.Wait() // Wait for the goroutine to complete - fmt.Fprintf(stderrWriter(), "%s%s", ansiCarriageReturn, ansiClearLine) + fmt.Fprintf(s.out, "%s%s", ansiCarriageReturn, ansiClearLine) } } } @@ -189,14 +191,14 @@ func (s *SpinnerWrapper) StopWithMessage(msg string) { if wasRunning { s.program.Quit() s.wg.Wait() // Wait for the goroutine to complete - fmt.Fprintf(stderrWriter(), "%s%s%s\n", ansiCarriageReturn, ansiClearLine, msg) + fmt.Fprintf(s.out, "%s%s%s\n", ansiCarriageReturn, ansiClearLine, msg) } else { // Still print the message even if spinner wasn't running - fmt.Fprintf(stderrWriter(), "%s\n", msg) + fmt.Fprintf(s.out, "%s\n", msg) } } else if msg != "" { // If spinner is disabled, still print the message for user feedback - fmt.Fprintf(stderrWriter(), "%s\n", msg) + fmt.Fprintf(s.out, "%s\n", msg) } } diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go index 7a693752873..ea4135c6547 100644 --- a/pkg/logger/logger.go +++ b/pkg/logger/logger.go @@ -51,6 +51,9 @@ func buildColorPalette() []lipgloss.Style { order := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 3} palette := make([]lipgloss.Style, 0, len(order)) for _, idx := range order { + if idx >= len(basePaletteColors) { + panic(fmt.Sprintf("logger: buildColorPalette index %d out of range (len=%d)", idx, len(basePaletteColors))) + } palette = append(palette, lipgloss.NewStyle().Foreground(basePaletteColors[idx])) } return palette diff --git a/pkg/logger/logger_test.go b/pkg/logger/logger_test.go index 561904ba253..3a6f1d47386 100644 --- a/pkg/logger/logger_test.go +++ b/pkg/logger/logger_test.go @@ -4,7 +4,6 @@ package logger import ( "bytes" - "fmt" "os" "strings" "testing" @@ -323,19 +322,6 @@ func TestNoColorEnvironment(t *testing.T) { } } -func TestColorProfileWriterStripsANSIWithNoColor(t *testing.T) { - var buf bytes.Buffer - w := newColorProfileWriter(&buf, []string{"NO_COLOR=1", "TERM=xterm-256color"}) - - if _, err := fmt.Fprint(w, "\x1b[38;2;255;0;0mhello\x1b[0m"); err != nil { - t.Fatalf("write failed: %v", err) - } - - if strings.Contains(buf.String(), "\x1b[") { - t.Fatalf("expected NO_COLOR writer output to be ANSI-free, got %q", buf.String()) - } -} - func TestInitDebugEnv(t *testing.T) { tests := []struct { name string From 38c7ff0141d7d1875c6846a9246db145bceb23f7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Jul 2026 15:36:48 +0000 Subject: [PATCH 8/8] fix(logger): remove panic from buildColorPalette to satisfy panicinlibrarycode linter Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/logger/logger.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go index ea4135c6547..7a693752873 100644 --- a/pkg/logger/logger.go +++ b/pkg/logger/logger.go @@ -51,9 +51,6 @@ func buildColorPalette() []lipgloss.Style { order := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 3} palette := make([]lipgloss.Style, 0, len(order)) for _, idx := range order { - if idx >= len(basePaletteColors) { - panic(fmt.Sprintf("logger: buildColorPalette index %d out of range (len=%d)", idx, len(basePaletteColors))) - } palette = append(palette, lipgloss.NewStyle().Foreground(basePaletteColors[idx])) } return palette