@@ -0,0 +1,95 @@
+package config
+
+import (
+ "io"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestAttributionMigration(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ configJSON string
+ expectedTrailer TrailerStyle
+ expectedGenerate bool
+ }{
+ {
+ name: "old setting co_authored_by=true migrates to co-authored-by",
+ configJSON: `{
+ "options": {
+ "attribution": {
+ "co_authored_by": true,
+ "generated_with": false
+ }
+ }
+ }`,
+ expectedTrailer: TrailerStyleCoAuthoredBy,
+ expectedGenerate: false,
+ },
+ {
+ name: "old setting co_authored_by=false migrates to none",
+ configJSON: `{
+ "options": {
+ "attribution": {
+ "co_authored_by": false,
+ "generated_with": true
+ }
+ }
+ }`,
+ expectedTrailer: TrailerStyleNone,
+ expectedGenerate: true,
+ },
+ {
+ name: "new setting takes precedence over old setting",
+ configJSON: `{
+ "options": {
+ "attribution": {
+ "trailer_style": "assisted-by",
+ "co_authored_by": true,
+ "generated_with": false
+ }
+ }
+ }`,
+ expectedTrailer: TrailerStyleAssistedBy,
+ expectedGenerate: false,
+ },
+ {
+ name: "default when neither setting present",
+ configJSON: `{
+ "options": {
+ "attribution": {
+ "generated_with": true
+ }
+ }
+ }`,
+ expectedTrailer: TrailerStyleCoAuthoredBy,
+ expectedGenerate: true,
+ },
+ {
+ name: "default when attribution is null",
+ configJSON: `{
+ "options": {}
+ }`,
+ expectedTrailer: TrailerStyleCoAuthoredBy,
+ expectedGenerate: true,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+
+ cfg, err := loadFromReaders([]io.Reader{strings.NewReader(tt.configJSON)})
+ require.NoError(t, err)
+
+ cfg.setDefaults(t.TempDir(), "")
+
+ require.Equal(t, tt.expectedTrailer, cfg.Options.Attribution.TrailerStyle)
+ require.Equal(t, tt.expectedGenerate, cfg.Options.Attribution.GeneratedWith)
+ })
+ }
+}
@@ -14,6 +14,7 @@ import (
"github.com/charmbracelet/catwalk/pkg/catwalk"
"github.com/charmbracelet/crush/internal/csync"
"github.com/charmbracelet/crush/internal/env"
+ "github.com/invopop/jsonschema"
"github.com/tidwall/sjson"
)
@@ -176,9 +177,19 @@ const (
type Attribution struct {
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"`
+ CoAuthoredBy *bool `json:"co_authored_by,omitempty" jsonschema:"description=Deprecated: use trailer_style instead"`
GeneratedWith bool `json:"generated_with,omitempty" jsonschema:"description=Add Generated with Crush line to commit messages and issues and PRs,default=true"`
}
+// JSONSchemaExtend marks the co_authored_by field as deprecated in the schema.
+func (Attribution) JSONSchemaExtend(schema *jsonschema.Schema) {
+ if schema.Properties != nil {
+ if prop, ok := schema.Properties.Get("co_authored_by"); ok {
+ prop.Deprecated = true
+ }
+ }
+}
+
type Options struct {
ContextPaths []string `json:"context_paths,omitempty" jsonschema:"description=Paths to files containing context information for the AI,example=.cursorrules,example=CRUSH.md"`
TUI *TUIOptions `json:"tui,omitempty" jsonschema:"description=Terminal user interface options"`
@@ -356,6 +356,13 @@ func (c *Config) setDefaults(workingDir, dataDir string) {
TrailerStyle: TrailerStyleCoAuthoredBy,
GeneratedWith: true,
}
+ } else if c.Options.Attribution.TrailerStyle == "" {
+ // Migrate deprecated co_authored_by or apply default
+ if c.Options.Attribution.CoAuthoredBy != nil && !*c.Options.Attribution.CoAuthoredBy {
+ c.Options.Attribution.TrailerStyle = TrailerStyleNone
+ } else {
+ c.Options.Attribution.TrailerStyle = TrailerStyleCoAuthoredBy
+ }
}
}
@@ -15,6 +15,11 @@
"description": "Style of attribution trailer to add to commits",
"default": "co-authored-by"
},
+ "co_authored_by": {
+ "type": "boolean",
+ "description": "Deprecated: use trailer_style instead",
+ "deprecated": true
+ },
"generated_with": {
"type": "boolean",
"description": "Add Generated with Crush line to commit messages and issues and PRs",