diff --git a/.github/workflows/build_scenario_wheels.yaml b/.github/workflows/build_scenario_wheels.yaml deleted file mode 100644 index 87833841a..000000000 --- a/.github/workflows/build_scenario_wheels.yaml +++ /dev/null @@ -1,61 +0,0 @@ -name: Build ops-scenario wheels - -# TODO: adjust this workflow to properly build ops-scenario from the operator repo -# and then this should be adjusted to run when appropriate (not on push to main -# any more, but as part of the releasing workflow we agree on). -on: - workflow_dispatch -# push: -# branches: -# - main - -jobs: - build_wheel: - name: Build wheel on ubuntu (where else???) - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-python@v5 - - - name: Install build - run: pip install build - - - name: Build wheel - run: python -m build - - - uses: actions/upload-artifact@v3 - with: - path: ./dist/*.whl - - - name: Get the version - id: get_version - run: echo "VERSION=$(sed -n 's/^ *version.*=.*"\([^"]*\)".*/\1/p' pyproject.toml)" >> $GITHUB_OUTPUT - - - name: release - uses: actions/create-release@v1 - id: create_release - with: - draft: false - prerelease: false - tag_name: ${{ steps.get_version.outputs.VERSION }} - release_name: ${{ steps.get_version.outputs.VERSION }} - - env: - GITHUB_TOKEN: ${{ github.token }} - - - name: upload wheel - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ github.token }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./dist/ops_scenario-${{ steps.get_version.outputs.VERSION }}-py3-none-any.whl - asset_name: ops_scenario-${{ steps.get_version.outputs.VERSION }}-py3-none-any.whl - asset_content_type: application/wheel - - - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 - with: - password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish-ops-scenario.yaml similarity index 84% rename from .github/workflows/publish.yml rename to .github/workflows/publish-ops-scenario.yaml index 05c1527e8..15664910f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish-ops-scenario.yaml @@ -1,8 +1,8 @@ name: Publish on: - release: - types: - - published + push: + tags: + - 'scenario-7.*' jobs: framework-tests: @@ -12,7 +12,7 @@ jobs: hello-charm-tests: uses: ./.github/workflows/hello-charm-tests.yaml build-n-publish: - name: Build and Publish to PyPI + name: Build and Publish ops-scenario to PyPI runs-on: ubuntu-latest permissions: id-token: write @@ -27,9 +27,10 @@ jobs: run: pip install wheel build - name: Build run: python -m build + working-directory: ./testing - name: Attest build provenance uses: actions/attest-build-provenance@v1.4.3 with: - subject-path: 'dist/*' + subject-path: 'testing/dist/*' - name: Publish uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/publish-ops.yaml b/.github/workflows/publish-ops.yaml new file mode 100644 index 000000000..c391664a4 --- /dev/null +++ b/.github/workflows/publish-ops.yaml @@ -0,0 +1,39 @@ +name: Publish +on: + push: + tags: + # TODO: When we come to ops v3.x we will need to reconsider what to do + # here (we might want to start prefixing with 'v' for one thing), and we + # will likely be publishing at least three packages: ops, ops-scenario, + # and ops-harness. + - '2.*' + +jobs: + framework-tests: + uses: ./.github/workflows/framework-tests.yaml + observability-charm-tests: + uses: ./.github/workflows/observability-charm-tests.yaml + hello-charm-tests: + uses: ./.github/workflows/hello-charm-tests.yaml + build-n-publish: + name: Build and Publish ops to PyPI + runs-on: ubuntu-latest + permissions: + id-token: write + attestations: write + contents: read + needs: [framework-tests, observability-charm-tests, hello-charm-tests] + steps: + - uses: actions/checkout@v4 + - name: Setup Python + uses: actions/setup-python@v5 + - name: Install build dependencies + run: pip install wheel build + - name: Build + run: python -m build + - name: Attest build provenance + uses: actions/attest-build-provenance@v1.4.3 + with: + subject-path: 'dist/*' + - name: Publish + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/smoke.yaml b/.github/workflows/smoke.yaml index 3f69d1e2f..b95940d17 100644 --- a/.github/workflows/smoke.yaml +++ b/.github/workflows/smoke.yaml @@ -2,8 +2,8 @@ name: ops Smoke Tests on: workflow_dispatch: - schedule: - - cron: '0 7 25 * *' + schedule: + - cron: '0 7 25 * *' jobs: test: diff --git a/.github/workflows/test-publish-ops-scenario.yaml b/.github/workflows/test-publish-ops-scenario.yaml new file mode 100644 index 000000000..398d5af1b --- /dev/null +++ b/.github/workflows/test-publish-ops-scenario.yaml @@ -0,0 +1,35 @@ +name: Test Publish +on: [workflow_dispatch, workflow_call] + +jobs: + framework-tests: + uses: ./.github/workflows/framework-tests.yaml + observability-charm-tests: + uses: ./.github/workflows/observability-charm-tests.yaml + hello-charm-tests: + uses: ./.github/workflows/hello-charm-tests.yaml + build-n-publish: + name: Build and Publish ops-scenario to Test PyPI + runs-on: ubuntu-latest + permissions: + id-token: write + attestations: write + contents: read + needs: [framework-tests, observability-charm-tests, hello-charm-tests] + steps: + - uses: actions/checkout@v4 + - name: Setup Python + uses: actions/setup-python@v5 + - name: Install build dependencies + run: pip install wheel build + - name: Build + run: python -m build + working-directory: ./testing + - name: Attest build provenance + uses: actions/attest-build-provenance@v1.4.3 + with: + subject-path: 'testing/dist/*' + - name: Publish to test.pypi.org + uses: pypa/gh-action-pypi-publish@release/v1 + with: + repository-url: https://test.pypi.org/legacy/ diff --git a/.github/workflows/test-publish.yml b/.github/workflows/test-publish-ops.yaml similarity index 95% rename from .github/workflows/test-publish.yml rename to .github/workflows/test-publish-ops.yaml index da6fb5567..dcfcb3b9d 100644 --- a/.github/workflows/test-publish.yml +++ b/.github/workflows/test-publish-ops.yaml @@ -9,7 +9,7 @@ jobs: hello-charm-tests: uses: ./.github/workflows/hello-charm-tests.yaml build-n-publish: - name: Build and Publish to Test PyPI + name: Build and Publish ops to Test PyPI runs-on: ubuntu-latest permissions: id-token: write diff --git a/HACKING.md b/HACKING.md index 0ba4ffb55..2873ede45 100644 --- a/HACKING.md +++ b/HACKING.md @@ -179,14 +179,17 @@ Pull requests should have a short title that follows the * revert * test -If the PR is limited to changes in ops.testing (Harness), also include the scope -`(harness)` in the title. At present, we do not add a scope in any other cases. +At present, we only add a scope in these cases: + +* If the PR is limited to changes in ops/_private/harness.py, also include the scope `(harness)` +* If the PR is limited to changes in testing/, also include the scope `(testing)` For example: * feat: add the ability to observe change-updated events * fix!: correct the type hinting for config data * docs(harness): clarify the types of exceptions that Harness.add_user_secret may raise +* ci(testing): adjust the workflow that publishes ops-scenario Note that the commit messages to the PR's branch do not need to follow the conventional commit format, as these will be squashed into a single commit to `main` @@ -202,10 +205,10 @@ The copyright information in existing files does not need to be updated when tho In general, new functionality should always be accompanied by user-focused documentation that is posted to -https://juju.is/docs/sdk. The content for this site is written and hosted on -https://discourse.charmhub.io/c/doc. New documentation should get a new +https://juju.is/docs/sdk. The content for this site is written and hosted on +https://discourse.charmhub.io/c/doc. New documentation should get a new topic/post on this Discourse forum and then should be linked into the main -docs navigation page(s) as appropriate. The ops library's SDK page +docs navigation page(s) as appropriate. The ops library's SDK page content is pulled from [the corresponding Discourse topic](https://discourse.charmhub.io/t/the-charmed-operator-software-development-kit-sdk-docs/4449). Each page on [juju.is](https://juju.is/docs/sdk) has a link at the bottom that @@ -221,13 +224,21 @@ Currently we don't publish separate versions of documentation for separate relea next to the relevant content (e.g. headings, etc.). The ops library's API reference is automatically built and published to -[ops.readthedocs.io](https://ops.readthedocs.io/en/latest/). Please be complete with +[ops.readthedocs.io](https://ops.readthedocs.io/en/latest/). Please be complete with docstrings and keep them informative for _users_. The published docs are always for the in-development (main branch) of ops, and do not include any notes -indicating changes or additions across versions - we encourage all charmers to +indicating changes or additions across ops versions - we encourage all charmers to promptly upgrade to the latest version of ops, and to refer to the release notes and changelog for learning about changes. +We do note when features behave differently when using different Juju versions. +Use the `.. jujuadded:: x.y` directive to indicate that the feature is only +available when using version x.y (or higher) of Juju, `..jujuchanged:: x.y` +when the feature's behaviour _in ops_ changes, and `..jujuremoved:: x.y` when +the feature will be available in ops but not in that version (or later) of Juju. +Unmarked features are assumed to work and be available in the current LTS +version of Juju. + During the release process, changes also get a new entry in [CHANGES.md](CHANGES.md). These are grouped into the same groupings as [commit messages](https://www.conventionalcommits.org/en/) @@ -307,47 +318,155 @@ the build frontend is [build](https://pypi.org/project/build/). # Publishing a Release -To make a release of the ops library, do the following: - -1. Visit the [releases page on GitHub](https://github.com/canonical/operator/releases). -2. Click "Draft a new release" -3. The "Release Title" is simply the full version number, in the form `..` - and a brief summary of the main changes in the release - E.g. 2.3.12 Bug fixes for the Juju foobar feature when using Python 3.12 -4. Use the "Generate Release Notes" button to get a copy of the changes into the - notes field. -5. Group the changes by the commit type (feat, fix, etc.) and use full names (e.g., "Features", - not "feat") for group headings. Strip the commit type prefix from the bullet point. Strip the - username (who did each commit) if the author is a member of the Charm Tech team. -6. Where appropriate, collapse multiple tightly related bullet points into a - single point that refers to multiple commits. -7. Create a new branch, and copy this text to the [CHANGES.md](CHANGES.md) file, - stripping out links, who did each commit, the new contributor list, and the - link to the full changelog. -8. Change [version.py](ops/version.py)'s `version` to the - [appropriate string](https://semver.org/). -9. Check if there's a `chore: update charm pins` auto-generated PR in the queue. If it looks - good, merge it and check that tests still pass. If needed, you can re-trigger the - `Update Charm Pins` workflow manually to ensure latest charms and ops get tested. -10. Add, commit, and push, and open a PR to get the changelog and version bump +To make a release of the `ops` and/or `ops-scenario` packages, do the following: + +1. Check if there's a `chore: update charm pins` auto-generated PR in the queue. + If it looks good, merge it and check that tests still pass. If needed, you + can re-trigger the `Update Charm Pins` workflow manually to ensure latest + charms and ops get tested. +2. Visit the [releases page on GitHub](https://github.com/canonical/operator/releases). +3. Click "Draft a new release" +4. The "Release Title" is the full version numbers of ops and/or ops-scenario, + in the form `ops .. and ops-scenario ..` + and a brief summary of the main changes in the release. + For example: `ops 2.3.12 Bug fixes for the Juju foobar feature when using Python 3.12` +5. If the last release was for both `ops` and `ops-scenario`, leave the previous + tag choice on `auto`. If the last release was for only one package, change + the previous tag to be the last time the same package(s) were being released. +6. Have the release create a new tag, in the form `..` for + `ops` and `scenario-..` for `ops-scenario`. If releasing + both packages, use the ops tag. +7. Use the "Generate Release Notes" button to get a copy of the changes into the + notes field. The 'Release Documentation' section below details the form that + the release notes and changelog should take. +8. For `ops`, change [version.py](ops/version.py)'s `version` to the + appropriate string. For `ops-scenario`, change the version in + [testing/pyproject.toml](testing/pyproject.toml). Both packages use + [semantic versioning]](https://semver.org/), and adjust independently + (that is: ops 2.18 doesn't imply ops-scenario 2.18, or any other number). +9. Add, commit, and push, and open a PR to get the changelogs and version bumps into main (and get it merged). -11. Back in the GitHub releases page, tweak the release notes - for example, - you might want to have a short paragraph at the intro on particularly - noteworthy changes. -12. Have someone else in the Charm-Tech team proofread the release notes. -13. When you are ready, click "Publish". (If you are not ready, click "Save as Draft".) +10. Save the release notes as a draft, and have someone else in the Charm-Tech + team proofread the release notes. +11. If the release includes both `ops` and `ops-scenario` packages, then push a + new tag in the form `scenario-..`. This is done by + executing `git tag scenario-x.y.z`, then `git push upstream tag scenario-x.y.z` locally + (assuming you have configured `canonical/operator` as a remote named + `upstream`). +12. When you are ready, click "Publish". GitHub will create the additional tag. + +Pushing the tags will trigger automatic builds for the Python packages and +publish them to PyPI ([ops](https://pypi.org/project/ops/) and +[ops-scenario](https://pypi.org/project/ops-scenario)) (authorisation is handled +via a [Trusted Publisher](https://docs.pypi.org/trusted-publishers/) relationship). +Note that it sometimes take a bit of time for the new releases to show up. + +See [.github/workflows/publish-ops.yaml](.github/workflows/publish-ops.yaml) and +[.github/workflows/publish-ops-scenario.yaml](.github/workflows/publish-ops-scenario.yaml) for details. +(Note that the versions in the YAML refer to versions of the GitHub actions, not the versions of the ops library.) -This will trigger an automatic build for the Python package and publish it to -[PyPI](https://pypi.org/project/ops/)) (authorisation is handled via a -[Trusted Publisher](https://docs.pypi.org/trusted-publishers/) relationship). -Note that it sometimes take a bit of time for the new release to show up. +You can troubleshoot errors on the [Actions Tab](https://github.com/canonical/operator/actions). -See [.github/workflows/publish.yml](.github/workflows/publish.yml) for details. (Note that the versions in publish.yml refer to versions of the GitHub actions, not the versions of the ops library.) +13. Announce the release on [Discourse](https://discourse.charmhub.io/c/framework/42) and [Matrix](https://matrix.to/#/#charmhub-charmdev:ubuntu.com). -You can troubleshoot errors on the [Actions Tab](https://github.com/canonical/operator/actions). +14. Open a PR to change the version strings to the expected + next version, with ".dev0" appended (for example, if 3.14.1 is the next + expected version, use `'3.14.1.dev0'`). + +## Release Documentation + +We produce several pieces of documentation for `ops` and `ops-scenario` +releases, each serving a separate purpose and covering a different level. + +Avoid using the word "Scenario", preferring "unit testing API" or "state +transition testing". Users should install `ops-scenario` with +`pip install ops[testing]` rather than using the `ops-scenario` package name +directly. + +### `git log` + +`git log` is used to see every change since a previous release. Obviously, no +special work needs to be done so that this is available. A link to the GitHub +view of the log will be included at the end of the GitHub release notes when +the "Generate Release Notes" button is used, in the form: + +``` +**Full Changelog**: https://github.com/canonical/operator/compare/2.17.0...2.18.0 +``` + +These changes include both `ops` and `ops-scenario`. If someone needs to see +changes only for one of the packages, then the `/testing/` folder can be +filtered in/out. + +### CHANGES.md + +A changelog is kept in version control that simply lists the changes in each +release, other than chores like bumping version numbers. The changelog for `ops` +is at the top level, in [CHANGES.md](CHANGES.md), and the changelog for +`ops-scenario` is in the `/testing` folder, [CHANGES.md](testing/CHANGES.md). +There will be overlap between the two files, as many PRs will include changes to +common infrastructure, or will adjust both `ops` and also the testing API in +`ops-scenario`. + +Adding the changes is done in preparation for a release. Use the "Generate +Release Notes" button in the GitHub releases page, and copy the text to the +CHANGES.md files. + +* Group the changes by the commit type (feat, fix, and so on) and use full names + ("Features", not "feat", "Fixes", not "fix") for group headings. +* Remove any bullets that do not apply to the package (`ops` only changes for + `ops-scenario`, and `ops-scenario` only changes for `ops`). +* Strip the commit type prefix from the bullet point, and capitalise the first + word. +* Strip the username (who did each commit) if the author is a member of the + Charm Tech team. +* Replace the link to the pull request with the PR number in parentheses. +* Where appropriate, collapse multiple tightly related bullet points into a + single point that refers to multiple commits. + +For example: the PR + +``` +* docs: clarify where StoredState is stored by @benhoyt in https://github.com/canonical/operator/pull/2006 +``` -13. Announce the release on [Discourse](https://discourse.charmhub.io/c/framework/42) and [Matrix](https://matrix.to/#/#charmhub-charmdev:ubuntu.com) +is added to the "Documentation" section as: + +``` +* Clarify where StoredState is stored (#2006) +``` + +### GitHub Release Notes + +The GitHub release notes include the list of changes found in the changelogs, +but: + +* If both `ops` and `ops-scenario` packages are being released, include all the + changes in the same set of release notes. If only one package is being + released, remove any bullets that apply only to the other package. +* The links to the PRs are left in full. +* Add a section above the list of changes that briefly outlines any key changes + in the release. + +### Discourse Release Announcement + +Post to the [framework category](https://discourse.charmhub.io/c/framework/42) +with a subject matching the GitHub release title. + +The post should resemble this: + +``` +The Charm Tech team has just released version x.y.z of ops! + +It’s available from PyPI by using `pip install ops`, and `pip install ops[testing]`, +which will pick up the latest version. Upgrade by running `pip install --upgrade ops`. + +The main improvements in this release are ... + +Read more in the [full release notes on GitHub](link to the GitHub release). +``` -14. Open a PR to change [version.py](ops/version.py)'s `version` to the expected - next version, with ".dev0" appended (for example, if 3.14.1 is the next expected version, use - `'3.14.1.dev0'`). +In the post, outline the key improvements both in `ops` and `ops-scenario` - +the point here is to encourage people to check out the full notes and to upgrade +promptly, so ensure that you entice them with the best that the new versions +have to offer. diff --git a/testing/CHANGES.md b/testing/CHANGES.md new file mode 100644 index 000000000..b60094e9a --- /dev/null +++ b/testing/CHANGES.md @@ -0,0 +1,43 @@ +# 7.0.5 - 20 Sep 2024 + +## Features + +* Use a slightly more strict type for `AnyJson` + +# 7.0.4 - 18 Sep 2024 + +## Chores + +* Add a `py.typed` file + +# 7.0.3 - 18 Sep 2024 + +## Fixes + +* `ops.Model.get_relation` should not raise when a relation with the specified ID does not exist + +# 7.0.2 - 13 Sep 2024 + +## Refactor + +* Adjustments to handle the upcoming release of ops 2.17 + +# 7.0.1 - 9 Sep 2024 + +## Fixes + +* Fix broken Python 3.8 compatibility. + +# 7.0.0 - 9 Sep 2024 + +## Features + +* Support for testing Pebble check events +* Container exec mocking can match against a command prefix +* Inspect a list of the commands that a charm has `exec`'d in a container +* Add consistency checks for `StoredState` +* Specifying your event is now done via `ctx.on` attributes +* The context manager is accessed via the `Context` object +* State collections are frozensets instead of lists +* Most classes now expect at least some arguments to be passed as keywords +* Secret tests are much simpler - particularly, revision numbers do not need to be managed