on main help sign in
feat(config): migrate deprecated co_authored_by
27093eaAmolith
internal/config/attribution_migration_test.gonew file+95
@@ -0,0 +1,95 @@
1+package config
2+
3+import (
4+ "io"
5+ "strings"
6+ "testing"
7+
8+ "github.com/stretchr/testify/require"
9+)
10+
11+func TestAttributionMigration(t *testing.T) {
12+ t.Parallel()
13+
14+ tests := []struct {
15+ name string
16+ configJSON string
17+ expectedTrailer TrailerStyle
18+ expectedGenerate bool
19+ }{
20+ {
21+ name: "old setting co_authored_by=true migrates to co-authored-by",
22+ configJSON: `{
23+ "options": {
24+ "attribution": {
25+ "co_authored_by": true,
26+ "generated_with": false
27+ }
28+ }
29+ }`,
30+ expectedTrailer: TrailerStyleCoAuthoredBy,
31+ expectedGenerate: false,
32+ },
33+ {
34+ name: "old setting co_authored_by=false migrates to none",
35+ configJSON: `{
36+ "options": {
37+ "attribution": {
38+ "co_authored_by": false,
39+ "generated_with": true
40+ }
41+ }
42+ }`,
43+ expectedTrailer: TrailerStyleNone,
44+ expectedGenerate: true,
45+ },
46+ {
47+ name: "new setting takes precedence over old setting",
48+ configJSON: `{
49+ "options": {
50+ "attribution": {
51+ "trailer_style": "assisted-by",
52+ "co_authored_by": true,
53+ "generated_with": false
54+ }
55+ }
56+ }`,
57+ expectedTrailer: TrailerStyleAssistedBy,
58+ expectedGenerate: false,
59+ },
60+ {
61+ name: "default when neither setting present",
62+ configJSON: `{
63+ "options": {
64+ "attribution": {
65+ "generated_with": true
66+ }
67+ }
68+ }`,
69+ expectedTrailer: TrailerStyleCoAuthoredBy,
70+ expectedGenerate: true,
71+ },
72+ {
73+ name: "default when attribution is null",
74+ configJSON: `{
75+ "options": {}
76+ }`,
77+ expectedTrailer: TrailerStyleCoAuthoredBy,
78+ expectedGenerate: true,
79+ },
80+ }
81+
82+ for _, tt := range tests {
83+ t.Run(tt.name, func(t *testing.T) {
84+ t.Parallel()
85+
86+ cfg, err := loadFromReaders([]io.Reader{strings.NewReader(tt.configJSON)})
87+ require.NoError(t, err)
88+
89+ cfg.setDefaults(t.TempDir(), "")
90+
91+ require.Equal(t, tt.expectedTrailer, cfg.Options.Attribution.TrailerStyle)
92+ require.Equal(t, tt.expectedGenerate, cfg.Options.Attribution.GeneratedWith)
93+ })
94+ }
95+}
internal/config/config.go+11
@@ -14,6 +14,7 @@ import (
14 "github.com/charmbracelet/catwalk/pkg/catwalk"
14 "github.com/charmbracelet/catwalk/pkg/catwalk"
15 "github.com/charmbracelet/crush/internal/csync"
15 "github.com/charmbracelet/crush/internal/csync"
16 "github.com/charmbracelet/crush/internal/env"
16 "github.com/charmbracelet/crush/internal/env"
17+ "github.com/invopop/jsonschema"
17 "github.com/tidwall/sjson"
18 "github.com/tidwall/sjson"
18)
19)
19
20
@@ -176,9 +177,19 @@ const (
176
177
177type Attribution struct {
178type Attribution struct {
178 TrailerStyle TrailerStyle `json:"trailer_style,omitempty" jsonschema:"description=Style of attribution trailer to add to commits,enum=none,enum=co-authored-by,enum=assisted-by,default=co-authored-by"`
179 TrailerStyle TrailerStyle `json:"trailer_style,omitempty" jsonschema:"description=Style of attribution trailer to add to commits,enum=none,enum=co-authored-by,enum=assisted-by,default=co-authored-by"`
180+ CoAuthoredBy *bool `json:"co_authored_by,omitempty" jsonschema:"description=Deprecated: use trailer_style instead"`
179 GeneratedWith bool `json:"generated_with,omitempty" jsonschema:"description=Add Generated with Crush line to commit messages and issues and PRs,default=true"`
181 GeneratedWith bool `json:"generated_with,omitempty" jsonschema:"description=Add Generated with Crush line to commit messages and issues and PRs,default=true"`
180}
182}
181
183
184+// JSONSchemaExtend marks the co_authored_by field as deprecated in the schema.
185+func (Attribution) JSONSchemaExtend(schema *jsonschema.Schema) {
186+ if schema.Properties != nil {
187+ if prop, ok := schema.Properties.Get("co_authored_by"); ok {
188+ prop.Deprecated = true
189+ }
190+ }
191+}
192+
182type Options struct {
193type Options struct {
183 ContextPaths []string `json:"context_paths,omitempty" jsonschema:"description=Paths to files containing context information for the AI,example=.cursorrules,example=CRUSH.md"`
194 ContextPaths []string `json:"context_paths,omitempty" jsonschema:"description=Paths to files containing context information for the AI,example=.cursorrules,example=CRUSH.md"`
184 TUI *TUIOptions `json:"tui,omitempty" jsonschema:"description=Terminal user interface options"`
195 TUI *TUIOptions `json:"tui,omitempty" jsonschema:"description=Terminal user interface options"`
internal/config/load.go+7
@@ -356,6 +356,13 @@ func (c *Config) setDefaults(workingDir, dataDir string) {
356 TrailerStyle: TrailerStyleCoAuthoredBy,
356 TrailerStyle: TrailerStyleCoAuthoredBy,
357 GeneratedWith: true,
357 GeneratedWith: true,
358 }
358 }
359+ } else if c.Options.Attribution.TrailerStyle == "" {
360+ // Migrate deprecated co_authored_by or apply default
361+ if c.Options.Attribution.CoAuthoredBy != nil && !*c.Options.Attribution.CoAuthoredBy {
362+ c.Options.Attribution.TrailerStyle = TrailerStyleNone
363+ } else {
364+ c.Options.Attribution.TrailerStyle = TrailerStyleCoAuthoredBy
365+ }
359 }
366 }
360}
367}
361
368
schema.json+5
@@ -15,6 +15,11 @@
15 "description": "Style of attribution trailer to add to commits",
15 "description": "Style of attribution trailer to add to commits",
16 "default": "co-authored-by"
16 "default": "co-authored-by"
17 },
17 },
18+ "co_authored_by": {
19+ "type": "boolean",
20+ "description": "Deprecated: use trailer_style instead",
21+ "deprecated": true
22+ },
18 "generated_with": {
23 "generated_with": {
19 "type": "boolean",
24 "type": "boolean",
20 "description": "Add Generated with Crush line to commit messages and issues and PRs",
25 "description": "Add Generated with Crush line to commit messages and issues and PRs",