Skip to content

Commit d5ddd12

Browse files
committed
Made pypi upload simpler
1 parent f30375d commit d5ddd12

File tree

3 files changed

+73
-72
lines changed

3 files changed

+73
-72
lines changed

.github/workflows/cleanup_pypi.yml

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,32 @@
11
name: Cleanup PyPI
22
on:
33
workflow_call:
4+
inputs:
5+
environment:
6+
description: CI environment to run in (test.pypi or production.pypi)
7+
type: string
8+
required: true
49
workflow_dispatch:
510
inputs:
611
dry-run:
712
description: List packages that would be deleted but don't delete them
813
type: boolean
914
default: false
15+
environment:
16+
description: CI environment to run in
17+
type: choice
18+
required: true
19+
default: test.pypi
20+
options:
21+
- test.pypi
22+
- production.pypi
23+
1024
jobs:
1125
cleanup_pypi:
1226
name: Remove Nightlies from PyPI
1327
runs-on: ubuntu-latest
28+
environment:
29+
name: ${{ inputs.environment }}
1430
env:
1531
PYPI_CLEANUP_PASSWORD: ${{secrets.PYPI_CLEANUP_PASSWORD}}
1632
PYPI_CLEANUP_OTP: ${{secrets.PYPI_CLEANUP_OTP}}
@@ -19,6 +35,19 @@ jobs:
1935
with:
2036
fetch-depth: 0
2137

38+
- if: ${{ vars.PYPI_HOST == '' }}
39+
run: |
40+
echo "Error: PYPI_HOST is not set in CI environment '${{ inputs.environment }}'"
41+
exit 1
42+
- if: ${{ vars.PYPI_CLEANUP_USERNAME == '' }}
43+
run: |
44+
echo "Error: PYPI_CLEANUP_USERNAME is not set in CI environment '${{ inputs.environment }}'"
45+
exit 1
46+
- if: ${{ vars.PYPI_MAX_NIGHTLIES == '' }}
47+
run: |
48+
echo "Error: PYPI_MAX_NIGHTLIES is not set in CI environment '${{ inputs.environment }}'"
49+
exit 1
50+
2251
- name: Install Astral UV
2352
uses: astral-sh/setup-uv@v6
2453
with:
@@ -27,8 +56,8 @@ jobs:
2756
- name: Run Cleanup
2857
run: |
2958
set -x
30-
uv -v sync --only-group pypi --no-install-project
31-
uv -v run --no-sync -s scripts/pypi_cleanup.py ${{ inputs.dry-run && '--dry' || '' }} \
59+
uv sync --only-group pypi --no-install-project
60+
uv run --no-sync -s scripts/pypi_cleanup.py ${{ inputs.dry-run && '--dry' || '' }} \
3261
--index-hostname "${{ vars.PYPI_HOST }}" \
3362
--username "${{ vars.PYPI_CLEANUP_USERNAME }}" \
3463
--max-nightlies ${{ vars.PYPI_MAX_NIGHTLIES }}

.github/workflows/on_external_dispatch.yml

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ on:
1717
required: false
1818
publish-packages:
1919
type: boolean
20-
description: Publish to S3
20+
description: Upload packages to S3
2121
required: true
2222
default: false
2323

@@ -77,10 +77,12 @@ jobs:
7777
duckdb-git-ref: ${{ inputs.duckdb-sha }}
7878
force-version: ${{ inputs.force-version }}
7979

80-
publish_s3:
81-
name: Publish Artifacts to the S3 Staging Bucket
80+
upload_s3:
81+
name: Upload Artifacts to the S3 Staging Bucket
8282
runs-on: ubuntu-latest
83-
needs: [ externally_triggered_build ]
83+
needs: externally_triggered_build
84+
outputs:
85+
version: ${{ steps.s3_upload.outputs.version }}
8486
if: ${{ github.repository_owner == 'duckdb' && inputs.publish-packages }}
8587
steps:
8688
- name: Fetch artifacts
@@ -97,10 +99,18 @@ jobs:
9799
aws-access-key-id: ${{ secrets.S3_DUCKDB_STAGING_ID }}
98100
aws-secret-access-key: ${{ secrets.S3_DUCKDB_STAGING_KEY }}
99101

100-
- name: Upload artifacts to S3 bucket
101-
# semantics: if a version is forced then we upload into a folder by the version name, otherwise we upload
102-
# into a folder that is named <run id>-<run-attempt>. Only the latter will be discovered be
103-
# upload_to_pypi.yml.
102+
- name: Upload Artifacts
103+
id: s3_upload
104104
run: |
105-
FOLDER="${{ inputs.force-version != '' && inputs.force-version || format('{0}-{1}', github.run_id, github.run_attempt) }}"
106-
aws s3 cp artifacts s3://duckdb-staging/${{ github.repository }}/${FOLDER}/ --recursive
105+
version=$(basename artifacts/*.tar.gz | sed 's/duckdb-\(.*\).tar.gz/\1/g')
106+
aws s3 cp artifacts s3://duckdb-staging/${{ github.repository }}/${version}/ --recursive
107+
echo "version=${version}" >> $GITHUB_OUTPUT
108+
109+
publish_to_pypi:
110+
name: Upload Artifacts to PyPI
111+
needs: upload_s3
112+
if: ${{ force-version == '' }}
113+
uses: ./.github/workflows/upload_to_pypi.yml
114+
with:
115+
version: ${{ needs.upload_s3.outputs.version }}
116+
environment: pypi.production
Lines changed: 22 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,47 @@
11
name: Upload Artifacts to PyPI
22
on:
3-
# this workflow runs after the "External Dispatch" workflow is completed
4-
workflow_run:
5-
workflows: [ External Dispatch ]
6-
types: [ completed ]
7-
branches:
8-
- main
9-
- v*.*-*
3+
workflow_call:
4+
inputs:
5+
environment:
6+
description: CI environment to run in (test.pypi or production.pypi)
7+
type: string
8+
required: true
9+
version:
10+
description: The version to upload (must be present in the S3 staging bucket)
11+
type: string
12+
required: true
1013
workflow_dispatch:
1114
inputs:
1215
environment:
13-
description: Environment to run in ()
16+
description: CI environment to run in (test.pypi or production.pypi)
1417
type: choice
1518
required: true
1619
default: test.pypi
1720
options:
1821
- test.pypi
1922
- production.pypi
20-
artifact-folder:
21-
description: The S3 folder that contains the artifacts (s3://duckdb-staging/duckdb/duckdb-python/<artifact-folder>)
23+
version:
24+
description: The version to upload (must be present in the S3 staging bucket)
2225
type: string
2326
required: true
2427

25-
concurrency: ${{ inputs.artifact-folder || format('{0}-{1}', github.event.workflow_run.id, github.event.workflow_run.run_attempt) }}
28+
concurrency:
29+
group: ${{ inputs.version }}
30+
cancel-in-progress: true
2631

2732
jobs:
28-
prepare:
29-
name: Prepare and guard upload
30-
if: ${{ github.repository_owner == 'duckdb' && ( github.event.workflow_run.conclusion == 'success' || github.event_name != 'workflow_run' ) }}
31-
runs-on: ubuntu-latest
32-
outputs:
33-
s3_prefix: ${{ steps.get_s3_prefix.outputs.s3_prefix }}
34-
steps:
35-
- name: Determine S3 Prefix
36-
id: get_s3_prefix
37-
run: |
38-
artifact_folder="${{ inputs.artifact-folder || format('{0}-{1}', github.event.workflow_run.id, github.event.workflow_run.run_attempt) }}"
39-
if [[ -n "${artifact_folder}" ]]; then
40-
s3_prefix="${{ github.repository }}/${artifact_folder}"
41-
echo "Created S3 prefix: ${s3_prefix}"
42-
echo "s3_prefix=${s3_prefix}" >> $GITHUB_OUTPUT
43-
else
44-
echo "Can't determine S3 prefix for event: ${{ github.event_name }}. Quitting."
45-
exit 1
46-
fi
47-
48-
- name: Authenticate With AWS
49-
uses: aws-actions/configure-aws-credentials@v4
50-
with:
51-
aws-region: 'us-east-2'
52-
aws-access-key-id: ${{ secrets.S3_DUCKDB_STAGING_ID }}
53-
aws-secret-access-key: ${{ secrets.S3_DUCKDB_STAGING_KEY }}
54-
55-
- name: Check S3 Prefix
56-
shell: bash
57-
run: |
58-
if [[ $(aws s3api list-objects-v2 \
59-
--bucket duckdb-staging \
60-
--prefix "${{ steps.get_s3_prefix.outputs.s3_prefix }}/" \
61-
--max-items 1 \
62-
--query 'Contents[0].Key' \
63-
--output text) == "None" ]]; then
64-
echo "Prefix does not exist: ${{ steps.get_s3_prefix.outputs.s3_prefix }}"
65-
echo "${{ github.event_name == 'workflow_run' && 'Possibly built a stable release?' || 'Unexpected error' }}"
66-
exit 1
67-
fi
68-
6933
publish-pypi:
7034
name: Publish Artifacts to PyPI
71-
needs: [ prepare ]
7235
runs-on: ubuntu-latest
7336
environment:
74-
name: ${{ github.event_name == 'workflow_dispatch' && inputs.environment || 'test.pypi' }}
37+
name: ${{ inputs.environment }}
7538
permissions:
7639
# this is needed for the OIDC flow that is used with trusted publishing on PyPI
7740
id-token: write
7841
steps:
79-
- name: Fail if PYPI_HOST is not set
80-
if: ${{ vars.PYPI_HOST == '' }}
81-
shell: bash
42+
- if: ${{ vars.PYPI_HOST == '' }}
8243
run: |
83-
env_name="${{ github.event_name == 'workflow_dispatch' && inputs.environment || 'test.pypi' }}"
84-
echo "Error: vars.PYPI_HOST is not set in the resolved environment (${env_name})"
44+
echo "Error: PYPI_HOST is not set in CI environment '${{ inputs.environment }}'"
8545
exit 1
8646
8747
- name: Authenticate With AWS
@@ -93,7 +53,7 @@ jobs:
9353

9454
- name: Download Artifacts From S3
9555
env:
96-
S3_URL: 's3://duckdb-staging/${{ needs.prepare.outputs.s3_prefix }}/'
56+
S3_URL: 's3://duckdb-staging/${{ github.repository }}/${{ inputs.version }}/'
9757
AWS_ACCESS_KEY_ID: ${{ secrets.S3_DUCKDB_STAGING_ID }}
9858
AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_DUCKDB_STAGING_KEY }}
9959
run: |
@@ -110,3 +70,5 @@ jobs:
11070
name: Remove Nightlies from PyPI
11171
needs: publish-pypi
11272
uses: ./.github/workflows/cleanup_pypi.yml
73+
with:
74+
environment: ${{ inputs.environment }}

0 commit comments

Comments
 (0)