diff --git a/pkg/workflow/compiler_yaml_test.go b/pkg/workflow/compiler_yaml_test.go index 7944031bb8..2b2e1af3da 100644 --- a/pkg/workflow/compiler_yaml_test.go +++ b/pkg/workflow/compiler_yaml_test.go @@ -1237,3 +1237,107 @@ This is a test workflow.` }) } } + +func TestLockMetadataVersionInReleaseBuilds(t *testing.T) { + // Save and restore original values + originalIsRelease := isReleaseBuild + originalVersion := compilerVersion + defer func() { + isReleaseBuild = originalIsRelease + compilerVersion = originalVersion + }() + + tmpDir := testutil.TempDir(t, "lock-metadata-version") + + // Test both dev and release modes + tests := []struct { + name string + isRelease bool + version string + expectVersion bool + }{ + { + name: "dev build should not include version", + isRelease: false, + version: "dev", + expectVersion: false, + }, + { + name: "release build should include version", + isRelease: true, + version: "v0.1.2", + expectVersion: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Set version and release flag + SetIsRelease(tt.isRelease) + SetVersion(tt.version) + + // Create a simple workflow + workflowContent := `--- +engine: copilot +on: issues +--- +# Test Workflow + +Test prompt. +` + workflowPath := filepath.Join(tmpDir, tt.name+".md") + if err := os.WriteFile(workflowPath, []byte(workflowContent), 0o644); err != nil { + t.Fatalf("Failed to write workflow file: %v", err) + } + + // Compile the workflow + compiler := NewCompiler() + err := compiler.CompileWorkflow(workflowPath) + if err != nil { + t.Fatalf("Failed to compile workflow: %v", err) + } + + // Read the lock file + lockFile := strings.TrimSuffix(workflowPath, ".md") + ".lock.yml" + lockContent, err := os.ReadFile(lockFile) + if err != nil { + t.Fatalf("Failed to read lock file: %v", err) + } + + lockContentStr := string(lockContent) + + // Extract metadata line + metadataLine := "" + lines := strings.Split(lockContentStr, "\n") + for _, line := range lines { + if strings.Contains(line, "gh-aw-metadata:") { + metadataLine = line + break + } + } + + if metadataLine == "" { + t.Fatal("Could not find gh-aw-metadata in lock file") + } + + // Check if version is present + hasVersion := strings.Contains(metadataLine, `"compiler_version"`) + + if tt.expectVersion && !hasVersion { + t.Errorf("Expected version to be included in metadata for release build, but it was not found.\nMetadata: %s", metadataLine) + } + + if !tt.expectVersion && hasVersion { + t.Errorf("Expected version to NOT be included in metadata for dev build, but it was found.\nMetadata: %s", metadataLine) + } + + // If version is expected, verify it matches + if tt.expectVersion && hasVersion { + expectedVersionStr := fmt.Sprintf(`"compiler_version":"%s"`, tt.version) + if !strings.Contains(metadataLine, expectedVersionStr) { + t.Errorf("Expected version '%s' to be in metadata, but got:\n%s", tt.version, metadataLine) + } + } + }) + } +} diff --git a/pkg/workflow/lock_schema.go b/pkg/workflow/lock_schema.go index 339108dda2..d21a03aeb2 100644 --- a/pkg/workflow/lock_schema.go +++ b/pkg/workflow/lock_schema.go @@ -24,6 +24,7 @@ type LockMetadata struct { SchemaVersion LockSchemaVersion `json:"schema_version"` FrontmatterHash string `json:"frontmatter_hash,omitempty"` StopTime string `json:"stop_time,omitempty"` + CompilerVersion string `json:"compiler_version,omitempty"` } // SupportedSchemaVersions lists all schema versions this build can consume @@ -115,12 +116,20 @@ func formatSupportedVersions() string { } // GenerateLockMetadata creates a LockMetadata struct for embedding in lock files +// For release builds, the compiler version is included in the metadata func GenerateLockMetadata(frontmatterHash string, stopTime string) *LockMetadata { - return &LockMetadata{ + metadata := &LockMetadata{ SchemaVersion: LockSchemaV1, FrontmatterHash: frontmatterHash, StopTime: stopTime, } + + // Include compiler version only for release builds + if IsRelease() { + metadata.CompilerVersion = GetVersion() + } + + return metadata } // ToJSON converts LockMetadata to a compact JSON string for embedding in comments diff --git a/pkg/workflow/lock_schema_test.go b/pkg/workflow/lock_schema_test.go index c1c790435a..0f3d926c2e 100644 --- a/pkg/workflow/lock_schema_test.go +++ b/pkg/workflow/lock_schema_test.go @@ -80,6 +80,19 @@ name: test expectLegacy: false, expectError: false, }, + { + name: "metadata with compiler version", + content: `# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"abc123","compiler_version":"v0.1.2"} +name: test +`, + expectMetadata: &LockMetadata{ + SchemaVersion: LockSchemaV1, + FrontmatterHash: "abc123", + CompilerVersion: "v0.1.2", + }, + expectLegacy: false, + expectError: false, + }, } for _, tt := range tests { @@ -98,6 +111,7 @@ name: test require.NotNil(t, metadata, "Expected metadata to be parsed") assert.Equal(t, tt.expectMetadata.SchemaVersion, metadata.SchemaVersion, "Schema version mismatch") assert.Equal(t, tt.expectMetadata.FrontmatterHash, metadata.FrontmatterHash, "Frontmatter hash mismatch") + assert.Equal(t, tt.expectMetadata.CompilerVersion, metadata.CompilerVersion, "Compiler version mismatch") } else if !tt.expectError { assert.Nil(t, metadata, "Expected nil metadata") } @@ -211,6 +225,40 @@ func TestIsSchemaVersionSupported(t *testing.T) { } func TestGenerateLockMetadata(t *testing.T) { + // Save and restore original values + originalIsRelease := isReleaseBuild + originalVersion := compilerVersion + defer func() { + isReleaseBuild = originalIsRelease + compilerVersion = originalVersion + }() + + // Test dev build (default) + SetIsRelease(false) + SetVersion("dev") + hash := "abcd1234" + stopTime := "2026-02-17 20:00:00" + metadata := GenerateLockMetadata(hash, stopTime) + + assert.NotNil(t, metadata, "Metadata should be created") + assert.Equal(t, LockSchemaV1, metadata.SchemaVersion, "Should use current schema version") + assert.Equal(t, hash, metadata.FrontmatterHash, "Should preserve frontmatter hash") + assert.Equal(t, stopTime, metadata.StopTime, "Should preserve stop time") + assert.Empty(t, metadata.CompilerVersion, "Dev builds should not include version") +} + +func TestGenerateLockMetadataReleaseBuild(t *testing.T) { + // Save and restore original values + originalIsRelease := isReleaseBuild + originalVersion := compilerVersion + defer func() { + isReleaseBuild = originalIsRelease + compilerVersion = originalVersion + }() + + // Test release build + SetIsRelease(true) + SetVersion("v0.1.2") hash := "abcd1234" stopTime := "2026-02-17 20:00:00" metadata := GenerateLockMetadata(hash, stopTime) @@ -219,6 +267,7 @@ func TestGenerateLockMetadata(t *testing.T) { assert.Equal(t, LockSchemaV1, metadata.SchemaVersion, "Should use current schema version") assert.Equal(t, hash, metadata.FrontmatterHash, "Should preserve frontmatter hash") assert.Equal(t, stopTime, metadata.StopTime, "Should preserve stop time") + assert.Equal(t, "v0.1.2", metadata.CompilerVersion, "Release builds should include version") } func TestGenerateLockMetadataWithoutStopTime(t *testing.T) { @@ -258,6 +307,19 @@ func TestLockMetadataToJSON(t *testing.T) { `"schema_version":"v1"`, }, }, + { + name: "metadata with compiler version", + metadata: &LockMetadata{ + SchemaVersion: LockSchemaV1, + FrontmatterHash: "test123", + CompilerVersion: "v0.1.2", + }, + contains: []string{ + `"schema_version":"v1"`, + `"frontmatter_hash":"test123"`, + `"compiler_version":"v0.1.2"`, + }, + }, } for _, tt := range tests {