diff --git a/.github/workflows/compliance.yaml b/.github/workflows/compliance.yaml new file mode 100644 index 0000000000..36aa65d508 --- /dev/null +++ b/.github/workflows/compliance.yaml @@ -0,0 +1,68 @@ +# SPDX-FileCopyrightText: 2026 Institute for Automation of Complex Power Systems, EONERC, RWTH Aachen University +# SPDX-License-Identifier: MPL-2.0 + +name: Compliance checks + +on: + pull_request: + branches: [master, main] + +jobs: + no-merge-commits: + name: No merge commits + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Fail if branch contains merge commits + run: | + BASE=${{ github.event.pull_request.base.sha }} + HEAD=${{ github.event.pull_request.head.sha }} + MERGES=$(git log --merges "$BASE".."$HEAD" --oneline) + if [ -n "$MERGES" ]; then + echo "Error: merge commits found in this branch:" + echo "$MERGES" + echo "" + echo "Please rebase your branch instead of merging the target branch:" + echo "" + echo " git fetch upstream" + echo " git rebase upstream/master" + echo " git push --force-with-lease" + exit 1 + fi + + clean-notebook-outputs: + name: Notebooks have no saved outputs + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: actions/setup-python@v5 + with: + python-version: '3.x' + + - name: Fail if a notebook touched by this PR has saved outputs + run: | + BASE=${{ github.event.pull_request.base.sha }} + HEAD=${{ github.event.pull_request.head.sha }} + NOTEBOOKS=$(git diff --name-only --diff-filter=d "$BASE" "$HEAD" -- '*.ipynb') + if [ -z "$NOTEBOOKS" ]; then + exit 0 + fi + pip install nbconvert + python -m nbconvert --ClearOutputPreprocessor.enabled=True --inplace $NOTEBOOKS + if ! git diff --quiet -- $NOTEBOOKS; then + echo "Error: the following notebooks have saved outputs committed:" + git diff --name-only -- $NOTEBOOKS + echo "" + echo "Strip outputs before committing:" + echo "" + echo " jupyter nbconvert --ClearOutputPreprocessor.enabled=True --inplace " + echo "" + echo "Or install pre-commit hooks (see CONTRIBUTING.md) to do this automatically." + exit 1 + fi diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2163547fc3..3f82fba20c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -48,3 +48,29 @@ repos: hooks: - id: markdownlint args: [-r, "~MD013,~MD033,~MD024"] + + - repo: local + hooks: + - id: clear-notebook-outputs + name: clear notebook outputs + language: python + additional_dependencies: ["nbconvert"] + entry: python -m nbconvert --ClearOutputPreprocessor.enabled=True --inplace + files: \.ipynb$ + + - id: no-merge-commits + name: no merge commits (use rebase) + language: system + entry: >- + bash -c '[ ! -f .git/MERGE_HEAD ] || { + echo ""; + echo "Error: merge commits are not allowed in DPsim."; + echo "Please rebase your branch onto the target branch instead:"; + echo ""; + echo " git rebase upstream/master"; + echo " git push --force-with-lease"; + echo ""; + exit 1; }' + stages: [pre-commit] + always_run: true + pass_filenames: false diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..a50de815f5 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,53 @@ + + +# Contributing to DPsim + +Thanks for your interest in contributing! Below are the essentials to get a pull request merged. For full detail see the [Development Guidelines](https://dpsim.fein-aachen.org/docs/development/guidelines/). + +## Quick start + +1. Fork the repository and clone your fork. +1. Run `pre-commit install` to activate automated checks (formatting, linear history guard, notebook output stripping). +1. Create a branch with a descriptive prefix (`feature/`, `fix/`, `docs/`). +1. Make your changes and commit using the conventional commit style (`feat:`, `fix:`, `docs:`, ...) with a sign-off: + + ```shell + git commit -s -m "fix: correct node voltage initialization in DiakopticsSolver" + ``` + +1. Keep your branch up to date by rebasing; do not merge the target branch into your branch: + + ```shell + git fetch upstream + git rebase upstream/master + git push --force-with-lease + ``` + +1. Open a pull request against `sogno-platform/dpsim:master`. + +## Key requirements + +- **Linear history**: merge commits in a feature branch are blocked by CI; rebase instead. +- **Sign-off**: every commit must include `Signed-off-by` via `git commit -s` ([DCO](https://developercertificate.org/)). +- **Formatting**: clang-format 17 for C++, black for Python, markdownlint for Markdown; all enforced by pre-commit. +- **SPDX headers**: new files must include a license header (see [guidelines](https://dpsim.fein-aachen.org/docs/development/guidelines/)). +- **No `std::cout` in C++**: use `SPDLOG_LOGGER_*` macros. +- **No saved notebook outputs**: strip outputs from `.ipynb` files before committing; pre-commit and CI both enforce this. +- **Contributions in forks only**: do not push feature branches to the main repository. + +## AI-assisted contributions + +DPsim follows the Linux Foundation [policy guidance on generative AI tools](https://www.linuxfoundation.org/legal/generative-ai), which permits AI-generated contributions and sets out what to check before making one. Two of its requirements bear directly on this project: the tool's terms must not restrict its output in ways that conflict with the [MPL-2.0](https://mozilla.org/MPL/2.0/), and where the output carries pre-existing third-party material you must have permission to contribute it and must supply the corresponding notice and attribution alongside your change. That guidance invites projects to add their own; the rest of this section is ours. + +Using an assistant changes nothing about who is accountable for the result, and none of this is peculiar to AI. Code you adapted from a forum answer, a blog post or another project has always carried the same obligations, and an assistant is not a special case. You are the author of everything you submit: read it, build it, run the tests, and be ready to defend it in review exactly as if you had typed it. Do not open a pull request containing code you cannot explain. Your `Signed-off-by` line is a statement under the [DCO](https://developercertificate.org/) that you have the right to submit the work, so sign it only once the checks above hold. + +Say so in the pull request description when a change is largely generated, so reviewers know where to look harder. Do not add AI co-author trailers to commit messages; the sign-off already identifies the author. Numerical results, validation notebooks and physical reasoning need the same scrutiny as code, and plausible-looking output is not evidence a model is correct. + +Do not paste unpublished research, confidential data or third-party code you do not own into an external service. + +## License + +By contributing you agree your work is released under the [Mozilla Public License 2.0](https://mozilla.org/MPL/2.0/). diff --git a/docs/hugo/content/en/docs/Contribution guidelines/index.md b/docs/hugo/content/en/docs/Contribution guidelines/index.md index 151aca3c9a..7f8ac36a38 100644 --- a/docs/hugo/content/en/docs/Contribution guidelines/index.md +++ b/docs/hugo/content/en/docs/Contribution guidelines/index.md @@ -8,4 +8,20 @@ description: > --- We'd love to accept your patches and contributions to this project. -Please send us a [pull request](https://github.com/sogno-platform/dpsim/pulls) or get in touch with us via mail or [slack](https://lfenergy.slack.com/archives/C054GB551TL) if you would like to contribute. +Please send us a [pull request](https://github.com/sogno-platform/dpsim/pulls) or get in touch with us via mail or [Slack](https://lfenergy.slack.com/archives/C054GB551TL) if you would like to contribute. + +For detailed requirements on code style, pull request format, and the linear history policy, see [Development Guidelines]({{< relref "/docs/Development/guidelines" >}}). + +# Quick start + +1. Fork the repository and clone your fork. +1. Run `pre-commit install` to activate the automated checks (formatting, linear history guard, etc.). +1. Create a feature branch and make your changes. +1. Keep your branch up to date by rebasing; do not merge the target branch into your branch: + + ```bash + git rebase upstream/master + git push --force-with-lease + ``` + +1. Open a pull request from your fork. diff --git a/docs/hugo/content/en/docs/Development/guidelines.md b/docs/hugo/content/en/docs/Development/guidelines.md index 5cd74875a6..961f54c523 100644 --- a/docs/hugo/content/en/docs/Development/guidelines.md +++ b/docs/hugo/content/en/docs/Development/guidelines.md @@ -51,6 +51,30 @@ There are no strict formal requirements besides the following: */ ``` +1. **Linear History (no merge commits)** + + DPsim maintains a linear git history. + Never merge the target branch into your feature branch — rebase instead: + + ```bash + git rebase upstream/master + git push --force-with-lease + ``` + + A `pre-commit` hook enforces this automatically once you have run `pre-commit install`. + +1. **No Saved Notebook Outputs** + + Jupyter notebooks must be committed without saved cell outputs (images, plots, printed text). + Saved outputs bloat diffs and can break the notebook test collector, which re-executes notebooks and re-extracts their outputs. + Strip outputs before committing: + + ```bash + jupyter nbconvert --ClearOutputPreprocessor.enabled=True --inplace + ``` + + A `pre-commit` hook does this automatically once you have run `pre-commit install`; CI also rejects any notebook that still has outputs saved. + # Creating New Releases (info for maintainers) DPsim currently uses to [Semantic Versioning](https://semver.org/). The periodic creation of