-
Notifications
You must be signed in to change notification settings - Fork 79
211 lines (197 loc) · 8.24 KB
/
release.yml
File metadata and controls
211 lines (197 loc) · 8.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# Release is called by duckdb's InvokeCI -> NotifyExternalRepositories job
name: Release
on:
workflow_dispatch:
inputs:
duckdb-python-sha:
type: string
description: The commit to build against (defaults to latest commit of current ref)
required: false
duckdb-sha:
type: string
description: The DuckDB submodule commit or ref to build against
required: true
stable-version:
type: string
description: Release a stable version (vX.Y.Z-((rc|post)N))
required: false
pypi-index:
type: choice
description: Which PyPI to use
required: true
options:
- test
- prod
store-s3:
type: boolean
description: Also store test packages in S3 (always true for prod)
default: false
defaults:
run:
shell: bash
jobs:
build_sdist:
name: Build an sdist and determine versions
uses: ./.github/workflows/packaging_sdist.yml
with:
testsuite: all
duckdb-python-sha: ${{ inputs.duckdb-python-sha != '' && inputs.duckdb-python-sha || github.sha }}
duckdb-sha: ${{ inputs.duckdb-sha }}
set-version: ${{ inputs.stable-version }}
workflow_state:
name: Set state for the release workflow
needs: build_sdist
outputs:
pypi_state: ${{ steps.index_check.outputs.pypi_state }}
ci_env: ${{ steps.ci_env_check.outputs.ci_env }}
s3_url: ${{ steps.s3_check.outputs.s3_url }}
runs-on: ubuntu-latest
steps:
- id: index_check
name: Check ${{ needs.build_sdist.outputs.package-version }} on PyPI
run: |
set -eu
# Check PyPI whether the release we're building is already present
pypi_hostname=${{ inputs.pypi-index == 'test' && 'test.' || '' }}pypi.org
pkg_version=${{ needs.build_sdist.outputs.package-version }}
url=https://${pypi_hostname}/pypi/duckdb/${pkg_version}/json
http_status=$( curl -s -o /dev/null -w "%{http_code}" $url || echo $? )
if [[ $http_status == "200" ]]; then
echo "::warning::Package version ${pkg_version} is already present on ${pypi_hostname}"
pypi_state=VERSION_FOUND
elif [[ $http_status == 000* ]]; then
echo "::error::Error checking PyPI at ${url}: curl exit code ${http_status#'000'}"
pypi_state=UNKNOWN
else
echo "::notice::Package version ${pkg_version} not found on ${pypi_hostname} (http status: ${http_status})"
pypi_state=VERSION_NOT_FOUND
fi
echo "pypi_state=${pypi_state}" >> $GITHUB_OUTPUT
- id: ci_env_check
name: Determine CI environment
run: |
set -eu
if [[ test == "${{ inputs.pypi-index }}" ]]; then
ci_env=pypi-test
elif [[ prod == "${{ inputs.pypi-index }}" ]]; then
ci_env=pypi-prod${{ inputs.stable-version == '' && '-nightly' || '' }}
else
echo "::error::Invalid value for inputs.pypi-index: ${{ inputs.pypi-index }}"
exit 1
fi
echo "ci_env=${ci_env}" >> "$GITHUB_OUTPUT"
echo "::notice::Using CI environment ${ci_env}"
- id: s3_check
name: Generate S3 upload URL
if: github.repository_owner == 'duckdb'
run: |
set -eu
should_store=${{ (inputs.pypi-index == 'prod' || inputs.store-s3) && '1' || '0' }}
if [[ $should_store == 0 ]]; then
echo "::notice::S3 upload disabled in inputs, not generating S3 URL"
exit 0
fi
if [[ VERSION_FOUND == "${{ steps.index_check.outputs.pypi_state }}" ]]; then
echo "::warning::S3 upload disabled because package version already uploaded to PyPI"
exit 0
fi
sha=${{ github.sha }}
dsha=${{ inputs.duckdb-sha }}
version=${{ needs.build_sdist.outputs.package-version }}
s3_url="s3://duckdb-staging/python/${version}/${sha:0:10}-duckdb-${dsha:0:10}/"
echo "::notice::Generated S3 URL: ${s3_url}"
echo "s3_url=${s3_url}" >> $GITHUB_OUTPUT
build_wheels:
name: Build and test releases
needs: workflow_state
if: ${{ needs.workflow_state.outputs.pypi_state != 'VERSION_FOUND' }}
uses: ./.github/workflows/packaging_wheels.yml
with:
minimal: false
testsuite: all
duckdb-python-sha: ${{ inputs.duckdb-python-sha != '' && inputs.duckdb-python-sha || github.sha }}
duckdb-sha: ${{ inputs.duckdb-sha }}
set-version: ${{ inputs.stable-version }}
upload_s3:
name: Upload Artifacts to S3
runs-on: ubuntu-latest
needs: [build_sdist, build_wheels, workflow_state]
if: ${{ needs.workflow_state.outputs.s3_url }}
steps:
- name: Fetch artifacts
uses: actions/download-artifact@v4
with:
pattern: '{sdist,wheel}*'
path: artifacts/
merge-multiple: true
- name: Authenticate with AWS
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: 'us-east-2'
aws-access-key-id: ${{ secrets.S3_DUCKDB_STAGING_ID }}
aws-secret-access-key: ${{ secrets.S3_DUCKDB_STAGING_KEY }}
- name: Upload Artifacts
run: |
aws s3 cp artifacts ${{ needs.workflow_state.outputs.s3_url }} --recursive
publish_pypi:
name: Publish Artifacts to PyPI
runs-on: ubuntu-latest
needs: [workflow_state, build_sdist, build_wheels]
environment:
name: ${{ needs.workflow_state.outputs.ci_env }}
permissions:
# this is needed for the OIDC flow that is used with trusted publishing on PyPI
id-token: write
steps:
- if: ${{ vars.PYPI_HOST == '' }}
run: |
echo "Error: PYPI_HOST is not set in CI environment '${{ needs.workflow_state.outputs.ci_env }}'"
exit 1
- name: Fetch artifacts
uses: actions/download-artifact@v4
with:
pattern: '{sdist,wheel}*'
path: packages/
merge-multiple: true
- name: Upload artifacts to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: 'https://${{ vars.PYPI_HOST }}/legacy/'
packages-dir: packages
verbose: 'true'
cleanup_nightlies:
name: Remove Nightlies from PyPI
needs: [workflow_state, publish_pypi]
if: ${{ inputs.stable-version == '' }}
uses: ./.github/workflows/cleanup_pypi.yml
with:
environment: ${{ needs.workflow_state.outputs.ci_env }}
secrets:
# reusable workflows and secrets are not great: https://github.com/actions/runner/issues/3206
PYPI_CLEANUP_OTP: ${{secrets.PYPI_CLEANUP_OTP}}
PYPI_CLEANUP_PASSWORD: ${{secrets.PYPI_CLEANUP_PASSWORD}}
summary:
name: Release summary
runs-on: ubuntu-latest
needs: [build_sdist, workflow_state, build_wheels, upload_s3, publish_pypi, cleanup_nightlies]
if: always()
steps:
- run: |
sha=${{ github.sha }}
dsha=${{ inputs.duckdb-sha }}
pversion=${{ needs.build_sdist.outputs.package-version }}
long_pversion="${pversion} (${sha:0:10})"
pypi_host=${{ inputs.pypi-index == 'test' && 'test.' || '' }}pypi.org
pypi_duckdb_url=https://${pypi_host}/project/duckdb/${pversion}/
was_released=${{ needs.publish_pypi.result == 'success' && '1' || '0' }}
if [[ $was_released == 1 ]]; then
echo "## Version ${long_pversion} successfully released" >> $GITHUB_STEP_SUMMARY
echo "* Package URL: [${pypi_duckdb_url}](${pypi_duckdb_url})" >> $GITHUB_STEP_SUMMARY
else
echo "## Version ${long_pversion} was not released" >> $GITHUB_STEP_SUMMARY
echo "* Package index state before release: ${{ needs.workflow_state.outputs.pypi_state }}" >> $GITHUB_STEP_SUMMARY
fi
echo "* Package index: ${pypi_host}" >> $GITHUB_STEP_SUMMARY
echo "* Vendored DuckDB Version: ${{ needs.build_sdist.outputs.duckdb-version }} (${dsha:0:10})" >> $GITHUB_STEP_SUMMARY
echo "* S3 upload status: ${{ needs.upload_s3.result == 'success' && needs.workflow_state.outputs.s3_url || needs.upload_s3.result }}" >> $GITHUB_STEP_SUMMARY
echo "* CI Environment: ${{ needs.workflow_state.outputs.ci_env }}" >> $GITHUB_STEP_SUMMARY