File: graph.go

package info (click to toggle)
aptly 1.6.2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 49,928 kB
  • sloc: python: 10,398; sh: 252; makefile: 184
file content (136 lines) | stat: -rw-r--r-- 2,937 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package cmd

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/aptly-dev/aptly/deb"
	"github.com/aptly-dev/aptly/utils"
	"github.com/smira/commander"
)

func aptlyGraph(cmd *commander.Command, args []string) error {
	var err error

	if len(args) != 0 {
		cmd.Usage()
		return commander.ErrCommandError
	}

	layout := context.Flags().Lookup("layout").Value.String()

	fmt.Printf("Generating graph...\n")
	collectionFactory := context.NewCollectionFactory()
	graph, err := deb.BuildGraph(collectionFactory, layout)
	if err != nil {
		return err
	}

	buf := bytes.NewBufferString(graph.String())

	tempfile, err := os.CreateTemp("", "aptly-graph")
	if err != nil {
		return err
	}
	_ = tempfile.Close()
	_ = os.Remove(tempfile.Name())

	format := context.Flags().Lookup("format").Value.String()
	output := context.Flags().Lookup("output").Value.String()

	if filepath.Ext(output) != "" {
		format = filepath.Ext(output)[1:]
	}

	tempfilename := tempfile.Name() + "." + format

	command := exec.Command("dot", "-T"+format, "-o"+tempfilename)
	command.Stderr = os.Stderr

	stdin, err := command.StdinPipe()
	if err != nil {
		return err
	}

	err = command.Start()
	if err != nil {
		return fmt.Errorf("unable to execute dot: %s (is graphviz package installed?)", err)
	}

	_, err = io.Copy(stdin, buf)
	if err != nil {
		return err
	}

	err = stdin.Close()
	if err != nil {
		return err
	}

	err = command.Wait()
	if err != nil {
		return err
	}

	if output != "" {
		err = utils.CopyFile(tempfilename, output)
		if err != nil {
			return fmt.Errorf("unable to copy %s -> %s: %s", tempfilename, output, err)
		}

		fmt.Printf("Output saved to %s\n", output)
		_ = os.Remove(tempfilename)
	} else {
		command := getOpenCommand()
		fmt.Printf("Displaying %s file: %s %s\n", format, command, tempfilename)

		args := strings.Split(command, " ")

		viewer := exec.Command(args[0], append(args[1:], tempfilename)...)
		viewer.Stderr = os.Stderr
		err = viewer.Start()
	}

	return err
}

// getOpenCommand tries to guess command to open image for OS
func getOpenCommand() string {
	switch runtime.GOOS {
	case "darwin":
		return "/usr/bin/open"
	case "windows":
		return "cmd /c start"
	default:
		return "xdg-open"
	}
}

func makeCmdGraph() *commander.Command {
	cmd := &commander.Command{
		Run:       aptlyGraph,
		UsageLine: "graph",
		Short:     "render graph of relationships",
		Long: `
Command graph displays relationship between mirrors, local repositories,
snapshots and published repositories using graphviz package to render
graph as an image.

Example:

  $ aptly graph
`,
	}

	cmd.Flag.String("format", "png", "render graph to specified format (png, svg, pdf, etc.)")
	cmd.Flag.String("output", "", "specify output filename, default is to open result in viewer")
	cmd.Flag.String("layout", "horizontal", "create a more 'vertical' or a more 'horizontal' graph layout")

	return cmd
}