Skip to content

Commit 6dbff0e

Browse files
emmettbutlerdubloom
authored andcommitted
perf: use serverless-specific wheel in layer (#17201)
## Description This change splits the wheel build/validate/upload jobs into "regular" and "serverless" branches, for the purpose of allowing datadog-lambda-python to use custom-built wheels. ## Testing CI passes ## Risks Risk mitigated by keeping the existing wheel upload path working as usual while refactoring it. ## Additional Notes Depended on by DataDog/datadog-lambda-python#762 and DataDog/serverless-tools#101 Co-authored-by: emmett.butler <emmett.butler@datadoghq.com>
1 parent eccdfd7 commit 6dbff0e

6 files changed

Lines changed: 69 additions & 44 deletions

File tree

.github/workflows/system-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,4 +504,4 @@ jobs:
504504
needs.integration-frameworks-system-tests.result == 'cancelled'||
505505
needs.tracer-release.result == 'failure' ||
506506
needs.tracer-release.result == 'cancelled'
507-
run: exit 1
507+
run: exit 1

.gitlab-ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,9 @@ serverless lambda tests:
125125
trigger:
126126
project: DataDog/datadog-lambda-python
127127
strategy: depend
128-
branch: main
128+
branch: main
129129
needs:
130-
- job: "upload manylinux2014"
130+
- job: "upload serverless"
131131
variables:
132132
UPSTREAM_PIPELINE_ID: $CI_PIPELINE_ID
133133
UPSTREAM_PROJECT_URL: $CI_PROJECT_URL

.gitlab/benchmarks/serverless.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ benchmark-serverless:
55
stage: benchmarks
66
trigger:
77
project: DataDog/serverless-tools
8+
branch: main
89
strategy: depend
910
needs:
1011
- job: "upload serverless"

.gitlab/package.yml

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -197,16 +197,18 @@ variables:
197197
"upload serverless":
198198
extends: .upload_wheels_base
199199
needs:
200-
- job: "build linux"
200+
- job: "build linux serverless"
201201
artifacts: true
202202
parallel:
203203
matrix:
204204
- ARCH_TAG: "amd64"
205-
PYTHON_TAG: "cp311-cp311"
206-
IMAGE_TAG: "v85383392-751efc0-manylinux2014_x86_64"
205+
PYTHON_TAG: *PYTHON_TAGS
206+
IMAGE_TAG: *X86_64_IMAGES
207+
DD_SERVERLESS_BUILD: "1"
207208
- ARCH_TAG: "arm64"
208-
PYTHON_TAG: "cp311-cp311"
209-
IMAGE_TAG: "v85383414-751efc0-manylinux2014_aarch64"
209+
PYTHON_TAG: *PYTHON_TAGS
210+
IMAGE_TAG: *AARCH64_IMAGES
211+
DD_SERVERLESS_BUILD: "1"
210212
- job: "package version"
211213
variables:
212214
UPLOAD_SUFFIX: "serverless"
@@ -379,20 +381,32 @@ download_dependency_wheels:
379381
- "pywheels/*.whl"
380382

381383
# Aggregate and validate all built wheels
382-
"ddtrace package":
384+
.package_base:
383385
image: registry.ddbuild.io/images/mirror/python:3.14.0
384386
tags: [ "arch:amd64" ]
385387
stage: package
388+
before_script:
389+
- pip install 'packaging>=24.0,<26' # ci-deps: allow
390+
artifacts:
391+
paths:
392+
- "pywheels/*.whl"
393+
- "pywheels/*.tar.gz"
394+
395+
"ddtrace package":
396+
extends: .package_base
386397
needs:
387398
- "build linux"
388399
- "build macos"
389400
- "build sdist"
390401
- "collect windows wheels"
391402
- "package version"
392403
script:
393-
- pip install 'packaging>=24.0,<26' # ci-deps: allow
394404
- .gitlab/validate-ddtrace-package.py pywheels
395-
artifacts:
396-
paths:
397-
- "pywheels/*.whl"
398-
- "pywheels/*.tar.gz"
405+
406+
"ddtrace package serverless":
407+
extends: .package_base
408+
needs:
409+
- "build linux serverless"
410+
- "package version"
411+
script:
412+
- .gitlab/validate-ddtrace-package.py pywheels --mode=serverless

.gitlab/release.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ variables:
2626
- pip install -r ci/requirements/ci.txt
2727
- python -m twine check --strict pywheels/*
2828
script:
29-
- rm -rf pywheels/ddtrace_serverless*
30-
- python -m twine upload --repository ${PYPI_REPOSITORY} pywheels/*
29+
- python -m twine upload --repository ${PYPI_REPOSITORY} pywheels/ddtrace-*
3130
artifacts:
3231
paths:
3332
- pywheels/*.whl

.gitlab/validate-ddtrace-package.py

Lines changed: 39 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
PACKAGE_VERSION: Version from pyproject.toml (set by "package version" job)
1717
"""
1818

19+
import argparse
1920
import os
2021
from pathlib import Path
2122
import sys
@@ -41,21 +42,26 @@
4142
SERVERLESS_PLATFORMS = [p for p in BASE_PLATFORMS if "linux" in p]
4243

4344

44-
def build_expected_set(version: str) -> set[tuple[str, str, str]]:
45+
def build_expected_set(version: str, args: argparse.Namespace) -> set[tuple[str, str, str, str]]:
4546
"""Build set of expected (version, python_tag, platform, flavor) tuples."""
4647
expected: set[tuple[str, str, str, str]] = set()
4748
for py_tag in PYTHON_TAGS:
48-
for platform in BASE_PLATFORMS:
49-
expected.add((version, py_tag, platform, ""))
50-
# Add win_arm64 for Python 3.11+
51-
if py_tag in WIN_ARM64_PYTHON_TAGS:
52-
expected.add((version, py_tag, "win_arm64", ""))
49+
if args.mode == "serverless":
50+
for platform in SERVERLESS_PLATFORMS:
51+
expected.add((version, py_tag, platform, "_serverless"))
52+
else:
53+
for platform in BASE_PLATFORMS:
54+
expected.add((version, py_tag, platform, ""))
55+
# Add win_arm64 for Python 3.11+
56+
if py_tag in WIN_ARM64_PYTHON_TAGS:
57+
expected.add((version, py_tag, "win_arm64", ""))
58+
5359
return expected
5460

5561

5662
def reconstruct_wheel_filename(version: str, python_tag: str, platform: str, flavor: str) -> str:
5763
"""Reconstruct wheel filename from components."""
58-
package_name = f"ddtrace{flavor.replace('-', '_')}"
64+
package_name = f"ddtrace{flavor}"
5965
return f"{package_name}-{version}-{python_tag}-{python_tag}-{platform}.whl"
6066

6167

@@ -89,7 +95,9 @@ def validate_sdist(wheels_dir: str, package_version: str) -> tuple[bool, str, st
8995
return False, f"Failed to parse sdist filename: {e}", sdist_path.name
9096

9197

92-
def parse_actual_wheels(wheels_dir: str) -> tuple[set[tuple[str, str, str, str]], list[str], int]:
98+
def parse_actual_wheels(
99+
wheels_dir: str,
100+
) -> tuple[set[tuple[str, str, str, str]], list[str], int]:
93101
"""Parse actual wheel files.
94102
95103
Returns:
@@ -101,7 +109,6 @@ def parse_actual_wheels(wheels_dir: str) -> tuple[set[tuple[str, str, str, str]]
101109
for wheel_file in sorted(Path(wheels_dir).glob("*.whl")):
102110
try:
103111
name, version, build, tags = parse_wheel_filename(wheel_file.name)
104-
flavor = name.replace("ddtrace", "").replace("_", "-")
105112
# Extract python tag - all tags should have the same interpreter
106113
py_tag = next(iter(tags)).interpreter
107114

@@ -110,12 +117,13 @@ def parse_actual_wheels(wheels_dir: str) -> tuple[set[tuple[str, str, str, str]]
110117
# We know: name=ddtrace, abi=python tag (e.g., cp310)
111118
# So platform is everything after: ddtrace-{version}-{python}-{python}-
112119
wheel_base = wheel_file.name.replace(".whl", "")
113-
marker = f"{name}-{version}-{py_tag}-{py_tag}-"
120+
marker = f"{name.replace('-', '_')}-{version}-{py_tag}-{py_tag}-"
114121
if marker in wheel_base:
115122
platform = wheel_base.split(marker)[1]
116123
else:
117-
raise ValueError(f"Cannot parse platform from {wheel_file.name}")
124+
raise ValueError(f"Cannot parse platform from {wheel_file.name} - searched for marker {marker}")
118125

126+
flavor = name.replace("ddtrace", "").replace("-", "_")
119127
actual.add((str(version), py_tag, platform, flavor))
120128
except Exception as e:
121129
errors.append(f"{wheel_file.name}: {e}")
@@ -124,7 +132,7 @@ def parse_actual_wheels(wheels_dir: str) -> tuple[set[tuple[str, str, str, str]]
124132

125133

126134
def identify_version_mismatches(
127-
actual_set: set[tuple[str, str, str]], package_version: str
135+
actual_set: set[tuple[str, str, str, str]], package_version: str
128136
) -> dict[str, tuple[str, str]]:
129137
"""Identify wheels with wrong versions."""
130138
mismatches: dict[str, tuple[str, str]] = {}
@@ -135,12 +143,9 @@ def identify_version_mismatches(
135143
return mismatches
136144

137145

138-
def main() -> None:
139-
"""Main validation function."""
140-
# Get arguments
141-
wheels_dir = sys.argv[1] if len(sys.argv) > 1 else "pywheels"
146+
def main(args: argparse.Namespace) -> None:
147+
wheels_dir = args.wheels_dir
142148

143-
# Get version from environment
144149
package_version: str | None = os.environ.get("PACKAGE_VERSION")
145150
if not package_version:
146151
print("[ERROR] PACKAGE_VERSION not set. Ensure 'package version' job ran.")
@@ -167,15 +172,17 @@ def main() -> None:
167172
print()
168173

169174
# Phase 2: SDist Validation
170-
print("[Phase 2] SDist Validation")
171-
sdist_ok, sdist_msg, sdist_name = validate_sdist(wheels_dir, package_version)
172-
if not sdist_ok:
173-
print(f"✗ {sdist_msg}")
174-
errors.append(f"SDist validation: {sdist_msg}")
175-
else:
176-
print(f"✓ Found sdist: {sdist_name}")
177-
print(f"✓ {sdist_msg}")
178-
print()
175+
sdist_ok = False
176+
if args.mode != "serverless":
177+
print("[Phase 2] SDist Validation")
178+
sdist_ok, sdist_msg, sdist_name = validate_sdist(wheels_dir, package_version)
179+
if not sdist_ok:
180+
print(f"✗ {sdist_msg}")
181+
errors.append(f"SDist validation: {sdist_msg}")
182+
else:
183+
print(f"✓ Found sdist: {sdist_name}")
184+
print(f"✓ {sdist_msg}")
185+
print()
179186

180187
# Phase 3: Parse Actual Wheels
181188
print("[Phase 3] Parsing Actual Wheels")
@@ -194,7 +201,7 @@ def main() -> None:
194201

195202
# Phase 4: Build Expected Set
196203
print("[Phase 4] Building Expected Set")
197-
expected_set = build_expected_set(package_version)
204+
expected_set = build_expected_set(package_version, args)
198205
print(f"Expected {len(expected_set)} wheels:")
199206
print(f" - {len(PYTHON_TAGS)} Python versions (cp39-cp314)")
200207
print(f" - {len(BASE_PLATFORMS)} base platforms")
@@ -281,4 +288,8 @@ def main() -> None:
281288

282289

283290
if __name__ == "__main__":
284-
main()
291+
parser = argparse.ArgumentParser(prog="Validate DDTrace Package")
292+
parser.add_argument("--mode", choices=["main", "serverless"], default="main")
293+
parser.add_argument("wheels_dir", nargs="?", default="pywheels")
294+
args = parser.parse_args()
295+
main(args)

0 commit comments

Comments
 (0)