@@ -299,6 +299,32 @@ func renderCopilotSetupUpdateInstructions(filePath string, actionMode workflow.A
299299var setupCliUsesPattern = regexp .MustCompile (
300300 `(?m)^(\s+uses:[ \t]*)"?(github/gh-aw(?:-actions)?/(?:actions/)?setup-cli@[^"\n]*)"?([ \t]*)$` )
301301
302+ // versionInWithPattern matches the version: parameter in the with: block that immediately
303+ // follows any setup-cli uses: line (any ref format: version tag, SHA-pinned, or quoted).
304+ // It is anchored to the same action repos as setupCliUsesPattern so that it only updates
305+ // the version belonging to the setup-cli step, but is independent of the exact ref value.
306+ // This allows it to correct pre-existing drift where the uses: comment and with: version:
307+ // were already out of sync before the upgrade was run.
308+ //
309+ // Pattern breakdown:
310+ //
311+ // [ \t]+uses:[ \t]* — indented uses: key with optional surrounding spaces
312+ // "?github/gh-aw(?:-actions)?/(?:actions/)?setup-cli@[^"\n]*"?
313+ // — any setup-cli ref (version tag, SHA+comment, or quoted)
314+ // [^\n]*\n — rest of the uses: line (e.g. trailing spaces)
315+ // (?:[^\n]*\n)*? — zero or more lines between uses: and with: (non-greedy)
316+ // [ \t]+with:[ \t]*\n — indented with: key
317+ // (?:[^\n]*\n)*? — zero or more lines between with: and version: (non-greedy)
318+ // [ \t]+version:[ \t]* — indented version: key (final part of the prefix captured as group 1)
319+ // (\S+) — the version value (captured as group 2)
320+ // ([ \t]*(?:\n|$)) — trailing whitespace and line terminator (captured as group 3)
321+ //
322+ // Note: In the full pattern, group 1 wraps the entire prefix from the setup-cli `uses:` line
323+ // through the `version:` key (and following spaces), group 2 is just the version value, and
324+ // group 3 is the trailing whitespace plus the line terminator.
325+ var versionInWithPattern = regexp .MustCompile (
326+ `(?s)([ \t]+uses:[ \t]*"?github/gh-aw(?:-actions)?/(?:actions/)?setup-cli@[^"\n]*"?[^\n]*\n(?:[^\n]*\n)*?[ \t]+with:[ \t]*\n(?:[^\n]*\n)*?[ \t]+version:[ \t]*)(\S+)([ \t]*(?:\n|$))` )
327+
302328// upgradeSetupCliVersionInContent replaces the setup-cli action reference and the
303329// associated version: parameter in the raw YAML content using targeted regex
304330// substitutions, preserving all other formatting in the file.
@@ -325,13 +351,9 @@ func upgradeSetupCliVersionInContent(content []byte, actionMode workflow.ActionM
325351 updated := setupCliUsesPattern .ReplaceAll (content , []byte ("${1}" + newUses + "${3}" ))
326352
327353 // Replace the version: value in the with: block immediately following the
328- // setup-cli uses: line. A combined multiline match is used so that only the
329- // version: parameter belonging to this specific step is updated.
330- // This pattern cannot be pre-compiled at package level because it embeds
331- // the runtime value newUses (which varies with version and resolver output).
332- escapedNewUses := regexp .QuoteMeta (newUses )
333- versionInWithPattern := regexp .MustCompile (
334- `(?s)(uses:[ \t]*` + escapedNewUses + `[^\n]*\n(?:[^\n]*\n)*?[ \t]+with:[ \t]*\n(?:[^\n]*\n)*?[ \t]+version:[ \t]*)(\S+)([ \t]*(?:\n|$))` )
354+ // setup-cli uses: line. versionInWithPattern matches any valid setup-cli
355+ // reference so it succeeds even when there was pre-existing drift between
356+ // the uses: comment and the version: parameter before the upgrade was run.
335357 updated = versionInWithPattern .ReplaceAll (updated , []byte ("${1}" + version + "${3}" ))
336358
337359 if bytes .Equal (content , updated ) {
0 commit comments