Skip to content

Commit b1cbbcb

Browse files
Add --show-all flag to repo/package list
1 parent a671af6 commit b1cbbcb

File tree

6 files changed

+134
-38
lines changed

6 files changed

+134
-38
lines changed

.github/workflows/release.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ jobs:
1818
run: |
1919
python -m pip install --upgrade pip
2020
pip install shiv
21+
pip install cloudsmith-cli
2122
- name: Get version
2223
id: get_version
2324
run: echo "VERSION=$(cat cloudsmith_cli/data/VERSION)" >> $GITHUB_ENV
@@ -43,3 +44,10 @@ jobs:
4344
asset_path: ./cloudsmith-${{ env.VERSION }}.pyz
4445
asset_name: cloudsmith-${{ env.VERSION }}.pyz
4546
asset_content_type: application/zip
47+
- name: Rename asses to cloudsmith.pyz
48+
run: mv ./cloudsmith-${{ env.VERSION }}.pyz cloudsmith.pyz
49+
- name: Upload to Cloudsmith
50+
run: cloudsmith push raw ${{ vars.CLOUDSMITH_NAMESPACE }}/${{ vars.CLOUDSMITH_REPO }} cloudsmith.pyz --name "cloudsmith-cli" --version ${{ env.VERSION }}
51+
env:
52+
CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }}
53+
VERSION: ${{ env.VERSION }}

CONTRIBUTING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ For most purposes, you probably just want `pip install -r requirements.txt`.
2222

2323
Our [direnv config](./.envrc) file codifies the development environment setup which we use internally.
2424

25+
Run `pre-commit run -a black` to format the code.
2526

2627
## Coding Conventions
2728

cloudsmith_cli/cli/commands/list_.py

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ def entitlements_(*args, **kwargs): # pylint: disable=missing-docstring
128128
help=("A boolean-like search term for querying package attributes."),
129129
)
130130
@click.pass_context
131-
def packages(ctx, opts, owner_repo, page, page_size, query):
131+
def packages(ctx, opts, owner_repo, page, page_size, show_all, query):
132132
"""
133133
List packages for a repository.
134134
@@ -165,20 +165,48 @@ def packages(ctx, opts, owner_repo, page, page_size, query):
165165
166166
--query 'name:^foo$ filename:.zip$ architecture:~x86'
167167
168+
Options:
169+
-p, --page INTEGER The page of results to show.
170+
-l, --page-size INTEGER The number of results to return per page.
171+
--show-all Show all results by automatically paginating through all pages.
172+
-q, --query TEXT A boolean-like search term for querying package attributes.
168173
"""
169174
owner, repo = owner_repo
170175

176+
if show_all:
177+
if page is not None or page_size is not None:
178+
raise click.UsageError(
179+
"--show-all cannot be used with --page or --page-size"
180+
)
181+
else:
182+
if page is None and page_size is None:
183+
show_all = True
184+
171185
# Use stderr for messages if the output is something else (e.g. # JSON)
172186
use_stderr = opts.output != "pretty"
173187

174-
click.echo("Getting list of packages ... ", nl=False, err=use_stderr)
188+
if show_all:
189+
click.echo(
190+
f"Retrieving all packages for repository {owner}/{repo}. This may take a while...",
191+
err=use_stderr,
192+
)
193+
else:
194+
click.echo("Getting list of packages ... ", nl=False, err=use_stderr)
175195

176196
context_msg = "Failed to get list of packages!"
177197
with handle_api_exceptions(ctx, opts=opts, context_msg=context_msg):
178198
with maybe_spinner(opts):
179-
packages_, page_info = list_packages(
180-
owner=owner, repo=repo, page=page, page_size=page_size, query=query
181-
)
199+
if show_all:
200+
packages_, page_info = utils.get_all_pages(
201+
list_packages,
202+
owner=owner,
203+
repo=repo,
204+
query=query,
205+
)
206+
else:
207+
packages_, page_info = list_packages(
208+
owner=owner, repo=repo, page=page, page_size=page_size, query=query
209+
)
182210

183211
click.secho("OK", fg="green", err=use_stderr)
184212

@@ -211,7 +239,10 @@ def packages(ctx, opts, owner_repo, page, page_size, query):
211239
num_results = len(packages_)
212240
list_suffix = "package%s visible" % ("s" if num_results != 1 else "")
213241
utils.pretty_print_list_info(
214-
num_results=num_results, page_info=page_info, suffix=list_suffix
242+
num_results=num_results,
243+
page_info=page_info,
244+
suffix=list_suffix,
245+
show_all=show_all,
215246
)
216247

217248

@@ -229,7 +260,7 @@ def packages(ctx, opts, owner_repo, page, page_size, query):
229260
required=False,
230261
)
231262
@click.pass_context
232-
def repos(ctx, opts, owner_repo, page, page_size):
263+
def repos(ctx, opts, owner_repo, page, page_size, show_all):
233264
"""
234265
List repositories for a namespace (owner).
235266

cloudsmith_cli/cli/commands/repos.py

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from .main import main
1313

1414

15-
def print_repositories(opts, data, page_info=None, show_list_info=True):
15+
def print_repositories(opts, data, page_info=None, show_list_info=True, show_all=False):
1616
"""Print repositories as a table or output in another format."""
1717
headers = [
1818
"Name",
@@ -51,7 +51,10 @@ def print_repositories(opts, data, page_info=None, show_list_info=True):
5151
num_results = len(data)
5252
list_suffix = "repositor%s visible" % ("ies" if num_results != 1 else "y")
5353
utils.pretty_print_list_info(
54-
num_results=num_results, page_info=page_info, suffix=list_suffix
54+
num_results=num_results,
55+
page_info=page_info,
56+
suffix=list_suffix,
57+
show_all=show_all,
5558
)
5659

5760

@@ -83,7 +86,7 @@ def repositories(ctx, opts): # pylink: disable=unused-argument
8386
required=False,
8487
)
8588
@click.pass_context
86-
def get(ctx, opts, owner_repo, page, page_size):
89+
def get(ctx, opts, owner_repo, page, page_size, show_all):
8790
"""
8891
List repositories for a namespace (owner).
8992
@@ -95,12 +98,23 @@ def get(ctx, opts, owner_repo, page, page_size):
9598
9699
If OWNER isn't specified it'll default to the currently authenticated user
97100
(if any). If you're unauthenticated, no results will be returned.
101+
102+
Options:
103+
-p, --page INTEGER The page of results to show.
104+
-l, --page-size INTEGER The number of results to return per page.
105+
--show-all Show all results by automatically paginating through all pages.
98106
"""
107+
if show_all:
108+
if page is not None or page_size is not None:
109+
raise click.UsageError(
110+
"--show-all cannot be used with --page or --page-size"
111+
)
112+
elif page is None and page_size is None:
113+
show_all = True
114+
99115
# Use stderr for messages if the output is something else (e.g. # JSON)
100116
use_stderr = opts.output != "pretty"
101117

102-
click.echo("Getting list of repositories ... ", nl=False, err=use_stderr)
103-
104118
if isinstance(owner_repo, list):
105119
if len(owner_repo) == 1:
106120
owner = owner_repo[0]
@@ -109,26 +123,49 @@ def get(ctx, opts, owner_repo, page, page_size):
109123
owner, repo = owner_repo
110124
if isinstance(owner_repo, str):
111125
repo = None
112-
113126
if owner_repo:
114127
owner = owner_repo
115128
else:
116129
owner = None
117130

131+
if show_all:
132+
if owner:
133+
click.echo(
134+
f"Retrieving all repositories for {owner}. This may take a while...",
135+
err=use_stderr,
136+
)
137+
else:
138+
click.echo(
139+
"Retrieving all repositories. This may take a while...", err=use_stderr
140+
)
141+
else:
142+
click.echo("Getting list of repositories ... ", nl=False, err=use_stderr)
143+
118144
context_msg = "Failed to get list of repositories!"
119145
with handle_api_exceptions(ctx, opts=opts, context_msg=context_msg):
120146
with maybe_spinner(opts):
121-
repos_, page_info = api.list_repos(
122-
owner=owner, repo=repo, page=page, page_size=page_size
123-
)
147+
if show_all:
148+
repos_, page_info = utils.get_all_pages(
149+
api.list_repos,
150+
owner=owner,
151+
repo=repo,
152+
)
153+
else:
154+
repos_, page_info = api.list_repos(
155+
owner=owner, repo=repo, page=page, page_size=page_size
156+
)
124157

125158
click.secho("OK", fg="green", err=use_stderr)
126159

127160
if utils.maybe_print_as_json(opts, repos_, page_info):
128161
return
129162

130163
print_repositories(
131-
opts=opts, data=repos_, show_list_info=False, page_info=page_info
164+
opts=opts,
165+
data=repos_,
166+
show_list_info=False,
167+
page_info=page_info,
168+
show_all=show_all,
132169
)
133170

134171

cloudsmith_cli/cli/decorators.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -142,31 +142,33 @@ def wrapper(ctx, *args, **kwargs):
142142

143143

144144
def common_cli_list_options(f):
145-
"""Add common list options to commands."""
145+
"""Add common options for list commands."""
146146

147147
@click.option(
148148
"-p",
149149
"--page",
150150
default=1,
151-
type=int,
152-
help="The page to view for lists, where 1 is the first page",
151+
help="The page of results to show.",
152+
type=click.INT,
153153
callback=validators.validate_page,
154154
)
155155
@click.option(
156156
"-l",
157157
"--page-size",
158-
default=30,
159-
type=int,
160-
help="The amount of items to view per page for lists.",
158+
default=None,
159+
help="The number of results to return per page.",
160+
type=click.INT,
161161
callback=validators.validate_page_size,
162162
)
163-
@click.pass_context
163+
@click.option(
164+
"--show-all",
165+
is_flag=True,
166+
default=False,
167+
help="Show all results by automatically paginating through all pages.",
168+
)
164169
@functools.wraps(f)
165-
def wrapper(ctx, *args, **kwargs):
166-
# pylint: disable=missing-docstring
167-
opts = config.get_or_create_options(ctx)
168-
kwargs["opts"] = opts
169-
return ctx.invoke(f, *args, **kwargs)
170+
def wrapper(*args, **kwargs):
171+
return f(*args, **kwargs)
170172

171173
return wrapper
172174

cloudsmith_cli/cli/utils.py

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,24 @@ def make_user_agent(prefix=None):
1919
return f"cloudsmith-cli/{prefix} cli:{get_cli_version()} api:{get_api_version()}"
2020

2121

22-
def pretty_print_list_info(num_results, page_info=None, suffix=None):
22+
def pretty_print_list_info(num_results, page_info=None, suffix=None, show_all=False):
2323
"""Pretty print list info, with pagination, for user display."""
2424
num_results_fg = "green" if num_results else "red"
2525
num_results_text = click.style(str(num_results), fg=num_results_fg)
2626

2727
if page_info and page_info.is_valid:
28-
page_range = page_info.calculate_range(num_results)
29-
page_info_text = f"page: {click.style(str(page_info.page), bold=True)}/{click.style(str(page_info.page_total), bold=True)}, page size: {click.style(str(page_info.page_size), bold=True)}"
30-
range_results_text = "%(from)s-%(to)s (%(num_results)s) of %(total)s" % {
31-
"num_results": num_results_text,
32-
"from": click.style(str(page_range[0]), fg=num_results_fg),
33-
"to": click.style(str(page_range[1]), fg=num_results_fg),
34-
"total": click.style(str(page_info.count), fg=num_results_fg),
35-
}
28+
if show_all:
29+
range_results_text = f"{num_results_text} of {click.style(str(page_info.count), fg=num_results_fg)}"
30+
page_info_text = "all pages"
31+
else:
32+
page_range = page_info.calculate_range(num_results)
33+
page_info_text = f"page: {click.style(str(page_info.page), bold=True)}/{click.style(str(page_info.page_total), bold=True)}, page size: {click.style(str(page_info.page_size), bold=True)}"
34+
range_results_text = "%(from)s-%(to)s (%(num_results)s) of %(total)s" % {
35+
"num_results": num_results_text,
36+
"from": click.style(str(page_range[0]), fg=num_results_fg),
37+
"to": click.style(str(page_range[1]), fg=num_results_fg),
38+
"total": click.style(str(page_info.count), fg=num_results_fg),
39+
}
3640
else:
3741
page_info_text = ""
3842
range_results_text = num_results_text
@@ -192,3 +196,16 @@ def maybe_spinner(opts):
192196
else:
193197
with spinner() as spin:
194198
yield spin
199+
200+
201+
def get_all_pages(func, **kwargs):
202+
"""Retrieve all pages for a paginated API call."""
203+
all_items = []
204+
page = 1
205+
while True:
206+
items, page_info = func(page=page, **kwargs)
207+
all_items.extend(items)
208+
if page >= page_info.page_total:
209+
break
210+
page += 1
211+
return all_items, page_info

0 commit comments

Comments
 (0)