Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

release #23

Merged
merged 12 commits into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: lint

on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version-file: go.mod

- name: Install dependencies
run: go mod download

- name: Install deps
run: make deps-lint

- name: lint
run: make lint
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
nvcf
deploy.yaml

dist/
3 changes: 3 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
run:
skip-files:
- "preflight/containerutil/container_smoketest_grpc.go"
55 changes: 55 additions & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# This is an example .goreleaser.yml file with some sensible defaults.
# Make sure to check the documentation at https://goreleaser.com

# The lines below are called `modelines`. See `:help modeline`
# Feel free to remove those if you don't want/need to use them.
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
# vim: set ts=2 sw=2 tw=0 fo=cnqoj

version: 2

before:
hooks:
- go mod tidy

builds:
- env:
- CGO_ENABLED=0
goos:
- linux
- windows
- darwin

brews:
- name: nvcf
homepage: https://github.com/brevdev/nvcf
description: CLI tool for working with NVIDIA cloud functions
repository:
name: tap
owner: brevdev
name: homebrew-nvcf
commit_author:
name: github-actions
email: 41898282+github-actions[bot]@users.noreply.github.com

archives:
- format: tar.gz
# this name template makes the OS and Arch compatible with the results of `uname`.
name_template: >-
{{ .ProjectName }}_
{{- title .Os }}_
{{- if eq .Arch "amd64" }}x86_64
{{- else if eq .Arch "386" }}i386
{{- else }}{{ .Arch }}{{ end }}
{{- if .Arm }}v{{ .Arm }}{{ end }}
# use zip for windows archives
format_overrides:
- goos: windows
format: zip

changelog:
sort: asc
filters:
exclude:
- "^docs:"
- "^test:"
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,11 @@ docs:
cleandocs:
rm -rf ./docs

dry-release:
goreleaser release --snapshot --clean

release:
goreleaser release

.PHONY: all build clean test check fmt vet lint q deps-lint help

92 changes: 48 additions & 44 deletions cmd/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,50 +46,45 @@ func authLoginCmd() *cobra.Command {
return &cobra.Command{
Use: "login",
Short: "Authenticate with NVIDIA Cloud",
Run: func(cmd *cobra.Command, args []string) {
RunE: func(cmd *cobra.Command, args []string) error {
apiKey := output.Prompt("Enter your NVIDIA Cloud API key: ", true)

err := config.SetAPIKey(apiKey)
if err != nil {
output.Error(cmd, "Error saving API key", err)
return
return output.Error(cmd, "Error saving API key", err)
}

// Use the API key to get the first org
client := api.NewClient(apiKey)
orgsInfo := map[string]interface{}{}
err = client.Get(cmd.Context(), "/v2/orgs", nil, &orgsInfo)
if err != nil {
output.Error(cmd, "Failed to fetch organization information", err)
return
return output.Error(cmd, "Failed to fetch organization information", err)
}

organizations, ok := orgsInfo["organizations"].([]interface{})
if !ok || len(organizations) == 0 {
output.Error(cmd, "No organizations found", nil)
return
return output.Error(cmd, "No organizations found", nil)
}

firstOrg, ok := organizations[0].(map[string]interface{})
if !ok {
output.Error(cmd, "Failed to parse organization information", nil)
return
return output.Error(cmd, "Failed to parse organization information", nil)
}

orgID, ok := firstOrg["name"].(string)
if !ok {
output.Error(cmd, "Organization ID not found", nil)
return
return output.Error(cmd, "Organization ID not found", nil)
}

err = config.SetOrgID(orgID)
if err != nil {
output.Error(cmd, "Error saving Org ID", err)
return
return output.Error(cmd, "Error saving Org ID", err)
}

output.PrintASCIIArt(cmd)
output.Success(cmd, fmt.Sprintf("Authentication successful. You are now authenticated with organization ID: %s", orgID))
return nil
},
}
}
Expand All @@ -98,29 +93,27 @@ func authConfigureDockerCmd() *cobra.Command {
return &cobra.Command{
Use: "configure-docker",
Short: "Configure Docker to use NGC API key for nvcr.io",
Run: func(cmd *cobra.Command, args []string) {
RunE: func(cmd *cobra.Command, args []string) error {
apiKey := config.GetAPIKey()
if apiKey == "" {
output.Error(cmd, "NGC API key not found. Please run 'nvcf auth login' first.", nil)
return
return output.Error(cmd, "NGC API key not found. Please run 'nvcf auth login' first.", nil)
}
// Check if Docker is installed
_, err := exec.LookPath("docker")
if err != nil {
output.Error(cmd, "Docker is not installed or not in the system PATH", err)
return
return output.Error(cmd, "Docker is not installed or not in the system PATH", err)
}
// TODO: check for existing nvcr.io config?
dockerCmd := exec.Command("docker", "login", "nvcr.io", "-u", "$oauthtoken", "--password-stdin")
dockerCmd.Stdin = strings.NewReader(apiKey)
out, err := dockerCmd.CombinedOutput()
if err != nil {
output.Error(cmd, "Failed to configure Docker", err)
cmd.Println(string(out))
return
return output.Error(cmd, "Failed to configure Docker", err)
}
output.Success(cmd, "Docker configured successfully for nvcr.io")
cmd.Println(string(out))
return nil
},
}
}
Expand Down Expand Up @@ -165,13 +158,22 @@ func authLogoutCmd() *cobra.Command {
return &cobra.Command{
Use: "logout",
Short: "Logout from NVIDIA Cloud",
Run: func(cmd *cobra.Command, args []string) {
RunE: func(cmd *cobra.Command, args []string) error {
if !config.IsAuthenticated() {
output.Info(cmd, "You are currently not logged in")
return
return nil
}
err := config.ClearAPIKey()
if err != nil {
return output.Error(cmd, "Failed to clear API key", err)
}
err = config.ClearOrgID()
if err != nil {

return output.Error(cmd, "Failed to clear Org ID", err)
}
config.ClearAPIKey()
output.Success(cmd, "Logged out successfully")
return nil
},
}
}
Expand All @@ -182,19 +184,21 @@ func authWhoAmICmd() *cobra.Command {
return &cobra.Command{
Use: "whoami",
Short: "Display information about the authenticated user",
Run: func(cmd *cobra.Command, args []string) {
RunE: func(cmd *cobra.Command, args []string) error {
client := api.NewClient(config.GetAPIKey())
whoamiInfo := map[string]any{}
err := client.Get(cmd.Context(), whoamiURL, nil, &whoamiInfo)
if err != nil {
output.Error(cmd, "Failed to fetch user information", err)
return
return output.Error(cmd, "Failed to fetch user information", err)
}

jsonMode, _ := cmd.Flags().GetBool("json")
if jsonMode {
json.NewEncoder(cmd.OutOrStdout()).Encode(whoamiInfo)
return
err = json.NewEncoder(cmd.OutOrStdout()).Encode(whoamiInfo)
if err != nil {
return output.Error(cmd, "Failed to encode user information", err)
}
return nil
}
userInfo, _ := whoamiInfo["user"].(map[string]any)
table := tablewriter.NewWriter(cmd.OutOrStdout())
Expand All @@ -205,6 +209,7 @@ func authWhoAmICmd() *cobra.Command {
userInfo["name"].(string),
})
table.Render()
return nil
},
}
}
Expand All @@ -213,23 +218,24 @@ func authOrgsCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "orgs",
Short: "Display organization and team information for the authenticated user",
Run: func(cmd *cobra.Command, args []string) {
RunE: func(cmd *cobra.Command, args []string) error {
client := api.NewClient(config.GetAPIKey())
userInfo := map[string]interface{}{}
err := client.Get(cmd.Context(), "/v2/users/me", nil, &userInfo)
if err != nil {
output.Error(cmd, "Failed to fetch user information", err)
return
return output.Error(cmd, "Failed to fetch user information", err)
}
jsonMode, _ := cmd.Flags().GetBool("json")
if jsonMode {
json.NewEncoder(cmd.OutOrStdout()).Encode(userInfo)
return
err = json.NewEncoder(cmd.OutOrStdout()).Encode(userInfo)
if err != nil {
return output.Error(cmd, "Failed to encode user information", err)
}
return nil
}
userRoles, ok := userInfo["userRoles"].([]interface{})
if !ok {
output.Error(cmd, "Failed to parse user roles information", nil)
return
return output.Error(cmd, "Failed to parse user roles information", nil)
}
type OrgTeamInfo struct {
OrgName string
Expand Down Expand Up @@ -287,6 +293,7 @@ func authOrgsCmd() *cobra.Command {
}
}
table.Render()
return nil
},
}

Expand All @@ -298,34 +305,31 @@ func authOrgIDCmd() *cobra.Command {
return &cobra.Command{
Use: "org-id",
Short: "Display the name of the first organization",
Run: func(cmd *cobra.Command, args []string) {
RunE: func(cmd *cobra.Command, args []string) error {
client := api.NewClient(config.GetAPIKey())
orgsInfo := map[string]interface{}{}
err := client.Get(cmd.Context(), "/v2/orgs", nil, &orgsInfo)
if err != nil {
output.Error(cmd, "Failed to fetch organization information", err)
return
return output.Error(cmd, "Failed to fetch organization information", err)
}

organizations, ok := orgsInfo["organizations"].([]interface{})
if !ok || len(organizations) == 0 {
output.Error(cmd, "No organizations found", nil)
return
return output.Error(cmd, "No organizations found", nil)
}

firstOrg, ok := organizations[0].(map[string]interface{})
if !ok {
output.Error(cmd, "Failed to parse organization information", nil)
return
return output.Error(cmd, "Failed to parse organization information", nil)
}

name, ok := firstOrg["name"].(string)
if !ok {
output.Error(cmd, "Organization name not found", nil)
return
return output.Error(cmd, "Organization name not found", nil)
}

fmt.Println(name)
return nil
},
}
}
Expand Down
26 changes: 20 additions & 6 deletions cmd/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package cmd
import (
"os"

"github.com/brevdev/nvcf/output"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -52,18 +53,31 @@ PowerShell:
DisableFlagsInUseLine: true,
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
Hidden: true,
Args: cobra.ExactValidArgs(1),
Run: func(cmd *cobra.Command, args []string) {
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
switch args[0] {
case "bash":
cmd.Root().GenBashCompletion(os.Stdout)
err := cmd.Root().GenBashCompletion(os.Stdout)
if err != nil {
return output.Error(cmd, "Failed to generate bash completion", err)
}
case "zsh":
cmd.Root().GenZshCompletion(os.Stdout)
err := cmd.Root().GenZshCompletion(os.Stdout)
if err != nil {
return output.Error(cmd, "Failed to generate zsh completion", err)
}
case "fish":
cmd.Root().GenFishCompletion(os.Stdout, true)
err := cmd.Root().GenFishCompletion(os.Stdout, true)
if err != nil {
return output.Error(cmd, "Failed to generate fish completion", err)
}
case "powershell":
cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
err := cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
if err != nil {
return output.Error(cmd, "Failed to generate powershell completion", err)
}
}
return nil
},
}

Expand Down
Loading
Loading