Merge remote-tracking branch 'origin' into json-format

This commit is contained in:
Mislav Marohnić 2021-04-13 20:31:11 +02:00
commit 298ef8add5
7 changed files with 131 additions and 32 deletions

View file

@ -1,6 +1,6 @@
name: PR Automation
on:
pull_request:
pull_request_target:
types: [ready_for_review, opened, reopened]
jobs:
pr-auto:

View file

@ -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),
Hidden: true,
Run: func(cmd *cobra.Command, args []string) {
actionsRun(opts)
@ -34,28 +34,45 @@ func NewCmdActions(f *cmdutil.Factory) *cobra.Command {
return cmd
}
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 cs != nil {
header = cs.Bold(header)
runHeader = cs.Bold(runHeader)
workflowHeader = cs.Bold(workflowHeader)
}
return heredoc.Docf(`
%s
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 download: Download artifacts generated by runs
To see more help, run 'gh help run <subcommand>'
%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
To see more help, run 'gh help workflow <subcommand>'
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)
}
func actionsRun(opts ActionsOptions) {
cs := opts.IO.ColorScheme()
fmt.Fprint(opts.IO.Out, heredoc.Docf(`
Welcome to GitHub Actions on the command line.
This part of gh is in beta and subject to change!
To follow along while we get to GA, please see this
tracking issue: https://github.com/cli/cli/issues/2889
%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
%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
`,
cs.Bold("Interacting with workflow runs"),
cs.Bold("Interacting with workflow files")))
fmt.Fprintln(opts.IO.Out, actionsExplainer(cs))
}

View file

@ -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
}

View file

@ -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)
}

View file

@ -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")
}

View file

@ -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)
}
}
}

View file

@ -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
@ -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 {
@ -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 {