2222 options :
2323 - test
2424 - prod
25+ nightly-stale-after-days :
26+ type : string
27+ description : After how many days should nightlies be considered stale
28+ required : true
29+ default : 4
2530 store-s3 :
2631 type : boolean
2732 description : Also store test packages in S3 (always true for prod)
@@ -51,23 +56,32 @@ jobs:
5156 runs-on : ubuntu-latest
5257 steps :
5358 - id : index_check
54- name : Check ${{ needs.build_sdist.outputs.package- version }} on PyPI
59+ name : Check version on PyPI
5560 run : |
56- set -eu
57- # Check PyPI whether the release we're building is already present
61+ set -ex
5862 pypi_hostname=${{ inputs.pypi-index == 'test' && 'test.' || '' }}pypi.org
59- pkg_version=${{ needs.build_sdist.outputs.package-version }}
60- url=https://${pypi_hostname}/pypi/duckdb/${pkg_version}/json
61- http_status=$( curl -s -o /dev/null -w "%{http_code}" $url || echo $? )
62- if [[ $http_status == "200" ]]; then
63- echo "::warning::Package version ${pkg_version} is already present on ${pypi_hostname}"
64- pypi_state=VERSION_FOUND
65- elif [[ $http_status == 000* ]]; then
66- echo "::error::Error checking PyPI at ${url}: curl exit code ${http_status#'000'}"
67- pypi_state=UNKNOWN
68- else
69- echo "::notice::Package version ${pkg_version} not found on ${pypi_hostname} (http status: ${http_status})"
63+ # install duckdb
64+ curl https://install.duckdb.org | sh
65+ # query pypi
66+ result=$(cat <<EOF | ${HOME}/.duckdb/cli/latest/duckdb | xargs
67+ ---- Output lines
68+ .mode line
69+ ---- Query that fetches the given version's age, if the version already exists
70+ SELECT
71+ today() - (file.value->>'upload_time_iso_8601')::DATE AS age,
72+ FROM read_json('https://${pypi_hostname}/pypi/duckdb/json') AS jd
73+ CROSS JOIN json_each(jd.releases) AS rel(key, value)
74+ CROSS JOIN unnest(FROM_JSON(rel.value, '["JSON"]')) AS file(value)
75+ WHERE rel.key='${{ inputs.version }}'
76+ LIMIT 1;
77+ EOF
78+ )
79+ if [ -z "$result" ]; then
7080 pypi_state=VERSION_NOT_FOUND
81+ elif [ "${result#age = }" -ge "${{ inputs.nightly-stale-after-days }}" ]; then
82+ pypi_state=VERSION_STALE
83+ else
84+ pypi_state=VERSION_FOUND
7185 fi
7286 echo "pypi_state=${pypi_state}" >> $GITHUB_OUTPUT
7387
96110 echo "::notice::S3 upload disabled in inputs, not generating S3 URL"
97111 exit 0
98112 fi
99- if [[ VERSION_FOUND = = "${{ steps.index_check.outputs.pypi_state }}" ]]; then
113+ if [[ VERSION_NOT_FOUND ! = "${{ steps.index_check.outputs.pypi_state }}" ]]; then
100114 echo "::warning::S3 upload disabled because package version already uploaded to PyPI"
101115 exit 0
102116 fi
@@ -107,10 +121,37 @@ jobs:
107121 echo "::notice::Generated S3 URL: ${s3_url}"
108122 echo "s3_url=${s3_url}" >> $GITHUB_OUTPUT
109123
124+ bump_submodule :
125+ name : Bump submodule to given SHA if version on PyPI is stale
126+ needs : workflow_state
127+ if : ${{ needs.workflow_state.outputs.pypi_state == 'VERSION_STALE' }}
128+ runs-on : ubuntu-latest
129+ steps :
130+ - name : Checkout DuckDB Python
131+ uses : actions/checkout@v4
132+ with :
133+ ref : ${{ inputs.duckdb-python-sha }}
134+ fetch-depth : 0
135+ submodules : true
136+
137+ - name : Create PR to bump DuckDB submodule
138+ shell : bash
139+ if : ${{ inputs.duckdb-sha }}
140+ run : |
141+ cd external/duckdb
142+ git fetch origin
143+ git checkout ${{ inputs.duckdb-sha }}
144+ cd ../../
145+ git add external/duckdb
146+ git commit -m "Bump submodule"
147+ run_url=https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
148+ msg="Created from ${run_url}. Version on PyPI is older than ${{ inputs.nightly-stale-after-days }} days."
149+ gh pr create -B ${{ github.ref_name }} --title "Bump submodule" --body "${msg}"
150+
110151 build_wheels :
111152 name : Build and test releases
112153 needs : workflow_state
113- if : ${{ needs.workflow_state.outputs.pypi_state != 'VERSION_FOUND ' }}
154+ if : ${{ needs.workflow_state.outputs.pypi_state == 'VERSION_NOT_FOUND ' }}
114155 uses : ./.github/workflows/packaging_wheels.yml
115156 with :
116157 minimal : false
0 commit comments