github.com/joselitofilho/goreleaser@v0.155.1-0.20210123221854-e4891856c593/internal/pipe/nfpm/nfpm.go (about) 1 // Package nfpm implements the Pipe interface providing nFPM bindings. 2 package nfpm 3 4 import ( 5 "fmt" 6 "os" 7 "path/filepath" 8 "strings" 9 10 "github.com/apex/log" 11 "github.com/goreleaser/nfpm/v2" 12 _ "github.com/goreleaser/nfpm/v2/apk" // blank import to register the format 13 _ "github.com/goreleaser/nfpm/v2/deb" // blank import to register the format 14 "github.com/goreleaser/nfpm/v2/files" 15 _ "github.com/goreleaser/nfpm/v2/rpm" // blank import to register the format 16 "github.com/imdario/mergo" 17 18 "github.com/goreleaser/goreleaser/internal/artifact" 19 "github.com/goreleaser/goreleaser/internal/deprecate" 20 "github.com/goreleaser/goreleaser/internal/ids" 21 "github.com/goreleaser/goreleaser/internal/linux" 22 "github.com/goreleaser/goreleaser/internal/pipe" 23 "github.com/goreleaser/goreleaser/internal/semerrgroup" 24 "github.com/goreleaser/goreleaser/internal/tmpl" 25 "github.com/goreleaser/goreleaser/pkg/config" 26 "github.com/goreleaser/goreleaser/pkg/context" 27 ) 28 29 const defaultNameTemplate = "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}{{ if .Mips }}_{{ .Mips }}{{ end }}" 30 31 // Pipe for nfpm packaging. 32 type Pipe struct{} 33 34 func (Pipe) String() string { 35 return "linux packages" 36 } 37 38 // Default sets the pipe defaults. 39 func (Pipe) Default(ctx *context.Context) error { 40 var ids = ids.New("nfpms") 41 for i := range ctx.Config.NFPMs { 42 var fpm = &ctx.Config.NFPMs[i] 43 if fpm.ID == "" { 44 fpm.ID = "default" 45 } 46 if fpm.Bindir == "" { 47 fpm.Bindir = "/usr/local/bin" 48 } 49 if fpm.PackageName == "" { 50 fpm.PackageName = ctx.Config.ProjectName 51 } 52 if fpm.FileNameTemplate == "" { 53 fpm.FileNameTemplate = defaultNameTemplate 54 } 55 if len(fpm.Files) > 0 { 56 for src, dst := range fpm.Files { 57 fpm.Contents = append(fpm.Contents, &files.Content{ 58 Source: src, 59 Destination: dst, 60 }) 61 } 62 deprecate.Notice(ctx, "nfpms.files") 63 } 64 if len(fpm.ConfigFiles) > 0 { 65 for src, dst := range fpm.ConfigFiles { 66 fpm.Contents = append(fpm.Contents, &files.Content{ 67 Source: src, 68 Destination: dst, 69 Type: "config", 70 }) 71 } 72 deprecate.Notice(ctx, "nfpms.config_files") 73 } 74 if len(fpm.Symlinks) > 0 { 75 for src, dst := range fpm.Symlinks { 76 fpm.Contents = append(fpm.Contents, &files.Content{ 77 Source: src, 78 Destination: dst, 79 Type: "symlink", 80 }) 81 } 82 deprecate.Notice(ctx, "nfpms.symlinks") 83 } 84 if len(fpm.RPM.GhostFiles) > 0 { 85 for _, dst := range fpm.RPM.GhostFiles { 86 fpm.Contents = append(fpm.Contents, &files.Content{ 87 Destination: dst, 88 Type: "ghost", 89 Packager: "rpm", 90 }) 91 } 92 deprecate.Notice(ctx, "nfpms.rpm.ghost_files") 93 } 94 if len(fpm.RPM.ConfigNoReplaceFiles) > 0 { 95 for src, dst := range fpm.RPM.ConfigNoReplaceFiles { 96 fpm.Contents = append(fpm.Contents, &files.Content{ 97 Source: src, 98 Destination: dst, 99 Type: "config|noreplace", 100 Packager: "rpm", 101 }) 102 } 103 deprecate.Notice(ctx, "nfpms.rpm.config_noreplace_files") 104 } 105 if fpm.Deb.VersionMetadata != "" { 106 deprecate.Notice(ctx, "nfpms.deb.version_metadata") 107 fpm.VersionMetadata = fpm.Deb.VersionMetadata 108 } 109 110 if len(fpm.Builds) == 0 { 111 for _, b := range ctx.Config.Builds { 112 fpm.Builds = append(fpm.Builds, b.ID) 113 } 114 } 115 ids.Inc(fpm.ID) 116 } 117 return ids.Validate() 118 } 119 120 // Run the pipe. 121 func (Pipe) Run(ctx *context.Context) error { 122 for _, nfpm := range ctx.Config.NFPMs { 123 if len(nfpm.Formats) == 0 { 124 // FIXME: this assumes other nfpm configs will fail too... 125 return pipe.Skip("no output formats configured") 126 } 127 if err := doRun(ctx, nfpm); err != nil { 128 return err 129 } 130 } 131 return nil 132 } 133 134 func doRun(ctx *context.Context, fpm config.NFPM) error { 135 var linuxBinaries = ctx.Artifacts.Filter(artifact.And( 136 artifact.ByType(artifact.Binary), 137 artifact.ByGoos("linux"), 138 artifact.ByIDs(fpm.Builds...), 139 )).GroupByPlatform() 140 if len(linuxBinaries) == 0 { 141 return fmt.Errorf("no linux binaries found for builds %v", fpm.Builds) 142 } 143 var g = semerrgroup.New(ctx.Parallelism) 144 for _, format := range fpm.Formats { 145 for platform, artifacts := range linuxBinaries { 146 format := format 147 arch := linux.Arch(platform) 148 artifacts := artifacts 149 g.Go(func() error { 150 return create(ctx, fpm, format, arch, artifacts) 151 }) 152 } 153 } 154 return g.Wait() 155 } 156 157 func mergeOverrides(fpm config.NFPM, format string) (*config.NFPMOverridables, error) { 158 var overridden config.NFPMOverridables 159 if err := mergo.Merge(&overridden, fpm.NFPMOverridables); err != nil { 160 return nil, err 161 } 162 perFormat, ok := fpm.Overrides[format] 163 if ok { 164 err := mergo.Merge(&overridden, perFormat, mergo.WithOverride) 165 if err != nil { 166 return nil, err 167 } 168 } 169 return &overridden, nil 170 } 171 172 func create(ctx *context.Context, fpm config.NFPM, format, arch string, binaries []*artifact.Artifact) error { 173 overridden, err := mergeOverrides(fpm, format) 174 if err != nil { 175 return err 176 } 177 name, err := tmpl.New(ctx). 178 WithArtifact(binaries[0], overridden.Replacements). 179 WithExtraFields(tmpl.Fields{ 180 "Release": fpm.Release, 181 "Epoch": fpm.Epoch, 182 }). 183 Apply(overridden.FileNameTemplate) 184 if err != nil { 185 return err 186 } 187 188 var contents = append(files.Contents{}, overridden.Contents...) 189 190 // FPM meta package should not contain binaries at all 191 if !fpm.Meta { 192 var log = log.WithField("package", name+"."+format).WithField("arch", arch) 193 for _, binary := range binaries { 194 src := binary.Path 195 dst := filepath.Join(fpm.Bindir, binary.Name) 196 log.WithField("src", src).WithField("dst", dst).Debug("adding binary to package") 197 contents = append(contents, &files.Content{ 198 Source: src, 199 Destination: dst, 200 }) 201 } 202 } 203 204 log.WithField("files", destinations(contents)).Debug("all archive files") 205 206 var info = &nfpm.Info{ 207 Arch: arch, 208 Platform: "linux", 209 Name: fpm.PackageName, 210 Version: ctx.Version, 211 Section: fpm.Section, 212 Priority: fpm.Priority, 213 Epoch: fpm.Epoch, 214 Release: fpm.Release, 215 Prerelease: fpm.Prerelease, 216 VersionMetadata: fpm.VersionMetadata, 217 Maintainer: fpm.Maintainer, 218 Description: fpm.Description, 219 Vendor: fpm.Vendor, 220 Homepage: fpm.Homepage, 221 License: fpm.License, 222 Overridables: nfpm.Overridables{ 223 Conflicts: overridden.Conflicts, 224 Depends: overridden.Dependencies, 225 Recommends: overridden.Recommends, 226 Suggests: overridden.Suggests, 227 Replaces: overridden.Replaces, 228 EmptyFolders: overridden.EmptyFolders, 229 Contents: contents, 230 Scripts: nfpm.Scripts{ 231 PreInstall: overridden.Scripts.PreInstall, 232 PostInstall: overridden.Scripts.PostInstall, 233 PreRemove: overridden.Scripts.PreRemove, 234 PostRemove: overridden.Scripts.PostRemove, 235 }, 236 Deb: nfpm.Deb{ 237 Scripts: nfpm.DebScripts{ 238 Rules: overridden.Deb.Scripts.Rules, 239 Templates: overridden.Deb.Scripts.Templates, 240 }, 241 Triggers: nfpm.DebTriggers{ 242 Interest: overridden.Deb.Triggers.Interest, 243 InterestAwait: overridden.Deb.Triggers.InterestAwait, 244 InterestNoAwait: overridden.Deb.Triggers.InterestNoAwait, 245 Activate: overridden.Deb.Triggers.Activate, 246 ActivateAwait: overridden.Deb.Triggers.ActivateAwait, 247 ActivateNoAwait: overridden.Deb.Triggers.ActivateNoAwait, 248 }, 249 Breaks: overridden.Deb.Breaks, 250 Signature: nfpm.DebSignature{ 251 KeyFile: overridden.Deb.Signature.KeyFile, 252 KeyPassphrase: getPassphraseFromEnv(ctx, "DEB", fpm.ID), 253 Type: overridden.Deb.Signature.Type, 254 }, 255 }, 256 RPM: nfpm.RPM{ 257 Summary: overridden.RPM.Summary, 258 Group: overridden.RPM.Group, 259 Compression: overridden.RPM.Compression, 260 Signature: nfpm.RPMSignature{ 261 KeyFile: overridden.RPM.Signature.KeyFile, 262 KeyPassphrase: getPassphraseFromEnv(ctx, "RPM", fpm.ID), 263 }, 264 }, 265 APK: nfpm.APK{ 266 Signature: nfpm.APKSignature{ 267 KeyFile: overridden.APK.Signature.KeyFile, 268 KeyPassphrase: getPassphraseFromEnv(ctx, "APK", fpm.ID), 269 KeyName: overridden.APK.Signature.KeyName, 270 }, 271 }, 272 }, 273 } 274 275 if ctx.SkipSign { 276 info.APK.Signature = nfpm.APKSignature{} 277 info.RPM.Signature = nfpm.RPMSignature{} 278 info.Deb.Signature = nfpm.DebSignature{} 279 } 280 281 if err = nfpm.Validate(info); err != nil { 282 return fmt.Errorf("invalid nfpm config: %w", err) 283 } 284 285 packager, err := nfpm.Get(format) 286 if err != nil { 287 return err 288 } 289 290 var path = filepath.Join(ctx.Config.Dist, name+"."+format) 291 log.WithField("file", path).Info("creating") 292 w, err := os.Create(path) 293 if err != nil { 294 return err 295 } 296 defer w.Close() 297 if err := packager.Package(nfpm.WithDefaults(info), w); err != nil { 298 return fmt.Errorf("nfpm failed: %w", err) 299 } 300 if err := w.Close(); err != nil { 301 return fmt.Errorf("could not close package file: %w", err) 302 } 303 ctx.Artifacts.Add(&artifact.Artifact{ 304 Type: artifact.LinuxPackage, 305 Name: name + "." + format, 306 Path: path, 307 Goos: binaries[0].Goos, 308 Goarch: binaries[0].Goarch, 309 Goarm: binaries[0].Goarm, 310 Extra: map[string]interface{}{ 311 "Builds": binaries, 312 "ID": fpm.ID, 313 "Format": format, 314 "Files": contents, 315 }, 316 }) 317 return nil 318 } 319 320 func destinations(contents files.Contents) []string { 321 var result = make([]string, 0, len(contents)) 322 for _, f := range contents { 323 result = append(result, f.Destination) 324 } 325 return result 326 } 327 328 func getPassphraseFromEnv(ctx *context.Context, packager string, nfpmID string) string { 329 var passphrase string 330 331 nfpmID = strings.ToUpper(nfpmID) 332 packagerSpecificPassphrase := ctx.Env[fmt.Sprintf( 333 "NFPM_%s_%s_PASSPHRASE", 334 nfpmID, 335 packager, 336 )] 337 if packagerSpecificPassphrase != "" { 338 passphrase = packagerSpecificPassphrase 339 } else { 340 generalPassphrase := ctx.Env[fmt.Sprintf("NFPM_%s_PASSPHRASE", nfpmID)] 341 passphrase = generalPassphrase 342 } 343 344 return passphrase 345 }