insightfactory-cli 1.1.0.dev22__tar.gz → 1.1.1.dev25__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. insightfactory_cli-1.1.1.dev25/.github/workflows/release.yml +94 -0
  2. insightfactory_cli-1.1.1.dev25/CHANGELOG.md +125 -0
  3. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/CLAUDE.md +6 -0
  4. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/PKG-INFO +31 -2
  5. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/README.md +30 -1
  6. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/pyproject.toml +6 -1
  7. insightfactory_cli-1.1.1.dev25/tests/test_release_tools.py +490 -0
  8. insightfactory_cli-1.1.1.dev25/tools/check_release.py +79 -0
  9. insightfactory_cli-1.1.1.dev25/tools/release_notes.py +280 -0
  10. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/uv.lock +1 -1
  11. insightfactory_cli-1.1.0.dev22/.github/workflows/release.yml +0 -45
  12. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/.github/workflows/ci.yml +0 -0
  13. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/.github/workflows/claude.yml +0 -0
  14. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/.gitignore +0 -0
  15. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/.python-version +0 -0
  16. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/AGENTS.md +0 -0
  17. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/LICENSE +0 -0
  18. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/src/if_cli/__init__.py +0 -0
  19. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/src/if_cli/__main__.py +0 -0
  20. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/src/if_cli/assets/__init__.py +0 -0
  21. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/src/if_cli/assets/insightfactoryai-logo.svg +0 -0
  22. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/src/if_cli/cache.py +0 -0
  23. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/src/if_cli/callback_page.py +0 -0
  24. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/src/if_cli/cli.py +0 -0
  25. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/src/if_cli/colour.py +0 -0
  26. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/src/if_cli/commands/__init__.py +0 -0
  27. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/src/if_cli/commands/api.py +0 -0
  28. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/src/if_cli/commands/config.py +0 -0
  29. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/src/if_cli/commands/login.py +0 -0
  30. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/src/if_cli/commands/logout.py +0 -0
  31. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/src/if_cli/commands/mcp.py +0 -0
  32. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/src/if_cli/commands/profiles.py +0 -0
  33. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/src/if_cli/commands/set_token.py +0 -0
  34. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/src/if_cli/commands/token.py +0 -0
  35. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/src/if_cli/config.py +0 -0
  36. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/src/if_cli/constants.py +0 -0
  37. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/src/if_cli/http.py +0 -0
  38. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/src/if_cli/main.py +0 -0
  39. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/src/if_cli/oauth.py +0 -0
  40. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/src/if_cli/router/__init__.py +0 -0
  41. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/src/if_cli/router/catalog.py +0 -0
  42. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/src/if_cli/router/log.py +0 -0
  43. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/src/if_cli/router/policy.py +0 -0
  44. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/src/if_cli/router/server.py +0 -0
  45. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/src/if_cli/router/upstream.py +0 -0
  46. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/src/if_cli/runtime.py +0 -0
  47. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/tests/__init__.py +0 -0
  48. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/tests/cache_writer.py +0 -0
  49. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/tests/conftest.py +0 -0
  50. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/tests/helpers.py +0 -0
  51. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/tests/servers.py +0 -0
  52. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/tests/test_api.py +0 -0
  53. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/tests/test_api_command.py +0 -0
  54. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/tests/test_cache.py +0 -0
  55. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/tests/test_cli.py +0 -0
  56. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/tests/test_config.py +0 -0
  57. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/tests/test_config_command.py +0 -0
  58. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/tests/test_login.py +0 -0
  59. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/tests/test_mcp_command.py +0 -0
  60. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/tests/test_oauth.py +0 -0
  61. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/tests/test_oauth_flow.py +0 -0
  62. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/tests/test_oauth_force_refresh.py +0 -0
  63. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/tests/test_profiles.py +0 -0
  64. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/tests/test_programmatic_api.py +0 -0
  65. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/tests/test_router_catalog.py +0 -0
  66. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/tests/test_router_policy.py +0 -0
  67. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/tests/test_router_server.py +0 -0
  68. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/tests/test_router_upstream.py +0 -0
  69. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/tests/test_runtime.py +0 -0
  70. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/tests/test_set_token.py +0 -0
  71. {insightfactory_cli-1.1.0.dev22 → insightfactory_cli-1.1.1.dev25}/tests/test_token.py +0 -0
@@ -0,0 +1,94 @@
1
+ name: release
2
+
3
+ # CD: a push to develop publishes a `.dev<run_number>` pre-release; a `v*` tag
4
+ # creates the GitHub Release and publishes it to PyPI. A merge to main runs CI and
5
+ # publishes nothing -- see if_s_insightfactory_cli.yml for why releasing is a
6
+ # deliberate tag push.
7
+ # Verify + build live in insightfactory-ai/if_sre_github_actions
8
+ # (if_s_insightfactory_cli.yml). The publish job stays here because PyPI
9
+ # Trusted Publishing is registered for this repository, workflow `release.yml`,
10
+ # and environment `pypi` — a reusable workflow cannot be registered.
11
+ on:
12
+ push:
13
+ branches: [develop, main]
14
+ tags:
15
+ - "v*"
16
+
17
+ concurrency:
18
+ group: release-${{ github.ref }}
19
+ cancel-in-progress: false
20
+
21
+ permissions:
22
+ contents: read
23
+
24
+ jobs:
25
+ ci:
26
+ uses: insightfactory-ai/if_sre_github_actions/.github/workflows/if_s_insightfactory_cli.yml@main
27
+ secrets: inherit
28
+ permissions:
29
+ # The central workflow's release job creates the GitHub Release for the pushed
30
+ # tag, which needs `contents: write`. A called workflow may only reduce the
31
+ # caller's grant, never raise it, so the grant has to be made here. ci.yml
32
+ # stays read-only: its release job is push-gated and simply never runs.
33
+ contents: write
34
+
35
+ publish:
36
+ name: Publish to PyPI
37
+ needs: ci
38
+ if: needs.ci.outputs.should_publish == 'true'
39
+ runs-on: ${{ vars.RUN_LINUX }}
40
+ timeout-minutes: 10
41
+ environment:
42
+ name: pypi
43
+ url: https://pypi.org/p/insightfactory-cli
44
+ permissions:
45
+ id-token: write
46
+ # Publishing the draft Release the central workflow created. A job-level
47
+ # block replaces the set rather than adding to it, so id-token has to be
48
+ # repeated here.
49
+ contents: write
50
+ steps:
51
+ - uses: actions/download-artifact@v6
52
+ with:
53
+ name: dist-release
54
+ path: dist/
55
+
56
+ - uses: pypa/gh-action-pypi-publish@release/v1
57
+
58
+ # Only now, with the files actually on PyPI, does the Release stop being a
59
+ # draft: its body tells people to `pip install insightfactory-cli==X` and
60
+ # links the PyPI page, so publishing it any earlier would show instructions
61
+ # that 404, and leave them showing if this upload failed.
62
+ #
63
+ # Two details, both because this step runs after a successful upload and so
64
+ # must not be the thing that breaks:
65
+ #
66
+ # TAG is github.ref_name, not an output of the called workflow. On a tag
67
+ # push it is the tag, locally and unconditionally, and cannot drift if that
68
+ # workflow is edited.
69
+ #
70
+ # `make_latest: legacy` asks GitHub to pick Latest by version and date.
71
+ # `gh release edit --latest` marks this release Latest outright, and the
72
+ # concurrency group above is per-ref, so two tags can be in flight at once
73
+ # and an older one finishing last would take the badge from the newer
74
+ # release. The flag cannot express legacy, hence the raw API call.
75
+ - name: Publish the draft GitHub Release
76
+ if: startsWith(github.ref, 'refs/tags/v')
77
+ env:
78
+ GH_TOKEN: ${{ github.token }}
79
+ GH_REPO: ${{ github.repository }}
80
+ TAG: ${{ github.ref_name }}
81
+ run: |
82
+ set -euo pipefail
83
+ # The list endpoint, because get-by-tag does not return drafts. One page:
84
+ # a release created moments ago is among the newest.
85
+ id=$(gh api "repos/${GH_REPO}/releases?per_page=100" \
86
+ --jq 'map(select(.tag_name == env.TAG)) | first | .id')
87
+ if [ -z "$id" ] || [ "$id" = "null" ]; then
88
+ echo "::error::${TAG} is on PyPI but has no GitHub Release to publish. Re-run the release job for this tag."
89
+ exit 1
90
+ fi
91
+ gh api -X PATCH "repos/${GH_REPO}/releases/${id}" \
92
+ -F draft=false \
93
+ -f make_latest=legacy \
94
+ --jq '.html_url'
@@ -0,0 +1,125 @@
1
+ # Changelog
2
+
3
+ All notable changes to `insightfactory-cli` are recorded here.
4
+
5
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this
6
+ project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ The release job reads the section whose heading matches the version in
9
+ `pyproject.toml` and makes it the body of the GitHub Release. CI holds both ends of
10
+ that: a PR into `develop` or `main` must raise the version and add its section here,
11
+ and a tag whose version has no section fails rather than releasing an empty page.
12
+
13
+ ## 1.1.1
14
+
15
+ ### Added
16
+
17
+ - This changelog, and `tools/release_notes.py`, which renders a version's section
18
+ plus install instructions as the GitHub Release body.
19
+ - A `release-guard` CI job. A PR into `develop` or `main` that does not raise the
20
+ version in `pyproject.toml` and document it here now fails, instead of landing
21
+ under a section written for someone else's work.
22
+
23
+ ### Changed
24
+
25
+ - Pushing a `v*` tag now creates the GitHub Release with the wheel and sdist
26
+ attached, instead of stopping at PyPI and leaving the Release to be made by hand.
27
+ The Release is drafted first and published once PyPI has the files, so its
28
+ install instructions never point at a version that is not there yet.
29
+
30
+ ## 1.1.0
31
+
32
+ First release since 1.0.2. Two features: an MCP server that fronts several factory
33
+ environments at once, and a login callback page that looks like the rest of the login.
34
+
35
+ ### Added
36
+
37
+ - **`if-cli mcp`.** One MCP server over stdio, fronting several factory environments
38
+ as a single tool catalogue rather than one connection per environment. Every tool
39
+ takes a required `environment` argument, and each call goes to that environment's
40
+ own `/mcp` with that environment's profile. A profile's bearer token never reaches
41
+ another environment's host. Needs the `mcp` extra; the base install stays
42
+ dependency-free.
43
+ - Named factory sections in `~/.insightfactory/config`, which is where the router
44
+ reads its environments and its `writable` list. Writes are allowed only on the
45
+ environments named there. Everywhere else the router allows an allowlist of tools
46
+ known to read, so a tool it has not heard of is refused rather than assumed safe.
47
+ - A tool is published only on the environments whose input schema matches the
48
+ catalogue's, so a factory on an older release is never handed argument shapes it
49
+ does not accept.
50
+ - Databricks connect prompts. A tool that runs under your personal Databricks
51
+ identity used to fail with a refusal naming no page. The router now names the page
52
+ to visit, taken from the factory rather than a hardcoded route, and only when that
53
+ page is on the environment's own origin. The prompt never goes to the model:
54
+ linking a Databricks identity should be a deliberate trip to a page, not a link
55
+ offered mid tool call.
56
+ - `--read-only`, and a `--timeout` whose scope is documented.
57
+
58
+ ### Changed
59
+
60
+ - The loopback callback page after `if-cli login` is themed to match Auth0 Universal
61
+ Login, replacing the bare HTML it used to show.
62
+
63
+ ### Fixed
64
+
65
+ - A force-refreshed token no longer re-serves the token that was just rejected. The
66
+ refresh ran, the cached access token came back unchanged, and the 401 repeated.
67
+ - The router no longer blocks `initialize` on building its catalogue. It retries a
68
+ failed build, revalidates environments once they become reachable, and no longer
69
+ waits on an in-flight build at shutdown, so one slow factory cannot hold up the
70
+ handshake.
71
+ - `tools/list` pagination is followed, so a factory with more than one page of tools
72
+ publishes all of them.
73
+ - An unsupported repeat option dies loudly instead of being ignored.
74
+
75
+ ## 1.0.2
76
+
77
+ ### Added
78
+
79
+ - `timeout` can be raised per invocation on the programmatic API, for a call that is
80
+ expected to run long.
81
+
82
+ ### Fixed
83
+
84
+ - The timeout override no longer reaches the OAuth transport, where a long timeout
85
+ would have applied to token calls that should stay short.
86
+ - A misplaced HTTP method is named as such instead of the error blaming the path,
87
+ and the correction keeps the target you supplied.
88
+
89
+ ### Changed
90
+
91
+ - The package carries a proprietary licence and ships `LICENSE` in the wheel.
92
+
93
+ ## 1.0.1
94
+
95
+ ### Fixed
96
+
97
+ - Attached short-option values (`-ovalue`) and inline boolean values parse correctly.
98
+ - Positional argument order for the `api` and `config` commands.
99
+ - Exit status is preserved when stdout closes on Python 3.14.
100
+
101
+ ### Changed
102
+
103
+ - The Claude review workflow no longer caps its own permissions, which had stopped it
104
+ starting at all.
105
+
106
+ ## 1.0.0
107
+
108
+ First production release of the Python `if-cli`, the successor to the Node
109
+ `@insightfactory-ai/if-cli`.
110
+
111
+ ### Added
112
+
113
+ - `login`, `logout`, `profiles`, `token`, `set-token`, `config` and `api`, for
114
+ per-environment OAuth login and calling the Interfaces API from the command line.
115
+ OAuth is authorization-code with PKCE against a loopback-only callback; the CLI is
116
+ a public client with no secret.
117
+ - Runtime code is stdlib-only, so the base install pulls in nothing.
118
+ - On-disk profile and token-cache paths, JSON shapes and file permissions stay
119
+ byte-compatible with the Node CLI, so you can switch implementations without
120
+ logging in again.
121
+ - A supported in-process API: `if_cli.config.load_config`,
122
+ `if_cli.config.get_profile`, `if_cli.oauth.get_valid_token` and
123
+ `if_cli.runtime.CliError`. These raise `CliError` rather than exiting, never write
124
+ to stdout, and never block on a browser or on input. Changing any of them requires
125
+ a major version bump.
@@ -34,3 +34,9 @@ uv run ty check
34
34
  match `pyproject.toml`. Pushing `develop` publishes a `.dev{run_number}`
35
35
  pre-release. Creating the release tag is a deliberate human act — do not create
36
36
  or push one unless asked to cut a release.
37
+ - **Every PR into `develop` or `main` raises `version` in `pyproject.toml` and
38
+ documents that version in `CHANGELOG.md`.** The `release-guard` job runs
39
+ `tools/check_release.py` and fails the PR otherwise. Put user-visible changes in
40
+ the Added/Changed/Fixed group that fits; for internal-only work (tests, CI, a
41
+ refactor nothing observes) a one-line entry saying so is enough. The section
42
+ becomes the body of that version's GitHub Release.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: insightfactory-cli
3
- Version: 1.1.0.dev22
3
+ Version: 1.1.1.dev25
4
4
  Summary: Profile-based authentication CLI for the InsightFactory Interfaces API
5
5
  Project-URL: Homepage, https://insightfactory.ai
6
6
  Author-email: "insightfactory.ai Support" <support@insightfactory.ai>
@@ -340,6 +340,35 @@ and publishes nothing.
340
340
  git tag v1.0.1 && git push origin v1.0.1
341
341
  ```
342
342
 
343
+ The tag drafts the GitHub Release, uploads to PyPI, then publishes the Release.
344
+ Everything comes from one build, so the wheel and sdist attached to the Release
345
+ are the files that went to PyPI, and the draft means nobody sees `pip install
346
+ insightfactory-cli==1.1.1` on a Release page before that version exists on PyPI.
347
+ A failed upload leaves a draft rather than a published page pointing at a 404.
348
+
349
+ The body comes from [`CHANGELOG.md`](CHANGELOG.md) via `tools/release_notes.py`.
350
+ It covers the tagged version and any version opened since the last tag, because
351
+ one version per PR means unreleased versions pile up between tags: if 1.1.1 and
352
+ 1.1.2 both merge and only `v1.1.2` is tagged, 1.1.1's code ships inside that
353
+ artifact, so its notes belong on that Release. A checkout with no tags falls back
354
+ to the tagged version's section alone.
355
+
356
+ `develop` therefore carries the version the next release will use, and the
357
+ `release-guard` job holds it there. A PR into `develop` must raise `version` in
358
+ `pyproject.toml` above what `develop` already has, and must add that version's
359
+ `CHANGELOG.md` section. `tools/check_release.py` runs both checks and is what
360
+ fails the PR:
361
+
362
+ ```bash
363
+ BASE=origin/develop python3 tools/check_release.py
364
+ ```
365
+
366
+ One version per PR, then. The instinct is to let a second change join the
367
+ section the first one opened, and the guard refuses that: the version would be
368
+ tagged with notes that do not describe everything in it, and nothing stops the
369
+ tag going out between the two merges. Bump the patch number again, or fold the
370
+ second change into the first PR.
371
+
343
372
  Verify and build live in the reusable workflow
344
373
  [`if_s_insightfactory_cli.yml`](https://github.com/insightfactory-ai/if_sre_github_actions/blob/main/.github/workflows/if_s_insightfactory_cli.yml)
345
374
  in `insightfactory-ai/if_sre_github_actions`. This repository's
@@ -354,7 +383,7 @@ reusable workflow and uploads it with `pypa/gh-action-pypi-publish`.
354
383
  | Trigger | Published version | PyPI role |
355
384
  |---|---|---|
356
385
  | Push / merge to `develop` | `1.0.1.dev{N}` | pre-release (`pip install --pre`) |
357
- | Push of a `v*` tag | the version in `pyproject.toml`, which the tag must match | latest |
386
+ | Push of a `v*` tag | the version in `pyproject.toml`, which the tag must match | latest, plus a GitHub Release |
358
387
  | Merge to `main` | nothing, CI only | none |
359
388
 
360
389
  `N` is `github.run_number` of the reusable workflow. It is a per-workflow
@@ -316,6 +316,35 @@ and publishes nothing.
316
316
  git tag v1.0.1 && git push origin v1.0.1
317
317
  ```
318
318
 
319
+ The tag drafts the GitHub Release, uploads to PyPI, then publishes the Release.
320
+ Everything comes from one build, so the wheel and sdist attached to the Release
321
+ are the files that went to PyPI, and the draft means nobody sees `pip install
322
+ insightfactory-cli==1.1.1` on a Release page before that version exists on PyPI.
323
+ A failed upload leaves a draft rather than a published page pointing at a 404.
324
+
325
+ The body comes from [`CHANGELOG.md`](CHANGELOG.md) via `tools/release_notes.py`.
326
+ It covers the tagged version and any version opened since the last tag, because
327
+ one version per PR means unreleased versions pile up between tags: if 1.1.1 and
328
+ 1.1.2 both merge and only `v1.1.2` is tagged, 1.1.1's code ships inside that
329
+ artifact, so its notes belong on that Release. A checkout with no tags falls back
330
+ to the tagged version's section alone.
331
+
332
+ `develop` therefore carries the version the next release will use, and the
333
+ `release-guard` job holds it there. A PR into `develop` must raise `version` in
334
+ `pyproject.toml` above what `develop` already has, and must add that version's
335
+ `CHANGELOG.md` section. `tools/check_release.py` runs both checks and is what
336
+ fails the PR:
337
+
338
+ ```bash
339
+ BASE=origin/develop python3 tools/check_release.py
340
+ ```
341
+
342
+ One version per PR, then. The instinct is to let a second change join the
343
+ section the first one opened, and the guard refuses that: the version would be
344
+ tagged with notes that do not describe everything in it, and nothing stops the
345
+ tag going out between the two merges. Bump the patch number again, or fold the
346
+ second change into the first PR.
347
+
319
348
  Verify and build live in the reusable workflow
320
349
  [`if_s_insightfactory_cli.yml`](https://github.com/insightfactory-ai/if_sre_github_actions/blob/main/.github/workflows/if_s_insightfactory_cli.yml)
321
350
  in `insightfactory-ai/if_sre_github_actions`. This repository's
@@ -330,7 +359,7 @@ reusable workflow and uploads it with `pypa/gh-action-pypi-publish`.
330
359
  | Trigger | Published version | PyPI role |
331
360
  |---|---|---|
332
361
  | Push / merge to `develop` | `1.0.1.dev{N}` | pre-release (`pip install --pre`) |
333
- | Push of a `v*` tag | the version in `pyproject.toml`, which the tag must match | latest |
362
+ | Push of a `v*` tag | the version in `pyproject.toml`, which the tag must match | latest, plus a GitHub Release |
334
363
  | Merge to `main` | nothing, CI only | none |
335
364
 
336
365
  `N` is `github.run_number` of the reusable workflow. It is a per-workflow
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "insightfactory-cli"
3
- version = "1.1.0.dev22"
3
+ version = "1.1.1.dev25"
4
4
  description = "Profile-based authentication CLI for the InsightFactory Interfaces API"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -39,6 +39,11 @@ packages = ["src/if_cli"]
39
39
  [tool.pytest.ini_options]
40
40
  testpaths = ["tests"]
41
41
 
42
+ [tool.ty.environment]
43
+ # tools/ is not part of the package, so the scripts there are reached by path at
44
+ # runtime (sys.path.append) and ty needs telling where they live.
45
+ extra-paths = ["tools"]
46
+
42
47
  [tool.ruff]
43
48
  target-version = "py310"
44
49
  line-length = 120