Skip to content

Commit e8d7ae6

Browse files
committed
cmd/internal/pkgsite-cli: move the design memo to the package dir
This change will make the memo shown in pkgsite. Also apply minor polishing of help message and source code documentation. Change-Id: Ia26e705171aea9d649e3469ad14ba256ba044ebc Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/781241 Reviewed-by: Jonathan Amsterdam <jba@google.com> LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Ethan Lee <ethanalee@google.com> kokoro-CI: kokoro <noreply+kokoro@google.com>
1 parent 6edc7aa commit e8d7ae6

7 files changed

Lines changed: 149 additions & 96 deletions

File tree

cmd/internal/pkgsite-cli/README.md

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# pkgsite-cli
2+
3+
A command-line interface for querying [pkg.go.dev](https://pkg.go.dev/).
4+
5+
Currently, the API is on `v1beta`, but we expect to move to `v1` soon.
6+
7+
Related to issue [76718](https://go.dev/issue/76718).
8+
9+
## Quick start
10+
11+
To install the `pkgsite-cli` tool, run:
12+
13+
```bash
14+
go install golang.org/x/pkgsite/cmd/internal/pkgsite-cli@latest
15+
```
16+
17+
## Motivation
18+
19+
The [pkg.go.dev](https://pkg.go.dev/) service provides an API interface at
20+
https://pkg.go.dev/api to allow querying information about published Go
21+
packages and modules. The API uses a stateless, GET-only architecture designed
22+
for stability and efficient caching. `pkgsite-cli` is a lightweight CLI that
23+
uses this API. There is no official SDK for the API, but this tool serves as a
24+
reference client implementation that developers can use in other projects. See
25+
the [API spec](https://pkg.go.dev/api) and the
26+
[OpenAPI specification](https://pkg.go.dev/v1beta/openapi.yaml).
27+
28+
## Relationship to existing tools
29+
30+
- **`go doc`** renders documentation for packages available locally.
31+
`pkgsite-cli` does not replace it for reading local documentation.
32+
- **`cmd/pkgsite`** is a web server that serves documentation for packages
33+
available locally. It does not provide full version listings, vulnerability
34+
reports, reverse dependencies, licenses, or search capabilities (yet).
35+
- **`pkgsite-cli`** provides access to information that `go doc` or a local
36+
instance of `cmd/pkgsite` cannot reach: version listings, vulnerability
37+
reports, reverse dependencies, licenses, documentation of modules/packages,
38+
and search results for packages not yet downloaded.
39+
40+
Rule of thumb: Use `go doc` for local code; use `pkgsite-cli` for package
41+
discovery and metadata lookup.
42+
43+
## Commands
44+
45+
Run `pkgsite-cli <command> -h` for details on available flags for each command.
46+
47+
Available commands:
48+
* `package`
49+
* `module`
50+
* `search`
51+
52+
Additional commands will be added in the future.
53+
54+
## Usage Examples
55+
56+
### Search for packages:
57+
58+
```bash
59+
pkgsite-cli search uuid
60+
```
61+
62+
### Inspect a specific package:
63+
64+
```bash
65+
pkgsite-cli package github.com/google/go-cmp/cmp
66+
```
67+
68+
### See reverse dependencies for a package:
69+
70+
```bash
71+
pkgsite-cli package -imported-by github.com/google/go-cmp/cmp
72+
```
73+
74+
### List exported symbols declared by a package:
75+
76+
```bash
77+
pkgsite-cli package -symbols github.com/google/go-cmp/cmp
78+
```
79+
80+
### List versions of a module:
81+
82+
```bash
83+
pkgsite-cli module -versions github.com/google/go-cmp
84+
```
85+
86+
### List both versions and packages belonging to a module:
87+
88+
```bash
89+
pkgsite-cli module -packages -versions github.com/google/go-cmp
90+
```
91+
92+
## Details
93+
- **Ambiguous paths**: Unlike `go mod tidy` or the
94+
[pkg.go.dev](https://pkg.go.dev) web interface, which use the "longest
95+
module path" rule to resolve ambiguous package paths, the API requires the
96+
module to be specified unambiguously. If a package path is ambiguous
97+
because it exists in multiple modules, the API returns a list of candidates
98+
and reports an error. Use the `-module` flag to specify the correct
99+
module path.
100+
101+
102+
## Status and Implementation
103+
- **Experimental**: This tool is currently a prototype.
104+
- **Minimal Dependencies**: To facilitate potential migration to other
105+
repositories (e.g., `x/tools`), the tool depends only on the Go standard
106+
library.
107+
- **Duplicate Types**: API request and response types are duplicated in the
108+
tool's source instead of imported from `pkgsite` for now. We ruled out
109+
releasing a full SDK because the REST API is simple enough to consume
110+
directly. This keeps the tool self-contained. However, if this tool remains
111+
in this repository, we can eliminate this duplication by using the internal
112+
package.
113+

cmd/internal/pkgsite-cli/client/client.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5+
// Package client provides a client for the pkg.go.dev v1beta API.
56
package client
67

78
//go:generate go test -run=TestTypesUpToDate -update
@@ -166,6 +167,7 @@ type PackageOptions struct {
166167
GOARCH string
167168
}
168169

170+
// GetPackage fetches package information for the given path and version.
169171
func (c *Client) GetPackage(ctx context.Context, path, version string, opts PackageOptions) (*Package, error) {
170172
q := make(url.Values)
171173
if version != "" {
@@ -216,6 +218,7 @@ type SymbolsOptions struct {
216218
PaginationOptions
217219
}
218220

221+
// GetSymbols fetches symbols for the given package path and version.
219222
func (c *Client) GetSymbols(ctx context.Context, path, version string, opts SymbolsOptions) (*PaginatedResponse[Symbol], error) {
220223
q := make(url.Values)
221224
if version != "" {
@@ -251,6 +254,7 @@ type ImportedByOptions struct {
251254
PaginationOptions
252255
}
253256

257+
// GetImportedBy fetches packages that import the given package path and version.
254258
func (c *Client) GetImportedBy(ctx context.Context, path, version string, opts ImportedByOptions) (*PackageImportedBy, error) {
255259
q := make(url.Values)
256260
if version != "" {
@@ -280,6 +284,7 @@ type ModuleOptions struct {
280284
Licenses bool
281285
}
282286

287+
// GetModule fetches module information for the given path and version.
283288
func (c *Client) GetModule(ctx context.Context, path, version string, opts ModuleOptions) (*Module, error) {
284289
q := make(url.Values)
285290
if version != "" {
@@ -305,6 +310,7 @@ type VersionResponse struct {
305310
Version string `json:"version"`
306311
}
307312

313+
// GetVersions fetches a list of versions for the given module path.
308314
func (c *Client) GetVersions(ctx context.Context, path string, opts PaginationOptions) (*PaginatedResponse[VersionResponse], error) {
309315
q := make(url.Values)
310316
if opts.Limit > 0 {
@@ -322,6 +328,7 @@ func (c *Client) GetVersions(ctx context.Context, path string, opts PaginationOp
322328
return &resp, nil
323329
}
324330

331+
// GetVulns fetches a list of vulnerabilities for the given module path and version.
325332
func (c *Client) GetVulns(ctx context.Context, path, version string, opts PaginationOptions) (*PaginatedResponse[Vulnerability], error) {
326333
q := make(url.Values)
327334
if version != "" {
@@ -348,6 +355,7 @@ type ModulePackageResponse struct {
348355
Synopsis string `json:"synopsis"`
349356
}
350357

358+
// GetPackages fetches a list of packages for the given module path and version.
351359
func (c *Client) GetPackages(ctx context.Context, modulePath, version string, opts PaginationOptions) (*PaginatedResponse[ModulePackageResponse], error) {
352360
q := make(url.Values)
353361
if version != "" {
@@ -385,6 +393,7 @@ type SearchOptions struct {
385393
PaginationOptions
386394
}
387395

396+
// Search queries the pkg.go.dev API for packages matching the given query.
388397
func (c *Client) Search(ctx context.Context, query string, opts SearchOptions) (*PaginatedResponse[SearchResult], error) {
389398
q := make(url.Values)
390399
q.Set("q", query)

cmd/internal/pkgsite-cli/command.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,13 @@ func printCommandUsage(w io.Writer, c *command) {
5353

5454
// printUsage writes usage for all commands to w.
5555
func printUsage(w io.Writer, cmds []*command) {
56+
fmt.Fprintf(w, "%s queries the pkg.go.dev API for information about Go packages and modules.\n\n", filepath.Base(os.Args[0]))
5657
fmt.Fprintln(w, "Usage:")
5758
for _, c := range cmds {
5859
line := c.usageLine()
5960
fmt.Fprintf(w, " %-50s %s\n", line, c.summary)
6061
}
61-
fmt.Fprintf(w, "\nRun \"%s <command> -h\" for command-specific flags.\n", filepath.Base(os.Args[0]))
62+
fmt.Fprintf(w, "\nRun \"%s <command> -h\" for details on available flags for each command.\n", filepath.Base(os.Args[0]))
6263
}
6364

6465
// dispatch finds and runs the matching command. It returns the exit code.

cmd/internal/pkgsite-cli/main.go

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
// Command pkgsite-cli queries the pkg.go.dev API for information about
66
// Go packages and modules.
77
//
8-
// Usage:
8+
// For more information, see https://go.dev/blog/pkgsite-api.
99
//
10-
// pkgsite-cli package <package>[@version] [flags] package information
11-
// pkgsite-cli module <module>[@version] [flags] module information
12-
// pkgsite-cli search <query> [flags] search for packages
10+
// Usage:
1311
//
14-
// See doc/pkgsite-cli.md for the full design document.
12+
// pkgsite-cli package <package>[@version] [flags] Show package details.
13+
// pkgsite-cli module <module>[@version] [flags] Show module details.
14+
// pkgsite-cli search <query> [flags] Search for packages.
1515
package main
1616

1717
import (
@@ -57,7 +57,10 @@ func commands() []*command {
5757

5858
pkgRun := func(fs *flag.FlagSet, stdout, stderr io.Writer) int { return runPackage(fs, &pf, stdout, stderr) }
5959

60-
const packageDoc = `
60+
const packageDoc = `Queries information about a specific Go package from pkg.go.dev.
61+
By default, this prints basic metadata. Use flags to request additional
62+
information such as exported symbols, reverse dependencies, or rendered documentation.
63+
6164
When using -json, the output is a JSON object with the following structure:
6265
6366
type packageResult struct {
@@ -88,7 +91,10 @@ When using -json, the output is a JSON object with the following structure:
8891
}
8992
`
9093

91-
const moduleDoc = `
94+
const moduleDoc = `Queries information about a specific Go module from pkg.go.dev.
95+
By default, this prints basic metadata. Use flags to request additional
96+
information such as versions, vulnerabilities, or packages contained in the module.
97+
9298
When using -json, the output is a JSON object with the following structure:
9399
94100
type moduleResult struct {
@@ -113,7 +119,9 @@ When using -json, the output is a JSON object with the following structure:
113119
}
114120
`
115121

116-
const searchDoc = `
122+
const searchDoc = `Searches for Go packages on pkg.go.dev matching the given query.
123+
By default, this prints a list of matching packages with their synopsis.
124+
117125
When using -json, the output is a JSON object with the following structure:
118126
119127
type PaginatedResponse[SearchResult] struct {
@@ -135,35 +143,35 @@ When using -json, the output is a JSON object with the following structure:
135143
{
136144
name: "package",
137145
args: "<package>[@version]",
138-
summary: "package information",
146+
summary: "Show package details",
139147
description: strings.TrimSpace(packageDoc),
140148
flags: pkgFS,
141149
run: pkgRun,
142150
},
143151
{
144152
name: "module",
145153
args: "<module>[@version]",
146-
summary: "module information",
154+
summary: "Show module details",
147155
description: strings.TrimSpace(moduleDoc),
148156
flags: modFS,
149157
run: func(fs *flag.FlagSet, stdout, stderr io.Writer) int { return runModule(fs, &mf, stdout, stderr) },
150158
},
151159
{
152160
name: "search",
153161
args: "<query>",
154-
summary: "search for packages",
162+
summary: "Search for packages",
155163
description: strings.TrimSpace(searchDoc),
156164
flags: searchFS,
157165
run: func(fs *flag.FlagSet, stdout, stderr io.Writer) int { return runSearch(fs, &sf, stdout, stderr) },
158166
},
159167
{
160168
name: "help",
161-
summary: "show this help message",
169+
summary: "Show this help message",
162170
run: func(_ *flag.FlagSet, stdout, _ io.Writer) int { printUsage(stdout, cmds); return 0 },
163171
},
164172
{
165173
name: "version",
166-
summary: "print version information",
174+
summary: "Print version information",
167175
run: func(_ *flag.FlagSet, stdout, _ io.Writer) int { fmt.Fprintln(stdout, versionInfo()); return 0 },
168176
},
169177
}

cmd/internal/pkgsite-cli/module.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package main
77
import (
88
"context"
99
"flag"
10+
"fmt"
1011
"io"
1112

1213
"golang.org/x/pkgsite/cmd/internal/pkgsite-cli/client"
@@ -15,6 +16,7 @@ import (
1516

1617
func runModule(fs *flag.FlagSet, m *moduleFlags, stdout, stderr io.Writer) int {
1718
if fs.NArg() != 1 {
19+
fmt.Fprintf(stderr, "Error: expected exactly 1 module argument, got %d\n", fs.NArg())
1820
fs.Usage()
1921
return 2
2022
}

cmd/internal/pkgsite-cli/search.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package main
77
import (
88
"context"
99
"flag"
10+
"fmt"
1011
"io"
1112
"strings"
1213

@@ -15,6 +16,7 @@ import (
1516

1617
func runSearch(fs *flag.FlagSet, s *searchFlags, stdout, stderr io.Writer) int {
1718
if fs.NArg() < 1 {
19+
fmt.Fprintln(stderr, "Error: expected at least 1 search query argument")
1820
fs.Usage()
1921
return 2
2022
}

0 commit comments

Comments
 (0)