claude-dev-env 7.0.0 → 7.2.0
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/.agents/skills/pull-request/scripts/pull_request.py +2 -0
- package/.agents/skills/pull-request/scripts/test_pull_request.py +34 -0
- package/.agents/skills/session-continuity/AGENTS.md +1 -0
- package/.agents/skills/session-continuity/CLAUDE.md +1 -0
- package/.agents/skills/session-continuity/README.md +213 -0
- package/.agents/skills/session-continuity/SKILL.md +125 -0
- package/.agents/skills/session-continuity/continuity.mjs +447 -0
- package/.agents/skills/session-continuity/continuity.test.mjs +373 -0
- package/.agents/skills/syncing-submodules/scripts/test_sync_parent_pointer.py +0 -7
- package/.agents/skills/windows-scheduled-task/SKILL.md +153 -0
- package/bin/cde.mjs +36 -8
- package/bin/cde.test.mjs +48 -0
- package/bin/ever-shipped-skills.mjs +2 -20
- package/bin/ever-shipped-skills.test.mjs +8 -0
- package/bin/expand_home_directory_tokens.mjs +4 -5
- package/bin/expand_home_directory_tokens.test.mjs +128 -0
- package/bin/git_hooks_installer.mjs +22 -3
- package/bin/git_hooks_installer.test.mjs +159 -5
- package/bin/install-session-continuity.mjs +88 -0
- package/bin/install-session-continuity.test.mjs +55 -0
- package/bin/install.codex-rules.test.mjs +5 -2
- package/bin/install.cursor-rules.test.mjs +5 -2
- package/bin/install.mjs +478 -63
- package/bin/install.policy-lint.test.mjs +56 -4
- package/bin/install.profiles.test.mjs +10 -0
- package/bin/install.prune.test.mjs +227 -17
- package/bin/install.test.mjs +500 -120
- package/bin/install.transaction.test.mjs +3 -3
- package/docs/CODE_RULES.md +1 -1
- package/hooks/blocking/code_rules_banned_identifiers.py +3 -0
- package/hooks/blocking/precommit_code_rules_gate.py +5 -8
- package/hooks/blocking/test_bash_dispatcher_interpreter_starts.py +7 -52
- package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +0 -69
- package/hooks/blocking/test_code_rules_banned_identifiers.py +64 -0
- package/hooks/blocking/test_pre_tool_use_dispatcher.py +10 -244
- package/hooks/blocking/test_shared_stdin_adoption.py +1 -47
- package/hooks/git-hooks/git_hooks_constants/verification_notice_constants.py +116 -0
- package/hooks/git-hooks/post_commit.py +33 -0
- package/hooks/git-hooks/pre_commit.py +18 -90
- package/hooks/git-hooks/pre_push.py +45 -541
- package/hooks/git-hooks/test_installed_policy_lint.py +55 -6
- package/hooks/git-hooks/test_native_hook_support.py +129 -0
- package/hooks/git-hooks/test_native_policy_commit.py +39 -6
- package/hooks/git-hooks/test_post_commit.py +43 -0
- package/hooks/git-hooks/test_pre_commit.py +36 -392
- package/hooks/git-hooks/test_pre_push.py +48 -1631
- package/hooks/git-hooks/test_verification_notice.py +60 -0
- package/hooks/git-hooks/test_verification_start.py +173 -0
- package/hooks/git-hooks/verification_notice.py +192 -0
- package/hooks/git-hooks/verification_notice_context.py +253 -0
- package/hooks/git-hooks/verification_notice_state.py +237 -0
- package/hooks/git-hooks/verification_start.py +100 -0
- package/hooks/hooks.json +0 -119
- package/hooks/hooks_constants/banned_identifiers_constants.py +4 -0
- package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +1 -15
- package/hooks/hooks_constants/post_tool_use_dispatcher_constants.py +0 -4
- package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +0 -50
- package/hooks/hooks_constants/stop_dispatcher_constants.py +1 -4
- package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +3 -66
- package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +42 -185
- package/hooks/hooks_constants/test_stop_dispatcher_constants.py +3 -25
- package/hooks/validation/test_post_tool_use_dispatcher.py +35 -298
- package/hooks/validators/run_all_validators.py +5 -1
- package/hooks/validators/security_checks.py +57 -2
- package/hooks/validators/test_hook_timing_harness.py +2 -5
- package/hooks/validators/test_run_all_validators_mutation_tool_parity.py +3 -11
- package/hooks/validators/test_run_all_validators_policy_lint.py +36 -0
- package/hooks/validators/test_security_checks.py +57 -0
- package/package.json +1 -1
- package/rules/confirm-implementation-forks.md +2 -0
- package/rules/git-workflow.md +12 -0
- package/rules/pstack-host-mapping.md +48 -0
- package/rules/vault-context.md +3 -5
- package/rules/verify-runtime-state.md +7 -0
- package/scripts/audit-hooks-cli.mjs +5 -0
- package/scripts/audit-hooks.mjs +72 -16
- package/scripts/automatic_advisory/__init__.py +1 -0
- package/scripts/automatic_advisory/checkout.py +51 -0
- package/scripts/automatic_advisory/cli.py +334 -0
- package/scripts/automatic_advisory/config/__init__.py +1 -0
- package/scripts/automatic_advisory/config/constants.py +93 -0
- package/scripts/automatic_advisory/config/timing.py +4 -0
- package/scripts/automatic_advisory/configuration.py +197 -0
- package/scripts/automatic_advisory/execution.py +350 -0
- package/scripts/automatic_advisory/git.py +69 -0
- package/scripts/automatic_advisory/model.py +107 -0
- package/scripts/automatic_advisory/process_host.py +32 -0
- package/scripts/automatic_advisory/publisher.py +84 -0
- package/scripts/automatic_advisory/runner.py +399 -0
- package/scripts/automatic_advisory/state.py +116 -0
- package/scripts/automatic_advisory/window_flags.py +41 -0
- package/scripts/automatic_advisory/windows_job.py +122 -0
- package/scripts/codex_compat_materializer.py +99 -13
- package/scripts/durable_post_lint.py +30 -2
- package/scripts/durable_post_lint_config/config/constants.py +10 -0
- package/scripts/durable_post_lint_config/config/test_constants.py +13 -0
- package/scripts/local_report_cli.py +184 -0
- package/scripts/local_report_core.py +247 -0
- package/scripts/local_report_publisher.py +12 -0
- package/scripts/local_report_validation.py +357 -0
- package/scripts/local_verification/__init__.py +23 -0
- package/scripts/local_verification/check_runner.py +364 -0
- package/scripts/local_verification/check_support.py +99 -0
- package/scripts/local_verification/cli.py +158 -0
- package/scripts/local_verification/command_runner.py +84 -0
- package/scripts/local_verification/config/__init__.py +1 -0
- package/scripts/local_verification/config/constants.py +89 -0
- package/scripts/local_verification/config/timing.py +1 -0
- package/scripts/local_verification/git_state.py +146 -0
- package/scripts/local_verification/manifest.py +224 -0
- package/scripts/local_verification/model.py +174 -0
- package/scripts/local_verification/runner.py +234 -0
- package/scripts/policy_lint/adapter_pairing.py +58 -3
- package/scripts/policy_lint/config/approved_test_pairs.py +323 -0
- package/scripts/pr_verification/__init__.py +1 -0
- package/scripts/pr_verification/branch_refs.py +114 -0
- package/scripts/pr_verification/config/__init__.py +1 -0
- package/scripts/pr_verification/config/constants.py +84 -0
- package/scripts/pr_verification/config/timing.py +5 -0
- package/scripts/pr_verification/github.py +137 -0
- package/scripts/pr_verification/github_api.py +376 -0
- package/scripts/pr_verification/github_labels.py +88 -0
- package/scripts/pr_verification/github_parsing.py +182 -0
- package/scripts/pr_verification/github_transport.py +91 -0
- package/scripts/pr_verification/lock.py +122 -0
- package/scripts/pr_verification/model.py +43 -0
- package/scripts/pr_verification/requirements.txt +1 -0
- package/scripts/test_durable_post_lint.py +97 -0
- package/scripts/tests/test-audit-hooks-cli.mjs +150 -0
- package/scripts/tests/test-audit-hooks.mjs +94 -2
- package/scripts/tests/test_automatic_advisory.py +376 -0
- package/scripts/tests/test_automatic_advisory_job_cleanup.py +71 -0
- package/scripts/tests/test_automatic_advisory_polling.py +216 -0
- package/scripts/tests/test_automatic_advisory_recovery.py +320 -0
- package/scripts/tests/test_automatic_advisory_repairs.py +400 -0
- package/scripts/tests/test_automatic_advisory_settings.py +48 -0
- package/scripts/tests/test_automatic_advisory_window_flags.py +163 -0
- package/scripts/tests/test_closed_pr_label.py +157 -0
- package/scripts/tests/test_codex_compat_materializer.py +189 -6
- package/scripts/tests/test_github_labels.py +101 -0
- package/scripts/tests/test_github_transport.py +125 -0
- package/scripts/tests/test_local_report_publisher.py +396 -0
- package/scripts/tests/test_local_report_publisher_cli.py +85 -0
- package/scripts/tests/test_local_verification.py +348 -0
- package/scripts/tests/test_local_verification_cli.py +98 -0
- package/scripts/tests/test_policy_lint_rules_pairing.py +163 -0
- package/scripts/tests/test_pr_verification_github.py +392 -0
- package/scripts/tests/test_pr_verification_lock.py +168 -0
- package/scripts/tests/test_verification_notice.py +347 -0
- package/hooks/blocking/ask_user_question_shape_blocker.py +0 -83
- package/hooks/blocking/bot_mention_comment_blocker.py +0 -76
- package/hooks/blocking/claude_md_orphan_file_blocker.py +0 -129
- package/hooks/blocking/cursor_cli_python_misfire_blocker.py +0 -108
- package/hooks/blocking/destructive_command_blocker.py +0 -2073
- package/hooks/blocking/fable_spawn_gate.py +0 -187
- package/hooks/blocking/luna_fast_mode_gate.py +0 -160
- package/hooks/blocking/nas_ssh_binary_enforcer.py +0 -163
- package/hooks/blocking/orchestrator_refresh_reschedule_gate.py +0 -260
- package/hooks/blocking/package_inventory_stale_blocker.py +0 -107
- package/hooks/blocking/pii_prevention_blocker.py +0 -365
- package/hooks/blocking/piped_pytest_blocker.py +0 -1223
- package/hooks/blocking/question_to_user_enforcer.py +0 -130
- package/hooks/blocking/send_user_file_open_locally_blocker.py +0 -71
- package/hooks/blocking/sensitive_file_protector.py +0 -167
- package/hooks/blocking/session_edit_stage_gate.py +0 -684
- package/hooks/blocking/session_handoff_blocker.py +0 -182
- package/hooks/blocking/shell_substitution_blocker.py +0 -350
- package/hooks/blocking/test_ask_user_question_shape.py +0 -69
- package/hooks/blocking/test_bot_mention_comment_blocker.py +0 -131
- package/hooks/blocking/test_claude_md_orphan_file_blocker.py +0 -1277
- package/hooks/blocking/test_cursor_cli_python_misfire_blocker.py +0 -208
- package/hooks/blocking/test_destructive_command_blocker.py +0 -2024
- package/hooks/blocking/test_destructive_command_blocker_deny_mode.py +0 -105
- package/hooks/blocking/test_destructive_command_blocker_no_verify.py +0 -122
- package/hooks/blocking/test_destructive_command_blocker_patterns.py +0 -133
- package/hooks/blocking/test_fable_spawn_gate.py +0 -381
- package/hooks/blocking/test_inventory_deadlock_resolution.py +0 -154
- package/hooks/blocking/test_luna_fast_mode_gate.py +0 -211
- package/hooks/blocking/test_nas_ssh_binary_enforcer.py +0 -225
- package/hooks/blocking/test_orchestrator_refresh_reschedule_gate.py +0 -231
- package/hooks/blocking/test_package_inventory_stale_blocker.py +0 -382
- package/hooks/blocking/test_pii_prevention_blocker.py +0 -1222
- package/hooks/blocking/test_pii_prevention_windows_git_path.py +0 -41
- package/hooks/blocking/test_pii_write_surface_ephemeral.py +0 -221
- package/hooks/blocking/test_piped_pytest_blocker.py +0 -587
- package/hooks/blocking/test_precommit_code_rules_gate_native_owner.py +0 -233
- package/hooks/blocking/test_question_to_user_enforcer.py +0 -169
- package/hooks/blocking/test_send_user_file_open_locally_blocker.py +0 -118
- package/hooks/blocking/test_sensitive_file_protector.py +0 -365
- package/hooks/blocking/test_session_edit_stage_gate.py +0 -537
- package/hooks/blocking/test_session_edit_stage_gate_staging.py +0 -64
- package/hooks/blocking/test_session_edit_stage_gate_support.py +0 -204
- package/hooks/blocking/test_session_edit_stage_gate_tracker.py +0 -116
- package/hooks/blocking/test_session_handoff_blocker.py +0 -318
- package/hooks/blocking/test_shell_substitution_blocker.py +0 -298
- package/hooks/blocking/test_unscoped_search_blocker.py +0 -102
- package/hooks/blocking/test_write_existing_file_blocker.py +0 -122
- package/hooks/blocking/tests/test_pii_prevention_blocker.py +0 -259
- package/hooks/blocking/unscoped_search_blocker.py +0 -391
- package/hooks/blocking/write_existing_file_blocker.py +0 -124
- package/hooks/hooks_constants/bot_mention_comment_blocker_constants.py +0 -19
- package/hooks/hooks_constants/cursor_cli_python_misfire_blocker_constants.py +0 -79
- package/hooks/hooks_constants/fable_spawn_gate_constants.py +0 -61
- package/hooks/hooks_constants/luna_fast_mode_gate_constants.py +0 -45
- package/hooks/hooks_constants/nas_ssh_binary_enforcer_constants.py +0 -20
- package/hooks/hooks_constants/orchestrator_refresh_reschedule_gate_constants.py +0 -57
- package/hooks/hooks_constants/send_user_file_open_locally_blocker_constants.py +0 -17
- package/hooks/hooks_constants/sensitive_file_protector_constants.py +0 -40
- package/hooks/hooks_constants/session_handoff_blocker_constants.py +0 -10
- package/hooks/hooks_constants/shell_substitution_blocker_constants.py +0 -80
|
@@ -34,11 +34,7 @@ from test_hook_subprocess_support import ( # noqa: E402
|
|
|
34
34
|
run_hook_as_subprocess,
|
|
35
35
|
)
|
|
36
36
|
|
|
37
|
-
ALL_CONVERTED_HOOK_FILENAMES = (
|
|
38
|
-
"open_questions_in_plans_blocker.py",
|
|
39
|
-
"claude_md_orphan_file_blocker.py",
|
|
40
|
-
"package_inventory_stale_blocker.py",
|
|
41
|
-
)
|
|
37
|
+
ALL_CONVERTED_HOOK_FILENAMES = ("open_questions_in_plans_blocker.py",)
|
|
42
38
|
|
|
43
39
|
EMPTY_STDIN_PAYLOAD = ""
|
|
44
40
|
WHITESPACE_STDIN_PAYLOAD = " \n\t "
|
|
@@ -126,48 +122,6 @@ def test_open_questions_blocker_still_allows_plan_without_open_questions(
|
|
|
126
122
|
assert _decision_from_stdout(completed) is None
|
|
127
123
|
|
|
128
124
|
|
|
129
|
-
def test_package_inventory_blocker_still_denies_uninventoried_new_file(
|
|
130
|
-
tmp_path: Path,
|
|
131
|
-
) -> None:
|
|
132
|
-
inventory_body = "# package\n\n| File | Role |\n|---|---|\n| `alpha.py` | A |\n| `beta.py` | B |\n"
|
|
133
|
-
(tmp_path / "README.md").write_text(inventory_body, encoding="utf-8")
|
|
134
|
-
(tmp_path / "alpha.py").write_text("x = 1\n", encoding="utf-8")
|
|
135
|
-
(tmp_path / "beta.py").write_text("x = 1\n", encoding="utf-8")
|
|
136
|
-
new_file_path = tmp_path / "gamma.py"
|
|
137
|
-
payload = json.dumps(
|
|
138
|
-
{
|
|
139
|
-
"tool_name": "Write",
|
|
140
|
-
"tool_input": {"file_path": str(new_file_path), "content": "x = 1\n"},
|
|
141
|
-
}
|
|
142
|
-
)
|
|
143
|
-
completed = _run_hook_script("package_inventory_stale_blocker.py", payload, tmp_path)
|
|
144
|
-
assert completed.returncode == 0, completed.stderr
|
|
145
|
-
assert _decision_from_stdout(completed) == "deny"
|
|
146
|
-
assert_hook_deny_log_contains(tmp_path, "package_inventory_stale_blocker.py")
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
def test_package_inventory_blocker_still_allows_inventoried_new_file(
|
|
150
|
-
tmp_path: Path,
|
|
151
|
-
) -> None:
|
|
152
|
-
inventory_body = (
|
|
153
|
-
"# package\n\n| File | Role |\n|---|---|\n"
|
|
154
|
-
"| `alpha.py` | A |\n| `beta.py` | B |\n| `gamma.py` | G |\n"
|
|
155
|
-
)
|
|
156
|
-
(tmp_path / "README.md").write_text(inventory_body, encoding="utf-8")
|
|
157
|
-
(tmp_path / "alpha.py").write_text("x = 1\n", encoding="utf-8")
|
|
158
|
-
(tmp_path / "beta.py").write_text("x = 1\n", encoding="utf-8")
|
|
159
|
-
new_file_path = tmp_path / "gamma.py"
|
|
160
|
-
payload = json.dumps(
|
|
161
|
-
{
|
|
162
|
-
"tool_name": "Write",
|
|
163
|
-
"tool_input": {"file_path": str(new_file_path), "content": "x = 1\n"},
|
|
164
|
-
}
|
|
165
|
-
)
|
|
166
|
-
completed = _run_hook_script("package_inventory_stale_blocker.py", payload, tmp_path)
|
|
167
|
-
assert completed.returncode == 0, completed.stderr
|
|
168
|
-
assert _decision_from_stdout(completed) is None
|
|
169
|
-
|
|
170
|
-
|
|
171
125
|
def test_converted_hooks_allow_unrelated_tool_name(tmp_path: Path) -> None:
|
|
172
126
|
payload = json.dumps({"tool_name": "Bash", "tool_input": {"command": "ls"}})
|
|
173
127
|
for each_hook_filename in ALL_CONVERTED_HOOK_FILENAMES:
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
NOTICE_EVENT_COMMIT: str = "commit"
|
|
4
|
+
NOTICE_EVENT_PUSH: str = "push"
|
|
5
|
+
ALL_NOTICE_EVENTS: frozenset[str] = frozenset({NOTICE_EVENT_COMMIT, NOTICE_EVENT_PUSH})
|
|
6
|
+
TARGET_REPOSITORY_REMOTE: str = "jonecho/python-automation"
|
|
7
|
+
GIT_CONFIG_SUBCOMMAND: str = "config"
|
|
8
|
+
GIT_REV_PARSE_SUBCOMMAND: str = "rev-parse"
|
|
9
|
+
ALL_GIT_REMOTE_URL_QUERY: tuple[str, ...] = (GIT_CONFIG_SUBCOMMAND, "--get", "remote.origin.url")
|
|
10
|
+
ALL_GIT_REPOSITORY_ROOT_QUERY: tuple[str, ...] = (GIT_REV_PARSE_SUBCOMMAND, "--show-toplevel")
|
|
11
|
+
ALL_GIT_HEAD_QUERY: tuple[str, ...] = (GIT_REV_PARSE_SUBCOMMAND, "--verify", "HEAD")
|
|
12
|
+
ALL_GIT_ABSOLUTE_DIRECTORY_QUERY: tuple[str, ...] = (
|
|
13
|
+
GIT_REV_PARSE_SUBCOMMAND,
|
|
14
|
+
"--absolute-git-dir",
|
|
15
|
+
)
|
|
16
|
+
GIT_COMMAND_SUCCESS_EXIT_CODE: int = 0
|
|
17
|
+
LOCAL_VERIFICATION_DIRECTORY_NAME: str = "local-verification"
|
|
18
|
+
MANIFEST_DIRECTORY_NAME: str = "config"
|
|
19
|
+
MANIFEST_FILE_NAME: str = "local-verification.json"
|
|
20
|
+
REPORT_FILE_NAME: str = "report.json"
|
|
21
|
+
GIT_DIRECTORY_NAME: str = ".git"
|
|
22
|
+
REPOSITORY_ARGUMENT: str = "--repo"
|
|
23
|
+
BASE_ARGUMENT: str = "--base"
|
|
24
|
+
BASE_REFERENCE: str = "origin/main"
|
|
25
|
+
OUTPUT_ARGUMENT: str = "--output"
|
|
26
|
+
PYTHON_COMMAND: str = "python"
|
|
27
|
+
SCOPED_VERIFICATION_SCRIPT_PATH: str = ".github/ci/local_verify.py"
|
|
28
|
+
EXECUTOR_ARGUMENT: str = "--executor"
|
|
29
|
+
EXECUTOR_DIRECTORY_NAME: str = "scripts"
|
|
30
|
+
LOCAL_VERIFICATION_PACKAGE_DIRECTORY_NAME: str = "local_verification"
|
|
31
|
+
EXECUTOR_FILE_NAME: str = "cli.py"
|
|
32
|
+
PACKAGE_ROOT_PARENT_INDEX: int = 2
|
|
33
|
+
POWERSHELL_QUOTE: str = "'"
|
|
34
|
+
POWERSHELL_ESCAPED_QUOTE: str = "''"
|
|
35
|
+
NOTICE_HEADER: str = "=== LOCAL VERIFICATION ADVISORY ==="
|
|
36
|
+
UNKNOWN_SHA: str = "unknown"
|
|
37
|
+
NO_VERIFIED_SHA: str = "none"
|
|
38
|
+
STATUS_PENDING: str = "pending"
|
|
39
|
+
STATUS_UNVERIFIED: str = "unverified"
|
|
40
|
+
STATUS_STALE: str = "stale"
|
|
41
|
+
STATUS_FAILED: str = "failed"
|
|
42
|
+
STATUS_PASSED: str = "passed"
|
|
43
|
+
MINIMUM_REMOTE_PART_COUNT: int = 2
|
|
44
|
+
OWNER_PART_INDEX: int = -2
|
|
45
|
+
REPOSITORY_PART_INDEX: int = -1
|
|
46
|
+
REPORT_HEAD_FIELD: str = "head"
|
|
47
|
+
REPORT_BASE_FIELD: str = "base"
|
|
48
|
+
REPORT_MANIFEST_DIGEST_FIELD: str = "manifest_digest"
|
|
49
|
+
REPORT_STATUS_FIELD: str = "status"
|
|
50
|
+
REPORT_AGGREGATE_FIELD: str = "aggregate"
|
|
51
|
+
REPORT_TOTAL_FIELD: str = "total"
|
|
52
|
+
REPORT_PASSED_FIELD: str = "passed"
|
|
53
|
+
REPORT_FAILED_FIELD: str = "failed"
|
|
54
|
+
REPORT_INCOMPLETE_FIELD: str = "incomplete"
|
|
55
|
+
REPORT_EXIT_CODE_FIELD: str = "exit_code"
|
|
56
|
+
REPORT_WORKTREE_CLEAN_FIELD: str = "worktree_clean"
|
|
57
|
+
REPORT_INPUTS_UNCHANGED_FIELD: str = "inputs_unchanged"
|
|
58
|
+
REPORT_PUBLISHABLE_FIELD: str = "publishable"
|
|
59
|
+
RESOLVED_SHA_LENGTH: int = 40
|
|
60
|
+
HEX_DIGITS: str = "0123456789abcdef"
|
|
61
|
+
JSON_ENCODING: str = "utf-8"
|
|
62
|
+
SHA256_ALGORITHM: str = "sha256"
|
|
63
|
+
NOTICE_LINE_SEPARATOR: str = "\n"
|
|
64
|
+
NOTICE_ADVISORY_LINE: str = "Advisory only. This notice does not block commit or push."
|
|
65
|
+
NOTICE_SETUP_PENDING_LINE: str = "Setup status: pending. The manifest is missing or unreadable."
|
|
66
|
+
NOTICE_NO_PASS_LINE: str = (
|
|
67
|
+
"Do not carry local-checks:passed to this revision. No complete current pass is recorded."
|
|
68
|
+
)
|
|
69
|
+
NOTICE_STALE_LINE: str = "Existing verification evidence belongs to another SHA. Do not carry local-checks:passed forward."
|
|
70
|
+
NOTICE_FAILED_LINE: str = "The recorded local checks failed. This advisory does not block commit or push. Fix the failure before reporting a pass."
|
|
71
|
+
NOTICE_PASSED_LINE: str = (
|
|
72
|
+
"local-checks:passed is valid only for this exact current SHA and manifest."
|
|
73
|
+
)
|
|
74
|
+
NOTICE_RUN_LINE: str = "Run the complete local checks with this canonical command:"
|
|
75
|
+
PROTECTED_BRANCH_PUSH_ADVISORY_MESSAGE: str = (
|
|
76
|
+
"Advisory: push would send local branch {local_branch!r} to protected remote "
|
|
77
|
+
"branch {remote_branch!r}. The native hook permits the push."
|
|
78
|
+
)
|
|
79
|
+
MANIFEST_VERSION_KEY: str = "version"
|
|
80
|
+
MANIFEST_CHECKS_KEY: str = "checks"
|
|
81
|
+
MANIFEST_EXCLUSIONS_KEY: str = "exclusions"
|
|
82
|
+
CHECK_ID_KEY: str = "id"
|
|
83
|
+
CHECK_ARGUMENTS_KEY: str = "argv"
|
|
84
|
+
CHECK_DIRECTORY_KEY: str = "cwd"
|
|
85
|
+
CHECK_TIMEOUT_KEY: str = "timeout_seconds"
|
|
86
|
+
CHECK_MINIMUM_TESTS_KEY: str = "minimum_tests"
|
|
87
|
+
EXCLUSION_SELECTOR_KEY: str = "selector"
|
|
88
|
+
EXCLUSION_REASON_KEY: str = "reason"
|
|
89
|
+
ALL_MANIFEST_DIGEST_SEPARATORS: tuple[str, str] = (",", ":")
|
|
90
|
+
WINDOWS_DIRECTORY_SEPARATOR: str = "\\"
|
|
91
|
+
POSIX_DIRECTORY_SEPARATOR: str = "/"
|
|
92
|
+
PARENT_DIRECTORY_NAME: str = ".."
|
|
93
|
+
ALL_GIT_STATUS_QUERY: tuple[str, ...] = (
|
|
94
|
+
"status",
|
|
95
|
+
"--porcelain=v1",
|
|
96
|
+
"--untracked-files=all",
|
|
97
|
+
)
|
|
98
|
+
ALL_GIT_BASE_QUERY_PREFIX: tuple[str, ...] = ("rev-parse", "--verify")
|
|
99
|
+
GIT_COMMIT_OBJECT_SUFFIX: str = "^{commit}"
|
|
100
|
+
SELECTION_FIELD: str = "selection"
|
|
101
|
+
SELECTED_MANIFEST_PATH_FIELD: str = "selected_manifest_path"
|
|
102
|
+
COMMAND_SEPARATOR: str = " "
|
|
103
|
+
RUNNER_FILE_NAME: str = "runner.json"
|
|
104
|
+
RUNNER_PYTHON_FIELD: str = "python"
|
|
105
|
+
RUNNER_SETTINGS_FIELD: str = "settings"
|
|
106
|
+
ALL_RUNNER_FIELDS: frozenset[str] = frozenset(
|
|
107
|
+
{
|
|
108
|
+
RUNNER_PYTHON_FIELD,
|
|
109
|
+
RUNNER_SETTINGS_FIELD,
|
|
110
|
+
}
|
|
111
|
+
)
|
|
112
|
+
AUTOMATIC_ADVISORY_DIRECTORY_NAME: str = "automatic_advisory"
|
|
113
|
+
AUTOMATIC_ADVISORY_CLI_FILE_NAME: str = "cli.py"
|
|
114
|
+
WINDOWS_PLATFORM: str = "win32"
|
|
115
|
+
CREATE_NO_WINDOW_ATTRIBUTE: str = "CREATE_NO_WINDOW"
|
|
116
|
+
POWERSHELL_CALL_OPERATOR: str = "& "
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Show the local verification reminder after a native Git commit."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from verification_notice import main as verification_notice_main
|
|
10
|
+
from git_hooks_constants.verification_notice_constants import (
|
|
11
|
+
NOTICE_EVENT_COMMIT,
|
|
12
|
+
REPOSITORY_ARGUMENT,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def main() -> int:
|
|
17
|
+
try:
|
|
18
|
+
verification_notice_main(
|
|
19
|
+
[
|
|
20
|
+
"--event",
|
|
21
|
+
NOTICE_EVENT_COMMIT,
|
|
22
|
+
REPOSITORY_ARGUMENT,
|
|
23
|
+
str(Path.cwd()),
|
|
24
|
+
],
|
|
25
|
+
stdout=sys.stdout,
|
|
26
|
+
)
|
|
27
|
+
return 0
|
|
28
|
+
except (OSError, UnicodeError, ValueError):
|
|
29
|
+
return 0
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
if __name__ == "__main__":
|
|
33
|
+
raise SystemExit(main())
|
|
@@ -1,106 +1,34 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
|
|
4
|
-
Git invokes this module through the installed native pre-commit shim. Policy
|
|
5
|
-
lint reads the Git index, including GIT_INDEX_FILE when Git supplies an
|
|
6
|
-
alternate index. The existing gate still covers checks whose replacement
|
|
7
|
-
coverage is unproven.
|
|
8
|
-
|
|
9
|
-
A missing linter, launch failure, or timeout is an infrastructure failure.
|
|
10
|
-
Linter diagnostics and failed-rule statuses reach Git.
|
|
11
|
-
"""
|
|
2
|
+
"""Show the local verification reminder before a native Git commit."""
|
|
12
3
|
|
|
13
4
|
from __future__ import annotations
|
|
14
5
|
|
|
15
|
-
import subprocess
|
|
16
6
|
import sys
|
|
17
7
|
from pathlib import Path
|
|
18
8
|
|
|
19
|
-
from
|
|
20
|
-
from git_hooks_constants import (
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
IMMEDIATE_SCOPE_ARGUMENT,
|
|
24
|
-
INVOKE_GATE_FAILURE_MESSAGE,
|
|
25
|
-
)
|
|
26
|
-
from git_hooks_constants.staged_policy_lint import (
|
|
27
|
-
POLICY_LINT_FAILED_MESSAGE,
|
|
28
|
-
POLICY_LINT_INFRASTRUCTURE_EXIT_CODE,
|
|
29
|
-
POLICY_LINT_PACKAGE_PARENT_INDEX,
|
|
30
|
-
POLICY_LINT_SCRIPT_RELATIVE_PATH,
|
|
31
|
-
POLICY_LINT_STAGED_ARGUMENT,
|
|
32
|
-
POLICY_LINT_TIMEOUT_SECONDS,
|
|
33
|
-
POLICY_LINT_UNAVAILABLE_MESSAGE,
|
|
9
|
+
from verification_notice import main as verification_notice_main
|
|
10
|
+
from git_hooks_constants.verification_notice_constants import (
|
|
11
|
+
NOTICE_EVENT_COMMIT,
|
|
12
|
+
REPOSITORY_ARGUMENT,
|
|
34
13
|
)
|
|
35
14
|
|
|
36
15
|
|
|
37
|
-
def resolve_policy_lint_script_path() -> Path:
|
|
38
|
-
"""Return the policy-linter entry point in this package or managed home."""
|
|
39
|
-
package_root = Path(__file__).resolve().parents[POLICY_LINT_PACKAGE_PARENT_INDEX]
|
|
40
|
-
return package_root / POLICY_LINT_SCRIPT_RELATIVE_PATH
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def run_staged_policy_lint() -> int:
|
|
44
|
-
"""Run the staged linter with Git's environment and working directory.
|
|
45
|
-
|
|
46
|
-
Returns:
|
|
47
|
-
The linter status, or the infrastructure status when it cannot run.
|
|
48
|
-
"""
|
|
49
|
-
try:
|
|
50
|
-
script_path = resolve_policy_lint_script_path()
|
|
51
|
-
if not script_path.is_file():
|
|
52
|
-
sys.stderr.write(POLICY_LINT_UNAVAILABLE_MESSAGE + "\n")
|
|
53
|
-
return POLICY_LINT_INFRASTRUCTURE_EXIT_CODE
|
|
54
|
-
completion = subprocess.run(
|
|
55
|
-
[sys.executable, str(script_path.resolve(strict=True)), POLICY_LINT_STAGED_ARGUMENT],
|
|
56
|
-
check=False,
|
|
57
|
-
timeout=POLICY_LINT_TIMEOUT_SECONDS,
|
|
58
|
-
)
|
|
59
|
-
except (OSError, subprocess.TimeoutExpired) as launch_error:
|
|
60
|
-
sys.stderr.write(POLICY_LINT_FAILED_MESSAGE.format(error=launch_error) + "\n")
|
|
61
|
-
return POLICY_LINT_INFRASTRUCTURE_EXIT_CODE
|
|
62
|
-
return completion.returncode
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def invoke_gate(gate_script_path: Path) -> int:
|
|
66
|
-
"""Invoke the shared staged gate and return its exit code."""
|
|
67
|
-
immediate_scope_argument = IMMEDIATE_SCOPE_ARGUMENT
|
|
68
|
-
invoke_gate_failure_message = INVOKE_GATE_FAILURE_MESSAGE
|
|
69
|
-
gate_infrastructure_failure_exit_code = GATE_INFRASTRUCTURE_FAILURE_EXIT_CODE
|
|
70
|
-
try:
|
|
71
|
-
resolved_gate_path = gate_script_path.resolve(strict=True)
|
|
72
|
-
completion = subprocess.run(
|
|
73
|
-
[sys.executable, str(resolved_gate_path), immediate_scope_argument],
|
|
74
|
-
check=False,
|
|
75
|
-
)
|
|
76
|
-
except OSError as launch_error:
|
|
77
|
-
print(
|
|
78
|
-
invoke_gate_failure_message.format(error=launch_error),
|
|
79
|
-
file=sys.stderr,
|
|
80
|
-
)
|
|
81
|
-
return gate_infrastructure_failure_exit_code
|
|
82
|
-
return completion.returncode
|
|
83
|
-
|
|
84
|
-
|
|
85
16
|
def main() -> int:
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if not is_safe_regular_file(gate_script_path, exact_allowed_path):
|
|
97
|
-
print(
|
|
98
|
-
gate_script_not_found_message.format(path=gate_script_path),
|
|
99
|
-
file=sys.stderr,
|
|
17
|
+
try:
|
|
18
|
+
verification_notice_main(
|
|
19
|
+
[
|
|
20
|
+
"--event",
|
|
21
|
+
NOTICE_EVENT_COMMIT,
|
|
22
|
+
"--notice-only",
|
|
23
|
+
REPOSITORY_ARGUMENT,
|
|
24
|
+
str(Path.cwd()),
|
|
25
|
+
],
|
|
26
|
+
stdout=sys.stdout,
|
|
100
27
|
)
|
|
101
28
|
return 0
|
|
102
|
-
|
|
29
|
+
except (OSError, UnicodeError, ValueError):
|
|
30
|
+
return 0
|
|
103
31
|
|
|
104
32
|
|
|
105
33
|
if __name__ == "__main__":
|
|
106
|
-
|
|
34
|
+
raise SystemExit(main())
|