claude-dev-env 8.24.0 → 8.25.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/CODE_RULES.md +1 -1
- package/hooks/blocking/code_rules_boolean_mustcheck.py +1 -82
- package/hooks/blocking/code_rules_enforcer.py +0 -6
- package/hooks/blocking/code_rules_imports_logging.py +1 -120
- package/hooks/blocking/reply_length_gate.py +20 -1
- package/hooks/blocking/test_code_rules_enforcer_narrow_edit.py +0 -16
- package/hooks/blocking/test_reply_length_gate.py +44 -0
- package/hooks/hooks_constants/blocking_check_limits.py +0 -13
- package/hooks/hooks_constants/code_rules_enforcer_constants.py +0 -6
- package/hooks/hooks_constants/local_identity.py +7 -120
- package/hooks/hooks_constants/reply_length_gate_constants.py +5 -0
- package/hooks/hooks_constants/test_local_identity.py +7 -43
- package/package.json +1 -1
- package/rules/shell-invocation.md +1 -1
- package/scripts/repository_checks/config/constants.py +0 -5
- package/docs/nas-ssh-invocation.md +0 -107
- package/hooks/blocking/test_code_rules_enforcer_ignored_must_check_return.py +0 -266
- package/hooks/blocking/test_code_rules_enforcer_split_mustcheck.py +0 -87
- package/hooks/blocking/test_code_rules_logging_printf_tokens.py +0 -134
- package/rules/nas-ssh-invocation.md +0 -29
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
"""Tests for the printf-token check on str.format-logger (automation_logging) calls."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import importlib.util
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
from types import ModuleType
|
|
8
|
-
|
|
9
|
-
ENFORCER_FILENAME = "code_rules_enforcer.py"
|
|
10
|
-
ENFORCER_MODULE_NAME = "code_rules_enforcer_printf_under_test"
|
|
11
|
-
PRODUCTION_FILE_PATH = "shared_utils/web_automation/sample.py"
|
|
12
|
-
TEST_FILE_PATH = "shared_utils/web_automation/tests/test_sample.py"
|
|
13
|
-
FORMAT_LOGGER_IMPORT = (
|
|
14
|
-
"from shared_utils.automation_logging import log_error, log_info, log_debug\n"
|
|
15
|
-
)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def load_enforcer_module() -> ModuleType:
|
|
19
|
-
enforcer_path = Path(__file__).parent / ENFORCER_FILENAME
|
|
20
|
-
module_spec = importlib.util.spec_from_file_location(ENFORCER_MODULE_NAME, enforcer_path)
|
|
21
|
-
assert module_spec is not None
|
|
22
|
-
assert module_spec.loader is not None
|
|
23
|
-
enforcer_module = importlib.util.module_from_spec(module_spec)
|
|
24
|
-
module_spec.loader.exec_module(enforcer_module)
|
|
25
|
-
return enforcer_module
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
enforcer = load_enforcer_module()
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def test_should_flag_percent_s_in_format_logger_call() -> None:
|
|
32
|
-
source = FORMAT_LOGGER_IMPORT + 'log_error("Skipping %s after error: %s", name, err)\n'
|
|
33
|
-
issues = enforcer.check_logging_printf_tokens(source, PRODUCTION_FILE_PATH)
|
|
34
|
-
assert len(issues) == 1
|
|
35
|
-
assert "printf token" in issues[0]
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def test_should_flag_percent_d_in_format_logger_call() -> None:
|
|
39
|
-
source = FORMAT_LOGGER_IMPORT + 'log_debug("attempt %d of %d", index, total)\n'
|
|
40
|
-
issues = enforcer.check_logging_printf_tokens(source, PRODUCTION_FILE_PATH)
|
|
41
|
-
assert len(issues) == 1
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def test_should_allow_brace_placeholders_in_format_logger_call() -> None:
|
|
45
|
-
source = FORMAT_LOGGER_IMPORT + 'log_info("Processing {} of {}", index, total)\n'
|
|
46
|
-
issues = enforcer.check_logging_printf_tokens(source, PRODUCTION_FILE_PATH)
|
|
47
|
-
assert issues == []
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def test_should_ignore_percent_token_without_format_logger_import() -> None:
|
|
51
|
-
source = 'log_error("Skipping %s", name)\n'
|
|
52
|
-
issues = enforcer.check_logging_printf_tokens(source, PRODUCTION_FILE_PATH)
|
|
53
|
-
assert issues == []
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def test_should_ignore_attribute_style_logger_call() -> None:
|
|
57
|
-
source = FORMAT_LOGGER_IMPORT + 'logger.info("delivered %s", message_id)\n'
|
|
58
|
-
issues = enforcer.check_logging_printf_tokens(source, PRODUCTION_FILE_PATH)
|
|
59
|
-
assert issues == []
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def test_should_inspect_message_literal_not_argument_value() -> None:
|
|
63
|
-
source = FORMAT_LOGGER_IMPORT + 'log_info("status: {}", "100%s done")\n'
|
|
64
|
-
issues = enforcer.check_logging_printf_tokens(source, PRODUCTION_FILE_PATH)
|
|
65
|
-
assert issues == []
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def test_should_exempt_test_files() -> None:
|
|
69
|
-
source = FORMAT_LOGGER_IMPORT + 'log_error("Skipping %s", name)\n'
|
|
70
|
-
issues = enforcer.check_logging_printf_tokens(source, TEST_FILE_PATH)
|
|
71
|
-
assert issues == []
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def test_should_resolve_aliased_format_logger_import() -> None:
|
|
75
|
-
source = (
|
|
76
|
-
"from shared_utils.automation_logging import log_error as report_error\n"
|
|
77
|
-
+ 'report_error("failed %s", name)\n'
|
|
78
|
-
)
|
|
79
|
-
issues = enforcer.check_logging_printf_tokens(source, PRODUCTION_FILE_PATH)
|
|
80
|
-
assert len(issues) == 1
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
def test_should_allow_token_bearing_message_with_no_format_args() -> None:
|
|
84
|
-
source = FORMAT_LOGGER_IMPORT + 'log_info("Use %s for string substitution")\n'
|
|
85
|
-
issues = enforcer.check_logging_printf_tokens(source, PRODUCTION_FILE_PATH)
|
|
86
|
-
assert issues == []
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def test_should_allow_documentation_style_token_message_with_no_args() -> None:
|
|
90
|
-
source = FORMAT_LOGGER_IMPORT + 'log_warning("avoid %s-style tokens here")\n'
|
|
91
|
-
issues = enforcer.check_logging_printf_tokens(source, PRODUCTION_FILE_PATH)
|
|
92
|
-
assert issues == []
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
def test_should_allow_percent_adjacent_to_word_in_format_message() -> None:
|
|
96
|
-
source = FORMAT_LOGGER_IMPORT + 'log_info("memory 80%free now", host)\n'
|
|
97
|
-
issues = enforcer.check_logging_printf_tokens(source, PRODUCTION_FILE_PATH)
|
|
98
|
-
assert issues == []
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
def test_should_allow_doubled_percent_in_format_message() -> None:
|
|
102
|
-
source = FORMAT_LOGGER_IMPORT + 'log_info("100%% done", host)\n'
|
|
103
|
-
issues = enforcer.check_logging_printf_tokens(source, PRODUCTION_FILE_PATH)
|
|
104
|
-
assert issues == []
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
def test_should_allow_trailing_percent_in_format_message() -> None:
|
|
108
|
-
source = FORMAT_LOGGER_IMPORT + 'log_info("100% done", host)\n'
|
|
109
|
-
issues = enforcer.check_logging_printf_tokens(source, PRODUCTION_FILE_PATH)
|
|
110
|
-
assert issues == []
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
def test_should_flag_width_token_in_format_logger_call() -> None:
|
|
114
|
-
source = FORMAT_LOGGER_IMPORT + 'log_error("item %5d", index)\n'
|
|
115
|
-
issues = enforcer.check_logging_printf_tokens(source, PRODUCTION_FILE_PATH)
|
|
116
|
-
assert len(issues) == 1
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
def test_should_flag_precision_token_in_format_logger_call() -> None:
|
|
120
|
-
source = FORMAT_LOGGER_IMPORT + 'log_error("took %0.2f sec", elapsed)\n'
|
|
121
|
-
issues = enforcer.check_logging_printf_tokens(source, PRODUCTION_FILE_PATH)
|
|
122
|
-
assert len(issues) == 1
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
def test_should_flag_float_general_token_in_format_logger_call() -> None:
|
|
126
|
-
source = FORMAT_LOGGER_IMPORT + 'log_error("ratio %g", ratio)\n'
|
|
127
|
-
issues = enforcer.check_logging_printf_tokens(source, PRODUCTION_FILE_PATH)
|
|
128
|
-
assert len(issues) == 1
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
def test_should_flag_scientific_token_in_format_logger_call() -> None:
|
|
132
|
-
source = FORMAT_LOGGER_IMPORT + 'log_error("value %e", measurement)\n'
|
|
133
|
-
issues = enforcer.check_logging_printf_tokens(source, PRODUCTION_FILE_PATH)
|
|
134
|
-
assert len(issues) == 1
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# Running Commands on the NAS
|
|
2
|
-
|
|
3
|
-
Reach the NAS through the `nas_ssh_key.py` runner, never through `ssh`, `scp` or `sftp`:
|
|
4
|
-
|
|
5
|
-
```
|
|
6
|
-
python <runner-path> <command-script.sh> <private-key-path>
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
The first argument is a **path to a file** holding a bash script, not a command string. Write that
|
|
10
|
-
file with the Write tool. The whole script runs in one shell, so variables, `cd` and `source` carry
|
|
11
|
-
from line to line.
|
|
12
|
-
|
|
13
|
-
The runner ships with the automation project it serves; read its path from that project's own
|
|
14
|
-
reference. Use the ops key under `~/.claude/keys` — keys under `~/.ssh` either carry a passphrase,
|
|
15
|
-
which cannot be answered unattended, or belong to other hosts.
|
|
16
|
-
|
|
17
|
-
The runner loads the key with paramiko and signs in the same process. The command-line clients check
|
|
18
|
-
the key file's permissions first and refuse it, and Git Bash's `ssh` then falls back to a password
|
|
19
|
-
prompt that hangs an unattended run. No hook denies an ssh-family word aimed at the NAS, so keep to
|
|
20
|
-
the runner yourself. When the `System32/OpenSSH` binary is unavoidable, pass `-o BatchMode=yes` so it
|
|
21
|
-
fails fast on a refused key.
|
|
22
|
-
|
|
23
|
-
Host, ssh port and ssh user are constants inside the runner and are also under the `nas` key in
|
|
24
|
-
`~/.claude/local-identity.json`. Keep all three out of anything committed or posted.
|
|
25
|
-
|
|
26
|
-
To copy a file, write it inside the script with a quoted heredoc rather than reaching for `scp`.
|
|
27
|
-
|
|
28
|
-
The `/tmp` size limit, how to tell a break in the code from a platform-only one, and a failure-to-cause
|
|
29
|
-
table: `@~/.claude/docs/nas-ssh-invocation.md`.
|