Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
aabc4ee
chore(deps): update all dependencies and go itself
f0086 Nov 18, 2025
a5978a3
fix(build): disable darwin builds for now
f0086 Nov 18, 2025
bb4ec29
chore(dependencies): update all dependencies, go version, action vers…
f0086 Dec 19, 2025
e99a68a
chore(deps): bump golang.org/x/crypto from 0.46.0 to 0.47.0
dependabot[bot] Jan 13, 2026
950e799
chore(deps): update dependencies and improve cobra command handling
f0086 Mar 2, 2026
9bab5cb
fix(node): add id parameter to node update action
f0086 Mar 2, 2026
5958d1b
fix(project): deprecate description and environment fields and fix ge…
f0086 Mar 2, 2026
282a6fc
fix(network): disable network create command until ListSubnets endpoi…
f0086 Mar 2, 2026
a9b2506
fix(cli): fix parameter descriptions and defaults across commands
f0086 Mar 2, 2026
02c4c21
chore(deps): update dependencies and go version to 1.26.2
f0086 Apr 27, 2026
c31097b
fix(cli): improve enum completion filtering and update cobra setup co…
f0086 May 6, 2026
aaf3b38
chore(deps): bump google.golang.org/grpc from 1.80.0 to 1.81.0
dependabot[bot] May 4, 2026
a7bffe0
ci(release): make next the prerelease branch
f0086 Jun 9, 2026
3120a01
chore(deps): bump gpcore gRPC/protobuf to 20260601 schema
f0086 Jun 8, 2026
4c0f041
feat(generator): add session-sourced params and --project-id override
f0086 Jun 8, 2026
ab0fd7b
fix(cli): accept --project-id or "project use" interchangeably
f0086 Jun 8, 2026
1508384
fix(cli): reconcile command params with the gRPC request fields
f0086 Jun 8, 2026
253efc7
feat(cli): add missing gRPC coverage commands
f0086 Jun 8, 2026
e737af3
fix(cli): keep active project consistent across user contexts
f0086 Jun 8, 2026
fc1e6d8
chore(project): remove disabled network-create stub
f0086 Jun 8, 2026
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
2 changes: 1 addition & 1 deletion .github/workflows/danger.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.3'
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
pull-requests: write # to be able to comment on released pull requests
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
fetch-tags: true
- name: Setup Node.js
Expand All @@ -32,7 +32,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v6
with:
go-version: '1.25.4'
go-version: '1.26.2'
- name: Install quill CLI
run: curl -sSfL https://raw.githubusercontent.com/anchore/quill/main/install.sh | sh -s -- -b /usr/local/bin
- name: Check if snapshot build
Expand All @@ -50,7 +50,7 @@ jobs:
QUILL_NOTARY_KEY_ID: ${{ secrets.QUILL_NOTARY_KEY_ID }}
QUILL_NOTARY_ISSUER: ${{ secrets.QUILL_NOTARY_ISSUER }}
- name: Upload dist artifacts to GitHub when not on main
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v6
if: "!contains(github.ref, 'main')"
with:
name: gpcore
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6

- name: Setup Go
uses: actions/setup-go@v6
with:
go-version: '1.25.4'
go-version: '1.26.2'

- name: Install dependencies
run: go mod download
Expand Down
27 changes: 14 additions & 13 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,20 @@ builds:
ldflags:
- "-s -w -X '{{ .ModulePath }}/cmd.version={{.Tag}}' -X '{{ .ModulePath }}/cmd.commit={{.Commit}}' -X '{{ .ModulePath }}/cmd.date={{.Date}}'"

- id: gpcore-macos
goos:
- darwin
ldflags:
- "-s -w -X '{{ .ModulePath }}/cmd.version={{.Tag}}' -X '{{ .ModulePath }}/cmd.commit={{.Commit}}' -X '{{ .ModulePath }}/cmd.date={{.Date}}'"
goarch:
- amd64
- arm64
hooks:
post:
- cmd: quill sign-and-notarize "{{ .Path }}" --dry-run={{ .IsSnapshot }} --ad-hoc={{ .IsSnapshot }} -vv
env:
- QUILL_LOG_FILE=/tmp/quill-{{ .Target }}.log
# TODO: Disabled for now due to a missing agreement from apple developer account.
# - id: gpcore-macos
# goos:
# - darwin
# ldflags:
# - "-s -w -X '{{ .ModulePath }}/cmd.version={{.Tag}}' -X '{{ .ModulePath }}/cmd.commit={{.Commit}}' -X '{{ .ModulePath }}/cmd.date={{.Date}}'"
# goarch:
# - amd64
# - arm64
# hooks:
# post:
# - cmd: quill sign-and-notarize "{{ .Path }}" --dry-run={{ .IsSnapshot }} --ad-hoc={{ .IsSnapshot }} -vv
# env:
# - QUILL_LOG_FILE=/tmp/quill-{{ .Target }}.log

archives:
- formats:
Expand Down
8 changes: 4 additions & 4 deletions .releaserc
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"branches": [
{
"name": "dev",
"prerelease": true
},
{
"name": "main",
"prerelease": false
},
{
"name": "next",
"prerelease": true
}
],
"plugins": [
Expand Down
75 changes: 75 additions & 0 deletions cmd/admin_project/update.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package admin_project

import (
"buf.build/gen/go/gportal/gpcore/grpc/go/gpcore/api/admin/v1/adminv1grpc"
adminv1 "buf.build/gen/go/gportal/gpcore/protocolbuffers/go/gpcore/api/admin/v1"
cloudv1 "buf.build/gen/go/gportal/gpcore/protocolbuffers/go/gpcore/api/cloud/v1"
"github.com/G-PORTAL/gpcore-cli/pkg/client"
"github.com/G-PORTAL/gpcore-cli/pkg/config"
"github.com/G-PORTAL/gpcore-cli/pkg/protobuf"
"github.com/spf13/cobra"
"google.golang.org/grpc"
)

// update is implemented manually (not generated) because
// admin.UpdateProjectRequest carries a nested BillingProfile message which the
// YAML generator cannot express as command flags. The billing profile is
// referenced by its UUID; the backend resolves the full profile from the ID.

var updateId string
var updateName string
var updateAvatarUrl string
var updateBillingProfileId string
var updateServerPoolIds []string

var updateCmd = &cobra.Command{
Args: cobra.OnlyValidArgs,
DisableFlagsInUseLine: true,
Long: "Update project details (admin)",
RunE: func(cobraCmd *cobra.Command, args []string) error {
ctx := client.ExtractContext(cobraCmd)
grpcConn := ctx.Value("conn").(*grpc.ClientConn)
grpcClient := adminv1grpc.NewAdminServiceClient(grpcConn)

req := &adminv1.UpdateProjectRequest{
Id: updateId,
Name: updateName,
AvatarUrl: updateAvatarUrl,
ServerPoolIds: updateServerPoolIds,
}
if updateBillingProfileId != "" {
req.BillingProfile = cloudv1.BillingProfile_builder{
Id: updateBillingProfileId,
}.Build()
}

resp, err := grpcClient.UpdateProject(cobraCmd.Context(), req)
if err != nil {
return err
}
if config.JSONOutput {
jsonData, err := protobuf.MarshalIndent(resp)
if err != nil {
return err
}
cobraCmd.Println(string(jsonData))
}
return nil
},
Short: "Update project details (admin)",
Use: "update",
ValidArgs: []string{"id", "name", "avatar-url", "billing-profile-id", "server-pool-ids"},
}

func init() {
updateCmd.Flags().StringVar(&updateId, "id", "", "Project UUID (required)")
updateCmd.Flags().StringVar(&updateName, "name", "", "Project name (required)")
updateCmd.Flags().StringVar(&updateAvatarUrl, "avatar-url", "", "Avatar URL")
updateCmd.Flags().StringVar(&updateBillingProfileId, "billing-profile-id", "", "Billing profile UUID")
updateCmd.Flags().StringSliceVar(&updateServerPoolIds, "server-pool-ids", nil, "Server pool UUIDs")

updateCmd.MarkFlagRequired("id")
updateCmd.MarkFlagRequired("name")

RootAdminProjectCommand.AddCommand(updateCmd)
}
5 changes: 5 additions & 0 deletions cmd/agent/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,16 @@ func New() *cobra.Command {
rootCmd.PersistentFlags().BoolVarP(&config.Verbose, "verbose", "v", false, "verbose mode")
rootCmd.PersistentFlags().BoolVarP(&config.JSONOutput, "json", "j", false, "output as JSON")
rootCmd.PersistentFlags().BoolVarP(&config.CSVOutput, "csv", "x", false, "output as CSV")
rootCmd.MarkFlagsMutuallyExclusive("json", "csv")

// Disable Cobra's built-in "completion" subcommand (a custom one is registered below)
rootCmd.CompletionOptions.DisableDefaultCmd = true

// Special client commands
cmd.SelfupdateCommand(&rootCmd)
cmd.SetLogLevelCommand(&rootCmd)
cmd.LiveLogCommand(&rootCmd)
cmd.NotificationsCommand(&rootCmd)
//InteractiveCLICommand(&rootCmd)

// Autogenerated commands
Expand Down
3 changes: 2 additions & 1 deletion cmd/agent/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"syscall"

"github.com/G-PORTAL/gpcore-cli/pkg/api"
"github.com/G-PORTAL/gpcore-cli/pkg/client"
"github.com/G-PORTAL/gpcore-cli/pkg/config"
"github.com/G-PORTAL/gpcore-cli/pkg/consts"
"github.com/charmbracelet/log"
Expand Down Expand Up @@ -71,7 +72,7 @@ var startCmd = &cobra.Command{

if err := rootCmd.ExecuteContext(ctx); err != nil {
log.Errorf("Error executing command on agent: %v", err)
rootCmd.Printf("Error executing command on agent: %v\n", err)
rootCmd.Printf("Error: %s\n", client.FormatCommandError(err))
_ = s.Exit(1) // send cmd exit code to the client
return
}
Expand Down
4 changes: 2 additions & 2 deletions cmd/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ func CompletionCommand(rootCmd *cobra.Command) {
RunE: func(cmd *cobra.Command, args []string) error {
switch args[0] {
case "bash":
_ = rootCmd.GenBashCompletion(cmd.OutOrStdout())
_ = rootCmd.GenBashCompletionV2(cmd.OutOrStdout(), true)
case "zsh":
_ = rootCmd.GenZshCompletion(cmd.OutOrStdout())
case "fish":
_ = rootCmd.GenFishCompletion(cmd.OutOrStdout(), true)
case "powershell":
_ = rootCmd.GenPowerShellCompletion(cmd.OutOrStdout())
_ = rootCmd.GenPowerShellCompletionWithDesc(cmd.OutOrStdout())
}

return nil
Expand Down
31 changes: 4 additions & 27 deletions cmd/livelog.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"buf.build/gen/go/gportal/gpcore/grpc/go/gpcore/api/admin/v1/adminv1grpc"
adminv1 "buf.build/gen/go/gportal/gpcore/protocolbuffers/go/gpcore/api/admin/v1"
cloudv1 "buf.build/gen/go/gportal/gpcore/protocolbuffers/go/gpcore/api/cloud/v1"
"github.com/charmbracelet/ssh"
"github.com/G-PORTAL/gpcore-cli/pkg/api"
"github.com/jedib0t/go-pretty/v6/text"
"github.com/spf13/cobra"
"google.golang.org/grpc"
Expand All @@ -18,37 +18,16 @@ func LiveLogCommand(rootCmd *cobra.Command) {
DisableFlagsInUseLine: true,
Args: cobra.OnlyValidArgs,
RunE: func(cobraCmd *cobra.Command, args []string) error {
sshSession := cobraCmd.Context().Value("ssh").(*ssh.Session)
cobraCmd.SetOut(*sshSession)

conn := cobraCmd.Context().Value("conn").(*grpc.ClientConn)
admin := adminv1grpc.NewAdminServiceClient(conn)
res, err := admin.SubscribeServerLogs(cobraCmd.Context(), &adminv1.SubscribeServerLogsRequest{})
stream, err := admin.SubscribeServerLogs(cobraCmd.Context(), &adminv1.SubscribeServerLogsRequest{})
if err != nil {
return err
}

connectionClosed := false
go func() {
breakChan := make(chan bool)
(*sshSession).Break(breakChan)
<-breakChan
connectionClosed = true
}()

cobraCmd.Printf("\033[33mWaiting for new notifications ...\033[0m\n")
// We wait for (and print out) relevant notifications until the break
// request is received.
for {
if connectionClosed {
break
}

msg, err := res.Recv() // Blocking
if err != nil {
return err
}

return api.StreamMessages(cobraCmd, stream, func(msg *adminv1.SubscribeServerLogsResponse) {
// TODO: Filter for source/server/datacenter
// TODO: Only above level ...

Expand Down Expand Up @@ -78,9 +57,7 @@ func LiveLogCommand(rootCmd *cobra.Command) {

cobraCmd.Printf("%s: [%s] [%s] [%s] -> %s\n", color.Sprint(time), datacenter, server, source, color.Sprint(m.GetMessage()))
}
}

return nil
})
},
})
}
22 changes: 18 additions & 4 deletions cmd/node/change_rescue_mode.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package node
import (
"buf.build/gen/go/gportal/gpcore/grpc/go/gpcore/api/cloud/v1/cloudv1grpc"
cloudv1 "buf.build/gen/go/gportal/gpcore/protocolbuffers/go/gpcore/api/cloud/v1"
"fmt"
"github.com/G-PORTAL/gpcore-cli/pkg/client"
"github.com/G-PORTAL/gpcore-cli/pkg/config"
"github.com/G-PORTAL/gpcore-cli/pkg/protobuf"
Expand All @@ -21,6 +22,20 @@ var changeRescueModeCmd = &cobra.Command{
Long: "Change rescue mode",
RunE: func(cobraCmd *cobra.Command, args []string) error {
ctx := client.ExtractContext(cobraCmd)

// --project-id is optional and falls back to the project selected via
// "project use" (mirrors the generated commands' behavior).
session := ctx.Value("config").(*config.SessionConfig)
if session == nil {
return fmt.Errorf("no session found, please login first")
}
if changeRescueModeProjectId == "" {
if session.CurrentProject == nil {
return fmt.Errorf("no project selected: pass --project-id or select one with \"project use\"")
}
changeRescueModeProjectId = *session.CurrentProject
}

grpcConn := ctx.Value("conn").(*grpc.ClientConn)
client := cloudv1grpc.NewCloudServiceClient(grpcConn)
resp, err := client.ChangeNodeRescueMode(cobraCmd.Context(), &cloudv1.ChangeNodeRescueModeRequest{
Expand Down Expand Up @@ -51,12 +66,11 @@ var changeRescueModeCmd = &cobra.Command{

func init() {
changeRescueModeCmd.Flags().StringVar(&changeRescueModeId, "id", "", "Node ID (required)")
changeRescueModeCmd.Flags().StringVar(&changeRescueModeProjectId, "project-id", "", "Project ID (required)")
changeRescueModeCmd.Flags().BoolVar(&changeRescueModeEnabled, "enabled", false, "Enable or disable rescue mode (required)")
changeRescueModeCmd.Flags().StringVar(&changeRescueModePassword, "password", "", "Password for rescue mode (required)")
changeRescueModeCmd.Flags().StringVar(&changeRescueModeProjectId, "project-id", "", "Project ID (defaults to the project selected via \"project use\")")
changeRescueModeCmd.Flags().BoolVar(&changeRescueModeEnabled, "enabled", false, "Enable or disable rescue mode")
changeRescueModeCmd.Flags().StringVar(&changeRescueModePassword, "password", "", "Password for rescue mode")

changeRescueModeCmd.MarkFlagRequired("id")
changeRescueModeCmd.MarkFlagRequired("project-id")

RootNodeCommand.AddCommand(changeRescueModeCmd)
}
18 changes: 5 additions & 13 deletions cmd/node/root.go
Original file line number Diff line number Diff line change
@@ -1,28 +1,20 @@
package node

import (
"fmt"
"github.com/G-PORTAL/gpcore-cli/pkg/config"
"github.com/spf13/cobra"
)

// Note: node subcommands no longer require a globally selected project here.
// Each subcommand accepts an optional --project-id that falls back to the
// project selected via "project use" (and errors if neither is set). This
// avoids forcing "project use" when the project is passed explicitly.
var RootNodeCommand = &cobra.Command{
Use: "node",
Short: "Utility to combine multiple nodes api actions",
Long: `Utility to combine multiple nodes api actions`,
GroupID: "resources",
DisableFlagsInUseLine: true,
Args: cobra.MatchAll(cobra.ExactArgs(0), cobra.OnlyValidArgs),
PersistentPreRunE: func(cobraCmd *cobra.Command, args []string) error {
// Context is not set in PersistentPreRunE, so we need to get the session config manually
config, err := config.GetSessionConfig()
if err != nil {
return err
}
if config.CurrentProject == nil {
return fmt.Errorf("no project selected")
}
return nil
},
RunE: func(cobraCmd *cobra.Command, args []string) error {
return cobraCmd.Usage()
},
Expand Down
Loading
Loading