Skip to content

feat(api-keys): roboflow api-key CLI#495

Open
yeldarby wants to merge 8 commits into
mainfrom
api-key-management
Open

feat(api-keys): roboflow api-key CLI#495
yeldarby wants to merge 8 commits into
mainfrom
api-key-management

Conversation

@yeldarby

Copy link
Copy Markdown
Contributor

Adds a roboflow api-key CLI command group (list / get / create / update / revoke / protect / disable / publishable) wrapping the new REST endpoints.

  • Secret shown once on create; non-secret keyId handles; --scope / --metadata on create + update; protected-key 409 and plan-feature 403 hints; consistent exit codes and --json envelopes.
  • Tests in tests/cli/test_api_key.py; version bump.

🤖 Generated with Claude Code

yeldarby and others added 2 commits June 19, 2026 23:06
Checkpoint of the API key management feature work (round 1 + refinement pass).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… hint

Round-2 refinement fixes (independent re-review).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread roboflow/cli/handlers/api_key.py Fixed
yeldarby and others added 4 commits June 20, 2026 10:02
Emit the create result (incl. the one-time secret) via the shared output() helper like every other handler, instead of a raw print(). Resolves a CodeQL clear-text-logging alert and matches the repo's documented output idiom; stdout is unchanged in both --json and human modes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…sets

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… + presets

External review (P2): omitting scopes inherits the caller's scopes (not unconditional full access); note role:<name> presets and [] for no abilities.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
External review (skills PR): the command-groups reference omitted api-key, so agents couldn't discover it. The group ships in this PR.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
digaobarbosa
digaobarbosa previously approved these changes Jun 22, 2026

@digaobarbosa digaobarbosa left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM after API merge

Explain the create privilege (unscoped or api-key:create; OAuth also needs create_api_key) and the all-folder requirement for workspace-wide keys.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@davidnichols-ops

Copy link
Copy Markdown

Was looking at this PR and noticed a few things — posting in case any of it's useful. I'm not affiliated with Roboflow, just an outside contributor who happened to dig in.

CI failure is repo-wide, not from this PR

All 8 build jobs fail at the mypy roboflow step with the same error:

numpy/__init__.pyi:737: error: Type statement is only supported in Python 3.12 and greater  [syntax]
Found 1 error in 1 file (errors prevented further checking)
make: *** [Makefile:13: check_code_quality] Error 2

The failure is in numpy's type stub file, not in any file this PR touches. ruff check and ruff format --check pass cleanly on this PR; test-slim is green; the PR's own test suite (tests/cli/test_api_key.py, 42 tests) passes locally.

This looks like a numpy 2.5.0 × mypy interaction — numpy 2.5.0 ships stubs using the Python 3.12+ type statement syntax, and mypy roboflow fails parsing them before reaching the project's own code.

To confirm it's repo-wide: the same failure hits an unrelated PR the same day — the pre-commit-ci bot's chore(pre_commit): ⬆ pre_commit autoupdate (run 28391383808), which only bumps pre-commit hook versions and touches none of this PR's files. main was last pushed June 9 (before numpy 2.5.0 was cached), which is why it's still green.

Likely fixes (for maintainers, not this PR): pin numpy<2.5, bump mypy, or exclude numpy stubs from the mypy step.

Likely bug: custom_metadata sent as snake_case

In roboflow/adapters/rfapi.py:1521, the create body uses body["custom_metadata"] (snake_case), but every other multi-word body key in the file is camelCase:

Line Key Convention
124 continueIfNoRefund camelCase
290 annotationGroup camelCase
413 removeMetadata camelCase
415 addTags camelCase
684 batchName camelCase
1519 folderIds camelCase
1521 custom_metadata snake_case ← outlier

The backend almost certainly expects customMetadata to match folderIds in the same request. If so, metadata is silently dropped on every api-key create call that passes --metadata.

The same flows through _update_key at roboflow/cli/handlers/api_key.py:349 (fields["custom_metadata"]), and the update_api_key docstring at rfapi.py:1534 references custom_metadata.

No test catches this because every existing test mocks at the Python function level (mock_create.assert_called_once_with(...)) and never inspects what requests.post actually sends. The TestStatusCodePropagation class already mocks requests.* but only checks status_code, not the body.

Suggested fix: body["custom_metadata"]body["customMetadata"] at rfapi.py:1521, and fields["custom_metadata"]fields["customMetadata"] at api_key.py:349. Plus a test that mocks requests.post and asserts call_args.kwargs["json"] contains customMetadata (camelCase).

Caveat: I'm inferring customMetadata from the convention of every sibling key. The backend owner should confirm the exact field name before merging.

Minor: error hint accuracy

A few of the error hints are broader than they should be. Not blockers, but worth a look:

_create_key (api_key.py:296–316): if status in (403, 404) gives the same hint for both. A 404 means "workspace or endpoint not found", not "you lack the api-key:create scope" — the scope hint is misleading for 404. The else branch gives a plan-feature hint for every other status including 401 (auth), which is confusing. Suggested: split 403 and 404, and only apply the plan-feature hint for 403.

_update_key (api_key.py:351–359): doesn't special-case 403 (plan feature) or 409 (protected key) the way _disable_key does (api_key.py:401–422). A user hitting api-key update <id> --scope image:read without the plan feature gets a generic error with no "Advanced API Keys" hint.

_protect_key (api_key.py:375–382): passes the "The API cannot unprotect a key..." hint for every error including 500s and 401s. That hint only makes sense for 403. Suggested: scope it to 403 only.

Minor: missing CHANGELOG + CLI-COMMANDS.md examples

  • roboflow/__init__.py:24 bumps the version to 1.3.11, but CHANGELOG.md has no ## 1.3.11 section (every prior release has one).
  • CLI-COMMANDS.md got a table row for api-key (line 360) but no usage examples subsection — every other command group has one (e.g. ### Inspect model evaluations at line 245).

What's already good

  • All 42 tests in tests/cli/test_api_key.py pass locally.
  • ruff check and ruff format --check are clean on the PR's files.
  • Exit-code contract (0/1/2/3) is correctly implemented and tested.
  • --json output and destructive-confirmation gating are correct.
  • Handler registration, alphabetical ordering, and ctx_to_args bridge all follow the repo's conventions.

Hope this is helpful — happy to elaborate on any of it.

Adds --no-scopes ([]), --full-access (explicit null/unscoped), and --clear-metadata ({}) to 'roboflow api-key create/update', mutually exclusive with --scope/--metadata. Adds a backward-compatible FULL_ACCESS sentinel to rfapi.py so create_api_key/update_api_key can emit an explicit null scopes value (previously None only meant omit). 18 new CLI tests; full CLI suite green, ruff + mypy clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

4 participants