From a63a3e9464ad1fb5803d421740a5196457f15d12 Mon Sep 17 00:00:00 2001 From: Jakub Malinowski Date: Thu, 17 Dec 2020 18:23:36 +0100 Subject: [PATCH] chore: migrate CircleCI to GH Actions --- .circleci/config.yml | 34 ------- .github/workflows/.gitkeep | 0 .github/workflows/ci.yml | 98 +++++++++++++++++++ integration-tests/utils/browserstack.conf.js | 2 - .../utils/browserstack.runner.js | 57 ++++++----- integration-tests/utils/buildApi.js | 29 +----- integration-tests/utils/local.conf.js | 10 ++ karma.conf.js | 45 +++++---- package.json | 7 +- 9 files changed, 166 insertions(+), 116 deletions(-) delete mode 100644 .circleci/config.yml create mode 100644 .github/workflows/.gitkeep create mode 100644 .github/workflows/ci.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index a861bac3..00000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,34 +0,0 @@ -version: 2.1 - -browsers_unit_tests: &browsers_unit_tests - resource_class: large - steps: - - checkout - - run: git submodule update --init --recursive - - run: npm install - - run: npm run build:release - - run: npx eslint --no-eslintrc --env es2015 dist/splunk-rum.js - - run: npx eslint --no-eslintrc --no-inline-config --env es2015 dist/splunk-rum.debug.js - - run: npm run lint:markdown - - run: npm run test - - run: npm run test:integration:local:_execute - - run: npm run test:integration:local:firefox:_execute - - run: npm run test:integration:_execute - - - store_artifacts: - path: dist/splunk-rum.js - destination: splunk-rum.js - - store_artifacts: - path: dist/splunk-rum.debug.js - destination: splunk-rum.debug.js - -jobs: - node-browsers: - docker: - - image: circleci/node:12-browsers - <<: *browsers_unit_tests - -workflows: - build-and-test: - jobs: - - node-browsers diff --git a/.github/workflows/.gitkeep b/.github/workflows/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..970d734e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,98 @@ +name: Continuous Integration +on: + push: + branches: [master] + pull_request: + +jobs: + unit-tests: + runs-on: ubuntu-latest + steps: + - name: Ensure that Chrome is present + run: google-chrome --version + - name: Ensure that Firefox is present + run: firefox --version + - name: Checkout + uses: actions/checkout@v1 + - name: Fix ssh+git access + run: git config --global url."https://github.com/".insteadOf "git@github.com:" + - name: Checkout submodules + run: git submodule update --init --recursive + - name: Cache Dependencies + uses: actions/cache@v2 + with: + path: | + node_modules + package-lock.json + packages/*/node_modules + key: ${{ runner.os }}-${{ hashFiles('**/package.json') }} + - run: npm install + - name: Lint code + run: npm run lint + - name: Lint docs + run: npm run lint:markdown + - run: npm run build:prod + - name: Unit test + run: npm run test + + browserstack-integration-tests: + runs-on: ubuntu-latest + env: + BROWSERSTACK_USER: ${{ secrets.BROWSERSTACK_USER }} + BROWSERSTACK_KEY: ${{ secrets.BROWSERSTACK_KEY }} + strategy: + fail-fast: false + matrix: + browser: [safari, edge] + experimental: [true] # due to instabilities of integration tests, let these fail, provided we review failures manually + steps: + - name: Checkout + uses: actions/checkout@v1 + - name: Fix ssh+git access + run: git config --global url."https://github.com/".insteadOf "git@github.com:" + - name: Checkout submodules + run: git submodule update --init --recursive + - name: Cache Dependencies + uses: actions/cache@v2 + with: + path: | + node_modules + package-lock.json + packages/*/node_modules + key: ${{ runner.os }}-${{ hashFiles('**/package.json') }} + - run: npm install + - run: npm run build:debug + - run: npm run build:prod + - name: Run integration tests in Browserstack + run: node ./integration-tests/utils/browserstack.runner.js -e ${{ matrix.browser }} -c ./integration-tests/utils/browserstack.conf.js + continue-on-error: true + timeout-minutes: 10 + + local-integration-tests: + runs-on: ubuntu-latest + steps: + - name: Ensure that Chrome is present + run: google-chrome --version + - name: Ensure that Firefox is present + run: firefox --version + - name: Checkout + uses: actions/checkout@v1 + - name: Fix ssh+git access for public submodules + run: git config --global url."https://github.com/".insteadOf "git@github.com:" + - name: Checkout submodules + run: git submodule update --init --recursive + - name: Cache Dependencies + uses: actions/cache@v2 + with: + path: | + node_modules + package-lock.json + packages/*/node_modules + key: ${{ runner.os }}-${{ hashFiles('**/package.json') }} + - run: npm install + - run: npm run build:debug + - run: npm run build:prod + - name: Local integration tests using Headless Chrome + run: npm run test:integration:local:_execute + - name: Local integration tests using Firefox + run: npm run test:integration:local:headlessFirefox:_execute diff --git a/integration-tests/utils/browserstack.conf.js b/integration-tests/utils/browserstack.conf.js index c4bbb88e..237f702f 100644 --- a/integration-tests/utils/browserstack.conf.js +++ b/integration-tests/utils/browserstack.conf.js @@ -15,7 +15,6 @@ limitations under the License. */ const path = require('path'); -const { getCurrentBuildId } = require('./buildApi'); console.log('Loading config.'); if (!process.env.BROWSERSTACK_USER || !process.env.BROWSERSTACK_KEY) { @@ -57,7 +56,6 @@ const nightwatch_config = { // metadata for grouping sessions in browserstack project: require('../../package.json').name, - build: getCurrentBuildId(), }, globals: { host: 'bs-local.com', diff --git a/integration-tests/utils/browserstack.runner.js b/integration-tests/utils/browserstack.runner.js index 92447aa0..b14088fe 100755 --- a/integration-tests/utils/browserstack.runner.js +++ b/integration-tests/utils/browserstack.runner.js @@ -19,11 +19,14 @@ require('dotenv').config(); const Nightwatch = require('nightwatch'); const browserstack = require('browserstack-local'); const { clearBuildId, generateBuildId } = require('./buildApi'); -const path = require('path'); -let browserstackLocal; -function createTunnel() { +function generateHex(length) { + return [...Array(length).keys()].map(() => parseInt(Math.random() * 16).toString(16)).join(''); +} + +let browserstackLocal; +function createTunnel({localIdentifier}) { return new Promise((resolve, reject) => { Nightwatch.browserstackLocal = browserstackLocal = new browserstack.Local(); @@ -32,7 +35,11 @@ function createTunnel() { throw new Error('You need to provide environment variable: BROWSERSTACK_KEY.'); } - browserstackLocal.start({ key }, function(error) { + browserstackLocal.start({ + key, + verbose: true, + localIdentifier, + }, function(error) { if (error) { reject(error); } else { @@ -44,37 +51,31 @@ function createTunnel() { }); } -(async function() { +async function runTests(argv) { let buildId; try { buildId = generateBuildId(); console.log(`Starting build ${buildId}`); console.log('Starting tunnel.'); - const tunnelHandle = await createTunnel(); + const localIdentifier = generateHex(32); + const tunnelHandle = await createTunnel({localIdentifier}); console.log('Tunnel started.'); - // TODO: restructure so that we don't have to rely on a path to the config file - const configPath = path.join(__dirname, 'browserstack.conf.js'); - console.log(`Loading config from ${configPath}`); - - const envs = ['safari', 'edge']; - const reporters = []; - for (const env of envs) { - const runner = new Nightwatch.CliRunner({ - config: configPath, - env, - - // note: this can be used to scope down tests, leaving here so I don't need to search for this in the future - // test: path.join(__dirname, '..', 'tests', 'websocket', 'websockets.spec.js') - }); - runner.setup(); - await runner.runTests(); - - reporters.push(runner.testRunner); - } + const runner = new Nightwatch.CliRunner({ + ...argv, + // note: this can be used to scope down tests, leaving here so I don't need to search for this in the future + // test: path.join(__dirname, '..', 'tests', 'websocket', 'websockets.spec.js') + }); + runner.setup({ + desiredCapabilities: { + 'browserstack.localIdentifier': localIdentifier, + build: generateBuildId(), + }, + }); + await runner.runTests(); - if (reporters.some(r => r.hasTestFailures)) { + if (runner.testRunner.hasTestFailures) { console.warn('Tests failed.'); process.exitCode = 1; } else { @@ -90,4 +91,6 @@ function createTunnel() { clearBuildId(); console.log(`Finished build ${buildId}`); } -}()); +} + +Nightwatch.cli(runTests); diff --git a/integration-tests/utils/buildApi.js b/integration-tests/utils/buildApi.js index 0297d92b..79129985 100644 --- a/integration-tests/utils/buildApi.js +++ b/integration-tests/utils/buildApi.js @@ -15,46 +15,19 @@ limitations under the License. */ const { execSync } = require('child_process'); -const fs = require('fs'); -const path = require('path'); - -const buildIdFilePath = path.join(__dirname, '..', '..', '.nightwatch_build_id'); function sanitizeCliOut(out) { return out.toString().replace(/\n/g, ''); } function generateBuildId() { - if (fs.existsSync(buildIdFilePath)) { - console.warn('Build ID file already exists. Please remove the existing file, before requesting a new ID.'); - } - const commitId = process.env.CIRCLE_SHA1 || sanitizeCliOut(execSync('git rev-parse HEAD')); const author = process.env.CIRCLE_PR_USERNAME || sanitizeCliOut(execSync('whoami')); const when = new Date().toJSON(); - const buildId = `${author}-${when}-${commitId.substring(0, 8)}`; - fs.writeFileSync(buildIdFilePath, buildId); - - return buildId; -} - -function getCurrentBuildId() { - if (!fs.existsSync(buildIdFilePath)) { - throw new Error('Build ID file does not exist.'); - } - - return fs.readFileSync(buildIdFilePath).toString(); -} - -function clearBuildId() { - if (fs.existsSync(buildIdFilePath)) { - fs.unlinkSync(buildIdFilePath); - } + return `${author}-${when}-${commitId.substring(0, 8)}`; } module.exports = { generateBuildId, - getCurrentBuildId, - clearBuildId, }; diff --git a/integration-tests/utils/local.conf.js b/integration-tests/utils/local.conf.js index 66b190bd..3c65a5ae 100644 --- a/integration-tests/utils/local.conf.js +++ b/integration-tests/utils/local.conf.js @@ -29,6 +29,7 @@ const nightwatch_config = { start_session: false, server_path: seleniumServer.path, port: 9515, + check_process_delay: 5000, cli_args: { 'webdriver.chrome.driver': chromeDriver.path, 'webdriver.gecko.driver': geckoDriver.path, @@ -71,6 +72,15 @@ const nightwatch_config = { marionette: true, } }, + headlessFirefox: { + desiredCapabilities: { + browserName: 'firefox', + marionette: true, + 'moz:firefoxOptions': { + 'args': ['--headless'] + }, + } + }, safari: { webdriver: { use_legacy_jsonwire: false, diff --git a/karma.conf.js b/karma.conf.js index a1cfc472..35606bed 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -1,5 +1,19 @@ -// Karma configuration -// Generated on Thu Jun 04 2020 09:56:24 GMT-0400 (Eastern Daylight Time) +/* +Copyright 2020 Splunk Inc. + +Licensed 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. +*/ + const json = require('@rollup/plugin-json'); const alias = require('@rollup/plugin-alias'); const commonjs = require('@rollup/plugin-commonjs'); @@ -7,8 +21,12 @@ const typescript = require('rollup-plugin-typescript2'); const resolve = require('@rollup/plugin-node-resolve'); const istanbulrollup = require('rollup-plugin-istanbul'); const rollupPolyfills = require('rollup-plugin-node-polyfills'); +const path = require('path'); + const rollupHelpers = require('./rollup.helpers'); +process.env.CHROME_BIN = require('puppeteer').executablePath(); + module.exports = function (config) { config.set({ @@ -57,22 +75,9 @@ module.exports = function (config) { 'test/index.js' ], - // list of files / patterns to exclude exclude: [], - - // preprocess matching files before serving them to the browser - // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor - preprocessors: {}, - - - // test results reporter to use - // possible values: 'dots', 'progress' - // available reporters: https://npmjs.org/browse/keyword/karma-reporter - reporters: ['progress'], - - // web server port port: 9876, @@ -90,9 +95,8 @@ module.exports = function (config) { autoWatch: false, - // start these browsers - // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher - browsers: ['Chrome'], + // start these browsers - might be using Puppeteer + browsers: ['ChromeHeadless'], // Continuous Integration mode @@ -145,8 +149,7 @@ module.exports = function (config) { }, }, - reporters: ['spec', 'coverage-istanbul'], - preprocessors: {'test/index.js': ['rollup']} - + reporters: ['progress', 'spec', 'coverage-istanbul'], + preprocessors: {'test/index.js': ['rollup']}, }); }; diff --git a/package.json b/package.json index 2bc216ce..b1fbb5ca 100644 --- a/package.json +++ b/package.json @@ -11,18 +11,17 @@ "lint": "eslint src/*.js test/*.js integration-tests/**/*.js integration-tests/**/**/*.js", "lint:markdown": "markdownlint README.md docs/*.md", "lint:fix": "eslint --fix src/*.js test/*.js integration-tests/**/*.js integration-tests/**/**/*.js", - "test:integration": "npm-run-all -s build:development test:integration:_execute", "test:integration:local": "npm-run-all -s build:development test:integration:local:_execute", "test:integration:local:firefox": "npm-run-all -s build:development test:integration:local:firefox:_execute", "test:integration:local:chrome": "npm-run-all -s build:development test:integration:local:chrome:_execute", "test:integration:local:safari": "npm-run-all -s build:development test:integration:local:safari:_execute", "test:integration:local:all": "npm-run-all -s build:development test:integration:local:all:_execute", - "test:integration:_execute": "node ./integration-tests/utils/browserstack.runner.js", "test:integration:local:_execute": "nightwatch -c ./integration-tests/utils/local.conf.js -e headlessChrome", - "test:integration:local:all:_execute": "nightwatch -c ./integration-tests/utils/local.conf.js -e headlessChrome,firefox,chrome,safari", + "test:integration:local:all:_execute": "nightwatch -c ./integration-tests/utils/local.conf.js -e headlessChrome,headlessFirefox,chrome,safari", "test:integration:local:firefox:_execute": "nightwatch -c ./integration-tests/utils/local.conf.js -e firefox", "test:integration:local:chrome:_execute": "nightwatch -c ./integration-tests/utils/local.conf.js -e chrome", "test:integration:local:safari:_execute": "nightwatch -c ./integration-tests/utils/local.conf.js -e safari", + "test:integration:local:headlessFirefox:_execute": "nightwatch --verbose -c ./integration-tests/utils/local.conf.js -e headlessFirefox", "test:integration:_selfSignCerts": "(cd utils && cd certs && openssl req -nodes -new -x509 -keyout server.key -out server.cert)", "test:integration:_build": "npm-run-all -s build:*", "build:debug": "DEBUG_BUILD=1 npx rollup -c", @@ -64,7 +63,7 @@ "nightwatch": "^1.5.1", "npm-run-all": "^4.1.5", "nyc": "^15.0.0", - "puppeteer": "^5.4.1", + "puppeteer": "^5.5.0", "rollup": "^2.33.1", "rollup-plugin-istanbul": "^2.0.1", "rollup-plugin-node-polyfills": "^0.2.1",