insightfactory-cli 1.0.1.dev5__py3-none-any.whl → 1.0.1.dev9__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.
if_cli/commands/api.py CHANGED
@@ -61,6 +61,27 @@ def describe_route(document: dict[str, Any], method: str, path: str) -> dict[str
61
61
  return operation if isinstance(operation, dict) else None
62
62
 
63
63
 
64
+ def _is_absolute_http_url(api_path: str) -> bool:
65
+ parsed = parse_url(api_path)
66
+ return parsed.scheme in {"http", "https"} and bool(parsed.hostname)
67
+
68
+
69
+ def _die_on_misplaced_http_method(api_path: str, positionals: list[str]) -> None:
70
+ """Explain a `curl`-shaped `if-cli api GET /path`, where the verb is read as the path.
71
+
72
+ Recognition spans every verb a user might reach for, not just the ones -X accepts:
73
+ naming the misplaced verb is more useful than a path error even for TRACE, which
74
+ -X then rejects on its own terms.
75
+ """
76
+ method = api_path.lower()
77
+ if method not in HTTP_METHODS:
78
+ return
79
+ targets = [value for value in positionals[1:] if value.startswith("/") or _is_absolute_http_url(value)]
80
+ target = targets[0] if targets else "/api/path"
81
+ suggestion = f"if-cli api -X {method.upper()} {target}"
82
+ die(f"'{api_path}' looks like an HTTP method, not a path — did you mean: {suggestion} ?")
83
+
84
+
64
85
  def _die_on_msys_mangled_path(api_path: str) -> None:
65
86
  if not WINDOWS_DRIVE_PATH.search(api_path):
66
87
  return
@@ -73,11 +94,6 @@ def _die_on_msys_mangled_path(api_path: str) -> None:
73
94
  )
74
95
 
75
96
 
76
- def _is_absolute_http_url(api_path: str) -> bool:
77
- parsed = parse_url(api_path)
78
- return parsed.scheme in {"http", "https"} and bool(parsed.hostname)
79
-
80
-
81
97
  def resolve_api_url(host: str, api_path: str) -> ParseResult:
82
98
  if not api_path.startswith("/") and not _is_absolute_http_url(api_path):
83
99
  _die_on_msys_mangled_path(api_path)
@@ -145,6 +161,7 @@ def api_command(argv: list[str]) -> None:
145
161
  api_path = positionals[0] if positionals else None
146
162
  if not api_path:
147
163
  die(API_USAGE)
164
+ _die_on_misplaced_http_method(api_path, positionals)
148
165
 
149
166
  if values["method"] is not None:
150
167
  method = values["method"].upper()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: insightfactory-cli
3
- Version: 1.0.1.dev5
3
+ Version: 1.0.1.dev9
4
4
  Summary: Profile-based authentication CLI for the InsightFactory Interfaces API
5
5
  Project-URL: Homepage, https://github.com/insightfactory-ai/insightfactory-cli
6
6
  Project-URL: Repository, https://github.com/insightfactory-ai/insightfactory-cli
@@ -102,6 +102,12 @@ prefixes.
102
102
  Override the config directory with `INSIGHTFACTORY_CONFIG_DIR` (useful in tests
103
103
  and CI). The default is `~/.insightfactory`.
104
104
 
105
+ The request method goes in `-X`; `if-cli api` takes exactly one positional
106
+ argument and reads it as the path. A `curl`-shaped `if-cli api GET /api/schedules`
107
+ therefore parses `GET` as the path — write `if-cli api -X GET /api/schedules`
108
+ instead. The CLI names the misplaced verb rather than reporting a problem with
109
+ `/api/schedules`.
110
+
105
111
  On Git Bash (MSYS2) on Windows, path conversion rewrites a leading-slash
106
112
  argument such as `/api/agent-projects` into a Windows path. Prefix the command
107
113
  with `MSYS_NO_PATHCONV=1`:
@@ -212,12 +218,16 @@ Python 3.10 or newer is required. Runtime code is stdlib-only.
212
218
 
213
219
  ## Release
214
220
 
215
- Publishing is automatic from GitHub Actions on merge. Verify and build live in
216
- the reusable workflow
221
+ **A release is cut by pushing a tag, not by merging.** Merging to `main` runs CI and
222
+ publishes nothing.
223
+
224
+ ```bash
225
+ git tag v1.0.1 && git push origin v1.0.1
226
+ ```
227
+
228
+ Verify and build live in the reusable workflow
217
229
  [`if_s_insightfactory_cli.yml`](https://github.com/insightfactory-ai/if_sre_github_actions/blob/main/.github/workflows/if_s_insightfactory_cli.yml)
218
- in `insightfactory-ai/if_sre_github_actions`
219
- ([PR #300](https://github.com/insightfactory-ai/if_sre_github_actions/pull/300)
220
- must merge before these stubs go green). This repository's
230
+ in `insightfactory-ai/if_sre_github_actions`. This repository's
221
231
  `.github/workflows/release.yml` keeps only the OIDC publish job, because PyPI
222
232
  Trusted Publishing is registered for **this** repo, workflow `release.yml`, and
223
233
  environment `pypi` — a reusable workflow cannot be registered.
@@ -228,24 +238,38 @@ the reusable workflow and uploads it with `pypa/gh-action-pypi-publish`.
228
238
 
229
239
  | Trigger | Published version | PyPI role |
230
240
  |---|---|---|
231
- | Push / merge to `develop` | `1.0.0.dev{N}` | pre-release (`pip install --pre`) |
232
- | Push / merge to `main` | `1.0.0` (the version in `pyproject.toml`) | latest |
233
- | Push of a `v*` tag | same as `main` | retry / explicit cut of that version |
241
+ | Push / merge to `develop` | `1.0.1.dev{N}` | pre-release (`pip install --pre`) |
242
+ | Push of a `v*` tag | the version in `pyproject.toml`, which the tag must match | latest |
243
+ | Merge to `main` | nothing — CI only | — |
234
244
 
235
245
  `N` is `github.run_number` of the reusable workflow: a per-workflow integer
236
246
  that goes up on every run and stays the same across re-runs of that run (so a
237
247
  failed publish can be retried under the same version). It does not depend on
238
- git history or clone depth. After a successful `main` publish, bump `version`
239
- in `pyproject.toml` on `develop` so later `.devN` builds sort *after* what just
240
- landed; PEP 440 puts `1.0.0.devN` before `1.0.0`. Because of that ordering, a
248
+ git history or clone depth. After a successful release, bump `version` in
249
+ `pyproject.toml` on `develop` so later `.devN` builds sort *after* what just
250
+ landed; PEP 440 puts `1.0.1.devN` before `1.0.1`. Because of that ordering, a
241
251
  dependency spec of `insightfactory-cli>=1.0.0` does not match develop-channel
242
252
  `.devN` builds; an in-process consumer that wants them must specify a
243
253
  pre-release-bearing spec such as `insightfactory-cli>=1.0.0.dev0` (which also
244
254
  matches the final `1.0.0` release).
245
255
 
246
- A version that is already on PyPI is skipped rather than failing, so a second
247
- push to `main` (or a `v*` tag for the same version) is a no-op. A `v*` tag
248
- whose name does not match `pyproject.toml` fails before publishing.
256
+ A `v*` tag whose name does not match `pyproject.toml` fails before publishing.
257
+
258
+ The two channels treat an already-published version differently, on purpose:
259
+
260
+ - **Release channel (a tag):** publishing fails, naming the fix. A tag is a request to
261
+ release that version; if it cannot be honoured, the run must say so rather than report
262
+ success having published nothing. (git also refuses to push a tag that already exists,
263
+ so this is hard to reach.)
264
+ - **Dev channel (`develop`):** publishing is skipped quietly. `.dev{N}` uses
265
+ `github.run_number`, which is stable across re-runs of one run, so an already-present
266
+ version there can only mean a re-run of a run that already published — real idempotency,
267
+ not a swallowed mistake.
268
+
269
+ This split is why releasing moved off `main`. While a merge was the trigger, an
270
+ already-published version *had* to be tolerated as a no-op for merges to stay green — so a
271
+ merge that released nothing looked exactly like one that released, and a forgotten version
272
+ bump shipped nothing silently.
249
273
 
250
274
  ## Differences from the Node CLI
251
275
 
@@ -10,14 +10,14 @@ if_cli/main.py,sha256=0bZZDNoT1NGHeZhH6sD2ZNBTtpYGGTfPTnRIJiGr2vA,2537
10
10
  if_cli/oauth.py,sha256=MUvG1aNGpknFfbtblq2Eg86rQv3VM9NrGl4oDgIr5oM,15596
11
11
  if_cli/runtime.py,sha256=Pz2iVclqy-uu1UMQWK2P_Hx7O4zAF30UpSFqQvGN0NY,2663
12
12
  if_cli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- if_cli/commands/api.py,sha256=f0hpf6ZzgoX3UNERXVeZi6RsQbAfJnMXQj9EQCVbz70,7499
13
+ if_cli/commands/api.py,sha256=Y759ndF1feZK0IOXjuRWS6v27dvGCTM2mfe72ivwJ_c,8346
14
14
  if_cli/commands/config.py,sha256=1KAxjme897NIbXTGBw1rNJlXnFvhbEUf5MAli8A93ns,1724
15
15
  if_cli/commands/login.py,sha256=vxsYcRZWsaNEzekyN1zy4ROJO-OCGUpjaVUBhtbK1LY,3255
16
16
  if_cli/commands/logout.py,sha256=TW0P4_tXcTtiHHR73xVPHyhlbOWD1jBnkZsMImCiCBw,668
17
17
  if_cli/commands/profiles.py,sha256=JNBrYWdHQ9MHauwZxH33IAOYgRYRniX7bwd7KBT6Vyk,3869
18
18
  if_cli/commands/set_token.py,sha256=BVyv_QmByvllr04pE2sz8JFEscHbIgOC-7ZPg61PRoc,1886
19
19
  if_cli/commands/token.py,sha256=CyiXkyH7vxgH91-n-XJzeHnLI6mZJwO9Js5AHvfHaeM,1120
20
- insightfactory_cli-1.0.1.dev5.dist-info/METADATA,sha256=5DCZVpYI9OxCWJHNAobvxRCtoDKh14AWrR_8xirXnLk,10880
21
- insightfactory_cli-1.0.1.dev5.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
22
- insightfactory_cli-1.0.1.dev5.dist-info/entry_points.txt,sha256=UlX854D7f-7dBj5F0wGdakVy6vopvP9hA1BN-gv739w,44
23
- insightfactory_cli-1.0.1.dev5.dist-info/RECORD,,
20
+ insightfactory_cli-1.0.1.dev9.dist-info/METADATA,sha256=Vj1zz9rwI1YoZqWM7OMZElRYaTLvIjSCL9gP6iecSls,11987
21
+ insightfactory_cli-1.0.1.dev9.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
22
+ insightfactory_cli-1.0.1.dev9.dist-info/entry_points.txt,sha256=UlX854D7f-7dBj5F0wGdakVy6vopvP9hA1BN-gv739w,44
23
+ insightfactory_cli-1.0.1.dev9.dist-info/RECORD,,