Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/dataflow-pr-discussion-dataset.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions .github/workflows/deep-report.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions .github/workflows/issue-monster.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .github/workflows/issue-monster.md
Original file line number Diff line number Diff line change
Expand Up @@ -284,8 +284,8 @@ on:
}

// Exclude issues with open PRs from Copilot coding agent
const openCopilotPRs = issue.linkedPRs?.filter(pr =>
pr.state === 'OPEN' &&
const openCopilotPRs = issue.linkedPRs?.filter(pr =>
pr.state === 'OPEN' &&
(pr.author === 'copilot-swe-agent' || pr.author?.includes('copilot'))
) || [];
if (openCopilotPRs.length > 0) {
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/shared/discussions-data-fetch.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,12 @@ steps:

# Extract discussions and normalize structure
echo "$RESULT" | jq -r '
.data.repository.discussions.nodes
.data.repository.discussions.nodes
| map({
number,
number,
title,
body,
createdAt,
createdAt,
updatedAt,
url,
category: .category.name,
Expand Down Expand Up @@ -180,7 +180,7 @@ This shared component fetches open discussions from the repository, with intelli
- **Cache Key**: `discussions-data` for workflow-level sharing
- **Cache Files**: Stored with today's date in the filename (e.g., `discussions-2024-11-18.json`)
- **Cache Location**: `/tmp/gh-aw/cache-memory/`
- **Cache Benefits**:
- **Cache Benefits**:
- Multiple workflows running on the same day share the same discussions data
- Reduces GitHub API rate limit usage
- Faster workflow execution after first fetch of the day
Expand Down
98 changes: 98 additions & 0 deletions pkg/workflow/compiler_yaml_line_writer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package workflow

import "strings"

// yamlBlockScalarState tracks whether the scanner is currently inside a YAML
// literal (|) or folded (>) block scalar. It is used by appendYAMLLine to
// decide whether a line's trailing whitespace may be safely trimmed.
//
// The zero value is ready to use. Create a new instance per YAML stream and
// pass it to every appendYAMLLine call in that stream.
type yamlBlockScalarState struct {
pending bool // saw a block-scalar header; waiting for the first content line
active bool // inside a block scalar's payload
headerIndent int // leading-space count of the block-scalar header line
bodyIndent int // leading-space count of the first payload line
}

// update advances the block-scalar state machine for sourceLine (the raw line
// as it appears in the source YAML, before any prefix or indentation adjustment
// is applied). It returns true when sourceLine is part of a block scalar's
// payload and its content must NOT be right-trimmed.
func (s *yamlBlockScalarState) update(sourceLine string) bool {
trimmed := strings.TrimRight(sourceLine, " \t")

if s.pending || s.active {
if trimmed == "" {
// Blank lines keep the state alive without advancing it:
// inside an active scalar they are payload; inside a pending
// scalar they simply delay the first content line.
return s.active
}
lineIndent := countLeadingSpaces(sourceLine)
if s.pending {
if lineIndent <= s.headerIndent {
// No indented content followed the header; leave the scalar.
s.pending = false
// Fall through to structural handling below.
} else {
s.bodyIndent = lineIndent
s.active = true
s.pending = false
return true // first payload line
}
}
if s.active {
if lineIndent < s.bodyIndent {
// Outdented non-blank line: we have left the block scalar.
s.active = false
// Fall through to structural handling below.
} else {
return true // still inside the block scalar
}
}
}

// Structural line: detect whether it introduces a new block scalar.
if trimmed != "" {
if headerIndent, ok := blockScalarHeaderIndentForLine(trimmed); ok {
s.pending = true
s.headerIndent = headerIndent
}
}
return false
}

// appendYAMLLine writes content to b with the given prefix.
//
// - Blank content (empty or whitespace-only) is always written as a bare
// newline with no prefix, regardless of isBlockScalarContent, to avoid
// emitting lines that contain only spaces (yamllint trailing-spaces).
// - When isBlockScalarContent is true, non-blank content is written verbatim
// so that trailing whitespace that is semantically significant inside a
// shell script or template literal is preserved.
// - When isBlockScalarContent is false, trailing whitespace is trimmed before
// writing so that yamllint trailing-spaces warnings are suppressed.
//
// Callers obtain isBlockScalarContent by calling yamlBlockScalarState.update
// with the original source line. When the source indentation is adjusted before
// writing (e.g. a 2-space prefix is stripped in writeStepsSection), callers
// must still pass the original source line to update so that indent-based
// entry/exit detection remains accurate, and then pass the adjusted content
// here.
func appendYAMLLine(b *strings.Builder, prefix, content string, isBlockScalarContent bool) {
// Always emit blank lines as bare newlines to avoid trailing-space violations.
if strings.TrimRight(content, " \t") == "" {
b.WriteByte('\n')
return
}
if isBlockScalarContent {
b.WriteString(prefix)
b.WriteString(content)
b.WriteByte('\n')
return
}
b.WriteString(prefix)
b.WriteString(strings.TrimRight(content, " \t"))
b.WriteByte('\n')
}
22 changes: 8 additions & 14 deletions pkg/workflow/compiler_yaml_runtime_setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,15 +312,10 @@ func (c *Compiler) addCustomStepsAsIs(yaml *strings.Builder, customSteps string)
// Remove "steps:" line and adjust indentation
lines := strings.Split(customSteps, "\n")
if len(lines) > 1 {
var blockScalarState yamlBlockScalarState
for _, line := range lines[1:] {
// Skip empty lines
if strings.TrimSpace(line) == "" {
yaml.WriteString("\n")
continue
}

// Simply add 6 spaces for job context indentation
yaml.WriteString(" " + line + "\n")
isBS := blockScalarState.update(line)
appendYAMLLine(yaml, " ", line, isBS)
}
}
}
Expand All @@ -337,9 +332,11 @@ func (c *Compiler) addCustomStepsWithRuntimeInsertion(yaml *strings.Builder, cus

insertedRuntime := false
i := 1 // Start from index 1 to skip "steps:" line
var blockScalarState yamlBlockScalarState

for i < len(lines) {
line := lines[i]
isBS := blockScalarState.update(line)

// Skip empty lines
if strings.TrimSpace(line) == "" {
Expand All @@ -349,7 +346,7 @@ func (c *Compiler) addCustomStepsWithRuntimeInsertion(yaml *strings.Builder, cus
}

// Add the line with proper indentation
yaml.WriteString(" " + line + "\n")
appendYAMLLine(yaml, " ", line, isBS)

// Check if this line starts a step with "- name:" or "- uses:"
trimmed := strings.TrimSpace(line)
Expand Down Expand Up @@ -389,11 +386,8 @@ func (c *Compiler) addCustomStepsWithRuntimeInsertion(yaml *strings.Builder, cus
}

// Add the line
if nextTrimmed == "" {
yaml.WriteString("\n")
} else {
yaml.WriteString(" " + nextLine + "\n")
}
nextIsBS := blockScalarState.update(nextLine)
appendYAMLLine(yaml, " ", nextLine, nextIsBS)
i++
}

Expand Down
Loading
Loading