Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@
# golden.test.ts). EOL conversion on checkout (Windows CRLF) breaks the
# byte-for-byte comparison, so they are checked out exactly as committed.
crates/socket-patch-core/tests/fixtures/redirect/** -text

crates/socket-patch-core/tests/fixtures/pdm-native/*.lock -text
142 changes: 142 additions & 0 deletions .github/workflows/pdm-compatibility.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
name: PDM patch compatibility

# Native PDM installer matrix: builds the CLI once per OS, bootstraps pinned
# PDM releases with uv, and runs `scripts/backtest-pdm.py` — hosted, vendored
# and agent mode against the public urllib3 free patch, verifying the
# INSTALLED bytes, lock/manifest stability, hash rejection and rollback. No
# Socket API token is needed. See docs/testing/pdm-compatibility.md.

on:
pull_request:
paths:
- '.github/workflows/pdm-compatibility.yml'
- 'scripts/backtest-pdm.py'
- 'crates/socket-patch-core/src/utils/pdm_lock.rs'
- 'crates/socket-patch-core/src/patch/redirect/**'
- 'crates/socket-patch-core/src/vendor/pypi*.rs'
- 'crates/socket-patch-core/src/vendor/common.rs'
- 'crates/socket-patch-core/src/vendor/lock_inventory.rs'
- 'crates/socket-patch-cli/src/commands/scan/**'
- 'crates/socket-patch-cli/src/commands/rollback.rs'
- 'crates/socket-patch-core/tests/fixtures/pdm-native/**'
push:
branches: [main]
paths:
- 'scripts/backtest-pdm.py'
- 'crates/socket-patch-core/src/utils/pdm_lock.rs'
- 'crates/socket-patch-core/src/patch/redirect/pdm.rs'
- 'crates/socket-patch-core/src/vendor/pypi_pdm.rs'
workflow_dispatch:
inputs:
versions:
description: 'Space-separated PDM versions (empty = the required matrix below)'
required: false
default: ''
shapes:
description: 'Space-separated shapes (empty = every shape)'
required: false
default: ''

permissions:
contents: read

concurrency:
group: pdm-compat-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

env:
SOCKET_NO_CONFIG: '1'
SOCKET_NO_UPDATE_CHECK: '1'
PDM_CHECK_UPDATE: 'false'

jobs:
build:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 30
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
key: pdm-compat
- run: cargo build --locked -p socket-patch-cli
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: pdm-cli-${{ matrix.os }}
path: |
target/debug/socket-patch
target/debug/socket-patch.exe
if-no-files-found: error
retention-days: 7

native:
needs: build
strategy:
fail-fast: false
matrix:
# Every stable PDM major family (0.x, 1.x, 2.x) and each 2.x lock-format
# boundary, on Linux and Windows; macOS samples the ends of the range.
os: [ubuntu-latest, windows-latest]
pdm: ['0.12.3', '1.15.5', '2.0.3', '2.1.5', '2.3.4', '2.6.1', '2.7.4', '2.8.2', '2.9.3', '2.10.4', '2.11.2', '2.17.3', '2.20.1', '2.25.9', '2.29.2']
include:
- { os: macos-latest, pdm: '0.12.3' }
- { os: macos-latest, pdm: '2.8.2' }
- { os: macos-latest, pdm: '2.29.2' }
runs-on: ${{ matrix.os }}
timeout-minutes: 45
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: pdm-cli-${{ matrix.os }}
path: native-cli
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: '3.12'
# uv bootstraps every pinned PDM release (and its interpreter) itself;
# pinning uv keeps the bootstrap reproducible across runner images.
- run: python -m pip install uv==0.11.19
- name: Exercise the native PDM installers
shell: bash
env:
PDM_VERSION: ${{ matrix.pdm }}
VERSIONS_OVERRIDE: ${{ github.event.inputs.versions }}
SHAPES_OVERRIDE: ${{ github.event.inputs.shapes }}
run: |
chmod +x native-cli/socket-patch* || true
cli="native-cli/socket-patch"
if [ "$RUNNER_OS" = "Windows" ]; then cli="native-cli/socket-patch.exe"; fi
versions="$PDM_VERSION"
if [ -n "$VERSIONS_OVERRIDE" ]; then versions="$VERSIONS_OVERRIDE"; fi
shapes_arg=()
if [ -n "$SHAPES_OVERRIDE" ]; then shapes_arg=(--shapes $SHAPES_OVERRIDE); fi
python3 scripts/backtest-pdm.py \
--socket-patch "$cli" \
--socket-patch-revision "$GITHUB_SHA" \
--output native-pdm \
--versions $versions \
--modes hosted vendored agent \
"${shapes_arg[@]}" \
--jobs 3
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: always()
with:
name: pdm-results-${{ matrix.os }}-${{ matrix.pdm }}
path: |
native-pdm/summary.json
native-pdm/summary.md
native-pdm/*.log
native-pdm/original/**/pdm.lock
native-pdm/original/**/pyproject.toml
native-pdm/captures/**/result.json
native-pdm/captures/**/cli-output.json
native-pdm/captures/**/pdm.lock
native-pdm/captures/**/*.log
retention-days: 14
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,33 @@ into the new version's section — see docs/releasing.md.
the verified remedy (`pipenv run pip uninstall -y <pkg> && pipenv sync`, or
a clean `pipenv --rm && pipenv sync`), and the stale purl is excluded from
the same-run `--vex`.
- **PDM projects take hosted patches, and hosted and vendored patches share
one validated `pdm.lock` rewriter.** `scan --mode hosted` rewrites `pdm.lock`
to point the target `[[package]]` at the hosted wheel URL with the patched
SHA-256 (`redirect_pdm_lock_package`), and `scan --mode vendored` wires the
same unit to a committed wheel through the shared rewriter
(`utils/pdm_lock.rs`). Both preserve line endings and non-canonical spacing,
support the legacy `[metadata.files]` table and separate `extras` entries,
are idempotent, and leave `pyproject.toml` and `content_hash` untouched.
Supported lock formats are `2` (PDM 0.12–1.4) and `4.3`–`4.5.1` (PDM 2.8.1+;
PDM 2.8.0 writes the same `4.3` lock but still loses candidate identity, so
upgrade to ≥ 2.8.1);
the identity-losing `3.1` / `4.0`–`4.2` formats (PDM 1.8–2.7) and unknown
future formats are refused before any write (`redirect_pdm_refused` /
`pypi_pdm_lock_version_unsupported`), leaving the registry lock installable.
A `pdm.lock` written by PDM 0.x/1.x (lock format `2`) warns
`redirect_pdm_legacy_sync_required`: those releases have an upstream
freshness bug, so `pdm install` can regenerate the lock — use `pdm sync`.
Verified end-to-end against real PDM 0.12–2.29 across hosted, vendored and
agent mode on Linux, Windows and macOS; see
`docs/testing/pdm-compatibility.md` and `scripts/backtest-pdm.py`. When
several Python lockfiles coexist, `uv.lock` and `poetry.lock` drive hosted
PyPI redirects ahead of `pdm.lock`, so a leftover `pdm.lock` beside them
neither blocks a live redirect nor is falsely attested. A hosted lock-only
`pdm.lock` checkout is discovered and redirected (the lock inventory now
reads `pdm.lock`), and a re-scan after an external `pdm lock` rebases the
ledger's recorded edits onto the relocked text so `rollback` stays
byte-invertible even when PDM reflows the lock's line endings.
- **Poetry projects take hosted patches, and vendored patches now cover every
`poetry.lock` generation.** `scan --mode hosted` rewrites `poetry.lock` to a
`[package.source] type = "url"` pointing at the Socket-hosted, SHA-256-pinned
Expand Down
Loading
Loading