Coverage for src/workstack/cli/commands/config.py: 89%

132 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-10-19 09:31 -0400

1from pathlib import Path 

2 

3import click 

4 

5from workstack.cli.config import LoadedConfig, load_config 

6from workstack.cli.core import discover_repo_context, ensure_workstacks_dir 

7from workstack.core.context import WorkstackContext 

8 

9 

10def _get_env_value(cfg: LoadedConfig, parts: list[str], key: str) -> None: 

11 """Handle env.* configuration keys. 

12 

13 Prints the value or exits with error if key not found. 

14 """ 

15 if len(parts) != 2: 

16 click.echo(f"Invalid key: {key}", err=True) 

17 raise SystemExit(1) 

18 

19 if parts[1] not in cfg.env: 

20 click.echo(f"Key not found: {key}", err=True) 

21 raise SystemExit(1) 

22 

23 click.echo(cfg.env[parts[1]]) 

24 

25 

26def _get_post_create_value(cfg: LoadedConfig, parts: list[str], key: str) -> None: 

27 """Handle post_create.* configuration keys. 

28 

29 Prints the value or exits with error if key not found. 

30 """ 

31 if len(parts) != 2: 

32 click.echo(f"Invalid key: {key}", err=True) 

33 raise SystemExit(1) 

34 

35 # Handle shell subkey 

36 if parts[1] == "shell": 

37 if not cfg.post_create_shell: 

38 click.echo(f"Key not found: {key}", err=True) 

39 raise SystemExit(1) 

40 click.echo(cfg.post_create_shell) 

41 return 

42 

43 # Handle commands subkey 

44 if parts[1] == "commands": 

45 for cmd in cfg.post_create_commands: 

46 click.echo(cmd) 

47 return 

48 

49 # Unknown subkey 

50 click.echo(f"Key not found: {key}", err=True) 

51 raise SystemExit(1) 

52 

53 

54@click.group("config") 

55def config_group() -> None: 

56 """Manage workstack configuration.""" 

57 

58 

59@config_group.command("list") 

60@click.pass_obj 

61def config_list(ctx: WorkstackContext) -> None: 

62 """Print a list of configuration keys and values.""" 

63 # Try to load global config 

64 try: 

65 workstacks_root = ctx.global_config_ops.get_workstacks_root() 

66 use_graphite = ctx.global_config_ops.get_use_graphite() 

67 show_pr_info = ctx.global_config_ops.get_show_pr_info() 

68 show_pr_checks = ctx.global_config_ops.get_show_pr_checks() 

69 click.echo(click.style("Global configuration:", bold=True)) 

70 click.echo(f" workstacks_root={workstacks_root}") 

71 click.echo(f" use_graphite={str(use_graphite).lower()}") 

72 click.echo(f" show_pr_info={str(show_pr_info).lower()}") 

73 click.echo(f" show_pr_checks={str(show_pr_checks).lower()}") 

74 except FileNotFoundError: 

75 click.echo(click.style("Global configuration:", bold=True)) 

76 click.echo(" (not configured - run 'workstack init' to create)") 

77 

78 # Try to load repo config 

79 try: 

80 repo = discover_repo_context(ctx, Path.cwd()) 

81 workstacks_dir = ensure_workstacks_dir(repo) 

82 cfg = load_config(workstacks_dir) 

83 

84 click.echo(click.style("\nRepository configuration:", bold=True)) 

85 if cfg.env: 

86 for key, value in cfg.env.items(): 

87 click.echo(f" env.{key}={value}") 

88 if cfg.post_create_shell: 

89 click.echo(f" post_create.shell={cfg.post_create_shell}") 

90 if cfg.post_create_commands: 

91 click.echo(f" post_create.commands={cfg.post_create_commands}") 

92 

93 if not cfg.env and not cfg.post_create_shell and not cfg.post_create_commands: 

94 click.echo(" (no configuration - run 'workstack init --repo' to create)") 

95 except Exception: 

96 click.echo(click.style("\nRepository configuration:", bold=True)) 

97 click.echo(" (not in a git repository)") 

98 

99 

100@config_group.command("get") 

101@click.argument("key", metavar="KEY") 

102@click.pass_obj 

103def config_get(ctx: WorkstackContext, key: str) -> None: 

104 """Print the value of a given configuration key.""" 

105 parts = key.split(".") 

106 

107 # Handle global config keys 

108 if parts[0] in ("workstacks_root", "use_graphite", "show_pr_info", "show_pr_checks"): 

109 try: 

110 if parts[0] == "workstacks_root": 

111 click.echo(str(ctx.global_config_ops.get_workstacks_root())) 

112 elif parts[0] == "use_graphite": 

113 click.echo(str(ctx.global_config_ops.get_use_graphite()).lower()) 

114 elif parts[0] == "show_pr_info": 

115 click.echo(str(ctx.global_config_ops.get_show_pr_info()).lower()) 

116 elif parts[0] == "show_pr_checks": 

117 click.echo(str(ctx.global_config_ops.get_show_pr_checks()).lower()) 

118 except FileNotFoundError as e: 

119 click.echo(f"Global config not found at {ctx.global_config_ops.get_path()}", err=True) 

120 raise SystemExit(1) from e 

121 return 

122 

123 # Handle repo config keys 

124 try: 

125 repo = discover_repo_context(ctx, Path.cwd()) 

126 workstacks_dir = ensure_workstacks_dir(repo) 

127 cfg = load_config(workstacks_dir) 

128 

129 if parts[0] == "env": 

130 _get_env_value(cfg, parts, key) 

131 return 

132 

133 if parts[0] == "post_create": 

134 _get_post_create_value(cfg, parts, key) 

135 return 

136 

137 click.echo(f"Invalid key: {key}", err=True) 

138 raise SystemExit(1) 

139 

140 except Exception as e: 

141 if "not in a git repository" in str(e).lower() or "not a git repository" in str(e).lower(): 

142 click.echo("Not in a git repository", err=True) 

143 else: 

144 click.echo(f"Error: {e}", err=True) 

145 raise SystemExit(1) from e 

146 

147 

148@config_group.command("set") 

149@click.argument("key", metavar="KEY") 

150@click.argument("value", metavar="VALUE") 

151@click.pass_obj 

152def config_set(ctx: WorkstackContext, key: str, value: str) -> None: 

153 """Update configuration with a value for the given key.""" 

154 # Parse key into parts 

155 parts = key.split(".") 

156 

157 # Handle global config keys 

158 if parts[0] in ("workstacks_root", "use_graphite", "show_pr_info", "show_pr_checks"): 

159 if not ctx.global_config_ops.exists(): 

160 click.echo(f"Global config not found at {ctx.global_config_ops.get_path()}", err=True) 

161 click.echo("Run 'workstack init' to create it.", err=True) 

162 raise SystemExit(1) 

163 

164 # Update value using set() 

165 if parts[0] == "workstacks_root": 

166 ctx.global_config_ops.set(workstacks_root=Path(value).expanduser().resolve()) 

167 elif parts[0] == "use_graphite": 

168 if value.lower() not in ("true", "false"): 

169 click.echo(f"Invalid boolean value: {value}", err=True) 

170 raise SystemExit(1) 

171 ctx.global_config_ops.set(use_graphite=value.lower() == "true") 

172 elif parts[0] == "show_pr_info": 

173 if value.lower() not in ("true", "false"): 

174 click.echo(f"Invalid boolean value: {value}", err=True) 

175 raise SystemExit(1) 

176 ctx.global_config_ops.set(show_pr_info=value.lower() == "true") 

177 elif parts[0] == "show_pr_checks": 

178 if value.lower() not in ("true", "false"): 

179 click.echo(f"Invalid boolean value: {value}", err=True) 

180 raise SystemExit(1) 

181 ctx.global_config_ops.set(show_pr_checks=value.lower() == "true") 

182 

183 click.echo(f"Set {key}={value}") 

184 return 

185 

186 # Handle repo config keys - not implemented yet 

187 click.echo("Setting repo config keys not yet implemented", err=True) 

188 raise SystemExit(1)