openapi-client-codegen 0.1.13__tar.gz → 0.1.14.dev36667872276__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 (41) hide show
  1. openapi_client_codegen-0.1.14.dev36667872276/.github/workflows/ci-cd.yml +98 -0
  2. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/AGENTS.md +2 -2
  3. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/PKG-INFO +1 -1
  4. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/pyproject.toml +4 -3
  5. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/release-devkit.yaml +1 -1
  6. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/src/openapi_client_codegen/cli.py +15 -1
  7. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/tests/test_cli.py +36 -0
  8. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/uv.lock +57 -3
  9. openapi_client_codegen-0.1.13/.github/workflows/ci.yml +0 -26
  10. openapi_client_codegen-0.1.13/.github/workflows/release.yml +0 -31
  11. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/.gitattributes +0 -0
  12. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/.gitignore +0 -0
  13. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/.python-version +0 -0
  14. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/CLAUDE.md +0 -0
  15. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/LICENSE +0 -0
  16. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/NOTICE +0 -0
  17. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/README.md +0 -0
  18. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/ruff.base.toml +0 -0
  19. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/ruff.toml +0 -0
  20. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/src/openapi_client_codegen/__init__.py +0 -0
  21. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/src/openapi_client_codegen/_data/configs/csharp.json +0 -0
  22. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/src/openapi_client_codegen/_data/configs/python.json +0 -0
  23. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/src/openapi_client_codegen/_data/openapi-generator-ignore +0 -0
  24. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/src/openapi_client_codegen/_data/templates-patches/csharp/0001-csharp-remove-errant-comma.patch +0 -0
  25. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/src/openapi_client_codegen/_data/templates-patches/csharp/0002-csharp-models-required-only-constructors.patch +0 -0
  26. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/src/openapi_client_codegen/_data/templates-patches/csharp/0003-csharp-httpclient-use-defaultT.patch +0 -0
  27. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/src/openapi_client_codegen/_data/templates-patches/csharp/0004-csharp-support-hybrid-multipart-payloads.patch +0 -0
  28. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/src/openapi_client_codegen/_data/templates-patches/csharp/0006-csharp-streaming-download-response.patch +0 -0
  29. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/src/openapi_client_codegen/_data/templates-patches/csharp/0007-csharp-model-default-initializers.patch +0 -0
  30. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/src/openapi_client_codegen/_data/templates-patches/csharp/0008-csharp-redirect-documentation-file.patch +0 -0
  31. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/src/openapi_client_codegen/client.py +0 -0
  32. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/src/openapi_client_codegen/downgrade.py +0 -0
  33. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/src/openapi_client_codegen/orchestrator.py +0 -0
  34. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/src/openapi_client_codegen/py.typed +0 -0
  35. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/src/openapi_client_codegen/resources.py +0 -0
  36. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/src/openapi_client_codegen/templates.py +0 -0
  37. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/src/openapi_client_codegen/unity.py +0 -0
  38. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/tests/test_config.py +0 -0
  39. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/tests/test_downgrade.py +0 -0
  40. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/tests/test_orchestrate.py +0 -0
  41. {openapi_client_codegen-0.1.13 → openapi_client_codegen-0.1.14.dev36667872276}/tests/test_unity.py +0 -0
@@ -0,0 +1,98 @@
1
+ name: CI/CD
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ push:
6
+ branches: [main, dev]
7
+ pull_request:
8
+ branches: [dev]
9
+
10
+ # Cancel superseded runs on the same branch, except main — where publish-stable
11
+ # must never die mid-release (overlapping release merges queue instead).
12
+ concurrency:
13
+ group: ci-${{ github.ref }}
14
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ env:
20
+ RELEASE_DEVKIT_VERSION: 0.1.25
21
+
22
+ jobs:
23
+ check:
24
+ if: github.ref != 'refs/heads/main'
25
+ runs-on: ubuntu-latest
26
+ steps:
27
+ - uses: actions/checkout@v5
28
+ with:
29
+ fetch-depth: 0
30
+ fetch-tags: true
31
+ persist-credentials: false
32
+ - uses: astral-sh/setup-uv@v7
33
+ with:
34
+ enable-cache: true
35
+ - run: uv run preflight-python
36
+ - shell: bash
37
+ run: uvx --from release-devkit==${{ env.RELEASE_DEVKIT_VERSION }} publish-stable --dry-run
38
+
39
+ ensure-release-pr:
40
+ if: github.event_name == 'push' && github.ref == 'refs/heads/dev'
41
+ runs-on: ubuntu-latest
42
+ permissions:
43
+ contents: write
44
+ pull-requests: write
45
+ env:
46
+ GH_TOKEN: ${{ github.token }}
47
+ steps:
48
+ - uses: actions/checkout@v5
49
+ with:
50
+ fetch-depth: 0
51
+ fetch-tags: true
52
+ persist-credentials: false
53
+ - uses: astral-sh/setup-uv@v7
54
+ - shell: bash
55
+ run: uvx --from release-devkit==${{ env.RELEASE_DEVKIT_VERSION }} ensure-release-pr
56
+
57
+ publish-stable:
58
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
59
+ runs-on: ubuntu-latest
60
+ environment: release
61
+ permissions:
62
+ contents: write
63
+ id-token: write
64
+ env:
65
+ GH_TOKEN: ${{ github.token }}
66
+ steps:
67
+ - uses: actions/checkout@v5
68
+ with:
69
+ ref: main
70
+ fetch-depth: 0
71
+ fetch-tags: true
72
+ - uses: astral-sh/setup-uv@v7
73
+ - shell: bash
74
+ run: uvx --from release-devkit==${{ env.RELEASE_DEVKIT_VERSION }} publish-stable
75
+ env:
76
+ NPM_CONFIG_LOGLEVEL: verbose
77
+
78
+ publish-dev:
79
+ if: github.event_name == 'push' && github.ref == 'refs/heads/dev'
80
+ needs: [check]
81
+ runs-on: ubuntu-latest
82
+ environment: release
83
+ permissions:
84
+ contents: read
85
+ id-token: write
86
+ env:
87
+ GH_TOKEN: ${{ github.token }}
88
+ steps:
89
+ - uses: actions/checkout@v5
90
+ with:
91
+ fetch-depth: 0
92
+ fetch-tags: true
93
+ persist-credentials: false
94
+ - uses: astral-sh/setup-uv@v7
95
+ - shell: bash
96
+ run: uvx --from release-devkit==${{ env.RELEASE_DEVKIT_VERSION }} publish-dev
97
+ env:
98
+ NPM_CONFIG_LOGLEVEL: verbose
@@ -8,7 +8,7 @@ The package is `openapi_client_codegen` (src-layout under `src/openapi_client_co
8
8
 
9
9
  ## Release flow
10
10
 
11
- Publishing rides `release.yml`, triggered by a successful CI run on a `main` push: release-devkit's `publish-stable` (an inlined, version-pinned `uvx` step in the publish job, keeping OIDC identity local), never a project dependency (openapi-client-codegen sits inside release-devkit's transitive dependency graph; a project-level edge is a resolver cycle) — computes the plan from the tag ledger and path-diff, patches the version ephemerally, and publishes to PyPI under OIDC trusted publishing (publisher bound to `release.yml`, no environment). The committed `pyproject.toml` version is permanently the `0.0.0.dev0` sentinel; the `openapi-client-codegen-v*` tags are the version ledger (declared `major_minor` line in `release-devkit.yaml`, patch-auto within the line). API-breaking changes ship with a manually bumped `major_minor` — patch-auto assumes additive changes.
11
+ Publishing rides `ci-cd.yml`, which combines CI and release: `check` runs `preflight-python` from the python-devkit dev dependency then `publish-stable --dry-run` as its trailing step, and the publish jobs (`publish-stable`, `publish-dev`, `ensure-release-pr`) run release-devkit's verbs as inlined `uvx --from release-devkit==${{ env.RELEASE_DEVKIT_VERSION }}` steps, version-pinned in the workflow `env:` (see release-devkit's `AGENTS.md`), never a project dependency (openapi-client-codegen sits inside release-devkit's transitive dependency graph; a project-level edge is a resolver cycle). The dev channel (`publish-dev` on `dev` push, an in-run job gated on `check`) ships immutable `-dev.<run-id>` prereleases; `ensure-release-pr` maintains the standing `dev` → `main` release-PR gate; `publish-stable` runs on `main` push under OIDC trusted publishing (publisher bound to `ci-cd.yml`, `release` environment). The committed `pyproject.toml` version is permanently the `0.0.0.dev0` sentinel; the `openapi-client-codegen-v*` tags are the version ledger (declared `major_minor` line in `release-devkit.yaml`, patch-auto within the line). API-breaking changes ship with a manually bumped `major_minor` — patch-auto assumes additive changes.
12
12
 
13
13
  ## Public API
14
14
 
@@ -20,7 +20,7 @@ The package init is empty (RUF067 strict mode — the canonical ruff config bans
20
20
  | `ProjectsConfig` | The pydantic schema of a consumer's client-generation config (`extra="forbid"`): required `projects`, `root_name`, `generated_root`, `spec_command`, `requires`; optional `spec_env`, `npm_scope`, `license_spdx`, `repository_url`. `requires` is a PEP 440 specifier; `load_config` self-checks the installed version and refuses outside range; the `0.0.0.dev0` sentinel bypasses the *check*, not the field's presence (omission is a validation error; "no constraint" is explicit `requires: ">=0.0"`). The single source of identity — the CLI validates the `--config` file into it and consumes it; nothing overrides it per invocation. |
21
21
  | `load_config(path)` | Parse the consumer's root YAML config into a `ProjectsConfig` (strictyaml, scalars as strings) and enforce the required `requires` version self-check. |
22
22
  | `SpecProducer(command, env=None)` | Callable class wrapping any command that prints the spec to stdout, run with the project directory as cwd and an optional per-call env overlay; the CLI command builds it from the config's `spec_command` / `spec_env`, and programmatic consumers construct it directly. |
23
- | `app` | The single-command CLI (invoked directly, no subcommand) behind the `openapi-client-codegen` console script and consumer `[project.scripts]` aliases (e.g. placeframe's `generate-clients`). It validates the `--config` YAML against the `ProjectsConfig` pydantic model (via `load_config`) (`extra="forbid"`: unknown keys rejected — a `ValidationError` propagates) and then runs the whole orchestration itself: per mapping entry (`projects` maps project paths to generator lists), produce the spec via `SpecProducer`, downgrade it, write `<project>/openapi.json` unless byte-identical to the committed spec (`no_cache` forces through), and `generate_client` per listed generator into `<generated_root>/<generator>/<names.base>`. The flags are invocation-scoped only (filters, `--no-cache`, `--root`; project paths resolve against `root`). Commands run shell-less (shlex-split, operators rejected), so env gating is the structured `spec_env`, never a `VAR=…` prefix. |
23
+ | `app` | The single-command CLI (invoked directly, no subcommand) behind the `openapi-client-codegen` console script and consumer `[project.scripts]` aliases (e.g. placeframe's `generate-clients`). It validates the `--config` YAML against the `ProjectsConfig` pydantic model (via `load_config`) (`extra="forbid"`: unknown keys rejected — a `ValidationError` propagates) and then runs the whole orchestration itself: per mapping entry (`projects` maps project paths to generator lists), produce the spec via `SpecProducer`, downgrade it, write `<project>/openapi.json` unless byte-identical to the committed spec (`no_cache` forces through), and `generate_client` per listed generator into `<generated_root>/<generator>/<names.base>`. The flags are invocation-scoped only (filters, `--no-cache`, `--check`, `--root`; project paths resolve against `root`). `--check` forces an unconditional regen and fails if any spec or generated client differs from the committed tree — the staleness gate consumers run in CI; the checked paths are fully derived from `ProjectsConfig` (every `<project>/openapi.json` plus `generated_root`), no per-consumer path args. Commands run shell-less (shlex-split, operators rejected), so env gating is the structured `spec_env`, never a `VAR=…` prefix. |
24
24
  | `regenerate_templates(target_dir, extra_patches_dir=None)` | Author the C# templates into `target_dir/csharp` and apply the shipped (then optional extra) patches. |
25
25
  | `generate_client(spec, generator, output_dir, names, templates_dir=None, extra_references=None, npm_scope=None, license_spdx=None, repository_url=None)` | Run the generator for one `(spec, generator)` and sync the result into `output_dir`. `templates_dir` is required for `csharp`. For csharp, `npm_scope` composes the UPM identity `{scope}.{base-minus-dashes}` (e.g. `org.outernet.placeframe.apiclient`); unset falls back to the legacy `org.nuget.{camel-lower}` identity. `license_spdx` / `repository_url` populate the manifest's `license` / `repository` fields when given. |
26
26
  | `write_unity_package_metadata(package_dir, package_name, extra_references=None, npm_name=None, license_spdx=None, repository_url=None)` | Write `package.json` / `.asmdef` / `csc.rsp` / `Directory.Build.props`. Called by `generate_client` for csharp. |
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: openapi-client-codegen
3
- Version: 0.1.13
3
+ Version: 0.1.14.dev36667872276
4
4
  Summary: OpenAPI client-generation wrapper: Litestar spec dump, 3.1->3.0 downgrade, patched openapi-generator templates, Unity package metadata
5
5
  License-File: LICENSE
6
6
  License-File: NOTICE
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "openapi-client-codegen"
3
- version = "0.1.13"
3
+ version = "0.1.14.dev36667872276"
4
4
  description = "OpenAPI client-generation wrapper: Litestar spec dump, 3.1->3.0 downgrade, patched openapi-generator templates, Unity package metadata"
5
5
  requires-python = ">=3.13"
6
6
  dependencies = [
@@ -16,6 +16,7 @@ dev = [
16
16
  "basedpyright>=1.39.10",
17
17
  "ruff>=0.14.11",
18
18
  "pytest>=8.0.0",
19
+ "python-devkit>=0.1.0",
19
20
  "pyyaml>=6.0.3",
20
21
  "types-pyyaml>=6.0.12.20260906",
21
22
  "types-strictyaml>=1.7.3.3",
@@ -31,8 +32,8 @@ openapi-client-codegen = "openapi_client_codegen.cli:app"
31
32
  [tool.hatch.build.targets.wheel]
32
33
  packages = ["src/openapi_client_codegen"]
33
34
  include = [
34
- "src/openapi_clientgen/py.typed",
35
- "src/openapi_clientgen/_data/**",
35
+ "src/openapi_client_codegen/py.typed",
36
+ "src/openapi_client_codegen/_data/**",
36
37
  ]
37
38
 
38
39
  [tool.uv.sources]
@@ -1,5 +1,5 @@
1
1
  requires: ">=0.1"
2
- ci_workflow: ci.yml
2
+ ci_workflow: ci-cd.yml
3
3
  packages:
4
4
  openapi-client-codegen:
5
5
  path: .
@@ -3,6 +3,7 @@ from pathlib import Path
3
3
  from tempfile import TemporaryDirectory
4
4
  from typing import Annotated
5
5
 
6
+ from bashrun.bash import bash, bash_output
6
7
  from typer import Option, Typer
7
8
 
8
9
  from .client import generate_client
@@ -23,11 +24,16 @@ def generate_projects_command(
23
24
  no_cache: Annotated[
24
25
  bool, Option("--no-cache", help="Regenerate even when the committed spec is unchanged")
25
26
  ] = False,
27
+ check: Annotated[
28
+ bool,
29
+ Option("--check", help="Regenerate, then fail if any spec or generated client differs from the committed tree"),
30
+ ] = False,
26
31
  root: Annotated[Path, Option(help="Repository root the project paths resolve against")] = Path(),
27
32
  ) -> None:
28
33
  settings = load_config(config)
29
34
  root = root.resolve()
30
35
  dump_spec = SpecProducer(settings.spec_command, env=settings.spec_env)
36
+ regenerate = no_cache or check
31
37
 
32
38
  with TemporaryDirectory() as templates_directory_string:
33
39
  templates_directory = Path(templates_directory_string)
@@ -49,7 +55,7 @@ def generate_projects_command(
49
55
  if spec_path.exists():
50
56
  old_spec = spec_path.read_text(encoding="utf-8")
51
57
 
52
- if openapi_spec == old_spec and not no_cache:
58
+ if openapi_spec == old_spec and not regenerate:
53
59
  print("OpenAPI spec unchanged, skipping client generation")
54
60
  continue
55
61
 
@@ -78,3 +84,11 @@ def generate_projects_command(
78
84
  license_spdx=settings.license_spdx,
79
85
  repository_url=settings.repository_url,
80
86
  )
87
+
88
+ if check:
89
+ pathspec = " ".join(f"{name}/openapi.json" for name in settings.projects) + f" {settings.generated_root}"
90
+ if bash_output(f"git status --porcelain -- {pathspec}", cwd=root).strip():
91
+ bash(f"git diff -- {pathspec}", cwd=root)
92
+ raise SystemExit(
93
+ "Generated OpenAPI specs/clients are stale; re-run this command without --check and commit the result."
94
+ )
@@ -27,6 +27,8 @@ FULL_SETTINGS: dict[str, object] = {
27
27
  class Recorder:
28
28
  def __init__(self) -> None:
29
29
  self.generated: list[tuple[str, Path]] = []
30
+ self.git_commands: list[str] = []
31
+ self.git_dirty = False
30
32
 
31
33
  def fake_bash_output(self, command: str, *, cwd: Path | None = None, env: dict[str, str] | None = None) -> str:
32
34
  return json.dumps({"openapi": "3.1.0", "info": {"title": "demo", "version": "0.0.0"}, "paths": {}})
@@ -48,6 +50,13 @@ class Recorder:
48
50
  ) -> None:
49
51
  self.generated.append((generator, output_dir))
50
52
 
53
+ def fake_git_status(self, command: str, *, cwd: Path | None = None, env: dict[str, str] | None = None) -> str:
54
+ self.git_commands.append(command)
55
+ return "M packages/generated/dummy\n" if self.git_dirty else ""
56
+
57
+ def fake_git_diff(self, command: str, *, cwd: Path | None = None, env: dict[str, str] | None = None) -> None:
58
+ self.git_commands.append(command)
59
+
51
60
 
52
61
  @pytest.fixture
53
62
  def recorder(monkeypatch: pytest.MonkeyPatch) -> Recorder:
@@ -55,6 +64,8 @@ def recorder(monkeypatch: pytest.MonkeyPatch) -> Recorder:
55
64
  monkeypatch.setattr(orchestrator, "bash_output", recorder.fake_bash_output)
56
65
  monkeypatch.setattr(cli, "regenerate_templates", recorder.fake_regenerate_templates)
57
66
  monkeypatch.setattr(cli, "generate_client", recorder.fake_generate_client)
67
+ monkeypatch.setattr(cli, "bash_output", recorder.fake_git_status)
68
+ monkeypatch.setattr(cli, "bash", recorder.fake_git_diff)
58
69
  return recorder
59
70
 
60
71
 
@@ -107,3 +118,28 @@ def test_missing_required_field_fails(tmp_path: Path, recorder: Recorder) -> Non
107
118
  assert result.exit_code != 0
108
119
  assert "spec_command" in str(result.exception)
109
120
  assert recorder.generated == []
121
+
122
+
123
+ def test_check_forces_regen_and_passes_when_clean(tmp_path: Path, recorder: Recorder) -> None:
124
+ config = write_config(tmp_path, FULL_SETTINGS)
125
+ (tmp_path / "docker" / "api").mkdir(parents=True)
126
+
127
+ result = runner.invoke(app, ["--config", str(config), "--root", str(tmp_path), "--check"])
128
+
129
+ assert result.exit_code == 0
130
+ assert [generator for generator, _ in recorder.generated] == ["python", "csharp"]
131
+ assert recorder.git_commands == ["git status --porcelain -- docker/api/openapi.json packages/generated"]
132
+
133
+
134
+ def test_check_fails_when_tree_stale(tmp_path: Path, recorder: Recorder) -> None:
135
+ config = write_config(tmp_path, FULL_SETTINGS)
136
+ (tmp_path / "docker" / "api").mkdir(parents=True)
137
+ recorder.git_dirty = True
138
+
139
+ result = runner.invoke(app, ["--config", str(config), "--root", str(tmp_path), "--check"])
140
+
141
+ assert result.exit_code != 0
142
+ assert recorder.git_commands == [
143
+ "git status --porcelain -- docker/api/openapi.json packages/generated",
144
+ "git diff -- docker/api/openapi.json packages/generated",
145
+ ]
@@ -34,11 +34,25 @@ wheels = [
34
34
 
35
35
  [[package]]
36
36
  name = "bashrun"
37
- version = "0.1.0"
37
+ version = "0.1.12"
38
38
  source = { registry = "https://pypi.org/simple" }
39
- sdist = { url = "https://files.pythonhosted.org/packages/24/cd/dc3df91cbe2bfbe57b6d9bfe225f5808a22888d636cf30005d89133237c8/bashrun-0.1.0.tar.gz", hash = "sha256:e376f34693ba12ec907a6ab43626ed425c876c2b7be6569563a6492e11c79bc7", size = 18983, upload-time = "2026-09-21T01:47:56.971Z" }
39
+ sdist = { url = "https://files.pythonhosted.org/packages/19/2c/742acb113293e712d82c5cfce35a30f344385a7d9852d5844b0bcff4f12f/bashrun-0.1.12.tar.gz", hash = "sha256:d5fd926ec1cb1df6e218a98fe8ad737c24b72bcf740bdfbe2ecf4c77894878a0", size = 32364, upload-time = "2026-09-30T04:02:19.854Z" }
40
40
  wheels = [
41
- { url = "https://files.pythonhosted.org/packages/be/9a/749dd28f3defde3690e0280131f9461e4100c43c117f9363798f6dedbb91/bashrun-0.1.0-py3-none-any.whl", hash = "sha256:80f87924f88336460890d669aa1abb6fb8c389b675fdb41ac8d2b6861d4ebddb", size = 7634, upload-time = "2026-09-21T01:47:56.233Z" },
41
+ { url = "https://files.pythonhosted.org/packages/f6/1d/4de203dbe4d711b39f7b440bd141b429864e2a3539faa9b6531eab456195/bashrun-0.1.12-py3-none-any.whl", hash = "sha256:9a188b90eb25249d24556dcdaccfa09ca3efe741406a1c71c7e3cecd170f41b0", size = 7511, upload-time = "2026-09-30T04:02:18.991Z" },
42
+ ]
43
+
44
+ [[package]]
45
+ name = "ci-devkit"
46
+ version = "0.1.14"
47
+ source = { registry = "https://pypi.org/simple" }
48
+ dependencies = [
49
+ { name = "bashrun" },
50
+ { name = "pydantic" },
51
+ { name = "pydantic-settings" },
52
+ ]
53
+ sdist = { url = "https://files.pythonhosted.org/packages/22/25/dc42c65e6147eb64477eae4f410b26d630f03f46c3d3bdcdab374df28081/ci_devkit-0.1.14.tar.gz", hash = "sha256:f8b45fa995bba626edda29c191883224173155a39137da5a1bc67f09ad28d337", size = 51204, upload-time = "2026-09-30T04:06:27.581Z" }
54
+ wheels = [
55
+ { url = "https://files.pythonhosted.org/packages/4f/08/56cafd065e2d00ad90d450867282d79469bc4c7c9eefa1dc11dcf84e0c96/ci_devkit-0.1.14-py3-none-any.whl", hash = "sha256:64b74d797a040d8d57735eda30dbb59844d0de9c64e53dd24e9769d10bc34e18", size = 25689, upload-time = "2026-09-30T04:06:26.522Z" },
42
56
  ]
43
57
 
44
58
  [[package]]
@@ -112,6 +126,7 @@ dependencies = [
112
126
  dev = [
113
127
  { name = "basedpyright" },
114
128
  { name = "pytest" },
129
+ { name = "python-devkit" },
115
130
  { name = "pyyaml" },
116
131
  { name = "ruff" },
117
132
  { name = "types-pyyaml" },
@@ -131,6 +146,7 @@ requires-dist = [
131
146
  dev = [
132
147
  { name = "basedpyright", specifier = ">=1.39.10" },
133
148
  { name = "pytest", specifier = ">=8.0.0" },
149
+ { name = "python-devkit", specifier = ">=0.1.0" },
134
150
  { name = "pyyaml", specifier = ">=6.0.3" },
135
151
  { name = "ruff", specifier = ">=0.14.11" },
136
152
  { name = "types-pyyaml", specifier = ">=6.0.12.20260906" },
@@ -226,6 +242,20 @@ wheels = [
226
242
  { url = "https://files.pythonhosted.org/packages/63/19/67830dda664e6bdf9285ee2e40f355d0d7d6b92aa0c42e8d217bb8d33d36/pydantic_core-2.46.5-cp314-cp314t-win_arm64.whl", hash = "sha256:acf8a67ba51f4ca9ddbd0e6b3000a65ac51ab734661778b3e7ba64d99a710f2f", size = 1989276, upload-time = "2026-08-28T10:00:16.984Z" },
227
243
  ]
228
244
 
245
+ [[package]]
246
+ name = "pydantic-settings"
247
+ version = "2.15.0"
248
+ source = { registry = "https://pypi.org/simple" }
249
+ dependencies = [
250
+ { name = "pydantic" },
251
+ { name = "python-dotenv" },
252
+ { name = "typing-inspection" },
253
+ ]
254
+ sdist = { url = "https://files.pythonhosted.org/packages/68/ca/31c57507b13119d7d3cfa1576dad2911a4861e3be07b579395f4e9d393f9/pydantic_settings-2.15.0.tar.gz", hash = "sha256:694b793e84f766ba76a90ebdefc01d0a9a045dab0382bee70393da93712ad117", size = 261253, upload-time = "2026-08-07T09:24:57.419Z" }
255
+ wheels = [
256
+ { url = "https://files.pythonhosted.org/packages/30/a4/2bffa9f8e804325a09867f0e9d30795c80ea9f8d62560bd1b6ad6220eb2f/pydantic_settings-2.15.0-py3-none-any.whl", hash = "sha256:0ba092c291c94baceb5eff768aa0d56400a457585bc0175925a5a5510303da42", size = 69413, upload-time = "2026-08-07T09:24:55.839Z" },
257
+ ]
258
+
229
259
  [[package]]
230
260
  name = "pygments"
231
261
  version = "2.20.0"
@@ -263,6 +293,30 @@ wheels = [
263
293
  { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
264
294
  ]
265
295
 
296
+ [[package]]
297
+ name = "python-devkit"
298
+ version = "0.1.15"
299
+ source = { registry = "https://pypi.org/simple" }
300
+ dependencies = [
301
+ { name = "bashrun" },
302
+ { name = "ci-devkit" },
303
+ { name = "pydantic" },
304
+ { name = "typer" },
305
+ ]
306
+ sdist = { url = "https://files.pythonhosted.org/packages/82/53/c3b37f69c35475b09cdf1c18deb193238d7ab5143ee6bc97e5c22c48fe87/python_devkit-0.1.15.tar.gz", hash = "sha256:ac1ef0f2a4622e9c97763f4e353005c41164e2d6d4b130cdb709e811947f253d", size = 34651, upload-time = "2026-09-30T04:12:18.88Z" }
307
+ wheels = [
308
+ { url = "https://files.pythonhosted.org/packages/b6/81/da4f76cbfccb5a4d5a58a9f51d3e2b7d40f6dc9edd931d7891b2789adeae/python_devkit-0.1.15-py3-none-any.whl", hash = "sha256:0c13684cc780e3ba9b3e25feed61065b355e8fc7436bee043e7c6adf71fa40e7", size = 14537, upload-time = "2026-09-30T04:12:17.918Z" },
309
+ ]
310
+
311
+ [[package]]
312
+ name = "python-dotenv"
313
+ version = "1.2.3"
314
+ source = { registry = "https://pypi.org/simple" }
315
+ sdist = { url = "https://files.pythonhosted.org/packages/6a/53/ed9d74092561d4b01a2ef1349d52cdbc135e526c245f366b089cfca6de49/python_dotenv-1.2.3.tar.gz", hash = "sha256:a20a594dabeaa385725aa239d5244871c143ecb356add8a20fcf23773a6c3a35", size = 58945, upload-time = "2026-08-16T16:54:54.067Z" }
316
+ wheels = [
317
+ { url = "https://files.pythonhosted.org/packages/0d/17/c5c6b53ddc18f297992099b3d9ec16c855c0ccc83263a21fe4d1c625ec6c/python_dotenv-1.2.3-py3-none-any.whl", hash = "sha256:904552145e8bfed22162c09dab1c2b9b54fefa7b23ba780f4f26ca0316b0f0d9", size = 22780, upload-time = "2026-08-16T16:54:52.473Z" },
318
+ ]
319
+
266
320
  [[package]]
267
321
  name = "pyyaml"
268
322
  version = "6.0.3"
@@ -1,26 +0,0 @@
1
- name: CI
2
-
3
- on:
4
- push:
5
- branches: [main]
6
- pull_request:
7
- branches: [main]
8
-
9
- concurrency:
10
- group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
11
- cancel-in-progress: true
12
-
13
- jobs:
14
- check:
15
- runs-on: ubuntu-latest
16
- permissions:
17
- contents: read
18
- steps:
19
- - uses: actions/checkout@v5
20
-
21
- - uses: astral-sh/setup-uv@v7
22
- with:
23
- enable-cache: true
24
-
25
- - name: Preflight
26
- run: uvx --from python-devkit==0.1.5 preflight-python
@@ -1,31 +0,0 @@
1
- name: Release
2
-
3
- on:
4
- workflow_run:
5
- workflows: [CI]
6
- types: [completed]
7
- branches: [main]
8
-
9
- concurrency:
10
- group: release
11
- cancel-in-progress: false
12
-
13
- jobs:
14
- publish:
15
- if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push' && github.event.workflow_run.head_branch == 'main'
16
- runs-on: ubuntu-latest
17
- permissions:
18
- contents: write
19
- id-token: write
20
- steps:
21
- - uses: actions/checkout@v5
22
- with:
23
- ref: ${{ github.event.workflow_run.head_sha }}
24
-
25
- # Workaround: fetch-tags is broken with shallow clones
26
- - run: git fetch --tags origin
27
-
28
- - uses: astral-sh/setup-uv@v7
29
-
30
- - name: Publish
31
- run: uvx --from release-devkit==0.1.20 publish-stable --config release-devkit.yaml