pkg/archive/spec.go (143 lines of code) (raw):
package archive
import (
"fmt"
"path/filepath"
"regexp"
"strings"
)
var (
nonAlnum = regexp.MustCompile(`[^a-zA-Z0-9]+`)
ExtensionMap = map[string]string{
"bionic": "deb",
"bookworm": "deb",
"bullseye": "deb",
"buster": "deb",
"focal": "deb",
"jammy": "deb",
"noble": "deb",
"rhel9": "rpm",
"rhel8": "rpm",
"mariner2": "rpm",
"windows": "zip",
}
OSMap = map[string]string{
"bookworm": "debian",
"bullseye": "debian",
"buster": "debian",
"bionic": "ubuntu",
"focal": "ubuntu",
"jammy": "ubuntu",
"noble": "ubuntu",
"rhel9": "el9",
"rhel8": "el8",
"mariner2": "cm2",
"windows": "windows",
}
VersionMap = map[string]string{
"bookworm": "12",
"bullseye": "11",
"buster": "10",
"bionic": "18.04",
"focal": "20.04",
"jammy": "22.04",
"noble": "24.04",
"rhel9": "el9",
"rhel8": "el8",
"mariner2": "cm2",
}
)
type Spec struct {
Pkg string `json:"package"`
Distro string `json:"distro"`
Arch string `json:"arch"`
Repo string `json:"repo"`
Commit string `json:"commit"`
Tag string `json:"tag"`
Revision string `json:"revision"`
}
// This function calculates the storage path for a package in the prod storage
// container.
func (spec *Spec) StoragePath() (string, error) {
pkg := spec.Pkg
pkgOS := spec.OS()
version := fmt.Sprintf("%s+azure", spec.Tag)
distro := spec.Distro
sanitizedArch := strings.ReplaceAll(spec.Arch, "/", "_")
base, err := spec.Basename()
if err != nil {
return "", err
}
storagePath := fmt.Sprintf("%s/%s/%s/%s_%s/%s", pkg, version, distro, pkgOS, sanitizedArch, base)
return storagePath, nil
}
// This logic is arbitrary, but the output must be reproducible. This is used
// to generate filenames for artifacts.
func (s *Spec) NameTagRevision() string {
pkg := s.Pkg
tag := s.Tag
rev := s.Revision
for _, ptr := range []*string{&pkg, &tag, &rev} {
*ptr = nonAlnum.ReplaceAllString(*ptr, "_")
}
return fmt.Sprintf("%s.%s.%s", pkg, tag, rev)
}
// Our pipelines have historically used opinionated filesystem layouts to place
// artifacts in a consistent location. This method will determine the directory
// structure for all path components except the basename of the artifact
// produced by this build spec definition. Because the base directory can be
// different depending on the situation, it is supplied as an argument. Use "."
// as the rootDir in order to use a relative path.
func (s *Spec) Dir(rootDir string) string {
pkgOS := s.OS()
sanitizedArch := strings.ReplaceAll(s.Arch, "/", "_")
osArchDir := fmt.Sprintf("%s_%s", pkgOS, sanitizedArch)
artifactDir := filepath.Join(rootDir, s.Distro, osArchDir)
return artifactDir
}
// There are semantic rules on the naming of packages for both debian- and rpm-
// based repositories. This method will generate the basename of the package
// name, according to those semantic rules, based on the information in the
// build spec.
func (s *Spec) Basename() (string, error) {
o, ok := OSMap[s.Distro]
if !ok {
return "", fmt.Errorf("Distro not understood: '%s'", s.Distro)
}
extension := ExtensionMap[s.Distro]
version := VersionMap[s.Distro]
sanitizedArch := strings.ReplaceAll(s.Arch, "/", "")
str := ""
switch o {
case "debian", "ubuntu":
str = fmt.Sprintf("%[1]s_%[2]s-%[3]s%[4]su%[5]s_%[6]s.%[7]s",
/* 1 */ s.Pkg,
/* 2 */ s.Tag,
/* 3 */ o,
/* 4 */ version,
/* 5 */ s.Revision,
/* 6 */ sanitizedArch,
/* 7 */ extension,
)
case "windows":
str = fmt.Sprintf("%[1]s-%[2]s+azure-u%[3]s.%[4]s.%[5]s",
/* 1 */ s.Pkg,
/* 2 */ s.Tag,
/* 3 */ s.Revision,
/* 4 */ sanitizedArch,
/* 5 */ extension,
)
default:
arch, ok := rpmArchMap[s.Arch]
if !ok {
arch = s.Arch
}
str = fmt.Sprintf("%[1]s-%[2]s-%[3]s.%[4]s.%[5]s.%[6]s",
/* 1 */ s.Pkg,
/* 2 */ s.Tag,
/* 3 */ s.Revision,
/* 4 */ o,
/* 5 */ arch,
/* 6 */ extension,
)
}
return str, nil
}
// This method is provided for convenience, simply combinging `.Dir()` and
// `.Basename()`. See the documentation for those methods for more information.
func (s *Spec) FullPath(rootDir string) (string, error) {
f, err := s.Basename()
if err != nil {
return "", err
}
return filepath.Join(s.Dir(rootDir), f), nil
}
func (s *Spec) OS() string {
if s.Distro == "windows" {
return "windows"
}
return "linux"
}