|
| 1 | +# wpm |
| 2 | + |
| 3 | +Go package manager for WordPress plugins and themes. Cobra-based CLI, binary |
| 4 | +name `wpm`. See `@README.md` for the user-facing overview. |
| 5 | + |
| 6 | +**This is a Go project.** `package.json`, `bun.lock`, and `node_modules/` exist |
| 7 | +only to vendor `oxfmt` for Markdown/JSON/YAML formatting. Never propose |
| 8 | +JavaScript for runtime code, and never add a Node dep for anything but |
| 9 | +formatting. |
| 10 | + |
| 11 | +## Commands |
| 12 | + |
| 13 | +- Build: `go build -o build/wpm ./cmd/wpm` (or `./scripts/build/binary`) |
| 14 | +- Run without building: `go run ./cmd/wpm <command>` |
| 15 | +- Format Go: `golangci-lint fmt` — **never** call `go fmt`, `gofmt`, or |
| 16 | + `gofumpt` directly. Project `gci` order, `gofumpt` rules, and |
| 17 | + `interface{} → any` rewrite all flow through this command. |
| 18 | +- Lint Go: `golangci-lint run` |
| 19 | +- Format non-Go: `bunx format` |
| 20 | +- Test single package: `go test ./pkg/pm/installer/...` |
| 21 | +- Test with race detector (required for `pkg/pm/installer` or |
| 22 | + `pkg/pm/resolution` work): `go test -race ./...` |
| 23 | +- Regenerate CLI reference docs: `./scripts/docs/generate-md` |
| 24 | + |
| 25 | +## Layout |
| 26 | + |
| 27 | +Three-layer separation — do not collapse layers for convenience: |
| 28 | + |
| 29 | +- `cmd/wpm/` — entrypoint and signal handling |
| 30 | +- `cmd/docgen/` — regenerates marker regions in `docs/cli/*.md` |
| 31 | +- `cli/` — arg parsing, output, error→exit-code mapping. Commands registered in |
| 32 | + `cli/command/commands/commands.go`. **The `cli/` layer must not contain |
| 33 | + package-manager logic** — it builds an options struct and delegates to `pkg/`. |
| 34 | +- `pkg/pm/{wpmjson,wpmlock,wpmignore,registry,resolution,installer,workspace,signatures}` |
| 35 | + — the package-manager engine |
| 36 | +- `pkg/api/` — registry HTTP client and cache |
| 37 | +- `pkg/archive/` — tar/zip extraction (handles untrusted content) |
| 38 | +- `pkg/output/`, `pkg/progress/`, `pkg/streams/` — CLI UI primitives |
| 39 | +- `pkg/{config,version,wp,jsonpretty,asciisanitizer,unsafeconv}` — utilities |
| 40 | + |
| 41 | +## Project conventions |
| 42 | + |
| 43 | +- **No `fmt.Printf`/`Println` in `pkg/`.** Return values or use `pkg/output` / |
| 44 | + `pkg/progress` so the CLI can honor `--verbose` and future JSON output. |
| 45 | + `forbidigo` enforces. |
| 46 | +- **Errors:** never `panic()` outside `main`. Wrap with `%w`. Inspect with |
| 47 | + `errors.Is` / `errors.As` — never string-match error messages. |
| 48 | +- **Concurrency:** use `golang.org/x/sync/errgroup`. Always honor |
| 49 | + `context.Context` cancellation. |
| 50 | +- **Logging:** use `logrus`, not stdlib `log` (depguard enforces). Stdlib |
| 51 | + `io/ioutil` is denied — use `os` / `io`. |
| 52 | +- **CLI reference docs** (`docs/cli/*.md`) have an auto-generated marker block |
| 53 | + (`<!---MARKER_GEN_START-->` / `<!---MARKER_GEN_END-->`). **Never edit inside |
| 54 | + the markers.** Only `## Description` and `## Examples` are read by the |
| 55 | + generator — don't add other top-level `##` headings. Full conventions and |
| 56 | + workflow: `@docs/README.md`. |
| 57 | + |
| 58 | +## Testing |
| 59 | + |
| 60 | +- **Append tests to the existing `_test.go`** for the code you're changing. |
| 61 | + Create a new test file only when adding a new source file with no natural test |
| 62 | + home. |
| 63 | +- **Verify the test actually tests your change.** A passing test that would also |
| 64 | + pass on `origin/main` isn't testing your fix: |
| 65 | + |
| 66 | + ```sh |
| 67 | + git stash |
| 68 | + go test -run TestName ./path/to/pkg # should FAIL |
| 69 | + git stash pop |
| 70 | + go test -run TestName ./path/to/pkg # should PASS |
| 71 | + ``` |
| 72 | + |
| 73 | + If it passes in both states, rewrite the test. |
| 74 | + |
| 75 | +- **Never use `time.Sleep` to wait for a condition.** Use channels, |
| 76 | + `sync.WaitGroup`, `context` deadlines, or bounded polling against the actual |
| 77 | + condition. |
| 78 | +- Mock the registry with `net/http/httptest`. Existing patterns: `pkg/api/`, |
| 79 | + `pkg/pm/registry/`. |
| 80 | +- Use `t.TempDir()`, `t.Setenv()`, `t.Helper()` (lint-enforced). |
| 81 | +- For CLI output assertions, drive through `cli/command.Cli` and capture `Out()` |
| 82 | + / `Err()`. Don't touch global state. |
| 83 | + |
| 84 | +## Git & remote operations |
| 85 | + |
| 86 | +- **Explicit ask → do it.** "Commit", "push", "open a PR" — perform the |
| 87 | + operation and report what was done. Don't re-ask. |
| 88 | +- **Ambiguous ask → confirm first.** If changes were described but no |
| 89 | + push/PR/tag word appeared, confirm before any remote-write operation. Local |
| 90 | + commits on a working branch don't need confirmation. |
| 91 | +- **Never push directly to `main`.** Always open a PR. |
| 92 | +- **Never bypass safety controls**: no `--no-verify`, no force-push to shared |
| 93 | + branches, no skipping signature verification. |
| 94 | +- Branch names: any reasonable name. Attribution belongs in the PR body. |
| 95 | + |
| 96 | +## Before referencing a symbol |
| 97 | + |
| 98 | +Don't assume functions, interfaces, fields, methods, flags, or config keys |
| 99 | +exist. Search the repo or read the file first. Common traps: |
| 100 | + |
| 101 | +- CLI flags → `cli/flags/` and `cli/command/<name>/` |
| 102 | +- `wpm.json` fields → `pkg/pm/wpmjson/types/types.go` |
| 103 | +- Command names / aliases → `cli/command/commands/commands.go` |
| 104 | +- `Cli` interface methods → `cli/command/cli.go` |
| 105 | +- Issue / PR numbers (in tests, comments, commits) — must be real, never |
| 106 | + placeholders |
| 107 | + |
| 108 | +## Deeper context — fetch when relevant |
| 109 | + |
| 110 | +Project documentation is hosted at <https://wpm.so/docs>. Do not preload these; |
| 111 | +fetch on demand when the task touches the area. |
| 112 | + |
| 113 | +- **Discover what's available**: <https://wpm.so/docs/sitemap.xml> |
| 114 | +- **Read a page as markdown** — transform the URL: |
| 115 | + |
| 116 | + ``` |
| 117 | + page: https://wpm.so/docs/fundamentals/dependencies |
| 118 | + md: https://wpm.so/docs/llms.mdx/docs/fundamentals/dependencies/content.md |
| 119 | + ``` |
| 120 | + |
| 121 | +Typical triggers: `wpm.json` schema or scripts work → fundamentals docs; |
| 122 | +registry/auth/tokens → registry docs; resolution or lockfile semantics → |
| 123 | +resolution docs; signature verification → signatures docs. |
| 124 | + |
| 125 | +## Gotchas |
| 126 | + |
| 127 | +1. **Project root**: don't assume `os.Getwd()` is the `wpm.json` root in nested |
| 128 | + package logic. Walk up, or accept the root as a parameter. |
| 129 | +2. **`wpm.json` field changes** require all of: update |
| 130 | + `pkg/pm/wpmjson/types/types.go` (and `validator/` if constrained), update |
| 131 | + `README.md` and `docs/`, add a manifest test case. |
| 132 | +3. **Untrusted archives**: `pkg/archive` and `pkg/pm/installer` extract registry |
| 133 | + content. Zip Slip is a real risk — reuse the existing extraction helpers, |
| 134 | + don't reimplement. |
0 commit comments