openapi-client-codegen 0.1.14__tar.gz → 0.1.15.dev37253060679__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. openapi_client_codegen-0.1.15.dev37253060679/.github/actions/setup-release-devkit/action.yml +11 -0
  2. openapi_client_codegen-0.1.15.dev37253060679/.github/workflows/integrate.yml +56 -0
  3. openapi_client_codegen-0.1.15.dev37253060679/.github/workflows/merge-gate.yml +41 -0
  4. openapi_client_codegen-0.1.15.dev37253060679/.github/workflows/release.yml +70 -0
  5. openapi_client_codegen-0.1.15.dev37253060679/AGENTS.md +50 -0
  6. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/PKG-INFO +1 -1
  7. openapi_client_codegen-0.1.15.dev37253060679/README.md +3 -0
  8. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/pyproject.toml +1 -1
  9. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/release-devkit.yaml +1 -2
  10. openapi_client_codegen-0.1.14/.github/workflows/ci-cd.yml +0 -98
  11. openapi_client_codegen-0.1.14/AGENTS.md +0 -60
  12. openapi_client_codegen-0.1.14/README.md +0 -70
  13. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/.gitattributes +0 -0
  14. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/.gitignore +0 -0
  15. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/.python-version +0 -0
  16. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/CLAUDE.md +0 -0
  17. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/LICENSE +0 -0
  18. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/NOTICE +0 -0
  19. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/ruff.base.toml +0 -0
  20. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/ruff.toml +0 -0
  21. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/src/openapi_client_codegen/__init__.py +0 -0
  22. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/src/openapi_client_codegen/_data/configs/csharp.json +0 -0
  23. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/src/openapi_client_codegen/_data/configs/python.json +0 -0
  24. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/src/openapi_client_codegen/_data/openapi-generator-ignore +0 -0
  25. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/src/openapi_client_codegen/_data/templates-patches/csharp/0001-csharp-remove-errant-comma.patch +0 -0
  26. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/src/openapi_client_codegen/_data/templates-patches/csharp/0002-csharp-models-required-only-constructors.patch +0 -0
  27. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/src/openapi_client_codegen/_data/templates-patches/csharp/0003-csharp-httpclient-use-defaultT.patch +0 -0
  28. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/src/openapi_client_codegen/_data/templates-patches/csharp/0004-csharp-support-hybrid-multipart-payloads.patch +0 -0
  29. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/src/openapi_client_codegen/_data/templates-patches/csharp/0006-csharp-streaming-download-response.patch +0 -0
  30. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/src/openapi_client_codegen/_data/templates-patches/csharp/0007-csharp-model-default-initializers.patch +0 -0
  31. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/src/openapi_client_codegen/_data/templates-patches/csharp/0008-csharp-redirect-documentation-file.patch +0 -0
  32. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/src/openapi_client_codegen/cli.py +0 -0
  33. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/src/openapi_client_codegen/client.py +0 -0
  34. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/src/openapi_client_codegen/downgrade.py +0 -0
  35. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/src/openapi_client_codegen/orchestrator.py +0 -0
  36. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/src/openapi_client_codegen/py.typed +0 -0
  37. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/src/openapi_client_codegen/resources.py +0 -0
  38. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/src/openapi_client_codegen/templates.py +0 -0
  39. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/src/openapi_client_codegen/unity.py +0 -0
  40. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/tests/test_cli.py +0 -0
  41. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/tests/test_config.py +0 -0
  42. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/tests/test_downgrade.py +0 -0
  43. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/tests/test_orchestrate.py +0 -0
  44. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/tests/test_unity.py +0 -0
  45. {openapi_client_codegen-0.1.14 → openapi_client_codegen-0.1.15.dev37253060679}/uv.lock +0 -0
@@ -0,0 +1,11 @@
1
+ name: setup-release-devkit
2
+ description: Install release-devkit at the pinned commit into RUNNER_TEMP
3
+ runs:
4
+ using: composite
5
+ steps:
6
+ - shell: bash
7
+ env:
8
+ RELEASE_DEVKIT_COMMIT: ad4215260f7b24ec36d94ddc2ba6660be8afc5fb
9
+ run: |
10
+ git clone https://github.com/outernet-foundation/release-devkit.git "$RUNNER_TEMP/release-devkit"
11
+ git -C "$RUNNER_TEMP/release-devkit" checkout "$RELEASE_DEVKIT_COMMIT"
@@ -0,0 +1,56 @@
1
+ name: Integrate
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ pull_request:
6
+ branches: [dev]
7
+
8
+ concurrency:
9
+ group: integrate-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ permissions:
13
+ contents: read
14
+
15
+ jobs:
16
+ lint-workflows:
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - &checkout
20
+ uses: actions/checkout@v5
21
+ with:
22
+ ref: ${{ github.event.pull_request.head.sha }}
23
+ persist-credentials: false
24
+ - &uv-restore
25
+ uses: astral-sh/setup-uv@v7
26
+ with:
27
+ enable-cache: true
28
+ save-cache: "false"
29
+ - &setup-release-devkit
30
+ uses: ./.github/actions/setup-release-devkit
31
+ - run: uv run --project "$RUNNER_TEMP/release-devkit" --locked --no-dev lint-workflows
32
+
33
+ preflight:
34
+ needs: [lint-workflows]
35
+ runs-on: ubuntu-latest
36
+ steps:
37
+ - *checkout
38
+ - uses: astral-sh/setup-uv@v7
39
+ with:
40
+ enable-cache: true
41
+ save-cache: "true"
42
+ - run: uv run preflight-python
43
+
44
+ validate-release-plan:
45
+ runs-on: ubuntu-latest
46
+ steps:
47
+ - uses: actions/checkout@v5
48
+ with:
49
+ ref: ${{ github.event.pull_request.head.sha }}
50
+ # Perform a full clone, required for tags
51
+ fetch-depth: 0
52
+ fetch-tags: true
53
+ persist-credentials: false
54
+ - *uv-restore
55
+ - *setup-release-devkit
56
+ - run: uv run --project "$RUNNER_TEMP/release-devkit" --locked --no-dev validate-release-plan
@@ -0,0 +1,41 @@
1
+ name: Merge gate
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [dev]
6
+ types: [labeled]
7
+ workflow_run:
8
+ workflows: [Integrate]
9
+ types: [completed]
10
+
11
+ concurrency:
12
+ group: merge-gate-${{ github.event.pull_request.number || github.event.workflow_run.head_branch }}
13
+
14
+ permissions:
15
+ contents: read
16
+
17
+ jobs:
18
+ merge-gate:
19
+ if: github.event.label.name == 'ready-to-merge' || (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success')
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - uses: actions/checkout@v5
23
+ with:
24
+ ref: ${{ github.event.pull_request.head.sha || github.event.workflow_run.head_sha }}
25
+ # Perform a full clone, required so merge-gate can validate that this PR is a fast forward from dev
26
+ fetch-depth: 0
27
+ persist-credentials: false
28
+ - uses: astral-sh/setup-uv@v7
29
+ with:
30
+ enable-cache: true
31
+ save-cache: "false"
32
+ - uses: ./.github/actions/setup-release-devkit
33
+ - uses: actions/create-github-app-token@v3
34
+ id: mint
35
+ with:
36
+ app-id: ${{ vars.MERGE_BOT_APP_ID }}
37
+ private-key: ${{ secrets.MERGE_BOT_APP_PRIVATE_KEY }}
38
+ - run: uv run --project "$RUNNER_TEMP/release-devkit" --locked --no-dev merge-gate
39
+ env:
40
+ GITHUB_TOKEN: ${{ steps.mint.outputs.token }}
41
+ HEAD_SHA: ${{ github.event.pull_request.head.sha || github.event.workflow_run.head_sha }}
@@ -0,0 +1,70 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ branches: [main, dev]
6
+
7
+ concurrency:
8
+ group: release-${{ github.ref }}
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ jobs:
14
+ ensure-release-pr:
15
+ if: github.ref == 'refs/heads/dev'
16
+ runs-on: ubuntu-latest
17
+ permissions:
18
+ contents: write
19
+ pull-requests: write
20
+ steps:
21
+ - uses: actions/checkout@v5
22
+ with:
23
+ persist-credentials: false
24
+ - &uv-restore
25
+ uses: astral-sh/setup-uv@v7
26
+ with:
27
+ enable-cache: true
28
+ save-cache: "false"
29
+ - &setup-release-devkit
30
+ uses: ./.github/actions/setup-release-devkit
31
+ - run: uv run --project "$RUNNER_TEMP/release-devkit" --locked --no-dev ensure-release-pr
32
+ env:
33
+ GITHUB_TOKEN: ${{ github.token }}
34
+
35
+ publish-prerelease:
36
+ if: github.ref == 'refs/heads/dev'
37
+ runs-on: ubuntu-latest
38
+ permissions:
39
+ contents: read
40
+ id-token: write
41
+ steps:
42
+ - uses: actions/checkout@v5
43
+ with:
44
+ # Perform a full clone, required for tags
45
+ fetch-depth: 0
46
+ fetch-tags: true
47
+ persist-credentials: false
48
+ - *uv-restore
49
+ - *setup-release-devkit
50
+ - run: uv run --project "$RUNNER_TEMP/release-devkit" --locked --no-dev publish-prerelease
51
+
52
+ release:
53
+ if: github.ref == 'refs/heads/main'
54
+ runs-on: ubuntu-latest
55
+ permissions:
56
+ contents: write
57
+ id-token: write
58
+ steps:
59
+ - uses: actions/checkout@v5
60
+ with:
61
+ # Perform a full clone, required for tags
62
+ fetch-depth: 0
63
+ fetch-tags: true
64
+ # Persist credentials so that the release command can push release tags
65
+ persist-credentials: true
66
+ - *uv-restore
67
+ - *setup-release-devkit
68
+ - run: uv run --project "$RUNNER_TEMP/release-devkit" --locked --no-dev release
69
+ env:
70
+ GITHUB_TOKEN: ${{ github.token }}
@@ -0,0 +1,50 @@
1
+ # openapi-client-codegen
2
+
3
+ ## What this is
4
+
5
+ `openapi-client-codegen` wraps [OpenAPI Generator](https://openapi-generator.tech/) to turn an OpenAPI schema into typed client packages — a Python (`httpx`) client and a Unity-consumable C# (`httpclient`) client. It owns every step that is generic across consumers: the project orchestration loop, downgrading 3.1→3.0, the committed-spec unchanged skip, authoring and patching the C# templates, running the generator, and writing the Unity package metadata. The caller owns what is inherently project-specific: **producing the OpenAPI spec** (a command string in the consumer's config, run by the library's `SpecProducer`), the project→client mapping, the naming root, the npm scope, license, repository URL, and the generated output root.
6
+
7
+ The package is `openapi_client_codegen` (src-layout under `src/openapi_client_codegen/`). Python dependencies: `bashrun`, `packaging` (`requires` specifier parsing), `pydantic` (config schema), `strictyaml` (config loading), `typer` (thin CLI) — all from PyPI; git-source pins only in scratch branches testing unreleased changes. At **runtime** it also needs **Java (JDK 11+)** and **`uvx`** on PATH — the generator itself runs as `uvx --from 'openapi-generator-cli[jdk4py]==<pin>' ...`.
8
+
9
+ ## Public API
10
+
11
+ The package init is empty (RUF067 strict mode); import each symbol from its defining module: `client` (`generate_client`), `cli` (`app`), `downgrade` (`downgrade_openapi_3_1_to_3_0`, `JsonDict`), `orchestrator` (`ProjectsConfig`, `ClientNaming`, `SpecProducer`, `load_config`), `templates` (`regenerate_templates`), `unity` (`write_unity_package_metadata`).
12
+
13
+ - `ProjectsConfig` is the pydantic schema of a consumer's config (`extra="forbid"`) and the single source of identity — the CLI validates `--config` into it and nothing overrides it per invocation. Required: `projects` (project path → generator list), `root_name`, `generated_root`, `spec_command`, `requires` (a PEP 440 specifier; `load_config` self-checks the installed version and refuses outside range; "no constraint" is explicit `requires: ">=0.0"`; the `0.0.0.dev0` dev sentinel bypasses the check, not the field's presence). Optional: `spec_env`, `npm_scope`, `license_spdx`, `repository_url`.
14
+ - The CLI (`app`, single command, no subcommand; consumer `[project.scripts]` aliases point at it, e.g. placeframe's `generate-clients`) orchestrates per mapping entry: produce the spec via the config-built `SpecProducer` → downgrade 3.1→3.0 → write `<project>/openapi.json` unless byte-identical to the committed spec (`--no-cache` forces through) → `generate_client` per listed generator into `<generated_root>/<generator>/<base>`. `--check` is the CI staleness gate: unconditional regen, non-zero exit if any spec or generated client differs from the committed tree; the checked paths are fully derived from config, no per-consumer path args. Flags are invocation-scoped only (`--project`/`--client` filters, `--no-cache`, `--check`, `--root`). Commands run shell-less (shlex-split, operators rejected) — env gating is the structured `spec_env`, never a `VAR=…` prefix.
15
+ - `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; programmatic consumers construct it directly.
16
+ - `ClientNaming` — the derived per-project package identity: `base` (last path segment + `-client`), `dashed` (`{root_name}-{base}`), `underscored`, `camel`. Constructed inline by the CLI per project; a consumer needing different names calls the per-client API directly with its own `ClientNaming`.
17
+ - `generate_client(spec, generator, output_dir, names, ...)` — run the generator for one `(spec, generator)` and sync into `output_dir`; `templates_dir` is required for `csharp`, where `npm_scope` composes the UPM identity `{scope}.{base-minus-dashes}` (unset falls back to the legacy `org.nuget.{camel-lower}`).
18
+ - `write_unity_package_metadata(...)` — write `package.json` / `.asmdef` / `csc.rsp` / `Directory.Build.props`; called by `generate_client` for csharp.
19
+
20
+ The generator version pin and the shipped configs/patches/ignore-file live in `src/openapi_client_codegen/_data/` (paths in `_data.py`).
21
+
22
+ ## Constraints
23
+
24
+ ### The C# template patches are path-generic and repo-independent
25
+
26
+ The shipped patches (`_data/templates-patches/csharp/00NN-*.patch`) carry generic `a/csharp/<path>` headers, not any consumer's repo layout. `regenerate_templates` applies them with `git apply -p1` and `cwd=target_dir`, which lands them on the freshly-authored tree **without needing a surrounding git repository** — `git apply` patches worktree files fine outside any repo as long as `--index` is not used. Patches apply in filename order (the `00NN` prefix is load-bearing: later patches depend on earlier hunks' context); `extra_patches_dir` patches apply after the shipped set.
27
+
28
+ To author a new patch: author the raw templates (`openapi-generator-cli author template -g csharp --library httpclient`) into a scratch dir, copy it, edit a `.mustache` in the copy, `git diff --no-index` the two trees, and normalize the diff's path headers to `csharp/<path>` so it applies under the same `-p1` convention.
29
+
30
+ ### Generator env vars are passed per-call, never set at module level
31
+
32
+ `JAVA_OPTS=-Dlog.level=warn` (quiets the generator) is passed as `bash(..., env={"JAVA_OPTS": "-Dlog.level=warn"})` on the two `openapi-generator-cli` calls that need it — bashrun's `env=` overlays the inherited environment for that one child only. A consumer's spec command needing the same gating (e.g. `CODEGEN=1`) carries it in the config's structured `spec_env` key, which rides the same per-call overlay.
33
+
34
+ ### The Unity reference set is coupled to the patched templates
35
+
36
+ `unity.write_unity_package_metadata` hardcodes the `.asmdef` base references `Newtonsoft.Json / Polly / JsonSubTypes` — exactly the runtime support the patched C# templates emit calls against. They travel with the patches as one unit; a consumer adds project-specific references via `extra_references`, it does not replace the base set.
37
+
38
+ `BASE_DEPENDENCIES` is the UPM dual of that reference set: the same three libraries as `package.json` dependencies (Newtonsoft as the Unity-blessed `com.unity.nuget.newtonsoft-json`, the others as UnityNuGet `org.nuget.*` identities), as exact versions — UPM's package.json dependencies accept SemVer values only, and the resolver treats a value as a minimum, so consumer manifests carrying newer minors still resolve. It travels with `BASE_REFERENCES` — change one, change the other.
39
+
40
+ ### The csproj ships in the package; MSBuild output is redirected around it
41
+
42
+ The generated `.csproj` stays inside the UPM package because it is the `dotnet pack` input for the nuget feed. To keep IDE builds from writing `bin/`/`obj/` into the package (Unity errors on the missing `.meta` files, and the npm tarball must not carry build output), `write_unity_package_metadata` writes a `Directory.Build.props` next to it that redirects `BaseIntermediateOutputPath` / `BaseOutputPath` two directories up — outside the package root under every consumption layout (repo checkout → client grouping dir; Unity PackageCache → `Library/`, which the asset importer ignores). The shipped template patch `0008` points the csproj's `DocumentationFile` at `$(BaseOutputPath)` for the same reason; `Directory.Build.props` is imported before the project body, so the property is visible at that evaluation point.
43
+
44
+ ## Release flow
45
+
46
+ release-devkit's `AGENTS.md` owns the three-workflow contract; this repo follows it unchanged. Repo-specific facts: release-devkit is never a project dependency (this package sits inside release-devkit's transitive dependency graph; a project-level edge is a resolver cycle), and API-breaking changes ship with a manually bumped `major_minor` (patch-auto assumes additive changes).
47
+
48
+ ## See also
49
+
50
+ - [`bashrun`](https://github.com/outernet-foundation/bashrun) — the subprocess wrapper this package shells out through.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: openapi-client-codegen
3
- Version: 0.1.14
3
+ Version: 0.1.15.dev37253060679
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
@@ -0,0 +1,3 @@
1
+ # openapi-client-codegen
2
+
3
+ Typed API clients (Python `httpx` + Unity-consumable C# `httpclient`) generated from an OpenAPI schema via OpenAPI Generator. Agent-facing documentation lives in [`AGENTS.md`](./AGENTS.md).
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "openapi-client-codegen"
3
- version = "0.1.14"
3
+ version = "0.1.15.dev37253060679"
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 = [
@@ -1,5 +1,4 @@
1
- requires: ">=0.1"
2
- ci_workflow: ci-cd.yml
1
+ ci_workflow: release.yml
3
2
  packages:
4
3
  openapi-client-codegen:
5
4
  path: .
@@ -1,98 +0,0 @@
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
@@ -1,60 +0,0 @@
1
- # openapi-client-codegen
2
-
3
- ## What this is
4
-
5
- `openapi-client-codegen` wraps [OpenAPI Generator](https://openapi-generator.tech/) to turn an OpenAPI schema into typed client packages — a Python (`httpx`) client and a Unity-consumable C# (`httpclient`) client. It owns every step that is generic across consumers: the project orchestration loop, downgrading 3.1→3.0, the committed-spec unchanged skip, authoring and patching the C# templates, running the generator, and writing the Unity package metadata. The caller owns what is inherently project-specific: **producing the OpenAPI spec** (a command string in the consumer's config, run by the library's `SpecProducer`), the project→client mapping, the naming root, the npm scope, license, repository URL, and the generated output root — all as fields of the `ProjectsConfig` model.
6
-
7
- The package is `openapi_client_codegen` (src-layout under `src/openapi_client_codegen/`), renamed from `openapi-clientgen` before the first publish (operator, 2026-09-20 — no artifact carries the old name). Its Python dependencies are `bashrun` (the guardrailed subprocess wrapper), `packaging` (PEP 440 specifier parsing for `requires`), `pydantic` (the config schema), `strictyaml` (config loading), and `typer` (the thin CLI), all from PyPI — git-source pins only in scratch branches testing unreleased changes. At **runtime** it also needs **Java (JDK 11+)** and **`uvx`** on PATH — the generator itself runs as `uvx --from 'openapi-generator-cli[jdk4py]==<pin>' ...`.
8
-
9
- ## Release flow
10
-
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
-
13
- ## Public API
14
-
15
- The package init is empty (RUF067 strict mode — the canonical ruff config bans any content in `__init__.py`); import each symbol from its defining module: `client` (`generate_client`), `cli` (`app`), `downgrade` (`downgrade_openapi_3_1_to_3_0`, `JsonDict`), `orchestrator` (`ProjectsConfig`, `ClientNaming`, `SpecProducer`, `load_config`), `templates` (`regenerate_templates`), `unity` (`write_unity_package_metadata`).
16
-
17
- | Symbol | Role |
18
- |---|---|
19
- | `downgrade_openapi_3_1_to_3_0(schema)` | In-place 3.1.0→3.0.3 fixup (openapi-generator rejects 3.1). Pure function. |
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
- | `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
- | `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`, `--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
- | `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
- | `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
- | `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. |
27
- | `ClientNaming` | The derived per-project package identity: `base` (last path segment + `-client`), `dashed` (`{root_name}-{base}`), `underscored`, `camel`. Constructed inline by the CLI command per project; `DefaultNamingPolicy("placeframe")("docker/api")` semantics live there. |
28
-
29
- The CLI command orchestrates the whole run — `regenerate_templates` once, then per project: produce (via the config-built `SpecProducer`) → downgrade → committed-spec skip → per-client generate. The generator version pin and the shipped configs/patches/ignore-file live in `src/openapi_client_codegen/_data/` (paths in `_data.py`).
30
-
31
- ## Constraints
32
-
33
- ### The C# template patches are path-generic and repo-independent
34
-
35
- The shipped patches (`_data/templates-patches/csharp/00NN-*.patch`) carry generic `a/csharp/<path>` headers, not any consumer's repo layout. `regenerate_templates` applies them with `git apply -p1` and `cwd=target_dir`, which lands them on the freshly-authored tree **without needing a surrounding git repository** — `git apply` patches worktree files fine outside any repo as long as `--index` is not used. Patches apply in filename order (the `00NN` prefix is load-bearing: later patches depend on earlier hunks' context); `extra_patches_dir` patches apply after the shipped set.
36
-
37
- To author a new patch: author the raw templates (`openapi-generator-cli author template -g csharp --library httpclient`) into a scratch dir, copy it, edit a `.mustache` in the copy, `git diff --no-index` the two trees, and normalize the diff's path headers to `csharp/<path>` so it applies under the same `-p1` convention.
38
-
39
- ### Generator env vars are passed per-call, never set at module level
40
-
41
- `JAVA_OPTS=-Dlog.level=warn` (quiets the generator) is passed as `bash(..., env={"JAVA_OPTS": "-Dlog.level=warn"})` on the two `openapi-generator-cli` calls that need it, so it reaches only those child processes and never touches this process's `os.environ`. `bashrun`'s `env=` overlays the inherited environment for that one child (see its AGENTS.md), which is the whole point — the alternative, mutating the global `os.environ`, leaks the var into every later subprocess. This covers the generator this package runs itself; a consumer's spec command needing the same gating (e.g. `CODEGEN=1`) carries it in the config's structured `spec_env` key, which rides the same per-call overlay.
42
-
43
- ### The Unity reference set is coupled to the patched templates
44
-
45
- `unity.write_unity_package_metadata` hardcodes the `.asmdef` base references `Newtonsoft.Json / Polly / JsonSubTypes`. These are exactly the runtime support the patched C# templates emit calls against (Polly retry, JsonSubTypes discriminators, Newtonsoft serialization). They travel with the patches as one unit; a consumer adds project-specific references via `extra_references`, it does not replace the base set.
46
-
47
- `BASE_DEPENDENCIES` is the UPM dual of that reference set: the same three libraries expressed as `package.json` dependencies (Newtonsoft as the Unity-blessed `com.unity.nuget.newtonsoft-json`, the others as UnityNuGet `org.nuget.*` identities), as exact versions — UPM's package.json dependencies accept SemVer values only, no range syntax, and the resolver treats a value as a minimum, so consumer manifests carrying newer minors still resolve without conflict. It travels with `BASE_REFERENCES` — change one, change the other.
48
-
49
- ### The csproj ships in the package; MSBuild output is redirected around it
50
-
51
- The generated `.csproj` stays inside the UPM package because it is the `dotnet pack` input for the nuget feed. To keep IDE builds from writing `bin/`/`obj/` into the package (Unity errors on the missing `.meta` files, and the npm tarball must not carry build output), `write_unity_package_metadata` writes a `Directory.Build.props` next to it that redirects `BaseIntermediateOutputPath` / `BaseOutputPath` two directories up — outside the package root under every consumption layout (repo checkout → client grouping dir; Unity PackageCache → `Library/`, which the asset importer ignores). The shipped template patch `0008` points the csproj's `DocumentationFile` at `$(BaseOutputPath)` for the same reason; `Directory.Build.props` is imported before the project body, so the property is visible at that evaluation point.
52
-
53
- ### What the consumer owns
54
-
55
- The spec-production command (`spec_command` + optional `spec_env`, required — the package is consumable by projects that use no Python, no uv, or no dump entry point at all; a project whose spec is a committed file needs only `cat openapi.json`), the project→clients mapping, the naming root, the npm scope, license, repository URL, and the generated output root — all fields of the `ProjectsConfig` model, all carried in the consumer's config file, none overridable per invocation. Naming derivation is fixed inside the CLI command (`ClientNaming` built inline per project from the root name and the project's last path segment); a consumer needing different names calls the per-client API directly with its own `ClientNaming`.
56
-
57
- ## See also
58
-
59
- - `README.md` — human-facing setup and usage.
60
- - [`bashrun`](https://github.com/outernet-foundation/bashrun) — the subprocess wrapper this package shells out through.
@@ -1,70 +0,0 @@
1
- # openapi-client-codegen
2
-
3
- Generate typed API clients from an OpenAPI schema. `openapi-client-codegen` wraps [OpenAPI Generator](https://openapi-generator.tech/): it downgrades OpenAPI 3.1→3.0 (the generator only accepts 3.0), authors and patches the C# templates, runs the generator, and writes Unity package metadata. It produces a Python (`httpx`) client and a Unity-consumable C# (`httpclient`) client.
4
-
5
- The generic steps live here; the caller supplies the spec-production strategy (any callable from project directory to raw spec JSON — a shell command carried in the CLI config), the project→client mapping, the naming root, the npm scope, license, repository URL, and the output root. See [`AGENTS.md`](./AGENTS.md) for the API surface, the lib-owns/consumer-owns boundary, and the patch mechanism.
6
-
7
- ## Requirements
8
-
9
- - Python 3.13+ and [uv](https://docs.astral.sh/uv/)
10
- - **Java (JDK 11+)** and **`uvx`** on PATH at runtime — the generator runs as `uvx --from 'openapi-generator-cli[jdk4py]==<pin>' ...`
11
-
12
- ## CLI
13
-
14
- The CLI is the multi-project orchestrator, invoked directly (single command, no subcommand):
15
-
16
- ```bash
17
- uv run openapi-client-codegen --config clients.json
18
- ```
19
-
20
- `clients.json` carries everything consumer-owned in one place:
21
-
22
- ```json
23
- {
24
- "projects": {"services/api": ["python", "csharp"]},
25
- "root_name": "myproject",
26
- "generated_root": "generated",
27
- "spec_command": "uv run --project . python -m src.dump_openapi",
28
- "spec_env": {"CODEGEN": "1"},
29
- "npm_scope": "org.example.myproject",
30
- "license_spdx": "Apache-2.0",
31
- "repository_url": "https://github.com/org/repo.git"
32
- }
33
- ```
34
-
35
- `spec_command` is a command that prints the project's raw OpenAPI JSON to stdout, run with the project directory as cwd — no shell sits in front of it (commands are shlex-split; operators like `|` are rejected), so env gating rides the structured `spec_env` key, overlaid per call. The config is the single source of identity: `root_name`, `generated_root`, and `spec_command` are required there, and nothing overrides them per invocation — a different identity is a different config file. The remaining flags are invocation-scoped (`--project` / `--client` filters, `--no-cache`, `--root`). The orchestrator downgrades each produced spec, skips generation when the committed `<project>/openapi.json` is byte-identical (`--no-cache` forces through), and syncs each client under `<generated-root>/<generator>/<base-name>`.
36
-
37
- To run it as a first-class command in a consumer repo, point a `[project.scripts]` entry at the same app — e.g. `generate-clients = "openapi_client_codegen.cli:app"`.
38
-
39
- The same config drives the library API, for in-process consumption from a consumer's own command:
40
-
41
- ```python
42
- from pathlib import Path
43
-
44
- from openapi_client_codegen.orchestrator import ProjectsConfig, generate_projects
45
-
46
- config = ProjectsConfig.model_validate_json(Path("clients.json").read_text(encoding="utf-8"))
47
- generate_projects(config)
48
- ```
49
-
50
- A non-uv project declares its own `spec_command` instead — e.g. `dotnet run -- dump-openapi`; a project whose spec is a committed file uses `cat openapi.json`.
51
-
52
- ## Consuming from another repo
53
-
54
- Install from PyPI:
55
-
56
- ```toml
57
- [project]
58
- dependencies = ["openapi-client-codegen>=0.1.0"]
59
- ```
60
-
61
- `bashrun` resolves transitively. At **runtime** the generator also needs Java (JDK 11+) and `uvx` on PATH. To test an unreleased change, pin the repo at a git ref in a scratch branch instead (`openapi-client-codegen = { git = "…", rev = "<sha>" }` under `[tool.uv.sources]`) and drop the pin when the release lands.
62
-
63
- ## Development
64
-
65
- ```bash
66
- uv run ruff check .
67
- uv run ruff format --check .
68
- uv run basedpyright
69
- uv run pytest
70
- ```