diff --git a/.github/BRANCH_PROTECTION.md b/.github/BRANCH_PROTECTION.md index c386164..4555eb9 100644 --- a/.github/BRANCH_PROTECTION.md +++ b/.github/BRANCH_PROTECTION.md @@ -1,85 +1,56 @@ # Branch protection for `main` -Merging to `main` requires passing CI and a pull request. Configuration lives in -this repository so it can be reviewed and re-applied consistently. +Merging to `main` requires passing CI and a pull request. Configuration lives in this repository so it can be reviewed and re-applied consistently. ## What runs on every PR and push to `main` Workflow: [`.github/workflows/ci.yml`](workflows/ci.yml) -| Job | Purpose | -|-----|---------| -| **Lint (ruff + mypy)** | Style, imports, static types | -| **Test (Python 3.11–3.13)** | Unit tests + coverage | -| **Build sdist & wheel** | Package builds and passes `twine check` | -| **Required checks** | Gate job — fails if any job above failed | +The final job is always named **Required checks**. Branch rulesets require that status to be green before merge. -The ruleset requires the **Required checks** status to be green before merge. +## DCO (Developer Certificate of Origin) -Release tags (`v*`) use [`.github/workflows/release.yml`](workflows/release.yml), -which runs the same quality checks before publishing to PyPI. +Install the [DCO GitHub App](https://github.com/apps/dco) on the Atlas-Commons organization. -## Apply the ruleset (one-time, after creating the GitHub repo) - -GitHub rulesets are configured on the repository, not via git push. Use the -included script: +Every commit must include sign-off: ```bash -chmod +x .github/scripts/apply-main-ruleset.sh -./.github/scripts/apply-main-ruleset.sh Amateur-God technitiumdns-api +git commit -s -m "Your message" ``` -Or manually in the GitHub UI: - -1. **Settings → Rules → Rulesets → New ruleset → New branch ruleset** -2. **Ruleset name:** `Protect main` -3. **Enforcement:** Active -4. **Bypass list:** leave empty (or add org admins only) -5. **Target branches:** `main` -6. Enable rules: - - **Require a pull request before merging** (0 approvals is fine for solo work) - - **Require status checks to pass** → add **`Required checks`** - - **Require branches to be up to date before merging** - - **Block force pushes** - - **Restrict deletions** -7. Save - -### Important: check name must exist first +## Apply the ruleset (one-time) -GitHub only lets you select status checks that have run at least once. After the -repo exists: +GitHub rulesets are configured on the repository, not via git push. -1. Push a branch and open a PR against `main`, **or** -2. Push to `main` once before enabling the ruleset +```bash +chmod +x .github/scripts/apply-main-ruleset.sh +./.github/scripts/apply-main-ruleset.sh Atlas-Commons REPO_NAME +``` -Then apply the ruleset (script or UI). +Or apply to every catalog repo from a machine with `gh` authenticated: -## Ruleset definition (as code) +```bash +./scripts/apply-all-catalog-rulesets.sh +``` -See [`.github/rulesets/main.json`](rulesets/main.json): +### Private repositories (Bot, atlas-commons-website) -- Pull requests required before merge to `main` -- **`Required checks`** must pass (strict: branch must be up to date) -- Force-push blocked -- Deleting `main` blocked +Repository rulesets on **private** repos require GitHub Team or Pro. For those repos, configure branch protection manually under **Settings → Branches** until the org upgrades, or make the repo public. -## Optional: require approval +The apply script skips private repos automatically. -For team repos, edit `.github/rulesets/main.json`: +### Important: check name must exist first -```json -"required_approving_review_count": 1 -``` +GitHub only lets you select status checks that have run at least once. Open a PR against `main` (or push once) **before** applying the ruleset. -Re-run `apply-main-ruleset.sh`. +## Apply rulesets to all catalog repos -## Local checks before opening a PR +See [`atlas-commons-github-templates/scripts/apply-all-catalog-rulesets.sh`](https://github.com/Atlas-Commons/atlas-commons-github-templates) in the template pack, or run from any repo: ```bash -pip install -e ".[dev]" -ruff check src tests -ruff format --check src tests -mypy src -pytest -python -m build && twine check dist/* +for repo in Bot atlas-commons-website technitiumdns-api home-assistant-technitiumdns \ + StreamBooru Hassio-Addons Danbooru-Import-Scripts EmbyArrSync windowsRDP-SSH-tunnel-script; do + gh api --method POST "repos/Atlas-Commons/${repo}/rulesets" --input .github/rulesets/main.json 2>/dev/null || \ + gh api --method PUT "repos/Atlas-Commons/${repo}/rulesets/$(gh api repos/Atlas-Commons/${repo}/rulesets --jq '.[]|select(.name=="Protect main")|.id')" --input .github/rulesets/main.json +done ``` diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..34abd08 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# Default reviewers for Atlas Commons repositories. +* @Amateur-God diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..21f488d --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,60 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +- Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +- The use of sexualized language or imagery, and sexual attention or + advances of any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email + address, without their explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to **stephen@atlastechsolutions.co.uk**. + +All complaints will be reviewed and investigated promptly and fairly. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/version/2/0/code_of_conduct.html), version 2.0. diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..f00578d --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,39 @@ +# Contributing to Atlas Commons + +Thank you for contributing to [Atlas Commons](https://github.com/Atlas-Commons) open-source projects. + +## Before you start + +1. Search [existing issues](https://github.com/Atlas-Commons) for duplicates. +2. For large changes, open an issue first to discuss approach. +3. Read our [Code of Conduct](CODE_OF_CONDUCT.md). + +## Developer Certificate of Origin (DCO) + +**Every commit in a pull request must be signed off.** + +Use `-s` when committing: + +```bash +git commit -s -m "Describe your change" +``` + +This adds a `Signed-off-by:` line certifying you wrote the code or have the right to submit it under the project license. See [developercertificate.org](https://developercertificate.org/). + +The [DCO GitHub App](https://github.com/apps/dco) blocks merges when sign-off is missing. + +## Pull request process + +1. Fork the repository and create a branch from `main`. +2. Make focused changes with tests where applicable. +3. Ensure CI passes locally before opening the PR. +4. Open a pull request against `main` with a clear description. +5. Address review feedback; maintainers will merge when checks are green. + +## Local development + +See each repository's `README.md` for setup instructions. Most projects document install, test, and lint commands there. + +## Questions + +Open a [GitHub Discussion](https://github.com/orgs/Atlas-Commons/discussions) or issue in the relevant repository. diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..608911c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,39 @@ +name: Bug report +description: Report something that is broken or incorrect +title: "[Bug]: " +labels: + - bug +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to report a bug. Please search existing issues first. + - type: textarea + id: description + attributes: + label: What happened? + description: Describe the bug and what you expected instead. + validations: + required: true + - type: textarea + id: reproduce + attributes: + label: Steps to reproduce + description: Minimal steps to reproduce the behavior. + placeholder: | + 1. ... + 2. ... + 3. ... + validations: + required: true + - type: input + id: version + attributes: + label: Version or commit + placeholder: e.g. v1.2.0 or main @ abc1234 + - type: textarea + id: environment + attributes: + label: Environment + description: OS, runtime versions, relevant configuration. + placeholder: e.g. Ubuntu 24.04, Python 3.12, Docker 27 diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..029a1ab --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Security vulnerability + url: https://github.com/Atlas-Commons/.github/blob/main/SECURITY.md + about: Report security issues privately — do not use public issues. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..c32b6cc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,29 @@ +name: Feature request +description: Suggest an improvement or new capability +title: "[Feature]: " +labels: + - enhancement +body: + - type: markdown + attributes: + value: | + Describe the problem you want solved and how you imagine the feature working. + - type: textarea + id: problem + attributes: + label: Problem or use case + description: What problem does this solve? + validations: + required: true + - type: textarea + id: solution + attributes: + label: Proposed solution + description: How would you like this to work? + validations: + required: true + - type: textarea + id: alternatives + attributes: + label: Alternatives considered + description: Other approaches you thought about. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..9816e9c --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,15 @@ +## Summary + + + +## Related issues + + + +## Checklist + +- [ ] I have read [CONTRIBUTING.md](.github/CONTRIBUTING.md) +- [ ] Every commit is signed off (`git commit -s`) for [DCO](https://developercertificate.org/) +- [ ] CI passes locally (or I explain why not applicable) +- [ ] Documentation updated if user-facing behavior changed +- [ ] Tests added or updated where applicable diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 0000000..5afb3c5 --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,26 @@ +# Security Policy + +## Supported versions + +Security fixes are provided for the latest release on the default branch. Older releases may not receive patches. + +## Reporting a vulnerability + +**Please do not report security vulnerabilities via public GitHub issues.** + +Instead, use one of these channels: + +1. **GitHub Security Advisories** — [Open a private report](https://github.com/Atlas-Commons/.github/security/advisories/new) for the affected repository, or use **Report a vulnerability** on the repository Security tab. +2. **Email** — contact the maintainers at **stephen@atlastechsolutions.co.uk** with details and steps to reproduce. + +Include as much detail as possible: affected versions, impact, reproduction steps, and suggested mitigations if you have them. + +## Response timeline + +- **Acknowledgement** within 7 days +- **Fix or mitigation plan** within 60 days for confirmed issues +- Coordinated disclosure preferred; please allow time to release a fix before public disclosure + +## Bug bounty + +Atlas Commons does not operate a paid bug bounty program. We appreciate responsible disclosure and credit researchers in release notes when appropriate. diff --git a/.github/dco.yml b/.github/dco.yml new file mode 100644 index 0000000..c9db6be --- /dev/null +++ b/.github/dco.yml @@ -0,0 +1 @@ +allowSignOff: true diff --git a/.github/scripts/apply-main-ruleset.sh b/.github/scripts/apply-main-ruleset.sh index 8ca56d6..85068e8 100644 --- a/.github/scripts/apply-main-ruleset.sh +++ b/.github/scripts/apply-main-ruleset.sh @@ -8,10 +8,7 @@ # # Usage: # ./.github/scripts/apply-main-ruleset.sh -# ./.github/scripts/apply-main-ruleset.sh Amateur-God technitiumdns-api -# -# The ruleset requires the "Required checks" CI job to pass before merging -# to main. Run this once after creating the GitHub repository. +# ./.github/scripts/apply-main-ruleset.sh Atlas-Commons Bot set -euo pipefail @@ -40,7 +37,14 @@ fi echo "Applying ruleset to ${OWNER}/${REPO} ..." -EXISTING="$(gh api "repos/${OWNER}/${REPO}/rulesets" --jq '.[] | select(.name=="Protect main") | .id' 2>/dev/null || true)" +visibility="$(gh repo view "${OWNER}/${REPO}" --json visibility -q '.visibility' 2>/dev/null || echo unknown)" +if [[ "${visibility}" == "PRIVATE" ]]; then + echo "Cannot apply repository rulesets to private repos without GitHub Team/Pro." >&2 + echo "Configure branch protection manually: https://github.com/${OWNER}/${REPO}/settings/branches" >&2 + exit 1 +fi + +EXISTING="$(gh api "repos/${OWNER}/${REPO}/rulesets" --jq '.[] | select(.name=="Protect main" or .name=="main") | .id' 2>/dev/null | head -1 || true)" if [[ -n "${EXISTING}" ]]; then echo "Updating existing ruleset id=${EXISTING} ..." @@ -62,7 +66,5 @@ echo " https://github.com/${OWNER}/${REPO}/settings/rules" echo "" echo "Notes:" echo " - Merges to main require the 'Required checks' CI job (workflow: CI)." -echo " - Direct pushes to main are blocked; use pull requests." -echo " - Force-push and branch deletion on main are blocked." -echo " - The check name must exist before the ruleset can enforce it." -echo " Open one PR against main so CI runs at least once." +echo " - Install the DCO app: https://github.com/apps/dco" +echo " - Open one PR against main so CI runs before enforcing the ruleset." diff --git a/.github/scripts/verify-dco.sh b/.github/scripts/verify-dco.sh new file mode 100644 index 0000000..4b0bcfb --- /dev/null +++ b/.github/scripts/verify-dco.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# Verify all commits in a PR include Signed-off-by (DCO). +set -euo pipefail + +if [[ "${GITHUB_EVENT_NAME:-}" != "pull_request" ]]; then + echo "Not a pull request — skipping DCO check." + exit 0 +fi + +base="${GITHUB_BASE_REF:-main}" +git fetch origin "${base}" --depth=1 2>/dev/null || git fetch origin "${base}" + +missing=0 +while IFS= read -r sha; do + [[ -z "$sha" ]] && continue + if ! git log -1 --format=%B "$sha" | grep -qi '^Signed-off-by:'; then + echo "Missing Signed-off-by on commit ${sha:0:7}" + git log -1 --oneline "$sha" + missing=1 + fi +done < <(git rev-list "origin/${base}"..HEAD) + +if [[ "$missing" -ne 0 ]]; then + echo "" + echo "Add sign-off with: git commit -s --amend && git push --force-with-lease" + exit 1 +fi + +echo "All commits include DCO sign-off." diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 32208bd..360e184 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -80,11 +80,22 @@ jobs: name: dist-${{ github.run_id }} path: dist/ + dco: + name: DCO sign-off + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + - name: Verify DCO + run: bash .github/scripts/verify-dco.sh + # Single status check required by the main branch ruleset. required: name: Required checks runs-on: ubuntu-latest - needs: [lint, test, build] + needs: [lint, test, build, dco] if: always() steps: - name: Verify all CI jobs succeeded @@ -92,6 +103,7 @@ jobs: echo "lint: ${{ needs.lint.result }}" echo "test: ${{ needs.test.result }}" echo "build: ${{ needs.build.result }}" + echo "dco: ${{ needs.dco.result }}" if [[ "${{ needs.lint.result }}" != "success" ]]; then echo "Lint job failed or was skipped." exit 1 @@ -104,4 +116,8 @@ jobs: echo "Build job failed or was skipped." exit 1 fi + if [[ "${{ github.event_name }}" == "pull_request" && "${{ needs.dco.result }}" != "success" ]]; then + echo "DCO sign-off check failed." + exit 1 + fi echo "All required CI checks passed." diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 0000000..679d043 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,18 @@ +name: Dependency review + +on: + pull_request: + branches: [main] + +permissions: + contents: read + pull-requests: write + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/dependency-review-action@v4 + with: + comment-summary-in-pr: on-failure diff --git a/README.md b/README.md index f0045e5..add282a 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,15 @@ # technitiumdns-api +[![CI](https://github.com/Atlas-Commons/technitiumdns-api/actions/workflows/ci.yml/badge.svg)](https://github.com/Atlas-Commons/technitiumdns-api/actions/workflows/ci.yml) [![PyPI - Version](https://img.shields.io/pypi/v/technitiumdns-api?style=flat-square)](https://pypi.org/project/technitiumdns-api/) [![Python versions](https://img.shields.io/pypi/pyversions/technitiumdns-api?style=flat-square)](https://pypi.org/project/technitiumdns-api/) [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg?style=flat-square)](https://www.gnu.org/licenses/gpl-3.0) +Contributions welcome — see [CONTRIBUTING.md](.github/CONTRIBUTING.md). PRs require DCO sign-off (`git commit -s`). + Async + sync Python client for the [Technitium DNS Server HTTP API](https://github.com/TechnitiumSoftware/DnsServer/blob/master/APIDOCS.md). -Built originally to power the [home-assistant-technitiumdns](https://github.com/Amateur-God/home-assistant-technitiumdns) integration, but works in any Python project. +Built originally to power the [home-assistant-technitiumdns](https://github.com/Atlas-Commons/home-assistant-technitiumdns) integration, but works in any Python project. ## Features @@ -195,7 +198,7 @@ twine check dist/* ## Versioning & license Released under [GPL-3.0-or-later](LICENSE) to match the -[home-assistant-technitiumdns](https://github.com/Amateur-God/home-assistant-technitiumdns) +[home-assistant-technitiumdns](https://github.com/Atlas-Commons/home-assistant-technitiumdns) integration. Follows [Semantic Versioning](https://semver.org/) once the 1.0.0 stable line is published. diff --git a/pyproject.toml b/pyproject.toml index afb88a5..31a33c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ readme = "README.md" license = { text = "GPL-3.0-or-later" } requires-python = ">=3.11" authors = [ - { name = "Amateur-God", email = "stephen@atlastechsolutions.co.uk" }, + { name = "Atlas-Commons", email = "stephen@atlastechsolutions.co.uk" }, ] keywords = [ "technitium", @@ -55,11 +55,11 @@ dev = [ ] [project.urls] -Homepage = "https://github.com/Amateur-God/technitiumdns-api" -Documentation = "https://github.com/Amateur-God/technitiumdns-api#readme" -Repository = "https://github.com/Amateur-God/technitiumdns-api" -Issues = "https://github.com/Amateur-God/technitiumdns-api/issues" -Changelog = "https://github.com/Amateur-God/technitiumdns-api/blob/main/CHANGELOG.md" +Homepage = "https://github.com/Atlas-Commons/technitiumdns-api" +Documentation = "https://github.com/Atlas-Commons/technitiumdns-api#readme" +Repository = "https://github.com/Atlas-Commons/technitiumdns-api" +Issues = "https://github.com/Atlas-Commons/technitiumdns-api/issues" +Changelog = "https://github.com/Atlas-Commons/technitiumdns-api/blob/main/CHANGELOG.md" "Technitium API Docs" = "https://github.com/TechnitiumSoftware/DnsServer/blob/master/APIDOCS.md" [tool.hatch.version]