ai-kit-python 1.4.2__py3-none-any.whl → 1.4.3__py3-none-any.whl
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.
- {ai_kit_python-1.4.2.dist-info → ai_kit_python-1.4.3.dist-info}/METADATA +3 -1
- {ai_kit_python-1.4.2.dist-info → ai_kit_python-1.4.3.dist-info}/RECORD +23 -20
- aikit/.agents/skills/project-continuity/SKILL.md +2 -2
- aikit/.github/workflows/publish-pypi.yml +17 -8
- aikit/CHANGELOG.md +9 -5
- aikit/CONTRIBUTING.md +1 -1
- aikit/README.md +22 -6
- aikit/SKILL.md +1 -1
- aikit/VERSION +1 -1
- aikit/docs/PYPI.md +30 -14
- aikit/docs/PYPI_DESCRIPTION.md +2 -0
- aikit/docs/V1_4_2.md +17 -2
- aikit/docs/V1_4_3.md +33 -0
- aikit/docs/history/v0.13.0-changelog.md +8 -0
- aikit/scripts/check_distribution.py +59 -4
- aikit/scripts/check_kit.py +1 -1
- aikit/tests/test_pypi_events.py +201 -0
- {ai_kit_python-1.4.2.dist-info → ai_kit_python-1.4.3.dist-info}/WHEEL +0 -0
- {ai_kit_python-1.4.2.dist-info → ai_kit_python-1.4.3.dist-info}/entry_points.txt +0 -0
- {ai_kit_python-1.4.2.dist-info → ai_kit_python-1.4.3.dist-info}/licenses/LICENSE +0 -0
- {ai_kit_python-1.4.2.dist-info → ai_kit_python-1.4.3.dist-info}/licenses/template/ai-kit/vendor/security-audit/LICENSE +0 -0
- {ai_kit_python-1.4.2.dist-info → ai_kit_python-1.4.3.dist-info}/licenses/template/ai-kit/vendor/strix/LICENSE +0 -0
- {ai_kit_python-1.4.2.dist-info → ai_kit_python-1.4.3.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ai-kit-python
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.3
|
|
4
4
|
Summary: Offline installer and portable instructions that give AI coding agents project memory, stack rules, guardrails, and adaptive model routing.
|
|
5
5
|
Author: Igor Sazonov
|
|
6
6
|
License-Expression: MIT AND Apache-2.0
|
|
@@ -121,6 +121,8 @@ The distribution excludes root maintainer AGENTS.md, PROJECT_CONTEXT.md and WIKI
|
|
|
121
121
|
|
|
122
122
|
A separate [Docker image](https://hub.docker.com/r/tigusigalpa/ai-kit) packages the CLI for container use.
|
|
123
123
|
|
|
124
|
+
Package releases follow GitHub Release, automated checks and owner approval before PyPI upload through Trusted Publishing. Future stable tags must match VERSION; manual runs default to validation. See the [release procedure](https://github.com/tigusigalpa/ai-kit/blob/main/docs/PYPI.md#release-to-pypi-with-owner-approval).
|
|
125
|
+
|
|
124
126
|
## Documentation and license
|
|
125
127
|
|
|
126
128
|
- [Repository and full README](https://github.com/tigusigalpa/ai-kit)
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
ai_kit_python-1.4.
|
|
2
|
-
ai_kit_python-1.4.
|
|
3
|
-
ai_kit_python-1.4.
|
|
1
|
+
ai_kit_python-1.4.3.dist-info/licenses/LICENSE,sha256=Q4UBNXri7WF3F5T3Z2C_S22HoXWvOxiOs3CxSfPPNKE,1093
|
|
2
|
+
ai_kit_python-1.4.3.dist-info/licenses/template/ai-kit/vendor/security-audit/LICENSE,sha256=5ZjmlKpQZlDHGS1eo74OUKyg01bqAFUcU0KfCwHrY5E,1078
|
|
3
|
+
ai_kit_python-1.4.3.dist-info/licenses/template/ai-kit/vendor/strix/LICENSE,sha256=fblpcTQlHjFL2NOSV_4XDJiz4q2bLtZ-l6yvlhPnueM,11345
|
|
4
4
|
aikit/.dockerignore,sha256=w-_fnJAWI5nBcSjGJci5OE2bYzlojYSw99gg7JEJ3xM,1378
|
|
5
5
|
aikit/.gitattributes,sha256=L4mMl0D8gLiDXx1_soWU2NSdM8TnWyp672qCVbe21sA,170
|
|
6
6
|
aikit/.gitignore,sha256=OOFZre0vtV5U5t7nCO9lf2JMMlv8TEwteIaVQ3h6ahA,912
|
|
7
7
|
aikit/BOOTSTRAP_PROMPT.md,sha256=7r4CmBIfFPal5QilpsomlGIomEzGlI6nVbmksbEuMlw,1189
|
|
8
|
-
aikit/CHANGELOG.md,sha256
|
|
8
|
+
aikit/CHANGELOG.md,sha256=-p-YkUAkLkq2VFISx2hyyZtTF4QhdwVEEVAGgGV0zhA,11910
|
|
9
9
|
aikit/CLAUDE.md,sha256=R82TyQfYerEAkfrYIpVEcWZALc0q8tSucBe8RfSFerc,38
|
|
10
|
-
aikit/CONTRIBUTING.md,sha256=
|
|
10
|
+
aikit/CONTRIBUTING.md,sha256=td7vcyuQ4NIKiSGNKuSmpA3oL8-nfTJOoMWRdyKKBGk,5445
|
|
11
11
|
aikit/Dockerfile,sha256=8uUyWxXYV2dfr9VCulzeJEEO5RGXg-5hEuN1kkYXlbY,1852
|
|
12
12
|
aikit/KIMI.md,sha256=4xJ2oTEepf7mj-Klp7ymjoXRksZO45bE1x3NIyCIr4A,278
|
|
13
13
|
aikit/LICENSE,sha256=Q4UBNXri7WF3F5T3Z2C_S22HoXWvOxiOs3CxSfPPNKE,1093
|
|
14
14
|
aikit/MANIFEST.in,sha256=n9Yb2mcrR_mkNVXVSagxx6OUoLvbIsce8rDJk0SCa74,813
|
|
15
15
|
aikit/MANUS.md,sha256=gVV5e04bVPecmyKzGaJNUq2pryknFafQNDiPmJ0ufq8,279
|
|
16
16
|
aikit/Makefile,sha256=R_VqJHSi9awBzs24uccOioVXuso2Sz9WmnuHwYUXrd0,1476
|
|
17
|
-
aikit/README.md,sha256=
|
|
17
|
+
aikit/README.md,sha256=6c25fH-SRvAMKT55kOmFd8jtwZy-OjfQ2DzrRcGmx6k,96591
|
|
18
18
|
aikit/SECURITY.md,sha256=wATqnw5W4VyCqFhd-ksCBPQKQMx_neJo9RRxWykg9gg,922
|
|
19
|
-
aikit/SKILL.md,sha256=
|
|
20
|
-
aikit/VERSION,sha256=
|
|
19
|
+
aikit/SKILL.md,sha256=8gH7-I-Zzp4yUKyU9p1sVHRBIOc4up_YNqjecSaGjJo,11993
|
|
20
|
+
aikit/VERSION,sha256=vTHo2c-NFKHp4_3zufalJIGa7Q0bFGJ1I8Z2V2fH2PE,6
|
|
21
21
|
aikit/__init__.py,sha256=aNy2HbTnXlBlSZu868pK2uv1jLohV129gQn4dbup4k4,59
|
|
22
22
|
aikit/aikit_cli.py,sha256=NPTFFYSZXSeZyRwEyEiixy0MCLfrXmHwpzJjWofoR-s,2051
|
|
23
23
|
aikit/pyproject.toml,sha256=MWqpmeNwnDT7aMKNZVADklS_XEutbOGE8wnZfnBE8cg,2704
|
|
24
24
|
aikit/.agents/skills/go-work/SKILL.md,sha256=Hs1urC4XsEufaUyO5L-_Nb2s2TusX-niuXdnZ9BCD58,421
|
|
25
25
|
aikit/.agents/skills/laravel-work/SKILL.md,sha256=dJ55mRc2SPY2uTPArfDWZ-YtO7rfLYnv9E-tc91wzVY,589
|
|
26
26
|
aikit/.agents/skills/moodle-work/SKILL.md,sha256=ZwQTExl6MyK1QrgcHKqr-9pPow_NYRzOJwMuQZN0o5U,439
|
|
27
|
-
aikit/.agents/skills/project-continuity/SKILL.md,sha256=
|
|
27
|
+
aikit/.agents/skills/project-continuity/SKILL.md,sha256=vfUTJPeKkU-y4JJZeqRm6WatzsZBdHR98oTLXzMUOGI,11926
|
|
28
28
|
aikit/.agents/skills/python-work/SKILL.md,sha256=UBSgsbF7HF_8YvZgtI5v2Y0tw4BTzMcuV8hPcck6Trw,510
|
|
29
29
|
aikit/.github/workflows/check-kit.yml,sha256=p0sg5S8I7yyukCV_I5OrTvUFsGYGhEbKjHezSrH76HA,2809
|
|
30
|
-
aikit/.github/workflows/publish-pypi.yml,sha256=
|
|
30
|
+
aikit/.github/workflows/publish-pypi.yml,sha256=870enVcLWt7BMUSz2ksn86YbSl5bHkTAleFFKa_FTRw,4489
|
|
31
31
|
aikit/ai-kit/ADAPTERS.md,sha256=q4eH7mZnEWcpE1NnLNjy7Z18pnfurq3uGvPPB6uDdUw,335
|
|
32
32
|
aikit/ai-kit/CONTAINERS.md,sha256=Hegm0nmRDvOEa0Tz5jJ3N8EmcDidce9pHSbrPH-vQW8,339
|
|
33
33
|
aikit/ai-kit/ENGINEERING.md,sha256=Q9wOssaJOsSp1j2MT0NlTJ774rnVUK4H6O-T9NcU1ug,344
|
|
@@ -53,15 +53,16 @@ aikit/docs/DOCKER_HUB_OVERVIEW.md,sha256=ULsPKzx9e4uiu2yPfAVkSsBMOKaQ1_u83xXJRN4
|
|
|
53
53
|
aikit/docs/EVALUATION.md,sha256=TuP1Wj_aJqHixtNlacExMHzHsWqrb-pcG4dx7XDCU6Y,5383
|
|
54
54
|
aikit/docs/IMPLEMENTATION.md,sha256=TptjeIGfKvff-0JSLbI9WHv5E_kKX1t36TbOdfBvKOk,11923
|
|
55
55
|
aikit/docs/MANUS_REVIEW.md,sha256=kckW418jSVw9uLoIyEJXCATKQaVSiMV0woCfKYpih8Y,5238
|
|
56
|
-
aikit/docs/PYPI.md,sha256=
|
|
57
|
-
aikit/docs/PYPI_DESCRIPTION.md,sha256=
|
|
56
|
+
aikit/docs/PYPI.md,sha256=gYdaz77hXt2rCmSNvXYxZP8rHbcCMF4IFgFzrpwI_Rg,11884
|
|
57
|
+
aikit/docs/PYPI_DESCRIPTION.md,sha256=1TiDJwQvWK9E8YTvSgpRstXm7GAuE0h22ct-GCLNyWg,6382
|
|
58
58
|
aikit/docs/SECURITY_AUDIT.md,sha256=N4iVkwSXOJYyZ6vh7kTdSzL_1Pxu4SD0XU8Mgsu9p5k,5185
|
|
59
59
|
aikit/docs/STRIX.md,sha256=NLm6zV_p4uCjC5Mh4CL_GYRRNnSCpUmrOL-VgrzZSOc,6837
|
|
60
60
|
aikit/docs/V0_5_0.md,sha256=XGrBhvLsTegvIo2gOc5Mt2vaC5IWX67xUPqN5J5WR90,3186
|
|
61
61
|
aikit/docs/V0_6_4.md,sha256=kHKiU83LQv7Iq6rWOzGBurxnBNgKwkOrDgp4h7wltq0,2865
|
|
62
62
|
aikit/docs/V1_4_0.md,sha256=6GkMQ7N6x9deSB-7318K2rsC7BfG-Chex8YXhyFY_6A,8918
|
|
63
63
|
aikit/docs/V1_4_1.md,sha256=RuR1SxGLf4Ncd7IlaD3Hp3-znhjcl3XPydTUMwpJA54,6373
|
|
64
|
-
aikit/docs/V1_4_2.md,sha256=
|
|
64
|
+
aikit/docs/V1_4_2.md,sha256=VWoL-d1RJoUsbWl-uhKpq52bgmeMxWylfUgCLj_JhqQ,5050
|
|
65
|
+
aikit/docs/V1_4_3.md,sha256=8L0WfosF3J94T_H2RLjGGcLaHReRwQn0anAeQgLJcAE,4250
|
|
65
66
|
aikit/docs/WEB_DESIGN.md,sha256=K5Y_yZlyiYaWr8PikIpbZCQmHGnkhR2vokNNRuGoeqM,11819
|
|
66
67
|
aikit/docs/WINDOWS_CI_FIX.md,sha256=2HdCvpewggF0i2rnvKZiu2JAOHbVfKFvzlExradSTRc,3288
|
|
67
68
|
aikit/docs/adr/0000-template.md,sha256=voTxxy1-LqBBDzsp_-8B9lL-aVtmbMhIyeZtSZHNDc8,493
|
|
@@ -82,6 +83,7 @@ aikit/docs/adr/0014-local-dialogue-memory.md,sha256=MoRRfTb-swVd7EefofOJdhMjZ2u4
|
|
|
82
83
|
aikit/docs/history/original-bundle-checks.md,sha256=u8vcuLU00e6rfwzN4x9Hjhklj-RyDHUj9HPj8s6iDOM,1791
|
|
83
84
|
aikit/docs/history/v0.1-changelog.txt,sha256=ehslKASZnr-Q5ejqr4X4ShAeAhvWrYTIGs9edFLwJTg,6721
|
|
84
85
|
aikit/docs/history/v0.10-changelog.md,sha256=pv323p2EX6itkbPNK8Wxqoz3PEZDi3zjDmlXA1oHElY,2123
|
|
86
|
+
aikit/docs/history/v0.13.0-changelog.md,sha256=xYBsT5SPdCK17JnCSi_oxzrr5CiDKYalHWlDmEP1daw,1064
|
|
85
87
|
aikit/docs/history/v0.2-changelog.md,sha256=UZUd0-tzlDBfIqlSItNtPN6kmSSx-1Hy-fdo_i7ir5E,4974
|
|
86
88
|
aikit/docs/history/v0.3-changelog.md,sha256=cJoRPlSlyA7OAvMfVURKvKs54C5k2tyQLBl6c8bL0pI,3983
|
|
87
89
|
aikit/docs/history/v0.4-changelog.md,sha256=VNoFetIxEWE4V7EF41I8l-9ilk18MM1aLamB4gs7FAI,4881
|
|
@@ -110,8 +112,8 @@ aikit/scripts/adaptive.py,sha256=1epV_Qj68k33Dp92J7YfOOSM4Lx5_6ae5uF1KdCk3vs,103
|
|
|
110
112
|
aikit/scripts/adr.py,sha256=MiznKf87_lnd_aefa-WcIjLvqqvndhGUSrh7_r6-okY,2149
|
|
111
113
|
aikit/scripts/benchmark.py,sha256=zpqN6tzX9jWuf1M5bH-j4rBnTm-k5hSJr5CGKVOEIcc,31399
|
|
112
114
|
aikit/scripts/changelog.py,sha256=NVhRrHMQvLkHEkGEdByOt-CPRaRaaMPdMcPNhMnjZ6s,1868
|
|
113
|
-
aikit/scripts/check_distribution.py,sha256=
|
|
114
|
-
aikit/scripts/check_kit.py,sha256=
|
|
115
|
+
aikit/scripts/check_distribution.py,sha256=fFnWoZeJ0WD_HTFJwLZcSKp1J6-tA1zyPkzEe1_lvb4,12816
|
|
116
|
+
aikit/scripts/check_kit.py,sha256=17dJJkygEe6bM6ebBMaxRvVvVHTsat_WPQQD1scErWA,23194
|
|
115
117
|
aikit/scripts/context.py,sha256=iVxiJQ3ZeU-yqFDRRlzq4zxPCz9AtrXTnQAPWJqzNko,9679
|
|
116
118
|
aikit/scripts/docker_test_usage.py,sha256=GksIYslTVKm9mcFDulle_FtkMuKjVzHfdAr5FLXB0Bk,2529
|
|
117
119
|
aikit/scripts/doctor.py,sha256=VgWUFHkCVaY3c03HBzas3aLgh1K9UZvA9R5UUHy9EDQ,16057
|
|
@@ -264,6 +266,7 @@ aikit/tests/test_memory.py,sha256=bv1aiYMWBSMbvICa7fLTZnLeckAV2JSSApOoS2KmSXI,20
|
|
|
264
266
|
aikit/tests/test_metrics.py,sha256=SaZoNFNH66p74QucZPJRD0U8LkKoMUIqsY5x0Gk-WMI,11073
|
|
265
267
|
aikit/tests/test_native.py,sha256=YxU-mSnf7zT7EY1u4-SMRY0pcPws9ko86uilzd1WhXE,19068
|
|
266
268
|
aikit/tests/test_onboarding.py,sha256=0nJdWnfZVDtTTSQ4alcpgQgd-8_KpgI_Bl79SwWts7c,15255
|
|
269
|
+
aikit/tests/test_pypi_events.py,sha256=cn3TaZsgevqqaJaSHqMEX_9IOJlXZzvbtim0iDG50e0,10294
|
|
267
270
|
aikit/tests/test_router.py,sha256=bA_AWDxzn7l_G5cTHd9uvCadRlnpaY9DGH5lTzpXPeY,19667
|
|
268
271
|
aikit/tests/test_security_audit.py,sha256=kPW5Mjx_G0fWIxHr6sjPNF-HsdGrM_cXqFIL-873fvY,13527
|
|
269
272
|
aikit/tests/test_strix.py,sha256=ttBTs60eXUnemfn5sJvL4Sm329flcLXivv9U_RkRsFo,12867
|
|
@@ -271,8 +274,8 @@ aikit/tests/test_web_design.py,sha256=QU-1Nikt31tKSSp42QP7EjelUgFqHtQcm9Qtr6ixQG
|
|
|
271
274
|
aikit/tests/wheel_smoke.py,sha256=YqBDwMqpbJP0IoCFCVcyLr6wGCQg0Da6uZFWNP0KCFE,5842
|
|
272
275
|
aikit/tests/fixtures/create-junction.ps1,sha256=gWIMFbA3oQ8siQjy3wh2wU3SYPoF9K3rRlG3RgruBM4,632
|
|
273
276
|
aikit/tests/fixtures/golden/claude-settings-local.json,sha256=YEOcQqZs-Q93j8e4JMoxyAHLpk6ANSMduieMU6EzqCA,1340
|
|
274
|
-
ai_kit_python-1.4.
|
|
275
|
-
ai_kit_python-1.4.
|
|
276
|
-
ai_kit_python-1.4.
|
|
277
|
-
ai_kit_python-1.4.
|
|
278
|
-
ai_kit_python-1.4.
|
|
277
|
+
ai_kit_python-1.4.3.dist-info/METADATA,sha256=B4Uog5_ZbJnWV1eoJK2PhauuI_AQBfXot4TuaUvvaN8,7792
|
|
278
|
+
ai_kit_python-1.4.3.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
279
|
+
ai_kit_python-1.4.3.dist-info/entry_points.txt,sha256=cPpNaVjBYz9S8t0ysZf37HXnBsXiX14yZX-C1fePz4s,47
|
|
280
|
+
ai_kit_python-1.4.3.dist-info/top_level.txt,sha256=Ck-hW3dWXfVl4ca1lZReDP49M4R4lPu0zdyrt29YuqI,6
|
|
281
|
+
ai_kit_python-1.4.3.dist-info/RECORD,,
|
|
@@ -21,14 +21,14 @@ For measurement-led changes, test schema 1/2/3 compatibility, fixed A/B controls
|
|
|
21
21
|
Resolve temporary fixture roots before deriving paths used by guards, assertions, or injected failures. Test equivalent path spellings; keep cleanup bound to original disposable directories. [Windows CI regression](../../../docs/WINDOWS_CI_FIX.md) records the relevant scenario.
|
|
22
22
|
For reference upgrades, check the actual published tree and old paths left by overlays. Compatibility pointers link to canonical owners; never hide broken legacy links from the checker.
|
|
23
23
|
When editing onboarding docs, verify examples against the actual CLI/defaults and preserve links to policy owners. Keep installation, native discovery, and measured execution claims distinct. For [OpenRouter documentation](../../../README.md#working-with-openrouter), recheck primary API/cache/usage docs; distinguish shipped context manifests from proposed request assembly/runtime, prompt caching from response reuse, and observed usage from assumed savings. Root README and Docker Hub Overview have no size quota; retain scoped instruction budgets and normal link/language checks.
|
|
24
|
-
For [PyPI releases](../../../docs/PYPI.md), verify
|
|
24
|
+
For [PyPI releases](../../../docs/PYPI.md), verify metadata, VERSION, licenses, description, resources and private/path exclusions; build the wheel from sdist and smoke-install offline. Quote Twine globs; report OS gaps. A JSON 404 does not prove name eligibility. Test release/manual identity guards without uploads; preserve CI names and old tags. Published stable releases prepare PyPI artifacts; manual runs default to validation. Require exact version/tag/repository gates, owner approval and future tag coverage; agents must not approve deployments. OIDC belongs only in upload jobs consuming validated same-run artifacts. Keep setup, upload and live MCP activation separate.
|
|
25
25
|
For repository administration or changes to existing required CI names, consult [repository maintenance](../../../CONTRIBUTING.md#repository-maintenance), verify live protection and emitted names, and keep required checks aligned. New local jobs establish no hosted result or required-check update. Remote changes need explicit authorization; Core governs commits/pushes.
|
|
26
26
|
For every task assess [Docker image impact](../../../docs/DOCKER.md#change-impact-and-image-refresh), including packaged instructions and defaults. Follow canonical [image freshness](../../../template/ai-kit/CONTAINERS.md#image-freshness); rebuild/test affected final sources as ai-kit:dev or record why no rebuild is needed. For the image, verify actual client/daemon, pinned build inputs, VERSION/label identity, closed context, wheel resources, non-root bind permissions, read-only/offline preview and apply, preservation, conflicts, rerun and SIGTERM. Validate the Job schema; distinguish cluster/runtime coverage. Record test cache/volume ownership and size; follow canonical cleanup without shared-builder pruning. Keep image publication separate.
|
|
27
27
|
For [dialogue memory](../../../docs/DIALOGUE_MEMORY.md), test all supported imports, selected ChatGPT branches, omissions, untrusted roles, pending prose, explicit keyed conflicts/resolutions, source-bound review, immutable reruns, both exports and byte limits. Exercise linked/blocked paths, private/team upgrade preservation, Git/build/package exclusions and installed CLI execution. Do not infer semantic agreement from no keyed conflicts or claim live sync/upload from local export. Preserve raw sources and default project context.
|
|
28
28
|
For optional upstream skills, follow [security-audit](../../../docs/SECURITY_AUDIT.md) and [Strix integration](../../../docs/STRIX.md): preserve pinned bytes/license and newline attributes, verify bounded tree coverage/digests and nested-path aliases, explicit-only metadata, absent default/preset payloads, independent persistent choices, local adaptations/conflicts, links, visibility and wheel resources. Exempt only verified external examples from authored model/language/budget checks. Keep installation separate from audits/runtime setup; report unsupported validators. For Strix, check writable copy isolation, explicit full/diff scope, budget/data destination and no implicit cloud fallback; integration tests must start no scan, model or container.
|
|
29
29
|
For [Context7](../../../docs/CONTEXT7.md), check opt-in skill/registry selection, independent persistence, source/native copies, local conflicts and unchanged native MCP configuration. Keep normal skill discovery, version-aware sanitized queries and remote activation separate. Recheck client schemas/authentication against primary docs; a source revision does not pin the hosted service.
|
|
30
30
|
For [Web Design](../../../docs/WEB_DESIGN.md), check whole-module default absence, multi-entry skill selection, independent persistence, created-once project documents, native copies/retirement, local conflicts and linked/blocked paths. Exercise workflow context scope, required design memory above budgets, plain HTML without assumed stack facts, private raw output/team visibility and offline wheel resources. Keep TypeUI/SkillUI setup and real browser/Laravel acceptance separate from installer evidence; adopt external guidance only within the selected stack/visual system.
|
|
31
|
-
For
|
|
31
|
+
For a requested release, synchronize VERSION, root changelog, README, WIKI and maintainer context; keep kit history out of templates. Recheck CLI/version, kit, isolated archives, offline installation and prior-version upgrade preservation. Distinguish checks before/after labeling. Follow [the owner-approved release path](../../../docs/V1_4_3.md): preserve tags, wait for hosted build checks and report pending human approval; never approve PyPI for the owner. Git authorization follows Core. Release notes must distinguish installed guidance from external tools, live client/model setup and generated-site acceptance; verify security examples against pinned contracts.
|
|
32
32
|
For every Docker image release, follow the [Docker Hub checklist](../../../CONTRIBUTING.md#docker-hub-releases) and update the complete [Overview](../../../docs/DOCKER_HUB_OVERVIEW.md); synchronize version/features/examples before building and verified publication identity afterward.
|
|
33
33
|
For the current Docker/privacy release, follow [v1.4.1 preparation](../../../docs/V1_4_1.md); keep local image identity distinct from a registry digest and source-bound human review distinct from automatic conversation synchronization. After explicitly authorized publication, read back the registry digest, platform and version/configuration; preserve the prepared image and record later publication-only documentation separately. Do not assign additional tags implicitly.
|
|
34
34
|
Do not bootstrap the reference repository as an application or imply native/runtime checks passed from static validation.
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
name: Prepare or publish Python package
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
4
6
|
workflow_dispatch:
|
|
5
7
|
inputs:
|
|
6
8
|
destination:
|
|
@@ -19,11 +21,15 @@ permissions:
|
|
|
19
21
|
contents: read
|
|
20
22
|
|
|
21
23
|
concurrency:
|
|
22
|
-
group: python-package-${{ github.ref }}-${{ inputs.destination }}
|
|
24
|
+
group: python-package-${{ github.ref }}-${{ github.event_name == 'release' && 'pypi' || inputs.destination }}
|
|
23
25
|
cancel-in-progress: false
|
|
24
26
|
|
|
25
27
|
jobs:
|
|
26
28
|
build:
|
|
29
|
+
if: >-
|
|
30
|
+
github.event_name == 'workflow_dispatch' ||
|
|
31
|
+
(github.event_name == 'release' && github.event.action == 'published' &&
|
|
32
|
+
!github.event.release.draft && !github.event.release.prerelease)
|
|
27
33
|
runs-on: ubuntu-latest
|
|
28
34
|
steps:
|
|
29
35
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
@@ -32,13 +38,10 @@ jobs:
|
|
|
32
38
|
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
|
|
33
39
|
with:
|
|
34
40
|
python-version: '3.13'
|
|
35
|
-
- name: Verify
|
|
36
|
-
env:
|
|
37
|
-
RELEASE_VERSION: ${{ inputs.version }}
|
|
38
|
-
DESTINATION: ${{ inputs.destination }}
|
|
41
|
+
- name: Verify release or manual event identity before building
|
|
39
42
|
run: >-
|
|
40
43
|
python scripts/check_distribution.py identity
|
|
41
|
-
--
|
|
44
|
+
--event-name "$GITHUB_EVENT_NAME" --event-path "$GITHUB_EVENT_PATH"
|
|
42
45
|
--ref "$GITHUB_REF" --repository "$GITHUB_REPOSITORY"
|
|
43
46
|
- name: Validate sources and behavior
|
|
44
47
|
run: |
|
|
@@ -65,7 +68,10 @@ jobs:
|
|
|
65
68
|
|
|
66
69
|
testpypi:
|
|
67
70
|
needs: build
|
|
68
|
-
if:
|
|
71
|
+
if: >-
|
|
72
|
+
needs.build.result == 'success' && github.event_name == 'workflow_dispatch' &&
|
|
73
|
+
inputs.destination == 'testpypi' && github.repository == 'tigusigalpa/ai-kit' &&
|
|
74
|
+
startsWith(github.ref, 'refs/tags/v')
|
|
69
75
|
runs-on: ubuntu-latest
|
|
70
76
|
environment:
|
|
71
77
|
name: testpypi
|
|
@@ -87,7 +93,10 @@ jobs:
|
|
|
87
93
|
|
|
88
94
|
pypi:
|
|
89
95
|
needs: build
|
|
90
|
-
if:
|
|
96
|
+
if: >-
|
|
97
|
+
needs.build.result == 'success' &&
|
|
98
|
+
(github.event_name == 'release' || inputs.destination == 'pypi') &&
|
|
99
|
+
github.repository == 'tigusigalpa/ai-kit' && startsWith(github.ref, 'refs/tags/v')
|
|
91
100
|
runs-on: ubuntu-latest
|
|
92
101
|
environment:
|
|
93
102
|
name: pypi
|
aikit/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
# AI-KIT changelog
|
|
2
2
|
|
|
3
|
+
## v1.4.3 (2026-10-16)
|
|
4
|
+
|
|
5
|
+
- Added a live PyPI version badge to README.
|
|
6
|
+
- Added stable GitHub Release publication preparation, event/version/repository guards and regression tests; manual validation stays the default. PyPI upload waits for the owner. The live tag policy now accepts v* with reviewer protections preserved. [Procedure](docs/PYPI.md#release-to-pypi-with-owner-approval). Archived v0.13.0 text intact to retain the changelog budget.
|
|
7
|
+
- Added fifteen event regression tests. Before release labeling, 32 hosted CI checks and build-only workflow verification passed; kit checks, 368 packaged Linux tests (two platform skips), archive/privacy/Twine/offline wheel checks and Docker smoke also passed. [Release verification](docs/V1_4_3.md).
|
|
8
|
+
- Verified 1.4.3 CLI/archive/image identity, offline packaging, Docker smoke, 368 Linux tests and preserved private/team upgrades from v1.4.2.
|
|
9
|
+
|
|
3
10
|
## v1.4.2 (2026-10-16)
|
|
4
11
|
|
|
5
12
|
- Verified kit/archive/offline installation, 351 Linux tests (two skips), 23 Windows CLI/archive tests and Docker smoke. [Evidence](docs/V1_4_2.md).
|
|
6
|
-
-
|
|
13
|
+
- Published `ai-kit-python` 1.4.2 with verified PyPI hashes and installation. Preserved v1.4.1 Git/Docker; [evidence](docs/V1_4_2.md).
|
|
7
14
|
- Fixed quoted Twine wildcards for Windows CI.
|
|
8
15
|
- Added PyPI metadata, a portable [description](docs/PYPI_DESCRIPTION.md), README guidance and a [release procedure](docs/PYPI.md). Wheel/sdist checks cover metadata, licenses, resources, private files, offline installation and publication identity. Manual Trusted Publishing defaults to validation, requires exact version/tag/repository, and gives OIDC only to upload jobs consuming tested artifacts. Required CI names stay unchanged.
|
|
9
16
|
- Isolated builds inject synthetic private probes; archives exclude private drafts and credential filenames. Added tested `aikit --version`; published Docker 1.4.1 examples use its existing `--help`. Added a README Docker Hub badge.
|
|
@@ -46,10 +53,7 @@
|
|
|
46
53
|
|
|
47
54
|
## v0.13.0 (2026-10-11)
|
|
48
55
|
|
|
49
|
-
|
|
50
|
-
- Added rust-work, dotnet-work, java-work, and infra-work skills to the registry; Claude native copies install with the claude agent selection.
|
|
51
|
-
- Benchmark packs accept the new stack names; README, SKILL.md, and WIKI profile lists updated.
|
|
52
|
-
- Verified locally (Windows/Python 3.14.6): 254 tests ran, all passed, 1 runtime skip; kit validation returned zero errors and zero warnings; Python 3.10 grammar verified; end-to-end smoke detected RUST/INFRA with drafted context and installed source/native skills. Real applications of the new stacks remain untrialed.
|
|
56
|
+
[Archived release entry](docs/history/v0.13.0-changelog.md#v0130-2026-10-11).
|
|
53
57
|
|
|
54
58
|
## v0.12.0 (2026-10-11)
|
|
55
59
|
|
aikit/CONTRIBUTING.md
CHANGED
|
@@ -30,7 +30,7 @@ The root README and Docker Hub Overview are user documentation without a size qu
|
|
|
30
30
|
|
|
31
31
|
Follow [PyPI preparation and release](docs/PYPI.md) for every package release. Synchronize VERSION, pyproject metadata, [description](docs/PYPI_DESCRIPTION.md) and README; verify license/resource inclusion and local-file exclusions in both sdist and wheel, build the wheel from sdist, run strict Twine checks and offline fresh-venv smoke. Preserve existing CI matrix/job names. Refresh the image when packaging changes affect it.
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
A published stable GitHub Release starts PyPI preparation; manual dispatch defaults to build-only validation. Uploads require matching VERSION/tag, the canonical repository, a configured environment/publisher and the owner's human approval. Agents must not approve or bypass a pending deployment on the owner's behalf. Keep environment reviewer protections when allowing future `v*` tags; validate canonical stable versions separately. Build tests have no OIDC permission; upload jobs consume only validated same-run artifacts. Record local, hosted and registry results separately. Preparation alone authorizes no upload, account setup or Git operation.
|
|
34
34
|
|
|
35
35
|
## Repository maintenance
|
|
36
36
|
|
aikit/README.md
CHANGED
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
AI-KIT gives your coding agents persistent project memory, expert stack rules, **Kubernetes-ready** containers, **adaptive model routing**, and native configuration for **11 AI clients**. Add **Cloudflare security-audit** for structured security review and **Strix** for scoped pentesting workflows. One offline installer, zero installer dependencies, changes previewed before you apply them.
|
|
8
8
|
|
|
9
|
-
**[v1.4.
|
|
9
|
+
**[v1.4.3](CHANGELOG.md#v143-2026-10-16)** adds **GitHub Release → checks → your approval → PyPI**, future release-tag support, fifteen event regression tests and a live PyPI version badge. [Release verification](docs/V1_4_3.md) separates GitHub release preparation from registry publication. The latest verified PyPI package is [ai-kit-python 1.4.2](https://pypi.org/project/ai-kit-python/1.4.2/); the published Docker Hub image remains **1.4.1**. Shared dialogue memory and all optional modules remain available.
|
|
10
10
|
|
|
11
11
|
[](#install)
|
|
12
|
+
[](https://pypi.org/project/ai-kit-python/)
|
|
12
13
|
[](scripts/install.py)
|
|
13
14
|
[](#-works-with-your-ai-clients)
|
|
14
15
|
[](https://hub.docker.com/r/tigusigalpa/ai-kit)
|
|
@@ -227,19 +228,19 @@ Every command also runs through one CLI: `python aikit_cli.py install|doctor|rou
|
|
|
227
228
|
|
|
228
229
|
### Packaged CLI and PyPI status
|
|
229
230
|
|
|
230
|
-
AI-KIT
|
|
231
|
+
**AI-KIT 1.4.2 is available on [PyPI](https://pypi.org/project/ai-kit-python/1.4.2/).** Install package `ai-kit-python` and run command `aikit`; Python 3.10+ is required with no mandatory runtime dependencies. Publication uses GitHub Trusted Publishing. Uploaded wheel/source hashes and installation directly from PyPI were verified. TestPyPI was not used.
|
|
231
232
|
|
|
232
|
-
|
|
233
|
+
Install into a virtual environment:
|
|
233
234
|
|
|
234
235
|
~~~powershell
|
|
235
236
|
$cliEnv = Join-Path $env:LOCALAPPDATA 'AI-KIT\venv'
|
|
236
237
|
python -m venv "$cliEnv"
|
|
237
238
|
& "$cliEnv\Scripts\Activate.ps1"
|
|
238
|
-
python -m pip install
|
|
239
|
+
python -m pip install ai-kit-python
|
|
239
240
|
aikit --version
|
|
240
241
|
~~~
|
|
241
242
|
|
|
242
|
-
|
|
243
|
+
For an isolated CLI, use `pipx install ai-kit-python`. To install a reviewed source checkout instead, run `python -m pip install .` there. Updating uses `pipx upgrade ai-kit-python` or `python -m pip install --upgrade ai-kit-python`; installed application instructions are updated separately through preview/apply.
|
|
243
244
|
|
|
244
245
|
~~~powershell
|
|
245
246
|
aikit install "C:\Projects\my-app" --agent codex --mode private
|
|
@@ -249,7 +250,22 @@ aikit doctor "C:\Projects\my-app"
|
|
|
249
250
|
|
|
250
251
|
Then open that application in your client and run its bootstrap workflow. The CLI bundles templates and optional integration resources, uses no runtime third-party dependencies and preserves local documents/conflicts. [PyPI description](docs/PYPI_DESCRIPTION.md) contains portable publication copy; the [release guide](docs/PYPI.md) covers local builds, archive/privacy checks, TestPyPI, OIDC publisher fields and later publication.
|
|
251
252
|
|
|
252
|
-
For maintainers, [publish-pypi.yml](.github/workflows/publish-pypi.yml)
|
|
253
|
+
For maintainers, [publish-pypi.yml](.github/workflows/publish-pypi.yml) supports **GitHub Release → checks → your approval → PyPI**. Publishing a stable GitHub Release starts production preparation; the upload waits for your review of environment `pypi`. Manual dispatch remains available and defaults to build-only `validate`. No permanent registry token is needed.
|
|
254
|
+
|
|
255
|
+
#### Publish future releases to PyPI
|
|
256
|
+
|
|
257
|
+
1. Prepare the new version through the normal reviewed Git workflow. For example, put `1.5.0` in `VERSION`, update release documentation and complete the kit/package checks. Assess Docker impact through the existing image-refresh procedure. The tag's source must contain the updated release workflow and event guard.
|
|
258
|
+
2. Create a new tag such as `v1.5.0` at that prepared commit. **The tag and its committed `VERSION` must match exactly.** Preserve previous tags and published versions.
|
|
259
|
+
3. On GitHub, create and publish a normal Release for that tag. `release: published` starts the workflow automatically. Saving a draft, pushing a tag alone, editing an existing Release or marking it as a prerelease does not request automatic production publication.
|
|
260
|
+
4. Open **Actions → Prepare or publish Python package**. Its `build` job checks the release identity, runs kit validation and behavioral tests, builds a source archive and wheel, validates metadata/resources/privacy and the description, then smoke-installs the wheel. A failed check prevents the upload job from proceeding.
|
|
261
|
+
5. After checks pass, the `pypi` job waits at **Review deployments**. Inspect the tag, commit and successful checks, select environment **pypi**, and click **Approve and deploy**. Required reviewer: repository owner `tigusigalpa`. **This confirmation is a human step; agents must not approve or bypass it on your behalf.** For solo maintenance the owner may approve a run they initiated.
|
|
262
|
+
6. GitHub exchanges its OIDC identity for a short-lived PyPI credential and uploads only the same-run validated artifacts. The upload job builds no code. Verify the new version on [PyPI](https://pypi.org/project/ai-kit-python/), compare its file hashes with the run artifacts, and check installation in a fresh environment. The README PyPI badge then follows the published version automatically.
|
|
263
|
+
|
|
264
|
+
The live `pypi` deployment policy accepts **tags `v*`**, so future versions do not need a new allowlist entry. Branches are excluded. The event guard separately requires a canonical stable tag of the form `vMAJOR.MINOR.PATCH`, the canonical repository, a published non-draft/non-prerelease Release and matching `VERSION`. Tests and your approval remain required for each production run.
|
|
265
|
+
|
|
266
|
+
For a manual run, choose **Run workflow**, select the versioned tag, set `destination=pypi`, and type the exact version in `version`. Uploads use the same approval gate. `destination=validate` only builds and tests; `testpypi` is an explicit separate registry with its own environment/publisher setup. Prerelease trials can use that manual route. Republishing the same PyPI version fails rather than replacing or silently skipping files.
|
|
267
|
+
|
|
268
|
+
The Release path is included in **v1.4.3 and later tagged source**. Existing `v1.4.2` stays unchanged. The complete [release procedure](docs/PYPI.md#release-to-pypi-with-owner-approval) includes activation, recovery and configuration verification. Each stable release still needs your human approval before PyPI upload.
|
|
253
269
|
|
|
254
270
|
## Run AI-KIT with Docker
|
|
255
271
|
|
aikit/SKILL.md
CHANGED
|
@@ -86,7 +86,7 @@ CI and optional integrations stay outside presets. Read the audit, Strix, Contex
|
|
|
86
86
|
|
|
87
87
|
## Unified CLI
|
|
88
88
|
|
|
89
|
-
`python aikit_cli.py --help` lists commands; pip exposes `aikit`. [Dialogue memory](docs/DIALOGUE_MEMORY.md) imports/merges two dialogues and exports reviewed ChatGPT/OpenRouter references. [Docker](README.md#run-ai-kit-with-docker) mounts an application. [PyPI](docs/PYPI.md) covers
|
|
89
|
+
`python aikit_cli.py --help` lists commands; pip exposes `aikit`. [Dialogue memory](docs/DIALOGUE_MEMORY.md) imports/merges two dialogues and exports reviewed ChatGPT/OpenRouter references. [Docker](README.md#run-ai-kit-with-docker) mounts an application. [PyPI](docs/PYPI.md) covers checks and owner-approved release publishing; preparation uploads nothing. Standalone scripts and Makefile also work.
|
|
90
90
|
|
|
91
91
|
## Maintaining AI-KIT itself
|
|
92
92
|
|
aikit/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.4.
|
|
1
|
+
1.4.3
|
aikit/docs/PYPI.md
CHANGED
|
@@ -2,25 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
## Status and package identity
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Version [1.4.3](V1_4_3.md) includes the owner-approved Release path; publication requires human approval. Release [1.4.2 is published on PyPI](https://pypi.org/project/ai-kit-python/1.4.2/); [evidence](V1_4_2.md) records its workflow, uploaded hashes and clean registry installation. Trusted Publishing uses repository `tigusigalpa/ai-kit`, workflow `publish-pypi.yml`, environment `pypi`. On 2026-10-16 that environment was extended to future `v*` tags with owner review preserved. VERSION is the version source; distribution `ai-kit-python`, import package and executable `aikit`, Python 3.10+, no runtime dependencies. TestPyPI remains unconfigured.
|
|
6
6
|
|
|
7
7
|
PyPI rejected `ai-kit` and `ai-kit-cli` as too similar to existing projects; the owner selected `ai-kit-python`, whose pending-publisher registration succeeded. A public JSON 404 alone does not establish eligibility, and a pending publisher does not reserve a name. For a rename synchronize pyproject, archive checks, metadata smoke tests, workflow URLs, publisher configuration and install examples before rebuilding.
|
|
8
8
|
|
|
9
9
|
[pyproject.toml](../pyproject.toml) owns metadata. [PYPI_DESCRIPTION.md](PYPI_DESCRIPTION.md) is the complete Markdown description, with absolute public links and no GitHub-only diagrams. AI-KIT's own code remains MIT; the distribution also bundles Apache-2.0 Strix resources, so its SPDX expression is `MIT AND Apache-2.0`. All three declared license files are included under wheel dist-info/licenses and retained beside their payload resources.
|
|
10
10
|
|
|
11
|
-
## Install
|
|
11
|
+
## Install the published package
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Use an isolated CLI installation with `pipx install ai-kit-python`, or a Python virtual environment:
|
|
14
14
|
|
|
15
15
|
~~~powershell
|
|
16
16
|
$cliEnv = Join-Path $env:LOCALAPPDATA 'AI-KIT\venv'
|
|
17
17
|
python -m venv "$cliEnv"
|
|
18
18
|
& "$cliEnv\Scripts\Activate.ps1"
|
|
19
|
-
python -m pip install
|
|
19
|
+
python -m pip install ai-kit-python
|
|
20
20
|
aikit --version
|
|
21
21
|
~~~
|
|
22
22
|
|
|
23
|
-
Keep that environment local; it is not an application dependency.
|
|
23
|
+
Keep that environment local; it is not an application dependency. For a reviewed checkout, `python -m pip install .` remains available. Update via `pipx upgrade ai-kit-python` or `python -m pip install --upgrade ai-kit-python`. Upgrading the CLI does not rewrite installed project instructions: preview `aikit install TARGET` and apply after review. [Usage](../README.md#packaged-cli-and-pypi-status).
|
|
24
24
|
|
|
25
25
|
## Build and validate without uploading
|
|
26
26
|
|
|
@@ -53,7 +53,7 @@ python scripts/check_distribution.py identity
|
|
|
53
53
|
|
|
54
54
|
## Trusted Publishing configuration
|
|
55
55
|
|
|
56
|
-
[publish-pypi.yml](../.github/workflows/publish-pypi.yml)
|
|
56
|
+
[publish-pypi.yml](../.github/workflows/publish-pypi.yml) supports `release: published` for stable releases and `workflow_dispatch` for manual runs. The Release path prepares production PyPI artifacts automatically, then waits for the owner's environment approval. Drafts and prereleases skip automatic production preparation. Pushes, tag pushes and Release edits do not request publication. Manual dispatch defaults to `validate`, which builds/tests/retains artifacts and skips both upload jobs. Existing required CI names remain unchanged.
|
|
57
57
|
|
|
58
58
|
Configure these account settings separately when publication is authorized. PyPI and TestPyPI are separate services/accounts; use a pending publisher for a new project or add a publisher to an owned project.
|
|
59
59
|
|
|
@@ -66,17 +66,31 @@ Configure these account settings separately when publication is authorized. PyPI
|
|
|
66
66
|
| Workflow filename | publish-pypi.yml | publish-pypi.yml |
|
|
67
67
|
| Environment | pypi | testpypi |
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
The verified production environment requires reviewer `tigusigalpa` (GitHub user ID 2721390). Its custom deployment policy is **type `tag`, pattern `v*`**; matching branch names are not admitted. The same reviewer and all other environment protections were preserved when replacing the earlier v1.4.2-only policy. Self-review remains allowed so the sole owner can approve a run they started. The required confirmation must be performed by the human owner: agents must not approve or bypass it. [Contribution policy](../CONTRIBUTING.md#python-package-releases).
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
Keep publisher/environment names identical. No PyPI API token is stored: the pinned PyPA action exchanges GitHub OIDC identity for a short-lived credential after approval. Only upload jobs receive `id-token: write`; build/tests have read-only repository access. Upload jobs consume the validated same-run artifact without checking out or building source. Manual and Release production runs share a per-tag concurrency group and do not cancel each other during approval.
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
2. After the reviewed workflow exists on the default branch, a `validate` dispatch can exercise hosted build-only preparation. Hosted results remain unknown until that run finishes.
|
|
75
|
-
3. For an upload, select the reviewed `vVERSION` tag, explicitly choose `testpypi` or `pypi`, and type the exact VERSION as confirmation. The guard rejects branches, other versions, missing confirmation and noncanonical repositories before artifact building. Each registry uses its own environment/publisher; production does not automatically upload to TestPyPI.
|
|
76
|
-
4. TestPyPI is the recommended first external trial. After an explicitly authorized trial, install the exact version in a fresh environment using `--index-url https://test.pypi.org/simple/ --no-deps`, inspect the CLI/resources and compare downloaded hashes. Do not mix TestPyPI and PyPI using an extra index.
|
|
77
|
-
5. After production upload, verify the public version, metadata, files, SHA-256 hashes and fresh isolated installation from PyPI. Record registry evidence separately from prepared local hashes; update publication status/examples only after read-back. Duplicate uploads fail rather than silently skipping a file.
|
|
73
|
+
## Release to PyPI with owner approval
|
|
78
74
|
|
|
79
|
-
|
|
75
|
+
1. Review VERSION, metadata, description, README, licenses and changes since the prior release; complete checks and Docker-impact handling. Any Git commit/tag/push needs its own authorization. The reviewed release commit must contain the updated workflow and event guard. Existing tags, including v1.4.2, stay unchanged.
|
|
76
|
+
2. Prepare a new version, for example VERSION `1.5.0`, and a new tag `v1.5.0` at that commit. The automatic path accepts canonical stable `vMAJOR.MINOR.PATCH` tags; release tag, runner ref and the tag's VERSION must agree exactly. The guard also checks the event's repository against the canonical runner repository. Release notes/body are not commands or publication inputs.
|
|
77
|
+
3. Publish a stable GitHub Release for that tag. The workflow first verifies event identity, then validates the kit, runs tests, builds sdist followed by wheel, checks metadata/resources/privacy/licenses/description and smoke-installs offline. A failure stops the upload job.
|
|
78
|
+
4. In **Actions → Prepare or publish Python package**, wait for `build` to succeed. The `pypi` job then pauses at **Review deployments**. The human owner inspects the tag, commit and checks, selects `pypi`, and clicks **Approve and deploy**. Rejecting or leaving it pending uploads nothing. GitHub's required reviewer is the final gate; the agent does not submit this approval.
|
|
79
|
+
5. The approved upload job obtains OIDC credentials and publishes only that run's tested files. Verify the public version, metadata, files, hashes and fresh installation from PyPI; compare downloads against the actual hosted artifacts. Update publication receipts after read-back. The README badge follows PyPI availability, not GitHub Release creation.
|
|
80
|
+
|
|
81
|
+
### Manual runs and recovery
|
|
82
|
+
|
|
83
|
+
- **Build only:** use Run workflow with destination `validate`; a branch is allowed. No upload or environment approval occurs.
|
|
84
|
+
- **Manual upload:** select a `vVERSION` tag, choose `pypi` or `testpypi` and enter that exact version. Event inputs are checked before building; production uses the same human approval gate. Explicit manual prerelease trials remain possible.
|
|
85
|
+
- **TestPyPI:** configure its separate environment/publisher before an authorized trial. Install with `--index-url https://test.pypi.org/simple/ --no-deps`; do not mix it with PyPI using an extra index. Production does not automatically upload there.
|
|
86
|
+
- **Failed checks:** fix through the reviewed Git workflow and prepare a new release tag as needed. Do not move a released tag. The release workflow must exist in the tagged source; creating a Release for an older tag does not retrofit this implementation.
|
|
87
|
+
- **Duplicate version:** uploads fail instead of replacing files or silently skipping them. Use a new version for changed packages; check public files and hashes before considering a retry of a partial upload.
|
|
88
|
+
|
|
89
|
+
Committing these files alone neither creates a GitHub Release nor uploads a package. Keep prepared source, hosted checks, environment approval and registry evidence distinct. No future version is available on PyPI until read-back verifies it.
|
|
90
|
+
|
|
91
|
+
## Release-chain verification
|
|
92
|
+
|
|
93
|
+
Verified on 2026-10-16: kit checks returned no errors/warnings; 368 packaged Linux/Python 3.13.16 tests ran with two Windows-only skips. Fifteen new event tests cover future tags, invalid release identities, manual defaults and untrusted payloads. Workflow YAML/security assertions and Python 3.10 grammar passed. Isolated sdist-to-wheel, privacy probes, strict Twine and offline Windows wheel smoke passed. Local ai-kit:dev was rebuilt and passed Docker smoke; ten changed packaged resources matched source. These local runs did not cover macOS or a hosted Release event. No package/image was uploaded. The tested image retains the earlier documentation snapshot preceding this verification receipt.
|
|
80
94
|
|
|
81
95
|
## Docker and documentation continuity
|
|
82
96
|
|
|
@@ -85,3 +99,5 @@ Packaging metadata, description selection and bundled resource changes affect th
|
|
|
85
99
|
For every Python package release, refresh metadata/description/README, run archive checks, strict Twine validation and offline installation, and record unavailable OS/runtime checks. [Contribution rules](../CONTRIBUTING.md#python-package-releases) own the release-maintenance trigger.
|
|
86
100
|
|
|
87
101
|
Primary sources checked on 2026-10-16: [project metadata](https://packaging.python.org/en/latest/guides/writing-pyproject-toml/), [license expressions](https://packaging.python.org/en/latest/specifications/license-expression/), [PyPI descriptions](https://packaging.python.org/en/latest/guides/making-a-pypi-friendly-readme/), [pending publishers](https://docs.pypi.org/trusted-publishers/creating-a-project-through-oidc/), [OIDC publication](https://docs.pypi.org/trusted-publishers/using-a-publisher/), [PyPA action](https://github.com/pypa/gh-action-pypi-publish).
|
|
102
|
+
|
|
103
|
+
Release/approval sources checked on 2026-10-16: [release events](https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#release), [deployment tag policies](https://docs.github.com/en/rest/deployments/branch-policies), [human deployment review](https://docs.github.com/en/actions/how-tos/deploy/configure-and-manage-deployments/review-deployments).
|
aikit/docs/PYPI_DESCRIPTION.md
CHANGED
|
@@ -92,6 +92,8 @@ The distribution excludes root maintainer AGENTS.md, PROJECT_CONTEXT.md and WIKI
|
|
|
92
92
|
|
|
93
93
|
A separate [Docker image](https://hub.docker.com/r/tigusigalpa/ai-kit) packages the CLI for container use.
|
|
94
94
|
|
|
95
|
+
Package releases follow GitHub Release, automated checks and owner approval before PyPI upload through Trusted Publishing. Future stable tags must match VERSION; manual runs default to validation. See the [release procedure](https://github.com/tigusigalpa/ai-kit/blob/main/docs/PYPI.md#release-to-pypi-with-owner-approval).
|
|
96
|
+
|
|
95
97
|
## Documentation and license
|
|
96
98
|
|
|
97
99
|
- [Repository and full README](https://github.com/tigusigalpa/ai-kit)
|
aikit/docs/V1_4_2.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# AI-KIT v1.4.2
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Published on 2026-10-16. [CHANGELOG](../CHANGELOG.md#v142-2026-10-16) owns release history; [PyPI procedure](PYPI.md) owns publishing instructions.
|
|
4
4
|
|
|
5
5
|
## Release contents
|
|
6
6
|
|
|
@@ -28,6 +28,21 @@ PyPI rejected `ai-kit` and `ai-kit-cli` as too similar to existing projects. The
|
|
|
28
28
|
|
|
29
29
|
The GitHub environment was independently read back: only tag `v1.4.2`, required reviewer `tigusigalpa`, self-review permitted for solo maintenance. TestPyPI remains unconfigured and no TestPyPI upload was requested. A pending publisher does not reserve a project name or establish package availability.
|
|
30
30
|
|
|
31
|
-
The
|
|
31
|
+
The first hosted run exposed incorrect Twine wildcard quoting on Windows. The corrected complete glob passed PowerShell validation with a space-containing path. All 32 final push/PR CI checks succeeded, including the required Linux/Windows/macOS matrices and Docker. [PR 1](https://github.com/tigusigalpa/ai-kit/pull/1) was merged without bypassing checks. Tag `v1.4.2` points to `36e804a43f9a8a7912da050e7117821a407904b0`; the existing v1.4.1 tag and Docker Hub image were preserved.
|
|
32
|
+
|
|
33
|
+
## PyPI publication and read-back
|
|
34
|
+
|
|
35
|
+
[Publication workflow 38891485311](https://github.com/tigusigalpa/ai-kit/actions/runs/38891485311) completed successfully from that exact tag/commit. The build job validated the source, tests, both archives, description and offline wheel installation. The owner-approved `pypi` environment released only those artifacts through GitHub OIDC; TestPyPI was skipped.
|
|
36
|
+
|
|
37
|
+
The public [ai-kit-python 1.4.2 page](https://pypi.org/project/ai-kit-python/1.4.2/) and version JSON were read back. Both downloaded files match the SHA-256 values in the same-run GitHub artifacts:
|
|
38
|
+
|
|
39
|
+
| Published file | SHA-256 |
|
|
40
|
+
| --- | --- |
|
|
41
|
+
| ai_kit_python-1.4.2-py3-none-any.whl | `7bda41d548597ddbba318e0a006eedb886238b89a96168d0ee1514b73881d4f5` |
|
|
42
|
+
| ai_kit_python-1.4.2.tar.gz | `c4a278bcc94945b91363722929e8f1b1f3ec7b28604681348afd85f7b1082174` |
|
|
43
|
+
|
|
44
|
+
A fresh Windows/Python 3.12.14 environment installed `ai-kit-python==1.4.2` directly from `https://pypi.org/simple`, with the wheel hash required and dependencies/cache disabled. The installed import path belongs to that environment, `aikit --version` returned `aikit 1.4.2`, and the installed kit check reported zero errors/warnings. Complete smoke verification of the downloaded wheel also passed the optional resources and dialogue pipeline.
|
|
45
|
+
|
|
46
|
+
These are registry receipts, not local preparation hashes. The CLI image remains the tested 1.4.2 development snapshot; descriptive receipts added afterward do not require replacing it. Docker Hub still serves the previously published 1.4.1 image. Raw verification files, downloaded packages and logs are stored outside the repository.
|
|
32
47
|
|
|
33
48
|
[Core](../template/ai-kit/CORE.md#git-authorization) governs Git authorization. No account secrets belong in source, artifacts or chat. Future image publication remains separate; preserve existing release tags and Docker identities.
|
aikit/docs/V1_4_3.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# AI-KIT v1.4.3
|
|
2
|
+
|
|
3
|
+
Release date: 2026-10-16. [CHANGELOG](../CHANGELOG.md#v143-2026-10-16) owns history; [PyPI procedure](PYPI.md#release-to-pypi-with-owner-approval) owns publishing instructions.
|
|
4
|
+
|
|
5
|
+
## Release contents
|
|
6
|
+
|
|
7
|
+
- Published stable GitHub Releases automatically prepare the Python package: verify the event, run source/behavior checks, build sdist and wheel, check metadata/resources/privacy/licenses/description and smoke-install offline.
|
|
8
|
+
- PyPI upload waits for the human owner of environment `pypi`. Agents must not approve or bypass this confirmation. The upload uses OIDC and only the same run's validated artifacts; build jobs receive no OIDC permission.
|
|
9
|
+
- Future canonical `vMAJOR.MINOR.PATCH` tags work with the existing tag-only `v*` environment policy. Release tag, runner ref, committed VERSION and canonical repository must agree. Drafts and prereleases skip automatic production preparation.
|
|
10
|
+
- Manual dispatch stays available and defaults to build-only validation. Explicit manual uploads require an exact version/tag and production still requires owner approval. TestPyPI remains separate and unconfigured.
|
|
11
|
+
- Fifteen event regression tests, a live README PyPI version badge, updated release/recovery instructions and preserved archived changelog text.
|
|
12
|
+
|
|
13
|
+
VERSION, CLI, package metadata and the local development image advance to 1.4.3. The existing v1.4.2 tag and PyPI files stay unchanged. Docker Hub remains at the separately published 1.4.1 image; this GitHub release does not publish an image there.
|
|
14
|
+
|
|
15
|
+
## Verification
|
|
16
|
+
|
|
17
|
+
Before labeling, [PR 3](https://github.com/tigusigalpa/ai-kit/pull/3) passed all 32 push/PR CI checks across Linux, Windows and macOS, including the Python validation/wheel matrices and Docker. [Validate-only run 38897526857](https://github.com/tigusigalpa/ai-kit/actions/runs/38897526857) passed on the merged workflow: 368 tests, source/archive/description/private-probe checks and offline wheel smoke; both upload jobs skipped.
|
|
18
|
+
|
|
19
|
+
After labeling 1.4.3, verified locally on 2026-10-16:
|
|
20
|
+
|
|
21
|
+
- Kit validation returned zero errors/warnings; ten Windows/Python 3.12.14 CLI tests passed. The CLI and archive identity guard report 1.4.3.
|
|
22
|
+
- The packaged Linux/Python 3.13.16 image ran 368 tests in 30.323 seconds: 366 passed, two Windows-only skips.
|
|
23
|
+
- Isolated sdist-to-wheel builds, synthetic private-file probes, archive metadata/licenses/resources, strict Twine and offline Windows wheel smoke passed. The package description includes the owner-approved release procedure.
|
|
24
|
+
- Docker smoke passed offline/read-only execution, installation/refusal, all optional modules, preserved files, stable rerun, SIGTERM and the actual context-exclusion engine. The version label is 1.4.3. No test volumes were created; shared cache ownership is unknown, so no pruning was performed.
|
|
25
|
+
- Private/team upgrade fixtures used a Git archive of the actual v1.4.2 tag. README, MCP configuration, project facts, design brief, private dialogue data and settings including a custom extension survived; doctor reported 1.4.3 with no errors, private memory stayed ignored and reruns wrote nothing.
|
|
26
|
+
|
|
27
|
+
Build binaries, hashes and raw logs remain outside the source checkout. Descriptive verification receipts follow the tested image snapshot. These local runs do not cover macOS; hosted CI is verified separately before release creation. Synthetic tests do not establish live model/client activation, application benchmarks or Kubernetes deployment.
|
|
28
|
+
|
|
29
|
+
## GitHub Release and owner confirmation
|
|
30
|
+
|
|
31
|
+
Create the new v1.4.3 tag at the reviewed, checked release commit, preserving old tags. Publishing its stable GitHub Release triggers `publish-pypi.yml`. Wait for `build` to succeed, then the owner selects **Actions -> Review deployments -> pypi -> Approve and deploy**. Rejecting or leaving the request pending uploads nothing.
|
|
32
|
+
|
|
33
|
+
This record describes release preparation and the approval procedure; it is not a PyPI publication receipt. Verify public files/hashes and a fresh registry installation after the owner's approval before claiming publication. The last verified registry release at preparation was [1.4.2](V1_4_2.md). Descriptive verification/publication receipts added after building may retain the tested image's earlier documentation snapshot.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Archived AI-KIT release history
|
|
2
|
+
|
|
3
|
+
## v0.13.0 (2026-10-11)
|
|
4
|
+
|
|
5
|
+
- Added Rust, .NET, Java (Maven/Gradle), and Terraform/Helm stack profiles with evidence-based per-module detection: Cargo.toml (rust-version, cargo test/build/clippy), *.csproj/*.fsproj/*.vbproj and global.json (TargetFramework, dotnet test/build), pom.xml/mvnw/build.gradle(.kts)/gradlew (language level, wrapper-aware commands), and .tf files or Chart.yaml (fmt/validate, helm lint). Drafts remain suggestions until bootstrap confirms them.
|
|
6
|
+
- Added rust-work, dotnet-work, java-work, and infra-work skills to the registry; Claude native copies install with the claude agent selection.
|
|
7
|
+
- Benchmark packs accept the new stack names; README, SKILL.md, and WIKI profile lists updated.
|
|
8
|
+
- Verified locally (Windows/Python 3.14.6): 254 tests ran, all passed, 1 runtime skip; kit validation returned zero errors and zero warnings; Python 3.10 grammar verified; end-to-end smoke detected RUST/INFRA with drafted context and installed source/native skills. Real applications of the new stacks remain untrialed.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Validate
|
|
1
|
+
"""Validate release archives and manual/GitHub Release identity without uploading."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
4
|
import argparse
|
|
@@ -23,7 +23,8 @@ PRIVATE_PARTS = {".git", ".aws", ".codex", ".claude", ".secrets", ".metrics", ".
|
|
|
23
23
|
REQUIRED = {"VERSION", "LICENSE", "aikit_cli.py", "scripts/install.py", "scripts/check_distribution.py",
|
|
24
24
|
"template/AGENTS.md", "template/PROJECT_CONTEXT.md", "template/WIKI.md",
|
|
25
25
|
"template/ai-kit/settings.json", "docs/PYPI_DESCRIPTION.md", "docs/PYPI.md",
|
|
26
|
-
"docker/pypi-requirements.txt", ".github/workflows/publish-pypi.yml", "tests/build_distribution.py",
|
|
26
|
+
"docker/pypi-requirements.txt", ".github/workflows/publish-pypi.yml", "tests/build_distribution.py",
|
|
27
|
+
"tests/test_pypi_events.py", *LICENSES}
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
def release_identity(version: str, expected: str, destination: str, ref: str, repository: str) -> dict:
|
|
@@ -43,6 +44,50 @@ def release_identity(version: str, expected: str, destination: str, ref: str, re
|
|
|
43
44
|
"upload_performed": False}
|
|
44
45
|
|
|
45
46
|
|
|
47
|
+
def workflow_identity(version: str, event: str, payload: dict, ref: str, repository: str) -> dict:
|
|
48
|
+
"""Resolve supported GitHub events before any build or publication permission."""
|
|
49
|
+
if not isinstance(payload, dict):
|
|
50
|
+
raise ValueError("GitHub event must be a JSON object")
|
|
51
|
+
event_repository = payload.get("repository")
|
|
52
|
+
if not isinstance(event_repository, dict) or event_repository.get("full_name") != repository:
|
|
53
|
+
raise ValueError("GitHub event repository does not match the runner repository")
|
|
54
|
+
if event == "release":
|
|
55
|
+
release = payload.get("release")
|
|
56
|
+
if payload.get("action") != "published" or not isinstance(release, dict):
|
|
57
|
+
raise ValueError("Automatic publication requires a published release")
|
|
58
|
+
if release.get("draft") is not False or release.get("prerelease") is not False:
|
|
59
|
+
raise ValueError("Automatic publication requires a stable, non-draft release")
|
|
60
|
+
tag = release.get("tag_name")
|
|
61
|
+
if not isinstance(tag, str) or not re.fullmatch(r"v(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)", tag):
|
|
62
|
+
raise ValueError("Automatic publication requires a stable vMAJOR.MINOR.PATCH tag")
|
|
63
|
+
if ref != "refs/tags/" + tag:
|
|
64
|
+
raise ValueError("Release tag does not match the runner ref")
|
|
65
|
+
result = release_identity(version, tag[1:], "pypi", ref, repository)
|
|
66
|
+
elif event == "workflow_dispatch":
|
|
67
|
+
inputs = payload.get("inputs", {})
|
|
68
|
+
if not isinstance(inputs, dict):
|
|
69
|
+
raise ValueError("Manual workflow inputs must be an object")
|
|
70
|
+
expected = inputs.get("version", "")
|
|
71
|
+
destination = inputs.get("destination", "validate")
|
|
72
|
+
if not isinstance(expected, str) or not isinstance(destination, str):
|
|
73
|
+
raise ValueError("Manual version and destination must be strings")
|
|
74
|
+
result = release_identity(version, expected, destination, ref, repository)
|
|
75
|
+
else:
|
|
76
|
+
raise ValueError("Only release and workflow_dispatch events are supported")
|
|
77
|
+
return {**result, "event": event}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def read_workflow_event(path: Path) -> dict:
|
|
81
|
+
"""Read bounded runner event JSON without copying its release body into logs."""
|
|
82
|
+
if path.is_symlink() or not path.is_file():
|
|
83
|
+
raise ValueError("GitHub event must be a regular unlinked file")
|
|
84
|
+
with path.open("rb") as stream:
|
|
85
|
+
raw = stream.read(1_000_001)
|
|
86
|
+
if len(raw) > 1_000_000:
|
|
87
|
+
raise ValueError("GitHub event exceeds the inspection limit")
|
|
88
|
+
return json.loads(raw)
|
|
89
|
+
|
|
90
|
+
|
|
46
91
|
def _members(path: Path) -> dict[str, bytes]:
|
|
47
92
|
result: dict[str, bytes] = {}
|
|
48
93
|
total = 0
|
|
@@ -153,18 +198,28 @@ def check_archives(directory: Path, version: str) -> list[dict]:
|
|
|
153
198
|
def main(argv: list[str] | None = None) -> int:
|
|
154
199
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
155
200
|
sub = parser.add_subparsers(dest="command", required=True)
|
|
156
|
-
identity = sub.add_parser("identity", help="Validate a manual
|
|
201
|
+
identity = sub.add_parser("identity", help="Validate a manual or GitHub event request; never uploads")
|
|
157
202
|
identity.add_argument("--expected-version", default="")
|
|
158
203
|
identity.add_argument("--destination", choices=("validate", "testpypi", "pypi"), default="validate")
|
|
159
204
|
identity.add_argument("--ref", default="")
|
|
160
205
|
identity.add_argument("--repository", default="")
|
|
206
|
+
identity.add_argument("--event-name", default="")
|
|
207
|
+
identity.add_argument("--event-path", type=Path)
|
|
161
208
|
archives = sub.add_parser("archives", help="Inspect a prepared wheel and sdist; never uploads")
|
|
162
209
|
archives.add_argument("directory", type=Path)
|
|
163
210
|
args = parser.parse_args(argv)
|
|
164
211
|
try:
|
|
165
212
|
version = (ROOT / "VERSION").read_text(encoding="utf-8").strip()
|
|
166
213
|
if args.command == "identity":
|
|
167
|
-
|
|
214
|
+
if args.event_name or args.event_path is not None:
|
|
215
|
+
if not args.event_name or args.event_path is None:
|
|
216
|
+
raise ValueError("Both event name and event path are required")
|
|
217
|
+
if args.expected_version or args.destination != "validate":
|
|
218
|
+
raise ValueError("Use event inputs instead of manual options with a GitHub event")
|
|
219
|
+
result = workflow_identity(version, args.event_name, read_workflow_event(args.event_path),
|
|
220
|
+
args.ref, args.repository)
|
|
221
|
+
else:
|
|
222
|
+
result = release_identity(version, args.expected_version, args.destination, args.ref, args.repository)
|
|
168
223
|
else:
|
|
169
224
|
result = {"archives": check_archives(args.directory, version), "upload_performed": False}
|
|
170
225
|
print(json.dumps(result, indent=2))
|
aikit/scripts/check_kit.py
CHANGED
|
@@ -205,7 +205,7 @@ def check(root: Path, *, git: str | None = None) -> tuple[list[str], list[str]]:
|
|
|
205
205
|
"aikit_cli.py", "__init__.py", "MANIFEST.in", "pyproject.toml", "tests/wheel_smoke.py",
|
|
206
206
|
"scripts/check_distribution.py", "tests/test_distribution.py", "docs/PYPI.md", "docs/PYPI_DESCRIPTION.md",
|
|
207
207
|
"docker/pypi-requirements.txt", ".github/workflows/publish-pypi.yml",
|
|
208
|
-
"tests/build_distribution.py",
|
|
208
|
+
"tests/build_distribution.py", "tests/test_pypi_events.py",
|
|
209
209
|
".github/workflows/check-kit.yml", "docs/IMPLEMENTATION.md",
|
|
210
210
|
]
|
|
211
211
|
for name in install.OPTIONAL_SKILLS:
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"""Exercise release-triggered publication guards without publishing or approving jobs."""
|
|
2
|
+
from contextlib import redirect_stdout
|
|
3
|
+
import copy
|
|
4
|
+
import io
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
import sys
|
|
8
|
+
import tempfile
|
|
9
|
+
import unittest
|
|
10
|
+
from unittest import mock
|
|
11
|
+
|
|
12
|
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts"))
|
|
13
|
+
import check_distribution as release
|
|
14
|
+
|
|
15
|
+
REPOSITORY = "tigusigalpa/ai-kit"
|
|
16
|
+
VERSION = "1.5.0"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class PyPIEventTests(unittest.TestCase):
|
|
20
|
+
def setUp(self):
|
|
21
|
+
temporary = tempfile.TemporaryDirectory(prefix="aikit-release-event-")
|
|
22
|
+
self.addCleanup(temporary.cleanup)
|
|
23
|
+
self.root = Path(temporary.name).resolve()
|
|
24
|
+
(self.root / "VERSION").write_text(VERSION + "\n", encoding="utf-8")
|
|
25
|
+
self.event_path = self.root / "event.json"
|
|
26
|
+
|
|
27
|
+
def published(self, version=VERSION):
|
|
28
|
+
return {"action": "published", "repository": {"full_name": REPOSITORY},
|
|
29
|
+
"release": {"tag_name": "v" + version, "draft": False, "prerelease": False}}
|
|
30
|
+
|
|
31
|
+
def dispatch(self, **inputs):
|
|
32
|
+
return {"repository": {"full_name": REPOSITORY}, "inputs": inputs}
|
|
33
|
+
|
|
34
|
+
def resolve(self, payload, *, event="release", version=VERSION,
|
|
35
|
+
ref="refs/tags/v" + VERSION, repository=REPOSITORY):
|
|
36
|
+
return release.workflow_identity(version, event, payload, ref, repository)
|
|
37
|
+
|
|
38
|
+
def cli(self, payload, *extra, event="release", ref="refs/tags/v" + VERSION):
|
|
39
|
+
self.event_path.write_text(json.dumps(payload), encoding="utf-8")
|
|
40
|
+
output = io.StringIO()
|
|
41
|
+
with mock.patch.object(release, "ROOT", self.root), redirect_stdout(output):
|
|
42
|
+
code = release.main(["identity", "--event-name", event, "--event-path", str(self.event_path),
|
|
43
|
+
"--ref", ref, "--repository", REPOSITORY, *extra])
|
|
44
|
+
return code, json.loads(output.getvalue()), output.getvalue()
|
|
45
|
+
|
|
46
|
+
def test_future_stable_releases_select_production_without_uploading(self):
|
|
47
|
+
for version in ("1.4.3", "1.5.0", "2.0.0", "10.42.300"):
|
|
48
|
+
with self.subTest(version=version):
|
|
49
|
+
result = self.resolve(self.published(version), version=version, ref="refs/tags/v" + version)
|
|
50
|
+
self.assertEqual((result["version"], result["destination"], result["event"]),
|
|
51
|
+
(version, "pypi", "release"))
|
|
52
|
+
self.assertFalse(result["upload_performed"])
|
|
53
|
+
|
|
54
|
+
def test_draft_prerelease_and_missing_boolean_flags_refuse(self):
|
|
55
|
+
for field in ("draft", "prerelease"):
|
|
56
|
+
for value in (True, None, "false", 0):
|
|
57
|
+
with self.subTest(field=field, value=value):
|
|
58
|
+
payload = self.published()
|
|
59
|
+
payload["release"][field] = value
|
|
60
|
+
with self.assertRaisesRegex(ValueError, "stable, non-draft"):
|
|
61
|
+
self.resolve(payload)
|
|
62
|
+
payload = self.published()
|
|
63
|
+
del payload["release"][field]
|
|
64
|
+
with self.assertRaises(ValueError):
|
|
65
|
+
self.resolve(payload)
|
|
66
|
+
|
|
67
|
+
def test_other_release_actions_and_missing_release_refuse(self):
|
|
68
|
+
for action in ("created", "edited", "deleted", "released", "prereleased", ""):
|
|
69
|
+
with self.subTest(action=action):
|
|
70
|
+
payload = self.published()
|
|
71
|
+
payload["action"] = action
|
|
72
|
+
with self.assertRaises(ValueError):
|
|
73
|
+
self.resolve(payload)
|
|
74
|
+
for value in (None, [], "release"):
|
|
75
|
+
payload = self.published()
|
|
76
|
+
payload["release"] = value
|
|
77
|
+
with self.assertRaises(ValueError):
|
|
78
|
+
self.resolve(payload)
|
|
79
|
+
|
|
80
|
+
def test_automatic_tags_must_be_canonical_stable_versions(self):
|
|
81
|
+
for tag in ("1.5.0", "v1.5", "v1.5.0rc1", "v01.5.0", "v1.05.0", "v1.5.00",
|
|
82
|
+
"v1.5.0\n", "v1.5.0$(echo synthetic)", "v1/5/0"):
|
|
83
|
+
with self.subTest(tag=tag):
|
|
84
|
+
payload = self.published()
|
|
85
|
+
payload["release"]["tag_name"] = tag
|
|
86
|
+
with self.assertRaises(ValueError):
|
|
87
|
+
self.resolve(payload, ref="refs/tags/" + tag)
|
|
88
|
+
|
|
89
|
+
def test_release_tag_runner_ref_and_version_must_agree(self):
|
|
90
|
+
for ref in ("refs/heads/main", "refs/tags/v1.4.2", ""):
|
|
91
|
+
with self.subTest(ref=ref), self.assertRaises(ValueError):
|
|
92
|
+
self.resolve(self.published(), ref=ref)
|
|
93
|
+
with self.assertRaisesRegex(ValueError, "VERSION"):
|
|
94
|
+
self.resolve(self.published(), version="1.4.2")
|
|
95
|
+
|
|
96
|
+
def test_repository_mismatch_and_fork_publication_refuse(self):
|
|
97
|
+
for event_repository, repository in (("example/fork", REPOSITORY),
|
|
98
|
+
(REPOSITORY, "example/fork"),
|
|
99
|
+
("example/fork", "example/fork")):
|
|
100
|
+
payload = self.published()
|
|
101
|
+
payload["repository"]["full_name"] = event_repository
|
|
102
|
+
with self.subTest(repository=repository), self.assertRaises(ValueError):
|
|
103
|
+
self.resolve(payload, repository=repository)
|
|
104
|
+
for value in (None, [], {}):
|
|
105
|
+
payload = self.published()
|
|
106
|
+
payload["repository"] = value
|
|
107
|
+
with self.assertRaises(ValueError):
|
|
108
|
+
self.resolve(payload)
|
|
109
|
+
|
|
110
|
+
def test_dispatch_defaults_to_build_only_on_branches_and_forks(self):
|
|
111
|
+
payload = {"repository": {"full_name": "example/fork"}}
|
|
112
|
+
result = self.resolve(payload, event="workflow_dispatch", ref="refs/heads/review", repository="example/fork")
|
|
113
|
+
self.assertEqual(result["destination"], "validate")
|
|
114
|
+
self.assertFalse(result["upload_performed"])
|
|
115
|
+
|
|
116
|
+
def test_manual_upload_still_requires_explicit_matching_version(self):
|
|
117
|
+
for destination in ("pypi", "testpypi"):
|
|
118
|
+
for version in ("", "1.4.2"):
|
|
119
|
+
with self.subTest(destination=destination, version=version), self.assertRaises(ValueError):
|
|
120
|
+
self.resolve(self.dispatch(destination=destination, version=version), event="workflow_dispatch")
|
|
121
|
+
result = self.resolve(self.dispatch(destination=destination, version=VERSION), event="workflow_dispatch")
|
|
122
|
+
self.assertEqual(result["destination"], destination)
|
|
123
|
+
self.assertFalse(result["upload_performed"])
|
|
124
|
+
|
|
125
|
+
def test_invalid_inputs_and_unsupported_events_refuse(self):
|
|
126
|
+
for payload in ([], None, "event"):
|
|
127
|
+
with self.assertRaises(ValueError):
|
|
128
|
+
self.resolve(payload)
|
|
129
|
+
for inputs in (None, [], {"version": True}, {"destination": False}, {"destination": "other"}):
|
|
130
|
+
payload = self.dispatch()
|
|
131
|
+
payload["inputs"] = inputs
|
|
132
|
+
with self.assertRaises(ValueError):
|
|
133
|
+
self.resolve(payload, event="workflow_dispatch")
|
|
134
|
+
for event in ("push", "pull_request", "repository_dispatch", ""):
|
|
135
|
+
with self.subTest(event=event), self.assertRaises(ValueError):
|
|
136
|
+
self.resolve(self.published(), event=event)
|
|
137
|
+
|
|
138
|
+
def test_cli_ignores_release_body_and_conflicting_manual_inputs(self):
|
|
139
|
+
payload = self.published()
|
|
140
|
+
marker = "SYNTHETIC_PRIVATE_RELEASE_BODY"
|
|
141
|
+
payload["release"]["body"] = marker + "\ndestination=validate\nversion=9.9.9"
|
|
142
|
+
payload["inputs"] = {"destination": "validate", "version": "9.9.9"}
|
|
143
|
+
code, result, output = self.cli(payload)
|
|
144
|
+
self.assertEqual(code, 0)
|
|
145
|
+
self.assertEqual(result["destination"], "pypi")
|
|
146
|
+
self.assertNotIn(marker, output)
|
|
147
|
+
self.assertFalse(result["upload_performed"])
|
|
148
|
+
|
|
149
|
+
def test_cli_validation_upload_and_refusal_remain_distinct(self):
|
|
150
|
+
code, result, _ = self.cli(self.dispatch(), event="workflow_dispatch", ref="refs/heads/main")
|
|
151
|
+
self.assertEqual((code, result["destination"]), (0, "validate"))
|
|
152
|
+
code, result, _ = self.cli(self.dispatch(destination="pypi", version=VERSION), event="workflow_dispatch")
|
|
153
|
+
self.assertEqual((code, result["destination"]), (0, "pypi"))
|
|
154
|
+
bad = copy.deepcopy(self.published())
|
|
155
|
+
bad["release"]["tag_name"] = "v9.9.9"
|
|
156
|
+
code, result, _ = self.cli(bad)
|
|
157
|
+
self.assertEqual(code, 1)
|
|
158
|
+
self.assertIn("error", result)
|
|
159
|
+
self.assertFalse(result["upload_performed"])
|
|
160
|
+
|
|
161
|
+
def test_cli_rejects_mixed_argument_sources(self):
|
|
162
|
+
for extra in (("--expected-version", VERSION), ("--destination", "pypi")):
|
|
163
|
+
code, result, _ = self.cli(self.published(), *extra)
|
|
164
|
+
self.assertEqual(code, 1)
|
|
165
|
+
self.assertIn("event inputs", result["error"])
|
|
166
|
+
|
|
167
|
+
def test_cli_requires_both_event_arguments(self):
|
|
168
|
+
for arguments in (("--event-name", "release"), ("--event-path", str(self.event_path))):
|
|
169
|
+
output = io.StringIO()
|
|
170
|
+
with mock.patch.object(release, "ROOT", self.root), redirect_stdout(output):
|
|
171
|
+
code = release.main(["identity", *arguments])
|
|
172
|
+
self.assertEqual(code, 1)
|
|
173
|
+
self.assertIn("Both event", json.loads(output.getvalue())["error"])
|
|
174
|
+
|
|
175
|
+
def test_event_reader_bounds_and_invalid_json_do_not_echo_content(self):
|
|
176
|
+
self.event_path.write_bytes(b"x" * 1_000_001)
|
|
177
|
+
with self.assertRaisesRegex(ValueError, "inspection limit"):
|
|
178
|
+
release.read_workflow_event(self.event_path)
|
|
179
|
+
self.event_path.write_text("{SYNTHETIC_PRIVATE_RELEASE_BODY}", encoding="utf-8")
|
|
180
|
+
output = io.StringIO()
|
|
181
|
+
with mock.patch.object(release, "ROOT", self.root), redirect_stdout(output):
|
|
182
|
+
code = release.main(["identity", "--event-name", "release", "--event-path", str(self.event_path)])
|
|
183
|
+
self.assertEqual(code, 1)
|
|
184
|
+
self.assertNotIn("SYNTHETIC_PRIVATE_RELEASE_BODY", output.getvalue())
|
|
185
|
+
|
|
186
|
+
def test_event_reader_refuses_missing_directory_and_linked_files(self):
|
|
187
|
+
for path in (self.root / "missing.json", self.root):
|
|
188
|
+
with self.assertRaisesRegex(ValueError, "regular unlinked"):
|
|
189
|
+
release.read_workflow_event(path)
|
|
190
|
+
self.event_path.write_text("{}", encoding="utf-8")
|
|
191
|
+
linked = self.root / "linked.json"
|
|
192
|
+
try:
|
|
193
|
+
linked.symlink_to(self.event_path)
|
|
194
|
+
except OSError as exc:
|
|
195
|
+
self.skipTest("Native symlink creation unavailable: " + str(exc))
|
|
196
|
+
with self.assertRaisesRegex(ValueError, "regular unlinked"):
|
|
197
|
+
release.read_workflow_event(linked)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
if __name__ == "__main__":
|
|
201
|
+
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|