You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

137 lines
3.1 KiB

  1. package build
  2. import (
  3. "fmt"
  4. "path"
  5. "sort"
  6. )
  7. // ApplyUser wraps any build.Copy instructions as build.CopyAs using the given
  8. // UID/GID.
  9. //
  10. func ApplyUser(uid uint, gid uint, instructions []Instruction) []Instruction {
  11. applied := make([]Instruction, len(instructions))
  12. for i, instruction := range instructions {
  13. switch instruction.(type) {
  14. case Copy, CopyFrom:
  15. applied[i] = CopyAs{uid, gid, instruction}
  16. default:
  17. applied[i] = instruction
  18. }
  19. }
  20. return applied
  21. }
  22. // Chown returns a build.Run instruction for setting ownership on the given
  23. // path.
  24. //
  25. func Chown(uid uint, gid uint, path string) Run {
  26. return Run{"chown %s:%s", []string{fmt.Sprint(uid), fmt.Sprint(gid), path}}
  27. }
  28. // CreateDirectories returns a build.Run instruction for creating all the
  29. // given directories.
  30. //
  31. func CreateDirectories(paths []string) Run {
  32. return Run{"mkdir -p", paths}
  33. }
  34. // CreateDirectory returns a build.Run instruction for creating the given
  35. // directory.
  36. //
  37. func CreateDirectory(path string) Run {
  38. return CreateDirectories([]string{path})
  39. }
  40. // CreateUser returns build.Run instructions for creating the given user
  41. // account and group.
  42. //
  43. func CreateUser(name string, uid uint, gid uint) []Run {
  44. return []Run{
  45. {"groupadd -o -g %s -r", []string{fmt.Sprint(gid), name}},
  46. {"useradd -o -m -d %s -r -g %s -u %s", []string{homeDir(name), name, fmt.Sprint(uid), name}},
  47. }
  48. }
  49. // Home returns a build.Env instruction for setting the user's home directory.
  50. //
  51. func Home(name string) Env {
  52. return Env{map[string]string{"HOME": homeDir(name)}}
  53. }
  54. func homeDir(name string) string {
  55. if name == "root" {
  56. return "/root"
  57. }
  58. return "/home/" + name
  59. }
  60. // SortFilesByDir returns both the given files indexed by parent directory and
  61. // a sorted slice of those parent directories. The latter is useful in
  62. // ensuring deterministic iteration since the ordering of map keys is not
  63. // guaranteed.
  64. //
  65. func SortFilesByDir(files []string) ([]string, map[string][]string) {
  66. bydir := make(map[string][]string)
  67. for _, file := range files {
  68. dir := path.Dir(file) + "/"
  69. file = path.Clean(file)
  70. if dirfiles, found := bydir[dir]; found {
  71. bydir[dir] = append(dirfiles, file)
  72. } else {
  73. bydir[dir] = []string{file}
  74. }
  75. }
  76. dirs := make([]string, len(bydir))
  77. i := 0
  78. for dir := range bydir {
  79. dirs[i] = dir
  80. i++
  81. }
  82. sort.Strings(dirs)
  83. return dirs, bydir
  84. }
  85. // SyncFiles returns build instructions to copy over the given files after
  86. // creating their parent directories. Parent directories are created in a
  87. // sorted order.
  88. //
  89. func SyncFiles(files []string, dest string) []Instruction {
  90. if len(files) < 1 {
  91. return []Instruction{}
  92. }
  93. dirs, bydir := SortFilesByDir(files)
  94. mkdirs := []string{}
  95. copies := make([]Instruction, len(dirs))
  96. // make project subdirectories for requirements files if necessary, and
  97. // copy in requirements files
  98. for i, dir := range dirs {
  99. fulldir := dest + "/" + dir
  100. fulldir = path.Clean(fulldir) + "/"
  101. if dir != "./" {
  102. mkdirs = append(mkdirs, fulldir)
  103. }
  104. copies[i] = Copy{bydir[dir], fulldir}
  105. }
  106. ins := []Instruction{}
  107. if len(mkdirs) > 0 {
  108. ins = append(ins, CreateDirectories(mkdirs))
  109. }
  110. return append(ins, copies...)
  111. }