feat(tags): Add support to bulk assign tags - #74
Merged
Conversation
Rutvik G (rutvik-gs)
marked this pull request as ready for review
July 29, 2026 13:01
Ben Schwartz (btschwartz12)
approved these changes
Jul 30, 2026
Ben Schwartz (btschwartz12)
left a comment
Contributor
There was a problem hiding this comment.
LGTM!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
censys tags assign can only take assets one at a time. The platform already exposes an async bulk endpoint to assign tags in bulk by running a
cenqlquery.The mode boundary matters as bulk must never be inferred from a missing argument, because guessing wrong here
tags a very large number of assets.
Technical Changes
Bulk rides as flags on the existing assign verb, not a bulk-assign subcommand — the async
path renders a different payload (TagOperation vs per-asset results), which splits cleanly in
Run.
internal/command/tags/assign.go—--query,--max-assets,--wait/-w,--timeout,--yes/-y. Modes are mutually exclusive in PreRun and bulk-only flags without--queryaretyped errors, not silent no-ops. Bulk always confirms (tag, query, effective cap) unless
--yes, and refuses in a non-TTY without it. --wait maps the terminal status onto the exitcode: succeeded/limit_reached → 0 (the latter warns), failed/cancelled → non-zero with
the payload still rendered.
internal/command/tags/operationwait.go(new) — wait plumbing that operations get ownedprivately: parseWaitFlags, waitForOperation, terminal-status → exit code, track hints.
operations_get.go moves onto it; TAGS-12 reuses it.
internal/app/tags/bulk.go(new) — BulkAssign: rejects a blank query, resolves a tag namevia the existing resolveTagID, submits, maps the operation. TAGS-12's BulkUnassign joins it.
internal/pkg/clients/censys/tags.go— BulkCreateTagAssignments. Mocks regenerated; noSDK bump needed.
internal/command/tags/{short,utils}.go,internal/app/tags/operations.go— sharedhelpers extracted, no behavior change: renderOperationDetail, confirmAction (the block
delete/unassign each copied), printNote, mapOperationResult.
--timeout 0on operations get --wait expired before the first poll. It now means "no limit",matching the global --timeout-http; a negative value is a typed error.
--quietis now honored for the index-lag notes in assign/unassign, which printedunconditionally. Aborts and errors still always print — outcomes, not notes.
How to Test
Live, on a throwaway tag (bulk mutates at scale, so nothing below is automated):