diff --git a/CHANGELOG-developer.asciidoc b/CHANGELOG-developer.asciidoc index 54fdb5e7697..b47c53ca28a 100644 --- a/CHANGELOG-developer.asciidoc +++ b/CHANGELOG-developer.asciidoc @@ -34,3 +34,4 @@ The list below covers the major changes between 6.3.0 and master only. - Libbeat provides a global registry for beats developer that allow to register and retrieve plugin. {pull}7392[7392] - Added more options to control required and optional fields in schema.Apply(), error returned is a plain nil if no error happened {pull}7335[7335] +- Packaging on MacOS now produces a .dmg file containing an installer (.pkg) and uninstaller for the Beat. {pull}7481[7481] diff --git a/Makefile b/Makefile index 2eea39cd920..84c7fd4e4fa 100644 --- a/Makefile +++ b/Makefile @@ -144,7 +144,9 @@ snapshot: .PHONY: release release: beats-dashboards @$(foreach var,$(BEATS),$(MAKE) -C $(var) release || exit 1;) - @$(foreach var,$(BEATS),mkdir -p build/distributions/$(var) && mv -f $(var)/build/distributions/* build/distributions/$(var)/ || exit 1;) + @$(foreach var,$(BEATS), \ + test -d $(var)/build/distributions && test -n "$$(ls $(var)/build/distributions)" || exit 0; \ + mkdir -p build/distributions/$(var) && mv -f $(var)/build/distributions/* build/distributions/$(var)/ || exit 1;) # Builds a snapshot release. The Go version defined in .go-version will be # installed and used for the build. diff --git a/auditbeat/magefile.go b/auditbeat/magefile.go index 9f1723917d4..4611fb5742f 100644 --- a/auditbeat/magefile.go +++ b/auditbeat/magefile.go @@ -150,13 +150,16 @@ func customizePackaging() { ) for _, args := range mage.Packages { - switch args.Types[0] { + pkgType := args.Types[0] + switch pkgType { case mage.TarGz, mage.Zip: args.Spec.ReplaceFile("{{.BeatName}}.yml", shortConfig) args.Spec.ReplaceFile("{{.BeatName}}.reference.yml", referenceConfig) - default: + case mage.Deb, mage.RPM, mage.DMG: args.Spec.ReplaceFile("/etc/{{.BeatName}}/{{.BeatName}}.yml", shortConfig) args.Spec.ReplaceFile("/etc/{{.BeatName}}/{{.BeatName}}.reference.yml", referenceConfig) + default: + panic(errors.Errorf("unhandled package type: %v", pkgType)) } } } diff --git a/dev-tools/mage/common.go b/dev-tools/mage/common.go index 4baef2cb4a3..968dbcdf7c1 100644 --- a/dev-tools/mage/common.go +++ b/dev-tools/mage/common.go @@ -34,6 +34,7 @@ import ( "log" "net/http" "os" + "os/exec" "path/filepath" "regexp" "runtime" @@ -44,6 +45,7 @@ import ( "time" "unicode" + "github.com/magefile/mage/mg" "github.com/magefile/mage/sh" "github.com/magefile/mage/target" "github.com/magefile/mage/types" @@ -600,13 +602,47 @@ func CreateSHA512File(file string) error { return ioutil.WriteFile(file+".sha512", []byte(out), 0644) } +// Mage executes mage targets in the specified directory. +func Mage(dir string, targets ...string) error { + c := exec.Command("mage", targets...) + c.Dir = dir + if mg.Verbose() { + c.Env = append(os.Environ(), "MAGEFILE_VERBOSE=1") + } + c.Stdout = os.Stdout + c.Stderr = os.Stderr + c.Stdin = os.Stdin + log.Println("exec:", strings.Join(c.Args, " ")) + err := c.Run() + return err +} + // IsUpToDate returns true iff dst exists and is older based on modtime than all // of the sources. func IsUpToDate(dst string, sources ...string) bool { if len(sources) == 0 { panic("No sources passed to IsUpToDate") } - execute, err := target.Path(dst, sources...) + + var files []string + for _, s := range sources { + filepath.Walk(s, func(path string, info os.FileInfo, err error) error { + if err != nil { + if os.IsNotExist(err) { + return nil + } + return err + } + + if info.Mode().IsRegular() { + files = append(files, path) + } + + return nil + }) + } + + execute, err := target.Path(dst, files...) return err == nil && !execute } diff --git a/dev-tools/mage/dmgbuilder.go b/dev-tools/mage/dmgbuilder.go new file mode 100644 index 00000000000..8c7df259b23 --- /dev/null +++ b/dev-tools/mage/dmgbuilder.go @@ -0,0 +1,286 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package mage + +import ( + "os" + "os/exec" + "path/filepath" + "strings" + + "github.com/magefile/mage/mg" + "github.com/magefile/mage/sh" + "github.com/pkg/errors" +) + +type dmgBuilder struct { + PackageSpec + + SigningInfo *AppleSigningInfo + Identifier string + PreferencePaneDir string + PreferencePanePkgFile string + InternalBeatPkg string + BeatPkg string + + beatsDir string + dmgDir string + + // Build tools. + pkgbuild func(args ...string) error + productbuild func(args ...string) error + spctl func(args ...string) error + codesign func(args ...string) error + hdiutil func(args ...string) error +} + +func newDMGBuilder(spec PackageSpec) (*dmgBuilder, error) { + for _, cmd := range []string{"pkgbuild", "productbuild", "spctl", "codesign", "hdiutil"} { + if _, err := exec.LookPath(cmd); err != nil { + return nil, errors.Wrapf(err, "required tool '%v' for DMG packaging not found on PATH", cmd) + } + } + + beatsDir, err := ElasticBeatsDir() + if err != nil { + return nil, err + } + + preferencePaneDir := filepath.Join(beatsDir, "dev-tools/packaging/preference-pane") + preferencePanePkgFile := filepath.Join(preferencePaneDir, "build/BeatsPrefPane.pkg") + beatIdentifier, ok := spec.evalContext["identifier"].(string) + if !ok { + return nil, errors.Errorf("identifier not specified for DMG packaging") + } + + spec.OutputFile, err = spec.Expand(defaultBinaryName) + if err != nil { + return nil, err + } + + info, err := GetAppleSigningInfo() + if err != nil { + return nil, err + } + + return &dmgBuilder{ + PackageSpec: spec, + SigningInfo: info, + Identifier: beatIdentifier, + PreferencePaneDir: preferencePaneDir, + PreferencePanePkgFile: preferencePanePkgFile, + + beatsDir: beatsDir, + dmgDir: filepath.Join(spec.packageDir, "dmg"), + + pkgbuild: sh.RunCmd("pkgbuild"), + productbuild: sh.RunCmd("productbuild"), + spctl: sh.RunCmd("spctl", "-a", "-t"), + codesign: sh.RunCmd("codesign"), + hdiutil: sh.RunCmd("hdiutil"), + }, nil +} + +// Create .pkg for preference pane. +func (b *dmgBuilder) buildPreferencePane() error { + return errors.Wrap(Mage(b.PreferencePaneDir), "failed to build Beats preference pane") +} + +func (b *dmgBuilder) buildBeatPkg() error { + beatPkgRoot := filepath.Join(b.packageDir, "beat-pkg-root") + if err := os.RemoveAll(beatPkgRoot); err != nil { + return errors.Wrap(err, "failed to clean beat-pkg-root") + } + + // Copy files into the packaging root and set their mode. + for _, f := range b.Files { + target := filepath.Join(beatPkgRoot, f.Target) + if err := Copy(f.Source, target); err != nil { + return err + } + + info, err := os.Stat(target) + if err != nil { + return err + } + + if info.Mode().IsRegular() && info.Mode().Perm() != f.Mode { + if err = os.Chmod(target, f.Mode); err != nil { + return err + } + } + } + + b.InternalBeatPkg = filepath.Join(b.packageDir, "pkgs", "internal-"+b.OutputFile+".pkg") + + args := []string{ + "--root", beatPkgRoot, + "--scripts", filepath.Join(b.packageDir, "scripts"), + "--identifier", b.Identifier, + "--version", b.MustExpand("{{.Version}}{{if .Snapshot}}-SNAPSHOT{{end}}"), + } + if b.SigningInfo.Sign { + args = append(args, "--sign", b.SigningInfo.Installer.ID, "--timestamp") + } + args = append(args, createDir(b.InternalBeatPkg)) + if err := b.pkgbuild(args...); err != nil { + return err + } + + return nil +} + +func (b *dmgBuilder) buildProductPkg() error { + var ( + distributionPlist = filepath.Join(b.packageDir, "distributions.plist") + resourcesDir = filepath.Join(b.packageDir, "resources") + ) + + b.MustExpandFile( + filepath.Join(b.beatsDir, "dev-tools/packaging/templates/darwin/distribution.plist.tmpl"), + distributionPlist) + b.MustExpandFile( + filepath.Join(b.beatsDir, "dev-tools/packaging/templates/darwin/README.html.tmpl"), + filepath.Join(resourcesDir, "README.html")) + for t, pf := range b.Files { + if strings.HasSuffix(t, "LICENSE.txt") { + Copy(pf.Source, filepath.Join(resourcesDir, "LICENSE.txt")) + break + } + } + b.MustExpandFile( + filepath.Join(b.beatsDir, "dev-tools/packaging/templates/darwin/README.html.tmpl"), + filepath.Join(resourcesDir, "README.html")) + + if err := os.RemoveAll(b.dmgDir); err != nil { + return err + } + b.BeatPkg = filepath.Join(b.dmgDir, b.OutputFile+".pkg") + + // Create .pkg containing the previous two .pkg files. + args := []string{ + "--distribution", distributionPlist, + "--resources", resourcesDir, + "--package-path", filepath.Dir(b.InternalBeatPkg), + "--package-path", filepath.Dir(b.PreferencePanePkgFile), + "--component-compression", "auto", + } + if b.SigningInfo.Sign { + args = append(args, "--sign", b.SigningInfo.Installer.ID, "--timestamp") + } + args = append(args, createDir(b.BeatPkg)) + if err := b.productbuild(args...); err != nil { + return err + } + + if b.SigningInfo.Sign { + if err := b.spctl("install", b.BeatPkg); err != nil { + return err + } + } + + return nil +} + +func (b *dmgBuilder) buildUninstallApp() error { + const ( + uninstallerIcons = "Uninstall.app/Contents/Resources/uninstaller.icns" + uninstallScript = "Uninstall.app/Contents/MacOS/uninstall.sh" + infoPlist = "Uninstall.app/Contents/Info.plist" + ) + + inputDir := filepath.Join(b.beatsDir, "dev-tools/packaging/templates/darwin/dmg") + + Copy( + filepath.Join(inputDir, uninstallerIcons), + filepath.Join(b.dmgDir, uninstallerIcons), + ) + b.MustExpandFile( + filepath.Join(inputDir, infoPlist+".tmpl"), + filepath.Join(b.dmgDir, infoPlist), + ) + b.MustExpandFile( + filepath.Join(inputDir, uninstallScript+".tmpl"), + filepath.Join(b.dmgDir, uninstallScript), + ) + if err := os.Chmod(filepath.Join(b.dmgDir, uninstallScript), 0755); err != nil { + return err + } + + if b.SigningInfo.Sign { + uninstallApp := filepath.Join(b.dmgDir, "Uninstall.app") + if err := b.codesign("-s", b.SigningInfo.App.ID, "--timestamp", uninstallApp); err != nil { + return err + } + + if err := b.spctl("exec", uninstallApp); err != nil { + return err + } + } + + return nil +} + +// Create a .dmg file containing both the Uninstall.app and .pkg file. +func (b *dmgBuilder) buildDMG() error { + dmgFile := filepath.Join(distributionsDir, DMG.AddFileExtension(b.OutputFile)) + + args := []string{ + "create", + "-volname", b.MustExpand("{{.BeatName | title}} {{.Version}}{{if .Snapshot}}-SNAPSHOT{{end}}"), + "-srcfolder", b.dmgDir, + "-ov", + createDir(dmgFile), + } + if err := b.hdiutil(args...); err != nil { + return err + } + + // Sign the .dmg. + if b.SigningInfo.Sign { + if err := b.codesign("-s", b.SigningInfo.App.ID, "--timestamp", dmgFile); err != nil { + return err + } + + if err := b.spctl("install", dmgFile); err != nil { + return err + } + } + + return errors.Wrap(CreateSHA512File(dmgFile), "failed to create .sha512 file") +} + +func (b *dmgBuilder) Build() error { + // Mark this function as a dep so that is is only invoked once. + mg.Deps(b.buildPreferencePane) + + var err error + if err = b.buildBeatPkg(); err != nil { + return errors.Wrap(err, "failed to build internal beat pkg") + } + if err = b.buildProductPkg(); err != nil { + return errors.Wrap(err, "failed to build beat product pkg (pref pane + beat)") + } + if err = b.buildUninstallApp(); err != nil { + return errors.Wrap(err, "failed to build Uninstall.app") + } + if err = b.buildDMG(); err != nil { + return errors.Wrap(err, "failed to build beat dmg") + } + return nil +} diff --git a/dev-tools/mage/files/packages.yml b/dev-tools/mage/files/packages.yml deleted file mode 100644 index d57c3c98270..00000000000 --- a/dev-tools/mage/files/packages.yml +++ /dev/null @@ -1,222 +0,0 @@ ---- - -# This file contains the package specifications for both Community Beats and -# Official Beats. The shared section contains YAML anchors that are used to -# define common parts of the package in order to not repeat ourselves. - -shared: -- &common - name: '{{.BeatName}}' - service_name: '{{.BeatServiceName}}' - os: '{{.GOOS}}' - arch: '{{.PackageArch}}' - vendor: '{{.BeatVendor}}' - version: '{{ beat_version }}' - license: '{{.BeatLicense}}' - url: '{{.BeatURL}}' - description: '{{.BeatDescription}}' - -- &deb_rpm_spec - <<: *common - post_install_script: '{{ elastic_beats_dir }}/dev-tools/mage/files/linux/systemd-daemon-reload.sh' - files: - /usr/share/{{.BeatName}}/bin/{{.BeatName}}{{.BinaryExt}}: - source: build/golang-crossbuild/{{.BeatName}}-{{.GOOS}}-{{.Platform.Arch}}{{.BinaryExt}} - mode: 0755 - /etc/{{.BeatName}}/fields.yml: - source: fields.yml - mode: 0644 - /usr/share/{{.BeatName}}/LICENSE.txt: - source: '{{ repo.RootDir }}/LICENSE.txt' - mode: 0644 - /usr/share/{{.BeatName}}/NOTICE.txt: - source: '{{ repo.RootDir }}/NOTICE.txt' - mode: 0644 - /usr/share/{{.BeatName}}/README.md: - template: '{{ elastic_beats_dir }}/dev-tools/mage/templates/common/README.md.tmpl' - mode: 0644 - /usr/share/{{.BeatName}}/.build_hash.txt: - content: > - {{ commit }} - mode: 0644 - /etc/{{.BeatName}}/{{.BeatName}}.reference.yml: - source: '{{.BeatName}}.reference.yml' - mode: 0644 - /etc/{{.BeatName}}/{{.BeatName}}.yml: - source: '{{.BeatName}}.yml' - mode: 0600 - config: true - /usr/share/{{.BeatName}}/kibana: - source: _meta/kibana.generated - mode: 0644 - /usr/share/{{.BeatName}}/bin/{{.BeatName}}-god: - source: build/golang-crossbuild/god-{{.GOOS}}-{{.Platform.Arch}} - mode: 0755 - /usr/bin/{{.BeatName}}: - template: '{{ elastic_beats_dir }}/dev-tools/mage/templates/linux/beatname.sh.tmpl' - mode: 0755 - /lib/systemd/system/{{.BeatServiceName}}.service: - template: '{{ elastic_beats_dir }}/dev-tools/mage/templates/linux/systemd.unit.tmpl' - mode: 0755 - /etc/init.d/{{.BeatServiceName}}: - template: '{{ elastic_beats_dir }}/dev-tools/mage/templates/{{.PackageType}}/init.sh.tmpl' - mode: 0755 - -- &binary_files - '{{.BeatName}}{{.BinaryExt}}': - source: build/golang-crossbuild/{{.BeatName}}-{{.GOOS}}-{{.Platform.Arch}}{{.BinaryExt}} - mode: 0755 - fields.yml: - source: fields.yml - mode: 0644 - LICENSE.txt: - source: '{{ repo.RootDir }}/LICENSE.txt' - mode: 0644 - NOTICE.txt: - source: '{{ repo.RootDir }}/NOTICE.txt' - mode: 0644 - README.md: - template: '{{ elastic_beats_dir }}/dev-tools/mage/templates/common/README.md.tmpl' - mode: 0644 - .build_hash.txt: - content: > - {{ commit }} - mode: 0644 - '{{.BeatName}}.reference.yml': - source: '{{.BeatName}}.reference.yml' - mode: 0644 - '{{.BeatName}}.yml': - source: '{{.BeatName}}.yml' - mode: 0600 - config: true - kibana: - source: _meta/kibana.generated - mode: 0644 - -- &binary_spec - <<: *common - files: - <<: *binary_files - -- &windows_binary_spec - <<: *common - files: - <<: *binary_files - install-service-{{.BeatName}}.ps1: - template: '{{ elastic_beats_dir }}/dev-tools/mage/templates/windows/install-service.ps1.tmpl' - mode: 0755 - uninstall-service-{{.BeatName}}.ps1: - template: '{{ elastic_beats_dir }}/dev-tools/mage/templates/windows/uninstall-service.ps1.tmpl' - mode: 0755 - -- &elastic_license_for_binaries - license: "Elastic License" - files: - LICENSE.txt: - source: '{{ repo.RootDir }}/licenses/ELASTIC-LICENSE.txt' - mode: 0644 - -- &elastic_license_for_deb_rpm - license: "Elastic License" - files: - /usr/share/{{.BeatName}}/LICENSE.txt: - source: '{{ repo.RootDir }}/licenses/ELASTIC-LICENSE.txt' - mode: 0644 - -- &apache_license_for_binaries - license: "ASL 2.0" - files: - LICENSE.txt: - source: '{{ repo.RootDir }}/licenses/APACHE-LICENSE-2.0.txt' - mode: 0644 - -- &apache_license_for_deb_rpm - license: "Elastic License" - files: - /usr/share/{{.BeatName}}/LICENSE.txt: - source: '{{ repo.RootDir }}/licenses/APACHE-LICENSE-2.0.txt' - mode: 0644 - -# specs is a list of named packaging "flavors". -specs: - # Community Beats - community_beat: - - os: windows - types: [zip] - spec: - <<: *windows_binary_spec - - - os: darwin - types: [tgz] - spec: - <<: *binary_spec - - - os: linux - types: [tgz] - spec: - <<: *binary_spec - - - os: linux - types: [deb, rpm] - spec: - <<: *deb_rpm_spec - - # Official Beats - elastic_beat: - ### - # OSS Packages - ### - - os: windows - types: [zip] - spec: - <<: *windows_binary_spec - <<: *apache_license_for_binaries - name: '{{.BeatName}}-oss' - - - os: darwin - types: [tgz] - spec: - <<: *binary_spec - <<: *apache_license_for_binaries - name: '{{.BeatName}}-oss' - - - os: linux - types: [tgz] - spec: - <<: *binary_spec - <<: *apache_license_for_binaries - name: '{{.BeatName}}-oss' - - - os: linux - types: [deb, rpm] - spec: - <<: *deb_rpm_spec - <<: *apache_license_for_deb_rpm - name: '{{.BeatName}}-oss' - - ### - # Elastic Licensed Packages - ### - - os: windows - types: [zip] - spec: - <<: *windows_binary_spec - <<: *elastic_license_for_binaries - - - os: darwin - types: [tgz] - spec: - <<: *binary_spec - <<: *elastic_license_for_binaries - - - os: linux - types: [tgz] - spec: - <<: *binary_spec - <<: *elastic_license_for_binaries - - - os: linux - types: [deb, rpm] - spec: - <<: *deb_rpm_spec - <<: *elastic_license_for_deb_rpm diff --git a/dev-tools/mage/keychain.go b/dev-tools/mage/keychain.go new file mode 100644 index 00000000000..1a3d61e8a01 --- /dev/null +++ b/dev-tools/mage/keychain.go @@ -0,0 +1,159 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package mage + +import ( + "log" + "regexp" + "strconv" + "strings" + "sync" + + "github.com/magefile/mage/sh" + "github.com/pkg/errors" +) + +var _appleKeychain = &appleKeychain{} + +type appleKeychain struct{} + +// SigningIdentity represents a key pair (public/private) that can be used for +// signing. +type SigningIdentity struct { + ID string + Description string +} + +// ListIdentities queries the keychain to get a list of signing identities +// (certificate + private key). +func (k *appleKeychain) ListIdentities() ([]SigningIdentity, error) { + var re = regexp.MustCompile(`(?m)^\s*\d+\)\s+(\w+)\s+"(.+)"$`) + + out, err := sh.Output("security", "find-identity", "-v") + if err != nil { + return nil, err + } + + var idents []SigningIdentity + ids := map[string]struct{}{} + for _, match := range re.FindAllStringSubmatch(out, -1) { + ident := SigningIdentity{ID: match[1], Description: match[2]} + + // Deduplicate + if _, found := ids[ident.ID]; found { + continue + } + + idents = append(idents, ident) + ids[ident.ID] = struct{}{} + } + + return idents, nil +} + +// AppleSigningInfo indicate if signing is enabled and specifies the identities +// to use for signing applications and installers. +type AppleSigningInfo struct { + Sign bool + App SigningIdentity + Installer SigningIdentity +} + +var ( + appleSigningInfoValue *AppleSigningInfo + appleSigningInfoErr error + appleSigningInfoOnce sync.Once +) + +// GetAppleSigningInfo returns the signing identities used for code signing +// apps and installers. +// +// Environment Variables +// +// APPLE_SIGNING_ENABLED - Must be set to true to enable signing. Defaults to +// false. +// +// APPLE_SIGNING_IDENTITY_INSTALLER - filter for selecting the signing identity +// for installers. +// +// APPLE_SIGNING_IDENTITY_APP - filter for selecting the signing identity +// for apps. +func GetAppleSigningInfo() (*AppleSigningInfo, error) { + appleSigningInfoOnce.Do(func() { + appleSigningInfoValue, appleSigningInfoErr = getAppleSigningInfo() + }) + + return appleSigningInfoValue, appleSigningInfoErr +} + +func getAppleSigningInfo() (*AppleSigningInfo, error) { + var ( + signingEnabled, _ = strconv.ParseBool(EnvOr("APPLE_SIGNING_ENABLED", "false")) + identityInstaller = strings.ToLower(EnvOr("APPLE_SIGNING_IDENTITY_INSTALLER", "Developer ID Installer")) + identityApp = strings.ToLower(EnvOr("APPLE_SIGNING_IDENTITY_APP", "Developer ID Application")) + ) + + if !signingEnabled { + return &AppleSigningInfo{Sign: false}, nil + } + + idents, err := _appleKeychain.ListIdentities() + if err != nil { + return nil, err + } + + var install, app []SigningIdentity + for _, ident := range idents { + id, desc := strings.ToLower(ident.ID), strings.ToLower(ident.Description) + if strings.Contains(id, identityInstaller) || strings.Contains(desc, identityInstaller) { + install = append(install, ident) + } + if strings.Contains(id, identityApp) || strings.Contains(desc, identityApp) { + app = append(app, ident) + } + } + + if len(install) == 1 && len(app) == 1 { + log.Printf("Apple Code Signing Identities:\n App: %+v\n Installer: %+v", app[0], install[0]) + return &AppleSigningInfo{ + Sign: true, + Installer: install[0], + App: app[0], + }, nil + } + + if len(install) > 1 { + return nil, errors.Errorf("found multiple installer signing identities "+ + "that match '%v'. Set a more specific APPLE_SIGNING_IDENTITY_INSTALLER "+ + "value that will select one of %+v", identityInstaller, install) + } + + if len(app) > 1 { + return nil, errors.Errorf("found multiple installer signing identities "+ + "that match '%v'. Set a more specific APPLE_SIGNING_IDENTITY_APP "+ + "value that will select one of %+v", identityApp, app) + } + + if len(install) == 0 || len(app) == 0 { + return nil, errors.Errorf("apple signing was requested with " + + "APPLE_SIGNING_ENABLED=true, but the required signing identities " + + "for app and installer were not found") + } + + return &AppleSigningInfo{Sign: false}, nil +} diff --git a/dev-tools/mage/keychain_test.go b/dev-tools/mage/keychain_test.go new file mode 100644 index 00000000000..949ca9ac5bf --- /dev/null +++ b/dev-tools/mage/keychain_test.go @@ -0,0 +1,50 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build darwin + +package mage + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestAppleKeychainListIdentities(t *testing.T) { + idents, err := _appleKeychain.ListIdentities() + if err != nil { + t.Fatal(err) + } + + assert.NotZero(t, idents) + + for i, ident := range idents { + t.Log(i, ident) + } +} + +func TestGetAppleSigningInfo(t *testing.T) { + signingInfo, err := GetAppleSigningInfo() + if err != nil { + t.Fatal(err) + } + + if assert.NotNil(t, signingInfo) { + assert.False(t, signingInfo.Sign) + } +} diff --git a/dev-tools/mage/pkg.go b/dev-tools/mage/pkg.go index 688e5c22118..c98d3236ea3 100644 --- a/dev-tools/mage/pkg.go +++ b/dev-tools/mage/pkg.go @@ -20,6 +20,7 @@ package mage import ( "fmt" "log" + "runtime" "github.com/magefile/mage/mg" "github.com/magefile/mage/sh" @@ -27,11 +28,11 @@ import ( ) // Package packages the Beat for distribution. It generates packages based on -// the set of target plaforms and registered packaging specifications. +// the set of target platforms and registered packaging specifications. func Package() error { if len(Platforms) == 0 { - return errors.New("PLATFORMS environment must be set to a list of " + - "GOOS/Arch values, but Platforms is empty") + fmt.Println(">> package: Skipping because the platform list is empty") + return nil } if len(Packages) == 0 { @@ -47,6 +48,11 @@ func Package() error { } for _, pkgType := range pkg.Types { + if pkgType == DMG && runtime.GOOS != "darwin" { + log.Printf("Skipping DMG package type because build host isn't darwin") + continue + } + packageArch, err := getOSArchName(target, pkgType) if err != nil { log.Printf("Skipping arch %v for package type %v: %v", target.Arch(), pkgType, err) @@ -94,15 +100,24 @@ func (b packageBuilder) Build() error { // inspect things like file ownership and mode. func TestPackages() error { fmt.Println(">> Testing package contents") + goTest := sh.OutCmd("go", "test") + var args []string if mg.Verbose() { args = append(args, "-v") } args = append(args, - MustExpand("{{ elastic_beats_dir }}/dev-tools/package_test.go"), + MustExpand("{{ elastic_beats_dir }}/dev-tools/packaging/package_test.go"), "-files", MustExpand("{{.PWD}}/build/distributions/*"), ) - goTest := sh.RunCmd("go", "test") - return goTest(args...) + + if out, err := goTest(args...); err != nil { + if !mg.Verbose() { + fmt.Println(out) + } + return err + } + + return nil } diff --git a/dev-tools/mage/pkgspecs.go b/dev-tools/mage/pkgspecs.go index ee6c38a025a..11dda2e49f7 100644 --- a/dev-tools/mage/pkgspecs.go +++ b/dev-tools/mage/pkgspecs.go @@ -26,7 +26,7 @@ import ( "gopkg.in/yaml.v2" ) -const packageSpecFile = "dev-tools/mage/files/packages.yml" +const packageSpecFile = "dev-tools/packaging/packages.yml" // Packages defines the set of packages to be built when the package target is // executed. diff --git a/dev-tools/mage/pkgtypes.go b/dev-tools/mage/pkgtypes.go index a470ca90663..736ddebc8db 100644 --- a/dev-tools/mage/pkgtypes.go +++ b/dev-tools/mage/pkgtypes.go @@ -60,6 +60,7 @@ const ( Deb Zip TarGz + DMG ) // OSPackageArgs define a set of package types to build for an operating @@ -82,12 +83,15 @@ type PackageSpec struct { License string `yaml:"license,omitempty"` URL string `yaml:"url,omitempty"` Description string `yaml:"description,omitempty"` + PreInstallScript string `yaml:"pre_install_script,omitempty"` PostInstallScript string `yaml:"post_install_script,omitempty"` Files map[string]PackageFile `yaml:"files"` OutputFile string `yaml:"output_file,omitempty"` // Optional + ExtraVars map[string]string `yaml:"extra_vars,omitempty"` // Optional evalContext map[string]interface{} packageDir string + localPreInstallScript string localPostInstallScript string } @@ -115,6 +119,10 @@ var OSArchNames = map[string]map[PackageType]map[string]string{ "386": "x86", "amd64": "x86_64", }, + DMG: map[string]string{ + "386": "x86", + "amd64": "x86_64", + }, }, "linux": map[PackageType]map[string]string{ RPM: map[string]string{ @@ -194,6 +202,8 @@ func (typ PackageType) String() string { return "zip" case TarGz: return "tar.gz" + case DMG: + return "dmg" default: return "invalid" } @@ -215,6 +225,8 @@ func (typ *PackageType) UnmarshalText(text []byte) error { *typ = TarGz case "zip": *typ = Zip + case "dmg": + *typ = DMG default: return errors.Errorf("unknown package type: %v", string(text)) } @@ -242,6 +254,8 @@ func (typ PackageType) Build(spec PackageSpec) error { return PackageZip(spec) case TarGz: return PackageTarGz(spec) + case DMG: + return PackageDMG(spec) default: return errors.Errorf("unknown package type: %v", typ) } @@ -292,8 +306,10 @@ func (s PackageSpec) ExpandFile(src, dst string, args ...map[string]interface{}) // MustExpandFile expands a template file using data from the spec. It panics if // an error occurs. -func (s PackageSpec) MustExpandFile(src, dst string, args ...map[string]interface{}) error { - return s.ExpandFile(src, dst, args...) +func (s PackageSpec) MustExpandFile(src, dst string, args ...map[string]interface{}) { + if err := s.ExpandFile(src, dst, args...); err != nil { + panic(err) + } } // Evaluate expands all variables used in the spec definition and writes any @@ -307,6 +323,10 @@ func (s PackageSpec) Evaluate(args ...map[string]interface{}) PackageSpec { return MustExpand(in, args...) } + for k, v := range s.ExtraVars { + s.evalContext[k] = mustExpand(v) + } + s.Name = mustExpand(s.Name) s.ServiceName = mustExpand(s.ServiceName) s.OS = mustExpand(s.OS) @@ -316,6 +336,7 @@ func (s PackageSpec) Evaluate(args ...map[string]interface{}) PackageSpec { s.License = mustExpand(s.License) s.URL = mustExpand(s.URL) s.Description = mustExpand(s.Description) + s.PreInstallScript = mustExpand(s.PreInstallScript) s.PostInstallScript = mustExpand(s.PostInstallScript) s.OutputFile = mustExpand(s.OutputFile) @@ -368,7 +389,7 @@ func (s PackageSpec) Evaluate(args ...map[string]interface{}) PackageSpec { } case f.Template != "": f.Source = filepath.Join(s.packageDir, filepath.Base(f.Template)) - if err := s.ExpandFile(createDir(f.Template), f.Source); err != nil { + if err := s.ExpandFile(f.Template, createDir(f.Source)); err != nil { panic(errors.Wrapf(err, "failed to expand template file for target=%v", target)) } default: @@ -380,17 +401,37 @@ func (s PackageSpec) Evaluate(args ...map[string]interface{}) PackageSpec { // Replace the map instead of modifying the source. s.Files = evaluatedFiles - if s.PostInstallScript != "" { - // Copy the inside the build dir so that it's available inside of Docker for FPM. - s.localPostInstallScript = filepath.Join(s.packageDir, filepath.Base(s.PostInstallScript)) - if err := Copy(s.PostInstallScript, s.localPostInstallScript); err != nil { - panic(errors.Wrap(err, "failed to copy post install script to build dir")) - } + if err := copyInstallScript(s, s.PreInstallScript, &s.localPreInstallScript); err != nil { + panic(err) + } + if err := copyInstallScript(s, s.PostInstallScript, &s.localPostInstallScript); err != nil { + panic(err) } return s } +func copyInstallScript(spec PackageSpec, script string, local *string) error { + if script == "" { + return nil + } + + *local = filepath.Join(spec.packageDir, "scripts", filepath.Base(script)) + if filepath.Ext(*local) == ".tmpl" { + *local = strings.TrimSuffix(*local, ".tmpl") + } + + if err := spec.ExpandFile(script, createDir(*local)); err != nil { + return errors.Wrap(err, "failed to copy install script to package dir") + } + + if err := os.Chmod(*local, 0755); err != nil { + return errors.Wrap(err, "failed to chmod install script") + } + + return nil +} + func (s PackageSpec) hash() string { h, err := hashstructure.Hash(s, nil) if err != nil { @@ -767,3 +808,18 @@ func addFileToTar(ar *tar.Writer, baseDir string, pkgFile PackageFile) error { return file.Close() }) } + +// PackageDMG packages the Beat into a .dmg file containing an installer pkg +// and uninstaller app. +func PackageDMG(spec PackageSpec) error { + if runtime.GOOS != "darwin" { + return errors.New("packaging a dmg requires darwin") + } + + b, err := newDMGBuilder(spec) + if err != nil { + return err + } + + return b.Build() +} diff --git a/dev-tools/mage/settings.go b/dev-tools/mage/settings.go index dc66bdd33ea..702cf20a5b6 100644 --- a/dev-tools/mage/settings.go +++ b/dev-tools/mage/settings.go @@ -72,6 +72,7 @@ var ( "go_version": GoVersion, "repo": GetProjectRepoInfo, "title": strings.Title, + "tolower": strings.ToLower, } ) diff --git a/dev-tools/mage/files/linux/systemd-daemon-reload.sh b/dev-tools/packaging/files/linux/systemd-daemon-reload.sh similarity index 100% rename from dev-tools/mage/files/linux/systemd-daemon-reload.sh rename to dev-tools/packaging/files/linux/systemd-daemon-reload.sh diff --git a/dev-tools/package_test.go b/dev-tools/packaging/package_test.go similarity index 98% rename from dev-tools/package_test.go rename to dev-tools/packaging/package_test.go index 2021a42a4a2..1227acd6a3a 100644 --- a/dev-tools/package_test.go +++ b/dev-tools/packaging/package_test.go @@ -54,7 +54,7 @@ var ( ) var ( - files = flag.String("files", "../build/upload/*/*", "filepath glob containing package files") + files = flag.String("files", "../build/distributions/*/*", "filepath glob containing package files") ) func TestRPM(t *testing.T) { diff --git a/dev-tools/packaging/packages.yml b/dev-tools/packaging/packages.yml new file mode 100644 index 00000000000..aba1e720bbb --- /dev/null +++ b/dev-tools/packaging/packages.yml @@ -0,0 +1,303 @@ +--- + +# This file contains the package specifications for both Community Beats and +# Official Beats. The shared section contains YAML anchors that are used to +# define common parts of the package in order to not repeat ourselves. + +shared: + - &common + name: '{{.BeatName}}' + service_name: '{{.BeatServiceName}}' + os: '{{.GOOS}}' + arch: '{{.PackageArch}}' + vendor: '{{.BeatVendor}}' + version: '{{ beat_version }}' + license: '{{.BeatLicense}}' + url: '{{.BeatURL}}' + description: '{{.BeatDescription}}' + + # Deb/RPM spec for community beats. + - &deb_rpm_spec + <<: *common + post_install_script: '{{ elastic_beats_dir }}/dev-tools/packaging/files/linux/systemd-daemon-reload.sh' + files: + /usr/share/{{.BeatName}}/bin/{{.BeatName}}{{.BinaryExt}}: + source: build/golang-crossbuild/{{.BeatName}}-{{.GOOS}}-{{.Platform.Arch}}{{.BinaryExt}} + mode: 0755 + /etc/{{.BeatName}}/fields.yml: + source: fields.yml + mode: 0644 + /usr/share/{{.BeatName}}/LICENSE.txt: + source: '{{ repo.RootDir }}/LICENSE.txt' + mode: 0644 + /usr/share/{{.BeatName}}/NOTICE.txt: + source: '{{ repo.RootDir }}/NOTICE.txt' + mode: 0644 + /usr/share/{{.BeatName}}/README.md: + template: '{{ elastic_beats_dir }}/dev-tools/packaging/templates/common/README.md.tmpl' + mode: 0644 + /usr/share/{{.BeatName}}/.build_hash.txt: + content: > + {{ commit }} + mode: 0644 + /etc/{{.BeatName}}/{{.BeatName}}.reference.yml: + source: '{{.BeatName}}.reference.yml' + mode: 0644 + /etc/{{.BeatName}}/{{.BeatName}}.yml: + source: '{{.BeatName}}.yml' + mode: 0600 + config: true + /usr/share/{{.BeatName}}/kibana: + source: _meta/kibana.generated + mode: 0644 + /usr/share/{{.BeatName}}/bin/{{.BeatName}}-god: + source: build/golang-crossbuild/god-{{.GOOS}}-{{.Platform.Arch}} + mode: 0755 + /usr/bin/{{.BeatName}}: + template: '{{ elastic_beats_dir }}/dev-tools/packaging/templates/linux/beatname.sh.tmpl' + mode: 0755 + /lib/systemd/system/{{.BeatServiceName}}.service: + template: '{{ elastic_beats_dir }}/dev-tools/packaging/templates/linux/systemd.unit.tmpl' + mode: 0755 + /etc/init.d/{{.BeatServiceName}}: + template: '{{ elastic_beats_dir }}/dev-tools/packaging/templates/{{.PackageType}}/init.sh.tmpl' + mode: 0755 + + # MacOS pkg spec for community beats. + - &macos_beat_pkg_spec + <<: *common + extra_vars: + # OS X 10.8 Mountain Lion is the oldest supported by Go 1.10. + # https://golang.org/doc/go1.10#ports + min_supported_osx_version: 10.8 + identifier: 'co.{{.BeatVendor | tolower}}.beats.{{.BeatName}}' + install_path: /Library/Application Support + pre_install_script: '{{ elastic_beats_dir }}/dev-tools/packaging/templates/darwin/scripts/preinstall.tmpl' + post_install_script: '{{ elastic_beats_dir }}/dev-tools/packaging/templates/darwin/scripts/postinstall.tmpl' + files: + /Library/Application Support/{{.BeatVendor}}/{{.BeatName}}/bin/{{.BeatName}}{{.BinaryExt}}: + source: build/golang-crossbuild/{{.BeatName}}-{{.GOOS}}-{{.Platform.Arch}}{{.BinaryExt}} + mode: 0755 + /Library/Application Support/{{.BeatVendor}}/{{.BeatName}}/LICENSE.txt: + source: '{{ repo.RootDir }}/LICENSE.txt' + mode: 0644 + /Library/Application Support/{{.BeatVendor}}/{{.BeatName}}/NOTICE.txt: + source: '{{ repo.RootDir }}/NOTICE.txt' + mode: 0644 + /Library/Application Support/{{.BeatVendor}}/{{.BeatName}}/README.md: + template: '{{ elastic_beats_dir }}/dev-tools/packaging/templates/common/README.md.tmpl' + mode: 0644 + /Library/Application Support/{{.BeatVendor}}/{{.BeatName}}/.build_hash.txt: + content: > + {{ commit }} + mode: 0644 + /Library/Application Support/{{.BeatVendor}}/{{.BeatName}}/{{.identifier}}.plist: + template: '{{ elastic_beats_dir }}/dev-tools/packaging/templates/darwin/launchd-daemon.plist.tmpl' + mode: 0644 + /Library/Application Support/{{.BeatVendor}}/{{.BeatName}}/kibana: + source: _meta/kibana.generated + mode: 0644 + /etc/{{.BeatName}}/fields.yml: + source: fields.yml + mode: 0644 + /etc/{{.BeatName}}/{{.BeatName}}.reference.yml: + source: '{{.BeatName}}.reference.yml' + mode: 0644 + /etc/{{.BeatName}}/{{.BeatName}}.yml: + source: '{{.BeatName}}.yml' + mode: 0600 + config: true + + - &binary_files + '{{.BeatName}}{{.BinaryExt}}': + source: build/golang-crossbuild/{{.BeatName}}-{{.GOOS}}-{{.Platform.Arch}}{{.BinaryExt}} + mode: 0755 + fields.yml: + source: fields.yml + mode: 0644 + LICENSE.txt: + source: '{{ repo.RootDir }}/LICENSE.txt' + mode: 0644 + NOTICE.txt: + source: '{{ repo.RootDir }}/NOTICE.txt' + mode: 0644 + README.md: + template: '{{ elastic_beats_dir }}/dev-tools/packaging/templates/common/README.md.tmpl' + mode: 0644 + .build_hash.txt: + content: > + {{ commit }} + mode: 0644 + '{{.BeatName}}.reference.yml': + source: '{{.BeatName}}.reference.yml' + mode: 0644 + '{{.BeatName}}.yml': + source: '{{.BeatName}}.yml' + mode: 0600 + config: true + kibana: + source: _meta/kibana.generated + mode: 0644 + + # Binary package spec (tar.gz for linux/darwin) for community beats. + - &binary_spec + <<: *common + files: + <<: *binary_files + + # Binary package spec (zip for windows) for community beats. + - &windows_binary_spec + <<: *common + files: + <<: *binary_files + install-service-{{.BeatName}}.ps1: + template: '{{ elastic_beats_dir }}/dev-tools/packaging/templates/windows/install-service.ps1.tmpl' + mode: 0755 + uninstall-service-{{.BeatName}}.ps1: + template: '{{ elastic_beats_dir }}/dev-tools/packaging/templates/windows/uninstall-service.ps1.tmpl' + mode: 0755 + + # + # License modifiers for Apache 2.0 + # + - &apache_license_for_binaries + license: "ASL 2.0" + files: + LICENSE.txt: + source: '{{ repo.RootDir }}/licenses/APACHE-LICENSE-2.0.txt' + mode: 0644 + + - &apache_license_for_deb_rpm + license: "ASL 2.0" + files: + /usr/share/{{.BeatName}}/LICENSE.txt: + source: '{{ repo.RootDir }}/licenses/APACHE-LICENSE-2.0.txt' + mode: 0644 + + - &apache_license_for_macos_pkg + license: "ASL 2.0" + files: + /Library/Application Support/{{.BeatVendor}}/{{.BeatName}}/LICENSE.txt: + source: '{{ repo.RootDir }}/licenses/APACHE-LICENSE-2.0.txt' + mode: 0644 + + # + # License modifiers for the Elastic License + # + - &elastic_license_for_binaries + license: "Elastic License" + files: + LICENSE.txt: + source: '{{ repo.RootDir }}/licenses/ELASTIC-LICENSE.txt' + mode: 0644 + + - &elastic_license_for_deb_rpm + license: "Elastic License" + files: + /usr/share/{{.BeatName}}/LICENSE.txt: + source: '{{ repo.RootDir }}/licenses/ELASTIC-LICENSE.txt' + mode: 0644 + + - &elastic_license_for_macos_pkg + license: "Elastic License" + files: + /Library/Application Support/{{.BeatVendor}}/{{.BeatName}}/LICENSE.txt: + source: '{{ repo.RootDir }}/licenses/ELASTIC-LICENSE.txt' + mode: 0644 + +# specs is a list of named packaging "flavors". +specs: + # Community Beats + community_beat: + - os: windows + types: [zip] + spec: + <<: *windows_binary_spec + + - os: darwin + types: [tgz] + spec: + <<: *binary_spec + + - os: linux + types: [tgz] + spec: + <<: *binary_spec + + - os: linux + types: [deb, rpm] + spec: + <<: *deb_rpm_spec + + # Official Beats + elastic_beat: + ### + # OSS Packages + ### + - os: windows + types: [zip] + spec: + <<: *windows_binary_spec + <<: *apache_license_for_binaries + name: '{{.BeatName}}-oss' + + - os: darwin + types: [tgz] + spec: + <<: *binary_spec + <<: *apache_license_for_binaries + name: '{{.BeatName}}-oss' + + - os: darwin + types: [dmg] + spec: + <<: *macos_beat_pkg_spec + <<: *apache_license_for_macos_pkg + name: '{{.BeatName}}-oss' + + - os: linux + types: [tgz] + spec: + <<: *binary_spec + <<: *apache_license_for_binaries + name: '{{.BeatName}}-oss' + + - os: linux + types: [deb, rpm] + spec: + <<: *deb_rpm_spec + <<: *apache_license_for_deb_rpm + name: '{{.BeatName}}-oss' + + ### + # Elastic Licensed Packages + ### + - os: windows + types: [zip] + spec: + <<: *windows_binary_spec + <<: *elastic_license_for_binaries + + - os: darwin + types: [tgz] + spec: + <<: *binary_spec + <<: *elastic_license_for_binaries + + - os: darwin + types: [dmg] + spec: + <<: *macos_beat_pkg_spec + <<: *elastic_license_for_macos_pkg + + - os: linux + types: [tgz] + spec: + <<: *binary_spec + <<: *elastic_license_for_binaries + + - os: linux + types: [deb, rpm] + spec: + <<: *deb_rpm_spec + <<: *elastic_license_for_deb_rpm diff --git a/dev-tools/packaging/preference-pane/.gitignore b/dev-tools/packaging/preference-pane/.gitignore new file mode 100644 index 00000000000..567609b1234 --- /dev/null +++ b/dev-tools/packaging/preference-pane/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane.xcodeproj/project.pbxproj b/dev-tools/packaging/preference-pane/beats-preference-pane.xcodeproj/project.pbxproj similarity index 100% rename from dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane.xcodeproj/project.pbxproj rename to dev-tools/packaging/preference-pane/beats-preference-pane.xcodeproj/project.pbxproj diff --git a/dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/dev-tools/packaging/preference-pane/beats-preference-pane.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to dev-tools/packaging/preference-pane/beats-preference-pane.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane.xcodeproj/xcshareddata/IDETemplateMacros.plist b/dev-tools/packaging/preference-pane/beats-preference-pane.xcodeproj/xcshareddata/IDETemplateMacros.plist similarity index 100% rename from dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane.xcodeproj/xcshareddata/IDETemplateMacros.plist rename to dev-tools/packaging/preference-pane/beats-preference-pane.xcodeproj/xcshareddata/IDETemplateMacros.plist diff --git a/dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane.xcodeproj/xcuserdata/adrian.xcuserdatad/xcschemes/xcschememanagement.plist b/dev-tools/packaging/preference-pane/beats-preference-pane.xcodeproj/xcuserdata/adrian.xcuserdatad/xcschemes/xcschememanagement.plist similarity index 100% rename from dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane.xcodeproj/xcuserdata/adrian.xcuserdatad/xcschemes/xcschememanagement.plist rename to dev-tools/packaging/preference-pane/beats-preference-pane.xcodeproj/xcuserdata/adrian.xcuserdatad/xcschemes/xcschememanagement.plist diff --git a/dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/Authorization.h b/dev-tools/packaging/preference-pane/beats-preference-pane/Authorization.h similarity index 100% rename from dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/Authorization.h rename to dev-tools/packaging/preference-pane/beats-preference-pane/Authorization.h diff --git a/dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/Base.lproj/BeatView.xib b/dev-tools/packaging/preference-pane/beats-preference-pane/Base.lproj/BeatView.xib similarity index 100% rename from dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/Base.lproj/BeatView.xib rename to dev-tools/packaging/preference-pane/beats-preference-pane/Base.lproj/BeatView.xib diff --git a/dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/Base.lproj/BeatsPrefPane.xib b/dev-tools/packaging/preference-pane/beats-preference-pane/Base.lproj/BeatsPrefPane.xib similarity index 100% rename from dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/Base.lproj/BeatsPrefPane.xib rename to dev-tools/packaging/preference-pane/beats-preference-pane/Base.lproj/BeatsPrefPane.xib diff --git a/dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/Base.lproj/EditorWindow.xib b/dev-tools/packaging/preference-pane/beats-preference-pane/Base.lproj/EditorWindow.xib similarity index 100% rename from dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/Base.lproj/EditorWindow.xib rename to dev-tools/packaging/preference-pane/beats-preference-pane/Base.lproj/EditorWindow.xib diff --git a/dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/Base.lproj/pane.xib b/dev-tools/packaging/preference-pane/beats-preference-pane/Base.lproj/pane.xib similarity index 100% rename from dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/Base.lproj/pane.xib rename to dev-tools/packaging/preference-pane/beats-preference-pane/Base.lproj/pane.xib diff --git a/dev-tools/packaging/preference-pane/beats-preference-pane/BeatManager.m b/dev-tools/packaging/preference-pane/beats-preference-pane/BeatManager.m new file mode 100644 index 00000000000..650c745ddd6 --- /dev/null +++ b/dev-tools/packaging/preference-pane/beats-preference-pane/BeatManager.m @@ -0,0 +1,22 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#import "BeatManager.h" + +@implementation BeatManager + +@end diff --git a/dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/BeatViewController.h b/dev-tools/packaging/preference-pane/beats-preference-pane/BeatViewController.h similarity index 100% rename from dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/BeatViewController.h rename to dev-tools/packaging/preference-pane/beats-preference-pane/BeatViewController.h diff --git a/dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/BeatViewController.m b/dev-tools/packaging/preference-pane/beats-preference-pane/BeatViewController.m similarity index 100% rename from dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/BeatViewController.m rename to dev-tools/packaging/preference-pane/beats-preference-pane/BeatViewController.m diff --git a/dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/Beats.icns b/dev-tools/packaging/preference-pane/beats-preference-pane/Beats.icns similarity index 100% rename from dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/Beats.icns rename to dev-tools/packaging/preference-pane/beats-preference-pane/Beats.icns diff --git a/dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/BeatsPrefPane.h b/dev-tools/packaging/preference-pane/beats-preference-pane/BeatsPrefPane.h similarity index 100% rename from dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/BeatsPrefPane.h rename to dev-tools/packaging/preference-pane/beats-preference-pane/BeatsPrefPane.h diff --git a/dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/BeatsPrefPane.m b/dev-tools/packaging/preference-pane/beats-preference-pane/BeatsPrefPane.m similarity index 100% rename from dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/BeatsPrefPane.m rename to dev-tools/packaging/preference-pane/beats-preference-pane/BeatsPrefPane.m diff --git a/dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/EditorWindow.h b/dev-tools/packaging/preference-pane/beats-preference-pane/EditorWindow.h similarity index 100% rename from dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/EditorWindow.h rename to dev-tools/packaging/preference-pane/beats-preference-pane/EditorWindow.h diff --git a/dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/EditorWindow.m b/dev-tools/packaging/preference-pane/beats-preference-pane/EditorWindow.m similarity index 100% rename from dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/EditorWindow.m rename to dev-tools/packaging/preference-pane/beats-preference-pane/EditorWindow.m diff --git a/dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/Info.plist b/dev-tools/packaging/preference-pane/beats-preference-pane/Info.plist similarity index 100% rename from dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/Info.plist rename to dev-tools/packaging/preference-pane/beats-preference-pane/Info.plist diff --git a/dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/TabViewDelegate.h b/dev-tools/packaging/preference-pane/beats-preference-pane/TabViewDelegate.h similarity index 100% rename from dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/TabViewDelegate.h rename to dev-tools/packaging/preference-pane/beats-preference-pane/TabViewDelegate.h diff --git a/dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/TabViewDelegate.m b/dev-tools/packaging/preference-pane/beats-preference-pane/TabViewDelegate.m similarity index 100% rename from dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/TabViewDelegate.m rename to dev-tools/packaging/preference-pane/beats-preference-pane/TabViewDelegate.m diff --git a/dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/beats/Beats.h b/dev-tools/packaging/preference-pane/beats-preference-pane/beats/Beats.h similarity index 100% rename from dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/beats/Beats.h rename to dev-tools/packaging/preference-pane/beats-preference-pane/beats/Beats.h diff --git a/dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/beats/BeatsService.h b/dev-tools/packaging/preference-pane/beats-preference-pane/beats/BeatsService.h similarity index 100% rename from dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/beats/BeatsService.h rename to dev-tools/packaging/preference-pane/beats-preference-pane/beats/BeatsService.h diff --git a/dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/beats/BeatsService.m b/dev-tools/packaging/preference-pane/beats-preference-pane/beats/BeatsService.m similarity index 100% rename from dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/beats/BeatsService.m rename to dev-tools/packaging/preference-pane/beats-preference-pane/beats/BeatsService.m diff --git a/dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/common/common.h b/dev-tools/packaging/preference-pane/beats-preference-pane/common/common.h similarity index 100% rename from dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/common/common.h rename to dev-tools/packaging/preference-pane/beats-preference-pane/common/common.h diff --git a/dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/common/common.m b/dev-tools/packaging/preference-pane/beats-preference-pane/common/common.m similarity index 100% rename from dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/common/common.m rename to dev-tools/packaging/preference-pane/beats-preference-pane/common/common.m diff --git a/dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/config.h b/dev-tools/packaging/preference-pane/beats-preference-pane/config.h similarity index 100% rename from dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/config.h rename to dev-tools/packaging/preference-pane/beats-preference-pane/config.h diff --git a/dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/en.lproj/beats_preference_pane.strings b/dev-tools/packaging/preference-pane/beats-preference-pane/en.lproj/beats_preference_pane.strings similarity index 100% rename from dev-tools/packer/platforms/darwin/preference-pane/beats-preference-pane/en.lproj/beats_preference_pane.strings rename to dev-tools/packaging/preference-pane/beats-preference-pane/en.lproj/beats_preference_pane.strings diff --git a/dev-tools/packer/platforms/darwin/preference-pane/helper/main.m b/dev-tools/packaging/preference-pane/helper/main.m similarity index 100% rename from dev-tools/packer/platforms/darwin/preference-pane/helper/main.m rename to dev-tools/packaging/preference-pane/helper/main.m diff --git a/dev-tools/packer/platforms/darwin/preference-pane/helper/setboot.m b/dev-tools/packaging/preference-pane/helper/setboot.m similarity index 100% rename from dev-tools/packer/platforms/darwin/preference-pane/helper/setboot.m rename to dev-tools/packaging/preference-pane/helper/setboot.m diff --git a/dev-tools/packaging/preference-pane/magefile.go b/dev-tools/packaging/preference-pane/magefile.go new file mode 100644 index 00000000000..2100adb0198 --- /dev/null +++ b/dev-tools/packaging/preference-pane/magefile.go @@ -0,0 +1,157 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build mage + +package main + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/elastic/beats/dev-tools/mage" + "github.com/magefile/mage/mg" + "github.com/magefile/mage/sh" + "github.com/pkg/errors" +) + +var builder = preferencePaneBuilder{ + Project: "beats-preference-pane.xcodeproj", + Configuration: mage.EnvOr("XCODE_CONFIGURATION", "Release"), + PackageName: "BeatsPrefPane.pkg", + InstallDir: "/Library/PreferencePanes", + Identifier: "co.elastic.beats.preference-pane", + Version: "1.0.0", +} + +// Default specifies the default build target for mage. +var Default = All + +// All build, sign, and package the Beats Preference Pane. +func All() { mg.SerialDeps(Build, Package) } + +// Build builds the preference pane source using xcodebuild. +func Build() error { return builder.Build() } + +// Package packages the pref pane into BeatsPrefPane.pkg. +func Package() error { return builder.Package() } + +// Clean cleans the build artifacts. +func Clean() error { return sh.Rm("build") } + +// --- preferencePaneBuilder + +type preferencePaneBuilder struct { + Project string + Configuration string + PackageName string + InstallDir string + Identifier string + Version string +} + +func (b preferencePaneBuilder) SigningInfo() *mage.AppleSigningInfo { + info, err := mage.GetAppleSigningInfo() + if err != nil { + panic(err) + } + + return info +} + +func (b preferencePaneBuilder) Build() error { + if mage.IsUpToDate("build/Release/Beats.prefPane/Contents/MacOS/Beats", + "helper", "beats-preference-pane", "beats-preference-pane.xcodeproj") { + fmt.Println(">> Building MacOS Preference Pane (UP-TO-DATE)") + return nil + } + + fmt.Println(">> Building MacOS Preference Pane") + err := sh.Run("xcodebuild", "build", + "-project", b.Project, + "-alltargets", + "-configuration", b.Configuration, + // This disables xcodebuild from attempting to codesign. + // We do that in its own build step. + "CODE_SIGN_IDENTITY=", + "CODE_SIGNING_REQUIRED=NO") + if err != nil { + return err + } + + return b.Sign() +} + +func (b preferencePaneBuilder) Sign() error { + if !b.SigningInfo().Sign { + fmt.Println("Skipping signing of MacOS Preference Pane " + + "(APPLE_SIGNING_ENABLED not set to true)") + return nil + } + + codesign := sh.RunCmd("codesign", "-s", b.SigningInfo().App.ID, "--timestamp") + targets := []string{ + filepath.Join("build", b.Configuration, "Beats.prefPane/Contents/MacOS/helper"), + filepath.Join("build", b.Configuration, "Beats.prefPane"), + } + + fmt.Println(">> Signing MacOS Preference Pane") + for _, target := range targets { + if err := codesign(target); err != nil { + return errors.Wrapf(err, "failed to codesign %v", target) + } + } + return nil +} + +func (b preferencePaneBuilder) Package() error { + output := filepath.Join("build", b.PackageName) + input := filepath.Join("build", b.Configuration, "Beats.prefPane") + + if mage.IsUpToDate(output, input) { + fmt.Println(">> Packaging MacOS Preference Pane (UP-TO-DATE)") + return nil + } + + fmt.Println(">> Packaging MacOS Preference Pane") + const pkgroot = "build/pkgroot" + installDir := filepath.Join(pkgroot, b.InstallDir, filepath.Base(input)) + if err := os.MkdirAll(installDir, 0755); err != nil { + return err + } + + if err := mage.Copy(input, installDir); err != nil { + return err + } + + pkgbuild := sh.RunCmd("pkgbuild") + args := []string{ + "--root", pkgroot, + "--identifier", b.Identifier, + "--version", b.Version, + } + if b.SigningInfo().Sign { + args = append(args, "--sign", b.SigningInfo().Installer.ID, "--timestamp") + } else { + fmt.Println("Skipping signing of MacOS " + b.PackageName + + " (APPLE_SIGNING_ENABLED not set to true)") + } + args = append(args, output) + + return pkgbuild(args...) +} diff --git a/dev-tools/mage/templates/common/README.md.tmpl b/dev-tools/packaging/templates/common/README.md.tmpl similarity index 100% rename from dev-tools/mage/templates/common/README.md.tmpl rename to dev-tools/packaging/templates/common/README.md.tmpl diff --git a/dev-tools/mage/templates/common/magefile.go.tmpl b/dev-tools/packaging/templates/common/magefile.go.tmpl similarity index 100% rename from dev-tools/mage/templates/common/magefile.go.tmpl rename to dev-tools/packaging/templates/common/magefile.go.tmpl diff --git a/dev-tools/packaging/templates/darwin/README.html.tmpl b/dev-tools/packaging/templates/darwin/README.html.tmpl new file mode 100644 index 00000000000..9c2b8687bfe --- /dev/null +++ b/dev-tools/packaging/templates/darwin/README.html.tmpl @@ -0,0 +1,36 @@ + +
+{{.Description}}
+ +To get started with {{.BeatName | title}}, you need to set up Elasticsearch on +your localhost first. After that, start {{.BeatName | title}} with:
+ + ./{{.BeatName}} -c {{.BeatName}}.yml -e
+
+
+This will start {{.BeatName | title }} and send the data to your Elasticsearch +instance. To load the dashboards for {{.BeatName | title}} into Kibana, run:
+ +./{{.BeatName}} setup -e
+
+
+For further steps visit the +Getting started guide.
+ +Visit Elastic.co Docs +for the full {{.BeatName | title}} documentation.
+ +https://www.elastic.co/guide/en/beats/libbeat/{{ beat_doc_branch }}/release-notes-{{.Version}}.html
+ + diff --git a/dev-tools/packaging/templates/darwin/component.plist.tmpl b/dev-tools/packaging/templates/darwin/component.plist.tmpl new file mode 100644 index 00000000000..5dd5da85fdb --- /dev/null +++ b/dev-tools/packaging/templates/darwin/component.plist.tmpl @@ -0,0 +1,5 @@ + + +