* Completed List cmd and added API calls * Minor comments and add delete code to pass linting * Typo in descriptions * Minor comments * Validations * Validations-1 * improved branch flag validation * removed build * working after refactory with bad names * Command working, test not working * Corrected creation of service * Finalized structure using service * Deleted tests * cleanup * cleanup * cleanup * removed space with tab * aligned types in model.go * Update model.go * resolved comments * Refactor * removed long descriptions * Working incomplete tests * Completed tests * cleanup * checks * PR comments * PR comments * minor comment issue * minor comment issue * Added test cases for Delete * updated tests to work with workflow * Added missing error condition * Updated tests to support new option service * Improved eror handling for list * Fixed test case * Improved error handling * Error handling and test cases for delete API calls * Added test case for user confirmation delete. * Removed unused import from test * Fixed test case for error scenario * Upgraded go-gh * reusing rest client error * Fix for failing windows test cases * help cmd removed when cache isnt present on delete * Pretty print ratio and space between cols modified * Error handling wrapping * Reverted back error message after silencing help * Bugbash fixes for int limit, zero cache list msg * Test case fixes for error message changes * Handling no cache list scenario with(out) key * Minor Refactoring and avoided code duplication * Removed unused inputs after resolving conflicts * Formatted test file * removed err5xx as they all have same value. * Removed err5xx from list as well * Help and error message enhancements for list. * changing commandname to avoid conflicts * Ran `go fmt` formatter against all .go files * Removed command from root.go * Updated version to 1.0.0 Co-authored-by: t-dedah <t-dedah@github.com> Co-authored-by: Deepak Dahiya <59823596+t-dedah@users.noreply.github.com>
143 lines
4.0 KiB
Go
143 lines
4.0 KiB
Go
package internal
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
"os"
|
|
"os/exec"
|
|
"strconv"
|
|
"strings"
|
|
"unicode/utf8"
|
|
|
|
"github.com/TwiN/go-color"
|
|
"github.com/actions/gh-actions-cache/types"
|
|
gh "github.com/cli/go-gh"
|
|
"github.com/cli/go-gh/pkg/api"
|
|
ghRepo "github.com/cli/go-gh/pkg/repository"
|
|
"github.com/cli/safeexec"
|
|
"github.com/mattn/go-isatty"
|
|
"github.com/nleeper/goment"
|
|
"golang.org/x/term"
|
|
)
|
|
|
|
const MB_IN_BYTES = 1024 * 1024
|
|
const GB_IN_BYTES = 1024 * 1024 * 1024
|
|
const SIZE_COLUMN_WIDTH = 15
|
|
const LAST_ACCESSED_AT_COLUMN_WIDTH = 20
|
|
|
|
func GetRepo(r string) (ghRepo.Repository, error) {
|
|
if r != "" {
|
|
return ghRepo.Parse(r)
|
|
}
|
|
|
|
return gh.CurrentRepository()
|
|
}
|
|
|
|
func FormatCacheSize(size_in_bytes float64) string {
|
|
if size_in_bytes < 1024 {
|
|
return fmt.Sprintf("%.2f B", size_in_bytes)
|
|
}
|
|
|
|
if size_in_bytes < MB_IN_BYTES {
|
|
return fmt.Sprintf("%.2f KB", size_in_bytes/1024)
|
|
}
|
|
|
|
if size_in_bytes < GB_IN_BYTES {
|
|
return fmt.Sprintf("%.2f MB", size_in_bytes/MB_IN_BYTES)
|
|
}
|
|
|
|
return fmt.Sprintf("%.2f GB", size_in_bytes/GB_IN_BYTES)
|
|
}
|
|
|
|
func PrettyPrintCacheList(caches []types.ActionsCache) {
|
|
width, _, _ := getTerminalWidth(os.Stdout)
|
|
width = int(math.Max(float64(width), 100))
|
|
sizeWidth := SIZE_COLUMN_WIDTH // hard-coded size as the content is scoped
|
|
timeWidth := LAST_ACCESSED_AT_COLUMN_WIDTH // hard-coded size as the content is scoped
|
|
keyWidth := int(math.Floor(0.65 * float64(width-SIZE_COLUMN_WIDTH-LAST_ACCESSED_AT_COLUMN_WIDTH)))
|
|
refWidth := int(math.Floor(0.20 * float64(width-SIZE_COLUMN_WIDTH-LAST_ACCESSED_AT_COLUMN_WIDTH)))
|
|
|
|
for _, cache := range caches {
|
|
var formattedRow string = getFormattedCacheInfo(cache, keyWidth, sizeWidth, refWidth, timeWidth)
|
|
fmt.Println(formattedRow)
|
|
}
|
|
}
|
|
|
|
func PrettyPrintTrimmedCacheList(caches []types.ActionsCache) {
|
|
length := len(caches)
|
|
limit := 30
|
|
if length > limit {
|
|
PrettyPrintCacheList(caches[:limit])
|
|
fmt.Printf("... and %d more\n\n", length-limit)
|
|
} else {
|
|
PrettyPrintCacheList(caches[:length])
|
|
}
|
|
fmt.Print("\n")
|
|
}
|
|
|
|
func lastAccessedTime(lastAccessedAt string) string {
|
|
lastAccessed, _ := goment.New(lastAccessedAt)
|
|
return fmt.Sprintf(" %s", lastAccessed.FromNow())
|
|
}
|
|
|
|
func trimOrPad(value string, maxSize int) string {
|
|
if len(value) > maxSize {
|
|
value = value[:maxSize-3] + "..."
|
|
} else {
|
|
value = value + strings.Repeat(" ", maxSize-len(value))
|
|
}
|
|
return value
|
|
}
|
|
|
|
func getFormattedCacheInfo(cache types.ActionsCache, keyWidth int, sizeWidth int, refWidth int, timeWidth int) string {
|
|
key := trimOrPad(cache.Key, keyWidth)
|
|
size := trimOrPad(fmt.Sprintf("[%s]", FormatCacheSize(cache.SizeInBytes)), sizeWidth)
|
|
ref := trimOrPad(cache.Ref, refWidth)
|
|
time := trimOrPad(lastAccessedTime(cache.LastAccessedAt), timeWidth)
|
|
return fmt.Sprintf("%s %s %s %s", key, size, ref, time)
|
|
}
|
|
|
|
func RedTick() string {
|
|
src := "\u2713"
|
|
tick, _ := utf8.DecodeRuneInString(src)
|
|
redTick := color.Colorize(color.Red, string(tick))
|
|
return redTick
|
|
}
|
|
|
|
func PrintSingularOrPlural(count int, singularStr string, pluralStr string) string {
|
|
if count == 1 {
|
|
return fmt.Sprintf("%d %s", count, singularStr)
|
|
}
|
|
return fmt.Sprintf("%d %s", count, pluralStr)
|
|
}
|
|
|
|
func getTerminalWidth(f *os.File) (int, int, error) {
|
|
if !isatty.IsCygwinTerminal(f.Fd()) {
|
|
return term.GetSize(int(f.Fd()))
|
|
}
|
|
tputExe, err := safeexec.LookPath("tput")
|
|
if err != nil {
|
|
return 100, 100, nil
|
|
}
|
|
tputCmd := exec.Command(tputExe, "cols")
|
|
tputCmd.Stdin = os.Stdin
|
|
if out, err := tputCmd.Output(); err == nil {
|
|
if w, err := strconv.Atoi(strings.TrimSpace(string(out))); err == nil {
|
|
return w, 100, nil
|
|
}
|
|
}
|
|
return 100, 100, nil
|
|
}
|
|
|
|
func HttpErrorHandler(err error, errMsg404 string) types.HandledError {
|
|
var httpError api.HTTPError
|
|
if errors.As(err, &httpError) && httpError.StatusCode == 404 {
|
|
return types.HandledError{Message: errMsg404, InnerError: err}
|
|
} else if errors.As(err, &httpError) && httpError.StatusCode >= 400 && httpError.StatusCode < 500 {
|
|
return types.HandledError{Message: httpError.Message, InnerError: err}
|
|
} else {
|
|
return types.HandledError{Message: "We could not process your request due to internal error.", InnerError: err}
|
|
}
|
|
}
|