feat!: release v2 (#6545)
Co-authored-by: Artur <145776561+arturict@users.noreply.github.com> Co-authored-by: arturict <arturict@users.noreply.github.com> Co-authored-by: Thomas <thomas@ajnart.fr> Co-authored-by: Afonso Jorge Ramos <afonsojorgeramos@gmail.com> Co-authored-by: Danura30082 <danura30082+github@gmail.com> Co-authored-by: Kushie <122986913+kushiemoon-dev@users.noreply.github.com> Co-authored-by: ajnart <thomas@ajnart.fr> Co-authored-by: ajnart <thomas@ajnart.dev> Co-authored-by: chenjun <chenjun@foxmail.com> Co-authored-by: Wikibear <wikibear@users.noreply.github.com> Co-authored-by: Bergasha <bergasha@gmail.com> Co-authored-by: leafyelin <43306196+leafyelin@users.noreply.github.com> Co-authored-by: Lucas Araujo <lucas.lra@gmail.com> Co-authored-by: GreenMonito <86085570+GreenMonito@users.noreply.github.com> Co-authored-by: Nerdy-Technician <roffo@nerdy-technician.social> Co-authored-by: Jun <34545896+jcstrong@users.noreply.github.com> Co-authored-by: Edmond Abraham <54119952+gitedmond@users.noreply.github.com> Co-authored-by: thrawnx <tight.bulb9384@fastmail.com> Co-authored-by: Chris <58918656+nordada@users.noreply.github.com> Co-authored-by: yi111 <153097222+Yi-111-a@users.noreply.github.com> Co-authored-by: Yanis <naamaneyanis@gmail.com> Co-authored-by: Wikibear <me@sej.de> Co-authored-by: alessiottavo <93441886+alessiottavo@users.noreply.github.com>
This commit is contained in:
3286 files changed
+284386
-139176
No files matched your search
@@ -0,0 +1,197 @@
|
||||
// Package cli wires the command line surface. The interactive application
|
||||
// lives in internal/tui; everything here is the scriptable path that does not
|
||||
// need a terminal.
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/homarr-labs/homarr/tools/homarr-dev/internal/run"
|
||||
"github.com/homarr-labs/homarr/tools/homarr-dev/internal/tui"
|
||||
)
|
||||
|
||||
// App holds command state. The three function fields are seams: they let the
|
||||
// command wiring be tested without opening a terminal, and they are the only
|
||||
// mutable state in the package — the previous version reached for package-level
|
||||
// variables, which meant tests had to mutate globals to observe dispatch.
|
||||
type App struct {
|
||||
demo bool
|
||||
pr int
|
||||
env []string
|
||||
detach bool
|
||||
buildPR int
|
||||
|
||||
RunBrowser func() error
|
||||
RunInstances func() error
|
||||
Launch func(cmd *cobra.Command, args []string) error
|
||||
}
|
||||
|
||||
func NewApp() *App {
|
||||
app := &App{RunBrowser: tui.RunDev, RunInstances: tui.RunDashboard}
|
||||
app.Launch = app.launch
|
||||
return app
|
||||
}
|
||||
|
||||
// Execute runs the CLI.
|
||||
func Execute() error {
|
||||
args := os.Args[1:]
|
||||
if len(args) > 0 && args[0] == "--" {
|
||||
args = args[1:]
|
||||
}
|
||||
root := NewApp().Root()
|
||||
root.SetArgs(args)
|
||||
return root.Execute()
|
||||
}
|
||||
|
||||
func (a *App) Root() *cobra.Command {
|
||||
root := &cobra.Command{
|
||||
Use: "homarr",
|
||||
Short: "Launch and manage Homarr Docker instances",
|
||||
SilenceUsage: true,
|
||||
Long: `Launch and manage Homarr Docker containers.
|
||||
|
||||
Start a local image, a pull-request build from ghcr.io, browse open pull
|
||||
requests, or manage running instances — all from one interactive terminal
|
||||
application where builds and pulls run in the background.
|
||||
|
||||
Examples:
|
||||
homarr # interactive browser
|
||||
homarr --pr 6390 # launch a pull-request image directly
|
||||
homarr --pr 6390 -e FOO=bar # with environment overrides
|
||||
homarr run dev # launch local homarr:dev
|
||||
homarr run --detach dev # start in the background
|
||||
homarr dash # open on the instances screen
|
||||
homarr build feature # build this checkout as homarr:feature
|
||||
homarr build --pr 6390 # build a pull request locally
|
||||
homarr images # local images with provenance
|
||||
homarr data # instance data volumes
|
||||
homarr prune # remove stopped instances
|
||||
homarr doctor # check Docker and optional integrations`,
|
||||
Args: cobra.NoArgs,
|
||||
RunE: a.runRoot,
|
||||
}
|
||||
|
||||
a.addRunFlags(root)
|
||||
root.AddCommand(
|
||||
a.runCommand(),
|
||||
a.devCommand(),
|
||||
a.dashCommand(),
|
||||
a.buildCommand(),
|
||||
a.rebuildCommand(),
|
||||
a.listCommand(),
|
||||
a.imagesCommand(),
|
||||
a.dataCommand(),
|
||||
a.pruneCommand(),
|
||||
a.logsCommand(),
|
||||
a.stopCommand(),
|
||||
a.restartCommand(),
|
||||
a.removeCommand(),
|
||||
a.openCommand(),
|
||||
a.shellCommand(),
|
||||
a.ciCommand(),
|
||||
a.doctorCommand(),
|
||||
)
|
||||
return root
|
||||
}
|
||||
|
||||
func (a *App) addRunFlags(cmd *cobra.Command) {
|
||||
cmd.Flags().BoolVarP(&a.demo, "demo", "m", false, "Demo mode (mock integrations)")
|
||||
cmd.Flags().IntVarP(&a.pr, "pr", "p", 0, "Launch a pull-request image from ghcr.io")
|
||||
cmd.Flags().StringArrayVarP(&a.env, "env", "e", nil, "Extra env vars (repeatable, KEY=VALUE)")
|
||||
cmd.Flags().BoolVar(&a.detach, "detach", false, "Start in the background")
|
||||
}
|
||||
|
||||
// runRoot opens the browser unless a launch flag was given, in which case the
|
||||
// bare `homarr` invocation is shorthand for `homarr run`.
|
||||
func (a *App) runRoot(cmd *cobra.Command, args []string) error {
|
||||
for _, name := range []string{"pr", "env", "demo", "detach"} {
|
||||
if cmd.Flags().Changed(name) {
|
||||
return a.Launch(cmd, args)
|
||||
}
|
||||
}
|
||||
return a.RunBrowser()
|
||||
}
|
||||
|
||||
func (a *App) runCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "run [tag]",
|
||||
Short: "Launch a local image or a pull-request image",
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error { return a.Launch(cmd, args) },
|
||||
}
|
||||
a.addRunFlags(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (a *App) devCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "dev",
|
||||
Short: "Browse pull requests and local development images",
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(*cobra.Command, []string) error { return a.RunBrowser() },
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) dashCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "dash",
|
||||
Aliases: []string{"instances"},
|
||||
Short: "Open the interactive application on the instances screen",
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(*cobra.Command, []string) error { return a.RunInstances() },
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) launch(cmd *cobra.Command, args []string) error {
|
||||
tag := ""
|
||||
if len(args) > 0 {
|
||||
tag = args[0]
|
||||
}
|
||||
if a.pr < 0 {
|
||||
return fmt.Errorf("PR number must be positive")
|
||||
}
|
||||
if a.pr > 0 && tag != "" {
|
||||
return fmt.Errorf("tag and --pr cannot be used together")
|
||||
}
|
||||
if a.pr == 0 && tag == "" {
|
||||
if err := cmd.Usage(); err != nil {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("provide a tag or --pr")
|
||||
}
|
||||
|
||||
plan, err := run.BuildPlan(run.Options{
|
||||
Context: cmd.Context(),
|
||||
PR: a.pr,
|
||||
Tag: tag,
|
||||
Demo: a.demo,
|
||||
Env: a.env,
|
||||
FetchTitle: a.pr > 0,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Starting Homarr (%s)\n", plan.Label)
|
||||
fmt.Printf(" Image : %s\n", plan.Image)
|
||||
fmt.Printf(" Container : %s\n", plan.Name)
|
||||
fmt.Printf(" Volume : %s\n", plan.Volume)
|
||||
fmt.Printf(" Port : %d:7575\n", plan.HostPort)
|
||||
fmt.Printf(" URL : http://localhost:%d\n", plan.HostPort)
|
||||
if plan.PRNumber > 0 {
|
||||
fmt.Printf(" PR : https://github.com/homarr-labs/homarr/pull/%d\n", plan.PRNumber)
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
if a.detach {
|
||||
if err := run.StartDetached(plan); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Started in background: http://localhost:%d\n", plan.HostPort)
|
||||
return nil
|
||||
}
|
||||
return run.StartForeground(plan)
|
||||
}
|
||||
Reference in new issue
Block a user