Merge remote-tracking branch 'origin' into json-format
This commit is contained in:
commit
298ef8add5
7 changed files with 131 additions and 32 deletions
2
.github/workflows/prauto.yml
vendored
2
.github/workflows/prauto.yml
vendored
|
|
@ -1,6 +1,6 @@
|
|||
name: PR Automation
|
||||
on:
|
||||
pull_request:
|
||||
pull_request_target:
|
||||
types: [ready_for_review, opened, reopened]
|
||||
jobs:
|
||||
pr-auto:
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
32
pkg/cmd/run/download/zip_test.go
Normal file
32
pkg/cmd/run/download/zip_test.go
Normal 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)
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
|
|
|
|||
31
pkg/cmd/run/shared/shared_test.go
Normal file
31
pkg/cmd/run/shared/shared_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue