зеркало из https://github.com/mozilla/normandy.git
Switch CI to run tests outside of Docker
Running all our tests and lints and other CI tasks inside the built image makes our CI more complex, harder to work on, and less reliable. Move all the CI related tasks out of Docker. A docker image is still built during PRs. A docker image is built and published for commits to master and tags.
This commit is contained in:
Родитель
6e8cae6cdb
Коммит
c8dd652cd4
|
@ -4,66 +4,58 @@
|
|||
# DOCKER_USER
|
||||
# DOCKER_PASS
|
||||
#
|
||||
|
||||
version: 2
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
docker:
|
||||
# Image with Python/Node and therapist pre-installed
|
||||
- image: mozilla/cidockerbases:therapist-latest
|
||||
|
||||
environment:
|
||||
# Use Test configuration so that development dependencies aren't needed
|
||||
DJANGO_CONFIGURATION: "Test"
|
||||
# Use an in-memory database so that the migrations check doesn't try to access a real database
|
||||
DATABASE_URL: "sqlite://:memory:"
|
||||
# Ignore warning and info checks about not applying migrations, and about missing geoip database
|
||||
DJANGO_SILENCED_SYSTEM_CHECKS: "dockerflow.health.W001,normandy.recipes.I001,normandy.recipes.I002,normandy.recipes.I003,normandy.recipes.E006"
|
||||
|
||||
working_directory: ~/repo
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
# Install python dependencies
|
||||
- run:
|
||||
name: Create virtualenv
|
||||
command: |
|
||||
python3 -m venv ~/venv
|
||||
echo "source ~/venv/bin/activate" >> $BASH_ENV
|
||||
|
||||
- run:
|
||||
name: Install python dependencies
|
||||
command: pip install -U -r requirements/default.txt -c requirements/constraints.txt --require-hashes
|
||||
|
||||
# Download and cache node dependencies
|
||||
command: pip install -r requirements/default.txt
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v2-dependencies-{{ checksum "yarn.lock" }}
|
||||
# fallback to using the latest cache if no exact match is found
|
||||
- v2-dependencies-
|
||||
|
||||
- run:
|
||||
name: Install node dependencies
|
||||
command: yarn install --frozen-lockfile
|
||||
|
||||
- save_cache:
|
||||
paths:
|
||||
- "node_modules"
|
||||
key: v2-dependencies-{{ checksum "yarn.lock" }}
|
||||
|
||||
# Run lint suite using therapist
|
||||
- run:
|
||||
name: Lint
|
||||
command: therapist run --use-tracked-files
|
||||
|
||||
build-test-publish:
|
||||
docker-image-build:
|
||||
docker:
|
||||
- image: mozilla/cidockerbases:docker-latest
|
||||
|
||||
working_directory: ~/normandy
|
||||
|
||||
environment:
|
||||
COMPOSE_FILE: ci/docker-compose.yml
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
- setup_remote_docker:
|
||||
version: 17.09.0-ce
|
||||
docker_layer_caching: true
|
||||
|
||||
- run:
|
||||
name: Create version.json
|
||||
command: |
|
||||
|
@ -75,58 +67,31 @@ jobs:
|
|||
"$CIRCLE_PROJECT_REPONAME" \
|
||||
"$CIRCLE_BUILD_URL" \
|
||||
> version.json
|
||||
|
||||
- run:
|
||||
name: Starting artifact collector
|
||||
command: docker-compose run --user root artifact-collector
|
||||
background: true
|
||||
|
||||
name: Build Docker image
|
||||
command: docker build -t normandy:web .
|
||||
- run:
|
||||
name: Build and download Docker images
|
||||
# Build all local images, and pull all remote images, so that later
|
||||
# steps have everything they need already prepared. Note that "pull"
|
||||
# requires an explicit list so it doesn't try to pull images that
|
||||
# have already been built.
|
||||
name: Save image into workspace
|
||||
command: |
|
||||
./bin/download_geolite2.sh
|
||||
docker-compose build
|
||||
docker-compose pull db
|
||||
mkdir -p workspace
|
||||
docker save -o workspace/normandy-web.tar normandy:web
|
||||
gzip workspace/normandy-web.tar
|
||||
- persist_to_workspace:
|
||||
root: workspace
|
||||
paths:
|
||||
- normandy-web.tar.gz
|
||||
|
||||
docker-image-publish:
|
||||
docker:
|
||||
- image: mozilla/cidockerbases:docker-latest
|
||||
steps:
|
||||
- setup_remote_docker:
|
||||
version: 17.09.0-ce
|
||||
- attach_workspace:
|
||||
at: workspace
|
||||
- run:
|
||||
name: Python Tests
|
||||
command: docker-compose run web python-tests
|
||||
|
||||
- run:
|
||||
name: Missing migrations
|
||||
command: docker-compose run web migrations-check
|
||||
|
||||
- run:
|
||||
name: Contract tests
|
||||
command: |
|
||||
docker-compose up -d web
|
||||
docker-compose run web contracttest
|
||||
docker-compose kill web
|
||||
|
||||
- run:
|
||||
name: JavaScript tests
|
||||
command: |
|
||||
docker-compose up -d js-tests-browser
|
||||
docker-compose up js-tests
|
||||
docker-compose kill js-tests-browser
|
||||
|
||||
- run:
|
||||
name: Copy Artifacts
|
||||
when: always # run even if previous run commands failed
|
||||
command: |
|
||||
docker cp $(docker-compose ps -q artifact-collector):/artifacts /artifacts
|
||||
ls /artifacts
|
||||
|
||||
- store_artifacts:
|
||||
path: /artifacts
|
||||
|
||||
- store_test_results:
|
||||
path: /artifacts/test_results
|
||||
|
||||
name: Load Docker image from workspace
|
||||
command: docker load -i workspace/normandy-web.tar.gz
|
||||
- run:
|
||||
name: Push to Dockerhub
|
||||
command: |
|
||||
|
@ -184,26 +149,176 @@ jobs:
|
|||
name: Deploy docs to gh-pages
|
||||
command: gh-pages --dotfiles --message "[skip ci] Docs updates" --dist docs/_build/html
|
||||
|
||||
python-tests:
|
||||
docker:
|
||||
- image: circleci/python:3.7-node-browsers
|
||||
- image: circleci/postgres:9.6
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Fetch Geolite database
|
||||
command: ./bin/download_geolite2.sh
|
||||
- run:
|
||||
name: Create virtualenv
|
||||
command: |
|
||||
python3 -m venv ~/venv
|
||||
echo "source ~/venv/bin/activate" >> $BASH_ENV
|
||||
- run:
|
||||
name: Install python dependencies
|
||||
command: pip install -r requirements/default.txt
|
||||
- run:
|
||||
name: Install node dependencies
|
||||
command: yarn install --frozen-lockfile
|
||||
- run:
|
||||
name: Python tests
|
||||
command: |
|
||||
mkdir test-reports
|
||||
py.test normandy \
|
||||
--tb=short -vvv \
|
||||
--junitxml=test-reports/junit.xml
|
||||
- store_test_results:
|
||||
path: test-reports
|
||||
- store_artifacts:
|
||||
path: test-reports
|
||||
|
||||
js-tests:
|
||||
docker:
|
||||
- image: mozilla/cidockerbases:firefox-latest
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Install node dependencies
|
||||
command: yarn install --frozen-lockfile
|
||||
- run:
|
||||
name: JS tests
|
||||
environment:
|
||||
JUNIT_REPORT_PATH: ./junit/
|
||||
JUNIT_REPORT_NAME: test-results.xml
|
||||
command: |
|
||||
mkdir -p $JUNIT_REPORT_PATH
|
||||
yarn karma start ./karma.conf.js --single-run
|
||||
- store_test_results:
|
||||
path: test-reports
|
||||
- store_artifacts:
|
||||
path: test-reports
|
||||
|
||||
contract-tests:
|
||||
docker:
|
||||
- image: circleci/python:3.7-node-browsers
|
||||
- image: circleci/postgres:9.6
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_DB: normandy
|
||||
|
||||
environment:
|
||||
DJANGO_SETTINGS_MODULE: normandy.settings
|
||||
DJANGO_CONFIGURATION: ProductionInsecure
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Fetch Geolite database
|
||||
command: ./bin/download_geolite2.sh
|
||||
- run:
|
||||
name: Create virtualenv
|
||||
command: |
|
||||
python3 -m venv ~/venv
|
||||
echo "source ~/venv/bin/activate" >> $BASH_ENV
|
||||
- run:
|
||||
name: Install python dependencies
|
||||
command: pip install -r requirements/default.txt
|
||||
- run:
|
||||
name: Install node dependencies
|
||||
command: yarn install --frozen-lockfile
|
||||
- run:
|
||||
name: Create version.json
|
||||
command: |
|
||||
printf '{"commit":"%s","version":"%s","source":"https://github.com/%s/%s","build":"%s"}\n' \
|
||||
"$CIRCLE_SHA1" \
|
||||
"${CIRCLE_TAG-}" \
|
||||
"$CIRCLE_PROJECT_USERNAME" \
|
||||
"$CIRCLE_PROJECT_REPONAME" \
|
||||
"$CIRCLE_BUILD_URL" \
|
||||
> version.json
|
||||
- run:
|
||||
name: Prepare DB
|
||||
command: |
|
||||
./manage.py migrate
|
||||
./manage.py update_actions
|
||||
- run:
|
||||
name: Starting server
|
||||
command: gunicorn normandy.wsgi:application --bind localhost:8000
|
||||
background: true
|
||||
- run:
|
||||
name: Waiting for web server to be available
|
||||
command: |
|
||||
./ci/wait-for-it.sh localhost:8000 \
|
||||
--timeout=30 --strict \
|
||||
-- echo "Done waiting. It should work now."
|
||||
- run:
|
||||
name: Contract tests
|
||||
command: |
|
||||
mkdir test-reports
|
||||
py.test contract-tests \
|
||||
--tb=short -vvv \
|
||||
--junitxml=test-reports/junit.xml \
|
||||
--server http://localhost:8000
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
main:
|
||||
jobs:
|
||||
# By default CircleCI does not run any jobs on tags. To allow a job to
|
||||
# run on a tag, we specify `filters.tags.only: /.*/`. That does not
|
||||
# affect whether or not the job will run on PRs or on untagged master
|
||||
# commits. By default all jobs listed run on all commits in PRs and on
|
||||
# master (unless otherwise filtered).
|
||||
|
||||
# Group: Tests and lints
|
||||
- python-tests:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
- js-tests:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
- lint:
|
||||
# run on all tags, as well as the default of all other commits
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
- build-test-publish:
|
||||
requires:
|
||||
- lint
|
||||
# run on all tags, as well as the default of all other commits
|
||||
- contract-tests:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
|
||||
# Group: Building artifacts
|
||||
- docs-build
|
||||
- docker-image-build:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
|
||||
# Group: Publish
|
||||
# All of these should only run on master and tags
|
||||
- docs-publish:
|
||||
requires:
|
||||
- docs-build
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
- docker-image-publish:
|
||||
requires:
|
||||
- python-tests
|
||||
- js-tests
|
||||
- contract-tests
|
||||
- js-tests
|
||||
- lint
|
||||
- docker-image-build
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
tags:
|
||||
only: /v.*/
|
|
@ -25,6 +25,14 @@ actions:
|
|||
|
||||
yarn-audit:
|
||||
run: yarn audit
|
||||
|
||||
missing-migirations:
|
||||
include: "*.py"
|
||||
run: |
|
||||
./manage.py makemigrations --check --no-input --dry-run recipes studies || (
|
||||
echo "You probably have migrations that need to be created" && exit 1
|
||||
)
|
||||
|
||||
shortcuts:
|
||||
lint:
|
||||
flags:
|
||||
|
|
|
@ -34,6 +34,8 @@ ENV DJANGO_SETTINGS_MODULE=normandy.settings \
|
|||
NEW_RELIC_CONFIG_FILE=newrelic.ini
|
||||
EXPOSE $PORT
|
||||
|
||||
ENTRYPOINT ["/bin/bash", "/app/bin/run.sh"]
|
||||
|
||||
CMD ["start"]
|
||||
CMD $CMD_PREFIX gunicorn \
|
||||
--log-file - \
|
||||
--worker-class ${GUNICORN_WORKER_CLASS:-sync} \
|
||||
--max-requests ${GUNICORN_MAX_REQUESTS:-0} \
|
||||
normandy.wsgi:application
|
64
bin/run.sh
64
bin/run.sh
|
@ -1,64 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -eo pipefail
|
||||
|
||||
usage() {
|
||||
echo "usage: ./bin/run.sh python-tests|js-tests|start|migrations-check"
|
||||
exit 1
|
||||
}
|
||||
|
||||
function start_gunicorn {
|
||||
$CMD_PREFIX gunicorn \
|
||||
--log-file - \
|
||||
--worker-class ${GUNICORN_WORKER_CLASS:-sync} \
|
||||
--max-requests ${GUNICORN_MAX_REQUESTS:-0} \
|
||||
normandy.wsgi:application
|
||||
}
|
||||
|
||||
[ $# -lt 1 ] && usage
|
||||
|
||||
case $1 in
|
||||
migrations-check)
|
||||
./manage.py migrate
|
||||
echo "Checking that all migrations have been made"
|
||||
# The mozilla-django-product-details has a bug in that calling `./manage.py makemigrations`
|
||||
# on it will actually create a new migration (.py) file. So, be specific and only do this
|
||||
# migration check for *our* apps.
|
||||
# See https://github.com/mozilla/django-product-details/issues/68
|
||||
./manage.py makemigrations --check --no-input --dry-run recipes studies || (
|
||||
echo "You probably have migrations that need to be created" && exit 1
|
||||
)
|
||||
;;
|
||||
python-tests)
|
||||
echo "Running Python tests"
|
||||
junit_path=$ARTIFACTS_PATH/test_results/python_tests
|
||||
mkdir -p $junit_path
|
||||
py.test -vv --junitxml=$junit_path/junit.xml normandy/
|
||||
;;
|
||||
js-tests)
|
||||
echo "Running Karma"
|
||||
node ci/karma-ci.js
|
||||
;;
|
||||
first-start)
|
||||
echo "Starting the gunicorn server the first time"
|
||||
./manage.py migrate
|
||||
./manage.py update_actions
|
||||
start_gunicorn
|
||||
;;
|
||||
start)
|
||||
start_gunicorn
|
||||
;;
|
||||
contracttest)
|
||||
echo "Waiting for web server to start"
|
||||
./ci/wait-for-it.sh -t 30 web:8000 -- echo "Done waiting. It should work now."
|
||||
echo "Running acceptance tests"
|
||||
junit_path=$ARTIFACTS_PATH/test_results/contract_tests
|
||||
mkdir -p $junit_path
|
||||
py.test contract-tests/ \
|
||||
-vv \
|
||||
--server http://web:8000 \
|
||||
--junitxml=$junit_path/junit.xml
|
||||
;;
|
||||
*)
|
||||
exec "$@"
|
||||
;;
|
||||
esac
|
|
@ -4,6 +4,8 @@ var path = require('path');
|
|||
var karma = require('karma');
|
||||
var karmaConfig = require('karma/lib/config');
|
||||
|
||||
const JUNIT_OUTPUT = process.env.JUNIT_OUTPUT || '/artifacts/test_results/karma';
|
||||
|
||||
var config = karmaConfig.parseConfig(
|
||||
path.join(__dirname, '/../karma.conf.js'),
|
||||
{
|
||||
|
@ -11,7 +13,7 @@ var config = karmaConfig.parseConfig(
|
|||
oneShot: true,
|
||||
reporters: ['spec', 'junit'],
|
||||
junitReporter: {
|
||||
outputDir: '/artifacts/test_results/karma/',
|
||||
outputDir: JUNIT_OUTPUT,
|
||||
},
|
||||
hostname: '0.0.0.0',
|
||||
},
|
||||
|
|
|
@ -58,7 +58,7 @@ module.exports = function (config) {
|
|||
|
||||
// start these browsers
|
||||
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
|
||||
browsers: ['Firefox'],
|
||||
browsers: ['FirefoxHeadless'],
|
||||
|
||||
// Continuous Integration mode
|
||||
// if true, Karma captures browsers, runs the tests and exits
|
||||
|
@ -69,5 +69,14 @@ module.exports = function (config) {
|
|||
concurrency: Infinity,
|
||||
};
|
||||
|
||||
if (process.env.JUNIT_REPORT_PATH) {
|
||||
karmaConfig.reporters.push('junit');
|
||||
karmaConfig.junitReporter = {
|
||||
outputDir: process.env.JUNIT_REPORT_PATH,
|
||||
outputFile: process.env.JUNIT_REPORT_NAME,
|
||||
useBrowserNAme: false,
|
||||
};
|
||||
}
|
||||
|
||||
config.set(karmaConfig);
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче