Skip to content
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
172 changes: 166 additions & 6 deletions cmd/info.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,58 @@
package cmd

import (
"encoding/json"
"flag"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/fatih/color"
"github.com/hashicorp/cli"
"github.com/roots/trellis-cli/pkg/lima"
"github.com/roots/trellis-cli/trellis"
)

type InfoCommand struct {
UI cli.Ui
Trellis *trellis.Trellis
flags *flag.FlagSet
json bool
}

func NewInfoCommand(ui cli.Ui, trellis *trellis.Trellis) *InfoCommand {
c := &InfoCommand{UI: ui, Trellis: trellis}
c.flags = flag.NewFlagSet("", flag.ContinueOnError)
c.flags.Usage = func() { c.UI.Info(c.Help()) }
c.flags.BoolVar(&c.json, "json", false, "Output as JSON")
return c
}

type infoData struct {
Path string `json:"path"`
TrellisVersion string `json:"trellis_version,omitempty"`
Virtualenv string `json:"virtualenv"`
VM string `json:"vm"`
Sites map[string][]siteInfo `json:"sites"`
}

type siteInfo struct {
Name string `json:"name"`
URL string `json:"url"`
Redirects []string `json:"redirects,omitempty"`
LocalPath string `json:"local_path"`
SSL bool `json:"ssl"`
Cache bool `json:"cache"`
}

func (c *InfoCommand) Run(args []string) int {
if err := c.flags.Parse(args); err != nil {
return 1
}

args = c.flags.Args()

if err := c.Trellis.LoadProject(); err != nil {
c.UI.Error(err.Error())
return 1
Expand All @@ -29,18 +68,138 @@ func (c *InfoCommand) Run(args []string) int {
return 1
}

for name, config := range c.Trellis.Environments {
var siteNames []string
data := c.collectInfo()

for name := range config.WordPressSites {
siteNames = append(siteNames, name)
if c.json {
jsonBytes, err := json.MarshalIndent(data, "", " ")
if err != nil {
c.UI.Error(fmt.Sprintf("Error encoding JSON: %s", err))
return 1
}

c.UI.Info(fmt.Sprintf("%s => %s", name, strings.Join(siteNames, ", ")))
c.UI.Output(string(jsonBytes))
return 0
}

c.printInfo(data)
return 0
}

func (c *InfoCommand) collectInfo() infoData {
venvStatus := "inactive"
if c.Trellis.Virtualenv != nil && c.Trellis.Virtualenv.Active() {
venvStatus = "active"
} else if c.Trellis.Virtualenv != nil && c.Trellis.Virtualenv.Initialized() {
venvStatus = "initialized"
}

vmStatus := c.vmStatus()

sites := make(map[string][]siteInfo)
for _, env := range c.Trellis.EnvironmentNames() {
config := c.Trellis.Environments[env]
var envSites []siteInfo

for name, site := range config.WordPressSites {
si := siteInfo{
Name: name,
URL: site.MainUrl(),
LocalPath: site.LocalPath,
SSL: site.SslEnabled(),
Cache: site.Cache["enabled"] == true,
}

if len(site.SiteHosts) > 0 {
si.Redirects = site.SiteHosts[0].Redirects
}

envSites = append(envSites, si)
}

sites[env] = envSites
}

trellisVersion := ""
versionFile := filepath.Join(c.Trellis.Path, "trellis", "VERSION")
if data, err := os.ReadFile(versionFile); err == nil {
trellisVersion = strings.TrimSpace(string(data))
}

return infoData{
Path: c.Trellis.Path,
TrellisVersion: trellisVersion,
Virtualenv: venvStatus,
VM: vmStatus,
Sites: sites,
}
}

func (c *InfoCommand) vmStatus() string {
vmType := c.Trellis.VmManagerType()
if vmType == "" {
return "none"
}

if vmType != "lima" {
return vmType
}

manager, err := lima.NewManager(c.Trellis, c.UI)
if err != nil {
return vmType
}

instanceName, err := c.Trellis.GetVmInstanceName()
if err != nil {
return vmType
}

instance, ok := manager.GetInstance(instanceName)
if !ok {
return fmt.Sprintf("%s (not created)", vmType)
}

if instance.Running() {
return fmt.Sprintf("%s (running)", vmType)
}

return fmt.Sprintf("%s (stopped)", vmType)
}

func (c *InfoCommand) printInfo(data infoData) {
bold := color.New(color.Bold).SprintFunc()

c.UI.Output(fmt.Sprintf("%s %s", bold("Project:"), data.Path))

if data.TrellisVersion != "" {
c.UI.Output(fmt.Sprintf("%s %s", bold("Trellis:"), data.TrellisVersion))
}

c.UI.Output(fmt.Sprintf("%s %s", bold("Virtualenv:"), data.Virtualenv))
c.UI.Output(fmt.Sprintf("%s %s", bold("VM:"), data.VM))
c.UI.Output("")

for _, env := range c.Trellis.EnvironmentNames() {
sites := data.Sites[env]

c.UI.Output(bold(env))

for _, site := range sites {
c.UI.Output(fmt.Sprintf(" %s", site.Name))
c.UI.Output(fmt.Sprintf(" URL: %s", site.URL))

if len(site.Redirects) > 0 {
c.UI.Output(fmt.Sprintf(" Redirects: %s", strings.Join(site.Redirects, ", ")))
}

c.UI.Output(fmt.Sprintf(" Local Path: %s", site.LocalPath))
c.UI.Output(fmt.Sprintf(" SSL: %t", site.SSL))
c.UI.Output(fmt.Sprintf(" Cache: %t", site.Cache))
}

c.UI.Output("")
}
}

func (c *InfoCommand) Synopsis() string {
return "Displays information about this Trellis project"
}
Expand All @@ -52,6 +211,7 @@ Usage: trellis info [options]
Displays information about this Trellis project

Options:
--json Output as JSON
-h, --help show this help
`

Expand Down
115 changes: 115 additions & 0 deletions cmd/info_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package cmd

import (
"encoding/json"
"strings"
"testing"

"github.com/hashicorp/cli"
"github.com/roots/trellis-cli/trellis"
)

func TestInfoRunValidations(t *testing.T) {
defer trellis.LoadFixtureProject(t)()

cases := []struct {
name string
projectDetected bool
args []string
out string
code int
}{
{
"no_project",
false,
nil,
"No Trellis project detected",
1,
},
{
"too_many_args",
true,
[]string{"foo"},
"Error: too many arguments",
1,
},
}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
ui := cli.NewMockUi()
tr := trellis.NewMockTrellis(tc.projectDetected)
infoCommand := NewInfoCommand(ui, tr)

code := infoCommand.Run(tc.args)

if code != tc.code {
t.Errorf("expected code %d to be %d", code, tc.code)
}

combined := ui.OutputWriter.String() + ui.ErrorWriter.String()

if !strings.Contains(combined, tc.out) {
t.Errorf("expected output %q to contain %q", combined, tc.out)
}
})
}
}

func TestInfoRun(t *testing.T) {
defer trellis.LoadFixtureProject(t)()

ui := cli.NewMockUi()
tr := trellis.NewTrellis()

infoCommand := NewInfoCommand(ui, tr)
code := infoCommand.Run([]string{})

if code != 0 {
t.Errorf("expected code 0, got %d\nError: %s", code, ui.ErrorWriter.String())
}

output := ui.OutputWriter.String()

requiredStrings := []string{
"Project:",
"Virtualenv:",
"VM:",
"example.com",
}

for _, s := range requiredStrings {
if !strings.Contains(output, s) {
t.Errorf("expected output to contain %q\nGot: %s", s, output)
}
}
}

func TestInfoRunJSON(t *testing.T) {
defer trellis.LoadFixtureProject(t)()

ui := cli.NewMockUi()
tr := trellis.NewTrellis()

infoCommand := NewInfoCommand(ui, tr)
code := infoCommand.Run([]string{"--json"})

if code != 0 {
t.Errorf("expected code 0, got %d\nError: %s", code, ui.ErrorWriter.String())
}

output := ui.OutputWriter.String()

var data infoData
if err := json.Unmarshal([]byte(output), &data); err != nil {
t.Fatalf("expected valid JSON output, got error: %s\nOutput: %s", err, output)
}

if data.Path == "" {
t.Error("expected path to be set")
}

if len(data.Sites) == 0 {
t.Error("expected at least one environment in sites")
}
}
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ func main() {
return &cmd.GalaxyInstallCommand{UI: ui, Trellis: trellis}, nil
},
"info": func() (cli.Command, error) {
return &cmd.InfoCommand{UI: ui, Trellis: trellis}, nil
return cmd.NewInfoCommand(ui, trellis), nil
},
"init": func() (cli.Command, error) {
return cmd.NewInitCommand(ui, trellis), nil
Expand Down
Loading