E2E List cmd and added API calls (#3)

* 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

* Minor comments and Readme
This commit is contained in:
Deepak Dahiya
2022-06-22 15:23:55 +05:30
committed by GitHub
parent 60b70e6320
commit 08af75f371
7 changed files with 257 additions and 17 deletions

3
.gitignore vendored
View File

@@ -1 +1,2 @@
.DS_Store
.DS_Store
gh-actions-cache

View File

@@ -1 +1,28 @@
# gh-actions-cache
# gh-actions-cache
## Local Development
1. Build the extension using
go build
2. Install the extension
gh extension install <filepath-to-build>
If you are already in the same directory use this `gh extension install .`
3. Run the command
gh actions-cache <command> [Flags]
## Troubleshooting
1. `symlink /Users/.../gh-actions-cache /Users/.../share/gh/extensions/gh-actions-cache: file exists`
Uninstall the current version of extension using
gh extension remove gh-actions-cache

88
cmd/client.go Normal file
View File

@@ -0,0 +1,88 @@
package cmd
import (
"fmt"
"log"
"net/url"
gh "github.com/cli/go-gh"
"github.com/cli/go-gh/pkg/api"
ghRepo "github.com/cli/go-gh/pkg/repository"
)
type cacheInfo struct {
Key string
Ref string
LastAccessedAt string
Size float64
}
func getCacheUsage(repo ghRepo.Repository) float64 {
client, err := getRestClient(repo)
if err != nil {
log.Fatal(err)
}
pathComponent := fmt.Sprintf("repos/%s/%s/actions/cache/usage", repo.Owner(), repo.Name())
var apiResults map[string]interface{}
err = client.Get(pathComponent, &apiResults)
if err != nil {
log.Fatal(err)
}
cacheSizeResult := apiResults["active_caches_size_in_bytes"].(float64)
return cacheSizeResult
}
func listCaches(repo ghRepo.Repository, queryParams url.Values) []cacheInfo {
client, err := getRestClient(repo)
if err != nil {
log.Fatal(err)
}
pathComponent := fmt.Sprintf("repos/%s/%s/actions/caches", repo.Owner(), repo.Name())
var apiResults map[string]interface{}
err = client.Get(pathComponent+"?"+queryParams.Encode(), &apiResults)
if err != nil {
log.Fatal(err)
}
actionsCachesResult := apiResults["actions_caches"].([]interface{})
var caches []cacheInfo
for _, item := range actionsCachesResult {
caches = append(caches, cacheInfo{
Key: item.(map[string]interface{})["key"].(string),
Ref: item.(map[string]interface{})["ref"].(string),
LastAccessedAt: item.(map[string]interface{})["last_accessed_at"].(string),
Size: item.(map[string]interface{})["size_in_bytes"].(float64),
})
}
return caches
}
func deleteCaches(repo ghRepo.Repository, queryParams url.Values) float64 {
client, err := getRestClient(repo)
if err != nil {
log.Fatal(err)
}
pathComponent := fmt.Sprintf("repos/%s/%s/actions/caches", repo.Owner(), repo.Name())
var apiResults map[string]interface{}
err = client.Delete(pathComponent+"?"+queryParams.Encode(), &apiResults)
if err != nil {
log.Fatal(err)
}
totalDeletedCachesResult := apiResults["total_count"].(float64)
return totalDeletedCachesResult
}
func getRestClient(repo ghRepo.Repository) (api.RESTClient, error) {
opts := api.ClientOptions{
Host: repo.Host(),
Headers: map[string]string{"User-Agent": fmt.Sprintf("gh-actions-cache/%s/%s", VERSION, COMMAND) },
}
client, err := gh.RESTClient(&opts)
if err != nil {
return nil, err
}
return client, nil
}

View File

@@ -1,7 +1,7 @@
package cmd
import (
"fmt"
"log"
"github.com/spf13/cobra"
)
@@ -18,11 +18,17 @@ var deleteCmd = &cobra.Command{
Short: "Delete cache by key",
Long: `Delete cache by key`,
Run: func(cmd *cobra.Command, args []string) {
repo, _ := cmd.Flags().GetString("repo")
COMMAND = "delete"
r, _ := cmd.Flags().GetString("repo")
branch, _ := cmd.Flags().GetString("branch")
fmt.Println("DELETE")
fmt.Println(repo)
fmt.Println(branch)
repo, err := getRepo(r)
if err != nil {
log.Fatal(err)
}
queryParams := generateQueryParams(branch, 30, "", "", "")
deleteCaches(repo, queryParams)
},
}

View File

@@ -2,6 +2,7 @@ package cmd
import (
"fmt"
"log"
"github.com/spf13/cobra"
)
@@ -10,9 +11,10 @@ func init() {
rootCmd.AddCommand(listCmd)
listCmd.Flags().StringP("repo", "R", "", "Select another repository for finding actions cache.")
listCmd.Flags().StringP("branch", "B", "", "Filter by branch")
listCmd.Flags().IntP("limit", "", 30, "Maximum number of items to fetch (default is 30, max limit is 100)")
listCmd.Flags().StringP("key", "", "", "Filter by key")
listCmd.Flags().StringP("order", "", "", "Order of caches returned (asc/desc)")
listCmd.Flags().StringP("sort", "", "", "Sort fetched caches (used/size/created)")
listCmd.Flags().StringP("sort", "", "", "Sort fetched caches (last-used/size/created-at)")
listCmd.SetHelpTemplate(getListHelp())
}
@@ -21,20 +23,64 @@ var listCmd = &cobra.Command{
Short: "Lists the actions cache",
Long: `Lists the actions cache`,
Run: func(cmd *cobra.Command, args []string) {
repo, _ := cmd.Flags().GetString("repo")
COMMAND = "list"
if len(args) != 0 {
fmt.Printf("Invalid argument(s). Expected 0 received %d\n", len(args))
fmt.Println(getListHelp())
return
}
r, _ := cmd.Flags().GetString("repo")
branch, _ := cmd.Flags().GetString("branch")
limit, _ := cmd.Flags().GetInt("limit")
key, _ := cmd.Flags().GetString("key")
order, _ := cmd.Flags().GetString("order")
sort, _ := cmd.Flags().GetString("sort")
fmt.Println("LIST")
fmt.Println(repo)
fmt.Println(branch)
fmt.Println(key)
fmt.Println(order)
fmt.Println(sort)
repo, err := getRepo(r)
if err != nil {
log.Fatal(err)
}
validateInputs(sort, order, limit)
if branch == "" && key == "" {
totalCacheSize := getCacheUsage(repo)
fmt.Printf("Total caches size %s\n\n", formatCacheSize(totalCacheSize))
}
queryParams := generateQueryParams(branch, limit, key, order, sort)
caches := listCaches(repo, queryParams)
fmt.Printf("Showing %d of %d cache entries in %s/%s\n\n", displayedEntriesCount(len(caches), limit), len(caches), repo.Owner(), repo.Name())
for _, cache := range caches {
fmt.Printf("%s\t [%s]\t %s\t %s\n", cache.Key, formatCacheSize(cache.Size), cache.Ref, cache.LastAccessedAt)
}
},
}
func displayedEntriesCount(totalCaches int, limit int) int {
if totalCaches < limit {
return totalCaches
}
return limit
}
func validateInputs(sort string, order string, limit int){
if order != "" && order != "asc" && order != "desc"{
log.Fatal(fmt.Errorf(fmt.Sprintf("%s is not a valid value for order flag. Allowed values: asc/desc", order)))
}
if sort != "" && sort != "last-used" && sort != "size" && sort != "created-at"{
log.Fatal(fmt.Errorf(fmt.Sprintf("%s is not a valid value for sort flag. Allowed values: last-used/size/created-at", sort)))
}
if limit < 1{
log.Fatal(fmt.Errorf(fmt.Sprintf("%d is not a valid value for limit flag. Allowed values: > 1", limit)))
}
}
func getListHelp() string {
return `
gh-actions-cache: Works with GitHub Actions Cache.
@@ -48,9 +94,10 @@ ARGUMENTS:
FLAGS:
-R, --repo <[HOST/]owner/repo> Select another repository using the [HOST/]OWNER/REPO format
-B, --branch <string> Filter by branch
-L, --limit <int> Maximum number of items to fetch (default is 30, max limit is 100)
--key <string> Filter by key
--order <string> Order of caches returned (asc/desc)
--sort <string> Sort fetched caches (used/size/created)
--sort <string> Sort fetched caches (last-used/size/created-at)
INHERITED FLAGS
--help Show help for command

View File

@@ -6,11 +6,13 @@ import (
"github.com/spf13/cobra"
)
const VERSION = "0.0.1"
var COMMAND string = ""
var rootCmd = &cobra.Command{
Use: "gh-actions-cache",
Short: "Works with GitHub Actions Cache. ",
Long: `Works with GitHub Actions Cache.`,
// Run: func(cmd *cobra.Command, args []string) {},
}
func Execute() {

69
cmd/utils.go Normal file
View File

@@ -0,0 +1,69 @@
package cmd
import (
"fmt"
"net/url"
"strconv"
"strings"
gh "github.com/cli/go-gh"
ghRepo "github.com/cli/go-gh/pkg/repository"
)
const MB_IN_BYTES = 1024 * 1024
const GB_IN_BYTES = 1024 * 1024 * 1024
var SORT_INPUT_TO_QUERY_MAP = map[string]string{
"created-at": "created_at",
"last-used": "last_accessed_at",
"size": "size_in_bytes",
}
func generateQueryParams(branch string, limit int, key string, order string, sort string) url.Values {
query := url.Values{}
if branch != "" {
if strings.HasPrefix(branch, "refs/"){
query.Add("ref", branch)
} else {
query.Add("ref", fmt.Sprintf("refs/heads/%s", branch))
}
}
if limit != 30 {
query.Add("per_page", strconv.Itoa(limit))
}
if key != "" {
query.Add("key", key)
}
if order != "" {
query.Add("direction", order)
}
if sort != "" {
query.Add("sort", SORT_INPUT_TO_QUERY_MAP[sort])
}
return query
}
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)
}