Moar memory
This commit is contained in:
parent
4a610f13cf
commit
8a4a8dd451
3 changed files with 90 additions and 41 deletions
|
|
@ -1,6 +1,7 @@
|
|||
package shared
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
|
@ -93,7 +94,7 @@ type Step struct {
|
|||
Status Status
|
||||
Conclusion Conclusion
|
||||
Number int
|
||||
Log string
|
||||
Log *zip.File
|
||||
}
|
||||
|
||||
type Steps []Step
|
||||
|
|
|
|||
|
|
@ -3,15 +3,15 @@ package view
|
|||
import (
|
||||
"archive/zip"
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
|
|
@ -31,11 +31,39 @@ type browser interface {
|
|||
Browse(string) error
|
||||
}
|
||||
|
||||
type runLogCache interface {
|
||||
Exists(string) bool
|
||||
Create(string, io.ReadCloser) error
|
||||
Open(string) (*zip.ReadCloser, error)
|
||||
}
|
||||
|
||||
type rlc struct{}
|
||||
|
||||
func (rlc) Exists(path string) bool {
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
func (rlc) Create(path string, content io.ReadCloser) error {
|
||||
out, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
_, err = io.Copy(out, content)
|
||||
return err
|
||||
}
|
||||
func (rlc) Open(path string) (*zip.ReadCloser, error) {
|
||||
return zip.OpenReader(path)
|
||||
}
|
||||
|
||||
type ViewOptions struct {
|
||||
HttpClient func() (*http.Client, error)
|
||||
IO *iostreams.IOStreams
|
||||
BaseRepo func() (ghrepo.Interface, error)
|
||||
Browser browser
|
||||
HttpClient func() (*http.Client, error)
|
||||
IO *iostreams.IOStreams
|
||||
BaseRepo func() (ghrepo.Interface, error)
|
||||
Browser browser
|
||||
RunLogCache runLogCache
|
||||
|
||||
RunID string
|
||||
JobID string
|
||||
|
|
@ -52,10 +80,11 @@ type ViewOptions struct {
|
|||
|
||||
func NewCmdView(f *cmdutil.Factory, runF func(*ViewOptions) error) *cobra.Command {
|
||||
opts := &ViewOptions{
|
||||
IO: f.IOStreams,
|
||||
HttpClient: f.HttpClient,
|
||||
Now: time.Now,
|
||||
Browser: f.Browser,
|
||||
IO: f.IOStreams,
|
||||
HttpClient: f.HttpClient,
|
||||
Now: time.Now,
|
||||
Browser: f.Browser,
|
||||
RunLogCache: rlc{},
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
|
@ -228,16 +257,14 @@ func runView(opts *ViewOptions) error {
|
|||
}
|
||||
|
||||
opts.IO.StartProgressIndicator()
|
||||
runLogZip, err := getRunLog(httpClient, repo, run.ID)
|
||||
runLogZip, err := getRunLog(opts.RunLogCache, httpClient, repo, run.ID)
|
||||
defer runLogZip.Close()
|
||||
opts.IO.StopProgressIndicator()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get run log: %w", err)
|
||||
}
|
||||
|
||||
err = readRunLog(runLogZip, jobs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
attachRunLog(runLogZip, jobs)
|
||||
|
||||
return displayRunLog(opts.IO, jobs, opts.LogFailed)
|
||||
}
|
||||
|
|
@ -382,10 +409,27 @@ func getLog(httpClient *http.Client, logURL string) (io.ReadCloser, error) {
|
|||
return resp.Body, nil
|
||||
}
|
||||
|
||||
func getRunLog(httpClient *http.Client, repo ghrepo.Interface, runID int) (io.ReadCloser, error) {
|
||||
logURL := fmt.Sprintf("%srepos/%s/actions/runs/%d/logs",
|
||||
ghinstance.RESTPrefix(repo.RepoHost()), ghrepo.FullName(repo), runID)
|
||||
return getLog(httpClient, logURL)
|
||||
func getRunLog(cache runLogCache, httpClient *http.Client, repo ghrepo.Interface, runID int) (*zip.ReadCloser, error) {
|
||||
filename := fmt.Sprintf("run-log-%d.zip", runID)
|
||||
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)
|
||||
|
||||
resp, err := getLog(httpClient, logURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Close()
|
||||
|
||||
err = cache.Create(filepath, resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return cache.Open(filepath)
|
||||
}
|
||||
|
||||
func promptForJob(cs *iostreams.ColorScheme, jobs []shared.Job) (*shared.Job, error) {
|
||||
|
|
@ -427,35 +471,18 @@ func promptForJob(cs *iostreams.ColorScheme, jobs []shared.Job) (*shared.Job, er
|
|||
// It iterates through the list of jobs and trys to find the matching
|
||||
// log in the zip file. If the matching log is found it is attached
|
||||
// to the job.
|
||||
func readRunLog(rlz io.ReadCloser, jobs []shared.Job) error {
|
||||
defer rlz.Close()
|
||||
z, err := ioutil.ReadAll(rlz)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
zipReader, err := zip.NewReader(bytes.NewReader(z), int64(len(z)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
func attachRunLog(rlz *zip.ReadCloser, jobs []shared.Job) {
|
||||
for i, job := range jobs {
|
||||
for j, step := range job.Steps {
|
||||
filename := fmt.Sprintf("%s/%d_%s.txt", job.Name, step.Number, step.Name)
|
||||
for _, file := range zipReader.File {
|
||||
for _, file := range rlz.File {
|
||||
if file.Name == filename {
|
||||
log, err := readZipFile(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
jobs[i].Steps[j].Log = string(log)
|
||||
jobs[i].Steps[j].Log = file
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func readZipFile(zf *zip.File) ([]byte, error) {
|
||||
|
|
@ -482,10 +509,15 @@ func displayRunLog(io *iostreams.IOStreams, jobs []shared.Job, failed bool) erro
|
|||
continue
|
||||
}
|
||||
prefix := fmt.Sprintf("%s\t%s\t", job.Name, step.Name)
|
||||
scanner := bufio.NewScanner(strings.NewReader(step.Log))
|
||||
f, err := step.Log.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
fmt.Fprintf(io.Out, "%s%s\n", prefix, scanner.Text())
|
||||
}
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
package view
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
|
@ -798,6 +800,8 @@ func TestViewRun(t *testing.T) {
|
|||
|
||||
browser := &cmdutil.TestBrowser{}
|
||||
tt.opts.Browser = browser
|
||||
rlc := testRunLogCache{}
|
||||
tt.opts.RunLogCache = rlc
|
||||
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := runView(tt.opts)
|
||||
|
|
@ -822,6 +826,18 @@ func TestViewRun(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
type testRunLogCache struct{}
|
||||
|
||||
func (testRunLogCache) Exists(path string) bool {
|
||||
return false
|
||||
}
|
||||
func (testRunLogCache) Create(path string, content io.ReadCloser) error {
|
||||
return nil
|
||||
}
|
||||
func (testRunLogCache) Open(path string) (*zip.ReadCloser, error) {
|
||||
return zip.OpenReader("./fixtures/run_log.zip")
|
||||
}
|
||||
|
||||
var barfTheFobLogOutput = heredoc.Doc(`
|
||||
cool job barz the fob log line 1
|
||||
cool job barz the fob log line 2
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue