Repository navigation
🔧 Import sphinx-codelinks into the workspace as packages/sphinx-codelinks - #1871
Merged
Merged
Conversation
--------- Co-authored-by: Marco Heinemann <marco.heinemann@useblocks.com>
Co-authored-by: Marco Heinemann <marco.heinemann@useblocks.com>
…t-metadata 🔧 Improve project info
…release 🚀 Release 0.1.1
- added minimum config project - applied defaults when the config is not given - adapted test cases - simplified docs configs
…fault-config 🔧Fix default config
Co-authored-by: Marco Heinemann <marco.heinemann@useblocks.com>
…e-0.1.2 🚀 release 0.1.2
…eline-link 🐛fixed link
- use github-hosted runners - not specified cache dir in macos
This was referenced Sep 6, 2026
Closed
Add -W/--strict flag and shared suppress_warnings to codelinks analyse
useblocks/sphinx-codelinks#93
Closed
chrisjsewell
added a commit
that referenced
this pull request
Sep 6, 2026
… not 25 `ci.yaml`'s concurrency comment carries the same number the previous commit corrected elsewhere, and it moved for the same reason: #1871 added `Docs codelinks` and widened `tests-mounts` into `tests-extensions`, so a run is 26 jobs, not 25 (counted on run 34057129950, `ci.yaml` on `a3aebf1f`, success). The argument the comment makes -- a fix-up push would otherwise leave every one of them running against 60 org-wide runners -- is unchanged and only gets stronger.
This was referenced Sep 6, 2026
chrisjsewell
added a commit
that referenced
this pull request
Sep 6, 2026
…ign left behind (#1897) Follow-up to #1870 (the committed jar at `vendor/plantuml/`), from `master` after #1871, closing the four actionable findings of that pull request's round-2 review. **No behaviour changes**: two new tests, two strengthened assertions, four string literals and three sentences — nothing under any `src/`, so no changelog entry. ## The fence had no fence (review finding 5) The committed jar rests on **one flag**. `--verify` hashes the jar against `pin.toml` and never touches the network; without it the same script is the *bump* tool, which **downloads**. A site that lost the flag would turn the fence into a self-healing download that exits **0** on exactly the mistake it exists to catch — a bumped pin with no committed jar — repairing the runner's tree and reporting green (measured in review: with the flag, `EXIT=1` naming the missing jar; without it, a 29 MB download and `EXIT=0`). Nothing here could see that: `check_workspace.py` reads manifests, `actionlint` cannot know what a flag means, and no test executed a workflow. `tools/tests/test_ci_plantuml_steps.py` parses every `.github/workflows/*.y{a,}ml` and `.github/actions/*/action.y{a,}ml`, finds each step whose `run:` invokes the script, and asserts: - **`--verify`** at all **8** sites; and, for the **7** that capture the path, **`| tr -d '\r'`**, **`shell: bash`**, and the **very next line** writing `PLANTUML_JAR=` to `$GITHUB_ENV`; - the eighth — CI's Lint job, a bare check with no pipe and no capture — is exempt from the last two, and that exemption is *asserted* rather than granted by being left off a list; - **a positive control**: the site count. Every other case is parametrised over the walk, so without it an empty walk or a renamed script would report a tidy row of passes. Both clauses are load-bearing invisibly. Without `shell: bash` the runner's default `bash -e` has no `pipefail`: the failing script inside `$( )` exits 0, the step goes **green**, and `PLANTUML_JAR` is exported **empty**, which every consumer reads as "unset". Without the `tr`, Windows exports a path with a trailing CR that prints identically in a log and names no file. 31 cases, each named after the file and step; Lint already runs `pytest tools/tests`. ## A zero-byte jar (finding 4) `--verify` already refuses one — the hash of nothing is a real sha256 — but nothing pinned it: a `st_size == 0` early return that accepted the file left all 263 tests green. `test_verify_refuses_an_empty_jar` writes a 0-byte jar and asserts exit 1 and `e3b0c442…b855` in the message — what a truncated checkout or a failed LFS smudge leaves behind. ## "the jar `poe fetch-plantuml` puts in `vendor/plantuml/`" (finding 1) A leftover from the fetched design #1870 dropped, in **four** places. `poe fetch-plantuml` does not put the jar there — the checkout does, and the task re-hashes it; and the caller that reaches this message in CI is `--verify`, which fetches nothing at all. All four now read "…or unset it to render with the jar **committed at** `vendor/plantuml/`.", word for word, and both tests that reach it assert that clause instead of the substring `"unset it"`. ## "22 jobs … (measured: 24 jobs, all but `Lint` and the smoke test)" (finding 2) 22 was right; 24 was not, and the job that went missing was `check`, the alls-green aggregator. Re-counted on this head from run `34057129950` (`ci.yaml` on `a3aebf1f`, success): **26** jobs, now that #1871 has added `Docs codelinks` and widened `tests-mounts` into `tests-extensions`. The four that do not render are `Lint`, the smoke test, `Docs codelinks` (those docs draw no diagrams) and `check`. Corrected in the root `AGENTS.md`, `vendor/plantuml/README.md` and the docstring; the README also gains a paragraph naming the new test. **Mutations.** `--verify` dropped at one site · `shell: bash` at one · the `tr` at one · capture and export collapsed into one `echo` · `verify()` made to accept an empty file — **5 of 5 red**, each naming the file and step, everything else green.
chrisjsewell
added a commit
that referenced
this pull request
Sep 15, 2026
…st-reports The whole of useblocks/sphinx-test-reports' `master` -- 161 commits, tip `ca74b80` (🚀 Release v2.0.0, useblocks/sphinx-test-reports#160) -- rewritten by `git filter-repo` so that every historical commit already places its files under `packages/sphinx-test-reports/`, and merged here with `--allow-unrelated-histories`. It is the third such import, after sphinx-mounts (#1850) and sphinx-codelinks (#1871). Note the qualification above, because this message is the one place the rule below could not reach: it was written by hand AFTER filtering, so nothing rewrote its references. An unqualified number here would autolink to the sphinx-needs issue that happens to carry it, which is exactly the error the cap exists to prevent -- so every sphinx-test-reports reference in this message is written out in full. `#1850` and `#1871` ARE sphinx-needs numbers and are correctly bare. Three things came with it: * **Tags.** Its ten releases arrive prefixed, `sphinx-test-reports-v0.3.6` ... `sphinx-test-reports-v2.0.0`. The prefix is load-bearing rather than hygienic: six of the ten bare names -- `1.0.1`, `1.0.2`, `1.1.0`, `1.2.0`, `1.3.0` and `2.0.0` -- are existing sphinx-needs releases, and `release_plan.py` reads every tag without `-v` in it as a sphinx-needs release. * **A scoped commit-message rewrite.** Bare `#N` became `useblocks/sphinx-test-reports#N`, 150 of them, bounded to that repository's own number range, 1 to 161. Exactly three references are deliberately left bare, all in one commit ("build: require sphinx-needs 6.0.1 and Sphinx 7.4"): `#1468`, `#1527` and `#1611` are **sphinx-needs** issues, and left bare they autolink here to the issues they actually mean. A lazy `#([0-9]+)` rule would have mangled all three into references that do not exist. * **The PlantUML jars, removed.** `--path-glob '*.jar' --invert-paths` strips three jar blobs -- 19.97 MB, 94 % of this import's permanent weight -- from every historical tree. The import adds 1.07 MiB of objects instead of 18.75 MiB. This workspace already vendors one PlantUML jar, once, at `vendor/plantuml/`, under a `verify-plantuml` gate. The imported tree is otherwise byte-for-byte the source clone at `ca74b80`; the commit map records the strip and the blob hashes. `git log packages/sphinx-test-reports/<path>` reads with no `--follow`: filter-repo rewrote the paths in every commit, so from this repository's point of view those files were always there.
This was referenced Sep 16, 2026
Merged
chrisjsewell
added a commit
that referenced
this pull request
Sep 16, 2026
… fails every uv job Every rebase or recreate of #1940, and the run after #1944 reverted the versioning strategy, errored with "Dependabot can't resolve your Python dependency files". #1944 blamed the strategy; that was wrong, and this commit says so where the claim was made. The failing probe is docutils: since sphinx-codelinks arrived (#1871) with a bare `docutils` dependency, docutils is a direct, unpinned dependency, so dependabot tries to lock 0.23 -- by writing `docutils==0.23` on every requirement line it finds, the `typing` group's `docutils~=0.20.0` included -- and uv refuses that beside `sphinx~=7.4`. The lock already carries docutils at 0.20.1 in the typing split and 0.21.2 elsewhere, which is what `[tool.uv] conflicts` is for; dependabot has no way to express that, and the uv updater treats the refusal as fatal to the whole job. So docutils joins types-docutils under the same ignore: major and minor updates ignored, patches inside the series still flow, and the default environment's docutils moves by hand with `uv lock --upgrade-package docutils`. The earlier rationale for not listing it -- keeping new series flowing to the main environment -- never operated, because docutils was not a direct dependency until #1871; the default environment sat on 0.21.2 with 0.23 out. `increase-if-necessary` is therefore untested rather than unusable, and the comment records it as a candidate to retry once this rule is seen to keep the jobs green; the AGENTS.md paragraph is corrected to match.
chrisjsewell
added a commit
that referenced
this pull request
Sep 16, 2026
… fails every uv job (#1946) Adds `docutils` to the uv ecosystem's ignore list for major and minor updates, beside `types-docutils`, and corrects a wrong claim #1944 made. ### The error, and what it is not Every rebase or recreate of #1940 errored with "Dependabot can't resolve your Python dependency files", and so did the rebase job that ran after #1944 reverted `versioning-strategy`, under the default: ``` Because sphinx>=7.4.0,<=7.4.7 depends on docutils>=0.20,<0.22 and sphinx-needs-workspace:typing depends on docutils==0.23, … ``` #1944 attributed the error to `increase-if-necessary`. That was wrong: the same job fails under the default strategy. The comment in `dependabot.yml` and the paragraph in `AGENTS.md` now say so where the claim was made. ### What it is docutils 0.23 has been out since May. What changed is #1871: sphinx-codelinks arrived with a bare `docutils` dependency, so docutils became a *direct, unpinned* dependency in dependabot's eyes, and dependabot now tries to lock 0.23. It does that by writing `docutils==0.23` on every requirement line it finds, including the `typing` group's `docutils~=0.20.0`, and uv correctly refuses that beside `sphinx~=7.4`, which needs docutils below 0.22. The lock already carries docutils at 0.20.1 in the typing split and 0.21.2 everywhere else, which is exactly what `[tool.uv] conflicts` provides; dependabot has no way to express "0.20.x here, newest elsewhere", and the uv updater treats uv's refusal as fatal to the whole job. Group refreshes before #1871 (the 5 September batch, #1851) rebased fine. ### The fix docutils joins `types-docutils` under the same ignore: major and minor updates ignored, patches inside the series still flow. The earlier rationale for leaving docutils unlisted, keeping new series flowing to the main environment, never operated: dependabot only moves a lock entry for a direct dependency, and docutils was not one until #1871, so the default environment sat on 0.21.2 with 0.23 out. It moves by hand now, with `uv lock --upgrade-package docutils`, the way the typing series itself does. ### Consequences for the strategy question `increase-if-necessary` (#1942) was reverted for a reason that turns out not to hold, so it is untested rather than unusable. The comment records it as a candidate to retry once this rule has been seen to keep the jobs green; that is a separate decision and a separate pull request. ### Proof after the merge The dependabot run this config change triggers should complete green on the Insights page, where the red "Rebase update" entries are. There is no open dependabot PR to refresh right now, so that run is the evidence. ### Checked `uv run poe lint` 8/8. No lock change.
ubmarco
pushed a commit
to useblocks/sphinx-codelinks
that referenced
this pull request
Sep 30, 2026
A notice at the top of the README: the last change before this repository is archived. sphinx-codelinks now lives in useblocks/sphinx-needs under [`packages/sphinx-codelinks`](https://github.com/useblocks/sphinx-needs/tree/master/packages/sphinx-codelinks), imported with its full history in useblocks/sphinx-needs#1871. Its issues moved at the import, and the eight pull requests still open here were closed with a pointer to useblocks/sphinx-needs#1898, which carries a measured plan for moving each one; their branches stay, so nothing is lost. The pinned #107 stays as it is. The package keeps its name on PyPI and its documentation URL, and its next release from the monorepo points PyPI's links at the new repository. After this merges, the repository is archived.
patdhlk
pushed a commit
to patdhlk/sphinx-needs
that referenced
this pull request
Oct 7, 2026
The whole of useblocks/sphinx-ai-index' `main` -- 7 commits, tip `74c783b9` ("update
changelog", the tree tagged `v0.1.0` and published to PyPI as sphinx-ai-index 0.1.0) --
rewritten by `git filter-repo --to-subdirectory-filter packages/sphinx-ai-index` so that
every historical commit already places its files under `packages/sphinx-ai-index/`, and
merged here with `--allow-unrelated-histories`. It is the fourth such import, after
sphinx-mounts (useblocks#1850), sphinx-codelinks (useblocks#1871) and sphinx-test-reports (useblocks#1931).
* **The tree is the source, byte for byte.** `git archive` of the source clone at
`74c783b9` and of `packages/sphinx-ai-index` here extract to the same 22 files
(`diff -rq` exit 0), and the merge touches nothing outside the prefix. Nothing was
stripped and no commit message was rewritten: none of the seven carries an issue
reference, so there was nothing to qualify.
* **The tag.** Its one release arrives as `sphinx-ai-index-v0.1.0`
(`--tag-rename 'v:sphinx-ai-index-v'`, which strips the source's own `v`; the bare
`':sphinx-ai-index-v'` form gives `sphinx-ai-index-vv0.1.0`). It points at the imported
tip, `6e4b36da`, outside master, as the siblings' prefixed tags do.
* **The commit map.** filter-repo's map from the seven old hashes to the seven new ones is
recorded at `packages/sphinx-ai-index/design/import-commit-map.txt` by a following
commit, which keeps this merge's tree exactly the source's.
The member's wiring follows in the next commit: the root manifest, its source entry and the
relock. Until then `uv lock --check` fails here -- sphinx-needs' `docs` extra already names
sphinx-ai-index, which the `packages/*` glob now finds as a workspace member with no
`[tool.uv.sources]` entry.
`git log packages/sphinx-ai-index/<path>` reads with no `--follow`: filter-repo rewrote the
paths in every commit, so from this repository's point of view those files were always
there.
5 tasks done
chrisjsewell
added a commit
that referenced
this pull request
Oct 8, 2026
…scope association (moved from sphinx-codelinks#100) (#2128) ## What `packages/sphinx-codelinks`: moves useblocks/sphinx-codelinks#100 into the monorepo with the recipe from #1871 (11 commits, authorship kept), and addresses that PR's review on top. The declarative extraction fixtures (`tests/data/extraction/*.yaml`) now snapshot **the real production output**: exactly what `SourceAnalyse.dump_marked_content()` writes, as the flat sorted list of `Metadata.to_dict()` items. Before, they held a test-only projection. Warnings are a separate `[warnings].json` artefact per case. Three deviations from production are documented in the harness and the README: - `filepath` is relative to the case root. - One additive, test-only `tagged_scope_type` key is added. - The warnings are sorted (production reports them in tree-sitter capture order, which differs between runs). New `scope.yaml` (25 cases) covers marker scope association: above a function, inside a body, no scope, the Python docstring path, YAML's structure finder, and C++ class and struct bodies. ## Why Scope association (`find_associated_scope`) had no fixture coverage at all. Snapshotting the real shape also covers `filepath`, `source_map` columns and end positions, `type` and `marker`, which the projection dropped. Review of useblocks/sphinx-codelinks#100, addressed here: 1. **Warnings snapshots were order-unstable.** `_build_warnings` sorts the records again (`file_path, lineno, type, sub_type, msg`). Production reports them in tree-sitter capture order, which differs between two runs of the same input once several extractors are on, so it cannot be pinned. 2. **`SCOPE_NODE_TYPES[cpp]` named `class_definition`,** a kind tree-sitter-cpp never emits, so a marker in a class body had no scope. It now names `class_specifier` and `struct_specifier`, pinned by `scope.yaml`'s `cpp_class_body` / `cpp_struct_body` (🐛, changelog entry). 3. **The field-count wording** was already fixed on master by #2092 ("minimum is" / "maximum is"). Also from the review: - `test_extraction_is_crlf_insensitive` was imperative. It becomes a declarative `line_endings` key on the case schema, plus `line_endings.yaml` (CRLF and a lone CR); such a case also runs with LF endings and must match. - The extraction corpus is shared with ubCode, which copies the YAML files it also has (today 11 of the 14) byte for byte and takes these snapshots as the expected output of its parity test; the README and the harness header now say so, and useblocks/ubcode#3928 tracks how ubCode's sync script takes up this production shape. The LF pin moves to the root `.gitattributes` with that reason, and yamlfmt no longer formats the corpus (closes #2133). Production fixes the fixtures surfaced (physical columns, the need-ref span, docstring tags, a block comment's `*/` as an id) are in #2129, stacked on this one, so this stays reviewable as a move. ## Review of this PR, addressed on the branch (taken over 2026-10-08 with Marco's go) Four commits on top of Marco's, no source, corpus or snapshot change: - The README and the harness header state that the corpus and snapshots are shared with ubCode (the first bullet above). - The LF pin for the corpus and snapshots lives in the root `.gitattributes`, one commented block like the three conformance corpora, with the real reason: ubCode copies the bytes. The tests do not depend on it (YAML normalises line breaks, the harness writes each case's source as exact bytes, syrupy reads snapshots in text mode). The package-level `.gitattributes` is gone, and yamlfmt's exclude covers the corpus (closes #2133). - `_assert_portable_path` is removed: its input is `relative_to(...).as_posix()`, which cannot fail its checks. - The harness's contract comment and the README are cut to the facts; the README lists the warnings sort among the deviations. Found in that review, pre-existing and not changed here: production orders two marked-content entries on one row by tree-sitter's capture order, which differs between runs (#2150; no case here puts two entries on one row), and the scope walk binds a marker above a `typedef struct`, a union or a namespace to a struct further down the file now that `struct_specifier` is a scope kind (#2151). ## Tests The fixture harness and every `test_extraction_fixture` snapshot are converted to the production shape. Additions: - `scope.yaml` (25 cases) and `line_endings.yaml` (2 cases). - Re-captures after #2092 (warning wording, the moved `too_few_fields` case) and #2021 (marked-rst rows); every one of those snapshot diffs is a master change. `uv run poe test-codelinks`: **689 passed, 0 skipped**, stable across reruns (also under xdist and from the repository root); the same at the takeover tip, with lint, typecheck, check-workspace, the CLI suite under `GITHUB_ACTIONS=1` and `docs-codelinks` (0 warnings) green, and every corpus and snapshot byte unchanged. ## Checklist - [x] Every line of this change has been read: Marco's review of useblocks/sphinx-codelinks#100, the review of 2026-10-08, and the takeover's line-by-line pass; it was not generated automatically from an issue. - [x] I ran the package's tests (`uv run poe test-<package>`) and they pass. - [x] Documentation is updated where behaviour or options change. - [x] The package's `docs/changelog.rst` has an entry under *Unreleased*. - [x] `uv run poe lint` and `uv run poe typecheck` pass. Closes #2133 --------- Co-authored-by: Chris Sewell <chrisj_sewell@hotmail.com>
Merged
4 of 5 tasks
chrisjsewell
added a commit
that referenced
this pull request
Oct 9, 2026
…m sphinx-codelinks#82) (#2166) ## What `packages/sphinx-codelinks`: moves useblocks/sphinx-codelinks#82 (TypeScript and JavaScript comment type, by @arnoox) into the monorepo with the recipe from #1871, and addresses that PR's open review on top. `comment_type = "ts"` analyses `.ts`, `.tsx`, `.mts`, `.cts`, `.js`, `.jsx`, `.mjs` and `.cjs`. The grammar is chosen per file: TypeScript for `.ts`/`.mts`/`.cts`, where a `<T>x` cast is legal, and TSX for every other suffix, which also reads JSX. Legacy one-line `<!--` comments (classic scripts only) are read too. For this comment type `exclude` defaults to the usual build-output directories. The move: the branch was 24 commits, 3 of them merges of `main`, and #1898 measured five conflict stops on the old `main`. So it comes across as two commits with their authors kept: Arnaud Riess's work up to the 2026-08-07 merge, and Marco Heinemann's follow-ups. Both were filtered with the import's `git filter-repo` rule, which puts them on the commit the import merged, and cherry-picked onto master. The full history stays at useblocks/sphinx-codelinks#82. The conflicts against master were resolved in place: - The changelog entry moved to `docs/changelog.rst`, written for the release. - The docs hunks moved to `docs/components/`. - The audited `AGENTS.md` and the `@AGENTS.md` `CLAUDE.md` are kept; the PR's own `CLAUDE.md` is dropped. - `test_analyse_oneline_needs` takes the PR's case dicts with master's `get_multiline_needs`. - `uv.lock` gains only the `tree-sitter-typescript` entries. Then four commits (six, the takeover counted: a 📚 commit linking the changelog entry to this pull request follows the third, and a 🧪 commit pinning a `@need-ids:` line in a JSDoc block follows the fourth): 1. 🧪 The TypeScript cases are re-captured in the production-shape corpus of #2128. Multi-line needs work in TypeScript with no code change, now pinned by two cases. 2. 👌 Scope association covers TypeScript's declarations (the review below). 3. 👌 The one-row block, JSX and `<!-- -->` markers. 4. 👌 A JSDoc tag line is a `jsdoc_tag` warning, not a need. The merge of master: master moved by one commit since this branch was cut, `132607bb` (#2168: `src-trace` renders multi-line needs). It is merged in (`023dd70b`), not rebased, so the eight commits above are unchanged. Two files conflicted. In `docs/changelog.rst` the multi-line needs bullet takes master's text with this branch's language list, and the TypeScript bullet follows master's new `src-trace` bullet. In `tests/data/extraction/multiline_needs.yaml` master's four language cases and this branch's two sit side by side after `bash_hash_run`. Nothing else moved: against master the diff is this branch's own (+2974/−149) plus the takeover's commits below. A `//` run and a JSDoc block — the two shapes the corpus pins — now render through `src-trace`: `test_typescript_blocks_render`. The review round (two reviews of the takeover; every commit after the merge): `export declare …` binds the declaration it holds (`find_next_scope` looks one container deeper, pinned red first by `typescript_export_declare`), and a `declare`-prefixed `const`, `let` or `var` is a scope, as it holds no value (pinned red first by `typescript_declare_const_binds`, which now holds one of each); JSDoc's `@modifies` and TypeScript's JSX pragmas `jsx`, `jsxFrag`, `jsxImportSource` and `jsxRuntime` join the shared tag list (112 names); the grammar choice, the scope predicates, the C++ side of the `jsdoc_tag` gate, `<!--` runs and `discover -e` get the pins they lacked, each shown red under the wrong version it catches, but for the `<!--` pin, which records today's behaviour; the narrowed rule's question to the one-line parser (`jsdoc_tag_asks_the_one_line_parser`, with `;` as the separator) and every JSX pragma of the tag list are pinned too; the docs say what was measured (the `exclude` default's width, the `discover.rst` example, `[[` rather than `@need` as the suggested start sequence, where `<!--` is valid); and, in a commit of its own, the `jsdoc_tag` rule is narrowed (item 15). Closes #1890 ## Why The review of useblocks/sphinx-codelinks#82, open at the move. The first round (five threads) was fixed on that branch before it moved. 1. **The TSX grammar is not a superset of TypeScript's.** Fixed on that branch: the grammar is chosen per suffix. `oneline.yaml` `type_assertion_oneliner_typescript` now pins it through production wiring: under TSX, the marker after the cast is lost. 2. **Interfaces, enums, type aliases, abstract classes and generator functions bound to the next unrelated function.** They are scopes now. So are function signatures, modules, namespaces and interface/abstract method signatures. 3. **`TS_FUNCTION_VALUE_TYPES` was too narrow.** Generator functions and class expressions count, seen through `as`, `satisfies`, `!`, `<T>` and parentheses. 4. **The demo marker field order.** Fixed on that branch. 5. **Nothing pinned the production grammar choice.** The test fixtures now build their parsers through `init_tree_sitter`. The declarative cases go through production wiring, and their `tagged_scope` differs under the wrong grammar. 6. **JavaScript's assignment forms and anonymous default exports.** `module.exports = function () {}`, `Foo.prototype.bar = …` and `export default () => {}` bind. 7. **Class-field arrow functions bound to the class.** A class field holding a function binds to the field. 8. **`.d.ts` declarations had no scope.** `declare …` is searched like `export …`, so ambient declarations bind. `.d.ts` files stay discovered. 9. **TypeScript-only rules hardcoded in the shared traversal.** They now live in two per-language tables, `SCOPE_CONTAINER_TYPES` and `SCOPE_PREDICATES`. A comment type without a `SCOPE_NODE_TYPES` entry has no scopes, not C++'s. 10. **Six copies of one query.** The five grammars (six, the takeover counted: C/C++, C#, YAML, Go, JSONC and Bash) whose comments are one `comment` node share `SIMPLE_COMMENT_QUERY`. TypeScript's query differs, since it also captures `html_comment`. 11. **Five unsynchronised edits per language.** `test_every_comment_type_is_wired` fails when a `CommentType` member misses a table, rather than failing as a `KeyError` in discovery. 12. **The one-row `{/* @… */}` and `/* @… */` forms failed.** They work since #2129 (the closing `*/` is no marker text) and are pinned now. The docs no longer call them unsupported. 13. **The `CLAUDE.md` described an architecture that does not exist.** Dropped in the move. 14. **The changelog heading.** The entry is under *Unreleased*. 15. **Every JSDoc tag line warned** (the takeover's change to the rule, measured: one ordinary eight-line JSDoc comment with no comma anywhere gave six `jsdoc_tag` warnings, enough to fail a `-W` build, while the same text in `/* */` gave none). As `docstring_tag` was narrowed for Python, a line of a JSDoc comment is now a `jsdoc_tag` warning, and never a need, only when the one-line start sequence is `@`, nothing alphanumeric precedes it, a listed tag directly follows it and ends at a space, a tab, `{`, `}` or the line end, and the one-line parser would otherwise have read the line as a marker or warned about it. `@param a the augend` stays silent; `@param a the first, thing` still warns. Under `[[`, `[[param a, b]]` is a need. The rule is written once, in `jsdoc.yaml`'s header. It is its own commit, so it can be dropped. Follow-up findings on that PR: - `html_comment` markers were dropped. Fixed on that branch, and a one-row `<!-- … -->` now drops its `-->` as a block comment drops `*/`. - `@need-ids:` in a JSDoc block also produced a phantom need. Fixed on master by #2049: a reference-marker line is no one-line need. - Generated output was scanned as source. Fixed on that branch by the per-comment-type `exclude` default. - JSDoc tags with a comma became needs: `@param a First, second` gave a need with the id `second`. Now a `jsdoc_tag` warning applies, following the `docstring_tag` rule of #2129. A line of a JSDoc comment whose start sequence is directly followed by a JSDoc tag warns and yields no need (narrowed by the takeover to the lines the one-line parser would have read, under `@`: item 15). A JSDoc comment is one opened by exactly `/**`, under `comment_type = "ts"`. The tag must end at a space, a tab, `{`, `}` or the line's end. The tag list covers JSDoc's block tags, TSDoc's, TypeScript's and the inline tags; matching is case-sensitive. The warning says to choose a start sequence JSDoc does not use. `/* */`, `//` and `/*** */` comments are untouched, and so is C++ Doxygen. ## Tests - `scope.yaml`: 23 TypeScript and JavaScript cases (30 since the takeover's `typescript_export_declare`, `typescript_declare_const_binds`, and one case per scope predicate and for the abstract method signature), one association each, among them every declaration kind above. - `oneline.yaml`: 7 TypeScript-family cases: the three moved ones plus the one-row block, JSX inline, `<!-- -->` and cast cases (8 with the takeover's `jsx_text_oneliner_tsx`: JSX text is no comment, so `.tsx` → TSX is pinned). - `multiline_needs.yaml`: a `//` run and a JSDoc block (4 with the takeover's two `<!--` pins: a run of `<!--` lines holds no block, and a `<!--` row ends a `//` run). - `jsdoc.yaml` (new, 6 cases; 7 with the `@need-ids:` case, the takeover counted; 10 with its `jsdoc_more_tags`, `doxygen_in_cpp_is_no_jsdoc` and `jsdoc_tag_asks_the_one_line_parser`): tag lines warn and the non-tag marker is a need. It also covers where a tag ends, case sensitivity, comments that are no JSDoc, JavaScript, and a start sequence JSDoc does not use. - `test_jsdoc_tag`: the tag shape, the same table as ubCode's. - `test_every_comment_type_is_wired`. - The PR's own unit, discovery and CLI tests. - `test_multiline_needs_render.py::test_typescript_blocks_render` (the takeover's): a `ts` project beside the fixture's `src`, a `//` run with an RST body in a `.ts` file and a JSDoc block with a MyST body in a `.tsx` file, each traced on an RST page. Each need is recorded at its directive with its body's doctype and its links, its local URL is at the block's open line, its body renders the role link and the list, and the build warns nothing. With `get_multiline_needs` off for the project both needs are absent and the test fails. - The takeover's units: `test_ts_grammar_key` (`.ts`, `.mts`, `.cts`, `.d.ts` in any case → TypeScript; the rest → TSX); `test_a_ts_and_a_tsx_file_in_one_run_each_get_their_grammar` (one analysis, both orders); `test_discover_cli_excludes_replace_the_ts_default`; and the default-exclude test asserts what discovery lists, `build/`, `out/`, `coverage/` and a `.d.ts` included. - At the tip: `poe test-codelinks` 1051 passed, 0 skipped, on Sphinx 9.1 and on Sphinx 7.4 / Python 3.11; `poe docs-codelinks` builds with no warning; `poe import-check-codelinks` imports the wheel with `tree-sitter` 0.25.2 and `tree-sitter-typescript` 0.23.2 resolved from PyPI. The corpus is shared with ubCode, whose parity test replays every case above (useblocks/ubcode#2764, re-vendored from this branch the same day; see *ubCode* below). ## ubCode The twin is useblocks/ubcode#2764 (Marco's, taken over the same day and merged onto `main`). It re-vendors the shared corpus (`SOURCE.md`) from this branch while it is open and at this pull request's squash commit once it merges, and its parity test replays the TypeScript cases of `oneline.yaml`, `scope.yaml`, `jsdoc.yaml` and `multiline_needs.yaml`. The `jsdoc_tag` rule (the tag list, the end class, the exactly-`/**` rule, and since the review round the `@`-only and would-have-been-read clauses) is written once, here, in `jsdoc.yaml`'s header, and ubCode implements the same text. From the re-vendor the twin also mirrors the two-level container walk (`export declare …`), the ambient-variable arm of the declaration predicate (a `declare`-prefixed `const`, `let` or `var` is a scope) and the 112-name tag list. `js_html_row_breaks_a_line_run` in `multiline_needs.yaml` was written for the reference by ubCode's review of the twin. ## Checklist - [ ] I wrote this change myself and have read every line of it; it was not generated automatically from an issue. - [x] I ran the package's tests (`uv run poe test-codelinks`) and they pass. - [x] Documentation is updated where behaviour or options change. - [x] The package's `docs/changelog.rst` has an entry under *Unreleased*. - [x] `uv run poe lint` and `uv run poe typecheck` pass. --------- Co-authored-by: Arnaud Riess <arnaud.riesslgmail.com> Co-authored-by: Chris Sewell <chrisj_sewell@hotmail.com>
chrisjsewell
added a commit
that referenced
this pull request
Oct 14, 2026
Six members of the workspace released together, in one pull request and three tag waves: sphinx-needs 9.0.0 (the Sphinx half of the derived-values lockstep with ubCode's next release) and the five packages around it. Everything mechanical is `uv run poe bump`'s, one commit per member; the prose, the gap bullets and the version-marker sweep are by hand, each claim measured against the tree. ### What this releases | member | from → to | level | why | |---|---|---|---| | sphinx-ai-index | 0.1.0 → **1.0.0** | major | the `ai_docs_index.json` it writes has been the format's `"1.0"` since the first release, and ubCode reads it in production — that is the stability 1.0 declares (#2034); this is also its first release from this repository, on the workspace's Python 3.11 / Sphinx 7.4–9 / docutils 0.21 floors | | sphinx-mounts | 0.2.0 → **0.3.0** | minor (0.x) | variant data from `[variants]` through ub-project, with new refusals and two new `-W`-failing warnings; Python 3.11 again; docutils 0.21; first release from this repository | | sphinx-needs | 8.5.0 → **9.0.0** | major | computed values (derived fields, dependency order), `needextend` filters and the needs.json readers can change need data in certain corner cases; `[[…]]` in content and `need_func` removed; Python 3.11; `[variants]`; `needstable.js` | | ub-test-reports | 1.0.0 → **1.1.0** | minor | parser and converter fixes that add `ReportReadError` and change which exceptions escape and what a nested-suite report converts to | | sphinx-codelinks | 1.4.0 → **2.0.0** | major | requires Sphinx-Needs 9; `get_rst` / `[analyse.marked_rst]` refused, the `@rst` blocks replaced by multi-line needs; first release from this repository | | sphinx-test-reports | 2.0.0 → **3.0.0** | major | the Sphinx-free half is ub-test-reports; the import name moves to `sphinx_test_reports`; requires Sphinx-Needs 9 and ub-test-reports 1.1 | ub-project stays at 1.1.0: nothing to release. The six versions, the `__version__` literals, the floors (`sphinx-ai-index>=1.0.0,<2` in four `docs` extras; `sphinx-needs>=9.0.0,<10` in codelinks and test-reports; `ub-test-reports>=1.1.0,<2` in test-reports), the docker fallback (`sphinx-needs-v9.0.0`), the lock and the six changelog headings are `uv run poe bump`'s, one commit per member in the planner's order. ### What was added by hand - **sphinx-needs 9.0.0**: bullets for the changes the entry had not recorded —‼️ `[[…]]` in need content is plain text and the `need_func` role is removed (#1919); ✨ the needtable script (`needstable.js`, `needs_table_page_size` / `needs_table_page_sizes`, Excel / PDF export and column reordering removed; #1920); ✨ `[variants]` read through ub-project, a new dependency, with two new warning types (#1996, #1998); ⬆️ docutils 0.21 (#1977); ⬆️ jsonschema-rs up to 0.58 (#1851, #1980); 👌 deterministic CSS order (#1914); 🔧 the uv workspace, the `#subdirectory=packages/sphinx-needs` git install and the `sphinx-needs-v<version>` tags (#1839, #1846), in a new "Internal changes" section; 🔧 the sdist without `tests/` / `performance/` (#1905); 🐛 needgantt's integer completion (#1870); and the derived-fields bullet cites #2204 and #2205. - **sphinx-mounts 0.3.0**: the move bullet (#1850). - **sphinx-codelinks 2.0.0**: 🐛 `get_current_rev` reads `packed-refs` (#1871); the‼️ floor bullet says Sphinx-Needs 9.0 / `>=9.0.0,<10`; `codelinks write rst` "will be removed in 3.0.0"; the documentation is served by Read the Docs; the `ingest_need_record` note trimmed. - **sphinx-test-reports 3.0.0**: the floors it ships (`sphinx-needs>=9.0.0,<10`, ub-test-reports 1.1.0). - **ub-test-reports 1.1.0**: the UTF-8 bullet links #2024. - **The six summary paragraphs**, one per entry (sphinx-ai-index's replaces its old lead). - **Pages outside the changelogs** (each states what this release ships): - sphinx-codelinks' `codelinks write rst` deprecation (`WRITE_RST_DEPRECATED` in `cmd.py`, and `docs/components/write.rst`) now promises removal in 3.0.0, not 2.0.0, and is dated 2.0.0, not an uncut 1.5.0: 2.0.0 ships with the command. - `docs/basics/introduction.rst` says `src-trace` renders multi-line needs. - sphinx-needs' `docs/directives/choose.rst` says `versionadded:: 9.0.0`, not an uncut 8.6.0, and `docs/configuration.rst` says sphinx-mounts reads `[variants]` "from 0.3.0", not "from its next release". - sphinx-test-reports' `docs/install.rst` names the Sphinx-Needs 9 floor (`>=9.0.0,<10`). - Comments and agent notes naming the old `>=8.5.0,<9` floor (`compat-requirements.txt`, two `AGENTS.md`, `src_trace.py`) name 9.0.0. - A sweep of all 216 version markers (`versionadded` / `versionchanged` / `deprecated`) in the six members' docs and sources, against each member's released versions, found 13 more that named a release never cut. Each now names the release that shipped it: mounts 0.2.0 ×2, sphinx-needs 3.0.0 ×7, 4.0.0 ×1 and 7.0.0 ×1, test-reports 1.1.0 ×2. ### Merge and tag order Merge RIGHT BEFORE tagging. **Re-stamp `:Released:` on the merge day first** (it is stamped 2026-10-14, the earliest tag day); one line per file, each in its own format: | file | line | format | |---|---|---| | `packages/sphinx-ai-index/docs/changelog.rst` | l.11 `:Released: 2026-10-14` | ISO `YYYY-MM-DD` | | `packages/sphinx-mounts/docs/changelog.rst` | l.11 `:Released: 2026-10-14` | ISO | | `packages/sphinx-needs/docs/changelog.rst` | l.12 `:Released: 14.10.2026` | `DD.MM.YYYY` | | `packages/ub-test-reports/docs/changelog.rst` | l.11 `:Released: 2026-10-14` | ISO | | `packages/sphinx-codelinks/docs/changelog.rst` | l.11 `:Released: 14.10.2026` | `DD.MM.YYYY` | | `packages/sphinx-test-reports/docs/changelog.rst` | l.11 `:Released: 14.10.2026` | `DD.MM.YYYY` | Then tag the squash commit `<sha>` explicitly (so nothing merged later can slip into a release), in three waves, each after the previous wave's `publish` jobs are green — the plan job's check 4 refuses a member whose workspace dependencies (extras included) are not on PyPI at the version this tree builds: ```bash # wave 1 — dependencies already on PyPI (ub-project 1.1.0) or none git tag sphinx-ai-index-v1.0.0 <sha> && git push origin sphinx-ai-index-v1.0.0 git tag ub-test-reports-v1.1.0 <sha> && git push origin ub-test-reports-v1.1.0 # wave 2 — once sphinx-ai-index 1.0.0 is on PyPI git tag sphinx-mounts-v0.3.0 <sha> && git push origin sphinx-mounts-v0.3.0 git tag sphinx-needs-v9.0.0 <sha> && git push origin sphinx-needs-v9.0.0 # wave 3 — once sphinx-needs 9.0.0 and ub-test-reports 1.1.0 are on PyPI git tag sphinx-codelinks-v2.0.0 <sha> && git push origin sphinx-codelinks-v2.0.0 git tag sphinx-test-reports-v3.0.0 <sha> && git push origin sphinx-test-reports-v3.0.0 ``` The bare `9.0.0` tag is pushed by `release.yaml`'s last step for sphinx-needs ("Push the bare companion tag"); it is what moves Read the Docs' `stable` and keeps `git+…@<version>` pins working. Only if that step fails: `git tag 9.0.0 sphinx-needs-v9.0.0 && git push origin refs/tags/9.0.0`. A tag pushed too early fails only its plan job, with nothing published: re-run it once the dependency is up. After a green `publish`, a red later job is re-run with **Re-run failed jobs**, never *Re-run all jobs*. ### Before wave 1 Five of these PyPI projects release through `release.yaml` for the first time — sphinx-ai-index, sphinx-mounts, sphinx-needs (8.5.0 went out through the old token-based workflow), sphinx-codelinks and sphinx-test-reports. Each must list the trusted publisher **owner `useblocks`, repository `sphinx-needs`, workflow `release.yaml`, environment `pypi-<dist>`**. The GitHub side is ready (the five `pypi-<dist>` environments exist, each with the tag policy `<dist>-v*`); the PyPI side can only be checked by a project owner. A wrong publisher fails `publish` with nothing uploaded — fix it and re-run from the same tag — but sphinx-ai-index's is the very first tag, so it would stall the whole train. ### Rehearsal Two rehearsals can pass before the merge, from this branch: ```bash gh workflow run release.yaml --ref release/9.0.0-train -f tag=sphinx-ai-index-v1.0.0 gh workflow run release.yaml --ref release/9.0.0-train -f tag=ub-test-reports-v1.1.0 ``` Dispatched from this branch at `9a2dd5ec`: run [38479550037](https://github.com/useblocks/sphinx-needs/actions/runs/38479550037) (`sphinx-ai-index-v1.0.0`) and run [38479553168](https://github.com/useblocks/sphinx-needs/actions/runs/38479553168) (`ub-test-reports-v1.1.0`). A dispatch runs `plan` and `build` (the resolver gate and the compat cell) and never `publish`. It must name this branch: at `master` the tag's version is not the tree's (check 2). The other four cannot pass yet, because check 4 stays fatal in a rehearsal: sphinx-mounts and sphinx-needs need sphinx-ai-index 1.0.0 on PyPI (their `docs` extras), sphinx-codelinks and sphinx-test-reports need sphinx-needs 9.0.0 (and test-reports ub-test-reports 1.1.0). They can be rehearsed from `master` once the wave before them has published. ### What goes red and why - **Docs-Linkcheck** (not a required check): the new `8.5.0...sphinx-needs-v9.0.0` compare link 404s until the `sphinx-needs-v9.0.0` tag exists. - **`uv run poe import-check-codelinks` / `import-check-reports`**: red until wave 2 publishes — "only sphinx-needs<=8.5.0 is available and sphinx-codelinks==2.0.0 depends on sphinx-needs>=9.0.0,<10". Run them on `master` between waves 2 and 3: red then would be a real missing symbol. The other five import checks are green on this branch. - **Docker**: the fallback ref becomes `sphinx-needs-v9.0.0`, so the `docker/**` pull-request and manual-dispatch path, red since the move, goes green once that tag exists. This PR does not run it (its trigger is `docker/**` only), and the tag push builds the image from the tag itself. ### For a human (outside the repository) - **PyPI trusted publishers**: the five checks under "Before wave 1". - **codelinks.useblocks.com deep links**: the old GitHub Pages site served pages at the root (`…/components/analyse.html`). Under Read the Docs they 404 and live under `/en/latest/`. A Read the Docs page redirect (old path → `/en/latest/<path>`) on the `sphinx-codelinks` project would keep bookmarks and inbound links working. - **Read the Docs `stable` for sphinx-mounts and sphinx-codelinks** serves the Sphinx-Needs 8.5.0 documentation today (both projects build from this repository, whose highest PEP 440 tag is sphinx-needs' bare `8.5.0`). After this train it follows the bare `9.0.0` tag, then every later sphinx-needs release, and never a mounts- or codelinks-only one. Consider deactivating `stable`, or making `latest` the default version, on those two projects. ### ubCode ubCode's next release names sphinx-needs 9.0.0 as the version its derived-values reading pairs with. This changelog names no ubCode version, by design: it keeps the entry's "phase-2 release" wording. The ubCode release pull request is prepared separately. Closes #2034
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
useblocks/sphinx-codelinksmoves into this workspace aspackages/sphinx-codelinks/,with its history, its seven release tags and its release path. It is the third
distribution here and the first that depends on another one at runtime.
What the import is
The whole of that repository's
main— 110 commits, tip63adfe50— is merged in,rewritten by
git filter-reposo that every historical commit already places its filesunder
packages/sphinx-codelinks/. 207 files, +18 382 lines, and nothing outside thepackage. Three things came with it:
sphinx-codelinks-v0.1.1…sphinx-codelinks-v1.4.0. Here the prefix is load-bearing rather than hygienic:release_plan.pyreads every tag without-vin it as a sphinx-needs release, andthree of the seven bare names —
1.1.0,1.2.0,1.3.0— are existing sphinx-needsreleases. Without the rename the fetch would have moved them.
#Nreferences becameuseblocks/sphinx-codelinks#N, 110 of them onmain, bounded to the repository's ownnumber range (
#1–#106). The one already-qualified cross-repository reference,useblocks/ubcode-pub#75, is untouched — the rule's lookbehind is what leaves it alone.Every remaining
#in the history is a Markdown heading or a quoted shell comment, andnone is followed by a digit.
packages/sphinx-codelinks/design/import-commit-map.txt. The rewrite gives every commita new hash, and this project has seven released versions whose GitHub Releases,
changelog entries and PyPI page are full of the old ones. The old repository will be
archived, not deleted or transferred, so those permalinks keep serving; this file is what
carries them across.
How to read the history afterwards
git log packages/sphinx-codelinks/<path>works with no--follow— filter-reporewrote the paths, so from this repository's point of view those files were always there.
That is the opposite of the sphinx-needs files the 2026-09 restructure moved, which do need
it.
git blameneeds nothing either way, andgit log --first-parent mastershows thewhole import as one line.
If you have an open pull request in the old repository
Its commits move across with their authorship, and as cleanly as your branch merges
with
maintoday. The property that makes it work is that filter-repo's rewrite isdeterministic: filtering your branch with the same rules puts its commits on exactly the
commit this import merged.
Seven of the eight open pull requests conflict with
mainright now (only #100 doesnot), so seven authors will hit a conflict during the cherry-pick — the conflict they
already had, not one the import creates. Rebase your branch onto
mainin the OLDrepository first and the move is conflict-free.
Then resolve anything this branch's later commits touched, and push. The list, most
likely first:
pyproject.toml—[dependency-groups],[tool.ruff*],[tool.ty*]and
[tool.pytest.ini_options]no longer exist there; the root owns them. A branch thatadded a test dependency puts it in the ROOT
testgroup instead, and a new extra staysin the member's
[project.optional-dependencies]. This is the one a branch that touchesthe manifest at all will hit;
__init__.pyundersrc/sphinx_codelinks/;docs/development/change_log.rsttodocs/changelog.rst;analyse/utils.py, if your branch touches the git helpers the#106fix rewrote.Proven end to end twice: on sphinx-codelinks#100 (11 commits, 153 files, zero conflicts —
the one branch that is current with
main) and on #96, a one-commit fork branch fourcommits behind, where exactly one file conflicted (
pyproject.toml, for the reason above)and the other 25 applied cleanly, including a rename cherry-pick followed on its own.
What changed beyond the move
The standalone-repository files retire.
.github/(five workflows),.python-version,.pre-commit-config.yaml,tox.ini,.vscode/and.gitignore— the root owns every oneof them, and while the member kept a
.pre-commit-config.yamlprek treated it as a projectof its own. The three ignore entries the root lacked (
invalid_objs.json,output/,.ty_cache) moved up;uv.lockdeliberately did not, because this workspace tracks itslock.
gh_pages.ymlgoes with the rest even though the Pages site is still live: the oldrepository keeps serving it until the DNS moves, so a copy here would be dead weight. From
the manifest, the tables
check_workspace.pycheck (7) refuses — the pre-import work hadalready made them copies of the root's, so nothing about lint, types or collection changes
when they go. Added instead:
[project.urls], which this distribution has never had.The root wiring. The member is listed and sourced from the workspace;
docs-codelinksand
codelinks-libclangre-export its extras. The sharedtestgroup gainstoml(itsCLI tests write toml, and
tomllibonly reads) andfuro(two test fixtures sethtml_theme = "furo"and one is built by a test — a test dependency here, not a docs one).The
typinggroup gainstypes-Pygmentsandtypes-jsonschema, without which five of thepackage's
# ty: ignorecomments suppress nothing and theerror-on-warningratchetreports them as unused.
uv lockadds 29 package names / 31 entries, removes nothing andmoves no existing package's version.
testpathsis deliberately untouched: this package ships atests/__init__.pylikesphinx-mounts', so adding its path makes the bare root pytest collect nothing rather
than more. The Lint job's "Check a bare root pytest still collects" step already guards it.
Bringing it under the root's ruff cost two import re-sorts, and they are structural.
The root's
[tool.ruff] srcnamespackages/sphinx-needs/src, sosphinx_needsisfirst-party in this workspace and isort moves it out of the third-party block. This is
the first member whose source imports it — nothing in
packages/sphinx-mounts/srcdoes —so it is a class of change the pre-import convergence could not have anticipated from
outside. The member's one per-file-ignore entry moved to the root scoped to
packages/sphinx-codelinks/tests/*; as**/tests/*it would have loosened the otherthree suites too.
Four empty
__init__.py.analyse/,source_discover/,sphinx_extension/andsphinx_extension/directives/were PEP 420 namespace directories, sopkgutil.walk_packagesnever reported them andimport_check.py— the release gate thatimports every module of the built wheel against its dependencies as published — saw
5 of the package's modules and exited 0. With the four files, and with the new
--extraoption that gives the walk the optional engine, it reports 24, which isevery
.pyundersrc/sphinx_codelinks/(the four new files included).Four latent bugs the old repository's CI shape could not see
1.
__version__had read"0.1.0"since the first commit, through seven releases,while the manifest went to 1.4.0 — so an installed 1.4.0 reported the wrong version. It is
exported in
__all__, so it is public API.check_workspace.pycheck (5) is what caughtit on the first run here, and
poe bumpkeeps the two in step from now on.2.
locate_git_rootfound no git root in a linked worktree. It required.gitto be adirectory, and
get_remote_url/get_current_revthen read<root>/.git/configand<root>/.git/HEADdirectly. In a worktree.gitis a file holdinggitdir: …, theremotes live in the main repository's git directory named by
commondir, and onlyper-worktree state is local. The consequence is worse than the missing links the issue
reports:
poe docs-codelinksruns-nW, and this repository is worked in worktrees as amatter of routine, so the documentation build failed for every developer while staying
green in CI and on Read the Docs, which use plain clones. Reverting the fix here gives five
[codelinks.git_root]errors and a red build. Two helpers do it, and a plain checkout takesexactly the path it took before. The regression test builds a real
git worktreein bothshapes git makes — detached, where
HEADis a SHA, and a branch, where it is aref:intothe common directory. Closes sphinx-codelinks#106.
3. libclang was not actually optional for the suite. It is an optional extra at
runtime, and three test modules guarded it; a fourth imported
sphinx_codelinks.analyse.preprocunguarded, and that package imports the libclang loadereagerly. So an environment without the wheel failed during collection rather than
skipping. The old CI never saw it because its test group always installed libclang. One
pytest.importorskipline, and the suite now degrades honestly: 359 passed with theengine, 303 passed / 26 skipped without it — 26 rather than 56, because a module-level
importorskipis one skip per module and never collects the tests inside; 56 is how manytest cases stop running.
And CI asserts the engine is there.
extra-groups: codelinks-libclangis the onlyexecutable place that wheel is named, and losing it would be silent: a cell without the
group is green at
303 passed, 26 skipped. So the Extensions cell importsclang.cindexright after its sync and fails naming the group — the libclang half of the renderer check
the composite already carries, and the same rule the repository already applies to
graphviz and PlantUML.
4.
get_current_revreturnedNoneonce the refs were packed. It resolvedref: refs/heads/<x>by looking for a loose ref FILE, and there is none aftergit gcorgit pack-refs --all— which run unattended on any long-lived checkout. Same red-nWdocs build as #106, reached by a different route, and again invisible to CI and RTD, which
both leave a detached HEAD. It now falls back to
packed-refs, with a fixture case thatpacks the refs and asserts the sha.
A fifth, smaller one:
test_form_https_urlpassed afilepathrelative to the CWD, sothe suite only passed when pytest ran from the package directory. It is fixed rather than
worked around with a
working-directory:, which is what lets CI run it from the repositoryroot like every other cell.
CI
tests-mountsbecomestests-extensions: one cell per sphinx series (ubuntu ×sphinx-7/8/9, plus one
windows-latestcell on sphinx-9) running both extension packages'suites as steps, each with its own
--cov-report=xml:<name>.xmland its own codecovupload. The names of the checks change and the count does not. One cell for the two of
them rather than a second three-cell matrix because sphinx-codelinks' whole suite is under
20 seconds: as cells of its own it would spend more runner time starting jobs than testing.
The cost is that a failure in the first suite could hide the second, so the second pytest
step runs under
!cancelled().test-package.yamlcannot express two suites and should not learn to — aworkflow_callinput is a scalar and a job's steps cannot be generated from data — so
test-extensions.yamlis a second reusable workflow. What would have made that a step backwards is the 57 lines
of prologue the two units share, which is the duplication #1864 existed to remove. So those
move to a composite action,
.github/actions/prepare-cell: the renderer installs andtheir guards, the
Point PLANTUML_JAR at the vendored jarstep (#1870's, verifyingvendor/plantuml/against its pin), the cell sync, the version report andcheck_sphinx_cell.py.test-package.yamlis refactored onto it with no change to anycaller's inputs and no change to what a cell does — and
test-extensions.yamlneeds noPlantUML wiring of its own, because it already passes
needs-plantuml: trueand inheritsthe verified jar through the composite.
The composite holds
run:steps only. dependabot'sgithub-actionsecosystem is whatkeeps
actions/checkout,astral-sh/setup-uvandcodecov/codecov-actionpinned andbumped, and a pin moved inside a composite action stops being seen — so all three stay
written in the two workflows. The one consequence is that
setup-uvnow runs before therenderer installs rather than after them; nothing reads across that boundary, and #1870
needed
setup-uvfirst for its own reason anyway (its--verifystep runs a pythonscript), so the two changes converge.
libclang is a dependency group, not part of
test. The wheel is 23 MiB and 81 MB ondisk and only 56 of 359 tests use it, so every cell of every package would otherwise pay
for an engine it never calls. The Extensions cell adds
--group codelinks-libclang; therelease workflow's compat cell gets it from
packages/sphinx-codelinks/compat-requirements.txt, through the per-member hook thesphinx-mounts import already added. Without either, the affected tests skip rather than
fail — so both are load-bearing against a quieter green rather than a red.
A new
Docs codelinksjob builds them on every pull request — oneuv syncand one
sphinx-build, mirroringdocs.yaml's Docs-Linkcheck idiom rather than goingthrough
poe, which would install the wholedevgroup as well just to havepoeon thepath. The build itself is 7 seconds. Those docs are the one end-to-end exercise of the extension —
src-tracerunsover four projects of the package's own source — and nothing else in CI builds them:
docs.yamlis a linkcheck scoped to sphinx-needs' docs, and the Read the Docs pull-requestbuild is neither a required context nor even run when the package did not change. It is
also what caught the worktree bug. An equivalent for sphinx-mounts is worth having and is
not this pull request's.
codecov.ymlgains acodelinksflag withtarget: auto, asmountshas, until a realnumber is known.
checkgains both new jobs. Thebazellane,tests-core,tests-js,tests-no-mpl,smoke-needsandconcurrencyare untouched. Cost: roughly +2 runnerminutes per push — four Extensions cells each about 20 s longer, plus a
Docs codelinksjob of about a minute, of which the build itself is 7 s.
What is lost, and accepted
The old
ci.ymlran eight pytest cells: py3.11/3.12/3.13/3.14 across sphinx 7/8/9, twoWindows cells, macOS, and
ubuntu-24.04-arm. This repository runs one pinned interpreterfor the extension packages and one Windows cell. macOS, arm64 and the per-interpreter
matrix go, and that is a real reduction rather than an oversight — it is the shape this
repository already runs for sphinx-mounts, and the alternative is eight more cells on every
push for a package whose suite is nine seconds. The Windows cell is kept deliberately:
this package does path arithmetic, and three of the five latent bugs above are path
bugs: the packed refs, the worktree gitfile and the CWD-relative test parameter.
And the newly lowered Python floor is exercised by nothing. This pull request ships a
changelog bullet announcing "Python 3.11 is now supported; the floor moves down from 3.12"
— and
tests-extensionspasses nopython-version, so every cell runs on the root pin(3.13).
tests-core's 3.11 rows test sphinx-needs only, the release compat cell uses thepin too, and
check_workspace.pycomparesrequires-pythonstrings without executinganything. That is this repository's existing shape for sphinx-mounts as well, which is why
it is accepted here rather than fixed in this pull request; #1865 is the workspace-level
issue for it.
Also gone: the old
Documentation buildjob, replaced by theDocs codelinksjob above,and the standalone
coverage.yaml, folded into the Extensions cell's flag.Read the Docs
packages/sphinx-codelinks/.readthedocs.yamlis re-rooted — every path in it is relativeto the repository root, which is RTD's documented monorepo rule — and it carries the
post_checkoutjob that exits 183 when nothing under the package changed. sphinx-mounts'import deferred that because RTD's commit-status behaviour for a cancelled build was
unverified; it has since been measured (build 34411602:
cancelled, and asuccessstatuson GitHub), so this one has it from the start.
docs/conf.pyanddocs/ubproject.tomlnamed the old repository in five places — furo'sview/edit buttons and the four
remote_url_patternentriessrc-traceuses. All five nowname this repository, with
source_directory = "packages/sphinx-codelinks/docs/".{path}needed no change: it is computed relative to the git root, so a built page's blob link comes
out as
…/sphinx-needs/blob/<sha>/packages/sphinx-codelinks/tests/data/dcdc/charge/demo_1.cpp#L16.After the merge
Create the label
pkg: sphinx-codelinks— before the MERGE, and before the issuetransfers in step 3, which cannot apply a label that does not exist. It is not needed
for this pull request's own CI:
label-prs.yamlruns aspull_request_target, so itreads
.github/labeler.ymlfrom master, which has nopkg: sphinx-codelinksentryuntil this merges — the labeler never tries to apply it here and stays green (it applies
pkg: workspace, which this pull request earns). By the same token the newlabeler.ymlentry is unproven until the first pull request after the merge. Match theexisting convention (
pkg: sphinx-mountsis#0E8A16,pkg: sphinx-needs#1D76DB,pkg: workspace#5319E7), each with a one-line description.Push the seven tags —
git push origin 'refs/tags/sphinx-codelinks-v*'.Deliberately after, not before: pushed early they would make the codelinks commits
reachable while the code is not on master, and
release_plan.pywould already report athird member.
The issues. Nineteen of the old repository's 21 open issues transfer; transfer needs
write access on both repositories, so it must happen before archiving, and
pkg: sphinx-codelinksmust exist first or it cannot be applied in the same pass. Donot pass
createLabelsIfMissing— every label those issues carry already existshere, so they survive the transfer by name. Two do not transfer:
locate_git_rootin a worktree) is fixed by this pull request — close itwith a pointer;
sphinx>=7.4,<10andthe sphinx-9 cell is green. Close it with the evidence.
Two open pull requests carry an issue number in their title (needimport: Sort the imported needs using natural sort order #82 says "issue needs_template and needs_template_collapse removed? #69",
ID option does not support dyn. functions #99 says "(IDs are forced to uppercase. #43)"). A transfer does not rewrite PR titles, because the PRs do not move —
fix those by hand when they are re-opened here.
The eight open pull requests. None is mergeable as it stands: seven conflict with
mainand one is merely behind. Their authors move them with the recipe above, or closethem with a pointer — the branches stay readable in the archived repository. Two
decisions are worth taking at the freeze rather than later: Bug: dyn. func. copy() not working with tags #100 (test
infrastructure, the only mergeable one) and the three overlapping marked-RST pull
requests Wrong error message for missing link #44 / Need copy/mirror: Showing the same need at different places #66 / ID option does not support dyn. functions #99, which are three implementations of one issue.
Read the Docs, in this order, because RTD's automation rules run when a version is
created and both recommended actions build it:
a. the seven tags are pushed first (step 2), so RTD's initial sync creates those seven
versions before any rule exists — a rule added first would activate and build all
seven, and all seven would fail, because their trees predate this
.readthedocs.yaml;b. create the project against this repository; set Admin → Settings → Build
configuration file to
packages/sphinx-codelinks/.readthedocs.yaml. Do not touch thesphinx-needs project's setting — it applies to every version that project builds;
c. let the default
latestbuild go green once;d. add the two automation rules on
^sphinx-codelinks-v\d+\.\d+\.\d+$— Activateversion and Set version as default. RTD derives
stablewithpackaging.version.Version(<tag>)and drops what it cannot parse; a prefixed tag isnot PEP 440, so without the second rule the project never gets a default;
e.
docs/readthedocs.org:sphinx-codelinksmust not become a required status context.The exit-183 job makes that build absent on most pull requests, and a required context
that never reports blocks the merge. Same reason the sphinx-mounts one is not required.
The redirects, and then the domain. Four external repositories link deep into
codelinks.useblocks.comtoday (/basics/installation.html,/components/configuration.html,/components/analyse.html#marked-rst-blocks), andthose paths gain an
/en/stable/prefix under RTD. Add the redirects before the DNSswitch —
/basics/$rest,/components/$rest,/development/$restand/objects.inv→/en/stable/…;objects.invmatters because this project is anintersphinx target. Then add
codelinks.useblocks.comas an RTD custom domain and movethe DNS CNAME off
useblocks.github.io. Until that last step the OLD repository's Pagesdeployment IS the live site, so there is no window with no site — and only after it is
the old
gh_pages.ymlgenuinely dead.Archive the old repository — never delete, never transfer. Seven real
git+https://github.com/useblocks/sphinx-codelinks.git@…pins exist in the wild, infour repositories. An archived repository stays public and clonable read-only, so all
seven keep resolving; deleting it breaks every one, along with every blob permalink to a
pre-rewrite hash. Three of the four are useblocks' own (
bazel-drives-sphinx,needs-large-scale,ubtrace) and should re-point at PyPI or atgit+https://github.com/useblocks/sphinx-needs.git@…#subdirectory=packages/sphinx-codelinks;ubtrace's is an@mainpin, so it is the one that silently stops receiving updates.Refs #1803