Skip to content

Commit de3bf4b

Browse files
BradMclainclaude
andcommitted
feat(cli): add --limit flag to cap number of apps per command
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 441342e commit de3bf4b

2 files changed

Lines changed: 35 additions & 8 deletions

File tree

gitops/core.py

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,19 @@
1818

1919

2020
@task
21-
def summary(ctx: Any, filter: str = "", exclude: str = "") -> None:
21+
def summary(ctx: Any, filter: str = "", exclude: str = "", limit: int = 0) -> None:
2222
"""Produce a summary of apps, their tags, and their expected images & replicas.
2323
May not necessarily reflect actual app statuses if recent changes haven't yet been pushed to
2424
the remote, or the deployment has failed.
25+
Provide `limit` to show only the first N matching apps.
2526
"""
2627
get_apps(
2728
filter=filter,
2829
exclude=exclude,
2930
mode="PREVIEW",
3031
autoexclude_inactive=False,
3132
load_secrets=False,
33+
limit=limit,
3234
)
3335

3436

@@ -45,6 +47,7 @@ def bump( # noqa: C901
4547
redeploy: bool = False,
4648
skip_migrations: bool = False,
4749
skip_deploy: bool = False,
50+
limit: int = 0,
4851
) -> None:
4952
"""Bump image tag on selected app(s).
5053
Provide `image_tag` to set to a specific image tag, or provide `prefix` to use latest image
@@ -54,6 +57,7 @@ def bump( # noqa: C901
5457
Provide `redeploy` to redeploy servers even if nothing has changed.
5558
Provide `skip_migrations` to disable running migrations via helm hooks.
5659
Provide `skip_deploy` to skip deploying for non functional changes.
60+
Provide `limit` to process only the first N matching apps.
5761
"""
5862
prompt_message = "The following apps will have their image bumped"
5963
if image_tag:
@@ -68,6 +72,7 @@ def bump( # noqa: C901
6872
message=f"{prompt_message}{colourise(':', Fore.LIGHTBLUE_EX)}",
6973
load_secrets=False,
7074
mode="PROMPT" if interactive else "SILENT",
75+
limit=limit,
7176
)
7277
except AppOperationAborted:
7378
print(success_negative("Aborted."))
@@ -143,11 +148,13 @@ def redeploy(
143148
interactive: bool = True,
144149
push: bool = False,
145150
skip_migrations: bool = False,
151+
limit: int = 0,
146152
) -> None:
147153
"""Force redeploy selected app(s) without changing their image tag.
148154
Updates a UUID bump field to trigger a new deployment.
149155
Provide `push` to automatically push the commit (and retry on conflict.)
150156
Provide `skip_migrations` to disable running migrations via helm hooks.
157+
Provide `limit` to process only the first N matching apps.
151158
"""
152159
try:
153160
apps = get_apps(
@@ -157,6 +164,7 @@ def redeploy(
157164
message=f"{colourise('The following apps will be redeployed:', Fore.LIGHTBLUE_EX)}",
158165
load_secrets=False,
159166
mode="PROMPT" if interactive else "SILENT",
167+
limit=limit,
160168
)
161169
except AppOperationAborted:
162170
print(success_negative("Aborted."))
@@ -193,10 +201,12 @@ def command(
193201
interactive: bool = True,
194202
cpu: int = 0,
195203
memory: int = 0,
204+
limit: int = 0,
196205
) -> None:
197206
"""Run command on selected app(s).
198207
199208
eg. inv command customer,sandbox -e aesg "python manage.py migrate"
209+
Provide `limit` to process only the first N matching apps.
200210
"""
201211
try:
202212
apps = get_apps(
@@ -208,6 +218,7 @@ def command(
208218
f" {colourise('will be run on the following apps:', Fore.LIGHTBLUE_EX)}"
209219
),
210220
mode="PROMPT" if interactive else "SILENT",
221+
limit=limit,
211222
)
212223
except AppOperationAborted:
213224
print(success_negative("Aborted."))
@@ -233,8 +244,10 @@ def command(
233244

234245

235246
@task
236-
def tag(ctx: Any, filter: str, tag: str, exclude: str = "") -> None:
237-
"""Set a tag on selected app(s)."""
247+
def tag(ctx: Any, filter: str, tag: str, exclude: str = "", limit: int = 0) -> None:
248+
"""Set a tag on selected app(s).
249+
Provide `limit` to process only the first N matching apps.
250+
"""
238251
try:
239252
apps = get_apps(
240253
filter=filter,
@@ -245,6 +258,7 @@ def tag(ctx: Any, filter: str, tag: str, exclude: str = "") -> None:
245258
f" {colourise('will be added to the following apps:', Fore.LIGHTBLUE_EX)}"
246259
),
247260
load_secrets=False,
261+
limit=limit,
248262
)
249263
except AppOperationAborted:
250264
print(success_negative("Aborted."))
@@ -259,8 +273,10 @@ def tag(ctx: Any, filter: str, tag: str, exclude: str = "") -> None:
259273

260274

261275
@task
262-
def untag(ctx: Any, filter: str, tag: str, exclude: str = "") -> None:
263-
"""Unset a tag from selected app(s)."""
276+
def untag(ctx: Any, filter: str, tag: str, exclude: str = "", limit: int = 0) -> None:
277+
"""Unset a tag from selected app(s).
278+
Provide `limit` to process only the first N matching apps.
279+
"""
264280
try:
265281
apps = get_apps(
266282
filter=filter,
@@ -271,6 +287,7 @@ def untag(ctx: Any, filter: str, tag: str, exclude: str = "") -> None:
271287
f" {colourise('will be removed from the following apps:', Fore.LIGHTBLUE_EX)}"
272288
),
273289
load_secrets=False,
290+
limit=limit,
274291
)
275292
except AppOperationAborted:
276293
print(success_negative("Aborted."))
@@ -323,12 +340,13 @@ def _sort_envs(envs: dict[str, Any]) -> dict[str, Any]:
323340

324341

325342
@task
326-
def setenv(ctx: Any, filter: str, values: str, exclude: str = "") -> None:
343+
def setenv(ctx: Any, filter: str, values: str, exclude: str = "", limit: int = 0) -> None:
327344
"""Set one or more env vars on selected app(s).
328345
329346
eg. inv setenv customer,sandbox BG_RUNNER=DRAMATIQ,BUMP=2
330347
331348
NOTE: More broad-reaching environment changes should be made at the chart level.
349+
Provide `limit` to process only the first N matching apps.
332350
"""
333351
splitenvs = values.split(",") # pardon the pun.
334352
formatted_splitenvs = "\n".join(splitenvs)
@@ -339,6 +357,7 @@ def setenv(ctx: Any, filter: str, values: str, exclude: str = "") -> None:
339357
message=(
340358
f"{colourise('The env var(s)', Fore.LIGHTBLUE_EX)}\n{colourise(formatted_splitenvs, Fore.LIGHTYELLOW_EX)}\n{colourise('will be added to the following apps:', Fore.LIGHTBLUE_EX)}"
341359
),
360+
limit=limit,
342361
)
343362
except AppOperationAborted:
344363
print(success_negative("Aborted."))
@@ -366,12 +385,13 @@ def setenv(ctx: Any, filter: str, values: str, exclude: str = "") -> None:
366385

367386

368387
@task
369-
def unsetenv(ctx: Any, filter: str, values: str, exclude: str = "") -> None:
388+
def unsetenv(ctx: Any, filter: str, values: str, exclude: str = "", limit: int = 0) -> None:
370389
"""Unset one or more env vars on selected app(s).
371390
372391
eg. inv unsetenv customer,sandbox BG_RUNNER,BUMP
373392
374393
NOTE: More broad-reaching environment changes should be made at the chart level.
394+
Provide `limit` to process only the first N matching apps.
375395
"""
376396
splitenvs = values.split(",") # pardon the pun.
377397
formatted_splitenvs = "\n".join(splitenvs)
@@ -382,6 +402,7 @@ def unsetenv(ctx: Any, filter: str, values: str, exclude: str = "") -> None:
382402
message=(
383403
f"{colourise('The env var(s)', Fore.LIGHTBLUE_EX)}\n{colourise(formatted_splitenvs, Fore.LIGHTYELLOW_EX)}\n{colourise('will be removed from the following apps:', Fore.LIGHTBLUE_EX)}"
384404
),
405+
limit=limit,
385406
)
386407
except AppOperationAborted:
387408
print(success_negative("Aborted."))
@@ -400,10 +421,11 @@ def unsetenv(ctx: Any, filter: str, values: str, exclude: str = "") -> None:
400421

401422

402423
@task
403-
def setcluster(ctx: Any, filter: str, cluster: str, exclude: str = "") -> None:
424+
def setcluster(ctx: Any, filter: str, cluster: str, exclude: str = "", limit: int = 0) -> None:
404425
"""Move selected app(s) to given cluster.
405426
406427
eg. inv setcluster customer,sandbox eks-prod
428+
Provide `limit` to process only the first N matching apps.
407429
"""
408430
try:
409431
apps = get_apps(
@@ -414,6 +436,7 @@ def setcluster(ctx: Any, filter: str, cluster: str, exclude: str = "") -> None:
414436
f" {colourise(cluster, Fore.LIGHTYELLOW_EX)}"
415437
f" {colourise('cluster:', Fore.LIGHTBLUE_EX)}"
416438
),
439+
limit=limit,
417440
)
418441
except AppOperationAborted:
419442
print(success_negative("Aborted."))

gitops/utils/apps.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ def get_apps( # noqa: C901
7373
message: str | None = None,
7474
load_secrets: bool = True,
7575
encode_secrets: bool = True,
76+
limit: int = 0,
7677
) -> list[App]:
7778
"""Return apps that contain ALL of the tags listed in `filter` and NONE of the tags listed in
7879
`exclude`. The incoming filter and exclude params may come in as a list or commastring.
@@ -133,6 +134,9 @@ def get_apps( # noqa: C901
133134

134135
validate_tags(filter | exclude, existing_tags)
135136

137+
if limit: # 0 means no limit
138+
apps = apps[:limit]
139+
136140
if mode in ["PROMPT", "PREVIEW"]:
137141
if mode == "PROMPT" and message is None:
138142
message = "The following apps will be affected:"

0 commit comments

Comments
 (0)