Problem
When running gh aw upgrade to update the CLI extension (e.g., from v0.56.0 to v0.57.0), the lock files generated by the upgrade (or subsequent compile) continue to reference the previous (old) CLI version in metadata and workflow headers (e.g., v0.56.0) even though the CLI itself was upgraded to v0.57.0 during this operation.
This results in .lock.yml files and generated YAML referencing the wrong (stale) version, causing confusion and potential future automation or compatibility issues.
Root Cause Analysis
- The upgrade process is performed by a single running
gh-aw process.
gh aw upgrade (and compile, etc.) propagates the version number from Go build-time linker flags and from variables initialized at the start of process execution.
- When the command runs, it first checks for upgrades/advises to upgrade, but even after the user upgrades the CLI extension, the currently running process is still the old binary until the next execution.
- Compilation/lockfile-rebuild is therefore performed by the old binary with the old version string baked in.
- There is no re-execution or post-upgrade detection that would cause the new version to be used for lock file regeneration.
Direct Code References
1. The build-time version is only set at process start:
// Build-time variables set by GoReleaser
var (
version = "dev"
isRelease = "false" // Set to "true" during release builds
)
2. The main() entrypoint populates global version variables ONCE:
func main() {
// Set version information in the CLI package
cli.SetVersionInfo(version)
// Set version information in the workflow package for generated file headers
workflow.SetVersion(version)
// Set release flag in the workflow package
workflow.SetIsRelease(isRelease == "true")
...
}
3. The upgrade command only prints a warning, doesn't switch process:
// Step 0b: Ensure gh-aw extension is on the latest version
fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Checking gh-aw extension version..."))
if err := ensureLatestExtensionVersion(verbose); err != nil {
upgradeLog.Printf("Extension version check failed: %v", err)
return err
}
// -- ensureLatestExtensionVersion only prints:
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("A newer version of gh-aw is available: %s (current: %s)", latestVersion, currentVersion)))
fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Consider upgrading with: gh extension upgrade github/gh-aw"))
4. The compiler is created with the stale version:
compiler := createAndConfigureCompiler(CompileConfig{
Verbose: verbose,
WorkflowDir: workflowDir,
})
// ...
func NewCompiler(opts ...CompilerOption) *Compiler {
// Get default version
version := defaultVersion
...
c := &Compiler{
...
version: version,
5. The old version is written to lock file headers and metadata:
if IsReleasedVersion(c.version) {
fmt.Fprintf(yaml, " GH_AW_INFO_CLI_VERSION: \"%s\"\n", c.version)
}
// And in comments:
if IsReleasedVersion(GetVersion()) {
fmt.Fprintf(&header, "# This file was automatically generated by %s (%s). DO NOT EDIT.\n", generatedBy, GetVersion())
}
Steps to Reproduce
- Install
gh-aw v0.56.0.
- Run
gh aw upgrade (or use the automatic PR/CI upgrade workflow).
- Lock files generated after the upgrade still reference v0.56.0, even if
gh aw version now reports v0.57.0 in a new terminal.
Expected Behavior
- After upgrading to a new
gh-aw version, any lock files (and other files with metadata) generated as part of that upgrade should reference the new version, not the previous one.
Actual Behavior
- The files are generated with the version string corresponding to the binary that started the upgrade, not the current version after upgrade.
Root Cause Summary
The Go binary loads the version string once at process startup via linker flags. gh aw upgrade does not re-exec itself after extension upgrade, so all regeneration in that invocation uses the old version. The new version is picked up only in subsequent invocations of the CLI. There are also two version variables (compilerVersion, defaultVersion) in play which are not always synchronized.
Remediation Plan
- Refactor the
gh aw upgrade (and related) workflows so that after a successful extension upgrade, the process re-executes itself to continue further operations (regeneration, lock files etc.) under the correct binary.
- Consider a pattern where, upon detecting and completing a successful extension update, the
gh aw process either stops with a clear message ("upgrade complete, please re-run the command") or, if possible, auto-spawns the new binary and continues.
- Ensure that both
compilerVersion and defaultVersion are kept in sync.
- Consider adding smoke-tests or post-upgrade checks to validate the version in lock files matches the current CLI version used.
- Document in upgrade and contributing docs that lockfile regeneration with the new version only occurs after fresh process invocation.
Context
- Observed in v0.56.0→v0.57.0 but likely affects all versions.
- See CONTRIBUTING.md
Root Cause supplied below (for best-practice agentic debugging):
The bug is that gh aw upgrade performs lock file compilation inside the same process that started with the old binary's baked-in version, and nothing in the upgrade flow updates workflow.compilerVersion (or workflow.defaultVersion) to reflect the new version. The extension binary on disk may be v0.57.0 after gh extension upgrade runs, but the currently executing Go process has no mechanism to discover or apply that new version string to subsequent compilation steps.
Problem
When running
gh aw upgradeto update the CLI extension (e.g., from v0.56.0 to v0.57.0), the lock files generated by the upgrade (or subsequent compile) continue to reference the previous (old) CLI version in metadata and workflow headers (e.g., v0.56.0) even though the CLI itself was upgraded to v0.57.0 during this operation.This results in
.lock.ymlfiles and generated YAML referencing the wrong (stale) version, causing confusion and potential future automation or compatibility issues.Root Cause Analysis
gh-awprocess.gh aw upgrade(andcompile, etc.) propagates the version number from Go build-time linker flags and from variables initialized at the start of process execution.Direct Code References
1. The build-time version is only set at process start:
2. The main() entrypoint populates global version variables ONCE:
3. The upgrade command only prints a warning, doesn't switch process:
4. The compiler is created with the stale version:
5. The old version is written to lock file headers and metadata:
Steps to Reproduce
gh-awv0.56.0.gh aw upgrade(or use the automatic PR/CI upgrade workflow).gh aw versionnow reports v0.57.0 in a new terminal.Expected Behavior
gh-awversion, any lock files (and other files with metadata) generated as part of that upgrade should reference the new version, not the previous one.Actual Behavior
Root Cause Summary
The Go binary loads the version string once at process startup via linker flags.
gh aw upgradedoes not re-exec itself after extension upgrade, so all regeneration in that invocation uses the old version. The new version is picked up only in subsequent invocations of the CLI. There are also two version variables (compilerVersion,defaultVersion) in play which are not always synchronized.Remediation Plan
gh aw upgrade(and related) workflows so that after a successful extension upgrade, the process re-executes itself to continue further operations (regeneration, lock files etc.) under the correct binary.gh awprocess either stops with a clear message ("upgrade complete, please re-run the command") or, if possible, auto-spawns the new binary and continues.compilerVersionanddefaultVersionare kept in sync.Context
Root Cause supplied below (for best-practice agentic debugging):