pipelex-sdk 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. pipelex_sdk-0.1.0/.claude/skills/release/SKILL.md +133 -0
  2. pipelex_sdk-0.1.0/.github/workflows/changelog-check.yml +32 -0
  3. pipelex_sdk-0.1.0/.github/workflows/cla.yml +42 -0
  4. pipelex_sdk-0.1.0/.github/workflows/guard-branches.yml +100 -0
  5. pipelex_sdk-0.1.0/.github/workflows/lint-check.yml +65 -0
  6. pipelex_sdk-0.1.0/.github/workflows/package-check.yml +27 -0
  7. pipelex_sdk-0.1.0/.github/workflows/publish.yml +172 -0
  8. pipelex_sdk-0.1.0/.github/workflows/tests-check.yml +53 -0
  9. pipelex_sdk-0.1.0/.github/workflows/version-check.yml +74 -0
  10. pipelex_sdk-0.1.0/.gitignore +14 -0
  11. pipelex_sdk-0.1.0/.vscode/extensions.json +7 -0
  12. pipelex_sdk-0.1.0/.vscode/settings.json +28 -0
  13. pipelex_sdk-0.1.0/CHANGELOG.md +33 -0
  14. pipelex_sdk-0.1.0/CLA.md +94 -0
  15. pipelex_sdk-0.1.0/CLAUDE.md +110 -0
  16. pipelex_sdk-0.1.0/LICENSE +21 -0
  17. pipelex_sdk-0.1.0/Makefile +309 -0
  18. pipelex_sdk-0.1.0/PKG-INFO +133 -0
  19. pipelex_sdk-0.1.0/README.md +97 -0
  20. pipelex_sdk-0.1.0/docs/HANDOFF.md +56 -0
  21. pipelex_sdk-0.1.0/docs/architecture.md +172 -0
  22. pipelex_sdk-0.1.0/docs/ci-cd.md +36 -0
  23. pipelex_sdk-0.1.0/pipelex_sdk/__init__.py +0 -0
  24. pipelex_sdk-0.1.0/pipelex_sdk/_compat.py +10 -0
  25. pipelex_sdk-0.1.0/pipelex_sdk/_pydantic_utils.py +15 -0
  26. pipelex_sdk-0.1.0/pipelex_sdk/client.py +1024 -0
  27. pipelex_sdk-0.1.0/pipelex_sdk/errors.py +147 -0
  28. pipelex_sdk-0.1.0/pipelex_sdk/product_models.py +360 -0
  29. pipelex_sdk-0.1.0/pipelex_sdk/runs.py +198 -0
  30. pipelex_sdk-0.1.0/pipelex_sdk/validation_models.py +121 -0
  31. pipelex_sdk-0.1.0/pipelex_sdk/version.py +23 -0
  32. pipelex_sdk-0.1.0/pyproject.toml +363 -0
  33. pipelex_sdk-0.1.0/tests/unit/test_client_construction.py +90 -0
  34. pipelex_sdk-0.1.0/tests/unit/test_client_execute.py +96 -0
  35. pipelex_sdk-0.1.0/tests/unit/test_client_health.py +59 -0
  36. pipelex_sdk-0.1.0/tests/unit/test_client_lifecycle.py +249 -0
  37. pipelex_sdk-0.1.0/tests/unit/test_client_product.py +396 -0
  38. pipelex_sdk-0.1.0/tests/unit/test_client_run_fallback.py +187 -0
  39. pipelex_sdk-0.1.0/tests/unit/test_client_transport.py +108 -0
  40. pipelex_sdk-0.1.0/tests/unit/test_client_validate.py +128 -0
  41. pipelex_sdk-0.1.0/tests/unit/test_error_parsing.py +45 -0
  42. pipelex_sdk-0.1.0/tests/unit/test_runs.py +31 -0
  43. pipelex_sdk-0.1.0/tests/unit/test_validation_contract.py +199 -0
  44. pipelex_sdk-0.1.0/tests/unit/test_version.py +35 -0
  45. pipelex_sdk-0.1.0/uv.lock +750 -0
@@ -0,0 +1,133 @@
1
+ ---
2
+ name: release
3
+ description: >
4
+ Automates the pipelex-sdk-python release workflow: bumps the version in pyproject.toml, finalizes the CHANGELOG.md Unreleased section, runs quality checks, regenerates uv.lock, creates a release/vX.Y.Z branch, commits, pushes, and opens a PR to main. Use when user says "release", "cut a release", "bump version", "prepare a release", "make a release", "ship it", "create release branch", or any variation of shipping a new version of the pipelex-sdk Python package. The user can optionally provide changelog content inline when invoking the skill (e.g. "/release Added the storage routes"), which will be used as the changelog entry for this version.
5
+ ---
6
+
7
+ # pipelex-sdk-python Release Workflow
8
+
9
+ This skill handles the full release cycle for the `pipelex-sdk` Python package (import package `pipelex_sdk`, the `pipelex-sdk-python` repo). A release is a `release/vX.Y.Z` branch that PRs into `main`; merging to `main` triggers `publish.yml`, which builds the wheel, publishes it to PyPI as `pipelex-sdk` via Trusted Publishing (OIDC, no token), and creates a Sigstore-signed GitHub release from the changelog notes.
10
+
11
+ ## Files touched
12
+
13
+ - **`pyproject.toml`** — the `version` field (line 3, under `[project]`)
14
+ - **`CHANGELOG.md`** — add `## [vX.Y.Z] - YYYY-MM-DD` entry (convert the `## [Unreleased]` section if present)
15
+ - **`uv.lock`** — regenerated via `make li` (lock + install)
16
+
17
+ ## Workflow
18
+
19
+ ### 1. Pre-flight checks
20
+
21
+ - Read the current version from `pyproject.toml`.
22
+ - Read `CHANGELOG.md` to understand the current state (this repo keeps a `## [Unreleased]` section at the top).
23
+ - Run `git status` and `git log origin/main..HEAD` to assess the working tree:
24
+ - If there are **uncommitted changes** (staged or unstaged), warn the user and ask whether to commit them as part of the release, stash them, or abort.
25
+ - If there are **unpushed commits** on the current branch, list them so the user is aware — these will be included in the release branch.
26
+
27
+ ### 2. Determine the bump type
28
+
29
+ Ask the user which kind of version bump they want — **patch**, **minor**, or **major** — unless they already specified it. Show the current version and what the new version would be for each option so the choice is concrete.
30
+
31
+ While the package is pre-1.0 (`0.y.z`), treat the `0.MINOR.PATCH` segments the way the project has been using them: a breaking change bumps the minor, a backward-compatible feature or fix bumps the patch. If the changelog for this release contains a `### Breaking Changes` section (or otherwise describes a breaking change), steer the user toward at least a minor bump — this matches the repo's "pre-1.0 breaking changes → minor version bump" rule.
32
+
33
+ ### 3. Run quality checks
34
+
35
+ Run `make agent-check`. This is the gate — if it fails, stop and report the errors so they can be fixed before retrying. Do not proceed past this step on failure.
36
+
37
+ ### 4. Ensure we're on the right branch
38
+
39
+ The release branch must be named `release/vX.Y.Z` where X.Y.Z is the **new** version. The CI guards in this repo are strict about this:
40
+
41
+ - `guard-branches.yml` (`gate-main`) rejects any source branch other than `release/vX.Y.Z` merging into `main`.
42
+ - `version-check.yml` rejects a mismatch between the branch name and the `pyproject.toml` version.
43
+
44
+ Both guards match the **exact** regex `release/v[0-9]+\.[0-9]+\.[0-9]+` (strict three-segment semver, no suffix). All file modifications (changelog, version bump, lock) must happen on this branch.
45
+
46
+ - If already on `release/vX.Y.Z` matching the new version, stay on it.
47
+ - If on `dev`, `main`, or any other branch, create and switch to `release/vX.Y.Z` from the current HEAD.
48
+ - If on a `release/` branch for a **different** version, warn the user and ask how to proceed.
49
+
50
+ ### 5. Finalize the changelog
51
+
52
+ Add a new version entry for the release. This repo uses the workspace-wide `## [vX.Y.Z]` header convention (the changelog and publish workflows key off it).
53
+
54
+ 1. If there is an `## [Unreleased]` section, **convert it**: remove the `## [Unreleased]` heading (and any blank lines that immediately follow it) and replace it with the new `## [vX.Y.Z] - YYYY-MM-DD` heading. Any content that was under `[Unreleased]` becomes the content of the new version.
55
+ 2. If there is no `[Unreleased]` section, insert the new version heading directly after the `# Changelog` intro block.
56
+ 3. **Never recreate an `[Unreleased]` heading.** After a release the changelog should contain only concrete version entries — the next change adds a fresh `## [Unreleased]` section organically when someone starts the next cycle.
57
+ 4. If the user provided changelog content when invoking the skill (e.g. `/release Added the storage routes`), **merge** that content with any existing `[Unreleased]` content (do not discard either source). Format the combined content under the appropriate headings — this repo uses `### Breaking Changes`, `### Added`, `### Changed`, `### Fixed`, `### Removed` — inferring headings from the content when possible.
58
+ 5. If the release has no changelog content yet (neither from an `[Unreleased]` section nor from inline user input), ask the user what to include before proceeding.
59
+ 6. The result should look like:
60
+
61
+ ```markdown
62
+ # Changelog
63
+
64
+ All notable changes to `pipelex-sdk` are documented here. ...
65
+
66
+ ## [vX.Y.Z] - YYYY-MM-DD
67
+
68
+ ### Changed
69
+ - ...
70
+
71
+ ## [vPREVIOUS] - PREVIOUS-DATE
72
+ ...
73
+ ```
74
+
75
+ ### 6. Bump the version in pyproject.toml
76
+
77
+ Edit `pyproject.toml` line 3 (`version = "..."` under `[project]`) to the new version string. Only change the version field — don't touch anything else.
78
+
79
+ ### 7. Lock dependencies
80
+
81
+ Run `make li` to regenerate `uv.lock` and reinstall. This ensures the lockfile reflects the new version in `pyproject.toml`. The `package-check.yml` CI job runs `uv lock --locked` and fails the PR if `uv.lock` is out of sync, so this step is not optional. If it fails, stop and report the error.
82
+
83
+ ### 8. Commit and push
84
+
85
+ Stage all release-related changes. This includes at minimum `pyproject.toml`, `CHANGELOG.md`, and `uv.lock`, plus any other files the user chose to include in step 1 (e.g. previously uncommitted work that belongs in this release).
86
+
87
+ Commit with the message:
88
+
89
+ ```
90
+ Release vX.Y.Z
91
+ ```
92
+
93
+ Push the branch to origin with `-u` to set up tracking.
94
+
95
+ ### 9. Open a PR
96
+
97
+ Create a pull request targeting `main` with:
98
+
99
+ - **Title:** `Release vX.Y.Z`
100
+ - **Body:** Include:
101
+ - The changelog entries for this version (copied from CHANGELOG.md)
102
+ - A note about the version bump from old to new
103
+
104
+ Use this format for the PR body:
105
+
106
+ ```markdown
107
+ ## Release vX.Y.Z
108
+
109
+ Bumps version from `A.B.C` to `X.Y.Z`.
110
+
111
+ ### Changelog
112
+
113
+ <paste the changelog entries for this version here>
114
+ ```
115
+
116
+ Report the PR URL back to the user, and remind them that **merging the PR into `main` is what publishes** — `publish.yml` builds the wheel, pushes it to PyPI as `pipelex-sdk` (Trusted Publishing), and cuts the Sigstore-signed GitHub release automatically. Nothing publishes until the PR is merged.
117
+
118
+ ## Important details
119
+
120
+ - The version follows semver: `MAJOR.MINOR.PATCH`.
121
+ - Always confirm the bump type with the user before making changes.
122
+ - If `make agent-check` fails, the release is blocked — help the user fix the issues rather than skipping the checks.
123
+ - The CI gates a `release/vX.Y.Z` → `main` PR with:
124
+ - `version-check.yml` — the `pyproject.toml` version must match the `release/vX.Y.Z` branch name.
125
+ - `changelog-check.yml` — `CHANGELOG.md` must contain a `## [vX.Y.Z] -` entry for the new version.
126
+ - `package-check.yml` — `uv.lock` must be in sync with `pyproject.toml` (`uv lock --locked`).
127
+ - `tests-check.yml` — the test matrix must pass on every supported Python version (3.10 through 3.14).
128
+ - `lint-check.yml` — ruff format, ruff lint, pyright, and mypy merge checks across the same Python matrix (the same gates as `make agent-check`).
129
+ - `guard-branches.yml` — only `release/vX.Y.Z` branches may target `main`.
130
+ - `cla.yml` — the PR author must have signed the Pipelex CLA (maintainers are allow-listed; an external first-time author will be prompted to sign before the PR can merge).
131
+ - All checks must pass for the PR to be mergeable, so getting the changelog, version, and lockfile right is critical.
132
+ - **Pre-release versions are not supported through this flow.** Unlike `mthds-python`, this repo's `guard-branches.yml` (`gate-main`) and `version-check.yml` both match the exact regex `release/v[0-9]+\.[0-9]+\.[0-9]+` — a PEP 440 suffix (`a`/`b`/`rc`, e.g. `0.2.0rc1`) on a `release/v0.2.0rc1` branch would be **rejected** by the branch guard even though `publish.yml` can detect pre-releases. Stick to strict three-segment versions for the `release/vX.Y.Z` → `main` flow; raise it with the user if they ask for a pre-release.
133
+ - Today's date for the changelog entry: use the current date in `YYYY-MM-DD` format.
@@ -0,0 +1,32 @@
1
+ name: Changelog Version Check
2
+
3
+ on:
4
+ pull_request:
5
+ branches:
6
+ - main
7
+ types: [opened, synchronize, reopened]
8
+
9
+ jobs:
10
+ check-changelog:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+
15
+ - name: Check Changelog Version
16
+ run: |
17
+ # Get version from pyproject.toml
18
+ VERSION=$(grep -m 1 'version = ' pyproject.toml | cut -d '"' -f 2)
19
+ echo "Version from pyproject.toml: $VERSION"
20
+
21
+ # Look for the version in the changelog
22
+ if ! grep -q "## \[v$VERSION\] -" CHANGELOG.md; then
23
+ echo "❌ Error: No changelog entry found for version v$VERSION"
24
+ echo ""
25
+ echo "The following versions are in the changelog:"
26
+ grep -E "^## \[v[0-9]+" CHANGELOG.md | head -10
27
+ echo ""
28
+ echo "Please add a changelog entry: ## [v$VERSION] - YYYY-MM-DD"
29
+ exit 1
30
+ else
31
+ echo "✅ Changelog entry found for version v$VERSION"
32
+ fi
@@ -0,0 +1,42 @@
1
+ name: "CLA Assistant bot"
2
+ on:
3
+ issue_comment:
4
+ types: [created]
5
+ pull_request_target:
6
+ types: [opened, closed, synchronize]
7
+
8
+ permissions:
9
+ actions: write
10
+ contents: read
11
+ pull-requests: write
12
+ statuses: write
13
+
14
+ jobs:
15
+ CLAAssistant:
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - name: Get GitHub App token
19
+ id: app-token
20
+ uses: actions/create-github-app-token@v3
21
+ with:
22
+ app-id: ${{ secrets.CLA_GH_APP_ID }}
23
+ private-key: ${{ secrets.CLA_GH_APP_PRIVATE_KEY }}
24
+ owner: Pipelex
25
+ repositories: |
26
+ cla-signatures
27
+ pipelex-sdk-python
28
+
29
+ - name: "CLA Assistant"
30
+ if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target'
31
+ uses: contributor-assistant/github-action@v2.6.1
32
+ env:
33
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
34
+ PERSONAL_ACCESS_TOKEN: ${{ steps.app-token.outputs.token }}
35
+ with:
36
+ path-to-signatures: "signatures/version1/cla.json"
37
+ path-to-document: "https://github.com/Pipelex/pipelex-sdk-python/blob/main/CLA.md"
38
+ branch: main
39
+ allowlist: lchoquel,thomashebrard,bot*
40
+ remote-organization-name: Pipelex
41
+ remote-repository-name: cla-signatures
42
+ signed-commit-message: "$contributorName has signed the CLA in $owner/$repo#$pullRequestNo"
@@ -0,0 +1,100 @@
1
+ name: Guard branch flow
2
+ on:
3
+ pull_request_target:
4
+ types: [opened, edited, synchronize, reopened]
5
+
6
+ jobs:
7
+ # ───────────────────────────────────────────────────────────────
8
+ # 1) Only release/vX.Y.Z → main
9
+ # ───────────────────────────────────────────────────────────────
10
+ gate-main:
11
+ if: github.event.pull_request.base.ref == 'main'
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - name: Verify source branch is a Release
15
+ env:
16
+ HEAD: ${{ github.event.pull_request.head.ref }}
17
+ run: |
18
+ echo "PR → main from $HEAD"
19
+ if [[ ! "$HEAD" =~ ^release\/v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
20
+ echo "::error::Only release/vX.Y.Z branches may merge into main."
21
+ exit 1
22
+ fi
23
+
24
+ # ───────────────────────────────────────────────────────────────
25
+ # 2) Only work-branches → release/vX.Y.Z, pre-release/vX.Y.Z..., or dev
26
+ # ───────────────────────────────────────────────────────────────
27
+ gate-release:
28
+ if: startsWith(github.event.pull_request.base.ref, 'release/v') || startsWith(github.event.pull_request.base.ref, 'pre-release/v') || github.event.pull_request.base.ref == 'dev'
29
+ runs-on: ubuntu-latest
30
+ steps:
31
+ - name: Verify source branch uses allowed prefix
32
+ env:
33
+ HEAD: ${{ github.event.pull_request.head.ref }}
34
+ run: |
35
+ echo "PR → ${{ github.event.pull_request.base.ref }} from $HEAD"
36
+ if [[ "$HEAD" == "dev" ]]; then
37
+ exit 0
38
+ fi
39
+ if [[ ! "$HEAD" =~ ^(fix|feature|refactor|chore|docs|ci-cd|changelog|codex)\/[A-Za-z0-9._\/\<\>\=\-]+$ ]]; then
40
+ echo "::error::Branch must start with fix/, feature/, refactor/, chore/, docs/, or ci-cd/."
41
+ exit 1
42
+ fi
43
+
44
+ # ───────────────────────────────────────────────────────────────
45
+ # 3) Prevent forks from editing your workflows
46
+ # ───────────────────────────────────────────────────────────────
47
+ protect-workflows:
48
+ runs-on: ubuntu-latest
49
+ # Least privilege: querying the author's permission and diffing the head only needs read.
50
+ permissions:
51
+ contents: read
52
+ steps:
53
+ # Trust must hinge on the author's EFFECTIVE repository permission, not author_association.
54
+ # author_association is a social label: an org MEMBER or a COLLABORATOR can hold read-only
55
+ # access, so an association allow-list would let a read-only insider's workflow edits slip
56
+ # past this guard. Resolve the real permission and treat only write/maintain/admin as trusted.
57
+ - name: Resolve author repository permission
58
+ id: perm
59
+ uses: actions/github-script@v7
60
+ with:
61
+ script: |
62
+ const username = context.payload.pull_request.user.login;
63
+ let data = { permission: 'none', role_name: 'none' };
64
+ try {
65
+ ({ data } = await github.rest.repos.getCollaboratorPermissionLevel({
66
+ owner: context.repo.owner,
67
+ repo: context.repo.repo,
68
+ username,
69
+ }));
70
+ } catch (error) {
71
+ // A 404 means the author is not a resolvable collaborator (deleted/renamed
72
+ // account, or no access) — treat as untrusted and let the workflow-diff
73
+ // check run. Re-throw anything else so a transient API failure fails closed.
74
+ if (error.status !== 404) throw error;
75
+ }
76
+ // The legacy `permission` field collapses roles: admin → "admin",
77
+ // maintain & write → "write", triage & read → "read", none → "none".
78
+ const trusted = data.permission === 'admin' || data.permission === 'write';
79
+ core.info(`Author ${username}: permission=${data.permission} role=${data.role_name} trusted=${trusted}`);
80
+ core.setOutput('trusted', trusted ? 'true' : 'false');
81
+
82
+ - name: Checkout code
83
+ if: steps.perm.outputs.trusted != 'true'
84
+ uses: actions/checkout@v4
85
+ with:
86
+ # In pull_request_target the default checkout is the BASE branch; without this the
87
+ # diff below would compare base-against-base and never see the fork's changes. We
88
+ # only fetch/diff/grep here — the untrusted head is never executed — so checking
89
+ # out the PR head SHA is safe.
90
+ ref: ${{ github.event.pull_request.head.sha }}
91
+ fetch-depth: 0
92
+ - name: Detect workflow changes
93
+ if: steps.perm.outputs.trusted != 'true'
94
+ run: |
95
+ git fetch origin "${{ github.event.pull_request.base.ref }}" --depth=1
96
+ CHANGED=$(git diff --name-only FETCH_HEAD HEAD | grep -E '^\.github/workflows/.*\.ya?ml$' || true)
97
+ if [ -n "$CHANGED" ]; then
98
+ echo "::error::External contributors may not modify workflow files: $CHANGED"
99
+ exit 1
100
+ fi
@@ -0,0 +1,65 @@
1
+ name: Lint check
2
+
3
+ on:
4
+ pull_request:
5
+
6
+ jobs:
7
+ # --------------------------------------------------------------------------
8
+ # 1. Matrix job — one runner *per* Python version
9
+ # --------------------------------------------------------------------------
10
+ lint:
11
+ name: Lint (${{ matrix.python-version }})
12
+ runs-on: ubuntu-latest
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
17
+ env:
18
+ VIRTUAL_ENV: ${{ github.workspace }}/.venv
19
+
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+
23
+ - name: Set up Python ${{ matrix.python-version }}
24
+ uses: actions/setup-python@v4
25
+ with:
26
+ python-version: ${{ matrix.python-version }}
27
+
28
+ - name: Check UV installation
29
+ run: make check-uv
30
+
31
+ - name: Verify UV installation
32
+ run: uv --version
33
+
34
+ - name: Install dependencies
35
+ run: PYTHON_VERSION=${{ matrix.python-version }} TEST_PROFILE=ci make install
36
+
37
+ - name: Run ruff format merge check
38
+ run: make merge-check-ruff-format
39
+
40
+ - name: Run ruff lint merge check
41
+ run: make merge-check-ruff-lint
42
+
43
+ - name: Run pyright merge check
44
+ run: make merge-check-pyright
45
+
46
+ - name: Run mypy merge check
47
+ run: make merge-check-mypy
48
+
49
+ # --------------------------------------------------------------------------
50
+ # 2. Aggregator job — the *single* required status check
51
+ # --------------------------------------------------------------------------
52
+ lint-all:
53
+ name: Lint (all versions)
54
+ runs-on: ubuntu-latest
55
+ needs: lint # wait for every matrix leg
56
+ if: always() # run even if one leg already failed
57
+
58
+ steps:
59
+ - name: Fail if any matrix leg failed
60
+ run: |
61
+ if [ "${{ needs.lint.result }}" != "success" ]; then
62
+ echo "::error::At least one Python version failed linting."
63
+ exit 1
64
+ fi
65
+ echo "✅ All Python versions passed lint checks."
@@ -0,0 +1,27 @@
1
+ name: package-check
2
+
3
+ on:
4
+ pull_request:
5
+
6
+ jobs:
7
+ uv-lock-check:
8
+ runs-on: ubuntu-latest
9
+ steps:
10
+ - name: Checkout code
11
+ uses: actions/checkout@v4
12
+
13
+ - name: Install uv
14
+ uses: astral-sh/setup-uv@v3
15
+ with:
16
+ enable-cache: true
17
+
18
+ - name: Check if uv.lock is up to date
19
+ run: |
20
+ uv lock --locked
21
+ if ! git diff --exit-code uv.lock; then
22
+ echo "❌ uv.lock is out of date!"
23
+ echo "Please run 'uv lock' to update the lock file."
24
+ exit 1
25
+ else
26
+ echo "✅ uv.lock is up to date!"
27
+ fi
@@ -0,0 +1,172 @@
1
+ name: Publish Python 🐍 distribution 📦 to PyPI
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ jobs:
9
+ build:
10
+ name: Build distribution 📦
11
+ runs-on: ubuntu-latest
12
+
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ with:
16
+ persist-credentials: false
17
+ - name: Set up Python
18
+ uses: actions/setup-python@v5
19
+ with:
20
+ python-version: "3.x"
21
+ - name: Install pypa/build
22
+ run: >-
23
+ python3 -m
24
+ pip install
25
+ build
26
+ --user
27
+ - name: Build a binary wheel and a source tarball
28
+ run: python3 -m build
29
+ - name: Store the distribution packages
30
+ uses: actions/upload-artifact@v4
31
+ with:
32
+ name: python-package-distributions
33
+ path: dist/
34
+
35
+ publish-to-pypi:
36
+ name: >-
37
+ Publish Python 🐍 distribution 📦 to PyPI
38
+ needs:
39
+ - build
40
+ runs-on: ubuntu-latest
41
+ environment:
42
+ name: pypi
43
+ url: https://pypi.org/p/pipelex-sdk
44
+ permissions:
45
+ id-token: write # IMPORTANT: mandatory for trusted publishing
46
+
47
+ steps:
48
+ - name: Download all the dists
49
+ uses: actions/download-artifact@v4
50
+ with:
51
+ name: python-package-distributions
52
+ path: dist/
53
+ - name: Publish distribution 📦 to PyPI
54
+ uses: pypa/gh-action-pypi-publish@release/v1
55
+
56
+ github-release:
57
+ name: >-
58
+ Create GitHub Release with Changelog
59
+ needs:
60
+ - build
61
+ - publish-to-pypi
62
+ runs-on: ubuntu-latest
63
+
64
+ permissions:
65
+ contents: write # IMPORTANT: mandatory for making GitHub Releases
66
+ id-token: write # IMPORTANT: mandatory for sigstore
67
+
68
+ steps:
69
+ - uses: actions/checkout@v4
70
+ with:
71
+ persist-credentials: false
72
+ - name: Extract version and detect pre-release
73
+ id: get_version
74
+ run: |
75
+ VERSION=$(grep -m 1 'version = ' pyproject.toml | cut -d '"' -f 2)
76
+ echo "VERSION=$VERSION" >> $GITHUB_ENV
77
+
78
+ # Detect if version is a pre-release (PEP 440: contains a, b, or rc)
79
+ if [[ "$VERSION" =~ (a|b|rc)[0-9]+$ ]]; then
80
+ echo "IS_PRERELEASE=true" >> $GITHUB_ENV
81
+ echo "Detected pre-release version: $VERSION"
82
+ else
83
+ echo "IS_PRERELEASE=false" >> $GITHUB_ENV
84
+ echo "Detected stable version: $VERSION"
85
+ fi
86
+ - name: Extract changelog notes for current version
87
+ id: get_changelog
88
+ run: |
89
+ VERSION="${{ env.VERSION }}"
90
+ echo "Extracting changelog for version v$VERSION"
91
+
92
+ # Find the start of the current version section
93
+ START_LINE=$(grep -n "## \[v$VERSION\] - " CHANGELOG.md | cut -d: -f1)
94
+
95
+ if [ -z "$START_LINE" ]; then
96
+ echo "Warning: No changelog entry found for version v$VERSION"
97
+ echo "CHANGELOG_NOTES=" >> $GITHUB_ENV
98
+ exit 0
99
+ fi
100
+
101
+ # Find the start of the next version section (previous version)
102
+ NEXT_VERSION_LINE=$(tail -n +$((START_LINE + 1)) CHANGELOG.md | grep -n "^## \[v.*\] - " | head -1 | cut -d: -f1)
103
+
104
+ if [ -z "$NEXT_VERSION_LINE" ]; then
105
+ # No next version found, extract from current version till end of file
106
+ CHANGELOG_CONTENT=$(tail -n +$START_LINE CHANGELOG.md)
107
+ else
108
+ # Extract content from current version header to before next version
109
+ END_LINE=$((START_LINE + NEXT_VERSION_LINE - 1))
110
+ CHANGELOG_CONTENT=$(sed -n "$START_LINE,$((END_LINE - 1))p" CHANGELOG.md)
111
+ fi
112
+
113
+ # Clean up the content but preserve the blank line after the header
114
+ # First, get the header line and add a blank line after it
115
+ HEADER_LINE=$(echo "$CHANGELOG_CONTENT" | head -1)
116
+ CONTENT_LINES=$(echo "$CHANGELOG_CONTENT" | tail -n +2 | sed '/^$/d' | sed 's/^[[:space:]]*//' | sed 's/[[:space:]]*$//')
117
+
118
+ # Combine header + blank line + content
119
+ CHANGELOG_CONTENT=$(printf "%s\n\n%s" "$HEADER_LINE" "$CONTENT_LINES")
120
+
121
+ # Escape for GitHub Actions
122
+ echo "CHANGELOG_NOTES<<EOF" >> $GITHUB_ENV
123
+ echo "$CHANGELOG_CONTENT" >> $GITHUB_ENV
124
+ echo "EOF" >> $GITHUB_ENV
125
+ - name: Download all the dists
126
+ uses: actions/download-artifact@v4
127
+ with:
128
+ name: python-package-distributions
129
+ path: dist/
130
+ - name: Sign the dists with Sigstore
131
+ uses: sigstore/gh-action-sigstore-python@v3.0.0
132
+ with:
133
+ inputs: >-
134
+ ./dist/*.tar.gz
135
+ ./dist/*.whl
136
+ - name: Create GitHub Release
137
+ env:
138
+ GITHUB_TOKEN: ${{ github.token }}
139
+ run: |
140
+ # Set pre-release flag if version is a pre-release
141
+ PRERELEASE_FLAG=""
142
+ TITLE_SUFFIX=""
143
+ if [ "$IS_PRERELEASE" = "true" ]; then
144
+ PRERELEASE_FLAG="--prerelease"
145
+ TITLE_SUFFIX=" (Pre-release)"
146
+ fi
147
+
148
+ if [ -n "$CHANGELOG_NOTES" ]; then
149
+ gh release create "v$VERSION" \
150
+ --repo "$GITHUB_REPOSITORY" \
151
+ --title "v$VERSION$TITLE_SUFFIX" \
152
+ --generate-notes \
153
+ --notes "$CHANGELOG_NOTES" \
154
+ $PRERELEASE_FLAG
155
+ else
156
+ gh release create "v$VERSION" \
157
+ --repo "$GITHUB_REPOSITORY" \
158
+ --title "v$VERSION$TITLE_SUFFIX" \
159
+ --generate-notes \
160
+ --notes "Release v$VERSION" \
161
+ $PRERELEASE_FLAG
162
+ fi
163
+ - name: Upload artifact signatures to GitHub Release
164
+ env:
165
+ GITHUB_TOKEN: ${{ github.token }}
166
+ # Upload to GitHub Release using the `gh` CLI.
167
+ # `dist/` contains the built packages, and the
168
+ # sigstore-produced signatures and certificates.
169
+ run: >-
170
+ gh release upload
171
+ "v$VERSION" dist/**
172
+ --repo "$GITHUB_REPOSITORY"
@@ -0,0 +1,53 @@
1
+ name: Tests check
2
+
3
+ on:
4
+ pull_request:
5
+
6
+ concurrency:
7
+ group: ${{ github.workflow }}-${{ github.head_ref }}
8
+ cancel-in-progress: true
9
+
10
+ jobs:
11
+ # --------------------------------------------------------------------------
12
+ # 1. Test matrix — one job per supported Python version
13
+ # --------------------------------------------------------------------------
14
+ matrix-test:
15
+ name: Tests (py${{ matrix.python-version }})
16
+ runs-on: ubuntu-latest
17
+ strategy:
18
+ fail-fast: false
19
+ matrix:
20
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
21
+ # Least privilege: this job runs untrusted PR code and never uses OIDC, so it must not
22
+ # be able to mint an id-token. Read-only contents is all the test steps need.
23
+ permissions:
24
+ contents: read
25
+ env:
26
+ VIRTUAL_ENV: ${{ github.workspace }}/.venv
27
+ ENV: dev
28
+ steps:
29
+ - uses: actions/checkout@v4
30
+
31
+ - name: Install dependencies
32
+ run: PYTHON_VERSION=${{ matrix.python-version }} make install
33
+
34
+ - name: Run tests
35
+ run: make gha-tests
36
+
37
+ # --------------------------------------------------------------------------
38
+ # 2. Aggregator job — the *single* required status check
39
+ # --------------------------------------------------------------------------
40
+ tests-all:
41
+ name: Tests (all)
42
+ runs-on: ubuntu-latest
43
+ needs: [matrix-test]
44
+ if: always()
45
+
46
+ steps:
47
+ - name: Fail if any test job failed
48
+ run: |
49
+ if [ "${{ needs.matrix-test.result }}" != "success" ]; then
50
+ echo "::error::At least one test job failed."
51
+ exit 1
52
+ fi
53
+ echo "All test jobs passed."