Skip to content

Commit 7c875bc

Browse files
authored
Merge pull request #43 from scientific-python/tweaks
Add way to opt out of the two years for spec0.
2 parents 9853bdb + 601ab05 commit 7c875bc

12 files changed

Lines changed: 599 additions & 230 deletions

‎.github/workflows/test_action.yaml‎

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,14 @@ jobs:
1919
runs-on: ubuntu-latest
2020
strategy:
2121
matrix:
22-
excluded_packages: ["", "numpy,\nscikit-learn"]
23-
name: "Run action with exclusions: ${{ matrix.excluded_packages }}"
22+
include:
23+
- excluded_packages: ""
24+
spec0_support_years: ""
25+
- excluded_packages: "numpy,\nscikit-learn"
26+
spec0_support_years: ""
27+
- excluded_packages: "ipython matplotlib networkx numpy pandas scikit-image scikit-learn scipy xarray zarr python"
28+
spec0_support_years: "3"
29+
name: "Run action with support years: '${{ matrix.spec0_support_years }}', exclusions: '${{ matrix.excluded_packages }}'"
2430
steps:
2531
- name: Checkout
2632
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
@@ -31,10 +37,16 @@ jobs:
3137
create_pr: false
3238
schedule_path: tests/test_data/test_schedule.json
3339
excluded_packages: ${{ matrix.excluded_packages }}
40+
spec0_support_years: ${{ matrix.spec0_support_years }}
3441
- name: Check dependency updates
3542
env:
3643
EXCLUDED_PACKAGES: ${{ matrix.excluded_packages }}
44+
SPEC0_SUPPORT_YEARS: ${{ matrix.spec0_support_years }}
3745
run: |
46+
if [ -n "$SPEC0_SUPPORT_YEARS" ]; then
47+
git diff --exit-code -- tests/test_data/pyproject.toml
48+
exit 0
49+
fi
3850
git diff -U0 -- tests/test_data/pyproject.toml > changes.diff
3951
grep -q '^+.*pandas' changes.diff
4052
if [ -n "$EXCLUDED_PACKAGES" ]; then

‎action.yaml‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,17 @@ inputs:
3535
description: "GitHub token with pull-requests write permission to create pull requests. Defaults to the built-in GITHUB_TOKEN."
3636
required: false
3737
update_all:
38-
description: "If set, also update all non-SPEC0 dependencies to versions released within the last N years (e.g., 2)."
38+
description: "If set, update PEP dependencies without an applicable schedule floor or explicit core-package support period to versions first released within the last N years (e.g., 2)."
3939
required: false
4040
default: ""
4141
excluded_packages:
4242
description: "Comma- or whitespace-separated package names to leave unchanged, including with update_all. Use python to exclude Python requirements."
4343
required: false
4444
default: ""
45+
spec0_support_years:
46+
description: "If set, compute SPEC 0 core-package floors from PyPI using this many years of support (e.g., 3). Python still follows the supplied schedule; exclusions take precedence."
47+
required: false
48+
default: ""
4549
runs:
4650
using: "composite"
4751
steps:
@@ -70,6 +74,7 @@ runs:
7074
PROJECT_FILE_NAME: ${{ inputs.project_file_name }}
7175
SCHEDULE_INPUT: ${{ inputs.schedule_path }}
7276
UPDATE_ALL: ${{ inputs.update_all }}
77+
SPEC0_SUPPORT_YEARS: ${{ inputs.spec0_support_years }}
7378
EXCLUDED_PACKAGES: ${{ inputs.excluded_packages }}
7479
run: |
7580
set -e
@@ -79,11 +84,14 @@ runs:
7984
SCHEDULE_PATH="${GITHUB_WORKSPACE}/schedule.json"
8085
fi
8186
echo "Updating ${PROJECT_FILE_NAME} using schedule ${SCHEDULE_PATH}"
82-
UPDATE_ALL_ARGS=()
87+
UPDATE_ARGS=()
8388
if [ -n "$UPDATE_ALL" ]; then
84-
UPDATE_ALL_ARGS=(--update-all "$UPDATE_ALL")
89+
UPDATE_ARGS+=(--update-all "$UPDATE_ALL")
90+
fi
91+
if [ -n "$SPEC0_SUPPORT_YEARS" ]; then
92+
UPDATE_ARGS+=(--spec0-support-years "$SPEC0_SUPPORT_YEARS")
8593
fi
86-
pixi run --manifest-path "${GITHUB_ACTION_PATH}/pyproject.toml" update-dependencies "${GITHUB_WORKSPACE}/${PROJECT_FILE_NAME}" "$SCHEDULE_PATH" --excluded-packages "$EXCLUDED_PACKAGES" "${UPDATE_ALL_ARGS[@]}"
94+
pixi run --manifest-path "${GITHUB_ACTION_PATH}/pyproject.toml" update-dependencies "${GITHUB_WORKSPACE}/${PROJECT_FILE_NAME}" "$SCHEDULE_PATH" --excluded-packages "$EXCLUDED_PACKAGES" "${UPDATE_ARGS[@]}"
8795
- name: Changes
8896
id: changes
8997
shell: bash

‎readme.md‎

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ name: Update SPEC 0 dependencies
1414

1515
on:
1616
schedule:
17-
# Day 3 of each quarter. Allows one day buffer after the quarterly schedule release on day 1
17+
# Day 3 of each quarter.
18+
Allows one day buffer after the quarterly schedule release on day 1
1819
- cron: "0 0 3 1,4,7,10 *"
1920
workflow_dispatch:
2021

@@ -40,25 +41,27 @@ The built-in `GITHUB_TOKEN` is used by default as long as the workflow has `pull
4041

4142
### Parameters
4243

43-
| Input | Required | Default | Description |
44-
| ------------------- | -------- | ------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
45-
| `token` | no | `GITHUB_TOKEN` | Token with `pull-requests: write` permission to open PRs |
46-
| `project_file_name` | no | `pyproject.toml` | Path to the file to update, relative to repository root |
47-
| `schedule_path` | no | — | Path to a custom `schedule.json`, relative to repository root. Uses the latest release if unset |
48-
| `target_branch` | no | `main` | Branch to open the PR against |
49-
| `create_pr` | no | `true` | Set to `false` for a dry run |
50-
| `pr_title` | no | `chore: Drop support for unsupported packages conform SPEC 0` | Title of the opened PR |
51-
| `commit_msg` | no | `chore: Drop support for unsupported packages conform SPEC 0` | Commit message for the version update commit |
52-
| `update_all` | no | — | If set to a number N, also update non-SPEC0 dependencies to versions released within the last N years (e.g. `2`) |
53-
| `excluded_packages` | no | — | Comma- or whitespace-separated package names to leave unchanged, including with `update_all` |
44+
| Input | Required | Default | Description |
45+
| --------------------- | -------- | ------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
46+
| `token` | no | `GITHUB_TOKEN` | Token with `pull-requests: write` permission to open PRs |
47+
| `project_file_name` | no | `pyproject.toml` | Path to the file to update, relative to repository root |
48+
| `schedule_path` | no | — | Path to a custom `schedule.json`, relative to repository root. Uses the latest release if unset |
49+
| `target_branch` | no | `main` | Branch to open the PR against |
50+
| `create_pr` | no | `true` | Set to `false` for a dry run |
51+
| `pr_title` | no | `chore: Drop support for unsupported packages conform SPEC 0` | Title of the opened PR |
52+
| `commit_msg` | no | `chore: Drop support for unsupported packages conform SPEC 0` | Commit message for the version update commit |
53+
| `update_all` | no | — | If set to N years, update PEP dependencies without a schedule floor or custom core-package policy to the oldest stable version first released within that window |
54+
| `spec0_support_years` | no | — | Override the support period for SPEC 0 core packages using current PyPI release history (e.g. `3`); Python still follows the supplied schedule |
55+
| `excluded_packages` | no | — | Comma- or whitespace-separated package names to leave unchanged, including with `update_all` |
5456

5557
For examples of before/after see [tests/test_data/pyproject.toml](./tests/test_data/pyproject.toml) and [tests/test_data/pyproject_updated.toml](./tests/test_data/pyproject_updated.toml).
5658

5759
SPEC 0 packages include `ipython`, `matplotlib`, `networkx`, `numpy`, `pandas`, `scikit-image`, `scikit-learn`, `scipy`, `xarray`, and `zarr`.
5860

5961
### Excluding packages
6062

61-
To keep a package's lower bound as-is, for example to stay compatible with NumPy 1.26 while everything else updates, list it in `excluded_packages` before the action raises its bound (bounds are never lowered). Separate names with commas or whitespace; `python` excludes the Python requirement:
63+
To keep a package's lower bound as-is, for example to stay compatible with NumPy 1.26 while everything else updates, list it in `excluded_packages` before the action raises its bound (bounds are never lowered).
64+
Separate names with commas or whitespace; `python` excludes the Python requirement:
6265

6366
```yaml
6467
with:
@@ -68,18 +71,46 @@ with:
6871
python
6972
```
7073

71-
Exclusions win over the schedule and `update_all`. The CLI takes the same value via `--excluded-packages`.
74+
Exclusions win over the schedule, `spec0_support_years`, and `update_all`, and excluded packages cause no PyPI lookup.
75+
The CLI takes the same value via `--excluded-packages`.
76+
77+
### Changing the core-package support period
78+
79+
The supplied schedule gives SPEC 0 packages two years of support.
80+
To use three years for those packages while updating other dependencies independently:
81+
82+
```yaml
83+
with:
84+
spec0_support_years: 3
85+
update_all: 2
86+
```
87+
88+
For each release (`X.Y.0`; pre-, post-, and patch releases are ignored), support ends at the start of the quarter containing its release date plus the period, and the floor moves to the next release.
89+
The schedule generator and custom support periods use the earliest PyPI upload of a release, whether a source distribution or wheel; later uploads do not reset its age.
90+
Floors come from the PyPI release history, so any explicit value, can differ from a published schedule snapshot.
91+
Python always follows the schedule.
92+
93+
Precedence is `excluded_packages`, then `spec0_support_years` for SPEC 0 packages, then the schedule, then `update_all` for the remaining PEP dependencies.
94+
If PyPI cannot be reached, the affected dependency stays unchanged with a warning rather than falling back to the schedule or `update_all`.
95+
96+
The CLI takes `--spec0-support-years 3` and the Python API `spec0_support_years=3`.
97+
Extras such as `xarray[io]>=2026.5.1` and environment markers are preserved when bounds change.
7298

7399
## Limitations
74100

75-
1. The action only tightens lower bounds and leaves upper bounds untouched. An update can produce an unsolvable environment — for example `numpy = ">=1.25.0,<2"` becomes `numpy = ">=2.0.0,<2"`. Keeping the environment solvable is out of scope; adjust upper bounds manually if needed.
101+
1. The action only tightens lower bounds and leaves upper bounds untouched.
102+
A proposed floor that conflicts with an existing constraint is skipped; for example, `numpy = ">=1.25.0,<2"` stays unchanged when the proposed floor is `2.0.0`.
103+
It does not solve the full dependency graph or guarantee a compatible environment.
76104
2. Only `pyproject.toml` is currently supported.
105+
This includes Pixi tables within the file; standalone Conda environment files are unsupported.
106+
Conda-only version expressions such as `>=1.26|>=2.0` are left unchanged.
77107

78108
## Maintainer notes
79109

80110
### Releasing a new action version
81111

82-
Action versions are **git tags only**, do not create a GitHub Release for them. GitHub Releases in this repository are reserved for the quarterly schedule data.
112+
Action versions are **git tags only**, do not create a GitHub Release for them.
113+
GitHub Releases in this repository are reserved for the quarterly schedule data.
83114

84115
```bash
85116
git tag v1.x
@@ -88,13 +119,15 @@ git push origin v1.x
88119

89120
### Schedule releases
90121

91-
The SPEC 0 schedule (`schedule.json` and `schedule.md`) is published as a GitHub Release quarterly by the [Update SPEC 0 schedule](./.github/workflows/update_schedule.yml) workflow. Releases are tagged `schedule-YYYY-QN` (e.g. `schedule-2026-Q2`).
122+
The SPEC 0 schedule (`schedule.json` and `schedule.md`) is published as a GitHub Release quarterly by the [Update SPEC 0 schedule](./.github/workflows/update_schedule.yml) workflow.
123+
Releases are tagged `schedule-YYYY-QN` (e.g. `schedule-2026-Q2`).
92124

93125
The action always fetches `schedule.json` from the **latest** GitHub Release in this repository, which will always be a schedule release as long as action versions are never published as releases.
94126

95127
#### Bootstrap
96128

97-
Before the first quarterly schedule release exists, the action will fail. To create the initial release, trigger the workflow manually:
129+
Before the first quarterly schedule release exists, the action will fail.
130+
To create the initial release, trigger the workflow manually:
98131

99132
1. Go to **Actions → Update SPEC 0 schedule**
100133
2. Click **Run workflow**

‎run_spec0_update.py‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,14 @@
2222
type=float,
2323
default=None,
2424
metavar="YEARS",
25-
help="Also update all non-SPEC0 dependencies to versions released within the last YEARS years (e.g., 2).",
25+
help="Update PEP dependencies without an applicable schedule floor or explicit core-package support period to versions first released within the last YEARS years (e.g., 2).",
26+
)
27+
parser.add_argument(
28+
"--spec0-support-years",
29+
type=float,
30+
default=None,
31+
metavar="YEARS",
32+
help="Compute SPEC 0 core-package floors from PyPI using YEARS of support; Python still follows the supplied schedule.",
2633
)
2734
parser.add_argument(
2835
"--excluded-packages",
@@ -47,6 +54,7 @@
4754
project_data,
4855
schedule_data,
4956
update_all=args.update_all,
57+
spec0_support_years=args.spec0_support_years,
5058
excluded_packages=args.excluded_packages.replace(",", " ").split(),
5159
)
5260
write_toml(toml_path, project_data)

0 commit comments

Comments
 (0)