summaryrefslogtreecommitdiff
path: root/build/macros.go
blob: b99c03dd3213931641939884cd7e2fb57cd089c5 (plain)
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
137
package build

import (
	"fmt"
	"path"
	"sort"
)

// ApplyUser wraps any build.Copy instructions as build.CopyAs using the given
// UID/GID.
//
func ApplyUser(uid uint, gid uint, instructions []Instruction) []Instruction {
	applied := make([]Instruction, len(instructions))

	for i, instruction := range instructions {
		switch instruction.(type) {
		case Copy, CopyFrom:
			applied[i] = CopyAs{uid, gid, instruction}
		default:
			applied[i] = instruction
		}
	}

	return applied
}

// Chown returns a build.Run instruction for setting ownership on the given
// path.
//
func Chown(uid uint, gid uint, path string) Run {
	return Run{"chown %s:%s", []string{fmt.Sprint(uid), fmt.Sprint(gid), path}}
}

// CreateDirectories returns a build.Run instruction for creating all the
// given directories.
//
func CreateDirectories(paths []string) Run {
	return Run{"mkdir -p", paths}
}

// CreateDirectory returns a build.Run instruction for creating the given
// directory.
//
func CreateDirectory(path string) Run {
	return CreateDirectories([]string{path})
}

// CreateUser returns build.Run instructions for creating the given user
// account and group.
//
func CreateUser(name string, uid uint, gid uint) []Run {
	return []Run{
		{"groupadd -o -g %s -r", []string{fmt.Sprint(gid), name}},
		{"useradd -o -m -d %s -r -g %s -u %s", []string{homeDir(name), name, fmt.Sprint(uid), name}},
	}
}

// Home returns a build.Env instruction for setting the user's home directory.
//
func Home(name string) Env {
	return Env{map[string]string{"HOME": homeDir(name)}}
}

func homeDir(name string) string {
	if name == "root" {
		return "/root"
	}

	return "/home/" + name
}

// SortFilesByDir returns both the given files indexed by parent directory and
// a sorted slice of those parent directories. The latter is useful in
// ensuring deterministic iteration since the ordering of map keys is not
// guaranteed.
//
func SortFilesByDir(files []string) ([]string, map[string][]string) {
	bydir := make(map[string][]string)

	for _, file := range files {
		dir := path.Dir(file) + "/"
		file = path.Clean(file)

		if dirfiles, found := bydir[dir]; found {
			bydir[dir] = append(dirfiles, file)
		} else {
			bydir[dir] = []string{file}
		}
	}

	dirs := make([]string, len(bydir))
	i := 0

	for dir := range bydir {
		dirs[i] = dir
		i++
	}

	sort.Strings(dirs)

	return dirs, bydir
}

// SyncFiles returns build instructions to copy over the given files after
// creating their parent directories. Parent directories are created in a
// sorted order.
//
func SyncFiles(files []string, dest string) []Instruction {
	if len(files) < 1 {
		return []Instruction{}
	}

	dirs, bydir := SortFilesByDir(files)
	mkdirs := []string{}
	copies := make([]Instruction, len(dirs))

	// make project subdirectories for requirements files if necessary, and
	// copy in requirements files
	for i, dir := range dirs {
		fulldir := dest + "/" + dir
		fulldir = path.Clean(fulldir) + "/"

		if dir != "./" {
			mkdirs = append(mkdirs, fulldir)
		}

		copies[i] = Copy{bydir[dir], fulldir}
	}

	ins := []Instruction{}

	if len(mkdirs) > 0 {
		ins = append(ins, CreateDirectories(mkdirs))
	}

	return append(ins, copies...)
}