From eb0b2543768d19ab6b46eeef495ac7b8b5af19de Mon Sep 17 00:00:00 2001 From: AuraMindNest Date: Thu, 11 Jun 2026 17:26:50 -0600 Subject: [PATCH 1/3] #104:CHANGELOG + deprecation policy (stability signaling). --- .github/workflows/release.yml | 33 ++++++++++++++++++-- CHANGELOG.md | 59 +++++++++++++++++++++++++++++++++++ README.md | 4 ++- 3 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 CHANGELOG.md diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6c1c5c2..ac46708 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -68,6 +68,36 @@ jobs: git tag -a "${tag}" -m "Release ${tag} (Weblate ${weblate_version})" git push origin "${tag}" + - name: Extract changelog section for release + id: changelog + env: + PLUGIN_VERSION: ${{ steps.versions.outputs.plugin_version }} + WEBLATE_VERSION: ${{ steps.versions.outputs.weblate_version }} + run: | + set -euo pipefail + notes_file="${RUNNER_TEMP}/release-notes.md" + python3 <<'PY' + import os + import re + from pathlib import Path + + version = os.environ["PLUGIN_VERSION"] + weblate_version = os.environ["WEBLATE_VERSION"] + changelog = Path("CHANGELOG.md").read_text() + pattern = rf"^## \[{re.escape(version)}\].*?(?=^## |\Z)" + match = re.search(pattern, changelog, re.MULTILINE | re.DOTALL) + if not match: + raise SystemExit( + f"No ## [{version}] section found in CHANGELOG.md" + ) + section = match.group(0).rstrip() + notes_file = Path(os.environ["RUNNER_TEMP"]) / "release-notes.md" + notes_file.write_text( + f"{section}\n\n---\n\nBuilt against Weblate {weblate_version}.\n" + ) + PY + echo "notes_file=${notes_file}" >> "$GITHUB_OUTPUT" + - name: Create GitHub Release env: GH_TOKEN: ${{ github.token }} @@ -77,5 +107,4 @@ jobs: weblate_version="${{ steps.versions.outputs.weblate_version }}" gh release create "${tag}" \ --title "${tag} (Weblate ${weblate_version})" \ - --generate-notes \ - --notes "Built against Weblate ${weblate_version}." + --notes-file "${{ steps.changelog.outputs.notes_file }}" diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..93f3053 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,59 @@ + + +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [1.0.0] - 2026-06-11 + +### Added + +- **QuickBook format** — `QuickBookFormat` convert pipeline for `.qbk` templates; parsing and reconstruction in `boost_weblate.utils.quickbook`; registration via `WEBLATE_FORMATS` in `settings_override.py`. +- **Boost endpoint HTTP API** — routes under `/boost-endpoint/`: + - `GET /boost-endpoint/plugin-ping/` (public health check) + - `GET /boost-endpoint/info/` (authenticated plugin metadata) + - `POST /boost-endpoint/add-or-update/` (authenticated; Celery-backed project/component management) +- **Celery integration** — `boost_add_or_update_task` on Weblate's Celery app; `BoostComponentService` for GitHub submodule clone, scan, and Weblate ORM create/update. +- **Rate limiting** — scoped DRF throttles for protected endpoints (`info`: 60/minute; `add-or-update`: 10/hour); `BOOST_ENDPOINT_THROTTLE_INFO` and `BOOST_ENDPOINT_THROTTLE_ADD_OR_UPDATE` env overrides; HTTP 429 with `Retry-After`. +- **CI pipeline** — umbrella `ci.yml` with lint, test (90% coverage gate), package, dependency audit, Weblate pin sync, and Docker-based plugin smoke/auth/functional jobs. +- **CD pipeline** — staging auto-deploy on `develop` (`cd.yml`); production via `promote-main.yml` (ff-only `develop` → `main`) followed by `main` CD. +- **Weblate version pinning** — `Weblate[all]==…` in `pyproject.toml` synced with Docker `FROM weblate/weblate:…`; enforced by `ci-weblate-pin.yml`; scheduled bumps via `weblate-pin-bump.yml`. +- **Release workflow** — manual `release.yml` tags `main` from `pyproject.toml` version and creates GitHub Releases. + +## Deprecation Policy + +This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html). The plugin version in `pyproject.toml` uses `MAJOR.MINOR.PATCH`: + +- **MAJOR** — incompatible API or integration changes +- **MINOR** — backward-compatible functionality +- **PATCH** — backward-compatible bug fixes + +### Notice period + +Breaking removals or behavior changes require at least **one minor release** of deprecation. For example, a feature deprecated in `1.1.0` may be removed in `2.0.0`, not in `1.2.0`. + +### How changes are communicated + +1. **Changelog** — each deprecation is recorded under `### Deprecated` in the release where it is announced; removal appears under `### Removed` in the major release that drops it. +2. **Runtime warnings** — deprecated Python APIs emit `warnings.warn(..., DeprecationWarning)` where applicable so integrators can detect usage in tests or logs. + +### Public integration surface + +The following are subject to this policy: + +- **HTTP API** — request/response schema and auth requirements for `POST /boost-endpoint/add-or-update/`, `GET /boost-endpoint/info/`, and related Boost endpoint routes documented in `docs/boost-endpoint-api.md`. +- **Format registration** — dotted import paths registered in `WEBLATE_FORMATS` (e.g. `boost_weblate.formats.quickbook.QuickBookFormat`). +- **Settings hook** — documented environment variables read by `settings_override.py` (e.g. `BOOST_ENDPOINT_THROTTLE_INFO`, `BOOST_ENDPOINT_THROTTLE_ADD_OR_UPDATE`). + +### Non-guarantees + +Internal modules under `boost_weblate.utils.*` and undocumented environment variables may change in minor releases unless explicitly listed above. diff --git a/README.md b/README.md index 75d86ac..78284b0 100644 --- a/README.md +++ b/README.md @@ -316,7 +316,7 @@ Each deploy SSHes to `/opt/cppa-weblate-plugin`, pulls the branch, rebuilds with 1. **Promote** — run **Actions → Promote develop to main** ([`promote-main.yml`](.github/workflows/promote-main.yml)): fast-forward `main` to `develop` and push with repository secret **`PROMOTE_PAT`** (required so CI and deploy workflows run; `GITHUB_TOKEN` pushes do not trigger them). 2. **Deploy** — when CI on `main` succeeds, `cd.yml` deploys using **production** environment secrets (`SSH_HOST`, `SSH_USER`, `SSH_PRIVATE_KEY`, `WEBLATE_PORT`, `WEBLATE_URL_PREFIX`; optional `SSH_PORT`). -**Release** tagging ([`release.yml`](.github/workflows/release.yml)) is independent of deploy — run manually when you want a GitHub Release on `main`. +**Release** tagging ([`release.yml`](.github/workflows/release.yml)) is independent of deploy — run manually when you want a GitHub Release on `main`. Release notes are taken from the matching `## [version]` section in [`CHANGELOG.md`](CHANGELOG.md) for the version in `pyproject.toml`. Full deployment and promotion procedure: [docs/deployment-runbook.md](docs/deployment-runbook.md) (staging, production, `PROMOTE_PAT`, rollback, release tagging). @@ -341,6 +341,8 @@ Each script builds `docker/docker-compose.ci.yml`, waits for health, runs its py | Topic | File | Description | |-------|------|-------------| +| Changelog | [`CHANGELOG.md`](CHANGELOG.md) | Version history (Keep a Changelog) | +| API stability | [`CHANGELOG.md#deprecation-policy`](CHANGELOG.md#deprecation-policy) | SemVer, deprecation notice period, public API surface | | All env vars | [`.env.example`](.env.example) | Annotated template — copy to `.env` on the deploy server | | Deployment & promotion | [`docs/deployment-runbook.md`](docs/deployment-runbook.md) | Staging/production CD, `PROMOTE_PAT`, environments, health checks, rollback, release tagging | | Boost endpoint throttles | [`src/boost_weblate/settings_override.py`](src/boost_weblate/settings_override.py) | `BOOST_ENDPOINT_THROTTLE_INFO`, `BOOST_ENDPOINT_THROTTLE_ADD_OR_UPDATE`; merged into `REST_FRAMEWORK` | From 64085567a9c5d075e4b8c9b06e015131f021a5fd Mon Sep 17 00:00:00 2001 From: AuraMindNest Date: Thu, 11 Jun 2026 17:29:17 -0600 Subject: [PATCH 2/3] Fix the manifest CI fail. --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 78edd3d..9eb745e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -153,6 +153,7 @@ module-name = "boost_weblate" module-root = "src" source-include = [ ".env.example", + "CHANGELOG.md", "docker/**", "docs/**", "LICENSES/**", From a6b012aa989c33fde192ea3538da85824f4621d9 Mon Sep 17 00:00:00 2001 From: AuraMindNest Date: Fri, 12 Jun 2026 10:25:38 -0600 Subject: [PATCH 3/3] Fix due to the first reviewer. --- .github/workflows/release.yml | 26 +++++++++++++------------- docs/deployment-runbook.md | 9 ++++++--- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ac46708..60dcd2a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -55,19 +55,6 @@ jobs: exit 1 fi - - name: Configure git for tag push - run: | - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - - - name: Create and push tag - run: | - set -euo pipefail - tag="${{ steps.versions.outputs.tag }}" - weblate_version="${{ steps.versions.outputs.weblate_version }}" - git tag -a "${tag}" -m "Release ${tag} (Weblate ${weblate_version})" - git push origin "${tag}" - - name: Extract changelog section for release id: changelog env: @@ -98,6 +85,19 @@ jobs: PY echo "notes_file=${notes_file}" >> "$GITHUB_OUTPUT" + - name: Configure git for tag push + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + - name: Create and push tag + run: | + set -euo pipefail + tag="${{ steps.versions.outputs.tag }}" + weblate_version="${{ steps.versions.outputs.weblate_version }}" + git tag -a "${tag}" -m "Release ${tag} (Weblate ${weblate_version})" + git push origin "${tag}" + - name: Create GitHub Release env: GH_TOKEN: ${{ github.token }} diff --git a/docs/deployment-runbook.md b/docs/deployment-runbook.md index e3b058a..f5a86ba 100644 --- a/docs/deployment-runbook.md +++ b/docs/deployment-runbook.md @@ -385,14 +385,16 @@ The workflow does not check deploy status or server health. - Plugin version: `[project].version` (e.g. `1.0.0`) - Weblate pin: `Weblate[all]==…` (e.g. `2026.5`) 2. Fails if tag `v` already exists on `origin` (prevents duplicate releases) -3. Creates annotated tag `v` on current `main` HEAD and pushes it -4. Creates a GitHub Release with auto-generated notes, title `v (Weblate )`, and body noting Weblate compatibility +3. Extracts the matching `## []` section from [`CHANGELOG.md`](../CHANGELOG.md) (fails if missing, before any tag is pushed) +4. Creates annotated tag `v` on current `main` HEAD and pushes it +5. Creates a GitHub Release with title `v (Weblate )` and the extracted changelog as release notes, plus a trailing Weblate compatibility line Use the release title and body to verify which Weblate version the tagged tree was built against. ### Prerequisites - `version` in `pyproject.toml` on `main` must be the release you intend (bump on `develop` and promote, or commit on `main`, before running) +- [`CHANGELOG.md`](../CHANGELOG.md) on `main` must include a `## []` section for that version - Tag `v` must not already exist ### Failure modes @@ -400,7 +402,8 @@ Use the release title and body to verify which Weblate version the tagged tree w | Failure | Likely cause | |---------|----------------| | Tag already exists | Re-ran without bumping `version` in `pyproject.toml` | -| Wrong release contents | `main` HEAD did not include the expected `pyproject.toml` | +| No changelog section | `CHANGELOG.md` on `main` has no `## []` heading for the intended release | +| Wrong release contents | `main` HEAD did not include the expected `pyproject.toml` or `CHANGELOG.md` | | `gh release create` failed | Permissions or network; check whether the tag was pushed and finish the release manually on GitHub | ### Important