-
Notifications
You must be signed in to change notification settings - Fork 13
Add a separate action for removing old wheels #95
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 22 commits
d6264b8
9d4f667
002d2e5
40cd6bc
8a33f20
b18b4a1
deb3cba
05770f3
199b1c8
f8b96a0
70f8a3d
2fac172
6a1b65e
8bb7bb6
bae5ad6
f916adc
afedc16
35c9d59
7a7dbbc
7830eae
2f62d85
1f04be6
b6dbd44
88ce687
03979b0
bf8e993
433e7c3
4a3497d
71ede79
2d2e62a
556e9a6
e403c81
15b617e
288b2f2
9a447eb
6b0a6a1
3d1fb55
e8796a6
b58e1ad
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,7 +1,8 @@ | ||||||||||||||||||||||||||||||||||||||||||
| # Nightly upload | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| This is a GitHub Action that uploads nightly builds to the [scientific-python nightly channel][], | ||||||||||||||||||||||||||||||||||||||||||
| as recommended in [SPEC4 — Using and Creating Nightly Wheels][]. | ||||||||||||||||||||||||||||||||||||||||||
| This is a GitHub Action that uploads (and helps remove) nightly builds to | ||||||||||||||||||||||||||||||||||||||||||
| the [scientific-python nightly channel][], as recommended in | ||||||||||||||||||||||||||||||||||||||||||
| [SPEC4 — Using and Creating Nightly Wheels][]. | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| In a GitHub Actions workflow (`.github/workflows/*.yaml`), use the | ||||||||||||||||||||||||||||||||||||||||||
| following snippet on a Linux or macOS runner to upload built wheels to the | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -15,14 +16,46 @@ jobs: | |||||||||||||||||||||||||||||||||||||||||
| uses: scientific-python/upload-nightly-action@82396a2ed4269ba06c6b2988bb4fd568ef3c3d6b # 0.6.1 | ||||||||||||||||||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||||||||||||||||||
| artifacts_path: dist | ||||||||||||||||||||||||||||||||||||||||||
| anaconda_nightly_upload_token: ${{secrets.UPLOAD_TOKEN}} | ||||||||||||||||||||||||||||||||||||||||||
| anaconda_nightly_upload_token: ${{secrets.ANACONDA_TOKEN}} | ||||||||||||||||||||||||||||||||||||||||||
agriyakhetarpal marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| Note that we recommend pinning the action against a specific SHA | ||||||||||||||||||||||||||||||||||||||||||
| > [!IMPORTANT] | ||||||||||||||||||||||||||||||||||||||||||
| > Note that we recommend pinning the action against a specific SHA | ||||||||||||||||||||||||||||||||||||||||||
| (rather than a tag), to guard against the unlikely event of upstream | ||||||||||||||||||||||||||||||||||||||||||
| being compromised. | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| ## Updating the action | ||||||||||||||||||||||||||||||||||||||||||
| # Removing old nightly builds | ||||||||||||||||||||||||||||||||||||||||||
agriyakhetarpal marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| This repository also ships with an action to ease removals of older nightly wheels from a channel. | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| To use this functionality, add the following snippet to your workflow: | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| ```yml | ||||||||||||||||||||||||||||||||||||||||||
| jobs: | ||||||||||||||||||||||||||||||||||||||||||
| steps: | ||||||||||||||||||||||||||||||||||||||||||
| ... | ||||||||||||||||||||||||||||||||||||||||||
| - name: Remove old wheels | ||||||||||||||||||||||||||||||||||||||||||
| uses: scientific-python/upload-nightly-action/remove-wheels@cantknowhashyet # 0.6.0 | ||||||||||||||||||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||||||||||||||||||
| n_latest_uploads: ${{ env.N_LATEST_UPLOADS }} | ||||||||||||||||||||||||||||||||||||||||||
| anaconda_nightly_upload_organization: "your-organization" | ||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is called organization, not channel; are these interchangeable terms? If so, use channel; if not, explain the difference.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not quite sure, since they look like they have been used interchangeably. For example, the README outside of this PR: upload-nightly-action/README.md Lines 48 to 67 in 920fb59
mentions how one may upload to a different channel, but it uses "organization" as an input. |
||||||||||||||||||||||||||||||||||||||||||
| anaconda_nightly_token: ${{secrets.ANACONDA_TOKEN}} | ||||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| Which will remove all but the `n_latest_uploads` latest uploads from the channel. This is useful | ||||||||||||||||||||||||||||||||||||||||||
| to avoid hosting outdated development versions, as well as to clean up space. | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| Note that the ``scientific-python-nightly-wheels`` channel, specifically, already removes | ||||||||||||||||||||||||||||||||||||||||||
agriyakhetarpal marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||
| old artifacts daily. The `remove-wheels` action is, therefore, intended for use with | ||||||||||||||||||||||||||||||||||||||||||
| other channels. | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| If you do not wish to have this automated cleanup, please [open an issue](https://github.com/scientific-python/upload-nightly-action/) | ||||||||||||||||||||||||||||||||||||||||||
| to be added to the list of packages exempt from it. The current ones are named in | ||||||||||||||||||||||||||||||||||||||||||
| [`packages-ignore-from-cleanup.txt`](packages-ignore-from-cleanup.txt). | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| Please refer to the [artifact cleanup policy][] for more information. | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| ## Updating the actions | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| You can [use Dependabot to keep the GitHub Action up to date][], | ||||||||||||||||||||||||||||||||||||||||||
| with a `.github/dependabot.yml` config file similar to: | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -45,7 +78,7 @@ then generate a token at `https://anaconda.org/<anaconda cloud user name>/settin | |||||||||||||||||||||||||||||||||||||||||
| with permissions to _Allow write access to the API site_ and _Allow uploads to Standard Python repositories_, | ||||||||||||||||||||||||||||||||||||||||||
| and add the token as a secret to your GitHub repository. | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| ## Using a different channel | ||||||||||||||||||||||||||||||||||||||||||
| ## Using a channel other than ``scientific-python-nightly-wheels`` | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| This Github Action can upload your nightly builds to a different channel. To do so, | ||||||||||||||||||||||||||||||||||||||||||
| define the `anaconda_nightly_upload_organization` variable. Furthermore, | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -112,3 +145,4 @@ dependencies: | |||||||||||||||||||||||||||||||||||||||||
| [PyPI]: https://pypi.org/ | ||||||||||||||||||||||||||||||||||||||||||
| [scientific-python nightly channel]: https://anaconda.org/scientific-python-nightly-wheels | ||||||||||||||||||||||||||||||||||||||||||
| [SPEC4 — Using and Creating Nightly Wheels]: https://scientific-python.org/specs/spec-0004/ | ||||||||||||||||||||||||||||||||||||||||||
| [artifact cleanup policy]: #artifact-cleanup-policy-at-the-scientific-python-nightly-wheels-channel | ||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| name: Scientific Python / Remove Old Wheels | ||
| description: A GitHub Action to remove old wheels | ||
| permissions: | ||
| actions: read | ||
| contents: read | ||
| metadata: read | ||
| author: "Scientific-Python" | ||
| # TODO: have to think about versioning; whether to version separately, or | ||
| # for it to be in sync with the version for the upload action | ||
| version: "0.1.0" | ||
|
|
||
| inputs: | ||
| n_latest_uploads: | ||
| description: 'The number of previous wheel uploads to keep' | ||
| required: false | ||
| default: '5' | ||
| anaconda_nightly_upload_organization: | ||
| description: 'Anaconda Cloud organisation name to remove the wheels from' | ||
| required: false | ||
| default: scientific-python-nightly-wheels | ||
| anaconda_nightly_token: | ||
| description: 'Anaconda Cloud API token to authenticate with' | ||
| required: true | ||
|
|
||
| # TODO: Linux only for now, need to see how to add macOS support | ||
| runs: | ||
| using: "composite" | ||
| steps: | ||
| - name: Set up pixi | ||
| uses: prefix-dev/setup-pixi@ba3bb36eb2066252b2363392b7739741bb777659 # v0.8.1 | ||
| with: | ||
| locked: true | ||
| cache: true | ||
| cache-write: ${{ github.event_name == 'push' && github.ref_name == 'main' }} | ||
| # Avoid post cleanup errors if action run multiple times | ||
| post-cleanup: false | ||
|
|
||
| - name: Install dependencies | ||
| shell: bash | ||
| run: | | ||
| sudo apt-get update && sudo apt-get install -y curl jq | ||
agriyakhetarpal marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| - name: Remove old wheels | ||
| shell: bash | ||
| env: | ||
| INPUT_N_LATEST_UPLOADS: ${{ inputs.n_latest_uploads }} | ||
| INPUT_ANACONDA_USER: ${{ inputs.anaconda_user }} | ||
| INPUT_ANACONDA_TOKEN: ${{ inputs.anaconda_token }} | ||
| run: | | ||
| pixi run remove_old_wheels.sh | ||
agriyakhetarpal marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| #!/bin/bash | ||
|
|
||
| # fail on undefined variables | ||
| set -u | ||
| # Prevent pipe errors to be silenced | ||
| set -o pipefail | ||
| # Exit if any command exit as non-zero | ||
| set -e | ||
| # enable trace mode (print what it does) | ||
| set -x | ||
|
|
||
| # get the anaconda token from the github secrets | ||
| # | ||
| # this is to prevent accidental removals | ||
| echo "Getting anaconda token from github secrets..." | ||
|
|
||
| ANACONDA_USER="${INPUT_ANACONDA_USER}" | ||
| ANACONDA_TOKEN="${INPUT_ANACONDA_TOKEN}" | ||
| N_LATEST_UPLOADS="${INPUT_N_LATEST_UPLOADS}" | ||
|
|
||
|
Comment on lines
+17
to
+20
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure if the Anaconda token that is authorised to maintainers of, say, a package X is restricted to only remove wheels for said package X, or whether it can remove all packages in the index. This is because while users of SPNW won't be affected since we handle the deletions, users with their own organisation with wheels for packages X, Y, and Z being uploaded to it would either:
Which situation would be more plausible? If it is the latter, we'd have to provide another input in the action for a comma-separated string of packages to delete uploads for (and maybe P.S. This is all valid only if I'm not missing something about how the index and its permissions are structured :) Happy to receive others' thoughts!
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Based on @tupui's suggestion in #95 (comment), users will need to run the action multiple times to remove old uploads for multiple packages. Hence, the second option of allowing per-package deletions is better, and an input for a list of packages to delete wheels for isn't required.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. However, if someone has an appropriately scoped token for their organisation, they can very well remove multiple wheels from the index at a time, and some users might want to do that. So, it might be necessary to mention in the documentation how the tokens work and pre-emptively warn about possible deletions in an admonition.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To be clear, users have full control over their project. So they can add or remove any wheel they want on that project. People can just not add members to their project and they need to ask admins. |
||
|
|
||
| # if the ANACONDA_TOKEN is empty, exit with status -1 | ||
| # this is to prevent accidental removals | ||
| if [ -z "${ANACONDA_TOKEN}" ]; then | ||
| echo "ANACONDA_TOKEN is empty, exiting..." | ||
| exit -1 | ||
| fi | ||
|
|
||
| # if the N_LATEST_UPLOADS is empty, exit with status -1 | ||
| # as this should be set in by the user and it is better | ||
| # to fail on this to signal a problem. i.e., | ||
| # explicit is better than implicit. | ||
| if [ -z "${N_LATEST_UPLOADS}" ]; then | ||
| echo "N_LATEST_UPLOADS is empty, exiting..." | ||
| exit -1 | ||
| fi | ||
|
|
||
|
|
||
| # Query the package index for packages | ||
| # | ||
| # TODO: should be possible to alter this, since separating the workflow | ||
| # into two steps, one for uploading and one for cleanup, should make it | ||
| # possible for users to manually trigger the cleanup step before/after the | ||
| # upload step has completed in their own repos instead of us having to do it. | ||
| # | ||
| # TODO: raises questions on how to moderate cleanups among multiple users | ||
| # operating on the same channel, but that might be a different issue. | ||
| curl https://raw.githubusercontent.com/scientific-python/upload-nightly-action/main/packages-ignore-from-cleanup.txt --output packages-ignore-from-cleanup.txt | ||
|
Comment on lines
+41
to
+48
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please see above for context and my question on this. :)
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've put some thought into it, and this can be included as an input in the action. There would be two cases:
While the former would be recommended to limit deletion scopes, when the latter is used by the action users (as it is being done here for this repo), a Another reasonable message could be to add a warning: |
||
| anaconda show "${ANACONDA_USER}" &> >(grep "${ANACONDA_USER}/") | \ | ||
| awk '{print $1}' | \ | ||
| sed 's|.*/||g' | \ | ||
| grep -vf packages-ignore-from-cleanup.txt > package-names.txt | ||
|
|
||
| # Remove old uploads to save space | ||
| # Remove all _but_ the last ${N_LATEST_UPLOADS} package versions and | ||
| # remove all package versions older than 30 days. | ||
| if [ -s package-names.txt ]; then | ||
| threshold_date="$(date +%F -d '30 days ago')" | ||
|
|
||
| # Remember can't quote subshell as need to split on (space separated) token | ||
| for package_name in $(cat package-names.txt); do | ||
| # TODO: this outer loop can be removed when ready since there will be | ||
| # just one package to remove when the action is triggered manually from | ||
| # a user's (different) repo. | ||
|
|
||
| echo -e "\n# package: ${package_name}" | ||
|
Comment on lines
+60
to
+66
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This TODO item would also be resolved if we have a way forward with the questions above, since, if multiple packages are removed at once, this loop can stay; if we were to restrict the action to removing just one wheel at a time (which I don't think we should), then we could remove this. I think an action of the form: - name: Remove old wheels
uses: scientific-python/upload-nightly-action/remove-wheels@cantknowhashyet # 0.6.0
with:
n_latest_uploads: ${{ env.N_LATEST_UPLOADS }}
anaconda_nightly_upload_organization: "your-organization"
anaconda_nightly_token: ${{secrets.ANACONDA_TOKEN}}
packages_to_remove: "mypackage1,mypackage2,mypackage3" # or just "*"
# I could have suggested "all" here, but that breaks in the
# case where "all" is also the name of a package that has
# been uploaded (possible, albeit quite unlikely)is better than specifying the step and authenticating multiple times.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, a list of strings sounds elegant! :) |
||
|
|
||
| curl --silent https://api.anaconda.org/package/"${ANACONDA_USER}/${package_name}" | \ | ||
| jq -r '.releases[].version' > package-versions.txt | ||
| head --lines "-${N_LATEST_UPLOADS}" package-versions.txt > remove-package-versions.txt | ||
|
|
||
| for package_version in $(cat package-versions.txt); do | ||
| # c.f. https://github.com/Anaconda-Platform/anaconda-client/issues/682#issuecomment-1677283067 | ||
| upload_date=$(curl --silent https://api.anaconda.org/release/"${ANACONDA_USER}/${package_name}/${package_version}" | \ | ||
| jq -r '.distributions[].upload_time' | \ | ||
| sort | \ | ||
| tail --lines 1 | \ | ||
| awk '{print $1}') | ||
|
|
||
| # check upload_date is YYYY-MM-DD formatted | ||
| # c.f. https://github.com/scientific-python/upload-nightly-action/issues/73 | ||
| if [[ "${upload_date}" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then | ||
| if [[ "${upload_date}" < "${threshold_date}" ]]; then | ||
| echo "# ${ANACONDA_USER}/${package_name}/${package_version} last uploaded on ${upload_date}" | ||
| echo "${package_version}" >> remove-package-versions.txt | ||
| fi | ||
| else | ||
| echo "# ERROR: ${ANACONDA_USER}/${package_name}/${package_version} upload date ${upload_date} is not YYYY-MM-DD." | ||
| fi | ||
|
|
||
| done | ||
|
|
||
| if [ -s remove-package-versions.txt ]; then | ||
| # Guard against duplicate entries from packages over | ||
| # count and time thresholds | ||
| sort --output remove-package-versions.txt --unique remove-package-versions.txt | ||
|
|
||
| for package_version in $(cat remove-package-versions.txt); do | ||
| echo "# Removing ${ANACONDA_USER}/${package_name}/${package_version}" | ||
| anaconda --token "${ANACONDA_TOKEN}" remove \ | ||
| --force \ | ||
| "${ANACONDA_USER}/${package_name}/${package_version}" | ||
| done | ||
| fi | ||
|
|
||
| done | ||
| fi | ||
|
|
||
| echo "Finished removing old wheels except the last ${N_LATEST_UPLOADS} uploads from the ${ANACONDA_USER} channel." | ||
Uh oh!
There was an error while loading. Please reload this page.