diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c2f36b0..6e2db48 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,42 +8,58 @@ on: branches: - main +permissions: + contents: read + jobs: + release-change: + name: Detect release merge + runs-on: ubuntu-latest + outputs: + is-release: ${{ steps.release.outputs.is-release }} + steps: + - uses: compas-dev/compas-actions/release-check@main + id: release + build: - if: "!contains(github.event.pull_request.labels.*.name, 'docs-only')" + needs: release-change + if: github.event_name == 'pull_request' || needs.release-change.outputs.is-release != 'true' runs-on: ${{ matrix.os }} strategy: + fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] python: ["3.9", "3.10", "3.11", "3.12", "3.13"] - + exclude: + - os: macos-latest + python: "3.9" steps: - - uses: compas-dev/compas-actions.build@v5 + - uses: compas-dev/compas-actions/ci@main with: - python: ${{ matrix.python }} - management_tool: uv - invoke_lint: true - invoke_test: true + python-version: ${{ matrix.python }} + management-tool: uv + invoke-tasks: lint test + create-assets: needs: build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - name: Set up Python - uses: actions/setup-python@v4 + - uses: actions/checkout@v7.0.1 + with: + persist-credentials: false + + - uses: compas-dev/compas-actions/setup-python@main with: - python-version: '3.12' - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -e .[dev] + python-version: "3.12" + management-tool: uv + extras: dev + - name: Test asset creation - run: | - invoke create-class-assets - echo "Generated assets:" - ls dist/ + run: invoke pre-build + - name: Upload test artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7.0.1 with: name: protobuf-test-assets path: dist/proto/*.zip + if-no-files-found: error diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml deleted file mode 100644 index fc79635..0000000 --- a/.github/workflows/docs.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: docs - -# Called by the release workflow after a successful publish, and dispatchable by hand. -# This deliberately does not listen for `release: published`: the release is created by -# release.yml using GITHUB_TOKEN, and events raised with that token never start new -# workflow runs, so that trigger can never fire. -on: - workflow_call: - workflow_dispatch: - -permissions: - contents: write - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: 3.x - - - name: Install dependencies - run: | - pip install -e ".[docs,dev]" - - - name: Configure Git Credentials - run: | - git config user.name github-actions[bot] - git config user.email 41898282+github-actions[bot]@users.noreply.github.com - - - name: Generate Protobuf Docs - run: | - invoke proto-docs - - - name: Get version - run: | - VERSION=$(python -c "import compas_pb; print(compas_pb.__version__)") - echo "VERSION=$VERSION" >> $GITHUB_ENV - - - name: Deploy to GitHub Pages - run: | - git fetch origin gh-pages --depth=1 - mike deploy --push --update-aliases ${{ env.VERSION }} latest diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index fa70e31..130828c 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -1,4 +1,5 @@ name: verify-pr-checklist + on: pull_request: types: [assigned, opened, synchronize, reopened, labeled, unlabeled] @@ -6,16 +7,21 @@ on: - main - master +permissions: + contents: read + jobs: - build: - name: Check Actions + release: + name: Validate release metadata + runs-on: ubuntu-latest + steps: + - uses: compas-dev/compas-actions/release-check@main + + changelog: + name: Check changelog runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 - - name: Changelog check - uses: Zomzog/changelog-checker@v1.2.0 + - uses: compas-dev/compas-actions/pr-checks@main with: - fileName: CHANGELOG.md - checkNotification: Simple - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + changelog-path: CHANGELOG.md + skip-label: no changelog diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml new file mode 100644 index 0000000..a571cc1 --- /dev/null +++ b/.github/workflows/prepare-release.yml @@ -0,0 +1,26 @@ +name: prepare release + +on: + workflow_dispatch: + inputs: + release-type: + description: Semantic version component to bump + required: true + type: choice + options: + - patch + - minor + - major + +permissions: + contents: write + pull-requests: write + +jobs: + release-pr: + runs-on: ubuntu-latest + steps: + - uses: compas-dev/compas-actions/release-pr@main + with: + release-type: ${{ inputs.release-type }} + github-token: ${{ github.token }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 07664fe..afd3738 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,68 +2,94 @@ name: release on: push: - tags: - - "v*" + branches: + - main + +permissions: + contents: read jobs: + release: + name: Detect release merge + runs-on: ubuntu-latest + outputs: + is-release: ${{ steps.release.outputs.is-release }} + version: ${{ steps.release.outputs.version }} + tag: ${{ steps.release.outputs.tag }} + steps: + - uses: compas-dev/compas-actions/release-check@main + id: release + build: + needs: release + if: needs.release.outputs.is-release == 'true' runs-on: ${{ matrix.os }} strategy: + fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] python: ["3.11", "3.12", "3.13"] + steps: + - uses: compas-dev/compas-actions/ci@main + with: + python-version: ${{ matrix.python }} + management-tool: uv + invoke-tasks: lint test + prepare: + needs: [release, build] + if: needs.release.outputs.is-release == 'true' + runs-on: ubuntu-latest steps: - - uses: compas-dev/compas-actions.build@v5 + - uses: compas-dev/compas-actions/prepare-release@main with: - python: ${{ matrix.python }} - management_tool: uv - invoke_lint: true - invoke_test: true + python-version: "3.12" + management-tool: uv + run-prebuild: true + release-assets: dist/proto/*.zip publish: - needs: build + needs: [release, prepare] + if: needs.release.outputs.is-release == 'true' runs-on: ubuntu-latest permissions: - contents: write + contents: read id-token: write steps: - - uses: compas-dev/compas-actions.publish@add-trusted-publishing + - name: Download package distributions + uses: actions/download-artifact@v8.0.1 with: - github_token: ${{ secrets.GITHUB_TOKEN }} + name: python-package-distributions + path: dist + + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@v1.14.2 - upload-assets: - needs: publish + create-release: + needs: [release, publish] + if: needs.release.outputs.is-release == 'true' runs-on: ubuntu-latest permissions: contents: write steps: - - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v4 + - uses: compas-dev/compas-actions/github-release@main with: - python-version: "3.12" - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -e .[dev] + github-token: ${{ github.token }} + tag-name: ${{ needs.release.outputs.tag }} + target: ${{ github.sha }} + release-assets-artifact-name: github-release-assets - - name: Generate protobuf assets - run: invoke create-class-assets - - - name: Upload Assets to Existing Release - uses: softprops/action-gh-release@v2 - with: - files: dist/proto/*.zip - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - # Called directly rather than left to fire on `release: published`, which never happens - # for a release created with GITHUB_TOKEN. docs: - needs: publish - uses: ./.github/workflows/docs.yml + needs: [release, create-release] + if: needs.release.outputs.is-release == 'true' + runs-on: ubuntu-latest permissions: contents: write + steps: + - uses: compas-dev/compas-actions/docs@main + with: + deploy: true + version: ${{ needs.release.outputs.version }} + extras: docs,dev + pre-build-tasks: proto-docs + strict: false diff --git a/README.md b/README.md index a538745..aef77b4 100644 --- a/README.md +++ b/README.md @@ -86,3 +86,7 @@ please check out the online documentation here: [compas_pb docs](https://compas. ## Issue Tracker If you find a bug or if you have a problem with running the code, please file an issue on the [Issue Tracker](https://github.com/compas-dev/compas_pb/issues). + +## Maintainers + +Releases are prepared and approved through pull requests. See [RELEASING.md](RELEASING.md). diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 0000000..16ab7be --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,31 @@ +# Releasing + +Releases are prepared and approved through pull requests. Do not create a +release by running `invoke release` or by pushing a tag locally. + +## Prepare a release + +1. Keep user-facing changes in the `Unreleased` section of `CHANGELOG.md` as + normal pull requests are merged. +2. Open **Actions → prepare release → Run workflow**. +3. Select `patch`, `minor`, or `major`. +4. Review the generated `release/vX.Y.Z` pull request. Edit the changelog in + that branch if the release notes need additional work. +5. Merge the pull request using merge, squash, or rebase. The merge strategy + does not affect the release. + +The merge to `main` validates the version and changelog, runs the full build, +publishes to PyPI with trusted publishing, creates the version tag and GitHub +release, and deploys the versioned documentation. + +## Repository setup + +PyPI's trusted publisher must match this repository, the +`.github/workflows/release.yml` workflow, and the `pypi` environment. + +The preparation workflow uses the repository's standard `GITHUB_TOKEN`. In +**Settings → Actions → General → Workflow permissions**, enable **Allow GitHub +Actions to create and approve pull requests**. GitHub holds checks from the +automatically created release pull request for manual approval; a maintainer +with write access must select **Approve workflows to run** before it can be +merged. diff --git a/pyproject.toml b/pyproject.toml index 147d4d8..0cadc92 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -131,8 +131,8 @@ doctest_optionflags = [ [tool.bumpversion] current_version = "1.1.3" message = "Bump version to {new_version}" -commit = true -tag = true +commit = false +tag = false [[tool.bumpversion.files]] filename = "src/compas_pb/__init__.py" @@ -191,4 +191,3 @@ max-doc-length = 179 [tool.ruff.format] docstring-code-format = true docstring-code-line-length = "dynamic" - diff --git a/tasks.py b/tasks.py index a1a453f..7d032b2 100644 --- a/tasks.py +++ b/tasks.py @@ -1,15 +1,22 @@ from pathlib import Path -from invoke.collection import Collection from compas_invocations2 import build from compas_invocations2 import style from compas_invocations2 import tests +from invoke.collection import Collection +from invoke.tasks import task -from compas_pb.invocations import generate_proto_classes from compas_pb.invocations import create_class_assets +from compas_pb.invocations import generate_proto_classes from compas_pb.invocations import proto_docs +@task +def pre_build(ctx): + """Create generated protobuf archives before building a release.""" + create_class_assets(ctx) + + ns = Collection( style.check, style.lint, @@ -17,9 +24,8 @@ tests.test, tests.testdocs, tests.testcodeblocks, - build.prepare_changelog, build.clean, - build.release, + pre_build, generate_proto_classes, create_class_assets, proto_docs,