diff --git a/LICENSE b/LICENSE index eaf2ebea..b061f7b5 100644 --- a/LICENSE +++ b/LICENSE @@ -174,7 +174,7 @@ of your accepting any such warranty or additional liability. - Copyright 2022 LiveKit, Inc. + Copyright 2021-2024 LiveKit, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/cmd/lk/app.go b/cmd/lk/app.go index c36f2ff7..8ab27694 100644 --- a/cmd/lk/app.go +++ b/cmd/lk/app.go @@ -138,12 +138,12 @@ var ( } ) -func requireProject(ctx context.Context, cmd *cli.Command) error { +func requireProject(ctx context.Context, cmd *cli.Command) (context.Context, error) { var err error if project, err = loadProjectDetails(cmd); err != nil { - if err = loadProjectConfig(ctx, cmd); err != nil { + if _, err = loadProjectConfig(ctx, cmd); err != nil { // something is wrong with config file - return err + return nil, err } // choose from existing credentials or authenticate @@ -157,9 +157,9 @@ func requireProject(ctx context.Context, cmd *cli.Command) error { Description("If you'd like to use a different project, run `lk cloud auth` to add credentials"). Options(options...). Value(&project). - WithTheme(theme). + WithTheme(util.Theme). Run(); err != nil { - return err + return nil, err } } else { shouldAuth := true @@ -167,23 +167,23 @@ func requireProject(ctx context.Context, cmd *cli.Command) error { Title("No local projects found. Authenticate one now?"). Inline(true). Value(&shouldAuth). - WithTheme(theme). + WithTheme(util.Theme). Run(); err != nil { - return err + return nil, err } if shouldAuth { initAuth(ctx, cmd) if err = tryAuthIfNeeded(ctx, cmd); err != nil { - return err + return nil, err } return requireProject(ctx, cmd) } else { - return errors.New("no project selected") + return nil, errors.New("no project selected") } } } - return err + return nil, err } func listTemplates(ctx context.Context, cmd *cli.Command) error { @@ -196,11 +196,11 @@ func listTemplates(ctx context.Context, cmd *cli.Command) error { util.PrintJSON(templates) } else { const maxDescLength = 64 - table := CreateTable().Headers("Template", "Description").BorderRow(true) + table := util.CreateTable().Headers("Template", "Description").BorderRow(true) for _, t := range templates { desc := strings.Join(util.WrapToLines(t.Desc, maxDescLength), "\n") - url := theme.Focused.Title.Render(t.URL) - tags := theme.Help.ShortDesc.Render("#" + strings.Join(t.Tags, " #")) + url := util.Theme.Focused.Title.Render(t.URL) + tags := util.Theme.Help.ShortDesc.Render("#" + strings.Join(t.Tags, " #")) table.Row( t.Name, desc+"\n\n"+url+"\n"+tags, @@ -251,10 +251,10 @@ func setupTemplate(ctx context.Context, cmd *cli.Command) error { templateSelect := huh.NewSelect[string](). Title("Select Template"). Value(&templateURL). - WithTheme(theme) + WithTheme(util.Theme) var options []huh.Option[string] for _, t := range templateOptions { - descStyle := theme.Help.ShortDesc + descStyle := util.Theme.Help.ShortDesc optionText := t.Name + " " + descStyle.Render("#"+strings.Join(t.Tags, " #")) options = append(options, huh.NewOption(optionText, t.URL)) } @@ -293,13 +293,13 @@ func setupTemplate(ctx context.Context, cmd *cli.Command) error { } return nil }). - WithTheme(theme)) + WithTheme(util.Theme)) } if len(preinstallPrompts) > 0 { group := huh.NewGroup(preinstallPrompts...) if err := huh.NewForm(group). - WithTheme(theme). + WithTheme(util.Theme). RunWithContext(ctx); err != nil { return err } @@ -364,7 +364,7 @@ func cloneTemplate(_ context.Context, cmd *cli.Command, url, appName string) err Action(func() { stdout, stderr, cmdErr = bootstrap.CloneTemplate(url, tempName) }). - Style(theme.Focused.Title). + Style(util.Theme.Focused.Title). Run(); err != nil { return err } @@ -424,7 +424,7 @@ func instantiateEnv(ctx context.Context, cmd *cli.Command, rootPath string, addl Title("Enter " + key + "?"). Placeholder(oldValue). Value(&newValue). - WithTheme(theme). + WithTheme(util.Theme). Run(); err != nil || newValue == "" { return oldValue, err } @@ -485,7 +485,7 @@ func doInstall(ctx context.Context, task bootstrap.KnownTask, rootPath string, v if err := spinner.New(). Title("Installing..."). Action(func() { cmdErr = install() }). - Style(theme.Focused.Title). + Style(util.Theme.Focused.Title). Accessible(true). Run(); err != nil { return err @@ -512,7 +512,7 @@ func runTask(ctx context.Context, cmd *cli.Command) error { Title("Select Task"). Options(options...). Value(&taskName). - WithTheme(theme). + WithTheme(util.Theme). Run(); err != nil { return err } @@ -526,7 +526,7 @@ func runTask(ctx context.Context, cmd *cli.Command) error { if err := spinner.New(). Title("Running task " + taskName + "..."). Action(func() { cmdErr = task() }). - Style(theme.Focused.Title). + Style(util.Theme.Focused.Title). Accessible(verbose). Run(); err != nil { return err diff --git a/cmd/lk/cloud.go b/cmd/lk/cloud.go index d71670a4..55f5bad4 100644 --- a/cmd/lk/cloud.go +++ b/cmd/lk/cloud.go @@ -220,14 +220,14 @@ func NewAuthClient(client *http.Client, baseURL string) *AuthClient { return a } -func initAuth(ctx context.Context, cmd *cli.Command) error { +func initAuth(ctx context.Context, cmd *cli.Command) (context.Context, error) { authClient = *NewAuthClient(&http.Client{}, serverURL) - return nil + return nil, nil } func handleAuth(ctx context.Context, cmd *cli.Command) error { if revoke { - if err := loadProjectConfig(ctx, cmd); err != nil { + if _, err := loadProjectConfig(ctx, cmd); err != nil { return err } token, err := requireToken(ctx, cmd) @@ -265,7 +265,7 @@ func requireToken(_ context.Context, cmd *cli.Command) (string, error) { } func tryAuthIfNeeded(ctx context.Context, cmd *cli.Command) error { - if err := loadProjectConfig(ctx, cmd); err != nil { + if _, err := loadProjectConfig(ctx, cmd); err != nil { return err } @@ -274,7 +274,7 @@ func tryAuthIfNeeded(ctx context.Context, cmd *cli.Command) error { if err := huh.NewInput(). Title("What is the name of this device?"). Value(&deviceName). - WithTheme(theme). + WithTheme(util.Theme). Run(); err != nil { return err } @@ -303,7 +303,7 @@ func tryAuthIfNeeded(ctx context.Context, cmd *cli.Command) error { Action(func() { ak, pollErr = pollClaim(ctx, cmd) }). - Style(theme.Focused.Title). + Style(util.Theme.Focused.Title). Run(); err != nil { return err } @@ -319,7 +319,7 @@ func tryAuthIfNeeded(ctx context.Context, cmd *cli.Command) error { Title("Make this project default?"). Value(&isDefault). Inline(true). - WithTheme(theme). + WithTheme(util.Theme). Run(); err != nil { return err } @@ -339,7 +339,7 @@ func tryAuthIfNeeded(ctx context.Context, cmd *cli.Command) error { } return nil }). - WithTheme(theme). + WithTheme(util.Theme). Run(); err != nil { return err } diff --git a/cmd/lk/dispatch.go b/cmd/lk/dispatch.go index e529b19e..12b8a3f2 100644 --- a/cmd/lk/dispatch.go +++ b/cmd/lk/dispatch.go @@ -1,3 +1,17 @@ +// Copyright 2024 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package main import ( @@ -71,14 +85,14 @@ var ( dispatchClient *lksdk.AgentDispatchClient ) -func createDispatchClient(ctx context.Context, cmd *cli.Command) error { +func createDispatchClient(ctx context.Context, cmd *cli.Command) (context.Context, error) { pc, err := loadProjectDetails(cmd) if err != nil { - return err + return nil, err } dispatchClient = lksdk.NewAgentDispatchServiceClient(pc.URL, pc.APIKey, pc.APISecret, withDefaultClientOpts(pc)...) - return nil + return nil, nil } func getAgentDispatch(ctx context.Context, cmd *cli.Command) error { @@ -128,7 +142,7 @@ func listDispatchAndPrint(cmd *cli.Command, req *livekit.ListAgentDispatchReques if cmd.Bool("json") { util.PrintJSON(res) } else { - table := CreateTable(). + table := util.CreateTable(). Headers("DispatchID", "Room", "AgentName", "Metadata") for _, item := range res.AgentDispatches { if item == nil { diff --git a/cmd/lk/egress.go b/cmd/lk/egress.go index 44ba6f8a..d2348241 100644 --- a/cmd/lk/egress.go +++ b/cmd/lk/egress.go @@ -1,4 +1,4 @@ -// Copyright 2023 LiveKit, Inc. +// Copyright 2022-2024 LiveKit, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -381,14 +381,14 @@ var ( egressClient *lksdk.EgressClient ) -func createEgressClient(ctx context.Context, c *cli.Command) error { - pc, err := loadProjectDetails(c) +func createEgressClient(ctx context.Context, cmd *cli.Command) (context.Context, error) { + pc, err := loadProjectDetails(cmd) if err != nil { - return err + return nil, err } egressClient = lksdk.NewEgressClient(pc.URL, pc.APIKey, pc.APISecret, withDefaultClientOpts(pc)...) - return nil + return nil, nil } func handleEgressStart(ctx context.Context, cmd *cli.Command) error { @@ -600,7 +600,7 @@ func listEgress(ctx context.Context, cmd *cli.Command) error { if cmd.Bool("json") { util.PrintJSON(items) } else { - table := CreateTable(). + table := util.CreateTable(). Headers("EgressID", "Status", "Type", "Source", "Started At", "Error") for _, item := range items { var startedAt string diff --git a/cmd/lk/ingress.go b/cmd/lk/ingress.go index e9b7d535..ad34140e 100644 --- a/cmd/lk/ingress.go +++ b/cmd/lk/ingress.go @@ -1,4 +1,4 @@ -// Copyright 2023 LiveKit, Inc. +// Copyright 2022-2024 LiveKit, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -161,14 +161,14 @@ var ( ingressClient *lksdk.IngressClient ) -func createIngressClient(ctx context.Context, cmd *cli.Command) error { +func createIngressClient(ctx context.Context, cmd *cli.Command) (context.Context, error) { pc, err := loadProjectDetails(cmd) if err != nil { - return err + return nil, err } ingressClient = lksdk.NewIngressClient(pc.URL, pc.APIKey, pc.APISecret, withDefaultClientOpts(pc)...) - return nil + return nil, nil } func createIngress(ctx context.Context, cmd *cli.Command) error { @@ -224,7 +224,7 @@ func listIngress(ctx context.Context, cmd *cli.Command) error { if cmd.Bool("verbose") || cmd.Bool("json") { util.PrintJSON(res) } else { - table := CreateTable(). + table := util.CreateTable(). Headers("IngressID", "Name", "Room", "StreamKey", "URL", "Status", "Error") for _, item := range res.Items { if item == nil { diff --git a/cmd/lk/join.go b/cmd/lk/join.go index c4ce0fdf..ca442e48 100644 --- a/cmd/lk/join.go +++ b/cmd/lk/join.go @@ -1,4 +1,4 @@ -// Copyright 2023 LiveKit, Inc. +// Copyright 2021-2024 LiveKit, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/cmd/lk/join_test.go b/cmd/lk/join_test.go index 7385b35c..5f90f355 100644 --- a/cmd/lk/join_test.go +++ b/cmd/lk/join_test.go @@ -1,4 +1,4 @@ -// Copyright 2023 LiveKit, Inc. +// Copyright 2021-2023 LiveKit, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/cmd/lk/loadtest.go b/cmd/lk/loadtest.go index 3f7e59af..3e8bdec1 100644 --- a/cmd/lk/loadtest.go +++ b/cmd/lk/loadtest.go @@ -1,4 +1,4 @@ -// Copyright 2023 LiveKit, Inc. +// Copyright 2021-2024 LiveKit, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/cmd/lk/main.go b/cmd/lk/main.go index 13e280c4..55be5d7c 100644 --- a/cmd/lk/main.go +++ b/cmd/lk/main.go @@ -1,4 +1,4 @@ -// Copyright 2023 LiveKit, Inc. +// Copyright 2021-2024 LiveKit, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -39,7 +39,7 @@ func main() { Suggest: true, HideHelpCommand: true, UseShortOptionHandling: true, - Flags: persistentFlags, + Flags: globalFlags, Commands: []*cli.Command{ { Name: "generate-fish-completion", @@ -53,18 +53,7 @@ func main() { }, }, }, - Before: func(ctx context.Context, cmd *cli.Command) error { - logConfig := &logger.Config{ - Level: "info", - } - if cmd.Bool("verbose") { - logConfig.Level = "debug" - } - logger.InitFromConfig(logConfig, "lk") - lksdk.SetLogger(logger.GetLogger()) - - return nil - }, + Before: initLogger, } app.Commands = append(app.Commands, AppCommands...) @@ -114,6 +103,19 @@ func checkForLegacyName() { } } +func initLogger(ctx context.Context, cmd *cli.Command) (context.Context, error) { + logConfig := &logger.Config{ + Level: "info", + } + if cmd.Bool("verbose") { + logConfig.Level = "debug" + } + logger.InitFromConfig(logConfig, "lk") + lksdk.SetLogger(logger.GetLogger()) + + return nil, nil +} + func generateFishCompletion(ctx context.Context, cmd *cli.Command) error { fishScript, err := cmd.ToFishCompletion() if err != nil { diff --git a/cmd/lk/project.go b/cmd/lk/project.go index 16579b9c..19a05110 100644 --- a/cmd/lk/project.go +++ b/cmd/lk/project.go @@ -1,4 +1,4 @@ -// Copyright 2023 LiveKit, Inc. +// Copyright 2022-2024 LiveKit, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -93,10 +93,10 @@ var ( urlRegex = regexp.MustCompile(`^(http|https|ws|wss)://[^\s/$.?#].[^\s]*$`) ) -func loadProjectConfig(ctx context.Context, cmd *cli.Command) error { +func loadProjectConfig(ctx context.Context, cmd *cli.Command) (context.Context, error) { conf, err := config.LoadOrCreate() if err != nil { - return err + return nil, err } cliConfig = conf @@ -108,7 +108,7 @@ func loadProjectConfig(ctx context.Context, cmd *cli.Command) error { } } } - return nil + return nil, nil } func addProject(ctx context.Context, cmd *cli.Command) error { @@ -207,7 +207,7 @@ func addProject(ctx context.Context, cmd *cli.Command) error { Title("Make this project default?"). Value(&isDefault). Inline(true). - WithTheme(theme)) + WithTheme(util.Theme)) } if len(prompts) > 0 { @@ -216,7 +216,7 @@ func addProject(ctx context.Context, cmd *cli.Command) error { groups = append(groups, huh.NewGroup(p)) } err = huh.NewForm(groups...). - WithTheme(theme). + WithTheme(util.Theme). RunWithContext(ctx) if err != nil { return err @@ -244,14 +244,14 @@ func listProjects(ctx context.Context, cmd *cli.Command) error { return nil } - baseStyle := theme.Form.Foreground(fg).Padding(0, 1) + baseStyle := util.Theme.Form.Foreground(util.Fg).Padding(0, 1) headerStyle := baseStyle.Bold(true) - selectedStyle := theme.Focused.Title.Padding(0, 1) + selectedStyle := util.Theme.Focused.Title.Padding(0, 1) if cmd.Bool("json") { util.PrintJSON(cliConfig.Projects) } else { - table := CreateTable(). + table := util.CreateTable(). StyleFunc(func(row, col int) lipgloss.Style { switch { case row == table.HeaderRow: @@ -303,7 +303,7 @@ func setDefaultProject(ctx context.Context, cmd *cli.Command) error { if err := cliConfig.PersistIfNeeded(); err != nil { return err } - fmt.Println("Default project set to [" + theme.Focused.Title.Render(p.Name) + "]") + fmt.Println("Default project set to [" + util.Theme.Focused.Title.Render(p.Name) + "]") return nil } diff --git a/cmd/lk/proto.go b/cmd/lk/proto.go index eac9c4d5..f7c78d88 100644 --- a/cmd/lk/proto.go +++ b/cmd/lk/proto.go @@ -195,7 +195,7 @@ func listAndPrint[ if cmd.Bool("json") { util.PrintJSON(res) } else { - table := CreateTable(). + table := util.CreateTable(). Headers(header...) for _, item := range res.GetItems() { if item == nil { diff --git a/cmd/lk/replay.go b/cmd/lk/replay.go index c6093842..6149bd8f 100644 --- a/cmd/lk/replay.go +++ b/cmd/lk/replay.go @@ -1,3 +1,17 @@ +// Copyright 2024 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package main import ( @@ -99,10 +113,10 @@ var ( replayClient *replayServiceClient ) -func createReplayClient(ctx context.Context, cmd *cli.Command) error { +func createReplayClient(ctx context.Context, cmd *cli.Command) (context.Context, error) { pc, err := loadProjectDetails(cmd) if err != nil { - return err + return nil, err } url := lksdk.ToHttpURL(pc.URL) @@ -112,7 +126,7 @@ func createReplayClient(ctx context.Context, cmd *cli.Command) error { apiKey: pc.APIKey, apiSecret: pc.APISecret, } - return nil + return nil, nil } func listReplays(ctx context.Context, cmd *cli.Command) error { @@ -130,7 +144,7 @@ func listReplays(ctx context.Context, cmd *cli.Command) error { if cmd.Bool("json") { util.PrintJSON(res.Replays) } else { - table := CreateTable().Headers("ReplayID") + table := util.CreateTable().Headers("ReplayID") for _, info := range res.Replays { table.Row(info.ReplayId) } diff --git a/cmd/lk/room.go b/cmd/lk/room.go index 99754aea..c158301e 100644 --- a/cmd/lk/room.go +++ b/cmd/lk/room.go @@ -1,4 +1,4 @@ -// Copyright 2023 LiveKit, Inc. +// Copyright 2021-2024 LiveKit, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -37,9 +37,8 @@ import ( var ( RoomCommands = []*cli.Command{ { - Name: "room", - Usage: "Create or delete rooms and manage existing room properties", - HideHelpCommand: true, + Name: "room", + Usage: "Create or delete rooms and manage existing room properties", Commands: []*cli.Command{ { Name: "create", @@ -523,14 +522,14 @@ var ( roomClient *lksdk.RoomServiceClient ) -func createRoomClient(ctx context.Context, cmd *cli.Command) error { +func createRoomClient(ctx context.Context, cmd *cli.Command) (context.Context, error) { pc, err := loadProjectDetails(cmd) if err != nil { - return err + return nil, err } roomClient = lksdk.NewRoomServiceClient(pc.URL, pc.APIKey, pc.APISecret, withDefaultClientOpts(pc)...) - return nil + return nil, nil } func createRoom(ctx context.Context, cmd *cli.Command) error { @@ -662,7 +661,7 @@ func listRooms(ctx context.Context, cmd *cli.Command) error { if cmd.Bool("json") { util.PrintJSON(res) } else { - table := CreateTable().Headers("RoomID", "Name", "Participants", "Publishers") + table := util.CreateTable().Headers("RoomID", "Name", "Participants", "Publishers") for _, rm := range res.Rooms { table.Row( rm.Sid, diff --git a/cmd/lk/sip.go b/cmd/lk/sip.go index dc79f6c3..a8ff7402 100644 --- a/cmd/lk/sip.go +++ b/cmd/lk/sip.go @@ -1,4 +1,4 @@ -// Copyright 2023 LiveKit, Inc. +// Copyright 2023-2024 LiveKit, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/cmd/lk/token.go b/cmd/lk/token.go index 756944df..c0912283 100644 --- a/cmd/lk/token.go +++ b/cmd/lk/token.go @@ -1,4 +1,4 @@ -// Copyright 2023 LiveKit, Inc. +// Copyright 2021-2024 LiveKit, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -300,7 +300,7 @@ func createToken(ctx context.Context, c *cli.Command) error { Title("Token Permissions"). Description("See https://docs.livekit.io/home/get-started/authentication/#Video-grant"). Value(&permissions). - WithTheme(theme). + WithTheme(util.Theme). Run(); err != nil || len(permissions) == 0 { return errors.New("no permissions were given in this grant, see --help") } else { diff --git a/cmd/lk/ulimit_unix.go b/cmd/lk/ulimit_unix.go index 3a28164a..d26e3ba1 100644 --- a/cmd/lk/ulimit_unix.go +++ b/cmd/lk/ulimit_unix.go @@ -1,4 +1,4 @@ -// Copyright 2023 LiveKit, Inc. +// Copyright 2022-2023 LiveKit, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/cmd/lk/ulimit_windows.go b/cmd/lk/ulimit_windows.go index d2959bbe..8a0ce272 100644 --- a/cmd/lk/ulimit_windows.go +++ b/cmd/lk/ulimit_windows.go @@ -1,4 +1,4 @@ -// Copyright 2023 LiveKit, Inc. +// Copyright 2022-2023 LiveKit, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/cmd/lk/utils.go b/cmd/lk/utils.go index 588ac8cc..cd61b31b 100644 --- a/cmd/lk/utils.go +++ b/cmd/lk/utils.go @@ -1,4 +1,4 @@ -// Copyright 2023 LiveKit, Inc. +// Copyright 2021-2024 LiveKit, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -20,12 +20,11 @@ import ( "os" "strings" - "github.com/charmbracelet/lipgloss" - "github.com/charmbracelet/lipgloss/table" "github.com/twitchtv/twirp" "github.com/urfave/cli/v3" "github.com/livekit/livekit-cli/pkg/config" + "github.com/livekit/livekit-cli/pkg/util" "github.com/livekit/protocol/utils/interceptors" ) @@ -45,43 +44,37 @@ var ( Aliases: []string{"j"}, Usage: "Output as JSON", } - printCurl bool - persistentFlags = []cli.Flag{ + printCurl bool + globalFlags = []cli.Flag{ &cli.StringFlag{ - Name: "url", - Usage: "`URL` to LiveKit instance", - Sources: cli.EnvVars("LIVEKIT_URL"), - Value: "http://localhost:7880", - Persistent: true, + Name: "url", + Usage: "`URL` to LiveKit instance", + Sources: cli.EnvVars("LIVEKIT_URL"), + Value: "http://localhost:7880", }, &cli.StringFlag{ - Name: "api-key", - Usage: "Your `KEY`", - Sources: cli.EnvVars("LIVEKIT_API_KEY"), - Persistent: true, + Name: "api-key", + Usage: "Your `KEY`", + Sources: cli.EnvVars("LIVEKIT_API_KEY"), }, &cli.StringFlag{ - Name: "api-secret", - Usage: "Your `SECRET`", - Sources: cli.EnvVars("LIVEKIT_API_SECRET"), - Persistent: true, + Name: "api-secret", + Usage: "Your `SECRET`", + Sources: cli.EnvVars("LIVEKIT_API_SECRET"), }, &cli.StringFlag{ - Name: "project", - Usage: "`NAME` of a configured project", - Persistent: true, + Name: "project", + Usage: "`NAME` of a configured project", }, &cli.BoolFlag{ Name: "curl", Usage: "Print curl commands for API actions", Destination: &printCurl, Required: false, - Persistent: true, }, &cli.BoolFlag{ - Name: "verbose", - Required: false, - Persistent: true, + Name: "verbose", + Required: false, }, } ) @@ -138,25 +131,6 @@ func extractFlagOrArg(c *cli.Command, flag string) (string, error) { return value, nil } -func CreateTable() *table.Table { - baseStyle := theme.Form.Foreground(fg).Padding(0, 1) - headerStyle := baseStyle.Bold(true) - - styleFunc := func(row, col int) lipgloss.Style { - if row == table.HeaderRow { - return headerStyle - } - return baseStyle - } - - t := table.New(). - Border(lipgloss.NormalBorder()). - BorderStyle(theme.Form.Foreground(fg)). - StyleFunc(styleFunc) - - return t -} - type loadParams struct { requireURL bool } @@ -192,7 +166,7 @@ func loadProjectDetails(c *cli.Command, opts ...loadOption) (*config.ProjectConf if err != nil { return nil, err } - fmt.Println("Using project [" + theme.Focused.Title.Render(c.String("project")) + "]") + fmt.Println("Using project [" + util.Theme.Focused.Title.Render(c.String("project")) + "]") logDetails(c, pc) return pc, nil } @@ -230,7 +204,7 @@ func loadProjectDetails(c *cli.Command, opts ...loadOption) (*config.ProjectConf dp, err := config.LoadDefaultProject() if err == nil { if c.Bool("verbose") { - fmt.Println("Using default project [" + theme.Focused.Title.Render(dp.Name) + "]") + fmt.Println("Using default project [" + util.Theme.Focused.Title.Render(dp.Name) + "]") logDetails(c, dp) } return dp, nil diff --git a/cmd/lk/utils_test.go b/cmd/lk/utils_test.go index 3f9c64a0..776416ab 100644 --- a/cmd/lk/utils_test.go +++ b/cmd/lk/utils_test.go @@ -1,3 +1,17 @@ +// Copyright 2024 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package main import ( diff --git a/go.mod b/go.mod index f9db4418..d904fbf8 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ toolchain go1.23.3 require ( github.com/charmbracelet/huh v0.6.0 - github.com/charmbracelet/huh/spinner v0.0.0-20241011224433-983a50776b31 + github.com/charmbracelet/huh/spinner v0.0.0-20241216182847-438e4f741435 github.com/charmbracelet/lipgloss v1.0.0 github.com/frostbyte73/core v0.1.0 github.com/go-logr/logr v1.4.2 @@ -21,7 +21,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.10.0 github.com/twitchtv/twirp v8.1.3+incompatible - github.com/urfave/cli/v3 v3.0.0-alpha9 + github.com/urfave/cli/v3 v3.0.0-alpha9.7 go.uber.org/atomic v1.11.0 golang.org/x/sync v0.10.0 golang.org/x/time v0.8.0 @@ -49,10 +49,10 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chainguard-dev/git-urls v1.0.2 // indirect github.com/charmbracelet/bubbles v0.20.0 // indirect - github.com/charmbracelet/bubbletea v1.1.1 // indirect - github.com/charmbracelet/x/ansi v0.4.2 // indirect + github.com/charmbracelet/bubbletea v1.2.5-0.20241205214244-9306010a31ee // indirect + github.com/charmbracelet/x/ansi v0.4.5 // indirect github.com/charmbracelet/x/exp/strings v0.0.0-20240809174237-9ab0ca04ce0c // indirect - github.com/charmbracelet/x/term v0.2.0 // indirect + github.com/charmbracelet/x/term v0.2.1 // indirect github.com/cloudflare/circl v1.3.7 // indirect github.com/cyphar/filepath-securejoin v0.2.5 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -125,7 +125,6 @@ require ( github.com/stoewer/go-strcase v1.3.0 // indirect github.com/wlynxg/anet v0.0.5 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect - github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect github.com/zeebo/xxh3 v1.0.2 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect diff --git a/go.sum b/go.sum index 5d8f16d2..45d047de 100644 --- a/go.sum +++ b/go.sum @@ -54,22 +54,22 @@ github.com/chainguard-dev/git-urls v1.0.2 h1:pSpT7ifrpc5X55n4aTTm7FFUE+ZQHKiqpiw github.com/chainguard-dev/git-urls v1.0.2/go.mod h1:rbGgj10OS7UgZlbzdUQIQpT0k/D4+An04HJY7Ol+Y/o= github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE= github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= -github.com/charmbracelet/bubbletea v1.1.1 h1:KJ2/DnmpfqFtDNVTvYZ6zpPFL9iRCRr0qqKOCvppbPY= -github.com/charmbracelet/bubbletea v1.1.1/go.mod h1:9Ogk0HrdbHolIKHdjfFpyXJmiCzGwy+FesYkZr7hYU4= +github.com/charmbracelet/bubbletea v1.2.5-0.20241205214244-9306010a31ee h1:xNijbIIsd6zADvvqrQj3kfKmLqJshZpCspKAfspXkFU= +github.com/charmbracelet/bubbletea v1.2.5-0.20241205214244-9306010a31ee/go.mod h1:Hbk5+oE4a7cDyjfdPi4sHZ42aGTMYcmHnVDhsRswn7A= github.com/charmbracelet/huh v0.6.0 h1:mZM8VvZGuE0hoDXq6XLxRtgfWyTI3b2jZNKh0xWmax8= github.com/charmbracelet/huh v0.6.0/go.mod h1:GGNKeWCeNzKpEOh/OJD8WBwTQjV3prFAtQPpLv+AVwU= -github.com/charmbracelet/huh/spinner v0.0.0-20241011224433-983a50776b31 h1:HqaYBKXy1eQBnN9tCLJJHaQ+3btqonOVh25LZ/Xaxps= -github.com/charmbracelet/huh/spinner v0.0.0-20241011224433-983a50776b31/go.mod h1:Cxhgl8N0sX9A+EQxedzzGZAalaF8fUVL+JP/pSOW8cI= +github.com/charmbracelet/huh/spinner v0.0.0-20241216182847-438e4f741435 h1:GnQvPBetPFyWaq4xVP4iia8UZAaLMVUk4UZ1O3Gdx44= +github.com/charmbracelet/huh/spinner v0.0.0-20241216182847-438e4f741435/go.mod h1:YqGqPo+vKnyTc0xppm1sv3Ir8FwG9bSW2H33LT++Xdg= github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg= github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo= -github.com/charmbracelet/x/ansi v0.4.2 h1:0JM6Aj/g/KC154/gOP4vfxun0ff6itogDYk41kof+qk= -github.com/charmbracelet/x/ansi v0.4.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= +github.com/charmbracelet/x/ansi v0.4.5 h1:LqK4vwBNaXw2AyGIICa5/29Sbdq58GbGdFngSexTdRM= +github.com/charmbracelet/x/ansi v0.4.5/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b h1:MnAMdlwSltxJyULnrYbkZpp4k58Co7Tah3ciKhSNo0Q= github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= github.com/charmbracelet/x/exp/strings v0.0.0-20240809174237-9ab0ca04ce0c h1:6IUwt5Pfsv8ugjei3FKMzKIEJMZJt5QN8nU2VN/IOPw= github.com/charmbracelet/x/exp/strings v0.0.0-20240809174237-9ab0ca04ce0c/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ= -github.com/charmbracelet/x/term v0.2.0 h1:cNB9Ot9q8I711MyZ7myUR5HFWL/lc3OpU8jZ4hwm0x0= -github.com/charmbracelet/x/term v0.2.0/go.mod h1:GVxgxAbjUrmpvIINHIQnJJKpMlHiZ4cktEQCN6GWyF0= +github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= +github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= @@ -285,14 +285,12 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/twitchtv/twirp v8.1.3+incompatible h1:+F4TdErPgSUbMZMwp13Q/KgDVuI7HJXP61mNV3/7iuU= github.com/twitchtv/twirp v8.1.3+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A= -github.com/urfave/cli/v3 v3.0.0-alpha9 h1:P0RMy5fQm1AslQS+XCmy9UknDXctOmG/q/FZkUFnJSo= -github.com/urfave/cli/v3 v3.0.0-alpha9/go.mod h1:0kK/RUFHyh+yIKSfWxwheGndfnrvYSmYFVeKCh03ZUc= +github.com/urfave/cli/v3 v3.0.0-alpha9.7 h1:pkcHMTcfp821SiXRuYBkXdo30X8cuBzuJ95pv5q//54= +github.com/urfave/cli/v3 v3.0.0-alpha9.7/go.mod h1:FnIeEMYu+ko8zP1F9Ypr3xkZMIDqW3DR92yUtY39q1Y= github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= -github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= -github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= diff --git a/install-cli.sh b/install-cli.sh index 2e147c0c..566a7923 100755 --- a/install-cli.sh +++ b/install-cli.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright 2023 LiveKit, Inc. +# Copyright 2022-2024 LiveKit, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index 2948f5d5..0973959f 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -1,3 +1,17 @@ +// Copyright 2024 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package auth import ( diff --git a/pkg/config/config.go b/pkg/config/config.go index f098af33..34b78d85 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -1,4 +1,4 @@ -// Copyright 2023 LiveKit, Inc. +// Copyright 2022-2024 LiveKit, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/loadtester/loadtest.go b/pkg/loadtester/loadtest.go index cab0b9aa..394bdcd0 100644 --- a/pkg/loadtester/loadtest.go +++ b/pkg/loadtester/loadtest.go @@ -1,4 +1,4 @@ -// Copyright 2023 LiveKit, Inc. +// Copyright 2021-2024 LiveKit, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -19,13 +19,15 @@ import ( "fmt" "math/rand" "net/url" - "os" "sort" + "strconv" "strings" "sync" - "text/tabwriter" "time" + "github.com/charmbracelet/lipgloss" + "github.com/charmbracelet/lipgloss/table" + "github.com/livekit/livekit-cli/pkg/util" "github.com/pkg/errors" "golang.org/x/sync/errgroup" "golang.org/x/sync/syncmap" @@ -98,61 +100,86 @@ func (t *LoadTest) Run(ctx context.Context) error { names = append(names, name) } sort.Strings(names) - for _, name := range names { + + fmt.Println("\nTrack loading:") + testerTable := util.CreateTable(). + Headers("Tester", "Track", "Kind", "Pkts.", "Bitrate", "Pkt. Loss") + for n, name := range names { testerStats := stats[name] summaries[name] = getTesterSummary(testerStats) - fmt.Println(testerStats, summaries[name]) - - w := tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', 0) - _, _ = fmt.Fprintf(w, "\n%s\t| Track\t| Kind\t| Pkts\t| Bitrate\t| Dropped\n", name) trackStatsSlice := make([]*trackStats, 0, len(testerStats.trackStats)) for _, ts := range testerStats.trackStats { trackStatsSlice = append(trackStatsSlice, ts) } sort.Slice(trackStatsSlice, func(i, j int) bool { - nameI := t.trackNames[trackStatsSlice[i].trackID] - nameJ := t.trackNames[trackStatsSlice[j].trackID] - return strings.Compare(nameI, nameJ) < 0 + return strings.Compare( + string(trackStatsSlice[i].kind), + string(trackStatsSlice[j].kind), + ) < 0 }) - for _, trackStats := range trackStatsSlice { - dropped := formatStrings( + for i, trackStats := range trackStatsSlice { + dropped := formatLossRate( trackStats.packets.Load(), trackStats.dropped.Load()) - trackName := t.trackNames[trackStats.trackID] - _, _ = fmt.Fprintf(w, "\t| %s %s\t| %s\t| %d\t| %s\t| %s\n", - trackName, trackStats.trackID, trackStats.kind, trackStats.packets.Load(), - formatBitrate(trackStats.bytes.Load(), time.Since(trackStats.startedAt.Load())), dropped) + trackName := "" + if i == 0 { + trackName = name + } + testerTable.Row( + trackName, + trackStats.trackID, + string(trackStats.kind), + strconv.FormatInt(trackStats.packets.Load(), 10), + formatBitrate( + trackStats.bytes.Load(), + time.Since(trackStats.startedAt.Load()), + ), + dropped, + ) + } + if n != len(names)-1 { + testerTable.Row("", "", "", "", "", "") } - _ = w.Flush() + } + fmt.Println(testerTable) if len(summaries) == 0 { return nil } - // summary - w := tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', 0) - _, _ = fmt.Fprint(w, "\nSummary\t| Tester\t| Tracks\t| Bitrate\t| Total Dropped\t| Error\n") - + // tester summary + fmt.Println("\nSubscriber summaries:") + summaryTable := util.CreateTable(). + Headers("Tester", "Tracks", "Bitrate", "Total Pkt. Loss", "Error"). + StyleFunc(func(row, col int) lipgloss.Style { + if row == table.HeaderRow { + return util.FormHeaderStyle + } + if row == len(names) { + return util.FormBaseStyle.Bold(true).Reverse(true) + } + return util.FormBaseStyle + }) for _, name := range names { s := summaries[name] - sDropped := formatStrings(s.packets, s.dropped) + sDropped := formatLossRate(s.packets, s.dropped) sBitrate := formatBitrate(s.bytes, s.elapsed) - _, _ = fmt.Fprintf(w, "\t| %s\t| %d/%d\t| %s\t| %s\t| %s\n", - name, s.tracks, s.expected, sBitrate, sDropped, s.errString) + summaryTable.Row(name, fmt.Sprintf("%d/%d", s.tracks, s.expected), sBitrate, sDropped, s.errString) + } + { + // totals row + s := getTestSummary(summaries) + sDropped := formatLossRate(s.packets, s.dropped) + // avg bitrate per sub + sBitrate := fmt.Sprintf("%s (%s avg)", + formatBitrate(s.bytes, s.elapsed), + formatBitrate(s.bytes/int64(len(summaries)), s.elapsed), + ) + summaryTable.Row("Total", fmt.Sprintf("%d/%d", s.tracks, s.expected), sBitrate, sDropped, string(s.errCount)) } + fmt.Println(summaryTable) - s := getTestSummary(summaries) - sDropped := formatStrings(s.packets, s.dropped) - // avg bitrate per sub - sBitrate := fmt.Sprintf("%s (%s avg)", - formatBitrate(s.bytes, s.elapsed), - formatBitrate(s.bytes/int64(len(summaries)), s.elapsed), - ) - _, _ = fmt.Fprintf(w, "\t| %s\t| %d/%d\t| %s\t| %s\t| %d\n", - "Total", s.tracks, s.expected, sBitrate, sDropped, s.errCount) - - _ = w.Flush() return nil } @@ -180,8 +207,8 @@ func (t *LoadTest) RunSuite(ctx context.Context) error { {publishers: 1, subscribers: 1000, video: true}, } - w := tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', 0) - _, _ = fmt.Fprint(w, "\nPubs\t| Subs\t| Tracks\t| Audio\t| Video\t| Packet loss\t| Errors\n") + table := util.CreateTable(). + Headers("Pubs", "Subs", "Tracks", "Audio", "Video", "Pkt. Loss", "Errors") for _, c := range cases { caseParams := t.Params @@ -218,11 +245,18 @@ func (t *LoadTest) RunSuite(ctx context.Context) error { errCount++ } } - _, _ = fmt.Fprintf(w, "%d\t| %d\t| %d\t| Yes\t| %s\t| %.3f%%| %d\t\n", - c.publishers, c.subscribers, tracks, videoString, 100*float64(dropped)/float64(dropped+packets), errCount) + table.Row( + strconv.Itoa(c.publishers), + strconv.Itoa(c.subscribers), + strconv.FormatInt(tracks, 10), + "Yes", + videoString, + formatLossRate(packets, dropped), + strconv.FormatInt(errCount, 10), + ) } - _ = w.Flush() + fmt.Println(table) return nil } diff --git a/pkg/loadtester/loadtester.go b/pkg/loadtester/loadtester.go index 609dcebe..88eda33c 100644 --- a/pkg/loadtester/loadtester.go +++ b/pkg/loadtester/loadtester.go @@ -1,4 +1,4 @@ -// Copyright 2023 LiveKit, Inc. +// Copyright 2021-2024 LiveKit, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/loadtester/loadtestprovider.go b/pkg/loadtester/loadtestprovider.go index 2da5181c..8da5f7e8 100644 --- a/pkg/loadtester/loadtestprovider.go +++ b/pkg/loadtester/loadtestprovider.go @@ -1,4 +1,4 @@ -// Copyright 2023 LiveKit, Inc. +// Copyright 2022-2023 LiveKit, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/loadtester/speakersimulator.go b/pkg/loadtester/speakersimulator.go index a0e376ea..9ab7275a 100644 --- a/pkg/loadtester/speakersimulator.go +++ b/pkg/loadtester/speakersimulator.go @@ -1,4 +1,4 @@ -// Copyright 2023 LiveKit, Inc. +// Copyright 2023-2024 LiveKit, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/loadtester/stats.go b/pkg/loadtester/stats.go index b09a2874..4e4a435b 100644 --- a/pkg/loadtester/stats.go +++ b/pkg/loadtester/stats.go @@ -1,4 +1,4 @@ -// Copyright 2023 LiveKit, Inc. +// Copyright 2021-2024 LiveKit, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/loadtester/util.go b/pkg/loadtester/util.go index 8a809d83..0fcaade5 100644 --- a/pkg/loadtester/util.go +++ b/pkg/loadtester/util.go @@ -1,4 +1,4 @@ -// Copyright 2023 LiveKit, Inc. +// Copyright 2021-2023 LiveKit, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -31,7 +31,7 @@ func randStringRunes(n int) string { return string(b) } -func formatStrings(packets, dropped int64) (sDropped string) { +func formatLossRate(packets, dropped int64) (sDropped string) { sDropped = " - " if packets > 0 { @@ -43,6 +43,9 @@ func formatStrings(packets, dropped int64) (sDropped string) { } func formatPercentage(num int64, total int64) string { + if total == 0 { + return "0" + } return strings.TrimRight(strings.TrimRight(fmt.Sprintf("%.3f", float64(num)/float64(total)*100), "0"), ".") } diff --git a/pkg/provider/embeds.go b/pkg/provider/embeds.go index c1a7c3ac..50e7e7f8 100644 --- a/pkg/provider/embeds.go +++ b/pkg/provider/embeds.go @@ -1,4 +1,4 @@ -// Copyright 2023 LiveKit, Inc. +// Copyright 2022-2023 LiveKit, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/provider/h264looper.go b/pkg/provider/h264looper.go index 007b5c09..0e988e68 100644 --- a/pkg/provider/h264looper.go +++ b/pkg/provider/h264looper.go @@ -1,4 +1,4 @@ -// Copyright 2023 LiveKit, Inc. +// Copyright 2022-2024 LiveKit, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/provider/looper.go b/pkg/provider/looper.go index 6dbfde00..5c8919aa 100644 --- a/pkg/provider/looper.go +++ b/pkg/provider/looper.go @@ -1,4 +1,4 @@ -// Copyright 2023 LiveKit, Inc. +// Copyright 2022-2024 LiveKit, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/provider/opuslooper.go b/pkg/provider/opuslooper.go index 1fa69dd0..98fadc9e 100644 --- a/pkg/provider/opuslooper.go +++ b/pkg/provider/opuslooper.go @@ -1,4 +1,4 @@ -// Copyright 2023 LiveKit, Inc. +// Copyright 2022-2024 LiveKit, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/provider/vp8looper.go b/pkg/provider/vp8looper.go index 7ea9d695..071d55c3 100644 --- a/pkg/provider/vp8looper.go +++ b/pkg/provider/vp8looper.go @@ -1,4 +1,4 @@ -// Copyright 2023 LiveKit, Inc. +// Copyright 2022-2024 LiveKit, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/util/fs.go b/pkg/util/fs.go index a532542a..a133b366 100644 --- a/pkg/util/fs.go +++ b/pkg/util/fs.go @@ -1,3 +1,17 @@ +// Copyright 2024 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package util import ( diff --git a/pkg/util/json.go b/pkg/util/json.go index 99a515d7..5779619b 100644 --- a/pkg/util/json.go +++ b/pkg/util/json.go @@ -1,3 +1,17 @@ +// Copyright 2024 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package util import ( diff --git a/pkg/util/strings.go b/pkg/util/strings.go index b7fade56..b1339b14 100644 --- a/pkg/util/strings.go +++ b/pkg/util/strings.go @@ -1,3 +1,17 @@ +// Copyright 2024 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package util import ( diff --git a/pkg/util/strings_test.go b/pkg/util/strings_test.go index d3d0ec54..ff9ba7ec 100644 --- a/pkg/util/strings_test.go +++ b/pkg/util/strings_test.go @@ -1,3 +1,17 @@ +// Copyright 2024 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package util import ( diff --git a/pkg/util/table.go b/pkg/util/table.go new file mode 100644 index 00000000..5f218177 --- /dev/null +++ b/pkg/util/table.go @@ -0,0 +1,36 @@ +// Copyright 2024 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package util + +import ( + "github.com/charmbracelet/lipgloss" + "github.com/charmbracelet/lipgloss/table" +) + +func CreateTable() *table.Table { + styleFunc := func(row, col int) lipgloss.Style { + if row == table.HeaderRow { + return FormHeaderStyle + } + return FormBaseStyle + } + + t := table.New(). + Border(lipgloss.NormalBorder()). + BorderStyle(Theme.Form.Foreground(Fg)). + StyleFunc(styleFunc) + + return t +} diff --git a/cmd/lk/style.go b/pkg/util/theme.go similarity index 77% rename from cmd/lk/style.go rename to pkg/util/theme.go index d16b3af9..ad66e3fe 100644 --- a/cmd/lk/style.go +++ b/pkg/util/theme.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package util import ( "github.com/charmbracelet/huh" @@ -20,9 +20,12 @@ import ( ) var ( - fg = lipgloss.AdaptiveColor{Light: "235", Dark: "252"} - theme = func() *huh.Theme { + Theme = func() *huh.Theme { t := huh.ThemeBase16() return t }() + + Fg = lipgloss.AdaptiveColor{Light: "235", Dark: "252"} + FormBaseStyle = Theme.Form.Foreground(Fg).Padding(0, 1) + FormHeaderStyle = FormBaseStyle.Bold(true) ) diff --git a/version.go b/version.go index f37aabc0..c5e3fb30 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ -// Copyright 2023 LiveKit, Inc. +// Copyright 2021-2024 LiveKit, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License.