Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@

OPENAPI_DIR = ./pkg/api/openapi_client

all: format build

format:
go fmt ./...

Expand Down
77 changes: 77 additions & 0 deletions cmd/list-workspaces.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
Copyright © 2025 Codesphere Inc. <support@codesphere.com>
*/
package cmd

import (
"context"
"errors"
"fmt"

"github.com/codesphere-cloud/cs-go/pkg/api"
"github.com/codesphere-cloud/cs-go/pkg/cs"
"github.com/codesphere-cloud/cs-go/pkg/out"
"github.com/jedib0t/go-pretty/v6/table"

"github.com/spf13/cobra"
)

type ListWorkspacesCmd struct {
cmd *cobra.Command
opts ListWorkspacesOptions
}

type ListWorkspacesOptions struct {
GlobalOptions
TeamId *int
}

func addListWorkspacesCmd(p *cobra.Command, opts GlobalOptions) {
l := ListWorkspacesCmd{
cmd: &cobra.Command{
Use: "workspaces",
Short: "list resources",
Long: `list resources available in Codesphere`,
Example: `
List all workspaces:

$ cs list workspaces --team-id <team-id>
`,
},
opts: ListWorkspacesOptions{GlobalOptions: opts},
}
l.cmd.RunE = l.RunE
l.parseLogCmdFlags()
p.AddCommand(l.cmd)
}

func (l *ListWorkspacesCmd) parseLogCmdFlags() {
l.opts.TeamId = l.cmd.Flags().IntP("team-id", "t", -1, "ID of team to query")
}

func (l *ListWorkspacesCmd) RunE(_ *cobra.Command, args []string) (err error) {
if l.opts.TeamId == nil || *l.opts.TeamId < 0 {
return errors.New("team ID not set or invalid, please use --team-id to set one")
}
token, err := cs.GetApiToken()
if err != nil {
return fmt.Errorf("failed to get API token: %e", err)
}
client := api.NewClient(context.Background(), api.Configuration{
BaseUrl: l.opts.GetApiUrl(),
Token: token,
})
workspaces, err := client.ListWorkspaces(*l.opts.TeamId)
if err != nil {
return fmt.Errorf("failed to list workspaces: %e", err)
}

t := out.GetTableWriter()
t.AppendHeader(table.Row{"ID", "Name", "Repository"})
for _, w := range workspaces {
t.AppendRow(table.Row{w.Id, w.Name, *w.GitUrl.Get()})
}
t.Render()

return nil
}
34 changes: 34 additions & 0 deletions cmd/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
Copyright © 2025 Codesphere Inc. <support@codesphere.com>
*/
package cmd

import (
"github.com/spf13/cobra"
)

type ListCmd struct {
cmd *cobra.Command
}

func addListCmd(rootCmd *cobra.Command, opts GlobalOptions) {
l := ListCmd{
cmd: &cobra.Command{
Use: "list",
Short: "list resources",
Long: `list resources available in Codesphere`,
Example: `
List all workspaces:

$ cs list workspaces
`,
},
}
l.parseLogCmdFlags()
rootCmd.AddCommand(l.cmd)
addListWorkspacesCmd(l.cmd, opts)
}

func (l *ListCmd) parseLogCmdFlags() {

}
60 changes: 28 additions & 32 deletions cmd/log.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright © 2025 Alex Klein <alex@codesphere.com>
Copyright © 2025 Codesphere Inc. <support@codesphere.com>
*/
package cmd

Expand All @@ -24,14 +24,14 @@ import (
type LogCmd struct {
cmd *cobra.Command
scope LogCmdScope
opts GlobalOptions
}

type LogCmdScope struct {
workspaceId *int
server *string
step *int
replica *string
api *string
}

type LogEntry struct {
Expand All @@ -52,7 +52,7 @@ type SSE struct {
data string
}

func addLogCmd(rootCmd *cobra.Command) {
func addLogCmd(rootCmd *cobra.Command, opts GlobalOptions) {
logCmd := LogCmd{
cmd: &cobra.Command{
Use: "log",
Expand All @@ -74,6 +74,7 @@ func addLogCmd(rootCmd *cobra.Command) {
Get logs from a self-hosted Codesphere installation:
log --api https://codesphere.acme.com/api -w 637128 -s app`,
},
opts: opts,
}
logCmd.cmd.RunE = logCmd.RunE
logCmd.parseLogCmdFlags()
Expand All @@ -82,44 +83,39 @@ func addLogCmd(rootCmd *cobra.Command) {

func (logCmd *LogCmd) parseLogCmdFlags() {
logCmd.scope = LogCmdScope{
api: logCmd.cmd.Flags().String("api", "", "URL of Codesphere API (can also be CS_API)"),
workspaceId: logCmd.cmd.Flags().IntP("workspace-id", "w", 0, "ID of Codesphere workspace (can also be CS_WORKSPACE_ID)"),
server: logCmd.cmd.Flags().StringP("server", "s", "", "Name of the landscape server"),
workspaceId: logCmd.cmd.Flags().IntP("workspace-id", "w", 0, "ID of Codesphere workspace (can also be CS_WORKSPACE_ID)"),
step: logCmd.cmd.Flags().IntP("step", "n", 0, "Index of execution step (default 0)"),
replica: logCmd.cmd.Flags().StringP("replica", "r", "", "ID of server replica"),
}
}

func (logCmd *LogCmd) RunE(_ *cobra.Command, args []string) (err error) {
if *logCmd.scope.workspaceId == 0 {
*logCmd.scope.workspaceId, err = strconv.Atoi(os.Getenv("CS_WORKSPACE_ID"))
func (l *LogCmd) RunE(_ *cobra.Command, args []string) (err error) {
if *l.scope.workspaceId == 0 {
*l.scope.workspaceId, err = strconv.Atoi(os.Getenv("CS_WORKSPACE_ID"))
if err != nil {
return fmt.Errorf("failed to read env var: %e", err)
}
if *logCmd.scope.workspaceId == 0 {
if *l.scope.workspaceId == 0 {
return errors.New("workspace ID required, but not provided")
}
}

if *logCmd.scope.api == "" {
*logCmd.scope.api = cs.GetApiUrl()
}

if *logCmd.scope.replica != "" {
if *logCmd.scope.server != "codesphere-ide" {
if *l.scope.replica != "" {
if *l.scope.server != "codesphere-ide" {
slog.Warn(
"Ignoring server flag (providing replica ID is sufficient).",
"replica", *logCmd.scope.replica,
"server", *logCmd.scope.server,
"replica", *l.scope.replica,
"server", *l.scope.server,
)
}
return printLogsOfReplica("", &logCmd.scope)
return l.printLogsOfReplica("")
}
if *logCmd.scope.server != "" {
return printLogsOfServer(&logCmd.scope)
if *l.scope.server != "" {
return l.printLogsOfServer()
}

err = logCmd.printAllLogs()
err = l.printAllLogs()
if err != nil {
return fmt.Errorf("failed to print logs: %e", err)
}
Expand All @@ -145,7 +141,7 @@ func (l *LogCmd) printAllLogs() error {
*scope.step = s
*scope.replica = replica.Replica
prefix := fmt.Sprintf("|%-10s|%s", replica.Server, replica.Replica[len(replica.Replica)-11:])
err = printLogsOfReplica(prefix, &scope)
err = l.printLogsOfReplica(prefix)
if err != nil {
fmt.Printf("Error printling logs: %e\n", err)
}
Expand All @@ -157,24 +153,24 @@ func (l *LogCmd) printAllLogs() error {
return nil
}

func printLogsOfReplica(prefix string, scope *LogCmdScope) error {
func (l *LogCmd) printLogsOfReplica(prefix string) error {
endpoint := fmt.Sprintf(
"%s/workspaces/%d/logs/run/%d/replica/%s",
*scope.api,
*scope.workspaceId,
*scope.step,
*scope.replica,
l.opts.GetApiUrl(),
*l.scope.workspaceId,
*l.scope.step,
*l.scope.replica,
)
return printLogsOfEndpoint(prefix, endpoint)
}

func printLogsOfServer(scope *LogCmdScope) error {
func (l *LogCmd) printLogsOfServer() error {
endpoint := fmt.Sprintf(
"%s/workspaces/%d/logs/run/%d/server/%s",
*scope.api,
*scope.workspaceId,
*scope.step,
*scope.server,
l.opts.GetApiUrl(),
*l.scope.workspaceId,
*l.scope.step,
*l.scope.server,
)
return printLogsOfEndpoint("", endpoint)
}
Expand Down
19 changes: 18 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,21 @@ package cmd
import (
"os"

"github.com/codesphere-cloud/cs-go/pkg/cs"
"github.com/spf13/cobra"
)

type GlobalOptions struct {
apiUrl *string
}

func (o GlobalOptions) GetApiUrl() string {
if o.apiUrl != nil {
return *o.apiUrl
}
return cs.GetApiUrl()
}

func Execute() {
var rootCmd = &cobra.Command{
Use: "cs",
Expand All @@ -17,7 +29,12 @@ func Execute() {
via command line.`,
}

addLogCmd(rootCmd)
opts := GlobalOptions{}

addLogCmd(rootCmd, opts)
addListCmd(rootCmd, opts)

opts.apiUrl = rootCmd.PersistentFlags().StringP("api", "a", "", "URL of Codesphere API (can also be CS_API)")

err := rootCmd.Execute()
if err != nil {
Expand Down
11 changes: 9 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,17 @@ module github.com/codesphere-cloud/cs-go

go 1.24.2

require github.com/spf13/cobra v1.9.1
require (
github.com/jedib0t/go-pretty/v6 v6.6.7
github.com/spf13/cobra v1.9.1
gopkg.in/validator.v2 v2.0.1
)

require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/spf13/pflag v1.0.6 // indirect
gopkg.in/validator.v2 v2.0.1 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.22.0 // indirect
)
24 changes: 24 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,12 +1,36 @@
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jedib0t/go-pretty/v6 v6.6.7 h1:m+LbHpm0aIAPLzLbMfn8dc3Ht8MW7lsSO4MPItz/Uuo=
github.com/jedib0t/go-pretty/v6 v6.6.7/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/validator.v2 v2.0.1 h1:xF0KWyGWXm/LM2G1TrEjqOu4pa6coO9AlWSf3msVfDY=
gopkg.in/validator.v2 v2.0.1/go.mod h1:lIUZBlB3Im4s/eYp39Ry/wkR02yOPhZ9IwIRBjuPuG8=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Loading