diff --git a/.ci/packaging.groovy b/.ci/packaging.groovy index 7af3ff6f60c6..325084ef181f 100644 --- a/.ci/packaging.groovy +++ b/.ci/packaging.groovy @@ -40,6 +40,7 @@ pipeline { parameters { booleanParam(name: 'macos', defaultValue: false, description: 'Allow macOS stages.') booleanParam(name: 'linux', defaultValue: true, description: 'Allow linux stages.') + booleanParam(name: 'arm', defaultValue: true, description: 'Allow ARM stages.') } stages { stage('Filter build') { @@ -83,12 +84,13 @@ pipeline { } } setEnvVar("GO_VERSION", readFile("${BASE_DIR}/.go-version").trim()) + // Stash without any build/dependencies context to support different architectures. + stashV2(name: 'source', bucket: "${JOB_GCS_BUCKET_STASH}", credentialsId: "${JOB_GCS_CREDENTIALS}") withMageEnv(){ dir("${BASE_DIR}"){ setEnvVar('BEAT_VERSION', sh(label: 'Get beat version', script: 'make get-version', returnStdout: true)?.trim()) } } - stashV2(name: 'source', bucket: "${JOB_GCS_BUCKET_STASH}", credentialsId: "${JOB_GCS_CREDENTIALS}") } } stage('Build Packages'){ @@ -172,12 +174,73 @@ pipeline { } steps { withGithubNotify(context: "Packaging MacOS ${BEATS_FOLDER}") { - deleteDir() + deleteWorkspace() withMacOSEnv(){ release() } } } + post { + always { + // static workers require this + deleteWorkspace() + } + } + } + } + } + } + stage('Build Packages ARM'){ + matrix { + axes { + axis { + name 'BEATS_FOLDER' + values ( + 'auditbeat', + 'filebeat', + 'heartbeat', + 'journalbeat', + 'metricbeat', + 'packetbeat', + 'x-pack/auditbeat', + 'x-pack/elastic-agent', + 'x-pack/filebeat', + 'x-pack/heartbeat', + 'x-pack/metricbeat', + 'x-pack/packetbeat' + ) + } + } + stages { + stage('Package Docker images for linux/arm64'){ + agent { label 'arm' } + options { skipDefaultCheckout() } + when { + beforeAgent true + expression { + return params.arm + } + } + environment { + HOME = "${env.WORKSPACE}" + PACKAGES = "docker" + PLATFORMS = [ + 'linux/arm64', + ].join(' ') + } + steps { + withGithubNotify(context: "Packaging linux/arm64 ${BEATS_FOLDER}") { + deleteWorkspace() + release() + pushCIDockerImages() + } + } + post { + always { + // static workers require this + deleteWorkspace() + } + } } } } @@ -408,14 +471,43 @@ def getBeatsName(baseDir) { } def withBeatsEnv(Closure body) { + unstashV2(name: 'source', bucket: "${JOB_GCS_BUCKET_STASH}", credentialsId: "${JOB_GCS_CREDENTIALS}") + fixPermissions() withMageEnv(){ withEnv([ "PYTHON_ENV=${WORKSPACE}/python-env" ]) { - unstashV2(name: 'source', bucket: "${JOB_GCS_BUCKET_STASH}", credentialsId: "${JOB_GCS_CREDENTIALS}") dir("${env.BASE_DIR}"){ body() } } } } + +/** +* This method fixes the filesystem permissions after the build has happenend. The reason is to +* ensure any non-ephemeral workers don't have any leftovers that could cause some environmental +* issues. +*/ +def deleteWorkspace() { + catchError(buildResult: 'SUCCESS', stageResult: 'SUCCESS') { + fixPermissions() + deleteDir() + } +} + +def fixPermissions() { + if(isUnix()) { + catchError(buildResult: 'SUCCESS', stageResult: 'SUCCESS') { + dir("${env.BASE_DIR}") { + if (fileExists('script/fix_permissions.sh')) { + sh(label: 'Fix permissions', script: """#!/usr/bin/env bash + set +x + source ./dev-tools/common.bash + docker_setup + script/fix_permissions.sh ${WORKSPACE}""", returnStatus: true) + } + } + } + } +} \ No newline at end of file diff --git a/dev-tools/mage/build.go b/dev-tools/mage/build.go index 2efe61502ae2..20a6946d7794 100644 --- a/dev-tools/mage/build.go +++ b/dev-tools/mage/build.go @@ -92,6 +92,7 @@ func GolangCrossBuild(params BuildArgs) error { } defer DockerChown(filepath.Join(params.OutputDir, params.Name+binaryExtension(GOOS))) + defer DockerChown(filepath.Join(params.OutputDir)) return Build(params) } diff --git a/dev-tools/mage/crossbuild.go b/dev-tools/mage/crossbuild.go index 4340c7fdb4ea..368bd0a422d7 100644 --- a/dev-tools/mage/crossbuild.go +++ b/dev-tools/mage/crossbuild.go @@ -43,11 +43,26 @@ const defaultCrossBuildTarget = "golangCrossBuild" // See NewPlatformList for details about platform filtering expressions. var Platforms = BuildPlatforms.Defaults() +// Types is the list of package types +var SelectedPackageTypes []PackageType + func init() { // Allow overriding via PLATFORMS. if expression := os.Getenv("PLATFORMS"); len(expression) > 0 { Platforms = NewPlatformList(expression) } + + // Allow overriding via PACKAGES. + if packageTypes := os.Getenv("PACKAGES"); len(packageTypes) > 0 { + for _, pkgtype := range strings.Split(packageTypes, ",") { + var p PackageType + err := p.UnmarshalText([]byte(pkgtype)) + if err != nil { + continue + } + SelectedPackageTypes = append(SelectedPackageTypes, p) + } + } } // CrossBuildOption defines a option to the CrossBuild target. @@ -169,12 +184,13 @@ func CrossBuildXPack(options ...CrossBuildOption) error { return CrossBuild(o...) } -// buildMage pre-compiles the magefile to a binary using the native GOOS/GOARCH -// values for Docker. It has the benefit of speeding up the build because the +// buildMage pre-compiles the magefile to a binary using the GOARCH parameter. +// It has the benefit of speeding up the build because the // mage -compile is done only once rather than in each Docker container. func buildMage() error { - return sh.RunWith(map[string]string{"CGO_ENABLED": "0"}, "mage", "-f", "-goos=linux", "-goarch=amd64", - "-compile", CreateDir(filepath.Join("build", "mage-linux-amd64"))) + arch := runtime.GOARCH + return sh.RunWith(map[string]string{"CGO_ENABLED": "0"}, "mage", "-f", "-goos=linux", "-goarch="+arch, + "-compile", CreateDir(filepath.Join("build", "mage-linux-"+arch))) } func crossBuildImage(platform string) (string, error) { @@ -185,6 +201,9 @@ func crossBuildImage(platform string) (string, error) { tagSuffix = "darwin" case strings.HasPrefix(platform, "linux/arm"): tagSuffix = "arm" + if runtime.GOARCH == "arm64" { + tagSuffix = "base-arm-debian9" + } case strings.HasPrefix(platform, "linux/mips"): tagSuffix = "mips" case strings.HasPrefix(platform, "linux/ppc"): @@ -231,9 +250,10 @@ func (b GolangCrossBuilder) Build() error { } workDir := filepath.ToSlash(filepath.Join(mountPoint, cwd)) - buildCmd, err := filepath.Rel(workDir, filepath.Join(mountPoint, repoInfo.SubDir, "build/mage-linux-amd64")) + builderArch := runtime.GOARCH + buildCmd, err := filepath.Rel(workDir, filepath.Join(mountPoint, repoInfo.SubDir, "build/mage-linux-"+builderArch)) if err != nil { - return errors.Wrap(err, "failed to determine mage-linux-amd64 relative path") + return errors.Wrap(err, "failed to determine mage-linux-"+builderArch+" relative path") } dockerRun := sh.RunCmd("docker", "run") diff --git a/dev-tools/mage/dockerbuilder.go b/dev-tools/mage/dockerbuilder.go index 503fcae9cfc2..d02abad2c576 100644 --- a/dev-tools/mage/dockerbuilder.go +++ b/dev-tools/mage/dockerbuilder.go @@ -70,15 +70,17 @@ func (b *dockerBuilder) Build() error { return errors.Wrap(err, "failed to prepare build") } + tries := 3 tag, err := b.dockerBuild() - if err != nil { + for err != nil && tries != 0 { fmt.Println(">> Building docker images again (after 10 seconds)") // This sleep is to avoid hitting the docker build issues when resources are not available. time.Sleep(10) tag, err = b.dockerBuild() - if err != nil { - return errors.Wrap(err, "failed to build docker") - } + tries -= 1 + } + if err != nil { + return errors.Wrap(err, "failed to build docker") } if err := b.dockerSave(tag); err != nil { @@ -199,6 +201,12 @@ func (b *dockerBuilder) dockerBuild() (string, error) { } func (b *dockerBuilder) dockerSave(tag string) error { + if _, err := os.Stat(distributionsDir); os.IsNotExist(err) { + err := os.MkdirAll(distributionsDir, 0750) + if err != nil { + return fmt.Errorf("cannot create folder for docker artifacts: %+v", err) + } + } // Save the container as artifact outputFile := b.OutputFile if outputFile == "" { diff --git a/dev-tools/mage/pkg.go b/dev-tools/mage/pkg.go index 4ecdec89d39a..2341724b3509 100644 --- a/dev-tools/mage/pkg.go +++ b/dev-tools/mage/pkg.go @@ -45,16 +45,26 @@ func Package() error { var tasks []interface{} for _, target := range Platforms { for _, pkg := range Packages { - if pkg.OS != target.GOOS() { + if pkg.OS != target.GOOS() || pkg.Arch != "" && pkg.Arch != target.Arch() { continue } for _, pkgType := range pkg.Types { + if !isPackageTypeSelected(pkgType) { + log.Printf("Skipping %s package type because it is not selected", pkgType) + continue + } + if pkgType == DMG && runtime.GOOS != "darwin" { log.Printf("Skipping DMG package type because build host isn't darwin") continue } + if target.Name == "linux/arm64" && pkgType == Docker && runtime.GOARCH != "arm64" { + log.Printf("Skipping Docker package type because build host isn't arm") + continue + } + packageArch, err := getOSArchName(target, pkgType) if err != nil { log.Printf("Skipping arch %v for package type %v: %v", target.Arch(), pkgType, err) @@ -106,6 +116,19 @@ func Package() error { return nil } +func isPackageTypeSelected(pkgType PackageType) bool { + if SelectedPackageTypes != nil { + selected := false + for _, t := range SelectedPackageTypes { + if t == pkgType { + selected = true + } + } + return selected + } + return true +} + type packageBuilder struct { Platform BuildPlatform Spec PackageSpec diff --git a/dev-tools/mage/pkgtypes.go b/dev-tools/mage/pkgtypes.go index b7f7c7bbbee8..ece8b73bfabc 100644 --- a/dev-tools/mage/pkgtypes.go +++ b/dev-tools/mage/pkgtypes.go @@ -68,6 +68,7 @@ const ( // system using the contained PackageSpec. type OSPackageArgs struct { OS string `yaml:"os"` + Arch string `yaml:"arch,omitempty"` Types []PackageType `yaml:"types"` Spec PackageSpec `yaml:"spec"` } @@ -172,6 +173,7 @@ var OSArchNames = map[string]map[PackageType]map[string]string{ }, Docker: map[string]string{ "amd64": "amd64", + "arm64": "arm64", }, }, } diff --git a/dev-tools/packaging/packages.yml b/dev-tools/packaging/packages.yml index 53a6573bfd5b..827ad577d0de 100644 --- a/dev-tools/packaging/packages.yml +++ b/dev-tools/packaging/packages.yml @@ -402,6 +402,12 @@ shared: {{ commit }} mode: 0644 + - &agent_docker_arm_spec + <<: *agent_docker_spec + extra_vars: + from: 'arm64v8/centos:7' + buildFrom: 'arm64v8/centos:7' + # Deb/RPM spec for community beats. - &deb_rpm_spec <<: *common @@ -556,11 +562,22 @@ shared: mode: 0600 config: true + - &docker_arm_spec + <<: *docker_spec + extra_vars: + from: 'arm64v8/centos:7' + buildFrom: 'arm64v8/centos:7' + - &docker_ubi_spec extra_vars: image_name: '{{.BeatName}}-ubi8' from: 'docker.elastic.co/ubi8/ubi-minimal' + - &docker_arm_ubi_spec + extra_vars: + image_name: '{{.BeatName}}-ubi8' + from: 'registry.access.redhat.com/ubi8/ubi-minimal:8.2' + - &elastic_docker_spec extra_vars: repository: 'docker.elastic.co/beats' @@ -724,17 +741,20 @@ specs: <<: *elastic_license_for_deb_rpm - os: linux + arch: amd64 types: [docker] spec: <<: *docker_spec + <<: *docker_ubi_spec <<: *elastic_docker_spec <<: *elastic_license_for_binaries - os: linux + arch: arm64 types: [docker] spec: - <<: *docker_spec - <<: *docker_ubi_spec + <<: *docker_arm_spec + <<: *docker_arm_ubi_spec <<: *elastic_docker_spec <<: *elastic_license_for_binaries @@ -813,9 +833,11 @@ specs: source: ./{{.XPackDir}}/{{.BeatName}}/build/golang-crossbuild/{{.BeatName}}-{{.GOOS}}-{{.Platform.Arch}}{{.BinaryExt}} - os: linux + arch: amd64 types: [docker] spec: <<: *docker_spec + <<: *docker_ubi_spec <<: *elastic_docker_spec <<: *elastic_license_for_binaries files: @@ -823,10 +845,11 @@ specs: source: ./{{.XPackDir}}/{{.BeatName}}/build/golang-crossbuild/{{.BeatName}}-{{.GOOS}}-{{.Platform.Arch}}{{.BinaryExt}} - os: linux + arch: arm64 types: [docker] spec: - <<: *docker_spec - <<: *docker_ubi_spec + <<: *docker_arm_spec + <<: *docker_arm_ubi_spec <<: *elastic_docker_spec <<: *elastic_license_for_binaries files: @@ -892,9 +915,11 @@ specs: mode: 0755 - os: linux + arch: amd64 types: [docker] spec: <<: *agent_docker_spec + <<: *docker_ubi_spec <<: *elastic_docker_spec <<: *elastic_license_for_binaries files: @@ -902,10 +927,11 @@ specs: source: ./build/golang-crossbuild/{{.BeatName}}-{{.GOOS}}-{{.Platform.Arch}}{{.BinaryExt}} - os: linux + arch: arm64 types: [docker] spec: - <<: *agent_docker_spec - <<: *docker_ubi_spec + <<: *agent_docker_arm_spec + <<: *docker_arm_ubi_spec <<: *elastic_docker_spec <<: *elastic_license_for_binaries files: diff --git a/dev-tools/packaging/templates/docker/Dockerfile.elastic-agent.tmpl b/dev-tools/packaging/templates/docker/Dockerfile.elastic-agent.tmpl index 844192627c67..cf2788af09ed 100644 --- a/dev-tools/packaging/templates/docker/Dockerfile.elastic-agent.tmpl +++ b/dev-tools/packaging/templates/docker/Dockerfile.elastic-agent.tmpl @@ -28,9 +28,7 @@ RUN mkdir -p {{ $beatHome }}/data {{ $beatHome }}/data/elastic-agent-{{ commit_s FROM {{ .from }} {{- if contains .from "ubi-minimal" }} -RUN for iter in {1..10}; do microdnf update -y && microdnf install -y shadow-utils && microdnf clean all && exit_code=0 && break || exit_code=$? && echo "microdnf error: retry $iter in 10s" && sleep 10; done; (exit $exit_code) -RUN curl -L https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 -o /usr/local/bin/jq && \ - chmod +x /usr/local/bin/jq +RUN for iter in {1..10}; do microdnf update -y && microdnf install -y shadow-utils jq && microdnf clean all && exit_code=0 && break || exit_code=$? && echo "microdnf error: retry $iter in 10s" && sleep 10; done; (exit $exit_code) {{- else }} # Installing jq needs to be installed after epel-release and cannot be in the same yum install command. RUN for iter in {1..10}; do yum update --setopt=tsflags=nodocs -y && yum install --setopt=tsflags=nodocs -y epel-release && yum clean all && exit_code=0 && break || exit_code=$? && echo "yum error: retry $iter in 10s" && sleep 10; done; (exit $exit_code) @@ -69,9 +67,22 @@ ENV GODEBUG="madvdontneed=1" # Add an init process, check the checksum to make sure it's a match RUN set -e ; \ - TINI_VERSION='v0.19.0' ; \ - TINI_BIN='tini-amd64' ; \ - TINI_SHA256='93dcc18adc78c65a028a84799ecf8ad40c936fdfc5f2a57b1acda5a8117fa82c' ; \ + TINI_BIN=""; \ + TINI_SHA256=""; \ + TINI_VERSION="v0.19.0"; \ + case "$(arch)" in \ + x86_64) \ + TINI_BIN="tini-amd64"; \ + TINI_SHA256="93dcc18adc78c65a028a84799ecf8ad40c936fdfc5f2a57b1acda5a8117fa82c"; \ + ;; \ + aarch64) \ + TINI_BIN="tini-arm64"; \ + TINI_SHA256="07952557df20bfd2a95f9bef198b445e006171969499a1d361bd9e6f8e5e0e81"; \ + ;; \ + *) \ + echo >&2 ; echo >&2 "Unsupported architecture \$(arch)" ; echo >&2 ; exit 1 ; \ + ;; \ + esac ; \ curl --retry 8 -S -L -O "https://github.com/krallin/tini/releases/download/${TINI_VERSION}/${TINI_BIN}" ; \ echo "${TINI_SHA256} ${TINI_BIN}" | sha256sum -c - ; \ mv "${TINI_BIN}" /usr/bin/tini ; \ diff --git a/dev-tools/packaging/templates/docker/Dockerfile.tmpl b/dev-tools/packaging/templates/docker/Dockerfile.tmpl index e42e525644c0..26302f0d1791 100644 --- a/dev-tools/packaging/templates/docker/Dockerfile.tmpl +++ b/dev-tools/packaging/templates/docker/Dockerfile.tmpl @@ -31,10 +31,10 @@ RUN microdnf -y --setopt=tsflags=nodocs update && \ RUN yum -y --setopt=tsflags=nodocs update \ {{- if (eq .BeatName "heartbeat") }} && yum -y install epel-release \ - && yum -y install atk cups gtk gdk xrandr pango.x86_64 libXcomposite.x86_64 libXcursor.x86_64 libXdamage.x86_64 \ - libXext.x86_64 libXi.x86_64 libXtst.x86_64 cups-libs.x86_64 libXScrnSaver.x86_64 libXrandr.x86_64 GConf2.x86_64 \ - alsa-lib.x86_64 atk.x86_64 gtk3.x86_64 ipa-gothic-fonts xorg-x11-fonts-100dpi xorg-x11-fonts-75dpi xorg-x11-utils \ - xorg-x11-fonts-cyrillic xorg-x11-fonts-Type1 xorg-x11-fonts-misc \ + && yum -y install atk cups gtk gdk xrandr pango libXcomposite libXcursor libXdamage \ + libXext libXi libXtst cups-libs libXScrnSaver libXrandr GConf2 \ + alsa-lib atk gtk3 ipa-gothic-fonts xorg-x11-fonts-100dpi xorg-x11-fonts-75dpi xorg-x11-utils \ + xorg-x11-fonts-cyrillic xorg-x11-fonts-Type1 xorg-x11-fonts-misc \ {{- end }} && yum clean all && rm -rf /var/cache/yum # See https://access.redhat.com/discussions/3195102 for why rm is needed @@ -83,9 +83,22 @@ ENV GODEBUG="madvdontneed=1" # Add an init process, check the checksum to make sure it's a match RUN set -e ; \ - TINI_VERSION='v0.19.0' ; \ - TINI_BIN='tini-amd64' ; \ - TINI_SHA256='93dcc18adc78c65a028a84799ecf8ad40c936fdfc5f2a57b1acda5a8117fa82c' ; \ + TINI_BIN=""; \ + TINI_SHA256=""; \ + TINI_VERSION="v0.19.0"; \ + case "$(arch)" in \ + x86_64) \ + TINI_BIN="tini-amd64"; \ + TINI_SHA256="93dcc18adc78c65a028a84799ecf8ad40c936fdfc5f2a57b1acda5a8117fa82c"; \ + ;; \ + aarch64) \ + TINI_BIN="tini-arm64"; \ + TINI_SHA256="07952557df20bfd2a95f9bef198b445e006171969499a1d361bd9e6f8e5e0e81"; \ + ;; \ + *) \ + echo >&2 ; echo >&2 "Unsupported architecture \$(arch)" ; echo >&2 ; exit 1 ; \ + ;; \ + esac ; \ curl --retry 8 -S -L -O "https://github.com/krallin/tini/releases/download/${TINI_VERSION}/${TINI_BIN}" ; \ echo "${TINI_SHA256} ${TINI_BIN}" | sha256sum -c - ; \ mv "${TINI_BIN}" /usr/bin/tini ; \ @@ -119,8 +132,20 @@ ENV PATH="$NODE_PATH/node/bin:$PATH" # cached node_modules, heartbeat then calls the global executable to run test suites # Setup node RUN cd /usr/share/heartbeat/.node \ + && NODE_DOWNLOAD_URL="" \ + && case "$(arch)" in \ + x86_64) \ + NODE_DOWNLOAD_URL=https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.xz \ + ;; \ + aarch64) \ + NODE_DOWNLOAD_URL=https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-arm64.tar.xz \ + ;; \ + *) \ + echo >&2 ; echo >&2 "Unsupported architecture \$(arch)" ; echo >&2 ; exit 1 ; \ + ;; \ + esac \ && mkdir -p node \ - && curl https://nodejs.org/dist/v12.18.4/node-v12.18.4-linux-x64.tar.xz | tar -xJ --strip 1 -C node \ + && curl ${NODE_DOWNLOAD_URL} | tar -xJ --strip 1 -C node \ && chmod ug+rwX -R $NODE_PATH \ && npm i -g -f @elastic/synthetics && chmod ug+rwX -R $NODE_PATH {{- end }} diff --git a/journalbeat/magefile.go b/journalbeat/magefile.go index 0644ce7d275f..46d962c69b42 100644 --- a/journalbeat/magefile.go +++ b/journalbeat/magefile.go @@ -21,6 +21,7 @@ package main import ( "fmt" + "runtime" "strings" "time" @@ -138,6 +139,9 @@ func selectImage(platform string) (string, error) { switch { case strings.HasPrefix(platform, "linux/arm"): tagSuffix = "arm" + if runtime.GOARCH == "arm64" { + tagSuffix = "base-arm-debian9" + } case strings.HasPrefix(platform, "linux/mips"): tagSuffix = "mips" case strings.HasPrefix(platform, "linux/ppc"): diff --git a/script/fix_permissions.sh b/script/fix_permissions.sh index fd65a7916b50..fce5067f7279 100755 --- a/script/fix_permissions.sh +++ b/script/fix_permissions.sh @@ -5,7 +5,14 @@ readonly LOCATION="${1?Please define the path where the fix permissions should r if ! docker version ; then echo "It requires Docker daemon to be installed and running" else + ## Detect architecture to support ARM specific docker images. + ARCH=$(uname -m| tr '[:upper:]' '[:lower:]') + if [ "${ARCH}" == "aarch64" ] ; then + DOCKER_IMAGE=arm64v8/alpine:3 + else + DOCKER_IMAGE=alpine:3.4 + fi set -e # Change ownership of all files inside the specific folder from root/root to current user/group - docker run -v ${LOCATION}:/beat alpine:3.4 sh -c "find /beat -user 0 -exec chown -h $(id -u):$(id -g) {} \;" + docker run -v "${LOCATION}":/beat ${DOCKER_IMAGE} sh -c "find /beat -user 0 -exec chown -h $(id -u):$(id -g) {} \;" fi diff --git a/x-pack/elastic-agent/magefile.go b/x-pack/elastic-agent/magefile.go index fad5ef935aac..3918b6da4033 100644 --- a/x-pack/elastic-agent/magefile.go +++ b/x-pack/elastic-agent/magefile.go @@ -591,6 +591,9 @@ func packageAgent(requiredPackages []string, packagingFn func()) { cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Env = append(os.Environ(), fmt.Sprintf("PWD=%s", pwd), "AGENT_PACKAGING=on") + if envVar := selectedPackageTypes(); envVar != "" { + cmd.Env = append(cmd.Env, envVar) + } if err := cmd.Run(); err != nil { panic(err) @@ -613,6 +616,22 @@ func packageAgent(requiredPackages []string, packagingFn func()) { mg.SerialDeps(devtools.Package, TestPackages) } +func selectedPackageTypes() string { + if len(devtools.SelectedPackageTypes) == 0 { + return "" + } + + envVar := "PACKAGES=" + for _, p := range devtools.SelectedPackageTypes { + if p == devtools.Docker { + envVar += "targz," + } else { + envVar += p.String() + "," + } + } + return envVar[:len(envVar)-1] +} + func copyAll(from, to string) error { return filepath.Walk(from, func(path string, info os.FileInfo, err error) error { if err != nil {