Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!-- SPDX-FileCopyrightText: Vernum Projecten B.V. -->
<!-- SPDX-License-Identifier: BUSL-1.1 -->

## What changed and why

<!-- Describe the change and the reason for it. Keep it to what a reviewer needs. -->

Closes #NNN

## Licensing of contributions

- [ ] I accept the terms in [CONTRIBUTING.md § Licensing of contributions](../CONTRIBUTING.md#licensing-of-contributions): I have the right to submit this work, I license it under the project licence of the version it lands in, and I grant the Licensor the relicensing right stated there.

## Checklist

- [ ] Shell and workflow files are clean: `shellcheck --severity=style`, `actionlint`, `zizmor --min-severity=low .github/`.
- [ ] Rust gates pass, once a workspace exists: `cargo fmt --all --check`, `cargo clippy --workspace --all-targets --all-features -- -D warnings`, `cargo nextest run --workspace --locked`, `cargo deny check`.
- [ ] Docs are updated, if behaviour changed.
- [ ] Every commit is signed.
- [ ] No AI or assistant attribution anywhere in the commits or this PR.

Contributions carry the licensing terms in
[CONTRIBUTING.md](../CONTRIBUTING.md#licensing-of-contributions); the
`contribution-licence-guard` check reads the box above, and there is no separate
agreement to sign. See [CONTRIBUTING.md](../CONTRIBUTING.md) for the full
contribution guide.
44 changes: 44 additions & 0 deletions .github/workflows/contribution-licence.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# SPDX-FileCopyrightText: Vernum Projecten B.V.
# SPDX-License-Identifier: BUSL-1.1

# The contribution-licence guard.
#
# FerroSMART's contributions carry the licensing terms in CONTRIBUTING.md
# § Licensing of contributions: a contribution is licensed under the project
# licence of the version it lands in, and it grants the Licensor the relicensing
# right that keeps the work one work under one licensor. The pull-request
# template's checkbox is the record of acceptance, and this job is what makes
# it binding: a body without the ticked line fails, whether the box was left
# empty or the section deleted.
#
# Bots are skipped by author type. A bot cannot accept terms, and its changes
# (dependency and pin bumps) carry no copyrightable expression of their own.
#
# The pull-request body reaches the script through `env:` rather than `${{ }}`
# inside `run:`, because a body is attacker-controlled text and interpolation
# would be a template injection.
name: Contribution licence

on:
pull_request:
types: [opened, edited, reopened, synchronize]

permissions: {}

jobs:
contribution-licence:
name: contribution-licence-guard
if: github.event.pull_request.user.type != 'Bot'
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: The pull request body accepts the contribution licensing terms
env:
PR_BODY: ${{ github.event.pull_request.body }}
run: bash scripts/checks/contribution-licence.sh
48 changes: 48 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<!-- SPDX-FileCopyrightText: Vernum Projecten B.V. -->
<!-- SPDX-License-Identifier: BUSL-1.1 -->

# Contributing to FerroSMART

FerroSMART is one of the [FerroHEALTH](https://ferrohealth.eu/) family and has no
code yet. The design happens on the tracker of this repository, and the
tracker is the record of it. The conventions the family shares apply from the
first commit.

## Pull requests

- Branch from `main` with a conventional-type name (`feat/`, `fix/`, `chore/`,
`docs/`, `refactor/`, `perf/`, `test/`, `ci/`, `build/`, `release/`).
- Every commit is signed, and the pull request body declares `Closes #<n>` for
the tracker issue it answers, one `Closes` keyword per issue.
- No AI or assistant attribution anywhere in the commits or the pull request.
- Every first-party file carries the SPDX header
(`SPDX-FileCopyrightText: Vernum Projecten B.V.`,
`SPDX-License-Identifier: BUSL-1.1`).

## Licensing of contributions

FerroSMART's own code is licensed under the Business Source License 1.1
([`LICENSE`](LICENSE)). By submitting a contribution you:

1. certify that you wrote it, or otherwise have the right to submit it under
these terms;
2. license it under the Business Source License 1.1 as applied to the version it
lands in, including that version's Change License, so it becomes Apache 2.0
with the rest of that version; and
3. grant the Licensor named in `LICENSE` a perpetual, irrevocable, worldwide,
royalty-free, transferable right to use, reproduce, modify, distribute,
sublicense and relicense the contribution as part of the Licensed Work under
any terms, including commercial licences.

You keep your copyright. Point 3 is what lets the Licensed Work stay one work
with one licensor: a commercial licence, a change of the licence parameters, or a
transfer of the project can then cover every line, not only the maintainer's own.
There is no separate agreement to sign: the pull request template carries a
checkbox recording your acceptance of these terms, and a pull request from a
person does not merge without it (the `contribution-licence-guard` check, backed
by `scripts/checks/contribution-licence.sh`).

## Security

Report a vulnerability privately through the address in
[`SECURITY.md`](SECURITY.md), never in a public issue.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,8 @@ production use, a commercial licence for any other production use, and Apache
[MAINTAINERS.md](MAINTAINERS.md) is the contact for a commercial licence.

The brand assets under `assets/brand/` are part of the Licensed Work.

Contributions carry the terms in
[CONTRIBUTING.md](CONTRIBUTING.md#licensing-of-contributions): you keep your
copyright, and you grant the Licensor the relicensing right that keeps the work
one work under one licensor. There is no separate agreement to sign.
61 changes: 61 additions & 0 deletions scripts/checks/contribution-licence.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: Vernum Projecten B.V.
# SPDX-License-Identifier: BUSL-1.1
# Every pull request from a person records acceptance of the contribution
# licensing terms (CONTRIBUTING.md § Licensing of contributions).
#
# The terms turn a contribution into part of one work under one Licensor; a
# merge without a recorded acceptance is a line whose relicensing right was
# never granted. A checkbox in the pull request template is the record, and
# this gate is what makes it binding: a body without the ticked line fails,
# whether the box was left empty or the section deleted.
#
# Bots cannot accept terms, and their changes carry no copyrightable expression
# of their own (dependency and pin bumps); the workflow skips them by author
# type before this script runs.
#
# Usage: PR_BODY="$body" scripts/checks/contribution-licence.sh (no arguments)
#
# With PR_BODY unset there is no pull request to judge, so the script checks
# itself instead: a ticked line passes, an unticked line and a body without the
# line fail. That is what lets the local gate battery run this gate rather than
# skip it, and what a CI run never does, because CI always sets PR_BODY.
set -euo pipefail

if [ "$#" -ne 0 ]; then
echo "usage: $0 (reads the pull request body from PR_BODY; takes no arguments)" >&2
exit 2
fi

if [ -z "${PR_BODY+set}" ]; then
self="$0"
PR_BODY='- [x] I accept the terms in [CONTRIBUTING.md § Licensing of contributions](x)' "$self" >/dev/null
if PR_BODY='- [ ] I accept the terms in [CONTRIBUTING.md § Licensing of contributions](x)' "$self" 2>/dev/null; then
echo "self-test: an unticked line passed" >&2; exit 1
fi
if PR_BODY='Closes #1' "$self" 2>/dev/null; then
echo "self-test: a body without the line passed" >&2; exit 1
fi
echo "ok: contribution-licence self-test (a ticked line passes, an unticked or missing line fails)"
exit 0
fi

readonly ACCEPTED='^[[:space:]]*[-*] \[[xX]\] I accept the terms in \[CONTRIBUTING\.md § Licensing of contributions\]'
readonly UNTICKED='^[[:space:]]*[-*] \[ \] I accept the terms in \[CONTRIBUTING\.md § Licensing of contributions\]'

body="${PR_BODY:-}"
if printf '%s\n' "$body" | grep -qE "$ACCEPTED"; then
echo "ok: the contribution licensing terms are accepted in the pull request body"
exit 0
fi
if printf '%s\n' "$body" | grep -qE "$UNTICKED"; then
echo "error: the licensing checkbox in the pull request body is not ticked" >&2
else
echo "error: the pull request body carries no licensing acceptance line" >&2
echo " (the pull request template's 'Licensing of contributions' section was removed)" >&2
fi
echo >&2
echo "Tick the box under 'Licensing of contributions' in the pull request" >&2
echo "description (edit the description; the check re-runs on the edit). The" >&2
echo "terms are CONTRIBUTING.md § Licensing of contributions." >&2
exit 1