diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000..82d0693 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,34 @@ +name-template: "$RESOLVED_VERSION" +tag-template: "$RESOLVED_VERSION" +version-template: "$MAJOR.$MINOR.$PATCH" +version-resolver: + major: + labels: + - "semver:major" + minor: + labels: + - "semver:minor" + patch: + labels: + - "semver:patch" + default: patch +categories: + - title: "Features" + labels: + - feature + - enhancement + - title: "Bug Fixes" + labels: + - bug + - fix + - title: "Maintenance" + labels: + - chore + - maintenance + - refactor + - dependencies + - documentation +change-template: "- $TITLE (#$NUMBER) @$AUTHOR" +no-changes-template: "No user-facing changes." +exclude-labels: + - skip-changelog diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 0000000..3d5a26b --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,22 @@ +name: Draft Release Notes + +on: + push: + branches: + - master + workflow_dispatch: + +permissions: + contents: read + +jobs: + update_release_draft: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: read + + steps: + - uses: release-drafter/release-drafter@v6 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..b2a0b83 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,112 @@ +name: Publish Release + +on: + push: + tags: + - "*.*.*" + workflow_dispatch: + +permissions: + contents: read + id-token: write + +env: + PYTHON_VERSION: "3.12" + +jobs: + build: + name: Build distribution + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ env.PYTHON_VERSION }} + + - name: Install build backend + run: | + python -m pip install --upgrade pip + pip install build + + - name: Build package + run: python -m build + + - name: Upload dist artifact + uses: actions/upload-artifact@v4 + with: + name: syncmymoodle-dist + path: dist/* + if-no-files-found: error + + publish-testpypi: + name: Publish to TestPyPI + runs-on: ubuntu-latest + needs: build + environment: + name: testpypi + url: https://test.pypi.org/p/syncMyMoodle + permissions: + id-token: write + + steps: + - uses: actions/download-artifact@v4 + with: + name: syncmymoodle-dist + path: dist + + - name: Upload to TestPyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + repository-url: https://test.pypi.org/legacy/ + + publish-pypi: + name: Publish to PyPI + runs-on: ubuntu-latest + needs: + - build + - publish-testpypi + environment: + name: pypi + url: https://pypi.org/p/syncMyMoodle + permissions: + id-token: write + + steps: + - uses: actions/download-artifact@v4 + with: + name: syncmymoodle-dist + path: dist + + - name: Upload to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + + github-release: + name: Create GitHub release + runs-on: ubuntu-latest + needs: publish-pypi + if: ${{ needs.publish-pypi.result == 'success' }} + permissions: + contents: write + + steps: + - uses: actions/download-artifact@v4 + with: + name: syncmymoodle-dist + path: dist + + - name: Publish curated release notes + id: publish_release + uses: release-drafter/release-drafter@v6 + with: + publish: true + tag: ${{ github.ref_name }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload release artifacts + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: gh release upload ${{ github.ref_name }} dist/* --clobber diff --git a/README.md b/README.md index f828618..3bd1671 100644 --- a/README.md +++ b/README.md @@ -237,3 +237,8 @@ in the configuration file. If everything went alright, you won't need to enter your password again in the future, as it will be obtained automatically and securely from the Secret Service Integration. + + +## Maintenance + +Information to create releases and publish them can be found in `docs/releasing.md`. \ No newline at end of file diff --git a/docs/releasing.md b/docs/releasing.md new file mode 100644 index 0000000..a0e9a82 --- /dev/null +++ b/docs/releasing.md @@ -0,0 +1,54 @@ +# Releasing syncMyMoodle + +There are two GitHub Actions workflows that do publishing and prepare release notes. + +## Release Drafter workflow + +Every push to `master` has it's information go into `release-drafter.yml`, which updates the draft release on GitHub. Labels drive what happens: + +- `feature`, `bug`, `maintenance`, `dependencies`, `documentation`, etc. decide + which section a PR lands in. +- `semver:major`, `semver:minor`, `semver:patch` hint at the next version bump. +- `skip-changelog` hides a PR completely. + +Before you tag a release, open the draft on GitHub and tweak the wording. Do it +close to tagging so new merges don't overwrite your edits. + +## Publish Release workflow + +`release.yaml` runs on tags that look like our versions (`0.2.3`, `0.2.3.post1`, +`0.3.0-rc.1`, ...) or when you trigger it manually. The job order: + +1. Build sdist + wheel via `python -m build`. +2. Push both archives to TestPyPI and PyPI using Trusted Publishing (OIDC). +3. Publish the curated release draft and upload the built artifacts to it. + +Since this uses Trusted Publishing you don't need API tokens, but PyPI/TestPyPI +must trust the workflow first. + +## Trusted Publishing setup + +(this is already done) + +1. On PyPI and TestPyPI open **Manage project -> Publishing**. +2. Click **Add a trusted publisher -> GitHub Actions**. +3. Point it at this repo, the workflow `.github/workflows/release.yaml`, and the + matching environment (`pypi` or `testpypi`). +4. After the first workflow run, approve the pending publisher in the UI once. + +## Release todos + +1. Merge PRs with the right labels and useful titles for the release note draft. +2. Bump the version in `pyproject.toml`. +3. Commit and tag using the version string (`X.Y.Z`, `X.Y.Z.postN`, etc.): + ```bash + git tag 0.2.4 + git push origin 0.2.4 + ``` +4. Watch the "Publish Release" workflow. Approve the Trusted Publishing request + on PyPI/TestPyPI if one pops up (didn't have that happen for me yet). +5. Once it's green, PyPI/TestPyPI have the new files and the GitHub release is + live with the release notes + artifacts. + +To re-run the workflow, use the Actions +tab -> **Publish Release -> Run workflow**.