diff --git a/.github/workflows/create-frontend-release-pr.yml b/.github/workflows/create-frontend-release-pr.yml deleted file mode 100644 index 7596edc73..000000000 --- a/.github/workflows/create-frontend-release-pr.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Create Frontend Release PR - -on: - workflow_dispatch: - push: - branches: - - main - paths: - - 'frontend/**' - -permissions: - contents: write - pull-requests: write - -jobs: - create-frontend-release: - concurrency: - group: create-frontend-release-${{ github.ref }} - cancel-in-progress: true - - name: generate frontend changelog - runs-on: ubuntu-latest - if: github.repository == 'chanzuckerberg/cryoet-data-portal' - steps: - - name: create changelog - uses: google-github-actions/release-please-action@v4 - id: release - with: - # TODO Configuring using manifest file as workaround until we move the frontend to its own repo - config-file: release-please.config.json - manifest-file: release-please.manifest.json - token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/py-build.yml b/.github/workflows/py-build.yml deleted file mode 100644 index 647b6816c..000000000 --- a/.github/workflows/py-build.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Python cryoet_data_portal build - -on: - pull_request: - paths: - - "client/python/**" - - ".github/workflows/**" # Re-run if a workflow is modified - useful to test workflow changes in PRs - push: - branches: [main] - workflow_dispatch: - -jobs: - build_python_wheels: - name: Build Python wheel and sdist - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - uses: actions/setup-python@v4 - with: - python-version: "3.10" - - - name: Install deps - run: | - python -m pip install -U pip setuptools build - - - name: Build - run: python -m build - working-directory: client/python/cryoet_data_portal/ - - - name: Upload build artifact - uses: actions/upload-artifact@v3 - with: - path: client/python/cryoet_data_portal/dist/* diff --git a/.github/workflows/release-check.yml b/.github/workflows/release-check.yml new file mode 100644 index 000000000..afd2b725d --- /dev/null +++ b/.github/workflows/release-check.yml @@ -0,0 +1,34 @@ +# write a Github actions to verify the workflow "Python Client Tests" on the main branch ran successfully. +name: Client Release Check +on: + pull_request: + branches: + - release-please--branches--tsmith/automate-release--components--cryoet-data-portal-python-client + - tsmith/automate-release + push: + branches: + - release-please--branches--tsmith/automate-release--components--cryoet-data-portal-python-client + - tsmith/automate-release + +jobs: + release-check: + runs-on: ubuntu-latest + steps: + - name: check client tests on main + uses: actions/github-script@v7 + with: + retries: 10 + script: | + const { data } = await github.rest.checks.listForRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: 'main', + check_name: 'Python Client Tests', + filter: 'latest', + }); + if (data.total_count === 0) { + core.setFailed('No checks found on main'); + } + if (data.check_runs[0].conclusion !== 'success') { + core.setFailed('Python Client Tests on main did not pass'); + } diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..90e18a976 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,62 @@ +name: Create Release PRs + +on: + workflow_dispatch: + push: + branches: + - main + - tsmith/automate-release + +permissions: + contents: write + pull-requests: write + +jobs: + release-please: + concurrency: + group: release-prs-${{ github.ref }} + cancel-in-progress: true + + runs-on: ubuntu-latest + steps: + - name: release please + uses: googleapis/release-please-action@v4 + id: release + with: + # TODO Configuring using manifest file as workaround until we move the frontend to its own repo + manifest-file: "release-please.manifest.json" + config-file: "release-please.config.json" + target-branch: "tsmith/automate-release" + + outputs: + paths_released: ${{ steps.release.outputs.paths_released }} + + publish-pypi-package: + name: Build and publish Python package to PyPI + runs-on: ubuntu-latest + needs: release-please + if: contains(needs.release-please.outputs.paths_released, 'client/python/cryoet_data_portal') + environment: + name: pypi + url: https://pypi.org/p/cryoet-data-portal + permissions: + id-token: write # IMPORTANT: this permission is mandatory for trusted publishing + steps: + - name: Checkout ref branch + uses: actions/checkout@v4 + with: + ref: ${{ github.ref }} + fetch-depth: 0 + + - uses: actions/setup-python@v4 + with: + pyton-version: "3.10" + + - name: build + run: | + make build -C client/python/cryoet_data_portal + + - name: Publish distribution ๐Ÿ“ฆ to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + packages-dir: client/python/cryoet_data_portal/dist diff --git a/client/python/cryoet_data_portal/CHANGELOG.md b/client/python/cryoet_data_portal/CHANGELOG.md new file mode 100644 index 000000000..d569767d9 --- /dev/null +++ b/client/python/cryoet_data_portal/CHANGELOG.md @@ -0,0 +1,22 @@ +# Changelog + +## [3.1.0](https://github.com/chanzuckerberg/cryoet-data-portal/compare/cryoet-data-portal-python-client-v3.0.3...cryoet-data-portal-python-client-v3.1.0) (2024-08-20) + + +### โœจ Features + +* add user agent to client requests ([#966](https://github.com/chanzuckerberg/cryoet-data-portal/issues/966)) ([8209cd4](https://github.com/chanzuckerberg/cryoet-data-portal/commit/8209cd46cb8ab21341c7ee94672db3bae78f9aa2)) + + +### ๐Ÿž Bug Fixes + +* add link to documentation to pypi ([38c76b4](https://github.com/chanzuckerberg/cryoet-data-portal/commit/38c76b450294d35a276d102ed816b12dd810ee99)) +* create recursive_from_prefix path if it does not exist ([#940](https://github.com/chanzuckerberg/cryoet-data-portal/issues/940)) ([0069f08](https://github.com/chanzuckerberg/cryoet-data-portal/commit/0069f080987ac05efef82d024cb17f4dc307a0f3)) +* Use match with substring for exception check in client tests ([#895](https://github.com/chanzuckerberg/cryoet-data-portal/issues/895)) ([07352ec](https://github.com/chanzuckerberg/cryoet-data-portal/commit/07352ecdb8c6f50ffe97ff7be9777c0cf6dd66cb)) + + +### ๐Ÿงน Miscellaneous Chores + +* Add additional test case to TestGetDestinationPath ([#955](https://github.com/chanzuckerberg/cryoet-data-portal/issues/955)) ([a9412a8](https://github.com/chanzuckerberg/cryoet-data-portal/commit/a9412a80f3b24ff94b0803fdd59d3583b4521706)) +* automate pypi release ([508516b](https://github.com/chanzuckerberg/cryoet-data-portal/commit/508516b956b14d6aa18c8f39b5c28938f0b49220)) +* prepare client for release automation ([d7e5446](https://github.com/chanzuckerberg/cryoet-data-portal/commit/d7e5446519aebfbc180eaef98a11ce65ec7d49dc)) diff --git a/client/python/cryoet_data_portal/Makefile b/client/python/cryoet_data_portal/Makefile index d55a39414..67e74a946 100644 --- a/client/python/cryoet_data_portal/Makefile +++ b/client/python/cryoet_data_portal/Makefile @@ -20,3 +20,9 @@ test: export BOTO_ENDPOINT_URL=http://localhost:4000; \ export BOTO_SIGNATURE_VERSION=s3v4; \ pytest -vvv -s . $(TEST) + +.PHONY: build +build: + python -m pip install --upgrade pip + pip install build twine + python -m build diff --git a/client/python/cryoet_data_portal/pyproject.toml b/client/python/cryoet_data_portal/pyproject.toml index 6e95d01b6..0d000ada7 100644 --- a/client/python/cryoet_data_portal/pyproject.toml +++ b/client/python/cryoet_data_portal/pyproject.toml @@ -1,10 +1,10 @@ [build-system] -requires = ["setuptools>=45", "setuptools_scm[toml]>=6.2"] +requires = ["setuptools>=64"] build-backend = "setuptools.build_meta" [project] name = "cryoet_data_portal" -dynamic = ["version"] +version = "3.1.0" description = "API Client to facilitate the use of the CryoET Portal. For more information about the API and the project visit https://github.com/chanzuckerberg/cryoet-data-portal/" authors = [ { name = "Chan Zuckerberg Initiative", email = "cryoetdataportal@chanzuckerberg.com" } @@ -38,6 +38,7 @@ dependencies= [ [project.urls] homepage = "https://github.com/chanzuckerberg/cryoet-data-portal" repository = "https://github.com/chanzuckerberg/cryoet-data-portal" +documentation = "https://chanzuckerberg.github.io/cryoet-data-portal/python-api.html" [tool.setuptools.packages.find] where = ["src"] diff --git a/client/python/cryoet_data_portal/release_process.md b/client/python/cryoet_data_portal/release_process.md index 37b26ad06..ca88fe14b 100644 --- a/client/python/cryoet_data_portal/release_process.md +++ b/client/python/cryoet_data_portal/release_process.md @@ -1,105 +1,10 @@ # Python package release process +To release a new version of python client, merge a pull request to main with change to **./client/python/cryoet-data-portal**. Release-please will automatically create a PR with the a version bump if needed. The name of the PR will be **chore(main): release cryoet-data-portal-python-client vX.X.X**. Here is an [example](https://github.com/chanzuckerberg/cryoet-data-portal/pull/981) of a release PR. Merging the release-please PR will create a new release in github and a new version will be uploaded to pypi. If no changes were made that affect the python client, the python client will not be release. Closing the PR will skip the release of these changes and they will be included in the next release. -The following approach is used to manage releases of the Python package: +If the version number needs to be change see [release-plesae documentation](https://github.com/googleapis/release-please?tab=readme-ov-file#how-do-i-change-the-version-number) for how to change the version number. This same process can be used to manually release the package. -1. The package is automatically built (sdist and wheels) in a GitHub action, and build artifacts are uploaded to GitHub. -2. Release candidate testing is done by installing built assets from Github. -3. Build versions are managed via [`setuptools_scm`](https://github.com/pypa/setuptools_scm) and the version is automatically determined from git tags. -4. Releases are created and managed via GitHub Releases, leaving a tag in place from which future branches (eg, emergency fixes) can be created. -5. Built packages are published to PyPi _from_ the GitHub assets, i.e. are never hand-created, to reduce errors. - -## Prerequisites - -While not strictly required, this process assumes you have met the following prerequisites: - -- You have write access to the `chanzuckerberg/cryoet-data-portal` repo -- You have an account on pypi.org and test.pypi.org, both with access to the cryoet-data-portal project -- You have the Github CLI tool (`gh`) installed. See [documentation](https://cli.github.com/). -- You have the `pipx` CLI tool installed. See [documentation](https://pypa.github.io/pipx/). - -## Step 1: Building the package assets - -A build will occur automatically upon each commit to main, upon each commit to a PR, or when the build workflow is manually invoked. The build workflow is defined by the GH `py-build.yml` workflow. Build artifacts are the Python setuptools-created `sdist` and `wheel`, and are retained for a limited period of time (currently the GH default of 90 days). - -Unless you are revising and testing the build process itself, there is no need to manually perform a build. - -## Step 2: Release candidate testing - -Any pre-built asset on Github can be installed and tested from the Github URL. For example: - -1. Identify the GH workflow run ID that contains the asset you wish to test. A simple way to do this is: - ```shell - $ gh run list - ``` - Alternatively, you can use the "Actions" tag in the GitHub web UI. -2. Download the build artifact.zip from GitHub, using the GH Action run ID associated with the `build` action for your commit OR utilizing the web UI: - - ```shell - $ gh run download - ``` - - If you download using the browser, unzip into a temp directory, e.g., - - ```shell - $ unzip artifact.zip -d ./artifact/ - Archive: artifact.zip - inflating: ./artifact/cryoet-data-portal-0.0.1.dev0-py3-none-any.whl - inflating: ./artifact/cryoet-data-portal-0.0.1.dev0.tar.gz - ``` - -3. Install and test the downloaded build, e.g., - ```shell - $ pip uninstall cryoet-data-portal - $ pip install ./artifact/cryoet-data-portal-*-any.whl - ``` - -To test a release candidate: - -1. Identify the build you wish to test. Download and test the artifact built for that commit as described above. -2. Perform end-user testing, using the above installation method -3. If acceptable, proceed to Step 3 - create a release. - -If testing exposes problems, fix and commit a solution as you would any other change. - -## Step 3: Create a release - -Prior to this process, determine the correct semver version number for the new release. Please consider if this is a major, minor or patch release, and if it should have a tag (e.g., an alpha build, with a `a#` suffix or a pre-release candidate, with a `-rc` suffix). If you are not sure, please review [PEP 440](https://peps.python.org/pep-0440/) for more information. - -This process also assumes you are releasing from `main`. If you create a release PR, it should be merged to main before releasing. - -To create a release, perform the following: - -1. Identify both the (tested & validated) commit and semver for the release. -2. Tag the commit with the release version (_including_ a `v` prefix) and push the tag to origin. **Important**: use an annotated tag, e.g., `git tag -a v1.9.4 -m 'Release 1.9.4`. For example (please replace with your version, _including_ a `v`, e.g. `v1.9.4`: - ```shell - $ git tag -a -m 'Release ' - $ git push origin - ``` -3. Trigger a build for this tag by manually triggering the `py-build.yml` workflow. For example: - ```shell - $ gh workflow run py-build.yml --ref - ``` -4. When the workflow completes, make note of the run ID (e.g., using `gh run list`). -5. Optional, _but recommended_: download the asset from the build workflow and validate it. -6. Create and publish a GitHub Release, using the `` tag above (e.g., `v1.9.4`). It is recommended that you include a release summary and change log in the GH release. - -## Step 4: Publish assets to PyPi - -To publish built release assets to PyPi (_note_: this will require your pypi/testpypi login): - -1. Download the assets built for your release commit, using the same method as step 2 above, e.g., - ```shell - $ gh run download - ``` -2. Optional: upload to TestPyPi (this assumes the downloaded assets are in ./artifact/). - - ```shell - pipx run twine upload --repository testpypi ./artifact/* - ``` - - Following the upload, confirm correct presentation on the project page and ability to download install from TestPyPi. To test with TestPyPi, use `pipx run twine upload --repository testpypi ./artifact/*`. You can find more information [here](https://packaging.python.org/en/latest/guides/using-testpypi/). - -3. Use twine to upload to PyPi (this assumes the downloaded assets are in ./artifact/), e.g., - ```shell - pipx run twine upload ./artifact/* - ``` +## Build Locally +The python client can be build locally by running the following: +```bash +make build +``` diff --git a/client/python/cryoet_data_portal/src/cryoet_data_portal/__init__.py b/client/python/cryoet_data_portal/src/cryoet_data_portal/__init__.py index 67da96f9d..8abd2bf4f 100644 --- a/client/python/cryoet_data_portal/src/cryoet_data_portal/__init__.py +++ b/client/python/cryoet_data_portal/src/cryoet_data_portal/__init__.py @@ -18,9 +18,8 @@ TomogramAuthor, TomogramVoxelSpacing, ) -from ._version import version -__version__ = version +__version__ = "3.1.0" __all__ = [ "Client", diff --git a/release-please.config.json b/release-please.config.json index 9fd94f2cf..7d32d1431 100644 --- a/release-please.config.json +++ b/release-please.config.json @@ -1,6 +1,4 @@ { - "bootstrap-sha": "79825da91dd20c3be2f47912285e2c977848af32", - "skip-github-release": true, "changelog-sections": [ { "type": "feat", "section": "โœจ Features" }, { "type": "fix", "section": "๐Ÿž Bug Fixes" }, @@ -16,7 +14,14 @@ ], "packages": { "frontend": { - "release-type": "node" + "release-type": "node", + "skip-github-release": true, + "separate-pull-requests": true + }, + "client/python/cryoet_data_portal": { + "package-name": "cryoet-data-portal-python-client", + "release-type": "python", + "separate-pull-requests": true } } } diff --git a/release-please.manifest.json b/release-please.manifest.json index 1905c0725..826b45d1d 100644 --- a/release-please.manifest.json +++ b/release-please.manifest.json @@ -1,3 +1,4 @@ { - "frontend": "1.22.0" + "frontend": "1.22.0", + "client/python/cryoet_data_portal": "3.1.0" }