From 038b56e31e8e51ecd3fffdfcc74192c09fb7c7bd Mon Sep 17 00:00:00 2001 From: John Kleinschmidt Date: Thu, 4 Oct 2018 12:01:16 -0400 Subject: [PATCH] ci: Trigger an arm test on VSTS after CircleCI build (#14898) * ci: Trigger an arm test on VSTS after CircleCI build --- .circleci/config.yml | 23 ++++++++ Dockerfile.arm32v7 | 61 +++++++++++++++++++++ Dockerfile.arm64 | 35 ------------ Dockerfile.arm64v8 | 34 ++++++------ Dockerfile.armv7 | 36 ------------- script/ci-release-build.js | 24 ++++++--- script/download-circleci-artifacts.js | 78 +++++++++++++++++++++++++++ spec/api-desktop-capturer-spec.js | 2 +- spec/api-notification-dbus-spec.js | 1 + vsts-arm-test-steps.yml | 65 ++++++++++++++++++++++ vsts-arm32v7.yml | 12 +++++ vsts-arm64v8.yml | 12 +++++ 12 files changed, 289 insertions(+), 94 deletions(-) create mode 100644 Dockerfile.arm32v7 delete mode 100644 Dockerfile.arm64 delete mode 100644 Dockerfile.armv7 create mode 100644 script/download-circleci-artifacts.js create mode 100644 vsts-arm-test-steps.yml create mode 100644 vsts-arm32v7.yml create mode 100644 vsts-arm64v8.yml diff --git a/.circleci/config.yml b/.circleci/config.yml index ea4dbbb4d2..75cf453eb3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -182,6 +182,11 @@ step-nodejs-headers-build: &step-nodejs-headers-build cd src ninja -C out/Default third_party/electron_node:headers +step-nodejs-headers-store: &step-nodejs-headers-store + store_artifacts: + path: src/out/Default/gen/node_headers.tar.gz + destination: node_headers.tar.gz + step-electron-publish: &step-electron-publish run: name: Publish Electron Dist @@ -314,6 +319,17 @@ step-maybe-native-mksnapshot-store: &step-maybe-native-mksnapshot-store path: src/out/native_mksnapshot/mksnapshot.zip destination: native_mksnapshot.zip +step-maybe-trigger-arm-test: &step-maybe-trigger-arm-test + run: + name: Trigger an arm test on VSTS if applicable + command: | + cd src + # Only run for non-fork prs + if [ "$TRIGGER_ARM_TEST" == "true" ] && [ -z "$CIRCLE_PR_NUMBER" ]; then + #Trigger VSTS job, passing along CircleCI job number and branch to build + echo "Triggering electron-$TARGET_ARCH-testing build on VSTS" + node electron/script/ci-release-build.js --job=electron-$TARGET_ARCH-testing --ci=VSTS --armTest --circleBuildNum=$CIRCLE_BUILD_NUM $CIRCLE_BRANCH + fi # Lists of steps. steps-checkout: &steps-checkout steps: @@ -377,12 +393,16 @@ steps-electron-build-for-tests: &steps-electron-build-for-tests # Node.js headers - *step-nodejs-headers-build + - *step-nodejs-headers-store - *step-show-sccache-stats # Save all data needed for a further tests run. - *step-persist-data-for-tests + # Trigger tests on arm hardware if needed + - *step-maybe-trigger-arm-test + - *step-maybe-notify-slack-failure - *step-maybe-notify-slack-success @@ -416,6 +436,7 @@ steps-electron-build-for-publish: &steps-electron-build-for-publish # Node.js headers - *step-nodejs-headers-build + - *step-nodejs-headers-store # ffmpeg - *step-ffmpeg-gn-gen @@ -713,6 +734,7 @@ jobs: <<: *env-arm <<: *env-testing-build <<: *env-enable-sccache + TRIGGER_ARM_TEST: true <<: *steps-electron-build-for-tests linux-arm-ffmpeg: @@ -768,6 +790,7 @@ jobs: <<: *env-arm64 <<: *env-testing-build <<: *env-enable-sccache + TRIGGER_ARM_TEST: true <<: *steps-electron-build-for-tests linux-arm64-ffmpeg: diff --git a/Dockerfile.arm32v7 b/Dockerfile.arm32v7 new file mode 100644 index 0000000000..ee2e2fa05a --- /dev/null +++ b/Dockerfile.arm32v7 @@ -0,0 +1,61 @@ +FROM arm32v7/ubuntu:16.04 + +RUN groupadd --gid 1000 builduser \ + && useradd --uid 1000 --gid builduser --shell /bin/bash --create-home builduser + +# Set up TEMP directory +ENV TEMP=/tmp +RUN chmod a+rwx /tmp + +RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \ + bison \ + build-essential \ + clang \ + curl \ + gperf \ + git \ + libasound2 \ + libasound2-dev \ + libcap-dev \ + libcups2-dev \ + libdbus-1-dev \ + libgconf-2-4 \ + libgconf2-dev \ + libgnome-keyring-dev \ + libgtk2.0-0 \ + libgtk2.0-dev \ + libgtk-3-0 \ + libgtk-3-dev \ + libnotify-bin \ + libnss3 \ + libnss3-dev \ + libxss1 \ + libxtst-dev \ + libxtst6 \ + lsb-release \ + locales \ + nano \ + python-setuptools \ + python-pip \ + python-dbusmock \ + sudo \ + unzip \ + wget \ + xvfb \ +&& rm -rf /var/lib/apt/lists/* + +# Install Node.js +RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y nodejs \ + && rm -rf /var/lib/apt/lists/* + +# crcmod is required by gsutil, which is used for filling the gclient git cache +RUN pip install -U crcmod + +ADD tools/xvfb-init.sh /etc/init.d/xvfb +RUN chmod a+x /etc/init.d/xvfb + +RUN usermod -aG sudo builduser +RUN echo 'builduser ALL=(ALL:ALL) NOPASSWD:ALL' >> /etc/sudoers + +WORKDIR /home/builduser diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 deleted file mode 100644 index 721b817269..0000000000 --- a/Dockerfile.arm64 +++ /dev/null @@ -1,35 +0,0 @@ -FROM multiarch/debian-debootstrap:arm64-jessie - -RUN apt-get update && apt-get install -y\ - bison \ - build-essential \ - clang \ - curl \ - gperf \ - libasound2 \ - libasound2-dev \ - libcap-dev \ - libcups2-dev \ - libdbus-1-dev \ - libgconf-2-4 \ - libgconf2-dev \ - libgnome-keyring-dev \ - libgtk-3-0 \ - libgtk-3-dev \ - libnotify-bin \ - libnotify-dev \ - libnss3 \ - libnss3-dev \ - libx11-xcb-dev \ - libxss1 \ - libxtst-dev \ - libxtst6 \ - python-dbusmock \ - wget \ - xvfb - -ADD tools/xvfb-init.sh /etc/init.d/xvfb -RUN chmod a+x /etc/init.d/xvfb -ADD tools/run-electron.sh /run-electron.sh -RUN chmod a+x /run-electron.sh -CMD sh /run-electron.sh diff --git a/Dockerfile.arm64v8 b/Dockerfile.arm64v8 index a95dcfb73d..50f72bf98d 100644 --- a/Dockerfile.arm64v8 +++ b/Dockerfile.arm64v8 @@ -3,14 +3,13 @@ FROM arm64v8/ubuntu:16.04 RUN groupadd --gid 1000 builduser \ && useradd --uid 1000 --gid builduser --shell /bin/bash --create-home builduser -RUN groupadd --gid 114 jenkins \ - && useradd --uid 110 --gid jenkins --shell /bin/bash --create-home jenkins - # Set up TEMP directory ENV TEMP=/tmp RUN chmod a+rwx /tmp -RUN apt-get update && apt-get install -y\ +RUN dpkg --add-architecture armhf + +RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \ bison \ build-essential \ clang \ @@ -19,6 +18,7 @@ RUN apt-get update && apt-get install -y\ git \ libasound2 \ libasound2-dev \ + libc6:armhf \ libcap-dev \ libcups2-dev \ libdbus-1-dev \ @@ -29,35 +29,37 @@ RUN apt-get update && apt-get install -y\ libgtk2.0-dev \ libgtk-3-0 \ libgtk-3-dev \ - libnotify-dev \ + libnotify-bin \ libnss3 \ libnss3-dev \ - libx11-xcb-dev \ + libstdc++6:armhf \ libxss1 \ libxtst-dev \ libxtst6 \ lsb-release \ locales \ - ninja \ + nano \ python-setuptools \ python-pip \ python-dbusmock \ + sudo \ + unzip \ wget \ - xvfb + xvfb \ +&& rm -rf /var/lib/apt/lists/* -# Install node.js -RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - -RUN apt-get update && apt-get install -y nodejs +# Install Node.js +RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y nodejs \ + && rm -rf /var/lib/apt/lists/* -# Install crcmod +# crcmod is required by gsutil, which is used for filling the gclient git cache RUN pip install -U crcmod ADD tools/xvfb-init.sh /etc/init.d/xvfb RUN chmod a+x /etc/init.d/xvfb -# Install ninja in /usr/local -RUN cd /usr/local && git clone https://github.com/martine/ninja.git -b v1.5.3 -RUN cd /usr/local/ninja && ./configure.py --bootstrap +RUN usermod -aG sudo builduser +RUN echo 'builduser ALL=(ALL:ALL) NOPASSWD:ALL' >> /etc/sudoers -USER builduser WORKDIR /home/builduser diff --git a/Dockerfile.armv7 b/Dockerfile.armv7 deleted file mode 100644 index 7246ec432f..0000000000 --- a/Dockerfile.armv7 +++ /dev/null @@ -1,36 +0,0 @@ -FROM multiarch/debian-debootstrap:armhf-jessie - -RUN apt-get update && apt-get install -y\ - bison \ - build-essential \ - clang \ - curl \ - gperf \ - libasound2 \ - libasound2-dev \ - libcap-dev \ - libcups2-dev \ - libdbus-1-dev \ - libgconf-2-4 \ - libgconf2-dev \ - libgnome-keyring-dev \ - libgtk-3-0 \ - libgtk-3-dev \ - libnotify-bin \ - libnotify-dev \ - libnss3 \ - libnss3-dev \ - libx11-xcb-dev \ - libxss1 \ - libxtst-dev \ - libxtst6 \ - python-dbusmock \ - git \ - wget \ - xvfb - -ADD tools/xvfb-init.sh /etc/init.d/xvfb -RUN chmod a+x /etc/init.d/xvfb -ADD tools/run-electron.sh /run-electron.sh -RUN chmod a+x /run-electron.sh -CMD sh /run-electron.sh diff --git a/script/ci-release-build.js b/script/ci-release-build.js index 77651e8079..a7e5b06ff6 100644 --- a/script/ci-release-build.js +++ b/script/ci-release-build.js @@ -1,4 +1,4 @@ -require('dotenv-safe').load() +if (!process.env.CI) require('dotenv-safe').load() const assert = require('assert') const request = require('request') @@ -22,6 +22,11 @@ const vstsJobs = [ 'electron-release-osx-x64' ] +const vstsArmJobs = [ + 'electron-arm-testing', + 'electron-arm64-testing' +] + async function makeRequest (requestOptions, parseResponse) { return new Promise((resolve, reject) => { request(requestOptions, (err, res, body) => { @@ -135,7 +140,9 @@ function buildCircleCI (targetBranch, options) { } async function buildVSTS (targetBranch, options) { - if (options.job) { + if (options.armTest) { + assert(vstsArmJobs.includes(options.job), `Unknown VSTS CI arm test job name: ${options.job}. Valid values are: ${vstsArmJobs}.`) + } else if (options.job) { assert(vstsJobs.includes(options.job), `Unknown VSTS CI job name: ${options.job}. Valid values are: ${vstsJobs}.`) } console.log(`Triggering VSTS to run build on branch: ${targetBranch} with release flag.`) @@ -143,8 +150,12 @@ async function buildVSTS (targetBranch, options) { ELECTRON_RELEASE: 1 } - if (!options.ghRelease) { - environmentVariables.UPLOAD_TO_S3 = 1 + if (options.armTest) { + environmentVariables.CIRCLE_BUILD_NUM = options.circleBuildNum + } else { + if (!options.ghRelease) { + environmentVariables.UPLOAD_TO_S3 = 1 + } } const requestOpts = { @@ -226,12 +237,13 @@ module.exports = runRelease if (require.main === module) { const args = require('minimist')(process.argv.slice(2), { - boolean: ['ghRelease', 'automaticRelease'] + boolean: ['ghRelease', 'automaticRelease', 'armTest'] }) const targetBranch = args._[0] if (args._.length < 1) { console.log(`Trigger CI to build release builds of electron. - Usage: ci-release-build.js [--job=CI_JOB_NAME] [--ci=CircleCI|AppVeyor|VSTS] [--ghRelease] [--automaticRelease] TARGET_BRANCH + Usage: ci-release-build.js [--job=CI_JOB_NAME] [--ci=CircleCI|AppVeyor|VSTS] + [--ghRelease] [--automaticRelease] [--armTest] [--circleBuildNum=xxx] TARGET_BRANCH `) process.exit(0) } diff --git a/script/download-circleci-artifacts.js b/script/download-circleci-artifacts.js new file mode 100644 index 0000000000..484dc15b8f --- /dev/null +++ b/script/download-circleci-artifacts.js @@ -0,0 +1,78 @@ +const args = require('minimist')(process.argv.slice(2)) +const nugget = require('nugget') +const request = require('request') + +async function makeRequest (requestOptions, parseResponse) { + return new Promise((resolve, reject) => { + request(requestOptions, (err, res, body) => { + if (!err && res.statusCode >= 200 && res.statusCode < 300) { + if (parseResponse) { + const build = JSON.parse(body) + resolve(build) + } else { + resolve(body) + } + } else { + console.error('Error occurred while requesting:', requestOptions.url) + if (parseResponse) { + try { + console.log('Error: ', `(status ${res.statusCode})`, err || JSON.parse(res.body), requestOptions) + } catch (err) { + console.log('Error: ', `(status ${res.statusCode})`, err || res.body, requestOptions) + } + } else { + console.log('Error: ', `(status ${res.statusCode})`, err || res.body, requestOptions) + } + reject(err) + } + }) + }) +} + +async function downloadArtifact (name, buildNum, dest) { + const circleArtifactUrl = `https://circleci.com/api/v1.1/project/github/electron/electron/${args.buildNum}/artifacts?circle-token=${process.env.CIRCLE_TOKEN}` + const artifacts = await makeRequest({ + method: 'GET', + url: circleArtifactUrl, + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + } + }, true).catch(err => { + console.log('Error calling CircleCI:', err) + }) + const artifactToDownload = artifacts.find(artifact => { + return (artifact.path === name) + }) + if (!artifactToDownload) { + console.log(`Could not find artifact called ${name} to download for build #${buildNum}.`) + process.exit(1) + } else { + console.log(`Downloading ${artifactToDownload.url}.`) + await downloadFile(artifactToDownload.url, dest) + console.log(`Successfully downloaded ${name}.`) + } +} + +function downloadFile (url, directory) { + return new Promise((resolve, reject) => { + const nuggetOpts = { + dir: directory + } + nugget(url, nuggetOpts, (err) => { + if (err) { + reject(err) + } else { + resolve() + } + }) + }) +} + +if (!args.name || !args.buildNum || !args.dest) { + console.log(`Download CircleCI artifacts. + Usage: download-circleci-artifacts.js [--buildNum=CIRCLE_BUILD_NUMBER] [--name=artifactName] [--dest]`) + process.exit(0) +} else { + downloadArtifact(args.name, args.buildNum, args.dest) +} diff --git a/spec/api-desktop-capturer-spec.js b/spec/api-desktop-capturer-spec.js index b23859a9ed..42377df3e8 100644 --- a/spec/api-desktop-capturer-spec.js +++ b/spec/api-desktop-capturer-spec.js @@ -10,7 +10,7 @@ const isCI = remote.getGlobal('isCi') describe('desktopCapturer', () => { before(function () { - if (!features.isDesktopCapturerEnabled()) { + if (!features.isDesktopCapturerEnabled() || process.arch.indexOf('arm') === 0) { // It's been disabled during build time. this.skip() return diff --git a/spec/api-notification-dbus-spec.js b/spec/api-notification-dbus-spec.js index d61275be1f..34eb78c949 100644 --- a/spec/api-notification-dbus-spec.js +++ b/spec/api-notification-dbus-spec.js @@ -15,6 +15,7 @@ const { app } = remote.require('electron') const skip = process.platform !== 'linux' || process.arch === 'ia32' || + process.arch.indexOf('arm') === 0 || !process.env.DBUS_SESSION_BUS_ADDRESS; (skip ? describe.skip : describe)('Notification module (dbus)', () => { diff --git a/vsts-arm-test-steps.yml b/vsts-arm-test-steps.yml new file mode 100644 index 0000000000..ac8df09c62 --- /dev/null +++ b/vsts-arm-test-steps.yml @@ -0,0 +1,65 @@ +steps: +- task: CopyFiles@2 + displayName: 'Copy Files to: src/electron' + inputs: + TargetFolder: src/electron + +- bash: | + cd src/electron + npm install --verbose + displayName: 'NPM install' + +- bash: | + export ZIP_DEST=$PWD/src/out/Default + mkdir -p $ZIP_DEST + cd src/electron + node script/download-circleci-artifacts.js --buildNum=$CIRCLE_BUILD_NUM --name=dist.zip --dest=$ZIP_DEST + cd $ZIP_DEST + unzip -o dist.zip + displayName: 'Download and unzip dist files for test' + env: + CIRCLE_TOKEN: $(CIRCLECI_TOKEN) + +- bash: | + export NODE_HEADERS_DEST=$PWD/src/out/Default/gen + mkdir -p $NODE_HEADERS_DEST + cd src/electron + node script/download-circleci-artifacts.js --buildNum=$CIRCLE_BUILD_NUM --name=node_headers.tar.gz --dest=$NODE_HEADERS_DEST + cd $NODE_HEADERS_DEST + tar xzf node_headers.tar.gz + displayName: 'Download and untar node header files for test' + env: + CIRCLE_TOKEN: $(CIRCLECI_TOKEN) + +- bash: | + cd src + export npm_config_nodedir=$PWD/out/Default/gen/node_headers + cd electron/spec + npm install --verbose + displayName: Install test modules + +- bash: | + sh -e /etc/init.d/xvfb start + displayName: Setup for headless testing + env: + DISPLAY: ":99.0" + +- bash: | + cd src + ./out/Default/electron electron/spec --ci --enable-logging + displayName: 'Run Electron tests' + timeoutInMinutes: 10 + +- task: PublishTestResults@2 + displayName: 'Publish Test Results' + inputs: + testResultsFiles: '*.xml' + + searchFolder: '$(System.DefaultWorkingDirectory)/src/junit/' + + condition: succeededOrFailed() + +- task: mspremier.PostBuildCleanup.PostBuildCleanup-task.PostBuildCleanup@3 + displayName: 'Clean Agent Directories' + + condition: always() diff --git a/vsts-arm32v7.yml b/vsts-arm32v7.yml new file mode 100644 index 0000000000..865649cf2b --- /dev/null +++ b/vsts-arm32v7.yml @@ -0,0 +1,12 @@ +resources: + containers: + - container: arm32v7-test-container + image: electronbuilds/arm32v7:0.0.1 + +jobs: +- job: Test_Arm32v7 + container: arm32v7-test-container + displayName: Test Arm on Arm32v7 hardware + timeoutInMinutes: 30 + steps: + - template: vsts-arm-test-steps.yml diff --git a/vsts-arm64v8.yml b/vsts-arm64v8.yml new file mode 100644 index 0000000000..92be8a55aa --- /dev/null +++ b/vsts-arm64v8.yml @@ -0,0 +1,12 @@ +resources: + containers: + - container: arm64v8-test-container + image: electronbuilds/arm64v8:0.0.4 + +jobs: +- job: Test_Arm64 + container: arm64v8-test-container + displayName: Test Arm64 on Arm64 hardware + timeoutInMinutes: 30 + steps: + - template: vsts-arm-test-steps.yml