typekit2 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ # this_file: .env.example
2
+ TYPEKIT_API_KEY=replace-with-your-adobe-fonts-api-token
3
+
@@ -0,0 +1,61 @@
1
+ # this_file: .gitignore
2
+ # Byte-compiled / optimized / DLL files
3
+ __pycache__/
4
+ *.py[cod]
5
+ .DS_Store
6
+ .env
7
+ .venv/
8
+ .codegraph/
9
+ typekit2/__version__.py
10
+
11
+ # C extensions
12
+ *.so
13
+
14
+ # Distribution / packaging
15
+ .Python
16
+ env/
17
+ bin/
18
+ build/
19
+ develop-eggs/
20
+ dist/
21
+ eggs/
22
+ lib/
23
+ lib64/
24
+ parts/
25
+ sdist/
26
+ var/
27
+ *.egg-info/
28
+ .installed.cfg
29
+ *.egg
30
+
31
+ # Installer logs
32
+ pip-log.txt
33
+ pip-delete-this-directory.txt
34
+
35
+ # Unit test / coverage reports
36
+ htmlcov/
37
+ .tox/
38
+ .coverage
39
+ .cache
40
+ .pytest_cache/
41
+ .ruff_cache/
42
+ nosetests.xml
43
+ coverage.xml
44
+
45
+ # Translations
46
+ *.mo
47
+
48
+ # Mr Developer
49
+ .mr.developer.cfg
50
+ .project
51
+ .pydevproject
52
+
53
+ # Rope
54
+ .ropeproject
55
+
56
+ # Django stuff:
57
+ *.log
58
+ *.pot
59
+
60
+ # Sphinx documentation
61
+ docs/_build/
@@ -0,0 +1,29 @@
1
+ ---
2
+ this_file: CHANGELOG.md
3
+ ---
4
+
5
+ # Changelog
6
+
7
+ ## Unreleased
8
+
9
+ ### Changed
10
+
11
+ - Renamed the distribution and import package from `typekit` to `typekit2`.
12
+ - Replaced `setup.py` with standards-based `pyproject.toml` packaging.
13
+ - Migrated the client from Python 2 to typed Python 3.10+.
14
+ - Switched authentication from URL query parameters to the documented HTTPS `X-Typekit-Token` header.
15
+ - Reworked kit writes to use current parameter shapes and preserve omitted update fields.
16
+
17
+ ### Added
18
+
19
+ - `TYPEKIT_API_KEY` and `.env` configuration through `python-dotenv`.
20
+ - Fire CLI via `python -m typekit2` and the `typekit2` console command.
21
+ - Adobe Fonts API mapping and usage documentation.
22
+ - Offline tests for authentication, transport, payloads, errors, parsing, and CLI behavior.
23
+ - Tag-derived semantic versions using `hatch-vcs`.
24
+ - `publish.sh` workflow using `uvx gitnextver`, `uv build`, and `uv publish`.
25
+
26
+ ### Removed
27
+
28
+ - Python 2-only code, live destructive tests, `setup.py`, and generated distutils manifest.
29
+
@@ -0,0 +1,24 @@
1
+ ---
2
+ this_file: DEPENDENCIES.md
3
+ ---
4
+
5
+ # Dependencies
6
+
7
+ ## Runtime
8
+
9
+ - `requests`: HTTP transport with sessions, TLS verification, timeouts, and explicit status handling.
10
+ - `python-dotenv`: loads `TYPEKIT_API_KEY` from `.env` while preserving environment precedence.
11
+ - `fire`: generates the requested CLI from the small `TypekitCLI` command object.
12
+
13
+ ## Build and release
14
+
15
+ - `hatchling`: PEP 517 build backend configured in `pyproject.toml`.
16
+ - `hatch-vcs`: derives PEP 440 versions from semantic Git tags and writes the packaged `__version__.py`.
17
+ - `gitnextver` (run with `uvx`): creates the next semantic version commit/tag and pushes it during releases.
18
+ - `uv`: resolves dependencies, runs checks, builds wheel/sdist, and publishes distributions.
19
+
20
+ ## Development
21
+
22
+ - `pytest` and `pytest-cov`: offline contract tests and coverage enforcement.
23
+ - `ruff`: formatting and static lint checks.
24
+
typekit2-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Suchan Lee
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,161 @@
1
+ Metadata-Version: 2.5
2
+ Name: typekit2
3
+ Version: 1.0.0
4
+ Summary: Modern Python client and CLI for the Adobe Fonts (Typekit) API
5
+ Project-URL: Documentation, https://fonts.adobe.com/docs/api
6
+ Project-URL: Repository, https://github.com/fontlaborg/typekit-python
7
+ Author-email: Suchan Lee <lee.suchan@gmail.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: adobe-fonts,api,cli,typekit
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3 :: Only
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Requires-Python: >=3.10
20
+ Requires-Dist: fire>=0.7.1
21
+ Requires-Dist: python-dotenv>=1.2.3
22
+ Requires-Dist: requests>=2.32.5
23
+ Description-Content-Type: text/markdown
24
+
25
+ ---
26
+ this_file: README.md
27
+ ---
28
+
29
+ # typekit2
30
+
31
+ `typekit2` is a modern Python client and Fire CLI for the Adobe Fonts API formerly known as the Typekit API.
32
+
33
+ It replaces the abandoned `typekit` package’s Python 2 code and `setup.py` packaging with Python 3.10+, `pyproject.toml`, HTTPS header authentication, `.env` support, offline tests, and Git-tag-derived versions.
34
+
35
+ ## Install
36
+
37
+ ```bash
38
+ uv add typekit2
39
+ ```
40
+
41
+ For local development:
42
+
43
+ ```bash
44
+ uv sync
45
+ ```
46
+
47
+ Create `.env` from the supplied example, or export the key directly:
48
+
49
+ ```bash
50
+ cp .env.example .env
51
+ export TYPEKIT_API_KEY='your-token'
52
+ ```
53
+
54
+ `python-dotenv` loads `.env` without overriding an existing environment variable. Never commit `.env`; it is ignored.
55
+
56
+ ## Python API
57
+
58
+ ```python
59
+ from typekit2 import Typekit
60
+
61
+ client = Typekit() # reads TYPEKIT_API_KEY
62
+
63
+ kits = client.list_kits()
64
+ family = client.get_font_family("pcpv")
65
+ variations = client.get_font_variations("pcpv")
66
+ library = client.get_library("full", page=1, per_page=50)
67
+ ```
68
+
69
+ An explicit key is also supported:
70
+
71
+ ```python
72
+ client = Typekit(api_key="...")
73
+ ```
74
+
75
+ The compatibility keyword `api_token=` is accepted, but new code should use `api_key=` or `TYPEKIT_API_KEY`.
76
+
77
+ ### Kit workflow
78
+
79
+ ```python
80
+ created = client.create_kit(
81
+ "Example",
82
+ ["example.com", "www.example.com"],
83
+ [{"id": "pcpv", "subset": "all", "variations": ["n4", "i4"]}],
84
+ )
85
+ kit_id = created["kit"]["id"]
86
+
87
+ client.update_kit(kit_id, name="Example renamed")
88
+ client.add_font(kit_id, "gkmg", variations=["n4", "n7"])
89
+ client.publish_kit(kit_id)
90
+ ```
91
+
92
+ Publishing is asynchronous. Adobe documents that CDN propagation may take several minutes.
93
+
94
+ ## CLI
95
+
96
+ The installed `typekit2` command and `python -m typekit2` expose the same Fire CLI. Results are stable JSON.
97
+
98
+ ```bash
99
+ typekit2 doctor
100
+ typekit2 kits
101
+ typekit2 kit abc123
102
+ typekit2 kit abc123 --published=true
103
+ typekit2 family pcpv
104
+ typekit2 variations pcpv
105
+ typekit2 libraries
106
+ typekit2 library full --page=1 --per-page=50
107
+ ```
108
+
109
+ Mutating commands are explicit:
110
+
111
+ ```bash
112
+ typekit2 create-kit Example --domains=example.com,www.example.com
113
+ typekit2 add-font abc123 pcpv --variations=n4,i4 --subset=all
114
+ typekit2 update-kit abc123 --name='Renamed kit'
115
+ typekit2 publish-kit abc123
116
+ typekit2 remove-font abc123 pcpv
117
+ typekit2 remove-kit abc123
118
+ ```
119
+
120
+ For `--families`, pass a JSON list:
121
+
122
+ ```bash
123
+ typekit2 create-kit Example \
124
+ --domains=example.com \
125
+ --families='[{"id":"pcpv","subset":"all","variations":["n4","i4"]}]'
126
+ ```
127
+
128
+ Run `typekit2 --help` or `python -m typekit2 --help` for generated Fire help.
129
+
130
+ ## API behavior
131
+
132
+ - Requests use `https://typekit.com/api/v1/json`.
133
+ - Authentication uses the documented `X-Typekit-Token` header; keys never enter URLs or CLI output.
134
+ - Kit writes use URL-encoded Rails-style nested parameters.
135
+ - A 30-second timeout is applied by default and can be changed with `Typekit(timeout=...)`.
136
+ - HTTP, JSON, and documented API errors raise `TypekitAPIError`.
137
+ - `update_kit` sends only supplied fields; omitted fields are not replaced accidentally.
138
+
139
+ See [docs/API.md](docs/API.md) for the method-to-endpoint map and links to Adobe’s authoritative documentation.
140
+
141
+ ## Development
142
+
143
+ ```bash
144
+ ./test.sh
145
+ ```
146
+
147
+ The suite is offline: it uses request doubles and never creates, publishes, or deletes a real kit.
148
+
149
+ ## Releases
150
+
151
+ Versions come from Git tags through `hatch-vcs`; generated `typekit2/__version__.py` is explicitly ignored. To validate, commit/tag/push the next semantic version, build fresh distributions, and publish with `uv`:
152
+
153
+ ```bash
154
+ ./publish.sh
155
+ ```
156
+
157
+ Set `PUBLISH_SKIP_UPLOAD=1` to exercise the Git release and artifact verification flow without uploading to PyPI. `UV_PUBLISH_TOKEN` supplies a PyPI token when Trusted Publishing is unavailable.
158
+
159
+ ## License and provenance
160
+
161
+ MIT. The project began as `typekit-python` by Suchan Lee; `typekit2` is its Python 3 modernization.
typekit2-1.0.0/PLAN.md ADDED
@@ -0,0 +1,24 @@
1
+ ---
2
+ this_file: PLAN.md
3
+ ---
4
+
5
+ # Plan
6
+
7
+ The modernization target is a maintained Python 3 package whose library, CLI, documentation, and release artifacts agree with the current Adobe Fonts API contract.
8
+
9
+ ## Completed scope
10
+
11
+ - Rename both distribution and imports to `typekit2`.
12
+ - Replace legacy setup metadata with `pyproject.toml` and `hatch-vcs` versions.
13
+ - Modernize the HTTP client, HTTPS authentication, errors, typing, and API payloads.
14
+ - Load `TYPEKIT_API_KEY` from the environment or `.env`.
15
+ - Add a Fire CLI for reads and explicit kit mutations.
16
+ - Replace live account-mutating tests with deterministic request-level tests.
17
+ - Document Adobe endpoints, parameter semantics, installation, API, CLI, and releases.
18
+ - Add a guarded `gitnextver` and `uv publish` release workflow.
19
+
20
+ ## Future work
21
+
22
+ - Add opt-in live read-only smoke tests when a dedicated Adobe Fonts test account is available.
23
+ - Add API resources only when they appear in Adobe’s authoritative reference.
24
+
@@ -0,0 +1,137 @@
1
+ ---
2
+ this_file: README.md
3
+ ---
4
+
5
+ # typekit2
6
+
7
+ `typekit2` is a modern Python client and Fire CLI for the Adobe Fonts API formerly known as the Typekit API.
8
+
9
+ It replaces the abandoned `typekit` package’s Python 2 code and `setup.py` packaging with Python 3.10+, `pyproject.toml`, HTTPS header authentication, `.env` support, offline tests, and Git-tag-derived versions.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ uv add typekit2
15
+ ```
16
+
17
+ For local development:
18
+
19
+ ```bash
20
+ uv sync
21
+ ```
22
+
23
+ Create `.env` from the supplied example, or export the key directly:
24
+
25
+ ```bash
26
+ cp .env.example .env
27
+ export TYPEKIT_API_KEY='your-token'
28
+ ```
29
+
30
+ `python-dotenv` loads `.env` without overriding an existing environment variable. Never commit `.env`; it is ignored.
31
+
32
+ ## Python API
33
+
34
+ ```python
35
+ from typekit2 import Typekit
36
+
37
+ client = Typekit() # reads TYPEKIT_API_KEY
38
+
39
+ kits = client.list_kits()
40
+ family = client.get_font_family("pcpv")
41
+ variations = client.get_font_variations("pcpv")
42
+ library = client.get_library("full", page=1, per_page=50)
43
+ ```
44
+
45
+ An explicit key is also supported:
46
+
47
+ ```python
48
+ client = Typekit(api_key="...")
49
+ ```
50
+
51
+ The compatibility keyword `api_token=` is accepted, but new code should use `api_key=` or `TYPEKIT_API_KEY`.
52
+
53
+ ### Kit workflow
54
+
55
+ ```python
56
+ created = client.create_kit(
57
+ "Example",
58
+ ["example.com", "www.example.com"],
59
+ [{"id": "pcpv", "subset": "all", "variations": ["n4", "i4"]}],
60
+ )
61
+ kit_id = created["kit"]["id"]
62
+
63
+ client.update_kit(kit_id, name="Example renamed")
64
+ client.add_font(kit_id, "gkmg", variations=["n4", "n7"])
65
+ client.publish_kit(kit_id)
66
+ ```
67
+
68
+ Publishing is asynchronous. Adobe documents that CDN propagation may take several minutes.
69
+
70
+ ## CLI
71
+
72
+ The installed `typekit2` command and `python -m typekit2` expose the same Fire CLI. Results are stable JSON.
73
+
74
+ ```bash
75
+ typekit2 doctor
76
+ typekit2 kits
77
+ typekit2 kit abc123
78
+ typekit2 kit abc123 --published=true
79
+ typekit2 family pcpv
80
+ typekit2 variations pcpv
81
+ typekit2 libraries
82
+ typekit2 library full --page=1 --per-page=50
83
+ ```
84
+
85
+ Mutating commands are explicit:
86
+
87
+ ```bash
88
+ typekit2 create-kit Example --domains=example.com,www.example.com
89
+ typekit2 add-font abc123 pcpv --variations=n4,i4 --subset=all
90
+ typekit2 update-kit abc123 --name='Renamed kit'
91
+ typekit2 publish-kit abc123
92
+ typekit2 remove-font abc123 pcpv
93
+ typekit2 remove-kit abc123
94
+ ```
95
+
96
+ For `--families`, pass a JSON list:
97
+
98
+ ```bash
99
+ typekit2 create-kit Example \
100
+ --domains=example.com \
101
+ --families='[{"id":"pcpv","subset":"all","variations":["n4","i4"]}]'
102
+ ```
103
+
104
+ Run `typekit2 --help` or `python -m typekit2 --help` for generated Fire help.
105
+
106
+ ## API behavior
107
+
108
+ - Requests use `https://typekit.com/api/v1/json`.
109
+ - Authentication uses the documented `X-Typekit-Token` header; keys never enter URLs or CLI output.
110
+ - Kit writes use URL-encoded Rails-style nested parameters.
111
+ - A 30-second timeout is applied by default and can be changed with `Typekit(timeout=...)`.
112
+ - HTTP, JSON, and documented API errors raise `TypekitAPIError`.
113
+ - `update_kit` sends only supplied fields; omitted fields are not replaced accidentally.
114
+
115
+ See [docs/API.md](docs/API.md) for the method-to-endpoint map and links to Adobe’s authoritative documentation.
116
+
117
+ ## Development
118
+
119
+ ```bash
120
+ ./test.sh
121
+ ```
122
+
123
+ The suite is offline: it uses request doubles and never creates, publishes, or deletes a real kit.
124
+
125
+ ## Releases
126
+
127
+ Versions come from Git tags through `hatch-vcs`; generated `typekit2/__version__.py` is explicitly ignored. To validate, commit/tag/push the next semantic version, build fresh distributions, and publish with `uv`:
128
+
129
+ ```bash
130
+ ./publish.sh
131
+ ```
132
+
133
+ Set `PUBLISH_SKIP_UPLOAD=1` to exercise the Git release and artifact verification flow without uploading to PyPI. `UV_PUBLISH_TOKEN` supplies a PyPI token when Trusted Publishing is unavailable.
134
+
135
+ ## License and provenance
136
+
137
+ MIT. The project began as `typekit-python` by Suchan Lee; `typekit2` is its Python 3 modernization.
typekit2-1.0.0/TODO.md ADDED
@@ -0,0 +1,9 @@
1
+ ---
2
+ this_file: TODO.md
3
+ ---
4
+
5
+ # TODO
6
+
7
+ - [ ] Add opt-in live read-only smoke tests with dedicated test credentials.
8
+ - [ ] Track future changes to the Adobe Fonts API reference.
9
+
typekit2-1.0.0/WORK.md ADDED
@@ -0,0 +1,22 @@
1
+ ---
2
+ this_file: WORK.md
3
+ ---
4
+
5
+ # Work log
6
+
7
+ ## 2026-09-20
8
+
9
+ - Audited the Python 2 package, destructive live tests, and setup metadata.
10
+ - Checked current Adobe Fonts authentication, request, parameter, kit, family, and library documentation.
11
+ - Checked current Python Fire, python-dotenv, hatch-vcs, and uv packaging guidance.
12
+ - Renamed the package to `typekit2` and implemented the modern client and CLI.
13
+ - Added offline contract tests before implementation.
14
+ - Added tag-derived versioning and a guarded publish workflow.
15
+
16
+ ### Verification
17
+
18
+ - `ruff format --check` and `ruff check`: passed.
19
+ - Offline suite: 31 tests passed with 95.67% branch-aware coverage.
20
+ - Fresh `uv build --no-sources`: wheel and sdist built successfully; metadata names `typekit2`, the wheel contains `typekit2/__main__.py` and generated `typekit2/__version__.py`, and no legacy `typekit/` package is present.
21
+ - Installed-artifact smoke test from `/tmp`: both `typekit2 doctor` and `python -m typekit2 doctor` returned redacted JSON.
22
+ - Disposable release rehearsal: `publish.sh` advanced a local test repository from `v0.1.0` to `v0.1.1`, pushed matching branch/tag refs to a local bare remote, built matching artifacts, and skipped only PyPI upload under `PUBLISH_SKIP_UPLOAD=1`.
@@ -0,0 +1,45 @@
1
+ ---
2
+ this_file: docs/API.md
3
+ ---
4
+
5
+ # Adobe Fonts API map
6
+
7
+ The service and URL paths retain the Typekit name even though the product is now Adobe Fonts. The authoritative documentation is the [Adobe Fonts Typekit API reference](https://fonts.adobe.com/docs/api).
8
+
9
+ ## Authentication and transport
10
+
11
+ Adobe requires authenticated requests to use HTTPS. `typekit2` sends `TYPEKIT_API_KEY` as `X-Typekit-Token`, following the [authentication reference](https://fonts.adobe.com/docs/api/auth). It deliberately avoids the documented query-string fallback because URLs are commonly retained in logs and traces.
12
+
13
+ Form bodies follow Adobe’s [parameter encoding](https://fonts.adobe.com/docs/api/parameters): repeated values use `name[]`, while family objects use keys such as `families[0][id]`.
14
+
15
+ ## Client methods
16
+
17
+ | Python method | HTTP endpoint | Notes |
18
+ |---|---|---|
19
+ | `list_kits()` | `GET kits` | Authenticated user’s kits |
20
+ | `get_kit(id)` | `GET kits/:kit` | Current draft |
21
+ | `get_published_kit(id)` | `GET kits/:kit/published` | CDN-published version |
22
+ | `create_kit(...)` | `POST kits` | Creates a draft |
23
+ | `update_kit(...)` | `POST kits/:kit` | Supplied fields replace their current values |
24
+ | `remove_kit(id)` | `DELETE kits/:kit` | Permanent deletion |
25
+ | `publish_kit(id)` | `POST kits/:kit/publish` | Asynchronous publish |
26
+ | `add_font(...)` | `POST kits/:kit/families/:family` | Adds or replaces one family |
27
+ | `remove_font(...)` | `DELETE kits/:kit/families/:family` | Removes one family |
28
+ | `get_font_family(id)` | `GET families/:family` | IDs and slugs are accepted by Adobe |
29
+ | `get_font_variations(id)` | `GET families/:family` | Extracts FVD codes from the response |
30
+ | `list_libraries()` | `GET libraries` | Available font libraries |
31
+ | `get_library(...)` | `GET libraries/:library` | Supports `page` and `per_page` |
32
+
33
+ Reference pages: [kit manipulation](https://fonts.adobe.com/docs/api/kits), [font families](https://fonts.adobe.com/docs/api/v1/:format/families/:family), [libraries](https://fonts.adobe.com/docs/api/v1/:format/libraries), and [errors](https://fonts.adobe.com/docs/api/errors).
34
+
35
+ ## Important semantics
36
+
37
+ - Draft changes are not live until `publish_kit` is called.
38
+ - Publishing returns before CDN propagation finishes.
39
+ - When an update parameter is supplied, Adobe replaces that parameter’s existing value.
40
+ - Font variations use Font Variation Description codes such as `n4` and `i7`.
41
+ - `subset` is either `default` or `all`.
42
+ - The legacy analytics option is deprecated by Adobe and is intentionally not exposed.
43
+
44
+ The upstream documentation is old in vocabulary but remains live and dated © 2026. `typekit2` documents only behavior supported by those current pages.
45
+
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env bash
2
+ # this_file: publish.sh
3
+ set -euo pipefail
4
+ cd "$(dirname "${BASH_SOURCE[0]}")"
5
+
6
+ die() { printf '%s\n' "$*" >&2; exit 1; }
7
+ command -v uv >/dev/null || die 'uv is required.'
8
+ command -v uvx >/dev/null || die 'uvx is required.'
9
+ [[ "$(git rev-parse --show-prefix)" == "" ]] || die 'Run from the package Git root.'
10
+ git symbolic-ref --quiet HEAD >/dev/null || die 'Release from a branch, not detached HEAD.'
11
+
12
+ version_file=typekit2/__version__.py
13
+ git check-ignore --no-index -q "$version_file" || die "$version_file must be gitignored."
14
+
15
+ # Keep source changes for gitnextver to commit; remove only generated version dirt.
16
+ git rm --cached --ignore-unmatch -- "$version_file"
17
+ git clean -fX -- "$version_file" .DS_Store
18
+ uv sync --frozen --all-groups --reinstall-package typekit2
19
+ UV_FROZEN=1 ./test.sh
20
+ git clean -fX -- "$version_file" .DS_Store
21
+
22
+ # gitnextver stages source changes, creates the next SemVer commit/tag, and pushes.
23
+ uvx gitnextver --directory "$PWD"
24
+ [[ -z "$(git status --porcelain --untracked-files=all)" ]] || die 'gitnextver left uncommitted changes.'
25
+ release_tag=$(git describe --exact-match --tags --match 'v[0-9]*' HEAD) ||
26
+ die 'gitnextver did not create a release tag.'
27
+ [[ "$release_tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] || die "Not a SemVer release tag: $release_tag"
28
+
29
+ # Revalidate because gitnextver may integrate upstream changes before tagging.
30
+ uv sync --frozen --all-groups --reinstall-package typekit2
31
+ UV_FROZEN=1 ./test.sh
32
+ [[ -z "$(git status --porcelain --untracked-files=all)" ]] || die 'Validation dirtied the release tree.'
33
+
34
+ # Prove branch and tag reached the remote instead of trusting a success banner.
35
+ remote=$(git remote | head -n 1)
36
+ if git remote get-url origin >/dev/null 2>&1; then remote=origin; fi
37
+ if [[ -n "$remote" ]]; then
38
+ branch=$(git symbolic-ref --short HEAD)
39
+ git push "$remote" "HEAD:refs/heads/$branch" "refs/tags/$release_tag"
40
+ remote_head=$(git ls-remote "$remote" "refs/heads/$branch" | cut -f1)
41
+ remote_tag=$(git ls-remote "$remote" "refs/tags/$release_tag" | cut -f1)
42
+ [[ "$remote_head" == "$(git rev-parse HEAD)" ]] || die 'Remote branch does not match HEAD.'
43
+ [[ "$remote_tag" == "$(git rev-parse "refs/tags/$release_tag")" ]] || die 'Remote tag mismatch.'
44
+ fi
45
+
46
+ # A fresh directory prevents stale dist artifacts from being uploaded.
47
+ release_dist=$(mktemp -d "${TMPDIR:-/tmp}/typekit2-release.XXXXXX")
48
+ trap 'rm -rf -- "$release_dist"' EXIT
49
+ uv build --no-sources --out-dir "$release_dist"
50
+ uv run --frozen python - "$release_tag" "$release_dist" <<'PY'
51
+ import email
52
+ from pathlib import Path
53
+ import sys
54
+ import tarfile
55
+ import zipfile
56
+
57
+ expected, folder = sys.argv[1].removeprefix("v"), Path(sys.argv[2])
58
+ wheels, sdists = list(folder.glob("*.whl")), list(folder.glob("*.tar.gz"))
59
+ assert len(wheels) == len(sdists) == 1, "Expected one wheel and one sdist"
60
+ with zipfile.ZipFile(wheels[0]) as wheel:
61
+ metadata_name = next(name for name in wheel.namelist() if name.endswith(".dist-info/METADATA"))
62
+ assert email.message_from_bytes(wheel.read(metadata_name))["Version"] == expected
63
+ generated = {}
64
+ exec(wheel.read("typekit2/__version__.py"), generated)
65
+ assert generated["__version__"] == expected
66
+ with tarfile.open(sdists[0]) as sdist:
67
+ metadata_name = next(member for member in sdist.getmembers() if member.name.endswith("/PKG-INFO"))
68
+ assert email.message_from_bytes(sdist.extractfile(metadata_name).read())["Version"] == expected
69
+ print(f"Verified wheel, sdist, and generated version: {expected}")
70
+ PY
71
+ [[ -z "$(git status --porcelain --untracked-files=all)" ]] || die 'Build dirtied the release tree.'
72
+ mkdir -p dist
73
+ cp "$release_dist"/*.whl "$release_dist"/*.tar.gz dist/
74
+ if [[ "${PUBLISH_SKIP_UPLOAD:-0}" == 1 ]]; then
75
+ printf 'Validated %s; PUBLISH_SKIP_UPLOAD=1 skips only the package upload.\n' "$release_tag"
76
+ else
77
+ uv publish "$release_dist"/*.whl "$release_dist"/*.tar.gz "$@"
78
+ printf 'Published %s.\n' "$release_tag"
79
+ fi
80
+