Skip to content

contributing: Use SPDX copyright tags in file headers - #7743

Merged
wenzeslaus merged 1 commit into
OSGeo:mainfrom
wenzeslaus:spdx-headers
Sep 3, 2026
Merged

wenzeslaus merged 1 commit into
OSGeo:mainfrom
wenzeslaus:spdx-headers

Conversation

@wenzeslaus

@wenzeslaus wenzeslaus commented Jul 17, 2026 •

Copy link
Copy Markdown
Member

Replaces the COPYRIGHT: field and the GPL paragraph with SPDX tags in the 985 files where the conversion is mechanical.

  • Years and names preserved as is (no fixes), but split into multiple lines.
  • Every entry has "GRASS Development Team" (even if it did not have one before) without a year range unless it is the only holder in the original COPYRIGHT.
  • Leaves out all the unclear cases, fixes, e.g. misspellings and updates from AUTHORS and Git.

A typical replacements looks like this:

- * COPYRIGHT:    (C) 2008 by Glynn Clements and the GRASS Development Team
- *
- *               This program is free software under the GNU General Public
- *               License (>=v2). Read the file COPYING that comes with GRASS
- *               for details.
+ * SPDX-FileCopyrightText: 2008 Glynn Clements
+ * SPDX-FileCopyrightText: GRASS Development Team
+ * SPDX-License-Identifier: GPL-2.0-or-later

The policy decisions were made during GRASS Community Meeting 2026 in San Michele all'Adige.

Addresses partially #4190. Supersedes #7556, #7557, #7729 and #7716.

Details: rules, scope, deferred cases, and how it was checked (AI generated)

Rules

  • One holder per SPDX-FileCopyrightText line. Named holders first, the collective line last, the license identifier after all copyright lines.
  • Years are copied verbatim. Never normalized, collapsed, gap-filled, reordered, or corrected. v.net.connectivity keeps 2002-20014; g.gui.timeline keeps 2012-13. Those years are wrong, but they were already wrong, and fixing them is a separate question from converting the header.
  • The collective line carries years only where it is the sole copyright line. Where a holder is named, the named line carries the years and the collective line is bare.
  • Every converted file gets the collective line, including the 38 whose notice never named the team. Those notices name only an individual or an institution, for example lib/lidar/* (Politecnico di Milano), imagery/i.topo.corr/* (E. Jorge Tizado) and lib/psdriver/* (Glynn Clements), and they gain SPDX-FileCopyrightText: GRASS Development Team alongside the existing holder. This is deliberate rather than a side effect of the pattern matching: COPYING states that GRASS is copyright by members of the GRASS Development Team, so the collective already holds copyright in those files and the per-file notice was merely incomplete. The named holder is preserved exactly and loses nothing by it. It is the one place where a converted notice says something the old text did not, so it is called out here rather than left to be discovered file by file.
  • The notice text is the only input. Neither AUTHOR(S) nor the git history was consulted, so holder names are copied as written: v.report keeps MN, r.drain keeps metz, r.tileset keeps the misspelled Cedric Shoc. Normalizing them means reading AUTHOR(S) to decide a copyright holder, which is a follow-up with a person in the loop, not a regex.
  • MODULE:, AUTHOR(S): and PURPOSE: are untouched, as is every byte outside the header span.

Scope

converted 985
deferred, reported below 31
headers that define the convention, follow-up 5
archived email, never convertible 1
baseline git grep -lE 'COPYRIGHT:[[:space:]]*\([Cc]\)' 1021

Out of scope: roughly 1300 files whose notices use the bare (C) form with no COPYRIGHT: field, mostly lib/ Doxygen blocks and wxGUI docstrings. This PR converts the COPYRIGHT:-field corpus only, so the tree is left in a mixed state on purpose.

Depends on a follow-up

This PR puts SPDX tags in 985 files, but nothing in the tree documents that header form yet. A follow-up (#7749) updates the style guide templates and g.parser --script to the SPDX form, adds a short note to COPYING on how the GRASS Development Team line is meant, and handles the five files that specify the convention rather than merely carrying it: doc/development/style_guide.md, lib/gis/parser_script.c, general/g.parser/g.parser.md and .html, and utils/copywrite.pl. They are excluded here because each of them states what a header should look like, so they need reading rather than converting, and because two of them generate headers into new files.

RFC 2 says existing copyright headers must not be stripped without agreement. The holder name stays what the notices already used, so nothing changes hands; this is a change of notation, and it should merge after the notation is documented, even though it can be reviewed now.

Not converted

The 31 deferred files are attached as deferred-cases.md, quoted verbatim with the reason each was left alone. Summary:

  • 14 where the holder contains a comma, an ampersand, or the word and. Such punctuation separates two holders in Cedric Shoc, Martin Landa and sits inside one holder in mundialis GmbH & Co.KG and GDF Hannover bR, http://www.gdf-hannover.de. The notice alone cannot tell them apart.
  • 10 with more than one copyright clause under a single label, including the eight v.delaunay files carrying (C) RMIT 1993 alongside the GRASS notice.
  • 3 under the LGPL, not the GPL: i.albedo, i.aster.toar, i.evapo.time. Converting those to GPL-2.0-or-later would misstate the license.
  • 3 where a field interrupts the header, so no known license paragraph follows the notice.
  • 1 whose license paragraphs appear in a combination the tree does not otherwise use.

lib/vector/rtree/docs/MAILS is excluded outright: line 134 is a >-quoted 2001 email asking permission to add GRASS headers. It quotes a header rather than carrying one, and rewriting it would falsify a record.

How it was done, and how to check it

A deterministic script, not a language model. It is attached (spdx_headers.py.txt) together with its tests (spdx_headers_test.py.txt) and the validators (gates.py.txt), so the result can be reproduced or regenerated against a newer base. It is not committed, being a one-off: the repository already carries one abandoned header generator from 2006, utils/copywrite.pl, which has been unrunnable for years and is itself a conversion target.

The script keys on COPYRIGHT:\s*\([Cc]\), so a second run is a no-op by construction. It edits by byte-offset splice and never rebuilds a file from a line list, which is why no trailing newline can drift.

Validators, all run against the raw output before any formatter touched it:

  • Exactly the 985 intended files changed, no others.
  • No byte changed outside the header span; every deferred file is byte-identical.
  • Trailing bytes unchanged, and the size delta equals the span delta exactly.
  • No word appears in a new copyright line that was not in the old notice. You cannot invent a holder if you can only reuse words that were already there.
  • The four-digit year multiset is identical before and after.
  • One holder per line; no emitted C line exceeds 78 columns, below clang-format's limit of 80.
  • pre-commit run --all-files leaves the output byte-identical — 21 hooks, no reformatting.

29 tests cover the harness, including every comment syntax in the tree (*, #, ##, %, CMake #[[, undecorated), the verbatim-year table, and one case per deferral reason.

The conversion itself was performed by the script above. The script, the analysis behind it, and this description were written with AI assistance (Claude Code with Opus 4.8 and Fable 5).

deferred-cases.md
gates.py.txt
spdx_headers_test.py.txt
spdx_headers.py.txt

@wenzeslaus

wenzeslaus commented Jul 17, 2026 •

Copy link
Copy Markdown
Member Author

AI-review as is (not counting prompting and adjustments) since this is difficult to review for humans (edit, it slipped without my prefix):

I checked the claims in the description against the diff itself, without using the conversion script, by parsing the patches this PR reports. Every added line matches one of two shapes, SPDX-FileCopyrightText: or SPDX-License-Identifier: GPL-2.0-or-later, and every removed line is a COPYRIGHT: field, a known GPL paragraph, or a blank comment line. No copyright line names more than one holder, no holder contains a word that was not already in the notice it replaced, no four-digit year is lost or altered, and no emitted C line passes 78 columns. Two things worth recording.

lib/psdriver/psdriver.ps is invisible in the diff view

GitHub reports the file as +0/-0 with no patch, having classified the PostScript as binary. There is no .gitattributes rule for .ps, so this is content heuristics. The change is real and correct, and fetching the blob shows the expected three lines:

%  SPDX-FileCopyrightText: 2007 Glynn Clements
%  SPDX-FileCopyrightText: Other GRASS authors
%  SPDX-License-Identifier: GPL-2.0-or-later

The practical consequence is that this file cannot be reviewed in the web UI, so it needs git show or a blob fetch to see at all.

It also explains the file count arithmetic, which otherwise looks like drift: the local diff is +2142/-5365, this PR reports +2139/-5354, and the gap is exactly 3 additions and 11 deletions, which is precisely psdriver.ps. Nothing is missing from the PR; GitHub is not counting a file it thinks is binary. Worth a line in the description so nobody has to work that out.

The validators are less independent than the count suggests

The description lists a set of checks, and they did all pass, but they are not all independent evidence. The validator script imports the converter and calls it to recompute each header span, so the span-integrity and token-conservation checks compare the converter against its own expectation. A systematically wrong span finder would produce a matching wrong expectation and pass both.

The checks that are genuinely independent are the ones that do not go through the converter:

  • pre-commit run --all-files leaving the output byte-identical, which is what would have caught the trailing-newline regression in docs: adding SPDX License IDs to the source code #DeepSeek4 #7729,
  • the assertion that the prose block was consumed whole rather than in part, which reads the original text after the span,
  • and the patch-level checks in the first paragraph above, which parse the diff and import nothing.

That is roughly three independent facts plus a set of consistency checks, which is enough for a mechanical conversion of this shape, but the list should not be read as a dozen separate confirmations. For a one-off script I would not restructure this; it is worth knowing when weighing how much the passing checks prove.

On sequencing

The description is right that this should not merge before COPYING defines "Other GRASS authors", and the reasoning under RFC 2 is the substantive part rather than a formality: the rename is a notational change rather than a change of holder precisely because the project writes down that the two names mean the same group. Nothing in the PR state enforces that ordering at the moment, so it rests on a reader reaching that paragraph. Whether that matters depends on how quickly the follow-up appears.

@github-actions github-actions Bot added windows Microsoft Windows specific GUI wxGUI related macOS macOS specific vector Related to vector data processing raster Related to raster data processing temporal Related to temporal data processing Python Related code is in Python C Related code is in C C++ Related code is in C++ HTML Related code is in HTML translation Message translation related database Related to database management libraries module docs general display imagery tests Related to Test Suite raster3d notebook misc CMake labels Jul 17, 2026
@nilason

nilason commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

I really do not understand the incentive to change copyright holder in the proposed way (or any other for that matter), and then describe the change in another file (that it means the same thing as before!?). It looks particularly strange when "GRASS Development Team" is the only copyright holder:

- * COPYRIGHT:    (C) 2008 by the GRASS Development Team
- *
- *               This program is free software under the GNU General Public
- *               License (>=v2). Read the file COPYING that comes with GRASS
- *               for details.
+ * SPDX-FileCopyrightText: Other GRASS authors
+ * SPDX-License-Identifier: GPL-2.0-or-later

@wenzeslaus

Copy link
Copy Markdown
Member Author

The "collective author" makes more sense in the files with multiple authors which we may eventually get in the future, but I was purposefully not reading from the author field in this iteration. The old GRASS Development Team is not well defined and misleading, but some future effort will have to clean up that. (Our governance has Core Development Team which is a different entity.) The particular wording is not ideal, but I/we at the community meeting did not find anything better yet. (As for AI suggestions, Fable was suggesting to drop it.)

As for general need for this, @neteler is the one who initiated this.

@nilason

nilason commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

The "collective author" makes more sense in the files with multiple authors which we may eventually get in the future, but I was purposefully not reading from the author field in this iteration. The old GRASS Development Team is not well defined and misleading, but some future effort will have to clean up that. (Our governance has Core Development Team which is a different entity.) The particular wording is not ideal, but I/we at the community meeting did not find anything better yet. (As for AI suggestions, Fable was suggesting to drop it.)

As for general need for this, @neteler is the one who initiated this.

Let us bring this seemingly trivial issue to the grass dev discourse list, before merge. I’m not convinced.

@wenzeslaus

Copy link
Copy Markdown
Member Author

Sounds good to me, but I can lead only the implementation part at this point. No problem updating my PRs (but no promises on my response time right now).

@wenzeslaus

Copy link
Copy Markdown
Member Author

The convention is described in #7749

@neteler

neteler commented Aug 24, 2026

Copy link
Copy Markdown
Member

Let us bring this seemingly trivial issue to the grass dev discourse list, before merge. I’m not convinced.

Please explain what's really missing. We may consider this as a two-pass modification:

  • this PR contains the "dirty" work of streamlining the too many variants of wording etc. into a standardized SPDX header
  • if "Other GRASS authors" is better than the current "GRASS Development Team" I honestly don't know. But that could be changed/reverted rather easily then in a second pass.

Overall, in my view we can only gain by adopting the SPDX headers.

@nilason

nilason commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Let us bring this seemingly trivial issue to the grass dev discourse list, before merge. I’m not convinced.

Please explain what's really missing. We may consider this as a two-pass modification:

  • this PR contains the "dirty" work of streamlining the too many variants of wording etc. into a standardized SPDX header
  • if "Other GRASS authors" is better than the current "GRASS Development Team" I honestly don't know. But that could be changed/reverted rather easily then in a second pass.

Sorry for this brief, and late response to this, I intended to bring this to discourse once I found the time.

  1. "Other GRASS authors" is not more "precise" than "GRASS Development Team", as far as I see it we won't gain anything. On the contrary, if we only have copyright for "GRASS Development Team" a file, then it outright doesn't make any sense to replace it with "Other GRASS authors".
  2. We shouldn't lightly change a copyright, especially if we need to explain elsewhere that it means the same as before.
  3. Perhaps the most important: "GRASS Development Team" is a copyright of the team/project, whereas "Other GRASS authors" is very abstract in my view. This is a "legal" change, we should really think twice about.

We might consider change to "Other GRASS authors" in a second pass, but not the other way around.

Overall, in my view we can only gain by adopting the SPDX headers.

I'm all in for adopting the SPDX headers!

@cwhite911

Copy link
Copy Markdown
Contributor

I think we should stick with "GRASS Development Team" as it is already the standard that is set when citing GRASS. If something more official is desired, I suggest we define what qualifies a person as part of the "GRASS Development Team" and then have the PSC vote to adopt the language. However, the language will require some careful thought.

Also, sorry if this idea has already been brought up elsewhere I did not check.

@neteler

neteler commented Aug 28, 2026

Copy link
Copy Markdown
Member

If @wenzeslaus agrees to change back in this PR to "GRASS Development Team", I have the changes ready and just need to push.

Replace the COPYRIGHT field and the GPL paragraph with SPDX tags in the
985 files where the conversion is mechanical. The notice text is the only
input: neither AUTHOR(S) nor the git history was consulted.

Years are copied verbatim, including where they are plainly wrong, such as
2002-20014 in v.net.connectivity. Correcting them is a separate question
from converting the header, and guessing at them is how the previous
attempt invented copyright holders.

The collective line carries years only where it is the sole copyright
line. Where a holder is named, the named line carries the years and the
collective line is bare. COPYING defines the collective.

31 files are left untouched and reported separately: notices naming
several holders where the punctuation is ambiguous, notices with more than
one copyright clause, three files under the LGPL, and two where a field
interrupts the header. lib/vector/rtree/docs/MAILS is excluded outright,
being an archived email that quotes a header rather than carrying one.

The headers that define the convention are converted in a separate pull
request, and the roughly 1300 files whose notices use the bare (C) form
without a COPYRIGHT field are out of scope here.

The conversion was performed by a deterministic script, not by a language
model. The script and its report are attached to the pull request. It was
written with AI assistance (Claude Opus 4.8); the decisions are the
author's.
@wenzeslaus

Copy link
Copy Markdown
Member Author

I did the update to all PRs. (@neteler you said you have it for this one, but there is six total, so I did all at once and caught some unintentional copyright years removal in the process.)

PR descriptions updated to keep the changes of the collective author. Please, re-review.

@nilason nilason left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Look good to me, great work!

(In addition "saving" some 3000 lines).

@neteler neteler left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@wenzeslaus
wenzeslaus merged commit ba172cd into OSGeo:main Sep 3, 2026
26 of 28 checks passed
@wenzeslaus
wenzeslaus deleted the spdx-headers branch September 3, 2026 02:26
@github-actions github-actions Bot added this to the 8.6.0 milestone Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

C Related code is in C C++ Related code is in C++ CMake database Related to database management display docs general GUI wxGUI related HTML Related code is in HTML imagery libraries macOS macOS specific misc module notebook Python Related code is in Python raster Related to raster data processing raster3d temporal Related to temporal data processing tests Related to Test Suite translation Message translation related vector Related to vector data processing windows Microsoft Windows specific

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants