Skip to content

Commit a3c6b53

Browse files
committed
feat: add attribution config support, deprecate includeCoAuthoredBy
Add support for the new 'attribution' setting that replaces the deprecated 'includeCoAuthoredBy' parameter. The new format allows custom attribution strings for commits and PRs, with empty strings hiding attribution entirely. The deprecated parameter is converted to the new format for backwards compatibility: false -> hide all, true -> use defaults.
1 parent 9469877 commit a3c6b53

File tree

2 files changed

+151
-5
lines changed

2 files changed

+151
-5
lines changed

scripts/setup_environment.py

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3249,6 +3249,7 @@ def create_additional_settings(
32493249
include_co_authored_by: bool | None = None,
32503250
always_thinking_enabled: bool | None = None,
32513251
company_announcements: list[str] | None = None,
3252+
attribution: dict[str, str] | None = None,
32523253
) -> bool:
32533254
"""Create {command_name}-additional-settings.json with environment-specific settings.
32543255
@@ -3262,9 +3263,12 @@ def create_additional_settings(
32623263
model: Optional model alias or custom model name
32633264
permissions: Optional permissions configuration dict
32643265
env: Optional environment variables dict
3265-
include_co_authored_by: Optional flag to include co-authored-by in commits
3266+
include_co_authored_by: DEPRECATED - Optional flag to include co-authored-by in commits.
3267+
Use 'attribution' parameter instead.
32663268
always_thinking_enabled: Optional flag to enable always-on thinking mode
32673269
company_announcements: Optional list of company announcement strings
3270+
attribution: Optional dict with 'commit' and 'pr' keys for custom attribution strings.
3271+
Empty strings hide attribution. Takes precedence over include_co_authored_by.
32683272
32693273
Returns:
32703274
bool: True if successful, False otherwise.
@@ -3298,10 +3302,24 @@ def create_additional_settings(
32983302
for key in env:
32993303
info(f' - {key}')
33003304

3301-
# Add includeCoAuthoredBy if explicitly set (None means not configured, leave as default)
3302-
if include_co_authored_by is not None:
3303-
settings['includeCoAuthoredBy'] = include_co_authored_by
3304-
info(f'Setting includeCoAuthoredBy: {include_co_authored_by}')
3305+
# Handle attribution settings (new format takes precedence)
3306+
if attribution is not None:
3307+
settings['attribution'] = attribution
3308+
commit_preview = repr(attribution.get('commit', ''))[:30]
3309+
pr_preview = repr(attribution.get('pr', ''))[:30]
3310+
info(f'Setting attribution: commit={commit_preview}, pr={pr_preview}')
3311+
if include_co_authored_by is not None:
3312+
warning('Both "attribution" and deprecated "include-co-authored-by" specified. Using "attribution".')
3313+
elif include_co_authored_by is not None:
3314+
# DEPRECATED: Convert to new format
3315+
warning('Config key "include-co-authored-by" is deprecated. Use "attribution" instead.')
3316+
if include_co_authored_by is False:
3317+
# false -> hide attribution
3318+
settings['attribution'] = {'commit': '', 'pr': ''}
3319+
info('Setting attribution: hiding all (converted from include-co-authored-by: false)')
3320+
# Note: true -> don't set anything, let Claude Code use defaults
3321+
else:
3322+
info('include-co-authored-by: true -> using Claude Code defaults (no attribution override)')
33053323

33063324
# Add alwaysThinkingEnabled if explicitly set (None means not configured, leave as default)
33073325
if always_thinking_enabled is not None:
@@ -4126,6 +4144,9 @@ def main() -> None:
41264144
# Extract company_announcements configuration
41274145
company_announcements = config.get('company-announcements')
41284146

4147+
# Extract attribution configuration (new format, takes precedence over include-co-authored-by)
4148+
attribution = config.get('attribution')
4149+
41294150
# Extract claude-code-version configuration
41304151
claude_code_version = config.get('claude-code-version')
41314152
claude_code_version_normalized = None # Default to latest
@@ -4305,6 +4326,7 @@ def main() -> None:
43054326
include_co_authored_by,
43064327
always_thinking_enabled,
43074328
company_announcements,
4329+
attribution,
43084330
)
43094331

43104332
# Step 14: Create launcher script

tests/test_setup_environment.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,6 +1135,130 @@ def test_create_additional_settings_company_announcements_none_not_included(self
11351135

11361136
assert 'companyAnnouncements' not in settings
11371137

1138+
def test_create_additional_settings_attribution_full(self):
1139+
"""Test attribution with both commit and pr values."""
1140+
with tempfile.TemporaryDirectory() as tmpdir:
1141+
claude_dir = Path(tmpdir)
1142+
attribution = {
1143+
'commit': 'Custom commit attribution',
1144+
'pr': 'Custom PR attribution',
1145+
}
1146+
1147+
result = setup_environment.create_additional_settings(
1148+
{},
1149+
claude_dir,
1150+
'test-env',
1151+
attribution=attribution,
1152+
)
1153+
1154+
assert result is True
1155+
settings_file = claude_dir / 'test-env-additional-settings.json'
1156+
settings = json.loads(settings_file.read_text())
1157+
1158+
assert 'attribution' in settings
1159+
assert settings['attribution']['commit'] == 'Custom commit attribution'
1160+
assert settings['attribution']['pr'] == 'Custom PR attribution'
1161+
1162+
def test_create_additional_settings_attribution_hide_all(self):
1163+
"""Test attribution with empty strings to hide all."""
1164+
with tempfile.TemporaryDirectory() as tmpdir:
1165+
claude_dir = Path(tmpdir)
1166+
attribution = {'commit': '', 'pr': ''}
1167+
1168+
result = setup_environment.create_additional_settings(
1169+
{},
1170+
claude_dir,
1171+
'test-env',
1172+
attribution=attribution,
1173+
)
1174+
1175+
assert result is True
1176+
settings_file = claude_dir / 'test-env-additional-settings.json'
1177+
settings = json.loads(settings_file.read_text())
1178+
1179+
assert settings['attribution'] == {'commit': '', 'pr': ''}
1180+
1181+
def test_create_additional_settings_attribution_precedence_over_deprecated(self):
1182+
"""Test attribution takes precedence over deprecated include_co_authored_by."""
1183+
with tempfile.TemporaryDirectory() as tmpdir:
1184+
claude_dir = Path(tmpdir)
1185+
attribution = {'commit': 'New format', 'pr': 'New format'}
1186+
1187+
result = setup_environment.create_additional_settings(
1188+
{},
1189+
claude_dir,
1190+
'test-env',
1191+
include_co_authored_by=False, # This should be ignored
1192+
attribution=attribution,
1193+
)
1194+
1195+
assert result is True
1196+
settings_file = claude_dir / 'test-env-additional-settings.json'
1197+
settings = json.loads(settings_file.read_text())
1198+
1199+
assert 'attribution' in settings
1200+
assert settings['attribution'] == attribution
1201+
assert 'includeCoAuthoredBy' not in settings
1202+
1203+
def test_create_additional_settings_deprecated_include_co_authored_by_false(self):
1204+
"""Test deprecated include_co_authored_by=False converts to attribution."""
1205+
with tempfile.TemporaryDirectory() as tmpdir:
1206+
claude_dir = Path(tmpdir)
1207+
1208+
result = setup_environment.create_additional_settings(
1209+
{},
1210+
claude_dir,
1211+
'test-env',
1212+
include_co_authored_by=False,
1213+
)
1214+
1215+
assert result is True
1216+
settings_file = claude_dir / 'test-env-additional-settings.json'
1217+
settings = json.loads(settings_file.read_text())
1218+
1219+
# Should be converted to new format
1220+
assert 'attribution' in settings
1221+
assert settings['attribution'] == {'commit': '', 'pr': ''}
1222+
assert 'includeCoAuthoredBy' not in settings
1223+
1224+
def test_create_additional_settings_deprecated_include_co_authored_by_true(self):
1225+
"""Test deprecated include_co_authored_by=True does not override defaults."""
1226+
with tempfile.TemporaryDirectory() as tmpdir:
1227+
claude_dir = Path(tmpdir)
1228+
1229+
result = setup_environment.create_additional_settings(
1230+
{},
1231+
claude_dir,
1232+
'test-env',
1233+
include_co_authored_by=True,
1234+
)
1235+
1236+
assert result is True
1237+
settings_file = claude_dir / 'test-env-additional-settings.json'
1238+
settings = json.loads(settings_file.read_text())
1239+
1240+
# true -> use defaults, so neither key should be set
1241+
assert 'attribution' not in settings
1242+
assert 'includeCoAuthoredBy' not in settings
1243+
1244+
def test_create_additional_settings_attribution_none_not_included(self):
1245+
"""Test attribution not included when None."""
1246+
with tempfile.TemporaryDirectory() as tmpdir:
1247+
claude_dir = Path(tmpdir)
1248+
1249+
result = setup_environment.create_additional_settings(
1250+
{},
1251+
claude_dir,
1252+
'test-env',
1253+
attribution=None,
1254+
)
1255+
1256+
assert result is True
1257+
settings_file = claude_dir / 'test-env-additional-settings.json'
1258+
settings = json.loads(settings_file.read_text())
1259+
1260+
assert 'attribution' not in settings
1261+
11381262

11391263
class TestCreateLauncherScript:
11401264
"""Test launcher script creation."""

0 commit comments

Comments
 (0)