feat(cicd): build, test, and release github actions#5
feat(cicd): build, test, and release github actions#5gforsyth merged 6 commits intosubstrait-io:mainfrom
Conversation
|
This is my first time implementing GH actions, so please review carefully! |
gforsyth
left a comment
There was a problem hiding this comment.
Hey @danepitkin -- this looks good to me overall, and I think the logic is sound.
What do you think about splitting out the release and publish workflows into a separate file? It's still possible to use needs across files and it can avoid certain footguns.
I would also prefer this approach, but it started to get a bit hairy w/o myself having a good understanding of how to test these actions. AFAIK I can give it a shot! I tried using https://github.com/nektos/act for testing locally, but ran into issues trying to get the [1] https://stackoverflow.com/questions/72869054/github-action-add-needs-with-separate-workflow-file |
|
I could also just switch to a |
|
I would also assume that we would have a separate CI workflow for just the tests (that doesn't necessarily creates wheels first), and which would test on different OS, python versions, possibly versions of dependencies, etc And in that case, I think keeping a simple test step (essentially to verify that the build artifacts are OK) in the build and release workflow might be OK without splitting it. |
|
Yes, so essentially what you also said in your last comment |
| - name: Install package dependencies | ||
| run: | | ||
| python -m pip install --upgrade pip | ||
| pip install ".[test]" |
There was a problem hiding this comment.
If we keep a test step for this "release" workflow, we probably want to explicitly install one of the built packages (in /dist)
There was a problem hiding this comment.
I updated the new test.yml to install from dist/. It runs on push as well, but its not gating. Probably good to rely on tests to pass before running the release workflow?
| @@ -0,0 +1,34 @@ | |||
| name: Build | |||
There was a problem hiding this comment.
The Build workflow will always run on PRs, main branch, and version tags. It will upload the build artifact that can be accessed by downstream workflows with 1 day retention (default is 30 days).
| @@ -0,0 +1,55 @@ | |||
| name: Release | |||
There was a problem hiding this comment.
The Release workflow will run when the Test workflow completes AND is successful AND we are on the main branch. If the git ref is a git tag, create a Github release AND upload package to PyPi. If the git ref is the main branch, upload to Test PyPi.
assignUser
left a comment
There was a problem hiding this comment.
Very nice to see such an automatic release process :D
One major security issue on the test workflow and some additional comments.
The only way to really test these is to merge them to main on your fork and test them there (the pypi steps would fail but as you are relying on an action there it should be fine to assume they will work given the right creds)
|
|
||
| on: | ||
| workflow_run: | ||
| workflows: [build] |
There was a problem hiding this comment.
I was unable to confirm in the docs but iirc this need to be either the full name: value or the yaml file name including extension,
| - name: Download artifact | ||
| uses: actions/download-artifact@v3 | ||
| with: | ||
| name: dist | ||
| path: dist/ |
There was a problem hiding this comment.
This will not work as the artifact actions can only be used within the same run but workflow_run is disjunct from the triggering run. You have to use the api to get the artifact (see docs)
There was a problem hiding this comment.
Thank you! I had assumed the github action would have used the API under the hood, but I hadn't actually managed to test it yet.
| on: | ||
| workflow_run: | ||
| workflows: [build] | ||
| types: [completed] |
There was a problem hiding this comment.
This is actually a security risk as the workflow_run trigger starts a run with elevated permissions, which means that running any kind of untrusted code (e.g. from an outside PR).
The default permissions on the GITHUB_TOKEN are rw on almost everything, so even with no secrets exposed in the env a lot of damage could be done.
There was a problem hiding this comment.
My recommendation would be to move this into the build workflow as a secondary job with needs: as I don't really see a need to keep this in a separate file.
There was a problem hiding this comment.
Or just have this test workflow not rely on the build workflow. I think we can install the package from source here in one of the first steps (that essentially do that same, but without the need to upload/download artifacts, and this is a very fast step anyway for a small pure python package like we have)
There was a problem hiding this comment.
I'll remove all workflow_run usage. They need a big red security warning in the docs to it's more obvious!
| pull_request: | ||
| push: | ||
| branches: [ main ] | ||
| tags: [ 'v*.*.*' ] |
There was a problem hiding this comment.
Just to confirm that this is what you want: this will trigger the workflow off of any 'v*' tag on any branch (which would make sense if maintenance branches are planned).
There was a problem hiding this comment.
Good catch! I might update to a more strict regex.
| release: | ||
| name: Release | ||
| runs-on: ubuntu-latest | ||
| if: ${{ github.event.workflow_run.conclusion == 'success' && startsWith(github.ref, 'refs/tags/') }} |
There was a problem hiding this comment.
| if: ${{ github.event.workflow_run.conclusion == 'success' && startsWith(github.ref, 'refs/tags/') }} | |
| if: ${{ github.event.workflow_run.conclusion == 'success' && startsWith(github.ref, 'refs/tags/v') }} |
Maybe better to be specific (probably a bit paranoid 🤷 :D)
| - name: Publish package to PyPI | ||
| uses: pypa/gh-action-pypi-publish@release/v1 | ||
| with: | ||
| password: ${{ secrets.PYPI_API_TOKEN }} |
There was a problem hiding this comment.
Oh nice pypi supports OICD which might be worth a look: https://docs.pypi.org/trusted-publishers/
| - name: Set up Python | ||
| uses: actions/setup-python@v4 | ||
| with: | ||
| python-version: "3.x" |
There was a problem hiding this comment.
Would it not make sense to build on the lowest supported version?
There was a problem hiding this comment.
For our use case, that shouldn't matter
There was a problem hiding this comment.
3.x was also recommended by the official docs, which is why I chose it.
| python -m pip install build --user | ||
| - name: Build package | ||
| run: | | ||
| python -m build |
There was a problem hiding this comment.
IIUC this is a pure python project at the moment and will build a none-any wheel? Otherwise cibuildwheel would be my goto :D
There was a problem hiding this comment.
Yes, this will build a single pure python wheel, so cibuildwheel would be overkill at the moment
| - name: Install wheel and test dependencies | ||
| run: | | ||
| python -m pip install --upgrade pip | ||
| find ./dist/*.whl | xargs python -m pip install |
There was a problem hiding this comment.
| find ./dist/*.whl | xargs python -m pip install | |
| python -m pip install . |
Install from source (which will also go through building a wheel, but just on the fly without having to download the pre-built wheel)
|
I've updated the code to remove |
assignUser
left a comment
There was a problem hiding this comment.
LGTM now. A minor change that would mostly be for comparability future changes to the default token permissions would be to explicitly set the minimum required permissions: https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs
gforsyth
left a comment
There was a problem hiding this comment.
Thanks for putting this together @danepitkin !
And thanks for the security check @assignUser !
@jorisvandenbossche -- do you want to take another look over this? If not, I'll merge this in later today.
|
Thanks all for the review! @gforsyth any recommendations on how to get PyPI secrets configured? Are there substrait-owned secrets we can reuse? |
|
We can ask @cpcloud to add them to the repository once we've created them. I usually create a package on PyPI with a (near) empty I don't think there will be existing substrait secrets because there aren't any Python offerings (yet!) |
|
Ok, I've created https://pypi.org/project/substrait/ and invited @jacques-n and @cpcloud as owners of the package. @westonpace -- I couldn't find a PyPI username for you, if you have one, let me know and I'm happy to add you. The OIDC setup looks pretty straightforward, but will require someone who has access to the repository settings (and that's not me). Similarly, using a project-scoped API token (which I can create), will also require someone on the PMC to add to the repo secrets. |
I don't have one. However, if me having one would help in any way then please don't hesitate to reach out and I'll add one. |
|
closes #3 |
All good! I just didn't want to miss you if you did have one. |
|
Do we think its OK merging now, or should we wait for the secrets to be added to the repo? |
| name: Publish to Test PyPI | ||
| runs-on: ubuntu-latest | ||
| needs: build | ||
| if: github.ref == 'refs/heads/main' |
There was a problem hiding this comment.
Do we want to push to the test pypi server for every commit on main?
There was a problem hiding this comment.
probably not -- but we can leave it in place for the initial commit (after credentials are setup) to make sure everything is working, then strip it out
There was a problem hiding this comment.
Could be triggered manually via workflow_dispatch if that would be useful?
There was a problem hiding this comment.
This workflow was recommended by the Python packaging docs[1] themselves:
Now, whenever you push a tagged commit to your Git repository remote on GitHub, this workflow will publish it to PyPI. And it’ll publish any push to TestPyPI which is useful for providing test builds to your alpha users as well as making sure that your release pipeline remains healthy!
I think we wait, because we'll want to make sure the credentials are working without a bunch of extraneous commits (there will almost certainly be some). |
|
I've created the tokens and added them to the repository secrets -- going to merge this and see what happens. |
|
Looks like the release failed because of some unexpected yaml: https://github.com/substrait-io/substrait-python/actions/runs/5123352674/workflow |
Looking into it now! I must have misread the documentation on how this is configured.. |
|
Created #8 |
|
#8 Fixed the yaml issue and the release pipeline ran. We're getting close! Now it's that the versioneer style version isn't compliant: |
|
Added #9 |
Requires GH secrets to be added for
PYPI_API_TOKENandTEST_PYPI_API_TOKEN.