craftainer-crud-sdk 0.1.0__tar.gz
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.
- craftainer_crud_sdk-0.1.0/.claude/README.md +82 -0
- craftainer_crud_sdk-0.1.0/.claude/agents/plan-executor.md +72 -0
- craftainer_crud_sdk-0.1.0/.claude/hooks/self-check.sh +35 -0
- craftainer_crud_sdk-0.1.0/.claude/settings.json +36 -0
- craftainer_crud_sdk-0.1.0/.claude/skills/owasp-scan/SKILL.md +89 -0
- craftainer_crud_sdk-0.1.0/.devcontainer/Dockerfile +79 -0
- craftainer_crud_sdk-0.1.0/.devcontainer/Dockerfile.dockerignore +11 -0
- craftainer_crud_sdk-0.1.0/.devcontainer/README.md +164 -0
- craftainer_crud_sdk-0.1.0/.devcontainer/compose.instance.yml +5 -0
- craftainer_crud_sdk-0.1.0/.devcontainer/compose.yml +42 -0
- craftainer_crud_sdk-0.1.0/.devcontainer/devcontainer-lock.json +9 -0
- craftainer_crud_sdk-0.1.0/.devcontainer/devcontainer.json +39 -0
- craftainer_crud_sdk-0.1.0/.devcontainer/resolve-ssh-agent.sh +41 -0
- craftainer_crud_sdk-0.1.0/.gitattributes +1 -0
- craftainer_crud_sdk-0.1.0/.github/CONTENTS.md +31 -0
- craftainer_crud_sdk-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +48 -0
- craftainer_crud_sdk-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +32 -0
- craftainer_crud_sdk-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +11 -0
- craftainer_crud_sdk-0.1.0/.github/renovate.json +62 -0
- craftainer_crud_sdk-0.1.0/.github/scripts/README.md +46 -0
- craftainer_crud_sdk-0.1.0/.github/scripts/check_devcontainer_lock.py +57 -0
- craftainer_crud_sdk-0.1.0/.github/scripts/compute_next_version.py +116 -0
- craftainer_crud_sdk-0.1.0/.github/scripts/moderate_apply_result.sh +65 -0
- craftainer_crud_sdk-0.1.0/.github/scripts/moderate_cleanup.sh +30 -0
- craftainer_crud_sdk-0.1.0/.github/scripts/moderate_package_result.sh +40 -0
- craftainer_crud_sdk-0.1.0/.github/scripts/prompts/bug-fix.md +32 -0
- craftainer_crud_sdk-0.1.0/.github/scripts/prompts/bug-triage.md +71 -0
- craftainer_crud_sdk-0.1.0/.github/scripts/prompts/feature-build.md +34 -0
- craftainer_crud_sdk-0.1.0/.github/scripts/prompts/feature-triage.md +62 -0
- craftainer_crud_sdk-0.1.0/.github/scripts/reset_issue_state.sh +32 -0
- craftainer_crud_sdk-0.1.0/.github/scripts/run_claude.sh +27 -0
- craftainer_crud_sdk-0.1.0/.github/scripts/template_sync.py +315 -0
- craftainer_crud_sdk-0.1.0/.github/scripts/template_sync_manifest.py +122 -0
- craftainer_crud_sdk-0.1.0/.github/template-sync-manifest.yml +164 -0
- craftainer_crud_sdk-0.1.0/.github/template-sync-state.json +4 -0
- craftainer_crud_sdk-0.1.0/.github/workflows/README.md +301 -0
- craftainer_crud_sdk-0.1.0/.github/workflows/checks.yml +75 -0
- craftainer_crud_sdk-0.1.0/.github/workflows/codspeed.yml +53 -0
- craftainer_crud_sdk-0.1.0/.github/workflows/moderate-bug-fix-apply.yml +48 -0
- craftainer_crud_sdk-0.1.0/.github/workflows/moderate-bug-fix.yml +125 -0
- craftainer_crud_sdk-0.1.0/.github/workflows/moderate-bug-triage.yml +91 -0
- craftainer_crud_sdk-0.1.0/.github/workflows/moderate-cleanup.yml +49 -0
- craftainer_crud_sdk-0.1.0/.github/workflows/moderate-feature-build-apply.yml +48 -0
- craftainer_crud_sdk-0.1.0/.github/workflows/moderate-feature-build.yml +125 -0
- craftainer_crud_sdk-0.1.0/.github/workflows/moderate-feature-triage.yml +91 -0
- craftainer_crud_sdk-0.1.0/.github/workflows/moderate-setup.yml +38 -0
- craftainer_crud_sdk-0.1.0/.github/workflows/release.yml +205 -0
- craftainer_crud_sdk-0.1.0/.github/workflows/template-sync.yml +184 -0
- craftainer_crud_sdk-0.1.0/.gitignore +123 -0
- craftainer_crud_sdk-0.1.0/.mcp.json +8 -0
- craftainer_crud_sdk-0.1.0/.pre-commit-config.yaml +131 -0
- craftainer_crud_sdk-0.1.0/.secrets/.gitkeep +0 -0
- craftainer_crud_sdk-0.1.0/.secrets/README.md +23 -0
- craftainer_crud_sdk-0.1.0/.vscode/README.md +28 -0
- craftainer_crud_sdk-0.1.0/.vscode/extensions.json +3 -0
- craftainer_crud_sdk-0.1.0/.vscode/settings.json +4 -0
- craftainer_crud_sdk-0.1.0/.vscode/tasks.json +12 -0
- craftainer_crud_sdk-0.1.0/CLAUDE.md +144 -0
- craftainer_crud_sdk-0.1.0/LICENSE +674 -0
- craftainer_crud_sdk-0.1.0/Makefile +80 -0
- craftainer_crud_sdk-0.1.0/PKG-INFO +144 -0
- craftainer_crud_sdk-0.1.0/README.md +132 -0
- craftainer_crud_sdk-0.1.0/docs/README.md +35 -0
- craftainer_crud_sdk-0.1.0/docs/TEMPLATE.md +296 -0
- craftainer_crud_sdk-0.1.0/docs/adrs/0001-python-package-template.md +74 -0
- craftainer_crud_sdk-0.1.0/docs/adrs/0002-own-thin-cache-layer-with-sse-invalidation.md +134 -0
- craftainer_crud_sdk-0.1.0/docs/adrs/README.md +27 -0
- craftainer_crud_sdk-0.1.0/docs/adrs/template.md +23 -0
- craftainer_crud_sdk-0.1.0/docs/frs/README.md +30 -0
- craftainer_crud_sdk-0.1.0/docs/frs/template.md +21 -0
- craftainer_crud_sdk-0.1.0/docs/nfrs/README.md +28 -0
- craftainer_crud_sdk-0.1.0/docs/nfrs/template.md +27 -0
- craftainer_crud_sdk-0.1.0/docs/plans/README.md +63 -0
- craftainer_crud_sdk-0.1.0/docs/plans/template.md +35 -0
- craftainer_crud_sdk-0.1.0/pyproject.toml +97 -0
- craftainer_crud_sdk-0.1.0/scripts/README.md +29 -0
- craftainer_crud_sdk-0.1.0/scripts/develop.d/README.md +15 -0
- craftainer_crud_sdk-0.1.0/scripts/develop.sh +121 -0
- craftainer_crud_sdk-0.1.0/scripts/post-create.d/10-uv-sync.sh +6 -0
- craftainer_crud_sdk-0.1.0/scripts/post-create.d/README.md +12 -0
- craftainer_crud_sdk-0.1.0/scripts/post-create.sh +13 -0
- craftainer_crud_sdk-0.1.0/scripts/release-check.sh +90 -0
- craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/__init__.py +152 -0
- craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/auth.py +150 -0
- craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/cache/__init__.py +23 -0
- craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/cache/entry.py +145 -0
- craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/cache/keys.py +111 -0
- craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/cache/layer.py +740 -0
- craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/cache/memory.py +112 -0
- craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/cache/policy.py +61 -0
- craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/cache/redis.py +201 -0
- craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/client.py +952 -0
- craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/errors.py +217 -0
- craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/events.py +548 -0
- craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/interfaces.py +183 -0
- craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/manifest.py +397 -0
- craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/py.typed +0 -0
- craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/query.py +202 -0
- craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/wiring.py +152 -0
- craftainer_crud_sdk-0.1.0/tests/README.md +15 -0
- craftainer_crud_sdk-0.1.0/tests/__init__.py +1 -0
- craftainer_crud_sdk-0.1.0/tests/bench/__init__.py +1 -0
- craftainer_crud_sdk-0.1.0/tests/bench/test_entry_bench.py +11 -0
- craftainer_crud_sdk-0.1.0/tests/fixtures/README.md +6 -0
- craftainer_crud_sdk-0.1.0/tests/fixtures/manifest/admin.manifest.json +1078 -0
- craftainer_crud_sdk-0.1.0/tests/fixtures/manifest/admin.request.json +9 -0
- craftainer_crud_sdk-0.1.0/tests/fixtures/manifest/anonymous.manifest.json +281 -0
- craftainer_crud_sdk-0.1.0/tests/fixtures/manifest/anonymous.request.json +7 -0
- craftainer_crud_sdk-0.1.0/tests/fixtures/manifest/hero-reader.manifest.json +828 -0
- craftainer_crud_sdk-0.1.0/tests/fixtures/manifest/hero-reader.request.json +9 -0
- craftainer_crud_sdk-0.1.0/tests/fixtures/manifest/legacy-reader.manifest.json +531 -0
- craftainer_crud_sdk-0.1.0/tests/fixtures/manifest/legacy-reader.request.json +9 -0
- craftainer_crud_sdk-0.1.0/tests/fixtures/manifest/server.json +69 -0
- craftainer_crud_sdk-0.1.0/tests/fixtures/manifest/stranger.manifest.json +281 -0
- craftainer_crud_sdk-0.1.0/tests/fixtures/manifest/stranger.request.json +9 -0
- craftainer_crud_sdk-0.1.0/tests/unit/__init__.py +1 -0
- craftainer_crud_sdk-0.1.0/tests/unit/_fake_server.py +214 -0
- craftainer_crud_sdk-0.1.0/tests/unit/_support.py +68 -0
- craftainer_crud_sdk-0.1.0/tests/unit/cache/__init__.py +1 -0
- craftainer_crud_sdk-0.1.0/tests/unit/cache/harness.py +126 -0
- craftainer_crud_sdk-0.1.0/tests/unit/cache/test_async_layer.py +326 -0
- craftainer_crud_sdk-0.1.0/tests/unit/cache/test_keys_policy_entry.py +163 -0
- craftainer_crud_sdk-0.1.0/tests/unit/cache/test_layer.py +742 -0
- craftainer_crud_sdk-0.1.0/tests/unit/cache/test_memory_redis.py +306 -0
- craftainer_crud_sdk-0.1.0/tests/unit/concurrency/README.md +12 -0
- craftainer_crud_sdk-0.1.0/tests/unit/concurrency/__init__.py +1 -0
- craftainer_crud_sdk-0.1.0/tests/unit/concurrency/test_interleavings.py +380 -0
- craftainer_crud_sdk-0.1.0/tests/unit/concurrency/world.py +566 -0
- craftainer_crud_sdk-0.1.0/tests/unit/test_auth.py +195 -0
- craftainer_crud_sdk-0.1.0/tests/unit/test_client.py +636 -0
- craftainer_crud_sdk-0.1.0/tests/unit/test_client_async.py +317 -0
- craftainer_crud_sdk-0.1.0/tests/unit/test_errors.py +130 -0
- craftainer_crud_sdk-0.1.0/tests/unit/test_events.py +814 -0
- craftainer_crud_sdk-0.1.0/tests/unit/test_hardening.py +454 -0
- craftainer_crud_sdk-0.1.0/tests/unit/test_interfaces.py +69 -0
- craftainer_crud_sdk-0.1.0/tests/unit/test_manifest.py +273 -0
- craftainer_crud_sdk-0.1.0/tests/unit/test_query.py +195 -0
- craftainer_crud_sdk-0.1.0/tests/unit/test_scope_and_if_match.py +225 -0
- craftainer_crud_sdk-0.1.0/tests/unit/test_sdk_e2e.py +253 -0
- craftainer_crud_sdk-0.1.0/tests/unit/test_sdk_e2e_async.py +261 -0
- craftainer_crud_sdk-0.1.0/tests/unit/test_wiring_policies.py +18 -0
- craftainer_crud_sdk-0.1.0/uv.lock +1106 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# .claude/
|
|
2
|
+
|
|
3
|
+
Project-scope Claude Code configuration, shared with every collaborator
|
|
4
|
+
who opens this repo (see the root `.gitignore`, which does *not* exclude
|
|
5
|
+
this directory).
|
|
6
|
+
|
|
7
|
+
- `settings.json` — enables official plugins from the built-in
|
|
8
|
+
`claude-plugins-official` marketplace via `enabledPlugins`:
|
|
9
|
+
- `context7` — pulls up-to-date, version-specific library
|
|
10
|
+
documentation and examples into context from Context7's hosted
|
|
11
|
+
remote MCP server (`https://mcp.context7.com/mcp`); no local
|
|
12
|
+
install. Works anonymously; set a `CONTEXT7_API_KEY` env var for
|
|
13
|
+
higher rate limits.
|
|
14
|
+
- `security-guidance` — security review for Claude-generated code:
|
|
15
|
+
pattern-based warnings on `Edit`/`Write`, an LLM diff review when
|
|
16
|
+
Claude finishes a turn, and an agentic reviewer on `git commit`
|
|
17
|
+
that traces cross-file data flow. Needs only Python 3.8+ on
|
|
18
|
+
`PATH` — an instance without a Python runtime of its own should
|
|
19
|
+
confirm this is still true before relying on it.
|
|
20
|
+
|
|
21
|
+
`settings.json` also wires up the `PostToolUse` hook below.
|
|
22
|
+
- `hooks/self-check.sh` — after Claude edits a file, runs the fast,
|
|
23
|
+
pre-commit-stage hooks from `../.pre-commit-config.yaml` (whitespace,
|
|
24
|
+
YAML/TOML/JSON checks, ...) against just that file via `prek`, so
|
|
25
|
+
issues are caught and auto-fixed immediately instead of first at commit
|
|
26
|
+
time. An instance's own slower, pre-push-only hooks stay pre-push-only —
|
|
27
|
+
too slow to run after every edit.
|
|
28
|
+
- `agents/plan-executor.md` — the subagent that executes one work item
|
|
29
|
+
of a `docs/plans/` plan on a faster model (`model: sonnet`), under
|
|
30
|
+
the main session as orchestrator (see `../CLAUDE.md`, "Execute
|
|
31
|
+
multi-item work as an orchestrator"). It holds the executor's side of
|
|
32
|
+
the protocol: what a brief contains, the write-set and no-push rules,
|
|
33
|
+
the escalation triggers, and the report format.
|
|
34
|
+
- The `PreToolUse` hook on `Bash` runs `snip hook`, the
|
|
35
|
+
[snip](https://github.com/edouard-claude/snip) token-filtering proxy:
|
|
36
|
+
it rewrites supported commands (`git` and others; an instance's own
|
|
37
|
+
language tooling once it's added) to return only their signal
|
|
38
|
+
(failures, diffs, summaries) instead of full output. `snip` itself is
|
|
39
|
+
installed by `scripts/develop.sh`, pinned to `SNIP_VERSION` in
|
|
40
|
+
`../.devcontainer/Dockerfile`, and checksum-verified against its published release
|
|
41
|
+
checksums — see "Versions and config" in `docs/TEMPLATE.md`. Chosen
|
|
42
|
+
over the more popular
|
|
43
|
+
[rtk](https://github.com/rtk-ai/rtk) because its filters are
|
|
44
|
+
declarative YAML data (auditable) and it fails open (a broken filter
|
|
45
|
+
passes the command through unfiltered rather than blocking it); rtk's
|
|
46
|
+
larger community and multi-maintainer team are the tradeoff, so revisit
|
|
47
|
+
this choice if `snip` goes unmaintained.
|
|
48
|
+
- `../.mcp.json` — project-scope MCP servers not available as a
|
|
49
|
+
`claude-plugins-official` plugin, so they can't go through
|
|
50
|
+
`enabledPlugins` above:
|
|
51
|
+
- `clear-thought` (`@waldzellai/clear-thought-onepointfive`) — a
|
|
52
|
+
local stdio MCP server exposing structured reasoning/mental-model
|
|
53
|
+
tools, launched via `npx`. `npx` needs Node.js, which is the only
|
|
54
|
+
reason `scripts/develop.sh` installs it (pinned via
|
|
55
|
+
`../.devcontainer/Dockerfile`'s `NODE_VERSION` ARG) — Claude Code itself doesn't need it.
|
|
56
|
+
|
|
57
|
+
An instance adds its own stack-specific MCP servers here (database,
|
|
58
|
+
object storage, browser automation, ...), pinned to an exact
|
|
59
|
+
version/commit the same way, and reading credentials via `${VAR}` from
|
|
60
|
+
the same env vars its own `../.devcontainer/compose.yml` gives its dev
|
|
61
|
+
service — never re-pinned a second time.
|
|
62
|
+
|
|
63
|
+
## Do
|
|
64
|
+
|
|
65
|
+
- Keep this file's `enabledPlugins` pinned to specific plugin names —
|
|
66
|
+
don't rely on a whole marketplace being auto-installed.
|
|
67
|
+
|
|
68
|
+
## Don't
|
|
69
|
+
|
|
70
|
+
- Add personal preferences here — this file is committed and shared;
|
|
71
|
+
use your own user-scope Claude Code settings for those instead.
|
|
72
|
+
|
|
73
|
+
## Removing a tool
|
|
74
|
+
|
|
75
|
+
- **snip**: delete the `PreToolUse` hook entry in `settings.json` and the
|
|
76
|
+
`snip` install block (and `SNIP_VERSION` ARG in `../.devcontainer/Dockerfile`) from
|
|
77
|
+
`scripts/develop.sh`.
|
|
78
|
+
- **context7** / **security-guidance**: delete the plugin's
|
|
79
|
+
`enabledPlugins` entry in `settings.json`.
|
|
80
|
+
- **clear-thought**: delete its entry from `../.mcp.json`; if nothing
|
|
81
|
+
else needs Node.js, also remove its install block (and `NODE_VERSION`
|
|
82
|
+
ARG) from `../.devcontainer/Dockerfile`/`scripts/develop.sh`.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: plan-executor
|
|
3
|
+
description: Executes exactly one work item of a docs/plans/ plan, briefed by the orchestrating main session. Works only inside its brief's write set, in its own worktree, and hands back to the orchestrator instead of improvising when it hits an escalation trigger. Use one per independent work item; run independent items in parallel.
|
|
4
|
+
model: sonnet
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You execute one work item from a plan under `docs/plans/`. The
|
|
8
|
+
orchestrator (the main session) wrote your brief. It owns every decision
|
|
9
|
+
outside that brief and talks to the user. Your job is to finish your
|
|
10
|
+
item and report it accurately.
|
|
11
|
+
|
|
12
|
+
## Your brief contains
|
|
13
|
+
|
|
14
|
+
1. **Goal:** the work item, quoted from the plan, plus any Decision log
|
|
15
|
+
entries that apply. Decision log entries are settled. Don't reopen
|
|
16
|
+
them.
|
|
17
|
+
2. **Write set:** the only files and directories you may create, change
|
|
18
|
+
or delete. Everything else is read-only to you.
|
|
19
|
+
3. **Interfaces:** names and signatures you must provide, or may rely on
|
|
20
|
+
from items running in parallel. Code against them exactly as stated,
|
|
21
|
+
even if the other side doesn't exist yet in your worktree.
|
|
22
|
+
4. **Done when:** the checks that must pass before you report `done`.
|
|
23
|
+
|
|
24
|
+
## Rules
|
|
25
|
+
|
|
26
|
+
- Before writing anything, read `CLAUDE.md`, then every `README.md` on
|
|
27
|
+
the path to each file in your write set, root first (`CLAUDE.md`,
|
|
28
|
+
"Before writing anything").
|
|
29
|
+
- Before relying on a third-party library's API, look it up with
|
|
30
|
+
context7. Don't code from memory.
|
|
31
|
+
- Commit only to your own `wip/<id>` branch in your worktree. Never
|
|
32
|
+
push, open a PR, create a repo, tag, publish, or delete a branch.
|
|
33
|
+
- Don't run integration tests, e2e tests or load tests unless your
|
|
34
|
+
brief says to. They share the devcontainer's services, and the
|
|
35
|
+
orchestrator runs them one at a time in its own lanes. Run lint,
|
|
36
|
+
type-check and unit tests for your write set yourself.
|
|
37
|
+
- Show real check output. "Looks done" is not verification.
|
|
38
|
+
|
|
39
|
+
## Escalate: stop and hand back
|
|
40
|
+
|
|
41
|
+
Stop working and report `STATUS: escalate` as soon as any of these
|
|
42
|
+
happens. Don't work around it:
|
|
43
|
+
|
|
44
|
+
1. A required check still fails after two distinct fix attempts.
|
|
45
|
+
2. The fix needs a file outside your write set, or an interface that
|
|
46
|
+
differs from your brief.
|
|
47
|
+
3. Your brief contradicts the code, a `README.md`, an ADR or an NFR, or
|
|
48
|
+
is ambiguous enough that you'd be guessing.
|
|
49
|
+
4. A library or framework behaves differently from what your brief
|
|
50
|
+
assumes.
|
|
51
|
+
5. The only way to pass a check is a suppression comment, a lower
|
|
52
|
+
coverage threshold, or a weakened guardrail.
|
|
53
|
+
6. Anything under `.devcontainer/` would need to change. That needs a
|
|
54
|
+
rebuild only the user can trigger.
|
|
55
|
+
7. Any outward-facing or hard-to-reverse action would be needed.
|
|
56
|
+
8. A measurement contradicts the expected effect, or the run-to-run
|
|
57
|
+
spread is larger than the effect.
|
|
58
|
+
|
|
59
|
+
The orchestrator researches the problem, decides, and resumes you with
|
|
60
|
+
a message. Keep your worktree as it is when you escalate.
|
|
61
|
+
|
|
62
|
+
## Report: your final message, exactly this shape
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
STATUS: done | escalate
|
|
66
|
+
SUMMARY: <2-5 lines>
|
|
67
|
+
CHANGED: <files>
|
|
68
|
+
CHECKS: <command -> verbatim output tail, one per check>
|
|
69
|
+
COMMIT: <one-line message, then short bullets>
|
|
70
|
+
ESCALATION: <only when escalating: trigger number, what you tried,
|
|
71
|
+
evidence (verbatim output), options, your recommendation>
|
|
72
|
+
```
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# PostToolUse hook (Edit|Write): after Claude edits a file, run the fast,
|
|
3
|
+
# pre-commit-stage hooks from .pre-commit-config.yaml against just that
|
|
4
|
+
# file via prek -- reusing the same config as `git commit` would, instead
|
|
5
|
+
# of a second, separate set of checks -- so whitespace/lint issues are
|
|
6
|
+
# caught (and auto-fixed where possible) immediately, not first at commit
|
|
7
|
+
# time. An instance's own slower, pre-push-only hooks (type checker, test
|
|
8
|
+
# suite, lock-file check, ...) are deliberately excluded here: too slow to
|
|
9
|
+
# run after every single edit.
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
input="$(cat)"
|
|
13
|
+
file_path="$(jq -r '.tool_input.file_path // empty' <<<"$input")"
|
|
14
|
+
|
|
15
|
+
[ -n "$file_path" ] || exit 0
|
|
16
|
+
[ -f "$file_path" ] || exit 0
|
|
17
|
+
|
|
18
|
+
project_dir="${CLAUDE_PROJECT_DIR:-$PWD}"
|
|
19
|
+
case "$file_path" in
|
|
20
|
+
"$project_dir"/*) ;;
|
|
21
|
+
*) exit 0 ;;
|
|
22
|
+
esac
|
|
23
|
+
|
|
24
|
+
command -v prek >/dev/null 2>&1 || exit 0
|
|
25
|
+
|
|
26
|
+
cd "$project_dir"
|
|
27
|
+
relative_path="${file_path#"$project_dir"/}"
|
|
28
|
+
|
|
29
|
+
if ! output="$(prek run --files "$relative_path" 2>&1)"; then
|
|
30
|
+
echo "$output" >&2
|
|
31
|
+
echo "prek found issues in $relative_path (fixed automatically where possible) -- review the diff." >&2
|
|
32
|
+
exit 2
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
exit 0
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"attribution": {
|
|
3
|
+
"commit": "",
|
|
4
|
+
"pr": ""
|
|
5
|
+
},
|
|
6
|
+
"hooks": {
|
|
7
|
+
"PostToolUse": [
|
|
8
|
+
{
|
|
9
|
+
"matcher": "Edit|Write",
|
|
10
|
+
"hooks": [
|
|
11
|
+
{
|
|
12
|
+
"type": "command",
|
|
13
|
+
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/self-check.sh",
|
|
14
|
+
"timeout": 60
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"PreToolUse": [
|
|
20
|
+
{
|
|
21
|
+
"matcher": "Bash",
|
|
22
|
+
"hooks": [
|
|
23
|
+
{
|
|
24
|
+
"type": "command",
|
|
25
|
+
"command": "snip hook",
|
|
26
|
+
"timeout": 10
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
"enabledPlugins": {
|
|
33
|
+
"context7@claude-plugins-official": true,
|
|
34
|
+
"security-guidance@claude-plugins-official": true
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: owasp-scan
|
|
3
|
+
description: Scan every file under src/ for OWASP Top Ten (2025) issues, in parallel, and report findings.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# OWASP Top Ten scan
|
|
7
|
+
|
|
8
|
+
Check all files under `src/` against the OWASP Top Ten (2025) categories
|
|
9
|
+
and report what's found. Read-only — do not fix anything unless the user
|
|
10
|
+
asks afterward.
|
|
11
|
+
|
|
12
|
+
## Steps
|
|
13
|
+
|
|
14
|
+
1. **List target files.** `find src/ -type f -name "*.py"` (adjust the
|
|
15
|
+
extension filter if the tree isn't Python). Exclude generated/vendor
|
|
16
|
+
code if any exists under `src/`.
|
|
17
|
+
|
|
18
|
+
2. **Batch them.** Group files by their top-level subdirectory under
|
|
19
|
+
`src/app/` (`controllers`, `crud`, `models`, `repositories`, `views`,
|
|
20
|
+
`health`, etc.), splitting further so no batch exceeds ~8 files. Each
|
|
21
|
+
batch becomes one subagent task.
|
|
22
|
+
|
|
23
|
+
3. **Scan in parallel.** In a single message, launch one `Explore` agent
|
|
24
|
+
per batch (`run_in_background: false` is not needed — let them run in
|
|
25
|
+
parallel and collect results as they land). Give each agent:
|
|
26
|
+
- The exact file list for its batch (full paths).
|
|
27
|
+
- The checklist below.
|
|
28
|
+
- Instruction to report only findings it can point at a concrete
|
|
29
|
+
file:line and a concrete failure scenario (input/state that
|
|
30
|
+
triggers it) — no generic advice, no "consider adding X" without a
|
|
31
|
+
specific gap it fills.
|
|
32
|
+
- Instruction to reply in under 300 words per file with findings, or
|
|
33
|
+
say "no findings" for a file with none.
|
|
34
|
+
|
|
35
|
+
4. **Aggregate.** Merge all agents' findings into one list, drop
|
|
36
|
+
duplicates, sort most-severe first (Injection/Broken Access
|
|
37
|
+
Control/Auth failures above Security Misconfiguration above
|
|
38
|
+
Logging/Monitoring gaps).
|
|
39
|
+
|
|
40
|
+
5. **Report to the user** as a single markdown list grouped by OWASP
|
|
41
|
+
category, each item: `path:line — one-sentence defect — concrete
|
|
42
|
+
failure scenario`. Skip categories with no findings; don't pad the
|
|
43
|
+
report with reassurance about what's fine.
|
|
44
|
+
|
|
45
|
+
## OWASP Top Ten (2025) checklist for each agent
|
|
46
|
+
|
|
47
|
+
- **A01 Broken Access Control** — missing/incorrect authz checks,
|
|
48
|
+
IDOR (object access by ID without ownership check), path traversal,
|
|
49
|
+
CORS misconfig, and SSRF (outbound requests built from
|
|
50
|
+
user-controlled URLs/hosts without allowlisting — folded into this
|
|
51
|
+
category for 2025).
|
|
52
|
+
- **A02 Security Misconfiguration** — debug mode/verbose errors enabled
|
|
53
|
+
in non-dev paths, permissive defaults, unnecessary exposed
|
|
54
|
+
endpoints/features.
|
|
55
|
+
- **A03 Software Supply Chain Failures** — dependency/version pinned
|
|
56
|
+
elsewhere with a known vulnerability you can name, unpinned/floating
|
|
57
|
+
versions for security-sensitive packages, unverified third-party
|
|
58
|
+
build steps or CI actions.
|
|
59
|
+
- **A04 Cryptographic Failures** — plaintext secrets/passwords, weak or
|
|
60
|
+
homemade crypto/hashing, hardcoded keys, missing TLS enforcement.
|
|
61
|
+
- **A05 Injection** — SQL/NoSQL/OS command/LDAP injection via
|
|
62
|
+
unsanitized input, unsafe `eval`/template rendering, missing
|
|
63
|
+
parameterization.
|
|
64
|
+
- **A06 Insecure Design** — missing rate limiting or business-logic
|
|
65
|
+
guards where abuse is plausible (only flag with a concrete abuse
|
|
66
|
+
path, not speculative).
|
|
67
|
+
- **A07 Authentication Failures** — weak session/token handling,
|
|
68
|
+
missing expiry/invalidation, predictable tokens, password rules
|
|
69
|
+
absent where passwords are set.
|
|
70
|
+
- **A08 Software or Data Integrity Failures** — unsafe deserialization
|
|
71
|
+
(pickle, yaml.load without safe loader), unsigned/unverified updates
|
|
72
|
+
or artifacts.
|
|
73
|
+
- **A09 Security Logging & Alerting Failures** — security-relevant
|
|
74
|
+
events (auth failures, access-control denials) not logged, sensitive
|
|
75
|
+
data (passwords, tokens) logged in plaintext, or no alerting hook for
|
|
76
|
+
events that should page someone.
|
|
77
|
+
- **A10 Mishandling of Exceptional Conditions** — broad/bare exception
|
|
78
|
+
handlers that swallow security-relevant errors, error paths that
|
|
79
|
+
fail open (e.g. an auth check that defaults to "allow" if the
|
|
80
|
+
check itself throws), inconsistent error handling that leaks
|
|
81
|
+
internals in one path and hides failures in another.
|
|
82
|
+
|
|
83
|
+
## Notes
|
|
84
|
+
|
|
85
|
+
- This is a read pass, not `/security-review` or `/code-review` — don't
|
|
86
|
+
invoke those; this skill is narrower (OWASP-only, src/ only) and
|
|
87
|
+
faster because it fans out.
|
|
88
|
+
- If the user wants fixes applied afterward, treat that as a separate,
|
|
89
|
+
explicit follow-up — don't edit files as part of the scan itself.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1.28
|
|
2
|
+
# Single-stage build: develop (the devcontainer image). Built with the repo
|
|
3
|
+
# root as context (see compose.yml). The repo-root Dockerfile is reserved for
|
|
4
|
+
# an instance's own runtime image -- see docs/TEMPLATE.md's "Contents" and
|
|
5
|
+
# "Instance extension points".
|
|
6
|
+
|
|
7
|
+
ARG DEBIAN_VERSION=trixie
|
|
8
|
+
|
|
9
|
+
# renovate: datasource=github-releases depName=j178/prek
|
|
10
|
+
ARG PREK_VERSION=0.5.5
|
|
11
|
+
|
|
12
|
+
# renovate: datasource=npm depName=@anthropic-ai/claude-code
|
|
13
|
+
ARG CLAUDE_CODE_VERSION=2.1.296
|
|
14
|
+
|
|
15
|
+
# renovate: datasource=github-releases depName=edouard-claude/snip
|
|
16
|
+
ARG SNIP_VERSION=0.25.2
|
|
17
|
+
|
|
18
|
+
# renovate: datasource=github-releases depName=cli/cli
|
|
19
|
+
ARG GH_VERSION=2.102.0
|
|
20
|
+
|
|
21
|
+
# uv itself isn't a project dependency manager here -- it's only the
|
|
22
|
+
# mechanism scripts/develop.sh uses to install an exact, checksum-verified
|
|
23
|
+
# CPython build (see PYTHON_VERSION below) rather than an unpinned apt
|
|
24
|
+
# package. Left on PATH afterward; harmless, and an instance that adds
|
|
25
|
+
# Python as its own application runtime can reuse it directly.
|
|
26
|
+
# renovate: datasource=github-releases depName=astral-sh/uv
|
|
27
|
+
ARG UV_VERSION=0.13.0
|
|
28
|
+
|
|
29
|
+
# python3 is infrastructure tooling, not an application runtime this
|
|
30
|
+
# template assumes: prek needs an interpreter to build the venv for any
|
|
31
|
+
# "language: python" hook (.pre-commit-config.yaml's pre-commit-hooks
|
|
32
|
+
# repo), and .github/scripts/*.py need one directly.
|
|
33
|
+
# renovate: datasource=python-version depName=python
|
|
34
|
+
ARG PYTHON_VERSION=3.14.8
|
|
35
|
+
|
|
36
|
+
# Node.js is infrastructure tooling too, not an application runtime: npx
|
|
37
|
+
# (the clear-thought MCP server in .mcp.json) is the only thing that
|
|
38
|
+
# needs it.
|
|
39
|
+
# renovate: datasource=node-version depName=node
|
|
40
|
+
ARG NODE_VERSION=24.21.0
|
|
41
|
+
|
|
42
|
+
ARG SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
|
|
43
|
+
ARG SSL_CERT_DIR=/etc/ssl/certs
|
|
44
|
+
|
|
45
|
+
########################################
|
|
46
|
+
# develop — interactive devcontainer image, based on Microsoft's generic
|
|
47
|
+
# base devcontainer image. No *application* language runtime is assumed
|
|
48
|
+
# here — an instance adds its own (Python via uv, Rust via rustup, Go,
|
|
49
|
+
# Node.js, OpenTofu/Ansible, ...) on top of this stage, following the same
|
|
50
|
+
# ARG/scripts/develop.sh pattern established below. The Python and Node.js
|
|
51
|
+
# this stage does install are infrastructure tooling only -- see the ARG
|
|
52
|
+
# comments above.
|
|
53
|
+
########################################
|
|
54
|
+
FROM mcr.microsoft.com/devcontainers/base:${DEBIAN_VERSION} AS develop
|
|
55
|
+
ARG PREK_VERSION
|
|
56
|
+
ARG CLAUDE_CODE_VERSION
|
|
57
|
+
ARG SNIP_VERSION
|
|
58
|
+
ARG UV_VERSION
|
|
59
|
+
ARG PYTHON_VERSION
|
|
60
|
+
ARG NODE_VERSION
|
|
61
|
+
ARG GH_VERSION
|
|
62
|
+
ARG SSL_CERT_FILE
|
|
63
|
+
ARG SSL_CERT_DIR
|
|
64
|
+
|
|
65
|
+
ENV SSL_CERT_FILE=${SSL_CERT_FILE} \
|
|
66
|
+
SSL_CERT_DIR=${SSL_CERT_DIR} \
|
|
67
|
+
REQUESTS_CA_BUNDLE=${SSL_CERT_FILE} \
|
|
68
|
+
CURL_CA_BUNDLE=${SSL_CERT_FILE}
|
|
69
|
+
|
|
70
|
+
COPY scripts/develop.sh /tmp/develop.sh
|
|
71
|
+
RUN bash /tmp/develop.sh "$PREK_VERSION" "$CLAUDE_CODE_VERSION" "$SNIP_VERSION" "$UV_VERSION" "$PYTHON_VERSION" "$NODE_VERSION" "$GH_VERSION"
|
|
72
|
+
|
|
73
|
+
# Instance hook: scripts/develop.d/*.sh, see that directory's README.md.
|
|
74
|
+
COPY scripts/develop.d/ /tmp/develop.d/
|
|
75
|
+
RUN for f in /tmp/develop.d/*.sh; do [ -e "$f" ] || continue; echo "==> $f"; bash "$f" || exit 1; done
|
|
76
|
+
|
|
77
|
+
USER vscode
|
|
78
|
+
WORKDIR /workspace
|
|
79
|
+
CMD ["sleep", "infinity"]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Build context excludes for the devcontainer image (.devcontainer/Dockerfile).
|
|
2
|
+
# BuildKit reads <Dockerfile>.dockerignore next to the Dockerfile in place of
|
|
3
|
+
# the context root's .dockerignore, which stays free for an instance's own
|
|
4
|
+
# runtime image.
|
|
5
|
+
.git
|
|
6
|
+
.github
|
|
7
|
+
.devcontainer
|
|
8
|
+
.secrets
|
|
9
|
+
docs
|
|
10
|
+
README.md
|
|
11
|
+
CLAUDE.md
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# .devcontainer/
|
|
2
|
+
|
|
3
|
+
- `devcontainer.json` — references `compose.yml` and `compose.instance.yml`
|
|
4
|
+
as the devcontainer's `dockerComposeFile`, and configures the devcontainer itself
|
|
5
|
+
(features, mounts, editor settings).
|
|
6
|
+
- `Dockerfile` — the devcontainer image (a single `develop` stage), built
|
|
7
|
+
with the repo root as context; `Dockerfile.dockerignore` holds its
|
|
8
|
+
build-context excludes. The top-level `Dockerfile` is reserved for an
|
|
9
|
+
instance's runtime image (`../docs/TEMPLATE.md`).
|
|
10
|
+
- `compose.yml` — the dev service (`myapp`), built from `Dockerfile`'s
|
|
11
|
+
`develop` stage. An instance that needs backing services
|
|
12
|
+
(a database, a queue, ...) adds its own `stack/` directory (one
|
|
13
|
+
subdirectory per service, each with its own compose fragment) and an
|
|
14
|
+
`include:` list in `compose.instance.yml` pointing at them — see `stack/README.md`'s
|
|
15
|
+
"Devcontainer stack pattern" convention once an instance adds one.
|
|
16
|
+
- `compose.instance.yml` — instance-owned stub (`ignore` tier, never
|
|
17
|
+
touched by sync): an instance's `include:` list and extra `services.myapp`
|
|
18
|
+
settings. See `docs/TEMPLATE.md`'s "Instance extension points".
|
|
19
|
+
|
|
20
|
+
## Docker-in-Docker vs. the host's Docker
|
|
21
|
+
|
|
22
|
+
This devcontainer has its own isolated Docker-in-Docker daemon (the
|
|
23
|
+
`docker-in-docker` feature in `devcontainer.json`). It is **not** the
|
|
24
|
+
same daemon running any backing-service stack an instance adds — that
|
|
25
|
+
stack is started by whatever invoked "Reopen in Container" against the
|
|
26
|
+
*host's* Docker. So `docker`/`docker compose` run from inside the
|
|
27
|
+
devcontainer can build and run throwaway containers of their own, but can
|
|
28
|
+
never see or `exec` into a sibling container an instance's `stack/`
|
|
29
|
+
starts — only a `docker compose` invoked on the host can.
|
|
30
|
+
|
|
31
|
+
## SSH agent forwarding
|
|
32
|
+
|
|
33
|
+
`devcontainer.json`'s `mounts` bind-mounts two possible SSH agent
|
|
34
|
+
sockets, and `postStartCommand` runs `resolve-ssh-agent.sh` on every
|
|
35
|
+
container start to symlink whichever one actually works to
|
|
36
|
+
`/home/vscode/.ssh-agent-resolved.sock` — the path `remoteEnv` points
|
|
37
|
+
`SSH_AUTH_SOCK` at. Either way, `git push` over SSH inside the container
|
|
38
|
+
authenticates against the same agent and keys already loaded on the
|
|
39
|
+
host — no key material is ever copied into the container.
|
|
40
|
+
|
|
41
|
+
The two candidates:
|
|
42
|
+
|
|
43
|
+
- `/ssh-agent` — the host's own `SSH_AUTH_SOCK` (`${localEnv:SSH_AUTH_SOCK}`),
|
|
44
|
+
bind-mounted directly. This is a real Linux socket on WSL2, so normal
|
|
45
|
+
Unix permissions carry through and it works as-is (see "Windows" below
|
|
46
|
+
for getting an agent running there in the first place). If
|
|
47
|
+
`SSH_AUTH_SOCK` is unset when the container is created (true for
|
|
48
|
+
`devcontainers/ci` in `.github/workflows/checks.yml` — GitHub Actions
|
|
49
|
+
runners have no agent), `@devcontainers/cli` drops the empty source and
|
|
50
|
+
this mount degrades to a plain anonymous volume instead of erroring —
|
|
51
|
+
harmless there since CI never pushes.
|
|
52
|
+
- `/ssh-agent-macos` — Docker Desktop's own agent-forwarding proxy, always
|
|
53
|
+
available at the fixed path `/run/host-services/ssh-auth.sock` inside
|
|
54
|
+
its VM on both macOS and Windows. macOS has no real `SSH_AUTH_SOCK`
|
|
55
|
+
equivalent to WSL2's — the closest thing is this VM-internal proxy.
|
|
56
|
+
|
|
57
|
+
Both sockets arrive owned `root:root` mode `660` regardless of platform
|
|
58
|
+
or who owns the socket on the host side — bind-mounting a socket special
|
|
59
|
+
file carries over the owning uid/gid as seen inside the Docker Desktop
|
|
60
|
+
VM (root), not the host user's — so `remoteUser` (`vscode`) can't connect
|
|
61
|
+
to either without help. `resolve-ssh-agent.sh` runs `sudo chmod 666` on
|
|
62
|
+
each candidate before testing it (harmless: these are forwarding sockets
|
|
63
|
+
meant to be reachable by whatever connects to them, and chmod on a bind
|
|
64
|
+
mount only touches this one inode, not host files) — this needs the
|
|
65
|
+
passwordless `sudo` already granted to `vscode`.
|
|
66
|
+
|
|
67
|
+
Both also require an agent to actually be running on the host — on
|
|
68
|
+
macOS, `ssh-add -l` in a host Terminal should list a key; if it doesn't,
|
|
69
|
+
`ssh-add --apple-use-keychain ~/.ssh/id_ed25519` first — and a full
|
|
70
|
+
"Rebuild Container" after `mounts` changes (mounts don't apply to an
|
|
71
|
+
already-running container). `resolve-ssh-agent.sh` re-checks both
|
|
72
|
+
sockets on every start (not just container creation) since which one is
|
|
73
|
+
live can change if the host agent restarts between sessions.
|
|
74
|
+
|
|
75
|
+
### Windows
|
|
76
|
+
|
|
77
|
+
Launching VS Code the normal way (Start Menu, a desktop shortcut, or
|
|
78
|
+
"Open Folder") starts it with no `SSH_AUTH_SOCK` at all — Windows has
|
|
79
|
+
no such environment variable, even with an agent (Git Bash's own, or
|
|
80
|
+
the Windows OpenSSH Authentication Agent service) already holding your
|
|
81
|
+
keys — so this always hits the degraded-mount path above, silently, and
|
|
82
|
+
`git push`/`pull` inside the container has no key material to
|
|
83
|
+
authenticate with no matter what's in your Windows `~/.ssh`.
|
|
84
|
+
|
|
85
|
+
With Docker Desktop's WSL2 backend (not the Hyper-V backend — that one
|
|
86
|
+
has no comparable path), fix this by running the agent inside a WSL2
|
|
87
|
+
distro instead, since that's the same Linux environment Docker Desktop
|
|
88
|
+
runs containers in and shares socket/filesystem access with:
|
|
89
|
+
|
|
90
|
+
1. Make sure a real, user-facing WSL2 distro (e.g. Ubuntu — not just
|
|
91
|
+
the internal `docker-desktop` one) is installed and enabled under
|
|
92
|
+
Docker Desktop's Settings → Resources → WSL Integration.
|
|
93
|
+
2. Inside that distro, copy your keys in from Windows and start an
|
|
94
|
+
agent:
|
|
95
|
+
```sh
|
|
96
|
+
mkdir -p ~/.ssh && cp /mnt/c/Users/<you>/.ssh/id_ed25519* ~/.ssh/
|
|
97
|
+
chmod 700 ~/.ssh && chmod 600 ~/.ssh/id_ed25519
|
|
98
|
+
eval "$(ssh-agent -s)"
|
|
99
|
+
ssh-add ~/.ssh/id_ed25519
|
|
100
|
+
```
|
|
101
|
+
Put the `eval`/`ssh-add` lines in `~/.bashrc` (guard with
|
|
102
|
+
`ssh-add -l >/dev/null 2>&1 ||` so it doesn't spawn a new agent every
|
|
103
|
+
shell) so this survives new terminals without repeating it by hand.
|
|
104
|
+
3. From that same WSL shell, run `code .` — this opens VS Code via the
|
|
105
|
+
Remote-WSL extension first, with `SSH_AUTH_SOCK` inherited from the
|
|
106
|
+
shell that launched it.
|
|
107
|
+
4. From inside that window, run "Dev Containers: Reopen in Container".
|
|
108
|
+
`${localEnv:SSH_AUTH_SOCK}` now resolves to the WSL2 agent socket
|
|
109
|
+
instead of nothing, so the bind-mount above actually carries an
|
|
110
|
+
agent through.
|
|
111
|
+
|
|
112
|
+
Git Bash's own ssh-agent doesn't help here even though it holds the
|
|
113
|
+
same keys: its socket is an MSYS emulation, not a real Unix socket
|
|
114
|
+
Docker Desktop can bind-mount into a Linux container, and `code .` run
|
|
115
|
+
from a Git Bash prompt doesn't change what environment the VS Code GUI
|
|
116
|
+
process itself starts with anyway.
|
|
117
|
+
|
|
118
|
+
## After a rebuild
|
|
119
|
+
|
|
120
|
+
"Dev Containers: Rebuild Container" reruns `postCreateCommand` from
|
|
121
|
+
scratch (dependency installs, `prek install`, ...) — no manual setup
|
|
122
|
+
step there. Still worth doing once the container is back:
|
|
123
|
+
|
|
124
|
+
- Run `prek run --all-files --hook-stage manual` — the same check-suite
|
|
125
|
+
that gates CI (`checks.yml`) — to confirm whatever motivated the
|
|
126
|
+
rebuild (a `.devcontainer/Dockerfile`/`scripts/develop.sh` change, a new tool) works
|
|
127
|
+
end-to-end inside the container, not just that the image built.
|
|
128
|
+
- If the change added, removed, or reordered a `mounts` entry, confirm
|
|
129
|
+
it actually took effect — mounts apply only at container creation, not
|
|
130
|
+
on every start (see "SSH agent forwarding" above for the concrete
|
|
131
|
+
case: a mount change needs a full rebuild, not just a restart).
|
|
132
|
+
- If `forwardPorts`/`portsAttributes` changed, check the Ports panel
|
|
133
|
+
picked up the new list.
|
|
134
|
+
|
|
135
|
+
A plain "Reopen in Container" (no rebuild) is enough for changes outside
|
|
136
|
+
`.devcontainer/` and `scripts/develop.sh` — those only
|
|
137
|
+
need the window reconnected, not a fresh image.
|
|
138
|
+
|
|
139
|
+
## Do
|
|
140
|
+
|
|
141
|
+
- Add a new compose fragment's path to `compose.instance.yml`'s `include:`
|
|
142
|
+
list the same time you add the fragment — an unreferenced file starts
|
|
143
|
+
nothing.
|
|
144
|
+
- Keep service credentials and connection settings in the compose files'
|
|
145
|
+
`environment:` blocks, so opening the devcontainer is the only setup
|
|
146
|
+
step.
|
|
147
|
+
- Commit `devcontainer-lock.json` in the same change as any feature
|
|
148
|
+
edit in `devcontainer.json`. Regenerate it with
|
|
149
|
+
`npx @devcontainers/cli upgrade --workspace-folder .`, or by
|
|
150
|
+
rebuilding the devcontainer. Feature versions are bumped by hand,
|
|
151
|
+
not by Renovate: Renovate can't regenerate this file, so
|
|
152
|
+
`../.github/renovate.json` turns its devcontainer feature updates
|
|
153
|
+
off rather than let them merge with a stale lockfile.
|
|
154
|
+
|
|
155
|
+
## Don't
|
|
156
|
+
|
|
157
|
+
- Bind- or volume-mount a language runtime's dependency tree, or anything
|
|
158
|
+
else you don't want a host antivirus/DLP tool scanning or syncing — it
|
|
159
|
+
lives purely in the container's writable layer instead. (`claude-config`
|
|
160
|
+
and `gh-config` are deliberate, narrow exceptions — small login and
|
|
161
|
+
session state; see `compose.yml`'s comments on those volumes.)
|
|
162
|
+
- Add a `ports:` mapping or a `networks:` block anywhere here.
|
|
163
|
+
- Pin a feature or image version as `latest` — pin the exact version so
|
|
164
|
+
Renovate can bump it deliberately.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Dev service: the devcontainer itself, built from .devcontainer/Dockerfile's
|
|
2
|
+
# `develop` stage, with the repo root as build context. The workspace is bind-mounted so source
|
|
3
|
+
# edits are picked up without a rebuild. An instance that adds backing
|
|
4
|
+
# services (a database, a queue, ...) adds its own `include:` list to
|
|
5
|
+
# compose.instance.yml instead, pointing at its own `stack/*/compose.yml`
|
|
6
|
+
# fragments -- see
|
|
7
|
+
# .devcontainer/README.md's "Docker-in-Docker vs. the host's Docker".
|
|
8
|
+
services:
|
|
9
|
+
# Not `app`: Chrome HSTS-preloads the whole `.app` TLD, which also
|
|
10
|
+
# matches the bare hostname `app`, so a browser container (e.g. an
|
|
11
|
+
# instance's Selenium) would force http://app:<port> to HTTPS.
|
|
12
|
+
myapp:
|
|
13
|
+
build:
|
|
14
|
+
context: ..
|
|
15
|
+
dockerfile: .devcontainer/Dockerfile
|
|
16
|
+
target: develop
|
|
17
|
+
volumes:
|
|
18
|
+
- ..:/workspace:cached
|
|
19
|
+
# No volume for a language-runtime environment (venv, node_modules,
|
|
20
|
+
# target/, ...): a mounted one gets swept by Windows DLP/malware-
|
|
21
|
+
# scanning tools just like a bind-mounted one, so it lives purely in
|
|
22
|
+
# the container's writable layer instead -- see each language's own
|
|
23
|
+
# README for its specific env var/path convention once an instance
|
|
24
|
+
# adds one.
|
|
25
|
+
- claude-config:/home/vscode/.claude
|
|
26
|
+
- gh-config:/home/vscode/.config/gh
|
|
27
|
+
command: sleep infinity
|
|
28
|
+
# No ports: mapping and no networks: block — see .devcontainer/
|
|
29
|
+
# README.md's "Don't". An instance that needs host access adds
|
|
30
|
+
# forwardPorts to devcontainer.json; one with backing services
|
|
31
|
+
# reaches them by name on the default network compose generates for
|
|
32
|
+
# this project.
|
|
33
|
+
|
|
34
|
+
volumes:
|
|
35
|
+
# Persists Claude Code CLI session/credential state across container
|
|
36
|
+
# rebuilds. A small amount of session-state data, not a language
|
|
37
|
+
# runtime's dependency tree, so the DLP/malware-scanning concern above
|
|
38
|
+
# doesn't apply the same way here.
|
|
39
|
+
claude-config:
|
|
40
|
+
# Persists the GitHub CLI's login (`gh auth login`) across rebuilds, for
|
|
41
|
+
# the same reason.
|
|
42
|
+
gh-config:
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"features": {
|
|
3
|
+
"ghcr.io/devcontainers/features/docker-in-docker:4.1.3": {
|
|
4
|
+
"version": "4.1.3",
|
|
5
|
+
"resolved": "ghcr.io/devcontainers/features/docker-in-docker@sha256:fae40f3c8be5a7c3b0db344c1d30341bc2c91ba38d1d5b14cd2ad72e6d65d084",
|
|
6
|
+
"integrity": "sha256:fae40f3c8be5a7c3b0db344c1d30341bc2c91ba38d1d5b14cd2ad72e6d65d084"
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
}
|