Skip to content

docs: add CITATION.cff and bring the copyright notice up to date - #615

Merged
JarryShaw merged 2 commits into
mainfrom
docs/citation-cff-and-copyright-year
Sep 22, 2026
Merged

JarryShaw merged 2 commits into
mainfrom
docs/citation-cff-and-copyright-year

Conversation

@JarryShaw

Copy link
Copy Markdown
Owner

Requested by the owner, on the licence question: "fine it being used by anyone as long as my credits are being clearly stated and honoured", then "LISCENSE-wise, let's keep BSD and use your recommendations".

The licence is deliberately unchanged. It stays BSD-3-Clause. No switch to Apache-2.0 or MIT, no NOTICE file, and no change to the licence's terms. BSD-3-Clause already requires that the copyright notice be retained and forbids using the author's name to endorse derived products, which is what was asked for. What was missing was not permission language but discoverability of the credit.

What changed

1. CITATION.cff (new)

Citation File Format 1.2.0. GitHub renders it as the repository's Cite this repository button, and citation managers, Zenodo and dependency inventories read it directly — so credit carries into a paper or a bill of materials instead of depending on a reader opening LICENSE.

Every value is taken from the repository rather than invented:

field value source
version 1.5.0b4 pcapkit/__init__.py:132
date-released 2026-09-20 PyPI upload time of the 1.5.0b4 sdist
authors[0].email jarryshaw@icloud.com pyproject.toml:16
license BSD-3-Clause SPDX identifier for LICENSE
abstract from the README's own wording README.rst
keywords network, pcap, packet pyproject.toml:51

doi and orcid are omitted, not guessed. Neither exists for this project or its author today, and both are checked formats — an invented value would pass validation and still be wrong.

Validated three ways:

$ cffconvert --validate -i CITATION.cff
Citation metadata are valid according to schema version 1.2.0.

plus an independent jsonschema run against the published 1.2.0 schema (every top-level key and every author key checked against the schema's real property list, not from memory), and a render to prove the fields are actually consumable:

$ cffconvert -i CITATION.cff -f apalike
Shaw J. (2026). PyPCAPKit (version 1.5.0b4). URL: https://jarryshaw.github.io/PyPCAPKit/

2. Copyright year, 2018-20232018-2026

LICENSE:3 was the only occurrence in the tree. git grep -n "2018-20" and a case-insensitive sweep for copyright both confirm it; docs/source/conf.py:51 builds its own from datetime.date.today().year and needs no edit.

3. A stray character in the licence text

LICENSE:29 ended ...POSSIBILITY OF SUCH DAMAGE.s — a stray s after the final full stop, present since the Mozilla-to-BSD relicence in bc836cfa2. Removed. This changes no term; it makes the text match the canonical SPDX BSD-3-Clause wording, which matters for automated licence detection. Diffed against the SPDX template: after the fix the only remaining differences are the optional All rights reserved. line and bulleted rather than numbered clauses, both accepted variants. Before the fix there was a third difference, at damage. vs damage.s.

If you would rather the licence file not be touched beyond the year, this is the one hunk to drop.

What was deliberately NOT changed: PEP 639

pyproject.toml:21 still carries the deprecated table form license = { text="BSD 3-Clause License" }, and :62 still carries "License :: OSI Approved :: BSD License". The modern form would be license = "BSD-3-Clause" + license-files = ["LICENSE"] with the classifier removed.

Not done, because it would break installs on Python 3.8. The evidence, measured rather than assumed:

The SPDX string form requires setuptools ≥ 77. Confirmed by running the real build backend at both ends of the boundary:

  • setuptools 75.3.4ValueError: invalid pyproject.toml config: 'project.license'. configuration error: 'project.license' must be valid exactly by one definition (2 matches found) — a hard failure, not a warning.
  • setuptools 77.0.1 → succeeds, emitting Metadata-Version: 2.4, License-Expression: BSD-3-Clause, License-File: LICENSE.

setuptools ≥ 77 cannot be installed on Python 3.8. From a real 3.8.20 interpreter:

$ python3.8 -m pip install --dry-run --no-deps "setuptools>=77.0.0"
ERROR: Ignored the following versions that require a different python version:
  ... 76.1.0 Requires-Python >=3.9; 77.0.1 Requires-Python >=3.9; ...
ERROR: Could not find a version that satisfies the requirement setuptools>=77.0.0
ERROR: No matching distribution found for setuptools>=77.0.0

The candidate list stops at 75.3.4. The current setuptools>=61.0.0 floor is satisfiable there.

And Python 3.6–3.9 have no wheel to fall back on. The lowest published wheel tag is cp310/pp310 — verified against PyPI's file list for 1.5.0a1 through 1.5.0b4, and explained by util/wheel_rename.py, which rewrites the tag to the building interpreter's cp{major}{minor}, and by create-release.yml, whose matrix floors at 3.10. So on 3.8, pip install pypcapkit must build the sdist, which means it must resolve [build-system] requires.

Putting those together:

Python setuptools>=61.0.0 (today) setuptools>=77.0.0 wheel?
3.6 unsatisfiable — 61 needs ≥3.7 (already broken) unsatisfiable no
3.7 resolves 67.8.0; installs, then SyntaxError at import from the documented bpc-poseur bug (already broken) unsatisfiable no
3.8 resolves and works — and per the note at pyproject.toml:27 no bpc conversion is even needed at 3.8 unsatisfiable no
3.9 works works no
3.10+ works works yes

So the change would break exactly one configuration that works today — pip install pypcapkit on Python 3.8 — for an interpreter still named in classifiers and inside requires-python = ">=3.6, <4". Modernising a metadata field is not worth an install regression, and the owner asked for better credit, not a narrower support matrix. There is no conditional escape either: an environment marker can vary the setuptools floor per interpreter, but the SPDX license value is rejected outright by every setuptools below 77, so the build fails on 3.8 regardless of how the requirement is written.

Worth knowing for whenever this is revisited: setuptools 83.0.0+ requires Python ≥ 3.10, so the floor question will recur, and dropping the >=3.6 declaration is the change that actually unblocks PEP 639.

Verification

$ python -m build
Successfully built pypcapkit-1.5.0b4.tar.gz and pypcapkit-1.5.0b4-py3-none-any.whl

$ twine check dist/*
Checking dist/pypcapkit-1.5.0b4-py3-none-any.whl: PASSED
Checking dist/pypcapkit-1.5.0b4.tar.gz: PASSED

LICENSE still ships in both: pypcapkit-1.5.0b4/LICENSE in the sdist, and pypcapkit-1.5.0b4.dist-info/licenses/LICENSE in the wheel. MANIFEST.in is untouched.

Changelog regenerated; python util/changelog_md.py --check exits 0.

Two things found and not changed

  • CITATION.cff is not in the sdist. MANIFEST.in matches *.rst, *.py, README.rst, LICENSE and CHANGELOG.md, so a .cff file is picked up by nothing — confirmed by listing the built tarball. This does not affect the GitHub button, which reads the file from the repository, but a one-line include CITATION.cff would ship it for tools that read installed sources. Left alone because MANIFEST.in is owned by a concurrent branch this wave.
  • docs/source/conf.py:51 starts its copyright at 2017, where LICENSE starts at 2018. One of the two is wrong; picking which is the owner's call, and conf.py was outside this change.

No issue exists for this work, so there is no Fixes line.

@JarryShaw
JarryShaw force-pushed the docs/citation-cff-and-copyright-year branch from 54c3e84 to 3345798 Compare September 22, 2026 02:05
Requested by the owner, who is happy for anyone to use the project as long as
credit is clearly stated and honoured. BSD-3-Clause already delivers that, so
the licence is deliberately unchanged -- still BSD-3-Clause, no NOTICE file and
no change to its terms.

* Add CITATION.cff in Citation File Format 1.2.0, which GitHub renders as the
  "Cite this repository" button and which citation managers and dependency
  inventories read directly. Validated with `cffconvert --validate` and against
  the published 1.2.0 schema; `doi` and `orcid` are omitted rather than
  invented, since neither exists for this project today.
* Bump the LICENSE copyright line from 2018-2023 to 2018-2026. That was its only
  occurrence in the tree -- docs/source/conf.py already derives its own from the
  current year.
* Drop a stray `s` after the closing `DAMAGE.` of the licence text, present
  since the Mozilla-to-BSD relicence, so the wording now matches canonical
  BSD-3-Clause.

The PEP 639 licence-metadata modernisation was considered and deliberately not
made; the PR description carries the measurement that ruled it out.

`python -m build` and `twine check` both pass on the sdist and the wheel.
@JarryShaw
JarryShaw force-pushed the docs/citation-cff-and-copyright-year branch from 3345798 to 6783504 Compare September 22, 2026 02:36
@JarryShaw
JarryShaw merged commit 15189ab into main Sep 22, 2026
8 checks passed
@JarryShaw
JarryShaw deleted the docs/citation-cff-and-copyright-year branch September 22, 2026 03:17
@JarryShaw

Copy link
Copy Markdown
Owner Author

✅ GOOD TO MERGE — head 678350493. Scope is exactly the LICENSE year bump, the LICENSE stray-s fix, and the new CITATION.cff; pyproject.toml/setup.py confirmed untouched. All three claims behind dropping the PEP 639 change independently reproduced, and the CFF independently validated against the live 1.2.0 schema — a malformed one would put an error banner on the repository front page, so this was checked rather than assumed. One correction to the review brief I was given, not to this PR: af1f771b9 is not an ancestor of this branch, so diffing from it falsely shows this PR reverting #611's SECURITY.md work; the true merge-base is a2be2cc1a. A rebase is needed before merge (no semantic conflict). See appendix.

@JarryShaw

Copy link
Copy Markdown
Owner Author

Cross-review appendix — PR #615

Reviewer: Sonnet; PR authored on Opus 5. Reviewed at head 67835049352219c8779be4a99c50558c3d4cacf3 in an isolated worktree. This PR has no code changes, so no pcapkit import or test run was involved.

The assigned diff base was wrong — corrected before judging scope

git diff af1f771b9..678350493 shows SECURITY.md losing its "Handling untrusted captures" section, which looks like undisclosed scope creep. It is not:

git merge-base --is-ancestor af1f771b9 678350493   → exit 1 (NOT an ancestor)
git merge-base af1f771b9 678350493                 → a2be2cc1afcf1f7f56639d5e9a07add0fe1e2b2b
git log --oneline a2be2cc1a..af1f771b9             → af1f771b9 docs: ... SECURITY.md ... (#611)

af1f771b9 is one commit ahead of this branch's fork point, via unrelated already-merged #611. Diffing across that fork point spuriously attributes #611's removal to this PR, which never touches SECURITY.md. The same staleness applies to the other two open PRs in this wave (#621, #619) — all three fork from a2be2cc1a.

True scope, from the real merge-base

git diff a2be2cc1a..678350493 --stat, confirmed identical to git show 6783504935:

CHANGELOG.md                    |  1 +
CITATION.cff                    | 40 ++++++++++++++++++++++++++++++++++++++++
LICENSE                         |  4 ++--
docs/source/changelog/1.5.0.rst | 16 ++++++++++++++++
4 files changed, 59 insertions(+), 2 deletions(-)

pyproject.toml and setup.py absent from the stat — the PEP 639 work is genuinely not here. The two changelog files are this repo's standard bookkeeping, not scope creep.

PEP 639 drop — all three legs reproduced

(a) SPDX license string form needs setuptools ≥77. setuptools' own NEWS.rst at tag v77.0.1 carries the v77.0.0 entry "Added initial support for license expression (PEP 639)... Bump core metadata version to 2.4." Nuance the brief had wrong: 77.0.0 was never publishedhttps://pypi.org/pypi/setuptools/77.0.0/json returns HTTP 404, and PyPI's list jumps 76.1.0 → 77.0.1 → 77.0.3. The PR body correctly cites 77.0.1 as its boundary.

(b) setuptools ≥77 excludes Python 3.8. pypi.org/pypi/setuptools/77.0.1/jsonrequires_python: >=3.9. Reproduced on a real interpreter (Python 3.8.20): pip install --dry-run --no-deps "setuptools>=77.0.0"ERROR: Ignored the following versions that require a different python version: ... 77.0.1 Requires-Python >=3.9.

(c) The decisive claim — no wheel for 3.6–3.9, so [build-system] requires is load-bearing on 3.8. Scanned every wheel filename on PyPI myself:

1.5.0b4         (the series CITATION.cff names): 7 wheels → cp310 cp311 cp312 cp313 cp314 pp310 pp311
1.4.1.post2     (info.version, current stable):  6 wheels → cp311 cp312 cp313 cp314 pp310 pp311
any cp36/37/38/39 or universal py3-none-any?     → none in either

So on 3.8 pip install pypcapkit genuinely must build the sdist, and a [build-system] requires bump to setuptools ≥77 would break that install. A universal py2.py3-none-any wheel does exist in the pre-2019 0.9.x1.0.1 history — which would have undercut the framing had it still been current — but the project moved to per-interpreter tags from 1.0.2 on, and 3.8/3.9 tags are gone by the 1.4/1.5 series. The claim holds, and holds for current stable too, not just the prerelease line.

CITATION.cff — validated, not merely parsed

Structural check of my own: parses under yaml.safe_load; top-level keys abstract, authors, cff-version, date-released, keywords, license, message, repository-artifact, repository-code, title, type, url, version; all four schema-required keys (authors, cff-version, message, title) present; cff-version: 1.2.0.

Real schema validation, independently of the author's run: fetched the live schema (https://citation-file-format.github.io/1.2.0/schema.json, HTTP 200, required: ["authors","cff-version","message","title"]) and ran jsonschema.validate() under jsonschema 4.26.0 in a throwaway venv — validates, no exception. The author's own run used cffconvert 2.0.0 and jsonschema 3.2.0, so this is a genuinely separate validator version agreeing.

doi confirmed absent at top level and orcid absent from authors[0]. Both are pattern-checked formats in the schema, so an invented value would validate while being wrong — omitting them is the correct call and the file's own header comment says so explicitly.

LICENSE typo fix — provenance checked

The canonical SPDX BSD-3-Clause.txt diffed against the post-fix LICENSE leaves only the extra All rights reserved. line and bulleted-vs-numbered clauses, both accepted SPDX variants. git show bc836cfa2:LICENSE | tail -3 confirms the stray s (...SUCH DAMAGE.s) entered in bc836cfa2 ("Regular update", 2020-05-31, the Mozilla→BSD relicense).

Disagreement log

PEP 639 drop: agree, and the earlier recommendation to do it was wrong. All three legs reproduced above, including one detail the brief stated incorrectly (77.0.0 never shipped; 77.0.1 is the boundary). Dropping it was right.

LICENSE typo fix bundled with the year bump: agree with shipping it here. It is a single character deletion after the final full stop, proven against the canonical template with named provenance, in the same three-line region of the same 30-line file already being edited, and the PR body already offers the maintainer a one-hunk opt-out. The general principle that LICENSE byte-diffs deserve their own atomic review has real force — automated license scanners diff that file specifically — but it does not outweigh the cost of a second review cycle for a change with no semantic content. I would object if the edit touched a clause boundary or any wording that changed meaning; this does not.

One dispute, aimed at the review brief rather than the PR: using af1f771b9 as the base is incorrect (not an ancestor; real merge-base a2be2cc1a). A reviewer trusting that raw diff would wrongly accuse this PR of reverting SECURITY.md content it never touches. This PR needs a rebase onto current main before merge, but carries no semantic conflict.

JarryShaw added a commit that referenced this pull request Sep 22, 2026
Requested by the owner; no issue is open for it.

* `LICENSE:3` began the term at 2018, a year after the work it covers. The
  repository's first commit is `c57f7d0b7` "Initial commit", dated 2017-11-07,
  so the notice understated the term and disagreed with `docs/source/conf.py`,
  which already computes its Sphinx footer from 2017.
* That start year survived four hand edits to the same line -- `2018-2020`,
  then `2018-2022`, `2018-2023` and `2018-2026` (#615) -- because every one of
  them corrected the end year and copied `2018` forward untouched.
* The end year is dropped rather than automated. The notice ships inside the
  sdist and the wheel, so it cannot be computed at read time the way the docs
  footer is; copyright subsists from creation regardless of the notice; and
  BSD-3-Clause's canonical form carries a single year. No workflow, hook or
  script is added in its place -- that was considered and rejected.
* `docs/source/conf.py` is untouched: already self-maintaining, already
  starting at 2017, and a docs footer showing a range is conventional.
* Changelog bullet added; `CHANGELOG.md` regenerated with
  `util/changelog_md.py`, and `--check` exits 0.

The licence body is unaltered -- the whole-file diff is one hunk at line 3, and
the remaining 28 lines match the SPDX BSD-3-Clause text word for word. No tests
were run: one line of legal prose, nothing under `pcapkit/`.
JarryShaw added a commit that referenced this pull request Sep 22, 2026
Requested by the owner; no issue is open for it.

* `MANIFEST.in` had `include` lines for `README.md`, `LICENSE` and
  `CHANGELOG.md` but none for `CITATION.cff`, and its two `global-include`
  patterns are `*.rst` and `*.py`, neither of which matches a `.cff`. The file
  was therefore in the repository and in no source distribution.
* The gap is invisible from the web UI, since GitHub renders the "Cite this
  repository" button from the repository. Citation managers, Zenodo and
  dependency inventories read the published artifact, which is the surface that
  was missing it.
* It matters now because #625 has just taught `util/bump_version.py` to keep
  that file's `version` and `date-released` in step with the bump, so a release
  exercising that path would publish an sdist omitting the artefact under test.
  #615 flagged the omission when it added the file; #619 did not address it.
* The new line carries a comment, as its neighbours do, saying why it is
  load-bearing rather than belt-and-braces.
* Changelog bullet added; `CHANGELOG.md` regenerated with
  `util/changelog_md.py`, and `--check` exits 0.

Measured both ways with `python -m build --sdist`: `tar tzf | grep -i citation`
found nothing before and `pypcapkit-1.5.0b4/CITATION.cff` after; the two archive
listings differ by that one entry and nothing else, 860 against 861; the shipped
copy is byte-identical to the repository's; and `twine check --strict` reports
PASSED on both. No tests were run -- one line of packaging configuration,
nothing under `pcapkit/`.
JarryShaw added a commit that referenced this pull request Sep 22, 2026
Requested by the owner; no issue is open for it.

* `LICENSE:3` began the term at 2018, a year after the work it covers. The
  repository's first commit is `c57f7d0b7` "Initial commit", dated 2017-11-07,
  so the notice understated the term and disagreed with `docs/source/conf.py`,
  which already computes its Sphinx footer from 2017.
* That start year was wrong from the moment it was first written. `bc836cfa2`
  (2020-05-31) introduced `2018-2020` when the BSD-3-Clause text replaced
  MPL 2.0 -- the file until then was stock MPL carrying no author notice at all
  -- and the range was then bumped by hand three times, to `2018-2022`,
  `2018-2023` and `2018-2026` (#615), each bump correcting the end year and
  copying `2018` forward untouched.
* The end year is dropped rather than automated. The notice ships inside the
  sdist and the wheel, so it cannot be computed at read time the way the docs
  footer is; copyright subsists from creation regardless of the notice; and
  BSD-3-Clause's canonical form carries a single year. No workflow, hook or
  script is added in its place -- that was considered and rejected.
* `docs/source/conf.py` is untouched: already self-maintaining, already
  starting at 2017, and a docs footer showing a range is conventional.
* Changelog bullet added; `CHANGELOG.md` regenerated with
  `util/changelog_md.py`, and `--check` exits 0.

The licence body is unaltered -- the whole-file diff is one hunk at line 3, and
the remaining 28 lines carry the canonical BSD-3-Clause wording verbatim. Three
things distinguish the file from SPDX's bare `licenseText`, all of them
pre-existing: the `BSD 3-Clause License` title line, the `*` bullets in place of
`1.`/`2.`/`3.`, and the extra `All rights reserved.` line. No tests were run:
one line of legal prose, nothing under `pcapkit/`.
JarryShaw added a commit that referenced this pull request Sep 22, 2026
Requested by the owner; no issue is open for it.

* `MANIFEST.in` had `include` lines for `README.md`, `LICENSE` and
  `CHANGELOG.md` but none for `CITATION.cff`, and its two `global-include`
  patterns are `*.rst` and `*.py`, neither of which matches a `.cff`. The file
  was therefore in the repository and in no source distribution.
* Nor was there a default to fall back on. Removing all three `include` lines
  and rebuilding shows `README.md` and `LICENSE` shipping regardless --
  setuptools adds the latter from `license_files`, logging `adding license file
  'LICENSE'` and recording `License-File: LICENSE` in `PKG-INFO` -- while
  `CHANGELOG.md` disappears. Of the three only `CHANGELOG.md` is load-bearing,
  and a citation file, which nothing outside `MANIFEST.in` knows about, is in
  the same position.
* The gap is invisible from the web UI, since GitHub renders the "Cite this
  repository" button from the repository. Citation managers, Zenodo and
  dependency inventories read the published artifact, which is the surface that
  was missing it.
* It matters now because #625 has just taught `util/bump_version.py` to keep
  that file's `version` and `date-released` in step with the bump, so a release
  exercising that path would publish an sdist omitting the artefact under test.
  #615 flagged the omission when it added the file; #619 did not address it.
* Changelog bullet added; `CHANGELOG.md` regenerated with
  `util/changelog_md.py`, and `--check` exits 0.

Measured both ways with `python -m build --sdist`: `tar tzf | grep -i citation`
found nothing before and `pypcapkit-1.5.0b4/CITATION.cff` after; the two archive
listings differ by that one entry and nothing else, 860 against 861; the shipped
copy is byte-identical to the repository's; and `twine check --strict` reports
PASSED on both. No tests were run -- one line of packaging configuration,
nothing under `pcapkit/`.
JarryShaw added a commit that referenced this pull request Sep 22, 2026
Requested by the owner; no issue is open for it.

* `MANIFEST.in` had `include` lines for `README.md`, `LICENSE` and
  `CHANGELOG.md` but none for `CITATION.cff`, and its two `global-include`
  patterns are `*.rst` and `*.py`, neither of which matches a `.cff`. The file
  was therefore in the repository and in no source distribution.
* Nor was there a default to fall back on. Removing all three `include` lines
  and rebuilding shows `README.md` and `LICENSE` shipping regardless --
  setuptools adds the latter from `license_files`, logging `adding license file
  'LICENSE'` and recording `License-File: LICENSE` in `PKG-INFO` -- while
  `CHANGELOG.md` disappears. Of the three only `CHANGELOG.md` is load-bearing,
  and a citation file, which no packaging default covers at all, is in the same
  position.
* The gap is invisible from the web UI, since GitHub renders the "Cite this
  repository" button from the repository. Citation managers, Zenodo and
  dependency inventories read the published artifact, which is the surface that
  was missing it.
* It matters now because #625 has just taught `util/bump_version.py` to keep
  that file's `version` and `date-released` in step with the bump, so a release
  exercising that path would publish an sdist omitting the artefact under test.
  #615 flagged the omission when it added the file; #619 did not address it.
* It also lets `RepositoryCitationTests` in `tests/project/test_bump_version.py`
  -- the gate #625 added for the hand-authored bumps that never run the script --
  execute against an unpacked sdist, where today it skips itself with
  "CITATION.cff is not shipped in the source distribution".
* Changelog bullet added; `CHANGELOG.md` regenerated with
  `util/changelog_md.py`, and `--check` exits 0.

Rebased onto `cfb81d3f6` after #630 merged; the changelog conflict was resolved
keeping both bullets and regenerating `CHANGELOG.md` rather than merging it.

Measured both ways with `python -m build --sdist` against that base: `tar tzf |
grep -i citation` found nothing before and `pypcapkit-1.5.0b4/CITATION.cff`
after; the listings differ by that one entry and nothing else, 860 against 861;
the shipped copy is byte-identical to the repository's; and `twine check
--strict` reports PASSED on both. No tests were run -- one line of packaging
configuration, nothing under `pcapkit/`.
@JarryShaw JarryShaw added the docs Pull requests that change documentation only (docs: subject prefix) label Sep 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Pull requests that change documentation only (docs: subject prefix)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant