github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/docs/project-layout.md (about)

     1  # GitHub CLI project layout
     2  
     3  At a high level, these areas make up the `github.com/ungtb10d/cli` project:
     4  - [`cmd/`](../cmd) - `main` packages for building binaries such as the `gh` executable
     5  - [`pkg/`](../pkg) - most other packages, including the implementation for individual gh commands
     6  - [`docs/`](../docs) - documentation for maintainers and contributors
     7  - [`script/`](../script) - build and release scripts
     8  - [`internal/`](../internal) - Go packages highly specific to our needs and thus internal
     9  - [`go.mod`](../go.mod) - external Go dependencies for this project, automatically fetched by Go at build time
    10  
    11  Some auxiliary Go packages are at the top level of the project for historical reasons:
    12  - [`api/`](../api) - main utilities for making requests to the GitHub API
    13  - [`context/`](../context) - DEPRECATED: use only for referencing git remotes
    14  - [`git/`](../git) - utilities to gather information from a local git repository
    15  - [`test/`](../test) - DEPRECATED: do not use
    16  - [`utils/`](../utils) - DEPRECATED: use only for printing table output
    17  
    18  ## Command-line help text
    19  
    20  Running `gh help issue list` displays help text for a topic. In this case, the topic is a specific command,
    21  and help text for every command is embedded in that command's source code. The naming convention for gh
    22  commands is:
    23  ```
    24  pkg/cmd/<command>/<subcommand>/<subcommand>.go
    25  ```
    26  Following the above example, the main implementation for the `gh issue list` command, including its help
    27  text, is in [pkg/cmd/issue/list/list.go](../pkg/cmd/issue/list/list.go)
    28  
    29  Other help topics not specific to any command, for example `gh help environment`, are found in
    30  [pkg/cmd/root/help_topic.go](../pkg/cmd/root/help_topic.go).
    31  
    32  During our release process, these help topics are [automatically converted](../cmd/gen-docs/main.go) to
    33  manual pages and published under https://cli.github.com/manual/.
    34  
    35  ## How GitHub CLI works
    36  
    37  To illustrate how GitHub CLI works in its typical mode of operation, let's build the project, run a command,
    38  and talk through which code gets run in order.
    39  
    40  1. `go run script/build.go` - Makes sure all external Go dependencies are fetched, then compiles the
    41     `cmd/gh/main.go` file into a `bin/gh` binary.
    42  2. `bin/gh issue list --limit 5` - Runs the newly built `bin/gh` binary (note: on Windows you must use
    43     backslashes like `bin\gh`) and passes the following arguments to the process: `["issue", "list", "--limit", "5"]`.
    44  3. `func main()` inside `cmd/gh/main.go` is the first Go function that runs. The arguments passed to the
    45     process are available through `os.Args`.
    46  4. The `main` package initializes the "root" command with `root.NewCmdRoot()` and dispatches execution to it
    47     with `rootCmd.ExecuteC()`.
    48  5. The [root command](../pkg/cmd/root/root.go) represents the top-level `gh` command and knows how to
    49     dispatch execution to any other gh command nested under it.
    50  6. Based on `["issue", "list"]` arguments, the execution reaches the `RunE` block of the `cobra.Command`
    51     within [pkg/cmd/issue/list/list.go](../pkg/cmd/issue/list/list.go).
    52  7. The `--limit 5` flag originally passed as arguments be automatically parsed and its value stored as
    53     `opts.LimitResults`.
    54  8. `func listRun()` is called, which is responsible for implementing the logic of the `gh issue list` command.
    55  9. The command collects information from sources like the GitHub API then writes the final output to
    56     standard output and standard error [streams](../pkg/iostreams/iostreams.go) available at `opts.IO`.
    57  10. The program execution is now back at `func main()` of `cmd/gh/main.go`. If there were any Go errors as a
    58      result of processing the command, the function will abort the process with a non-zero exit status.
    59      Otherwise, the process ends with status 0 indicating success.
    60  
    61  ## How to add a new command
    62  
    63  1. First, check on our issue tracker to verify that our team had approved the plans for a new command.
    64  2. Create a package for the new command, e.g. for a new command `gh boom` create the following directory
    65     structure: `pkg/cmd/boom/`
    66  3. The new package should expose a method, e.g. `NewCmdBoom()`, that accepts a `*cmdutil.Factory` type and
    67     returns a `*cobra.Command`.
    68     * Any logic specific to this command should be kept within the command's package and not added to any
    69       "global" packages like `api` or `utils`.
    70  4. Use the method from the previous step to generate the command and add it to the command tree, typically
    71     somewhere in the `NewCmdRoot()` method.
    72  
    73  ## How to write tests
    74  
    75  This task might be tricky. Typically, gh commands do things like look up information from the git repository
    76  in the current directory, query the GitHub API, scan the user's `~/.ssh/config` file, clone or fetch git
    77  repositories, etc. Naturally, none of these things should ever happen for real when running tests, unless
    78  you are sure that any filesystem operations are strictly scoped to a location made for and maintained by the
    79  test itself. To avoid actually running things like making real API requests or shelling out to `git`
    80  commands, we stub them. You should look at how that's done within some existing tests.
    81  
    82  To make your code testable, write small, isolated pieces of functionality that are designed to be composed
    83  together. Prefer table-driven tests for maintaining variations of different test inputs and expectations
    84  when exercising a single piece of functionality.