From 185f6242b957f1167adc183f9c7ee7e414fdd479 Mon Sep 17 00:00:00 2001 From: vilmibm Date: Mon, 12 Apr 2021 13:58:40 -0500 Subject: [PATCH 01/11] make gh actions output Real --- pkg/cmd/actions/actions.go | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/pkg/cmd/actions/actions.go b/pkg/cmd/actions/actions.go index f0e3509a4..ac5a4208b 100644 --- a/pkg/cmd/actions/actions.go +++ b/pkg/cmd/actions/actions.go @@ -21,7 +21,7 @@ func NewCmdActions(f *cmdutil.Factory) *cobra.Command { cmd := &cobra.Command{ Use: "actions", Short: "Learn about working with GitHub actions", - Args: cobra.ExactArgs(0), + Long: actionsExplainer(nil, false), Hidden: true, Run: func(cmd *cobra.Command, args []string) { actionsRun(opts) @@ -34,15 +34,20 @@ func NewCmdActions(f *cmdutil.Factory) *cobra.Command { return cmd } -func actionsRun(opts ActionsOptions) { - cs := opts.IO.ColorScheme() - fmt.Fprint(opts.IO.Out, heredoc.Docf(` - Welcome to GitHub Actions on the command line. +func actionsExplainer(cs *iostreams.ColorScheme, color bool) string { + header := "Welcome to GitHub Actions on the command line." + runHeader := "Interacting with workflow runs" + workflowHeader := "Interacting with workflow files" + if color { + header = cs.Bold(header) + runHeader = cs.Bold(runHeader) + workflowHeader = cs.Bold(workflowHeader) + } - This part of gh is in beta and subject to change! + return fmt.Sprintf(heredoc.Docf(` + %s - To follow along while we get to GA, please see this - tracking issue: https://github.com/cli/cli/issues/2889 + gh integrates with Actions to help you manage runs and workflows. %s gh run list: List recent workflow runs @@ -50,12 +55,23 @@ func actionsRun(opts ActionsOptions) { gh run watch: Watch a workflow run while it executes gh run rerun: Rerun a failed workflow run + To see more help, run 'gh help run ' + %s gh workflow list: List all the workflow files in your repository gh workflow enable: Enable a workflow file gh workflow disable: Disable a workflow file gh workflow run: Trigger a workflow_dispatch run for a workflow file + + To see more help, run 'gh help workflow ' + + For more in depth help including examples, see online documentation at: + https://docs.github.com/en/actions/guides/managing-github-actions-with-github-cli `, - cs.Bold("Interacting with workflow runs"), - cs.Bold("Interacting with workflow files"))) + header, runHeader, workflowHeader)) +} + +func actionsRun(opts ActionsOptions) { + cs := opts.IO.ColorScheme() + fmt.Fprintf(opts.IO.Out, actionsExplainer(cs, true)) } From cb03a3a7761e092abf96a16f05c70347c76d9abc Mon Sep 17 00:00:00 2001 From: vilmibm Date: Mon, 12 Apr 2021 14:17:05 -0500 Subject: [PATCH 02/11] linter appeasement --- pkg/cmd/actions/actions.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pkg/cmd/actions/actions.go b/pkg/cmd/actions/actions.go index ac5a4208b..40c93f1ae 100644 --- a/pkg/cmd/actions/actions.go +++ b/pkg/cmd/actions/actions.go @@ -44,7 +44,7 @@ func actionsExplainer(cs *iostreams.ColorScheme, color bool) string { workflowHeader = cs.Bold(workflowHeader) } - return fmt.Sprintf(heredoc.Docf(` + return heredoc.Docf(` %s gh integrates with Actions to help you manage runs and workflows. @@ -67,11 +67,10 @@ func actionsExplainer(cs *iostreams.ColorScheme, color bool) string { For more in depth help including examples, see online documentation at: https://docs.github.com/en/actions/guides/managing-github-actions-with-github-cli - `, - header, runHeader, workflowHeader)) + `, header, runHeader, workflowHeader) } func actionsRun(opts ActionsOptions) { cs := opts.IO.ColorScheme() - fmt.Fprintf(opts.IO.Out, actionsExplainer(cs, true)) + fmt.Fprintln(opts.IO.Out, actionsExplainer(cs, true)) } From 9a0193b77c21f72e7ce4f0d1e34b46558eafe0a3 Mon Sep 17 00:00:00 2001 From: vilmibm Date: Mon, 12 Apr 2021 14:28:58 -0500 Subject: [PATCH 03/11] add gh run download --- pkg/cmd/actions/actions.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/pkg/cmd/actions/actions.go b/pkg/cmd/actions/actions.go index 40c93f1ae..b93e1171a 100644 --- a/pkg/cmd/actions/actions.go +++ b/pkg/cmd/actions/actions.go @@ -50,18 +50,19 @@ func actionsExplainer(cs *iostreams.ColorScheme, color bool) string { gh integrates with Actions to help you manage runs and workflows. %s - gh run list: List recent workflow runs - gh run view: View details for a workflow run or one of its jobs - gh run watch: Watch a workflow run while it executes - gh run rerun: Rerun a failed workflow run + gh run list: List recent workflow runs + gh run view: View details for a workflow run or one of its jobs + gh run watch: Watch a workflow run while it executes + gh run rerun: Rerun a failed workflow run + gh run download: Download artifacts generated by runs To see more help, run 'gh help run ' %s - gh workflow list: List all the workflow files in your repository - gh workflow enable: Enable a workflow file - gh workflow disable: Disable a workflow file - gh workflow run: Trigger a workflow_dispatch run for a workflow file + gh workflow list: List all the workflow files in your repository + gh workflow enable: Enable a workflow file + gh workflow disable: Disable a workflow file + gh workflow run: Trigger a workflow_dispatch run for a workflow file To see more help, run 'gh help workflow ' From 2b17de80f67b4a6cdbfdd2825ed810c4aa0475c4 Mon Sep 17 00:00:00 2001 From: vilmibm Date: Mon, 12 Apr 2021 14:38:45 -0500 Subject: [PATCH 04/11] workflow view --- pkg/cmd/actions/actions.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/cmd/actions/actions.go b/pkg/cmd/actions/actions.go index b93e1171a..f53cdf3ff 100644 --- a/pkg/cmd/actions/actions.go +++ b/pkg/cmd/actions/actions.go @@ -60,6 +60,7 @@ func actionsExplainer(cs *iostreams.ColorScheme, color bool) string { %s gh workflow list: List all the workflow files in your repository + gh workflow view: View details for a workflow file gh workflow enable: Enable a workflow file gh workflow disable: Disable a workflow file gh workflow run: Trigger a workflow_dispatch run for a workflow file From c724070cbe92b806e5cfdc6125d880d1f399d09a Mon Sep 17 00:00:00 2001 From: vilmibm Date: Mon, 12 Apr 2021 15:51:00 -0500 Subject: [PATCH 05/11] just check nil --- pkg/cmd/actions/actions.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/cmd/actions/actions.go b/pkg/cmd/actions/actions.go index f53cdf3ff..5be2df305 100644 --- a/pkg/cmd/actions/actions.go +++ b/pkg/cmd/actions/actions.go @@ -21,7 +21,7 @@ func NewCmdActions(f *cmdutil.Factory) *cobra.Command { cmd := &cobra.Command{ Use: "actions", Short: "Learn about working with GitHub actions", - Long: actionsExplainer(nil, false), + Long: actionsExplainer(nil), Hidden: true, Run: func(cmd *cobra.Command, args []string) { actionsRun(opts) @@ -34,11 +34,11 @@ func NewCmdActions(f *cmdutil.Factory) *cobra.Command { return cmd } -func actionsExplainer(cs *iostreams.ColorScheme, color bool) string { +func actionsExplainer(cs *iostreams.ColorScheme) string { header := "Welcome to GitHub Actions on the command line." runHeader := "Interacting with workflow runs" workflowHeader := "Interacting with workflow files" - if color { + if cs != nil { header = cs.Bold(header) runHeader = cs.Bold(runHeader) workflowHeader = cs.Bold(workflowHeader) @@ -74,5 +74,5 @@ func actionsExplainer(cs *iostreams.ColorScheme, color bool) string { func actionsRun(opts ActionsOptions) { cs := opts.IO.ColorScheme() - fmt.Fprintln(opts.IO.Out, actionsExplainer(cs, true)) + fmt.Fprintln(opts.IO.Out, actionsExplainer(cs)) } From 0b79471024a804bc0ee3510e370db7d97f205886 Mon Sep 17 00:00:00 2001 From: Sarah Edwards Date: Mon, 12 Apr 2021 15:16:12 -0700 Subject: [PATCH 06/11] Use `--exit-status` instead of `-e` in example --- pkg/cmd/run/view/view.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cmd/run/view/view.go b/pkg/cmd/run/view/view.go index d4d0db1f6..010b17154 100644 --- a/pkg/cmd/run/view/view.go +++ b/pkg/cmd/run/view/view.go @@ -105,7 +105,7 @@ func NewCmdView(f *cmdutil.Factory, runF func(*ViewOptions) error) *cobra.Comman $ gh run view --log --job 456789 # Exit non-zero if a run failed - $ gh run view 0451 -e && echo "run pending or passed" + $ gh run view 0451 --exit-status && echo "run pending or passed" `), RunE: func(cmd *cobra.Command, args []string) error { // support `-R, --repo` override From 3f3c8f2b26cd51952d7d8e8ab809d59e4cf18d79 Mon Sep 17 00:00:00 2001 From: Sam Coe Date: Mon, 12 Apr 2021 15:22:30 -0700 Subject: [PATCH 07/11] Add time since run to run selection survey options --- pkg/cmd/run/shared/shared.go | 14 +++++++++++++- pkg/cmd/run/shared/shared_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 pkg/cmd/run/shared/shared_test.go diff --git a/pkg/cmd/run/shared/shared.go b/pkg/cmd/run/shared/shared.go index 90a16071d..9ca0c48f8 100644 --- a/pkg/cmd/run/shared/shared.go +++ b/pkg/cmd/run/shared/shared.go @@ -257,6 +257,7 @@ func GetJobs(client *api.Client, repo ghrepo.Interface, run Run) ([]Job, error) func PromptForRun(cs *iostreams.ColorScheme, runs []Run) (string, error) { var selected int + now := time.Now() candidates := []string{} @@ -264,7 +265,7 @@ func PromptForRun(cs *iostreams.ColorScheme, runs []Run) (string, error) { symbol, _ := Symbol(cs, run.Status, run.Conclusion) candidates = append(candidates, // TODO truncate commit message, long ones look terrible - fmt.Sprintf("%s %s, %s (%s)", symbol, run.CommitMsg(), run.Name, run.HeadBranch)) + fmt.Sprintf("%s %s, %s (%s) %s", symbol, run.CommitMsg(), run.Name, run.HeadBranch, preciseAgo(now, run.CreatedAt))) } // TODO consider custom filter so it's fuzzier. right now matches start anywhere in string but @@ -380,3 +381,14 @@ func PullRequestForRun(client *api.Client, repo ghrepo.Interface, run Run) (int, return number, nil } + +func preciseAgo(now time.Time, createdAt time.Time) string { + ago := now.Sub(createdAt) + + if ago < 30*24*time.Hour { + s := ago.Truncate(time.Second).String() + return fmt.Sprintf("%s ago", s) + } + + return createdAt.Format("Jan _2, 2006") +} diff --git a/pkg/cmd/run/shared/shared_test.go b/pkg/cmd/run/shared/shared_test.go new file mode 100644 index 000000000..bd6e73962 --- /dev/null +++ b/pkg/cmd/run/shared/shared_test.go @@ -0,0 +1,31 @@ +package shared + +import ( + "testing" + "time" +) + +func TestPreciseAgo(t *testing.T) { + const form = "2006-Jan-02 15:04:05" + now, _ := time.Parse(form, "2021-Apr-12 14:00:00") + + cases := map[string]string{ + "2021-Apr-12 14:00:00": "0s ago", + "2021-Apr-12 13:59:30": "30s ago", + "2021-Apr-12 13:59:00": "1m0s ago", + "2021-Apr-12 13:30:15": "29m45s ago", + "2021-Apr-12 13:00:00": "1h0m0s ago", + "2021-Apr-12 02:30:45": "11h29m15s ago", + "2021-Apr-11 14:00:00": "24h0m0s ago", + "2021-Apr-01 14:00:00": "264h0m0s ago", + "2021-Mar-12 14:00:00": "Mar 12, 2021", + } + + for createdAt, expected := range cases { + d, _ := time.Parse(form, createdAt) + got := preciseAgo(now, d) + if got != expected { + t.Errorf("expected %s but got %s for %s", expected, got, createdAt) + } + } +} From 1ddebf6396deb724d88f9f3971b4012fd8d70898 Mon Sep 17 00:00:00 2001 From: Sam Coe Date: Mon, 12 Apr 2021 16:03:50 -0700 Subject: [PATCH 08/11] Fix trying to read from non-existent log file --- pkg/cmd/run/view/view.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/cmd/run/view/view.go b/pkg/cmd/run/view/view.go index d4d0db1f6..01fc8b3ab 100644 --- a/pkg/cmd/run/view/view.go +++ b/pkg/cmd/run/view/view.go @@ -498,6 +498,9 @@ func displayRunLog(io *iostreams.IOStreams, jobs []shared.Job, failed bool) erro if failed && !shared.IsFailureState(step.Conclusion) { continue } + if step.Log == nil { + continue + } prefix := fmt.Sprintf("%s\t%s\t", job.Name, step.Name) f, err := step.Log.Open() if err != nil { From 4dd8a44ff152c8bb891d6a0cf5ed582c117015af Mon Sep 17 00:00:00 2001 From: Sam Coe Date: Mon, 12 Apr 2021 16:27:51 -0700 Subject: [PATCH 09/11] make run log cache key unique --- pkg/cmd/run/view/view.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/cmd/run/view/view.go b/pkg/cmd/run/view/view.go index 01fc8b3ab..cd7f54e6e 100644 --- a/pkg/cmd/run/view/view.go +++ b/pkg/cmd/run/view/view.go @@ -256,7 +256,7 @@ func runView(opts *ViewOptions) error { } opts.IO.StartProgressIndicator() - runLogZip, err := getRunLog(opts.RunLogCache, httpClient, repo, run.ID) + runLogZip, err := getRunLog(opts.RunLogCache, httpClient, repo, run) opts.IO.StopProgressIndicator() if err != nil { return fmt.Errorf("failed to get run log: %w", err) @@ -408,13 +408,13 @@ func getLog(httpClient *http.Client, logURL string) (io.ReadCloser, error) { return resp.Body, nil } -func getRunLog(cache runLogCache, httpClient *http.Client, repo ghrepo.Interface, runID int) (*zip.ReadCloser, error) { - filename := fmt.Sprintf("run-log-%d.zip", runID) +func getRunLog(cache runLogCache, httpClient *http.Client, repo ghrepo.Interface, run *shared.Run) (*zip.ReadCloser, error) { + filename := fmt.Sprintf("run-log-%d-%d.zip", run.ID, run.CreatedAt.Unix()) filepath := filepath.Join(os.TempDir(), "gh-cli-cache", filename) if !cache.Exists(filepath) { // Run log does not exist in cache so retrieve and store it logURL := fmt.Sprintf("%srepos/%s/actions/runs/%d/logs", - ghinstance.RESTPrefix(repo.RepoHost()), ghrepo.FullName(repo), runID) + ghinstance.RESTPrefix(repo.RepoHost()), ghrepo.FullName(repo), run.ID) resp, err := getLog(httpClient, logURL) if err != nil { From 93b5bf20ebf5b8d4e7cc5f917190d5b92527750e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Tue, 13 Apr 2021 11:10:13 +0200 Subject: [PATCH 10/11] Fix secrets in PR automation being available to PR from forks --- .github/workflows/prauto.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prauto.yml b/.github/workflows/prauto.yml index 08d41a99f..20a88b31e 100644 --- a/.github/workflows/prauto.yml +++ b/.github/workflows/prauto.yml @@ -1,6 +1,6 @@ name: PR Automation on: - pull_request: + pull_request_target: types: [ready_for_review, opened, reopened] jobs: pr-auto: From f8c7fd1d2834011bc72ace326477f78d881f349a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Tue, 13 Apr 2021 19:15:14 +0200 Subject: [PATCH 11/11] Fix extracting workflow artifact to a relative path To prevent zipslip, we verify that each extracted file would fall strictly under the prefix of the path to extract to. However, this yielded a false positive when extracting to `.`, which is the default for downloading a single archive. --- pkg/cmd/run/download/zip.go | 8 ++++++-- pkg/cmd/run/download/zip_test.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 pkg/cmd/run/download/zip_test.go diff --git a/pkg/cmd/run/download/zip.go b/pkg/cmd/run/download/zip.go index e3959a091..d7a2613bc 100644 --- a/pkg/cmd/run/download/zip.go +++ b/pkg/cmd/run/download/zip.go @@ -16,9 +16,13 @@ const ( ) func extractZip(zr *zip.Reader, destDir string) error { - pathPrefix := filepath.Clean(destDir) + string(filepath.Separator) + destDirAbs, err := filepath.Abs(destDir) + if err != nil { + return err + } + pathPrefix := destDirAbs + string(filepath.Separator) for _, zf := range zr.File { - fpath := filepath.Join(destDir, filepath.FromSlash(zf.Name)) + fpath := filepath.Join(destDirAbs, filepath.FromSlash(zf.Name)) if !strings.HasPrefix(fpath, pathPrefix) { continue } diff --git a/pkg/cmd/run/download/zip_test.go b/pkg/cmd/run/download/zip_test.go new file mode 100644 index 000000000..f859511ee --- /dev/null +++ b/pkg/cmd/run/download/zip_test.go @@ -0,0 +1,32 @@ +package download + +import ( + "archive/zip" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" +) + +func Test_extractZip(t *testing.T) { + tmpDir := t.TempDir() + wd, err := os.Getwd() + require.NoError(t, err) + t.Cleanup(func() { _ = os.Chdir(wd) }) + + zipFile, err := zip.OpenReader("./fixtures/myproject.zip") + require.NoError(t, err) + defer zipFile.Close() + + extractPath := filepath.Join(tmpDir, "artifact") + err = os.MkdirAll(extractPath, 0700) + require.NoError(t, err) + require.NoError(t, os.Chdir(extractPath)) + + err = extractZip(&zipFile.Reader, ".") + require.NoError(t, err) + + _, err = os.Stat(filepath.Join("src", "main.go")) + require.NoError(t, err) +}