Repository: score-spec/score-compose (go)

Issue:
[bug] annotations lost from workload when workload is re-added
Given a score workload like:

```
apiVersion: score.dev/v1b1
metadata:
  name: foo
  annotations:
    example.com/thing: foo
containers:
  main:
    image: nginx:latest
```

Generating this once with an existing score file yields:

```
name: temp14
services:
    foo-main:
        annotations:
            compose.score.dev/workload-name: foo
            example.com/thing: foo
        hostname: foo
        image: nginx:latest
```

However, adding another copy of it returns

```
name: temp14
services:
    foo-main:
        annotations:
            compose.score.dev/workload-name: foo
        hostname: foo
        image: nginx:latest
    foo2-main:
        annotations:
            compose.score.dev/workload-name: foo2
            example.com/thing: foo
        hostname: foo2
        image: nginx:latest
```

Note the annotation removed from the source.

The state file contains:

```
workloads:
  foo:
    spec:
      apiVersion: score.dev/v1b1
      containers:
        main:
          image: nginx:latest
      metadata:
        annotations:
          example.com/thing: foo
        name: foo
    file: score.yaml
  foo2:
    spec:
      apiVersion: score.dev/v1b1
      containers:
        main:
          image: nginx:latest
      metadata:
        annotations:
          example.com/thing: foo
        name: foo2
    file: score.yaml2
resources: {}
shared_state: {}
compose_project: temp14
mounts_directory: .score-compose/mounts
```

So the annotation should be there.

Pull request:
fix(convert): fix missing annotations on state load
Fixes #135.


Patch:
diff --git a/internal/compose/convert.go b/internal/compose/convert.go
index d02bc44..71128e7 100644
--- a/internal/compose/convert.go
+++ b/internal/compose/convert.go
@@ -174,7 +174,11 @@ func ConvertSpec(state *project.State, spec *score.Workload) (*compose.Project,
 // buildWorkloadAnnotations returns an annotation set for the workload service.
 func buildWorkloadAnnotations(name string, spec *score.Workload) map[string]string {
 	var out map[string]string
-	if a, ok := spec.Metadata["annotations"].(map[string]interface{}); ok {
+	a, ok := spec.Metadata["annotations"].(map[string]interface{})
+	if !ok {
+		a, ok = spec.Metadata["annotations"].(score.WorkloadMetadata)
+	}
+	if ok {
 		out = make(map[string]string, len(a))
 		for k, v := range a {
 			// type is validated by the spec


Test patch:
diff --git a/internal/command/generate_test.go b/internal/command/generate_test.go
index 2a5c86f..828f319 100644
--- a/internal/command/generate_test.go
+++ b/internal/command/generate_test.go
@@ -1194,3 +1194,35 @@ resources:
 		assert.NoError(t, cmd.Run())
 	})
 }
+
+func TestGenerateKeepAnnotations(t *testing.T) {
+	td := changeToTempDir(t)
+	stdout, _, err := executeAndResetCommand(context.Background(), rootCmd, []string{"init"})
+	assert.NoError(t, err)
+	assert.Equal(t, "", stdout)
+	assert.NoError(t, os.WriteFile(filepath.Join(td, "score.yaml"), []byte(`
+apiVersion: score.dev/v1b1
+metadata:
+  name: example
+  annotations:
+    example.com/fizz: buzz
+containers:
+  example:
+    image: foo
+`), 0644))
+	stdout, _, err = executeAndResetCommand(context.Background(), rootCmd, []string{"generate", "score.yaml"})
+	assert.NoError(t, err)
+	assert.Equal(t, "", stdout)
+
+	raw, err := os.ReadFile(filepath.Join(td, "compose.yaml"))
+	assert.NoError(t, err)
+	assert.Contains(t, string(raw), `example.com/fizz: buzz`)
+
+	stdout, _, err = executeAndResetCommand(context.Background(), rootCmd, []string{"generate"})
+	assert.NoError(t, err)
+	assert.Equal(t, "", stdout)
+
+	raw, err = os.ReadFile(filepath.Join(td, "compose.yaml"))
+	assert.NoError(t, err)
+	assert.Contains(t, string(raw), `example.com/fizz: buzz`)
+}