Skip to content

build: run browserstack on circleci #13749

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 24, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,25 @@ jobs:

- *save_cache

# ----------------------------------------------------------------------------
# Job that runs the unit tests on Browserstack. The browsers that will be used
# to run the unit tests on Browserstack are set in: test/browser-providers.js
# ----------------------------------------------------------------------------
tests_browserstack:
<<: *job_defaults
resource_class: xlarge
environment:
BROWSER_STACK_USERNAME: "angularteam1"
BROWSER_STACK_ACCESS_KEY: "CaXMeMHD9pr5PHg8N7Jq"
steps:
- *checkout_code
- *restore_cache
- *yarn_install

- run: ./scripts/circleci/run-browserstack-tests.sh

- *save_cache

# --------------------------------------
# Job that builds the demo-app with AOT
# --------------------------------------
Expand Down Expand Up @@ -189,6 +208,7 @@ workflows:
unit_tests:
jobs:
- tests_local_browsers
- tests_browserstack

integration_tests:
jobs:
Expand Down
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ jobs:
include:
- env: "MODE=payload"
- env: "MODE=saucelabs_required"
- env: "MODE=browserstack_required"
- env: "DEPLOY_MODE=build-artifacts"
if: type = push
- env: "DEPLOY_MODE=docs-content"
Expand Down
53 changes: 27 additions & 26 deletions scripts/browserstack/start-tunnel.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,48 @@

set -e -o pipefail

# Workaround for Travis CI cookbook https://github.com/travis-ci/travis-ci/issues/4862,
# where $PATH will be extended with relative paths to the NPM binaries.
PATH=`echo ${PATH} | sed -e 's/:\.\/node_modules\/\.bin//'`
tunnelFileName="BrowserStackLocal-linux-x64.zip"
tunnelUrl="https://www.browserstack.com/browserstack-local/${tunnelFileName}"

TUNNEL_FILE="BrowserStackLocal-linux-x64.zip"
TUNNEL_URL="https://www.browserstack.com/browserstack-local/${TUNNEL_FILE}"
TUNNEL_DIR="/tmp/browserstack-tunnel"
TUNNEL_LOG="${LOGS_DIR}/browserstack-tunnel.log"

BROWSER_STACK_ACCESS_KEY=`echo ${BROWSER_STACK_ACCESS_KEY} | rev`
tunnelTmpDir="/tmp/material-browserstack"
tunnelLogFile="${tunnelTmpDir}/browserstack-local.log"
tunnelReadyFile="${tunnelTmpDir}/readyfile"
tunnelErrorFile="${tunnelTmpDir}/errorfile"

# Cleanup and create the folder structure for the tunnel connector.
rm -rf ${TUNNEL_DIR} ${BROWSER_PROVIDER_READY_FILE}
mkdir -p ${TUNNEL_DIR}
touch ${TUNNEL_LOG}
rm -rf ${tunnelTmpDir} ${tunnelReadyFile} ${tunnelErrorFile}
mkdir -p ${tunnelTmpDir}
touch ${tunnelLogFile}

cd ${TUNNEL_DIR}
# Go into temporary tunnel directory.
cd ${tunnelTmpDir}

# Download the browserstack local binaries.
curl ${TUNNEL_URL} -o ${TUNNEL_FILE} 2> /dev/null 1> /dev/null
curl ${tunnelUrl} -o ${tunnelFileName} 2> /dev/null 1> /dev/null

# Extract the browserstack local binaries from the tarball.
mkdir -p browserstack-tunnel
unzip -q ${TUNNEL_FILE} -d browserstack-tunnel
unzip -q ${tunnelFileName} -d browserstack-tunnel

# Cleanup the download directory.
rm ${TUNNEL_FILE}
# Cleanup the downloaded zip archive.
rm ${tunnelFileName}

ARGS=""

# Set tunnel-id only on Travis, to make local testing easier.
if [ ! -z "${TRAVIS_JOB_ID}" ]; then
ARGS="${ARGS} --local-identifier ${TRAVIS_JOB_ID}"
if [ ! -z "${CIRCLE_BUILD_NUM}" ]; then
ARGS="${ARGS} --local-identifier ${CIRCLE_BUILD_NUM}"
fi

echo "Starting Browserstack Local in the background, logging into: ${TUNNEL_LOG}"
echo "Starting Browserstack Local in the background, logging into: ${tunnelLogFile}"

# Extension to the BrowserStackLocal binaries, because those can't create a readyfile.
function create_ready_file {
# Process ID for the BrowserStack local asynchronous instance.
tunnelProcessPid=${1}

# To be able to exit the tail properly we need to have a sub shell spawned, which is
# used to track the state of tail.
{ sleep 120; touch ${BROWSER_PROVIDER_ERROR_FILE}; } &
{ sleep 120; touch ${tunnelErrorFile}; } &

TIMER_PID=${!}

Expand All @@ -54,16 +53,18 @@ function create_ready_file {

# When the tail recognizes the `Ctrl-C` log message the BrowserStack Tunnel is up.
{
tail -n0 -f ${TUNNEL_LOG} --pid ${TIMER_PID} | { sed '/Ctrl/q' && kill -9 ${TIMER_PID}; };
tail -n0 -f ${tunnelLogFile} --pid ${TIMER_PID} | { sed '/Ctrl/q' && kill -9 ${TIMER_PID}; };
} &> /dev/null

echo
echo "BrowserStack Tunnel ready"

touch ${BROWSER_PROVIDER_READY_FILE}
# Create the readyfile and write the PID for BrowserStack Local into it.
echo ${tunnelProcessPid} > ${tunnelReadyFile}
}

browserstack-tunnel/BrowserStackLocal -k ${BROWSER_STACK_ACCESS_KEY} ${ARGS} 2>&1 >> ${TUNNEL_LOG} &
browserstack-tunnel/BrowserStackLocal -k ${BROWSER_STACK_ACCESS_KEY} ${ARGS} 2>&1 >> \
${tunnelLogFile} &

# Wait for the tunnel to be ready and create the readyfile with the Browserstack PID
create_ready_file &
create_ready_file ${!} &
20 changes: 16 additions & 4 deletions scripts/browserstack/stop-tunnel.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,27 @@

set -e -o pipefail

tunnelTmpDir="/tmp/material-browserstack"
tunnelReadyFile="${tunnelTmpDir}/readyfile"

echo "Shutting down Browserstack tunnel"
if [[ ! -f ${tunnelReadyFile} ]]; then
echo "BrowserStack tunnel has not been started. Cannot stop tunnel.."
exit 1
fi

killall BrowserStackLocal
echo "Shutting down Browserstack tunnel.."

while [[ -n `ps -ef | grep "BrowserStackLocal" | grep -v "grep"` ]]; do
# The process id for the BrowserStack local instance is stored inside of the readyfile.
tunnelProcessId=$(cat ${tunnelReadyFile})

# Kill the process by using the PID that has been read from the readyfile. Note that
# we cannot use killall because CircleCI base container images don't have it installed.
kill ${tunnelProcessId}

while (ps -p ${tunnelProcessId} &> /dev/null); do
printf "."
sleep .5
done

echo ""
echo "Browserstack tunnel has been shut down"
echo "Browserstack tunnel has been shut down"
14 changes: 9 additions & 5 deletions scripts/browserstack/wait-tunnel.sh
Original file line number Diff line number Diff line change
@@ -1,28 +1,32 @@
#!/bin/bash

TUNNEL_LOG="$LOGS_DIR/browserstack-tunnel.log"
tunnelTmpDir="/tmp/material-browserstack"
tunnelLogFile="${tunnelTmpDir}/browserstack-local.log"
tunnelReadyFile="${tunnelTmpDir}/readyfile"
tunnelErrorFile="${tunnelTmpDir}/errorfile"

WAIT_DELAY=30

# Method that prints the logfile output of the browserstack tunnel.
printLog() {
echo "Logfile output of Browserstack tunnel (${TUNNEL_LOG}):"
echo "Logfile output of Browserstack tunnel (${tunnelLogFile}):"
echo ""
cat ${TUNNEL_LOG}
cat ${tunnelLogFile}
}

# Wait for Connect to be ready before exiting
# Time out if we wait for more than 2 minutes, so the process won't run forever.
let "counter=0"

# Exit the process if there are errors reported. Print the tunnel log to the console.
if [ -f $BROWSER_PROVIDER_ERROR_FILE ]; then
if [ -f ${tunnelErrorFile} ]; then
echo
echo "An error occurred while starting the tunnel. See error:"
printLog
exit 5
fi

while [ ! -f $BROWSER_PROVIDER_READY_FILE ]; do
while [ ! -f ${tunnelReadyFile} ]; do
let "counter++"

# Counter needs to be multiplied by two because the while loop only sleeps a half second.
Expand Down
26 changes: 26 additions & 0 deletions scripts/circleci/run-browserstack-tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/bin/bash

# In case any command failed, we want to immediately exit the script with the
# proper exit code.
set -e

# Go to project directory.
cd $(dirname ${0})/../..

# Decode access token and make it accessible for child processes.
export BROWSER_STACK_ACCESS_KEY=`echo ${BROWSER_STACK_ACCESS_KEY} | rev`

# Start tunnel and wait for it being ready.
./scripts/browserstack/start-tunnel.sh
./scripts/browserstack/wait-tunnel.sh

# Setup the test platform environment variable that will be read
# by the Karma configuration script.
export TEST_PLATFORM="browserstack"

# Run the unit tests on Browserstack with Karma.
yarn gulp ci:test

# Kill the Browserstack tunnel. This is necessary in order to avoid rate-limit
# errors that cause the unit tests to be flaky.
./scripts/browserstack/stop-tunnel.sh
3 changes: 1 addition & 2 deletions test/browser-providers.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,7 @@ function decodeToken(token) {
return (token || '').split('').reverse().join('');
}


/** Ensures that the Travis access keys work properly. */
/** Ensures that the Saucelabs and Browserstack access keys work properly. */
if (process.env.TRAVIS) {
process.env.SAUCE_ACCESS_KEY = decodeToken(process.env.SAUCE_ACCESS_KEY);
process.env.BROWSER_STACK_ACCESS_KEY = decodeToken(process.env.BROWSER_STACK_ACCESS_KEY);
Expand Down
12 changes: 11 additions & 1 deletion test/karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,17 @@ module.exports = (config) => {
});

if (process.env['CIRCLECI']) {
config.browsers = platformMap[process.env['TEST_PLATFORM']];
const tunnelIdentifier = process.env['CIRCLE_BUILD_NUM'];
const buildIdentifier = `angular-material-${tunnelIdentifier}`;
const testPlatform = process.env['TEST_PLATFORM'];

if (testPlatform === 'browserstack') {
config.browserStack.build = buildIdentifier;
config.browserStack.tunnelIdentifier = tunnelIdentifier;
}

// Configure Karma launch the browsers that belong to the given test platform.
config.browsers = platformMap[testPlatform];
}

if (process.env['TRAVIS']) {
Expand Down