github.com/goreleaser/nfpm/v2@v2.44.0/nfpm.go (about) 1 // Package nfpm provides ways to package programs in some linux packaging 2 // formats. 3 package nfpm 4 5 import ( 6 "errors" 7 "fmt" 8 "io" 9 "io/fs" 10 "os" 11 "slices" 12 "sort" 13 "strings" 14 "sync" 15 "time" 16 17 "dario.cat/mergo" 18 "github.com/AlekSi/pointer" 19 "github.com/Masterminds/semver/v3" 20 "github.com/goreleaser/chglog" 21 "github.com/goreleaser/nfpm/v2/files" 22 "github.com/goreleaser/nfpm/v2/internal/modtime" 23 "gopkg.in/yaml.v3" 24 ) 25 26 // nolint: gochecknoglobals 27 var ( 28 packagers = map[string]Packager{} 29 lock sync.Mutex 30 ) 31 32 // RegisterPackager a new packager for the given format. 33 func RegisterPackager(format string, p Packager) { 34 lock.Lock() 35 defer lock.Unlock() 36 packagers[format] = p 37 } 38 39 // ClearPackagers clear all registered packagers, used for testing. 40 func ClearPackagers() { 41 lock.Lock() 42 defer lock.Unlock() 43 packagers = map[string]Packager{} 44 } 45 46 // ErrNoPackager happens when no packager is registered for the given format. 47 type ErrNoPackager struct { 48 format string 49 } 50 51 func (e ErrNoPackager) Error() string { 52 return fmt.Sprintf("no packager registered for the format %s", e.format) 53 } 54 55 // Get a packager for the given format. 56 func Get(format string) (Packager, error) { 57 p, ok := packagers[format] 58 if !ok { 59 return nil, ErrNoPackager{format} 60 } 61 return p, nil 62 } 63 64 // Enumerate lists the available packagers 65 func Enumerate() []string { 66 lock.Lock() 67 defer lock.Unlock() 68 69 list := make([]string, 0, len(packagers)) 70 for key := range packagers { 71 if key != "" { 72 list = append(list, key) 73 } 74 } 75 76 sort.Strings(list) 77 return list 78 } 79 80 // Parse decodes YAML data from an io.Reader into a configuration struct. 81 func Parse(in io.Reader) (config Config, err error) { 82 return ParseWithEnvMapping(in, os.Getenv) 83 } 84 85 // ParseWithEnvMapping decodes YAML data from an io.Reader into a configuration struct. 86 func ParseWithEnvMapping(in io.Reader, mapping func(string) string) (config Config, err error) { 87 dec := yaml.NewDecoder(in) 88 dec.KnownFields(true) 89 if err = dec.Decode(&config); err != nil { 90 return config, err 91 } 92 config.envMappingFunc = mapping 93 if config.envMappingFunc == nil { 94 config.envMappingFunc = func(s string) string { return s } 95 } 96 97 config.expandEnvVars() 98 WithDefaults(&config.Info) 99 return config, nil 100 } 101 102 // ParseFile decodes YAML data from a file path into a configuration struct. 103 func ParseFile(path string) (config Config, err error) { 104 if path == "-" { 105 return ParseWithEnvMapping(os.Stdin, os.Getenv) 106 } 107 return ParseFileWithEnvMapping(path, os.Getenv) 108 } 109 110 // ParseFileWithEnvMapping decodes YAML data from a file path into a configuration struct. 111 func ParseFileWithEnvMapping(path string, mapping func(string) string) (config Config, err error) { 112 var file *os.File 113 file, err = os.Open(path) //nolint:gosec 114 if err != nil { 115 return config, err 116 } 117 defer file.Close() // nolint: errcheck,gosec 118 return ParseWithEnvMapping(file, mapping) 119 } 120 121 // Packager represents any packager implementation. 122 type Packager interface { 123 Package(info *Info, w io.Writer) error 124 ConventionalFileName(info *Info) string 125 } 126 127 type PackagerWithExtension interface { 128 Packager 129 ConventionalExtension() string 130 } 131 132 // Config contains the top level configuration for packages. 133 type Config struct { 134 Info `yaml:",inline" json:",inline"` 135 Overrides map[string]*Overridables `yaml:"overrides,omitempty" json:"overrides,omitempty" jsonschema:"title=overrides,description=override some fields when packaging with a specific packager,enum=apk,enum=deb,enum=rpm"` 136 envMappingFunc func(string) string 137 } 138 139 // Get returns the Info struct for the given packager format. Overrides 140 // for the given format are merged into the final struct. 141 func (c *Config) Get(format string) (info *Info, err error) { 142 info = &Info{} 143 // make a deep copy of info 144 if err = mergo.Merge(info, c.Info, mergo.WithOverride); err != nil { 145 return nil, fmt.Errorf("failed to merge config into info: %w", err) 146 } 147 override, ok := c.Overrides[format] 148 if !ok { 149 // no overrides 150 return info, nil 151 } 152 if err = mergo.Merge(&info.Overridables, override, mergo.WithOverride); err != nil { 153 return nil, fmt.Errorf("failed to merge overrides into info: %w", err) 154 } 155 156 var contents []*files.Content 157 for _, f := range info.Contents { 158 if f.Packager == format || f.Packager == "" { 159 contents = append(contents, f) 160 } 161 } 162 info.Contents = contents 163 return info, nil 164 } 165 166 // Validate ensures that the config is well typed. 167 func (c *Config) Validate() error { 168 if err := Validate(&c.Info); err != nil { 169 return err 170 } 171 for format := range c.Overrides { 172 if _, err := Get(format); err != nil { 173 return err 174 } 175 } 176 return nil 177 } 178 179 func (c *Config) expandEnvVarsStringSlice(items []string) []string { 180 for i, dep := range items { 181 val := strings.TrimSpace(os.Expand(dep, c.envMappingFunc)) 182 items[i] = val 183 } 184 for i := 0; i < len(items); i++ { 185 if items[i] == "" { 186 items = slices.Delete(items, i, i+1) 187 i-- // Since we just deleted items[i], we must redo that index 188 } 189 } 190 191 return items 192 } 193 194 func (c *Config) expandEnvVarsContents(contents files.Contents) files.Contents { 195 for i := range contents { 196 f := contents[i] 197 if !f.Expand { 198 continue 199 } 200 f.Destination = strings.TrimSpace(os.Expand(f.Destination, c.envMappingFunc)) 201 f.Source = strings.TrimSpace(os.Expand(f.Source, c.envMappingFunc)) 202 } 203 return contents 204 } 205 206 func (c *Config) expandEnvVars() { 207 // Version related fields 208 c.Release = os.Expand(c.Release, c.envMappingFunc) 209 c.Version = os.Expand(c.Version, c.envMappingFunc) 210 c.Prerelease = os.Expand(c.Prerelease, c.envMappingFunc) 211 c.Platform = os.Expand(c.Platform, c.envMappingFunc) 212 c.Arch = os.Expand(c.Arch, c.envMappingFunc) 213 for or := range c.Overrides { 214 c.Overrides[or].Conflicts = c.expandEnvVarsStringSlice(c.Overrides[or].Conflicts) 215 c.Overrides[or].Depends = c.expandEnvVarsStringSlice(c.Overrides[or].Depends) 216 c.Overrides[or].Replaces = c.expandEnvVarsStringSlice(c.Overrides[or].Replaces) 217 c.Overrides[or].Recommends = c.expandEnvVarsStringSlice(c.Overrides[or].Recommends) 218 c.Overrides[or].Provides = c.expandEnvVarsStringSlice(c.Overrides[or].Provides) 219 c.Overrides[or].Suggests = c.expandEnvVarsStringSlice(c.Overrides[or].Suggests) 220 c.Overrides[or].Contents = c.expandEnvVarsContents(c.Overrides[or].Contents) 221 } 222 c.Conflicts = c.expandEnvVarsStringSlice(c.Conflicts) 223 c.Depends = c.expandEnvVarsStringSlice(c.Depends) 224 c.Replaces = c.expandEnvVarsStringSlice(c.Replaces) 225 c.Recommends = c.expandEnvVarsStringSlice(c.Recommends) 226 c.Provides = c.expandEnvVarsStringSlice(c.Provides) 227 c.Suggests = c.expandEnvVarsStringSlice(c.Suggests) 228 c.Contents = c.expandEnvVarsContents(c.Contents) 229 230 // Basic metadata fields 231 c.Name = os.Expand(c.Name, c.envMappingFunc) 232 c.Homepage = os.Expand(c.Homepage, c.envMappingFunc) 233 c.Maintainer = os.Expand(c.Maintainer, c.envMappingFunc) 234 c.Vendor = os.Expand(c.Vendor, c.envMappingFunc) 235 c.Description = os.Expand(c.Description, c.envMappingFunc) 236 237 // Package signing related fields 238 c.Deb.Signature.KeyFile = os.Expand(c.Deb.Signature.KeyFile, c.envMappingFunc) 239 c.RPM.Signature.KeyFile = os.Expand(c.RPM.Signature.KeyFile, c.envMappingFunc) 240 c.APK.Signature.KeyFile = os.Expand(c.APK.Signature.KeyFile, c.envMappingFunc) 241 c.Deb.Signature.KeyID = pointer.ToString(os.Expand(pointer.GetString(c.Deb.Signature.KeyID), c.envMappingFunc)) 242 c.RPM.Signature.KeyID = pointer.ToString(os.Expand(pointer.GetString(c.RPM.Signature.KeyID), c.envMappingFunc)) 243 c.APK.Signature.KeyID = pointer.ToString(os.Expand(pointer.GetString(c.APK.Signature.KeyID), c.envMappingFunc)) 244 245 // Package signing passphrase 246 generalPassphrase := os.Expand("$NFPM_PASSPHRASE", c.envMappingFunc) 247 c.Deb.Signature.KeyPassphrase = generalPassphrase 248 c.RPM.Signature.KeyPassphrase = generalPassphrase 249 c.APK.Signature.KeyPassphrase = generalPassphrase 250 251 debPassphrase := os.Expand("$NFPM_DEB_PASSPHRASE", c.envMappingFunc) 252 if debPassphrase != "" { 253 c.Deb.Signature.KeyPassphrase = debPassphrase 254 } 255 256 rpmPassphrase := os.Expand("$NFPM_RPM_PASSPHRASE", c.envMappingFunc) 257 if rpmPassphrase != "" { 258 c.RPM.Signature.KeyPassphrase = rpmPassphrase 259 } 260 261 apkPassphrase := os.Expand("$NFPM_APK_PASSPHRASE", c.envMappingFunc) 262 if apkPassphrase != "" { 263 c.APK.Signature.KeyPassphrase = apkPassphrase 264 } 265 266 // RPM specific 267 c.RPM.Packager = os.Expand(c.RPM.Packager, c.envMappingFunc) 268 269 // Deb specific 270 for k, v := range c.Deb.Fields { 271 c.Deb.Fields[k] = os.Expand(v, c.envMappingFunc) 272 } 273 c.Deb.Predepends = c.expandEnvVarsStringSlice(c.Deb.Predepends) 274 275 // IPK specific 276 for k, v := range c.IPK.Fields { 277 c.IPK.Fields[k] = os.Expand(v, c.envMappingFunc) 278 } 279 c.IPK.Predepends = c.expandEnvVarsStringSlice(c.IPK.Predepends) 280 281 // RPM specific 282 c.RPM.Packager = os.Expand(c.RPM.Packager, c.envMappingFunc) 283 } 284 285 // Info contains information about a single package. 286 type Info struct { 287 Overridables `yaml:",inline" json:",inline"` 288 Name string `yaml:"name" json:"name" jsonschema:"title=package name"` 289 Arch string `yaml:"arch" json:"arch" jsonschema:"title=target architecture,example=amd64"` 290 Platform string `yaml:"platform,omitempty" json:"platform,omitempty" jsonschema:"title=target platform,example=linux,default=linux"` 291 Epoch string `yaml:"epoch,omitempty" json:"epoch,omitempty" jsonschema:"title=version epoch,example=2,default=extracted from version"` 292 Version string `yaml:"version" json:"version" jsonschema:"title=version,example=v1.0.2,example=2.0.1"` 293 VersionSchema string `yaml:"version_schema,omitempty" json:"version_schema,omitempty" jsonschema:"title=version schema,enum=semver,enum=none,default=semver"` 294 Release string `yaml:"release,omitempty" json:"release,omitempty" jsonschema:"title=version release,example=1"` 295 Prerelease string `yaml:"prerelease,omitempty" json:"prerelease,omitempty" jsonschema:"title=version prerelease,default=extracted from version"` 296 VersionMetadata string `yaml:"version_metadata,omitempty" json:"version_metadata,omitempty" jsonschema:"title=version metadata,example=git"` 297 Section string `yaml:"section,omitempty" json:"section,omitempty" jsonschema:"title=package section,example=default"` 298 Priority string `yaml:"priority,omitempty" json:"priority,omitempty" jsonschema:"title=package priority,example=extra"` 299 Maintainer string `yaml:"maintainer,omitempty" json:"maintainer,omitempty" jsonschema:"title=package maintainer,example=me@example.com"` 300 Description string `yaml:"description,omitempty" json:"description,omitempty" jsonschema:"title=package description"` 301 Vendor string `yaml:"vendor,omitempty" json:"vendor,omitempty" jsonschema:"title=package vendor,example=MyCorp"` 302 Homepage string `yaml:"homepage,omitempty" json:"homepage,omitempty" jsonschema:"title=package homepage,example=https://example.com"` 303 License string `yaml:"license,omitempty" json:"license,omitempty" jsonschema:"title=package license,example=MIT"` 304 Changelog string `yaml:"changelog,omitempty" json:"changelog,omitempty" jsonschema:"title=package changelog,example=changelog.yaml,description=see https://github.com/goreleaser/chglog for more details"` 305 DisableGlobbing bool `yaml:"disable_globbing,omitempty" json:"disable_globbing,omitempty" jsonschema:"title=whether to disable file globbing,default=false"` 306 MTime time.Time `yaml:"mtime,omitempty" json:"mtime,omitempty" jsonschema:"title=time to set into the files generated by nFPM"` 307 Target string `yaml:"-" json:"-"` 308 } 309 310 func (i *Info) Validate() error { 311 return Validate(i) 312 } 313 314 // GetChangeLog parses the provided changelog file. 315 func (i *Info) GetChangeLog() (log *chglog.PackageChangeLog, err error) { 316 // if the file does not exist chglog.Parse will just silently 317 // create an empty changelog but we should notify the user instead 318 if _, err = os.Stat(i.Changelog); errors.Is(err, fs.ErrNotExist) { 319 return nil, err 320 } 321 322 entries, err := chglog.Parse(i.Changelog) 323 if err != nil { 324 return nil, err 325 } 326 327 return &chglog.PackageChangeLog{ 328 Name: i.Name, 329 Entries: entries, 330 }, nil 331 } 332 333 func (i *Info) parseSemver() { 334 // parse the version as a semver so we can properly split the parts 335 // and support proper ordering for both rpm and deb 336 if v, err := semver.NewVersion(i.Version); err == nil { 337 i.Version = fmt.Sprintf("%d.%d.%d", v.Major(), v.Minor(), v.Patch()) 338 if i.Prerelease == "" { 339 i.Prerelease = v.Prerelease() 340 } 341 342 if i.VersionMetadata == "" { 343 i.VersionMetadata = v.Metadata() 344 } 345 } 346 } 347 348 // Overridables contain the field which are overridable in a package. 349 type Overridables struct { 350 Replaces []string `yaml:"replaces,omitempty" json:"replaces,omitempty" jsonschema:"title=replaces directive,example=nfpm"` 351 Provides []string `yaml:"provides,omitempty" json:"provides,omitempty" jsonschema:"title=provides directive,example=nfpm"` 352 Depends []string `yaml:"depends,omitempty" json:"depends,omitempty" jsonschema:"title=depends directive,example=nfpm"` 353 Recommends []string `yaml:"recommends,omitempty" json:"recommends,omitempty" jsonschema:"title=recommends directive,example=nfpm"` 354 Suggests []string `yaml:"suggests,omitempty" json:"suggests,omitempty" jsonschema:"title=suggests directive,example=nfpm"` 355 Conflicts []string `yaml:"conflicts,omitempty" json:"conflicts,omitempty" jsonschema:"title=conflicts directive,example=nfpm"` 356 Contents files.Contents `yaml:"contents,omitempty" json:"contents,omitempty" jsonschema:"title=files to add to the package"` 357 Umask os.FileMode `yaml:"umask,omitempty" json:"umask,omitempty" jsonschema:"title=umask for file contents,example=112"` 358 Scripts Scripts `yaml:"scripts,omitempty" json:"scripts,omitempty" jsonschema:"title=scripts to execute"` 359 RPM RPM `yaml:"rpm,omitempty" json:"rpm,omitempty" jsonschema:"title=rpm-specific settings"` 360 Deb Deb `yaml:"deb,omitempty" json:"deb,omitempty" jsonschema:"title=deb-specific settings"` 361 APK APK `yaml:"apk,omitempty" json:"apk,omitempty" jsonschema:"title=apk-specific settings"` 362 ArchLinux ArchLinux `yaml:"archlinux,omitempty" json:"archlinux,omitempty" jsonschema:"title=archlinux-specific settings"` 363 IPK IPK `yaml:"ipk,omitempty" json:"ipk,omitempty" jsonschema:"title=ipk-specific settings"` 364 } 365 366 type ArchLinux struct { 367 Pkgbase string `yaml:"pkgbase,omitempty" json:"pkgbase,omitempty" jsonschema:"title=explicitly specify the name used to refer to a split package, defaults to name"` 368 Arch string `yaml:"arch,omitempty" json:"arch,omitempty" jsonschema:"title=architecture in archlinux nomenclature"` 369 Packager string `yaml:"packager,omitempty" json:"packager,omitempty" jsonschema:"title=organization that packaged the software"` 370 Scripts ArchLinuxScripts `yaml:"scripts,omitempty" json:"scripts,omitempty" jsonschema:"title=archlinux-specific scripts"` 371 } 372 373 type ArchLinuxScripts struct { 374 PreUpgrade string `yaml:"preupgrade,omitempty" json:"preupgrade,omitempty" jsonschema:"title=preupgrade script"` 375 PostUpgrade string `yaml:"postupgrade,omitempty" json:"postupgrade,omitempty" jsonschema:"title=postupgrade script"` 376 } 377 378 // RPM is custom configs that are only available on RPM packages. 379 type RPM struct { 380 Arch string `yaml:"arch,omitempty" json:"arch,omitempty" jsonschema:"title=architecture in rpm nomenclature"` 381 BuildHost string `yaml:"buildhost,omitempty" json:"buildhost,omitempty" jsonschema:"title=host name of the build environment, default=os.Hostname()"` 382 Scripts RPMScripts `yaml:"scripts,omitempty" json:"scripts,omitempty" jsonschema:"title=rpm-specific scripts"` 383 Group string `yaml:"group,omitempty" json:"group,omitempty" jsonschema:"title=package group,example=Unspecified"` 384 Summary string `yaml:"summary,omitempty" json:"summary,omitempty" jsonschema:"title=package summary"` 385 Compression string `yaml:"compression,omitempty" json:"compression,omitempty" jsonschema:"title=compression algorithm to be used,enum=gzip,enum=lzma,enum=xz,enum=zstd,default=gzip:-1"` 386 Signature RPMSignature `yaml:"signature,omitempty" json:"signature,omitempty" jsonschema:"title=rpm signature"` 387 Packager string `yaml:"packager,omitempty" json:"packager,omitempty" jsonschema:"title=organization that actually packaged the software"` 388 Prefixes []string `yaml:"prefixes,omitempty" json:"prefixes,omitempty" jsonschema:"title=Prefixes for relocatable packages"` 389 } 390 391 // RPMScripts represents scripts only available on RPM packages. 392 type RPMScripts struct { 393 PreTrans string `yaml:"pretrans,omitempty" json:"pretrans,omitempty" jsonschema:"title=pretrans script"` 394 PostTrans string `yaml:"posttrans,omitempty" json:"posttrans,omitempty" jsonschema:"title=posttrans script"` 395 Verify string `yaml:"verify,omitempty" json:"verify,omitempty" jsonschema:"title=verify script"` 396 } 397 398 type PackageSignature struct { 399 // PGP secret key, can be ASCII-armored 400 KeyFile string `yaml:"key_file,omitempty" json:"key_file,omitempty" jsonschema:"title=key file,example=key.gpg"` 401 KeyID *string `yaml:"key_id,omitempty" json:"key_id,omitempty" jsonschema:"title=key id,example=bc8acdd415bd80b3"` 402 KeyPassphrase string `yaml:"-" json:"-"` // populated from environment variable 403 // SignFn, if set, will be called with the package-specific data to sign. 404 // For deb and rpm packages, data is the full package content. 405 // For apk packages, data is the SHA1 digest of control tgz. 406 // 407 // This allows for signing implementations other than using a local file 408 // (for example using a remote signer like KMS). 409 SignFn func(data io.Reader) ([]byte, error) `yaml:"-" json:"-"` // populated when used as a library 410 } 411 412 type RPMSignature struct { 413 PackageSignature `yaml:",inline" json:",inline"` 414 } 415 416 type APK struct { 417 Arch string `yaml:"arch,omitempty" json:"arch,omitempty" jsonschema:"title=architecture in apk nomenclature"` 418 Signature APKSignature `yaml:"signature,omitempty" json:"signature,omitempty" jsonschema:"title=apk signature"` 419 Scripts APKScripts `yaml:"scripts,omitempty" json:"scripts,omitempty" jsonschema:"title=apk scripts"` 420 } 421 422 type APKSignature struct { 423 PackageSignature `yaml:",inline" json:",inline"` 424 // defaults to <maintainer email>.rsa.pub 425 KeyName string `yaml:"key_name,omitempty" json:"key_name,omitempty" jsonschema:"title=key name,example=origin,default=maintainer_email.rsa.pub"` 426 } 427 428 type APKScripts struct { 429 PreUpgrade string `yaml:"preupgrade,omitempty" json:"preupgrade,omitempty" jsonschema:"title=pre upgrade script"` 430 PostUpgrade string `yaml:"postupgrade,omitempty" json:"postupgrade,omitempty" jsonschema:"title=post upgrade script"` 431 } 432 433 // Deb is custom configs that are only available on deb packages. 434 type Deb struct { 435 Arch string `yaml:"arch,omitempty" json:"arch,omitempty" jsonschema:"title=architecture in deb nomenclature"` 436 Scripts DebScripts `yaml:"scripts,omitempty" json:"scripts,omitempty" jsonschema:"title=scripts"` 437 Triggers DebTriggers `yaml:"triggers,omitempty" json:"triggers,omitempty" jsonschema:"title=triggers"` 438 Breaks []string `yaml:"breaks,omitempty" json:"breaks,omitempty" jsonschema:"title=breaks"` 439 Signature DebSignature `yaml:"signature,omitempty" json:"signature,omitempty" jsonschema:"title=signature"` 440 Compression string `yaml:"compression,omitempty" json:"compression,omitempty" jsonschema:"title=compression algorithm to be used,enum=gzip,enum=xz,enum=zstd,enum=none,default=gzip:-1"` 441 Fields map[string]string `yaml:"fields,omitempty" json:"fields,omitempty" jsonschema:"title=fields"` 442 Predepends []string `yaml:"predepends,omitempty" json:"predepends,omitempty" jsonschema:"title=predepends directive,example=nfpm"` 443 } 444 445 type DebSignature struct { 446 PackageSignature `yaml:",inline" json:",inline"` 447 // Only debsign still supported 448 Method string `yaml:"method,omitempty" json:"method,omitempty" jsonschema:"title=method role,enum=dpkg-sig,enum=debsign,default=debsign"` 449 // origin, maint or archive (defaults to origin) 450 Type string `yaml:"type,omitempty" json:"type,omitempty" jsonschema:"title=signer role,enum=origin,enum=maint,enum=archive,default=origin"` 451 Signer string `yaml:"signer,omitempty" json:"signer,omitempty" jsonschema:"title=signer"` 452 } 453 454 // DebTriggers contains triggers only available for deb packages. 455 // https://wiki.debian.org/DpkgTriggers 456 // https://man7.org/linux/man-pages/man5/deb-triggers.5.html 457 type DebTriggers struct { 458 Interest []string `yaml:"interest,omitempty" json:"interest,omitempty" jsonschema:"title=interest"` 459 InterestAwait []string `yaml:"interest_await,omitempty" json:"interest_await,omitempty" jsonschema:"title=interest await"` 460 InterestNoAwait []string `yaml:"interest_noawait,omitempty" json:"interest_noawait,omitempty" jsonschema:"title=interest noawait"` 461 Activate []string `yaml:"activate,omitempty" json:"activate,omitempty" jsonschema:"title=activate"` 462 ActivateAwait []string `yaml:"activate_await,omitempty" json:"activate_await,omitempty" jsonschema:"title=activate await"` 463 ActivateNoAwait []string `yaml:"activate_noawait,omitempty" json:"activate_noawait,omitempty" jsonschema:"title=activate noawait"` 464 } 465 466 // DebScripts is scripts only available on deb packages. 467 type DebScripts struct { 468 Rules string `yaml:"rules,omitempty" json:"rules,omitempty" jsonschema:"title=rules"` 469 Templates string `yaml:"templates,omitempty" json:"templates,omitempty" jsonschema:"title=templates"` 470 Config string `yaml:"config,omitempty" json:"config,omitempty" jsonschema:"title=config"` 471 } 472 473 // IPK is custom configs that are only available on deb packages. 474 type IPK struct { 475 ABIVersion string `yaml:"abi_version,omitempty" json:"abi_version,omitempty" jsonschema:"title=abi version"` 476 Alternatives []IPKAlternative `yaml:"alternatives,omitempty" json:"alternatives,omitempty" jsonschema:"title=alternatives"` 477 Arch string `yaml:"arch,omitempty" json:"arch,omitempty" jsonschema:"title=architecture in deb nomenclature"` 478 AutoInstalled bool `yaml:"auto_installed,omitempty" json:"auto_installed,omitempty" jsonschema:"title=auto installed,default=false"` 479 Essential bool `yaml:"essential,omitempty" json:"essential,omitempty" jsonschema:"title=whether package is essential,default=false"` 480 Fields map[string]string `yaml:"fields,omitempty" json:"fields,omitempty" jsonschema:"title=fields"` 481 Predepends []string `yaml:"predepends,omitempty" json:"predepends,omitempty" jsonschema:"title=predepends directive,example=nfpm"` 482 Tags []string `yaml:"tags,omitempty" json:"tags,omitempty" jsonschema:"title=tags"` 483 } 484 485 // IPKAlternative represents an alternative for an IPK package. 486 type IPKAlternative struct { 487 Priority int `yaml:"priority,omitempty" json:"priority,omitempty" jsonschema:"title=priority"` 488 Target string `yaml:"target,omitempty" json:"target,omitempty" jsonschema:"title=target"` 489 LinkName string `yaml:"link_name,omitempty" json:"link_name,omitempty" jsonschema:"title=link name"` 490 } 491 492 // Scripts contains information about maintainer scripts for packages. 493 type Scripts struct { 494 PreInstall string `yaml:"preinstall,omitempty" json:"preinstall,omitempty" jsonschema:"title=pre install"` 495 PostInstall string `yaml:"postinstall,omitempty" json:"postinstall,omitempty" jsonschema:"title=post install"` 496 PreRemove string `yaml:"preremove,omitempty" json:"preremove,omitempty" jsonschema:"title=pre remove"` 497 PostRemove string `yaml:"postremove,omitempty" json:"postremove,omitempty" jsonschema:"title=post remove"` 498 } 499 500 // ErrFieldEmpty happens when some required field is empty. 501 type ErrFieldEmpty struct { 502 field string 503 } 504 505 func (e ErrFieldEmpty) Error() string { 506 return fmt.Sprintf("package %s must be provided", e.field) 507 } 508 509 // PrepareForPackager validates the configuration for the given packager and 510 // prepares the contents for said packager. 511 func PrepareForPackager(info *Info, packager string) (err error) { 512 if info.Name == "" { 513 return ErrFieldEmpty{"name"} 514 } 515 516 if info.Arch == "" && 517 ((packager == "deb" && info.Deb.Arch == "") || 518 (packager == "rpm" && info.RPM.Arch == "") || 519 (packager == "apk" && info.APK.Arch == "")) { 520 return ErrFieldEmpty{"arch"} 521 } 522 if info.Version == "" { 523 return ErrFieldEmpty{"version"} 524 } 525 526 info.Contents, err = files.PrepareForPackager( 527 info.Contents, 528 info.Umask, 529 packager, 530 info.DisableGlobbing, 531 info.MTime, 532 ) 533 534 return err 535 } 536 537 // Validate the given Info and returns an error if it is invalid. Validate will 538 // no change the info's contents. 539 func Validate(info *Info) (err error) { 540 if info.Name == "" { 541 return ErrFieldEmpty{"name"} 542 } 543 if info.Arch == "" && (info.Deb.Arch == "" || info.RPM.Arch == "" || info.APK.Arch == "") { 544 return ErrFieldEmpty{"arch"} 545 } 546 if info.Version == "" { 547 return ErrFieldEmpty{"version"} 548 } 549 550 for packager := range packagers { 551 _, err := files.PrepareForPackager( 552 info.Contents, 553 info.Umask, 554 packager, 555 info.DisableGlobbing, 556 info.MTime, 557 ) 558 if err != nil { 559 return err 560 } 561 } 562 563 return nil 564 } 565 566 // WithDefaults set some sane defaults into the given Info. 567 func WithDefaults(info *Info) *Info { 568 if info.Platform == "" { 569 info.Platform = "linux" 570 } 571 if info.Description == "" { 572 info.Description = "no description given" 573 } 574 if info.Arch == "" { 575 info.Arch = "amd64" 576 } 577 if strings.HasPrefix(info.Arch, "mips") { 578 info.Arch = strings.NewReplacer( 579 "softfloat", "", 580 "hardfloat", "", 581 ).Replace(info.Arch) 582 } 583 if info.Version == "" { 584 info.Version = "v0.0.0-rc0" 585 } 586 if info.Umask == 0 { 587 info.Umask = 0o02 588 } 589 if info.MTime.IsZero() { 590 info.MTime = modtime.FromEnv() 591 } 592 switch info.VersionSchema { 593 case "none": 594 // No change to the version or prerelease info set in the YAML file 595 break 596 case "semver": 597 fallthrough 598 default: 599 info.parseSemver() 600 } 601 602 return info 603 } 604 605 // ErrSigningFailure is returned whenever something went wrong during 606 // the package signing process. The underlying error can be unwrapped 607 // and could be crypto-related or something that occurred while adding 608 // the signature to the package. 609 type ErrSigningFailure struct { 610 Err error 611 } 612 613 func (s *ErrSigningFailure) Error() string { 614 return fmt.Sprintf("signing error: %v", s.Err) 615 } 616 617 func (s *ErrSigningFailure) Unwarp() error { 618 return s.Err 619 }