Skip to content

Commit e8ee9d3

Browse files
Merge branch 'main' into msvc_static
2 parents c3628c9 + 3649e17 commit e8ee9d3

43 files changed

Lines changed: 1076 additions & 255 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/packaging_wheels.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,12 @@ jobs:
3333
python: [ cp39, cp310, cp311, cp312, cp313, cp314 ]
3434
platform:
3535
- { os: windows-2025, arch: amd64, cibw_system: win }
36+
- { os: windows-11-arm, arch: ARM64, cibw_system: win } # cibw requires ARM64 to be uppercase
3637
- { os: ubuntu-24.04, arch: x86_64, cibw_system: manylinux }
3738
- { os: ubuntu-24.04-arm, arch: aarch64, cibw_system: manylinux }
3839
- { os: macos-15, arch: arm64, cibw_system: macosx }
3940
- { os: macos-15, arch: universal2, cibw_system: macosx }
40-
- { os: macos-13, arch: x86_64, cibw_system: macosx }
41+
- { os: macos-15-intel, arch: x86_64, cibw_system: macosx }
4142
minimal:
4243
- ${{ inputs.minimal }}
4344
exclude:
@@ -46,6 +47,8 @@ jobs:
4647
- { minimal: true, python: cp312 }
4748
- { minimal: true, python: cp313 }
4849
- { minimal: true, platform: { arch: universal2 } }
50+
- { python: cp39, platform: { os: windows-11-arm, arch: ARM64 } } # too many dependency problems for win arm64
51+
- { python: cp310, platform: { os: windows-11-arm, arch: ARM64 } } # too many dependency problems for win arm64
4952
runs-on: ${{ matrix.platform.os }}
5053
env:
5154
### cibuildwheel configuration

.github/workflows/release.yml

Lines changed: 50 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ on:
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: 3
2530
store-s3:
2631
type: boolean
2732
description: Also store test packages in S3 (always true for prod)
@@ -41,6 +46,17 @@ jobs:
4146
duckdb-sha: ${{ inputs.duckdb-sha }}
4247
set-version: ${{ inputs.stable-version }}
4348

49+
submodule_pr:
50+
name: Create or update PR to bump submodule to given SHA
51+
needs: build_sdist
52+
uses: ./.github/workflows/submodule_auto_pr.yml
53+
with:
54+
duckdb-python-sha: ${{ inputs.duckdb-python-sha }}
55+
duckdb-sha: ${{ inputs.duckdb-sha }}
56+
secrets:
57+
# reusable workflows and secrets are not great: https://github.com/actions/runner/issues/3206
58+
DUCKDBLABS_BOT_TOKEN: ${{ secrets.DUCKDBLABS_BOT_TOKEN }}
59+
4460
workflow_state:
4561
name: Set state for the release workflow
4662
needs: build_sdist
@@ -51,23 +67,36 @@ jobs:
5167
runs-on: ubuntu-latest
5268
steps:
5369
- id: index_check
54-
name: Check ${{ needs.build_sdist.outputs.package-version }} on PyPI
70+
name: Check version on PyPI
5571
run: |
56-
set -eu
57-
# Check PyPI whether the release we're building is already present
72+
set -ex
5873
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})"
74+
# install duckdb
75+
curl https://install.duckdb.org | sh
76+
# query pypi
77+
result=$(cat <<EOF | ${HOME}/.duckdb/cli/latest/duckdb | xargs
78+
---- Output lines
79+
.mode line
80+
---- Query that fetches the given version's age, if the version already exists
81+
SELECT
82+
today() - (file.value->>'upload_time_iso_8601')::DATE AS age,
83+
FROM read_json('https://${pypi_hostname}/pypi/duckdb/json') AS jd
84+
CROSS JOIN json_each(jd.releases) AS rel(key, value)
85+
CROSS JOIN unnest(FROM_JSON(rel.value, '["JSON"]')) AS file(value)
86+
WHERE rel.key='${{ needs.build_sdist.outputs.package-version }}'
87+
LIMIT 1;
88+
EOF
89+
)
90+
if [ -z "$result" ]; then
7091
pypi_state=VERSION_NOT_FOUND
92+
else
93+
pypi_state=VERSION_FOUND
94+
fi
95+
if [[ -z "${{ inputs.stable-version }}" ]]; then
96+
age=${result#age = }
97+
if [ "${age}" -ge "${{ inputs.nightly-stale-after-days }}" ]; then
98+
echo "::warning title=Stale nightly for ${{ github.ref_name }}::Nightly is ${age} days old (max=${{ inputs.nightly-stale-after-days }})"
99+
fi
71100
fi
72101
echo "pypi_state=${pypi_state}" >> $GITHUB_OUTPUT
73102
@@ -96,7 +125,7 @@ jobs:
96125
echo "::notice::S3 upload disabled in inputs, not generating S3 URL"
97126
exit 0
98127
fi
99-
if [[ VERSION_FOUND == "${{ steps.index_check.outputs.pypi_state }}" ]]; then
128+
if [[ VERSION_NOT_FOUND != "${{ steps.index_check.outputs.pypi_state }}" ]]; then
100129
echo "::warning::S3 upload disabled because package version already uploaded to PyPI"
101130
exit 0
102131
fi
@@ -110,7 +139,7 @@ jobs:
110139
build_wheels:
111140
name: Build and test releases
112141
needs: workflow_state
113-
if: ${{ needs.workflow_state.outputs.pypi_state != 'VERSION_FOUND' }}
142+
if: ${{ needs.workflow_state.outputs.pypi_state == 'VERSION_NOT_FOUND' }}
114143
uses: ./.github/workflows/packaging_wheels.yml
115144
with:
116145
minimal: false
@@ -132,14 +161,12 @@ jobs:
132161
path: artifacts/
133162
merge-multiple: true
134163

135-
- name: Authenticate with AWS
136-
uses: aws-actions/configure-aws-credentials@v4
137-
with:
138-
aws-region: 'us-east-2'
139-
aws-access-key-id: ${{ secrets.S3_DUCKDB_STAGING_ID }}
140-
aws-secret-access-key: ${{ secrets.S3_DUCKDB_STAGING_KEY }}
141-
142164
- name: Upload Artifacts
165+
env:
166+
AWS_ENDPOINT_URL: ${{ secrets.S3_DUCKDB_STAGING_ENDPOINT }}
167+
AWS_ACCESS_KEY_ID: ${{ secrets.S3_DUCKDB_STAGING_ID }}
168+
AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_DUCKDB_STAGING_KEY }}
169+
143170
run: |
144171
aws s3 cp artifacts ${{ needs.workflow_state.outputs.s3_url }} --recursive
145172
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
name: Submodule Auto PR
2+
on:
3+
workflow_call:
4+
inputs:
5+
duckdb-python-sha:
6+
type: string
7+
description: The commit to build against (defaults to latest commit of current ref)
8+
required: false
9+
duckdb-sha:
10+
type: string
11+
description: The DuckDB submodule commit or ref to build against
12+
required: true
13+
auto-land:
14+
type: boolean
15+
description: Immediately merge the PR (placeholder - doesn't work)
16+
default: false
17+
secrets:
18+
DUCKDBLABS_BOT_TOKEN:
19+
description: Github token of the DuckDBLabs bot
20+
required: true
21+
22+
defaults:
23+
run:
24+
shell: bash
25+
26+
jobs:
27+
create_pr:
28+
name: Create PR to bump duckdb submodule to given SHA
29+
runs-on: ubuntu-latest
30+
steps:
31+
- name: Checkout DuckDB Python
32+
uses: actions/checkout@v4
33+
with:
34+
ref: ${{ inputs.duckdb-python-sha }}
35+
fetch-depth: 0
36+
submodules: true
37+
38+
- name: Checkout or Create Needed Branch
39+
run: |
40+
git fetch --all
41+
head_sha=${{ inputs.duckdb-python-sha }}
42+
branch_name="vendoring-${{ github.ref_name }}"
43+
if [[ `git rev-parse --verify ${branch_name} 2>/dev/null` ]]; then
44+
# branch exists
45+
git checkout ${branch_name}
46+
else
47+
# new branch
48+
git checkout -b ${branch_name}
49+
fi
50+
[[ ${head_sha} ]] && git reset --hard ${head_sha} || true
51+
52+
- name: Checkout DuckDB at Given SHA
53+
run: |
54+
cd external/duckdb
55+
git fetch origin
56+
git checkout ${{ inputs.duckdb-sha }}
57+
58+
- name: Determine GH PR Command
59+
id: gh_pr_command
60+
env:
61+
GH_TOKEN: ${{ secrets.DUCKDBLABS_BOT_TOKEN }}
62+
run: |
63+
pr_url=$( gh pr list --head vendoring-${{ github.ref_name }} --state open --json url --jq '.[].url' )
64+
if [[ $pr_url ]]; then
65+
echo "::notice::Found existing pr, will edit (${pr_url})"
66+
gh_command="edit ${pr_url}"
67+
else
68+
echo "::notice::No existing PR, will create new"
69+
gh_command="create --head vendoring-${{ github.ref_name }} --base ${{ github.ref_name }}"
70+
fi
71+
echo "subcommand=${gh_command}" >> $GITHUB_OUTPUT
72+
73+
- name: Set Git User
74+
run: |
75+
git config --global user.email "github_bot@duckdblabs.com"
76+
git config --global user.name "DuckDB Labs GitHub Bot"
77+
78+
- name: Create PR to Bump DuckDB Submodule
79+
env:
80+
GH_TOKEN: ${{ secrets.DUCKDBLABS_BOT_TOKEN }}
81+
run: |
82+
# No need to do anything if the submodule is already at the given sha
83+
[[ `git status --porcelain -- external/duckdb` == "" ]] && exit 0
84+
# We have changes. Commit and push
85+
git add external/duckdb
86+
git commit -m "Bump submodule"
87+
git push --force origin vendoring-${{ github.ref_name }}
88+
# create PR msg
89+
echo "Bump duckdb submodule:" > body.txt
90+
echo "- Target branch: ${{ github.ref_name }}" >> body.txt
91+
echo "- Date: $( date +"%Y-%m-%d %H:%M:%S" )" >> body.txt
92+
echo "- DuckDB SHA: ${{ inputs.duckdb-sha }}" >> body.txt
93+
echo "- Trigger: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> body.txt
94+
subcommand="${{ steps.gh_pr_command.outputs.subcommand }}"
95+
gh pr ${subcommand} \
96+
--title "[duckdb-labs bot] Bump DuckDB submodule" \
97+
--body-file body.txt > output.txt 2>&1
98+
success=$?
99+
# Show summary
100+
url=$( [[ $success ]] && gh pr view vendoring-${{ github.ref_name }} --json url --jq .url || true )
101+
echo "## Submodule PR Summary" >> $GITHUB_STEP_SUMMARY
102+
if [[ $success ]]; then
103+
prefix=$( [[ $subcommand == edit* ]] && echo "Created" || echo "Updated" )
104+
echo "### ${prefix} PR: [${url}](${url})" >> $GITHUB_STEP_SUMMARY
105+
else
106+
echo "### Failed to create PR" >> $GITHUB_STEP_SUMMARY
107+
fi
108+
echo '```' >> $GITHUB_STEP_SUMMARY
109+
cat output.txt >> $GITHUB_STEP_SUMMARY
110+
echo '```' >> $GITHUB_STEP_SUMMARY
111+
[[ $success ]] || exit 1
112+
113+
- name: Automerge PR
114+
if: ${{ inputs.auto-land }}
115+
env:
116+
GH_TOKEN: ${{ secrets.DUCKDBLABS_BOT_TOKEN }}
117+
run: |
118+
# PLACEHOLDER: DUCKDBLABS_BOT_TOKEN DOES NOT HAVE PERMISSIONS TO MERGE PRS
119+
set -ex
120+
gh pr merge vendoring-${{ github.ref_name }} --rebase > output.txt
121+
success=$?
122+
# Show summary
123+
if [[ $success ]]; then
124+
echo "### PR merged" >> $GITHUB_STEP_SUMMARY
125+
else
126+
echo "### Failed to auto-merge PR" >> $GITHUB_STEP_SUMMARY
127+
fi
128+
echo '```' >> $GITHUB_STEP_SUMMARY
129+
cat output.txt >> $GITHUB_STEP_SUMMARY
130+
echo '```' >> $GITHUB_STEP_SUMMARY
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
name: Targeted Platform Testing
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
platform:
7+
description: 'Platform to test on'
8+
required: true
9+
type: choice
10+
options:
11+
- 'windows-2025'
12+
- 'windows-11-arm'
13+
- 'ubuntu-24.04'
14+
- 'ubuntu-24.04-arm'
15+
- 'macos-15'
16+
- 'macos-15-intel'
17+
python_version:
18+
description: 'Python version to test'
19+
required: true
20+
type: choice
21+
options:
22+
- '3.9'
23+
- '3.10'
24+
- '3.11'
25+
- '3.12'
26+
- '3.13'
27+
- '3.14'
28+
testsuite:
29+
description: 'Test suite to run (ignored if custom_test_path is provided)'
30+
required: false
31+
type: choice
32+
options:
33+
- 'fast'
34+
- 'all'
35+
default: 'fast'
36+
custom_test_path:
37+
description: 'Custom test path (must be in tests/ directory, overrides testsuite)'
38+
required: false
39+
type: string
40+
verbose-uv:
41+
description: 'Let uv generate verbose output (pytest verbosity is always on)'
42+
required: false
43+
type: boolean
44+
default: true
45+
46+
jobs:
47+
test:
48+
name: 'Test with Python ${{ inputs.python_version }} on ${{ inputs.platform }}'
49+
runs-on: ${{ inputs.platform }}
50+
51+
steps:
52+
- name: Checkout DuckDB Python
53+
uses: actions/checkout@v4
54+
with:
55+
fetch-depth: 0
56+
submodules: true
57+
58+
- name: Install uv
59+
uses: astral-sh/setup-uv@v7
60+
with:
61+
version: "0.9.0"
62+
enable-cache: false
63+
python-version: ${{ inputs.python_version }}
64+
65+
- name: Set and validate test path
66+
id: test_path
67+
shell: bash
68+
run: |
69+
if [[ -n "${{ inputs.custom_test_path }}" ]]; then
70+
# test path was passed in
71+
tests_base="$( pwd -P )/tests"
72+
test_path="${{ inputs.custom_test_path }}"
73+
74+
# Ensure the given test path exists
75+
[[ -e "$test_path" ]] || { echo "${test_path} does not exist"; exit 1; }
76+
77+
# Resolve custom test path to absolute path
78+
test_path_abs=$(cd "$test_path" 2>/dev/null && pwd -P || ( cd "$(dirname "$test_path")" && printf '%s/%s' "$(pwd -P)" "$(basename "$test_path")" ) )
79+
80+
# Make sure test_path_abs is inside tests_base
81+
[[ "$test_path_abs" == "$tests_base" || "$test_path_abs" == "$tests_base"/* ]] || { echo "${test_path_abs} is not part of ${tests_base}?"; exit 1; }
82+
83+
echo "test_path=$test_path_abs" >> $GITHUB_OUTPUT
84+
else
85+
# use a testsuite
86+
echo "test_path=$GITHUB_WORKSPACE/${{ inputs.testsuite == 'fast' && 'tests/fast' || 'tests' }}" >> $GITHUB_OUTPUT
87+
fi
88+
89+
- name: Run tests
90+
shell: bash
91+
run: |
92+
uv ${{ inputs.verbose-uv && 'run -v' || 'run' }} pytest -vv ${{ steps.test_path.outputs.test_path }}

CHANGELOG.md

Lines changed: 0 additions & 19 deletions
This file was deleted.

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ pybind11_add_module(
7979
$<TARGET_OBJECTS:python_type>)
8080
# add _duckdb_dependencies
8181
target_link_libraries(_duckdb PRIVATE _duckdb_dependencies)
82+
duckdb_link_extensions(_duckdb)
8283

8384
# ────────────────────────────────────────────
8485
# Controlling symbol export

0 commit comments

Comments
 (0)