-
Notifications
You must be signed in to change notification settings - Fork 2
feat: parse project toml files manually #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 33 commits
ba3ce17
30e7fa7
0cdba7a
2a2b9c0
e6912aa
7fee19f
62b17f2
7ae79dc
9cc5d79
190e210
8319244
a5894a9
221ece3
f027d82
2b3570f
52acc1b
4cf09ab
890e1fa
fd06db1
0e0cad5
17d74fa
943e9a0
8359b1f
533ecca
4885cc3
a103c2f
5764a23
11f4297
1edb4ec
30042b8
fa31983
cad5872
999a1f3
d3f3bb6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# SCM syntax highlighting & preventing 3-way merges | ||
pixi.lock merge=binary linguist-language=YAML linguist-generated=true |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
name: Generate release schedule artifacts | ||
on: | ||
schedule: | ||
# At 00:00 on day-of-month 1 in every 3rd month. (i.e. every quarter) | ||
- cron: "0 0 1 */3 *" | ||
# On demand | ||
workflow_dispatch: | ||
|
||
jobs: | ||
create-artifacts: | ||
runs-on: ubuntu-latest | ||
permissions: | ||
contents: write | ||
steps: | ||
- name: Checkout | ||
uses: actions/checkout@v4 | ||
with: | ||
# We're going to make a tag that we can we release so we'll need the full history for that | ||
fetch-depth: 0 | ||
- name: Set up Python | ||
uses: actions/setup-python@v5 | ||
|
||
- uses: prefix-dev/[email protected] | ||
with: | ||
pixi-version: "v0.49.0" | ||
environments: schedule | ||
- name: Run spec_zero_versions.py | ||
run: | | ||
pixi run -e schedule generate_schedule | ||
- name: setup git | ||
run: | | ||
# git will complain if we don't do this first | ||
git config user.name "github-actions[bot]" | ||
git config user.email "github-actions[bot]@users.noreply.github.com" | ||
|
||
- name: determine tag name | ||
id: tag_name | ||
run: | | ||
echo "TAG_NAME=$(date '+%Y-Q%q')" >> "$GITHUB_OUTPUT" | ||
|
||
- name: Publish github release | ||
uses: softprops/action-gh-release@v2 | ||
env: | ||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
with: | ||
generate_release_notes: true | ||
tag_name: ${{ steps.tag_name.outputs.TAG_NAME }} | ||
make_latest: true | ||
files: | | ||
schedule.md | ||
chart.md | ||
schedule.json |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
name: Run the update test suite | ||
on: | ||
push: | ||
branches: main | ||
pull_request: | ||
branches: main | ||
# On demand | ||
workflow_dispatch: | ||
|
||
jobs: | ||
run-tests: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Checkout | ||
uses: actions/checkout@v4 | ||
|
||
- uses: prefix-dev/[email protected] | ||
with: | ||
pixi-version: "v0.49.0" | ||
environments: dev | ||
- run: | | ||
pixi run test |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,6 @@ | ||
.history | ||
chart.md | ||
schedule.md | ||
schedule.json | ||
# pixi environments | ||
.pixi/* | ||
!.pixi/config.toml | ||
pixi.lock | ||
__pycache__ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,72 @@ | ||
name: "Generate SPEC-0000 Data" | ||
description: "Based on the current SPEC 0 schedule, generate a tarball with the latest versions of all packages." | ||
author: Scientific Python Developers | ||
inputs: | ||
target_branch: | ||
description: "Target branch for the pull request" | ||
required: true | ||
default: "main" | ||
project_file_name: | ||
description: "The filename to the project file that lists your dependencies, relative to the repository root. Defaults to 'pyproject.toml' Curretnly only pyproject.toml is supported but others may be added." | ||
required: true | ||
default: "pyproject.toml" | ||
create_pr: | ||
description: "Whether the action should open a PR or not. If this is set to false, this action should have no effect. This is mainly useful for testing" | ||
required: true | ||
default: true | ||
schedule_path: | ||
description: "Path to the schedule.json file. If it does not exist yet, it will be downloaded from the spec0-action repository latest release" | ||
default: schedule.json | ||
token: | ||
description: "GitHub token with repo permissions to create pull requests" | ||
required: true | ||
|
||
runs: | ||
using: "composite" | ||
steps: | ||
- name: Set up Python | ||
uses: actions/setup-python@v5 | ||
- name: Checkout code | ||
uses: actions/checkout@v4 | ||
with: | ||
python-version: "3.13" | ||
- name: Install dependencies | ||
fetch-depth: 0 | ||
|
||
- name: Set up Git | ||
shell: bash | ||
run: | | ||
git config user.name "Scientific Python [bot]" | ||
git config user.email "[email protected]" | ||
|
||
- name: Download schedule artifact | ||
shell: bash | ||
env: | ||
SCHEDULE_FILE: ${{ inputs.schedule_path }} | ||
run: | | ||
pip install -r requirements.txt | ||
- name: Run spec_zero_versions.py | ||
if [ ! -f "$SCHEDULE_FILE" ]; then | ||
gh release download -R "scientific-python/spec0-action" --pattern schedule.json -O "$SCHEDULE_FILE" | ||
fi | ||
|
||
- uses: prefix-dev/[email protected] | ||
name: Setup pixi | ||
with: | ||
environments: >- | ||
update | ||
activate-environment: update | ||
pixi-version: v0.49.0 | ||
manifest-path: ${{github.action_path}}/pyproject.toml | ||
|
||
- name: Run Update script | ||
shell: bash | ||
run: | | ||
python spec_zero_versions.py | ||
- name: Upload files as an artifact | ||
uses: actions/upload-artifact@v4 | ||
python ${{github.action_path}}/spec0-update.py "${{github.workspace}}/${{inputs.project_file_name}}" "${{inputs.schedule_path}}" | ||
# let's cleanup after ourselves so it doesn't end up in the PR | ||
rm "${{inputs.schedule_path}}" | ||
|
||
- name: Create Pull Request | ||
if: ${{ inputs.create_pr == 'true' }} | ||
uses: peter-evans/create-pull-request@v6 | ||
with: | ||
name: spec-zero-versions | ||
path: | | ||
schedule.json | ||
schedule.md | ||
chart.md | ||
token: ${{ inputs.token }} | ||
commit-message: "chore: Drop support for unsupported packages conform SPEC 0" | ||
title: "Drop support for unsupported packages conform SPEC 0" | ||
body: "This PR was created automatically" | ||
base: ${{ inputs.target_branch }} | ||
branch: update-spec-0-dependencies-${{ github.run_id }} |
savente93 marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
[project] | ||
authors = [{ name = "Scientific Python Developers" }] | ||
name = "spec0-action" | ||
requires-python = ">= 3.11" | ||
version = "0.1.0" | ||
dependencies = ["packaging>=25.0"] | ||
|
||
[build-system] | ||
build-backend = "hatchling.build" | ||
requires = ["hatchling"] | ||
|
||
[tool.pixi.workspace] | ||
channels = ["conda-forge"] | ||
platforms = ["linux-64"] | ||
|
||
[tool.pixi.feature.update.pypi-dependencies] | ||
spec0-action = { path = ".", editable = true } | ||
|
||
[tool.pixi.feature.test.tasks] | ||
test = { cmd = ["pytest", "-vvv"] } | ||
|
||
[tool.pixi.feature.schedule.tasks] | ||
generate-schedule = { cmd = ["python", "generate_schedule.py"] } | ||
|
||
[tool.pixi.feature.test.dependencies] | ||
pytest = "*" | ||
|
||
[tool.pixi.feature.update.dependencies] | ||
tomlkit = ">=0.13.3,<0.14" | ||
|
||
[tool.pixi.feature.schedule.dependencies] | ||
pandas = "*" | ||
requests = "*" | ||
|
||
[tool.pixi.environments] | ||
dev = ["test", "schedule", "update"] | ||
schedule = ["schedule"] | ||
update = ["update"] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,63 +1,51 @@ | ||
# SPEC-0 Versions Action | ||
|
||
This repository contains a GitHub Action to generate the files required for the SPEC-0 documentation. | ||
This repository contains a Github Action to update Python dependencies such that they conform to the SPEC 0 support schedule. | ||
It also contains released versions of the schedule in various formats that that action can use to open PRs in your repository. | ||
|
||
## Using the action | ||
|
||
To use the action you can copy the yaml below, and paste it into `.github/workflows/update-spec-0.yaml`. | ||
The arguments below are filled with their default value, in most cases you won't have to fill them. | ||
All except for `token` are optional. | ||
|
||
Whenever the action is triggered it will open a PR in your repository that will update the dependencies of SPEC 0 to the new lower bound. | ||
For this you will have to provide it with a PAT that has write permissions in the `contents` and `pull request` scopes. | ||
Please refer to the GitHub documentation for instructions on how to do this [here](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens). | ||
|
||
|
||
```yaml | ||
name: Generate spec-zero data | ||
name: Update SPEC 0 dependencies | ||
|
||
on: | ||
push: | ||
branches: | ||
- main | ||
workflow_dispatch: | ||
schedule: | ||
# At 00:00 on day-of-month 2 in every 3rd month. (i.e. every quarter) | ||
# Releases should happen on the first day of the month in scientific-python/spec-zero-tools | ||
# so allow one day as a buffer to avoid timing issues | ||
- cron: "0 0 2 */3 *" | ||
|
||
permissions: | ||
contents: write | ||
pull-requests: write | ||
|
||
jobs: | ||
devstats-query: | ||
update: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: scientific-python/spec0-action@main | ||
- uses: scientific-python/spec0-action@v1 | ||
with: | ||
token: ${{ secrets.GH_PAT }} | ||
project_file_name: "pyproject.toml" | ||
target_branch: 'main' | ||
``` | ||
|
||
The above would produce an artifact named `spec-zero-versions`, the following files: `schedule.yaml`,`schedule.md` and `chart.md`. | ||
|
||
To help projects stay compliant with SPEC-0, we provide a `schedule.json` file that can be used by CI systems to determine new version boundaries. | ||
The structure of the file is as follows: | ||
It should update any of the packages listed in the `dependency`, or `tool.pixi.*` tables. | ||
|
||
```json | ||
[ | ||
{ | ||
"start_date": "iso8601_timestamp", | ||
"packages": { | ||
"package_name": "version" | ||
} | ||
} | ||
] | ||
``` | ||
## Limitations | ||
|
||
All information in the json file is in a string format that should be easy to use. | ||
The date is the first timestamp of the relevant quarter. | ||
Thus a workflow for using this file could be: | ||
1. Since this action simply parses the toml to do the upgrade and leaves any other bounds intact, it is possible that the environment of the PR becomes unsolvable. | ||
For example if you have a numpy dependency like so: `numpy = ">=1.25.0,<2"` this will get updated in the PR to `numpy = "2.0.0,<2"` which is infeasible. | ||
Keeping the resulting environment solvable is outside the scope of this action, so you might have to be adjusted manually. | ||
2. Currently only `pyproject.toml` is supported by this action, though other manifest files could be considered upon request. | ||
|
||
1. Fetch `schedule.json` | ||
2. Determine maximum date that is smaller than current date | ||
3. Update packages listed with new minimum versions | ||
|
||
You can obtain the new versions you should set by using this `jq` expression: | ||
|
||
```sh | ||
jq 'map(select(.start_date |fromdateiso8601 |tonumber < now))| sort_by("start_date") | reverse | .[0].packages ' schedule.json | ||
``` | ||
|
||
If you use a package manager like pixi you could update the dependencies with a bash script like this (untested): | ||
|
||
```sh | ||
curl -Ls -o schedule.json https://raw.githubusercontent.com/scientific-python/specs/main/spec-0000/schedule.json | ||
for line in $(jq 'map(select(.start_date |fromdateiso8601 |tonumber < now))| sort_by("start_date") | reverse | .[0].packages | to_entries | map(.key + ":" + .value)[]' --raw-output schedule.json); do | ||
package=$(echo "$line" | cut -d ':' -f 1) | ||
version=$(echo "$line" | cut -d ':' -f 2) | ||
if pixi list -x "^$package" &>/dev/null| grep "No packages" -q; then | ||
pixi add "$package>=$version"; | ||
fi | ||
done | ||
``` |
This file was deleted.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this committed to the repo? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because it has to be part of the release that the action will check against. We could generate the schedule from scratch every time, but then you lose a paper trail of what was actually in it, so I personally think this solution is better. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
[{"packages": {"ipython": "8.8.0", "numpy": "1.25.0", "python": "3.11", "scikit-learn": "1.3.0", "xarray": "2023.1.0"}, "start_date": "2024-10-01T00:00:00Z"}, {"packages": {"ipython": "8.13.0", "matplotlib": "3.8.0", "networkx": "3.1", "scikit-image": "0.21.0", "scipy": "1.11.0", "xarray": "2023.4.0", "zarr": "2.15.0"}, "start_date": "2025-01-01T00:00:00Z"}, {"packages": {"ipython": "8.15.0", "networkx": "3.2", "numpy": "1.26.0", "pandas": "2.1.0", "scikit-image": "0.22.0", "scikit-learn": "1.4.0", "scipy": "1.12.0", "xarray": "2023.7.0", "zarr": "2.16.0"}, "start_date": "2025-04-01T00:00:00Z"}, {"packages": {"ipython": "8.17.0", "matplotlib": "3.9.0", "numpy": "2.0.0", "pandas": "2.2.0", "xarray": "2023.10.0", "zarr": "2.17.0"}, "start_date": "2025-07-01T00:00:00Z"}, {"packages": {"ipython": "8.20.0", "networkx": "3.3", "python": "3.12", "scikit-image": "0.23.0", "xarray": "2024.1.0"}, "start_date": "2025-10-01T00:00:00Z"}, {"packages": {"ipython": "8.24.0", "pandas": "2.3.0", "scikit-learn": "1.5.0", "scipy": "1.13.0", "xarray": "2024.5.0", "zarr": "2.18.0"}, "start_date": "2026-01-01T00:00:00Z"}, {"packages": {"ipython": "8.27.0", "matplotlib": "3.10.0", "networkx": "3.4", "numpy": "2.1.0", "scikit-image": "0.25.0", "scikit-learn": "1.6.0", "scipy": "1.15.0", "xarray": "2024.7.0", "zarr": "3.0.0"}, "start_date": "2026-04-01T00:00:00Z"}, {"packages": {"ipython": "8.28.0", "numpy": "2.2.0", "xarray": "2024.10.0"}, "start_date": "2026-07-01T00:00:00Z"}, {"packages": {"ipython": "8.32.0", "networkx": "3.5", "numpy": "2.3.0", "python": "3.13", "scikit-learn": "1.7.0", "xarray": "2025.1.0"}, "start_date": "2026-10-01T00:00:00Z"}, {"packages": {"ipython": "9.1.0", "scipy": "1.16.0", "xarray": "2025.4.0", "zarr": "3.1.0"}, "start_date": "2027-01-01T00:00:00Z"}, {"packages": {"ipython": "9.4.0", "xarray": "2025.7.0"}, "start_date": "2027-04-01T00:00:00Z"}] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
from spec0_action import update_pyproject_toml, read_toml, write_toml, read_schedule | ||
from pathlib import Path | ||
from argparse import ArgumentParser | ||
|
||
|
||
if __name__ == '__main__': | ||
|
||
parser = ArgumentParser( | ||
prog='spec_zero_update', | ||
description='A script to update your project dependencies to be in line with the scientific python SPEC 0 support schedule', | ||
) | ||
|
||
parser.add_argument('toml_path', default="pyproject.toml", help="Path to the project file that lists the dependencies. defaults to 'pyproject.toml'.") | ||
parser.add_argument('schedule_path', default="schedule.json", help="Path to the schedule json payload. defaults to 'schedule.json'") | ||
|
||
args = parser.parse_args() | ||
|
||
toml_path = Path(args.toml_path) | ||
schedule_path = Path(args.schedule_path) | ||
|
||
if not toml_path.exists(): | ||
raise ValueError(f"{toml_path} was supplied as path to project file but it did not exist") | ||
|
||
project_data = read_toml(toml_path) | ||
schedule_data = read_schedule(schedule_path) | ||
update_pyproject_toml(project_data, schedule_data) | ||
|
||
write_toml(toml_path, project_data) |
Uh oh!
There was an error while loading. Please reload this page.