-
Notifications
You must be signed in to change notification settings - Fork 316
/
prebuild.go
121 lines (104 loc) · 3.68 KB
/
prebuild.go
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
package config
import (
"encoding/json"
"os"
"path/filepath"
"strings"
util "github.com/loft-sh/devpod/pkg/util/hash"
"github.com/moby/buildkit/frontend/dockerfile/dockerignore"
"github.com/moby/patternmatcher"
"github.com/pkg/errors"
"github.com/loft-sh/log"
"github.com/loft-sh/log/hash"
)
func CalculatePrebuildHash(originalConfig *DevContainerConfig, platform, architecture, contextPath, dockerfilePath, dockerfileContent string, log log.Logger) (string, error) {
parsedConfig := CloneDevContainerConfig(originalConfig)
if platform != "" {
splitted := strings.Split(platform, "/")
if len(splitted) == 2 && splitted[0] == "linux" {
architecture = splitted[1]
}
}
// delete all options that are not relevant for the build
parsedConfig.Origin = ""
parsedConfig.DevContainerActions = DevContainerActions{}
parsedConfig.NonComposeBase = NonComposeBase{}
parsedConfig.DevContainerConfigBase = DevContainerConfigBase{
Name: parsedConfig.Name,
Features: parsedConfig.Features,
OverrideFeatureInstallOrder: parsedConfig.OverrideFeatureInstallOrder,
}
parsedConfig.ImageContainer = ImageContainer{
Image: parsedConfig.Image,
}
parsedConfig.ComposeContainer = ComposeContainer{}
parsedConfig.DockerfileContainer = DockerfileContainer{
Dockerfile: parsedConfig.Dockerfile,
Context: parsedConfig.Context,
Build: parsedConfig.Build,
}
// marshal the config
configStr, err := json.Marshal(parsedConfig)
if err != nil {
return "", err
}
// find out excludes from dockerignore
excludes, err := readDockerignore(contextPath, dockerfilePath)
if err != nil {
return "", errors.Errorf("Error reading .dockerignore: %v", err)
}
excludes = append(excludes, DevPodContextFeatureFolder+"/")
// get hash of the context directory
contextHash, err := util.DirectoryHash(contextPath, excludes)
if err != nil {
return "", err
}
log.Debugf("Prebuild hash from:")
log.Debugf(" Arch: %s", architecture)
log.Debugf(" Config: %s", string(configStr))
log.Debugf(" DockerfileContent: %s", dockerfileContent)
log.Debugf(" ContextHash: %s", contextHash)
return "devpod-" + hash.String(architecture + string(configStr) + dockerfileContent + contextHash)[:32], nil
}
// readDockerignore reads the .dockerignore file in the context directory and
// returns the list of paths to exclude.
func readDockerignore(contextDir string, dockerfile string) ([]string, error) {
var (
f *os.File
err error
excludes = []string{}
)
dockerignorefilepath := dockerfile + ".dockerignore"
if filepath.IsAbs(dockerignorefilepath) {
f, err = os.Open(dockerignorefilepath)
} else {
f, err = os.Open(filepath.Join(contextDir, dockerignorefilepath))
}
if os.IsNotExist(err) {
dockerignorefilepath = ".dockerignore"
f, err = os.Open(filepath.Join(contextDir, dockerignorefilepath))
if os.IsNotExist(err) {
return ensureDockerIgnoreAndDockerFile(excludes, dockerfile, dockerignorefilepath), nil
} else if err != nil {
return nil, err
}
} else if err != nil {
return nil, err
}
defer f.Close()
excludes, err = dockerignore.ReadAll(f)
if err != nil {
return nil, err
}
return ensureDockerIgnoreAndDockerFile(excludes, dockerfile, dockerignorefilepath), nil
}
func ensureDockerIgnoreAndDockerFile(excludes []string, dockerfile, dockerignorefilepath string) []string {
_, dockerignorefile := filepath.Split(dockerignorefilepath)
if keep, _ := patternmatcher.MatchesOrParentMatches(dockerignorefile, excludes); keep {
excludes = append(excludes, "!"+dockerignorefile)
}
if keep, _ := patternmatcher.MatchesOrParentMatches(dockerfile, excludes); keep {
excludes = append(excludes, "!"+dockerfile)
}
return excludes
}