38 |
Successfully authenticated GitHub CLI
39 |
You may now close this tab and return to the terminal.
40 |
41 |
42 | `
43 |
--------------------------------------------------------------------------------
/internal/browser/browser.go:
--------------------------------------------------------------------------------
1 | package browser
2 |
3 | import (
4 | "io"
5 |
6 | ghBrowser "github.com/cli/go-gh/v2/pkg/browser"
7 | )
8 |
9 | type Browser interface {
10 | Browse(string) error
11 | }
12 |
13 | func New(launcher string, stdout, stderr io.Writer) Browser {
14 | b := ghBrowser.New(launcher, stdout, stderr)
15 | return b
16 | }
17 |
--------------------------------------------------------------------------------
/internal/browser/stub.go:
--------------------------------------------------------------------------------
1 | package browser
2 |
3 | type Stub struct {
4 | urls []string
5 | }
6 |
7 | func (b *Stub) Browse(url string) error {
8 | b.urls = append(b.urls, url)
9 | return nil
10 | }
11 |
12 | func (b *Stub) BrowsedURL() string {
13 | if len(b.urls) > 0 {
14 | return b.urls[0]
15 | }
16 | return ""
17 | }
18 |
19 | type _testing interface {
20 | Errorf(string, ...interface{})
21 | Helper()
22 | }
23 |
24 | func (b *Stub) Verify(t _testing, url string) {
25 | t.Helper()
26 | if url != "" {
27 | switch len(b.urls) {
28 | case 0:
29 | t.Errorf("expected browser to open URL %q, but it was never invoked", url)
30 | case 1:
31 | if url != b.urls[0] {
32 | t.Errorf("expected browser to open URL %q, got %q", url, b.urls[0])
33 | }
34 | default:
35 | t.Errorf("expected browser to open one URL, but was invoked %d times", len(b.urls))
36 | }
37 | } else if len(b.urls) > 0 {
38 | t.Errorf("expected no browser to open, but was invoked %d times: %v", len(b.urls), b.urls)
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/internal/build/build.go:
--------------------------------------------------------------------------------
1 | package build
2 |
3 | import (
4 | "os"
5 | "runtime/debug"
6 | )
7 |
8 | // Version is dynamically set by the toolchain or overridden by the Makefile.
9 | var Version = "DEV"
10 |
11 | // Date is dynamically set at build time in the Makefile.
12 | var Date = "" // YYYY-MM-DD
13 |
14 | func init() {
15 | if Version == "DEV" {
16 | if info, ok := debug.ReadBuildInfo(); ok && info.Main.Version != "(devel)" {
17 | Version = info.Main.Version
18 | }
19 | }
20 |
21 | // Signal the tcell library to skip its expensive `init` block. This saves 30-40ms in startup
22 | // time for the pullpo process. The downside is that some Unicode glyphs from user-generated
23 | // content might cause mis-alignment in tcell-enabled views.
24 | //
25 | // https://github.com/gdamore/tcell/commit/2f889d79bd61b1fd2f43372529975a65b792a7ae
26 | _ = os.Setenv("TCELL_MINIMIZE", "1")
27 | }
28 |
--------------------------------------------------------------------------------
/internal/codespaces/rpc/codespace/codespace_host_service.v1.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | option go_package = "./codespace";
4 |
5 | package Codespaces.Grpc.CodespaceHostService.v1;
6 |
7 | service CodespaceHost {
8 | rpc NotifyCodespaceOfClientActivity (NotifyCodespaceOfClientActivityRequest) returns (NotifyCodespaceOfClientActivityResponse);
9 | rpc RebuildContainerAsync (RebuildContainerRequest) returns (RebuildContainerResponse);
10 | }
11 |
12 | message NotifyCodespaceOfClientActivityRequest {
13 | string ClientId = 1;
14 | repeated string ClientActivities = 2;
15 | }
16 | message NotifyCodespaceOfClientActivityResponse {
17 | bool Result = 1;
18 | string Message = 2;
19 | }
20 |
21 | message RebuildContainerRequest {
22 | optional bool Incremental = 1;
23 | }
24 |
25 | message RebuildContainerResponse {
26 | bool RebuildContainer = 1;
27 | }
28 |
--------------------------------------------------------------------------------
/internal/codespaces/rpc/generate.md:
--------------------------------------------------------------------------------
1 | # Protocol Buffers for Codespaces
2 |
3 | Instructions for generating and adding gRPC protocol buffers.
4 |
5 | ## Generate Protocol Buffers
6 |
7 | 1. [Download `protoc`](https://grpc.io/docs/protoc-installation/)
8 | 2. [Download protocol compiler plugins for Go](https://grpc.io/docs/languages/go/quickstart/)
9 | 3. Install moq: `go install github.com/matryer/moq@latest`
10 | 4. Run `./generate.sh` from the `internal/codespaces/rpc` directory
11 |
12 | ## Add New Protocol Buffers
13 |
14 | 1. Download a `.proto` contract from the service repo
15 | 2. Create a new directory and copy the `.proto` to it
16 | 3. Update `generate.sh` to include the include the new `.proto`
17 | 4. Follow the instructions to [Generate Protocol Buffers](#generate-protocol-buffers)
18 |
--------------------------------------------------------------------------------
/internal/codespaces/rpc/generate.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | if ! protoc --version; then
6 | echo 'ERROR: protoc is not on your PATH'
7 | exit 1
8 | fi
9 | if ! protoc-gen-go --version; then
10 | echo 'ERROR: protoc-gen-go is not on your PATH'
11 | exit 1
12 | fi
13 | if ! protoc-gen-go-grpc --version; then
14 | echo 'ERROR: protoc-gen-go-grpc is not on your PATH'
15 | fi
16 |
17 | function generate {
18 | local dir="$1"
19 | local proto="$2"
20 |
21 | local contract="$dir/$proto"
22 |
23 | protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative "$contract" --experimental_allow_proto3_optional
24 | echo "Generated protocol buffers for $contract"
25 |
26 | services=$(grep -Eo "service .+ {" <$contract | awk '{print $2 "Server"}')
27 | moq -out "$contract.mock.go" "$dir" "$services"
28 | echo "Generated mock protocols for $contract"
29 | }
30 |
31 | generate jupyter jupyter_server_host_service.v1.proto
32 | generate codespace codespace_host_service.v1.proto
33 | generate ssh ssh_server_host_service.v1.proto
34 |
35 | echo 'Done!'
36 |
--------------------------------------------------------------------------------
/internal/codespaces/rpc/jupyter/jupyter_server_host_service.v1.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | option go_package = "./jupyter";
4 |
5 | package Codespaces.Grpc.JupyterServerHostService.v1;
6 |
7 | service JupyterServerHost {
8 | rpc GetRunningServer (GetRunningServerRequest) returns (GetRunningServerResponse);
9 | }
10 |
11 | message GetRunningServerRequest {
12 | }
13 |
14 | message GetRunningServerResponse {
15 | bool Result = 1;
16 | string Message = 2;
17 | string Port = 3;
18 | string ServerUrl = 4;
19 | }
20 |
--------------------------------------------------------------------------------
/internal/codespaces/rpc/ssh/ssh_server_host_service.v1.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | option go_package = "./ssh";
4 |
5 | package Codespaces.Grpc.SshServerHostService.v1;
6 |
7 | service SshServerHost {
8 | rpc StartRemoteServerAsync (StartRemoteServerRequest) returns (StartRemoteServerResponse);
9 | }
10 |
11 | message StartRemoteServerRequest {
12 | string UserPublicKey = 1;
13 | }
14 |
15 | message StartRemoteServerResponse {
16 | bool Result = 1;
17 | string ServerPort = 2;
18 | string User = 3;
19 | string Message = 4;
20 | }
21 |
--------------------------------------------------------------------------------
/internal/featuredetection/detector_mock.go:
--------------------------------------------------------------------------------
1 | package featuredetection
2 |
3 | type DisabledDetectorMock struct{}
4 |
5 | func (md *DisabledDetectorMock) IssueFeatures() (IssueFeatures, error) {
6 | return IssueFeatures{}, nil
7 | }
8 |
9 | func (md *DisabledDetectorMock) PullRequestFeatures() (PullRequestFeatures, error) {
10 | return PullRequestFeatures{}, nil
11 | }
12 |
13 | func (md *DisabledDetectorMock) RepositoryFeatures() (RepositoryFeatures, error) {
14 | return RepositoryFeatures{}, nil
15 | }
16 |
17 | type EnabledDetectorMock struct{}
18 |
19 | func (md *EnabledDetectorMock) IssueFeatures() (IssueFeatures, error) {
20 | return allIssueFeatures, nil
21 | }
22 |
23 | func (md *EnabledDetectorMock) PullRequestFeatures() (PullRequestFeatures, error) {
24 | return allPullRequestFeatures, nil
25 | }
26 |
27 | func (md *EnabledDetectorMock) RepositoryFeatures() (RepositoryFeatures, error) {
28 | return allRepositoryFeatures, nil
29 | }
30 |
--------------------------------------------------------------------------------
/internal/keyring/keyring.go:
--------------------------------------------------------------------------------
1 | // Package keyring is a simple wrapper that adds timeouts to the zalando/go-keyring package.
2 | package keyring
3 |
4 | import (
5 | "time"
6 |
7 | "github.com/zalando/go-keyring"
8 | )
9 |
10 | type TimeoutError struct {
11 | message string
12 | }
13 |
14 | func (e *TimeoutError) Error() string {
15 | return e.message
16 | }
17 |
18 | // Set secret in keyring for user.
19 | func Set(service, user, secret string) error {
20 | ch := make(chan error, 1)
21 | go func() {
22 | defer close(ch)
23 | ch <- keyring.Set(service, user, secret)
24 | }()
25 | select {
26 | case err := <-ch:
27 | return err
28 | case <-time.After(3 * time.Second):
29 | return &TimeoutError{"timeout while trying to set secret in keyring"}
30 | }
31 | }
32 |
33 | // Get secret from keyring given service and user name.
34 | func Get(service, user string) (string, error) {
35 | ch := make(chan struct {
36 | val string
37 | err error
38 | }, 1)
39 | go func() {
40 | defer close(ch)
41 | val, err := keyring.Get(service, user)
42 | ch <- struct {
43 | val string
44 | err error
45 | }{val, err}
46 | }()
47 | select {
48 | case res := <-ch:
49 | return res.val, res.err
50 | case <-time.After(3 * time.Second):
51 | return "", &TimeoutError{"timeout while trying to get secret from keyring"}
52 | }
53 | }
54 |
55 | // Delete secret from keyring.
56 | func Delete(service, user string) error {
57 | ch := make(chan error, 1)
58 | go func() {
59 | defer close(ch)
60 | ch <- keyring.Delete(service, user)
61 | }()
62 | select {
63 | case err := <-ch:
64 | return err
65 | case <-time.After(3 * time.Second):
66 | return &TimeoutError{"timeout while trying to delete secret from keyring"}
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/internal/text/text_test.go:
--------------------------------------------------------------------------------
1 | package text
2 |
3 | import (
4 | "testing"
5 | "time"
6 |
7 | "github.com/stretchr/testify/assert"
8 | )
9 |
10 | func TestRemoveExcessiveWhitespace(t *testing.T) {
11 | tests := []struct {
12 | name string
13 | input string
14 | want string
15 | }{
16 | {
17 | name: "nothing to remove",
18 | input: "one two three",
19 | want: "one two three",
20 | },
21 | {
22 | name: "whitespace b-gone",
23 | input: "\n one\n\t two three\r\n ",
24 | want: "one two three",
25 | },
26 | }
27 | for _, tt := range tests {
28 | t.Run(tt.name, func(t *testing.T) {
29 | got := RemoveExcessiveWhitespace(tt.input)
30 | assert.Equal(t, tt.want, got)
31 | })
32 | }
33 | }
34 |
35 | func TestFuzzyAgoAbbr(t *testing.T) {
36 | const form = "2006-Jan-02 15:04:05"
37 | now, _ := time.Parse(form, "2020-Nov-22 14:00:00")
38 | cases := map[string]string{
39 | "2020-Nov-22 14:00:00": "0m",
40 | "2020-Nov-22 13:59:00": "1m",
41 | "2020-Nov-22 13:30:00": "30m",
42 | "2020-Nov-22 13:00:00": "1h",
43 | "2020-Nov-22 02:00:00": "12h",
44 | "2020-Nov-21 14:00:00": "1d",
45 | "2020-Nov-07 14:00:00": "15d",
46 | "2020-Oct-24 14:00:00": "29d",
47 | "2020-Oct-23 14:00:00": "Oct 23, 2020",
48 | "2019-Nov-22 14:00:00": "Nov 22, 2019",
49 | }
50 | for createdAt, expected := range cases {
51 | d, err := time.Parse(form, createdAt)
52 | assert.NoError(t, err)
53 | fuzzy := FuzzyAgoAbbr(now, d)
54 | assert.Equal(t, expected, fuzzy)
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/pkg/cmd/alias/alias.go:
--------------------------------------------------------------------------------
1 | package alias
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc"
5 | deleteCmd "github.com/cli/cli/v2/pkg/cmd/alias/delete"
6 | importCmd "github.com/cli/cli/v2/pkg/cmd/alias/imports"
7 | listCmd "github.com/cli/cli/v2/pkg/cmd/alias/list"
8 | setCmd "github.com/cli/cli/v2/pkg/cmd/alias/set"
9 | "github.com/cli/cli/v2/pkg/cmdutil"
10 | "github.com/spf13/cobra"
11 | )
12 |
13 | func NewCmdAlias(f *cmdutil.Factory) *cobra.Command {
14 | cmd := &cobra.Command{
15 | Use: "alias