magefiles/build/checker.go (94 lines of code) (raw):

package build import ( "errors" "fmt" "os" "os/exec" "regexp" "strings" ) const ( skopeoImage = "quay.io/skopeo/stable:v1.12.0" ) var skopeoErrorMessageRegex = regexp.MustCompile(`time=".+"\slevel=\w+\smsg="(?P<message>.+)"`) type ResourceChecker interface { Exists() error } func NewResourceChecker(c Component) ResourceChecker { switch c.Type() { case TypeDockerImage: return newDockerImageChecker(c.Value()) case TypeFile: return newFileChecker(c.Value()) case TypeDockerImageArchive: return newFileChecker(c.Value()) case TypeOSBinary: return newBinaryPathChecker(c.Value()) case TypeMacOSPackage: return newBinaryPathChecker(c.Value()) default: return unknownResourceChecker{} } } type unknownResourceChecker struct { } func (unknownResourceChecker) Exists() error { return errors.New("unknown") } type fileChecker struct { file string } func newFileChecker(f string) fileChecker { return fileChecker{file: f} } func (f fileChecker) Exists() error { _, err := os.Stat(f.file) if err != nil { substr := fmt.Sprintf("stat %s: ", f.file) if strings.HasPrefix(err.Error(), substr) { return errors.New(strings.Replace(err.Error(), substr, "", 1)) } } return err } type dockerImageChecker struct { image string } func newDockerImageChecker(image string) *dockerImageChecker { return &dockerImageChecker{image: image} } func (d *dockerImageChecker) Exists() error { // the results of this function can be cached but there's no need atm args := []string{ "inspect", "--raw", "--no-tags", "docker://" + d.image, } command := "skopeo" _, err := exec.LookPath(command) if err != nil { command = "docker" args = append([]string{"run", "--rm", skopeoImage}, args...) } out, err := exec.Command(command, args...).CombinedOutput() if err == nil { return nil } if strings.Contains(string(out), "manifest unknown") { return errors.New("manifest unknown") } // parse skopeo error message such as // time="2023-10-10T22:45:14+03:00" level=fatal msg="Error parsing image name \"docker://gitlab-runner:bleeding\": // reading manifest bleeding in docker.io/library/gitlab-runner: requested access to the resource is denied" matches := skopeoErrorMessageRegex.FindStringSubmatch(string(out)) if len(matches) == 0 { return errors.New(string(out)) } errMessage := matches[skopeoErrorMessageRegex.SubexpIndex("message")] return errors.New(errMessage) } type binaryPathChecker struct { bin string } func newBinaryPathChecker(bin string) *binaryPathChecker { return &binaryPathChecker{bin: bin} } func (b *binaryPathChecker) Exists() error { _, err := exec.LookPath(b.bin) return err }