From b64106b730443c9239b10d006d96b041fc46df8e Mon Sep 17 00:00:00 2001 From: James Allen Date: Fri, 29 Dec 2017 08:08:19 +0000 Subject: [PATCH 001/118] Provide hosts and siblings container as environment settings and add npm run start script wip acceptence tests run, but don't all pass wip removed npm-debug from git --- .nvmrc | 2 +- Dockerfile | 19 + Jenkinsfile | 77 +- Makefile | 29 + app.coffee | 5 + bin/acceptance_test | 4 + config/settings.defaults.coffee | 29 +- docker-compose.ci.yml | 33 + docker-compose.yml | 51 + docker-runner | 1 + nodemon.json | 15 + package-lock.json | 3162 ++++++++++++++++++ package.json | 97 +- test/acceptance/coffee/helpers/Client.coffee | 1 + 14 files changed, 3431 insertions(+), 94 deletions(-) create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 bin/acceptance_test create mode 100644 docker-compose.ci.yml create mode 100644 docker-compose.yml create mode 160000 docker-runner create mode 100644 nodemon.json create mode 100644 package-lock.json diff --git a/.nvmrc b/.nvmrc index e18a34b..e1e5d13 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -6.11.2 +6.9.5 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d98547c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +FROM node:6.9.5 + +RUN wget -qO- https://get.docker.com/ | sh + +# ---- Copy Files/Build ---- +WORKDIR /app +COPY ./ /app +# Build react/vue/angular bundle static files +# RUN npm run build +RUN npm install + +RUN npm run compile + +EXPOSE 3013 + +ENV SHARELATEX_CONFIG /app/config/settings.production.coffee +ENV NODE_ENV production + +CMD ["node","/app/app.js"] diff --git a/Jenkinsfile b/Jenkinsfile index 0c289e1..ab90aaa 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,79 +1,72 @@ -pipeline { +String cron_string = BRANCH_NAME == "master" ? "@daily" : "" +pipeline { agent any triggers { pollSCM('* * * * *') - cron('@daily') + cron(cron_string) } stages { - stage('Clean') { - steps { - // This is a terrible hack to set the file ownership to jenkins:jenkins so we can cleanup the directory - sh 'docker run -v $(pwd):/app --rm busybox /bin/chown -R 111:119 /app' - sh 'rm -fr node_modules' - } - } stage('Install') { agent { docker { - image 'node:6.11.2' + image 'node:6.9.5' args "-v /var/lib/jenkins/.npm:/tmp/.npm -e HOME=/tmp" reuseNode true } } steps { + // we need to disable logallrefupdates, else git clones + // during the npm install will require git to lookup the + // user id which does not exist in the container's + // /etc/passwd file, causing the clone to fail. sh 'git config --global core.logallrefupdates false' - sh 'rm -fr node_modules' - checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: '_docker-runner'], [$class: 'CloneOption', shallow: true]], userRemoteConfigs: [[credentialsId: 'GIT_DEPLOY_KEY', url: 'git@github.com:sharelatex/docker-runner-sharelatex']]]) - sh 'npm install ./_docker-runner' - sh 'rm -fr ./_docker-runner ./_docker-runner@tmp' - sh 'npm install' - sh 'npm rebuild' - sh 'npm install --quiet grunt-cli' + sh 'rm -rf node_modules' + sh 'npm install && npm rebuild' } } - stage('Compile and Test') { + + stage('Compile') { agent { docker { - image 'node:6.11.2' + image 'node:6.9.5' reuseNode true } } steps { - sh 'node_modules/.bin/grunt compile:app' - sh 'node_modules/.bin/grunt compile:acceptance_tests' - sh 'NODE_ENV=development node_modules/.bin/grunt test:unit' + sh 'npm run compile:all' } } - stage('Acceptance Tests') { - environment { - TEXLIVE_IMAGE="quay.io/sharelatex/texlive-full:2017.1" - } + + stage('Unit Tests') { steps { - sh 'mkdir -p compiles cache' - // Not yet running, due to volumes/sibling containers - sh 'docker container prune -f' - sh 'docker pull $TEXLIVE_IMAGE' - sh 'docker pull sharelatex/acceptance-test-runner:clsi-6.11.2' - sh 'docker run --rm -e SIBLING_CONTAINER_USER=root -e SANDBOXED_COMPILES_HOST_DIR=$(pwd)/compiles -e SANDBOXED_COMPILES_SIBLING_CONTAINERS=true -e TEXLIVE_IMAGE=$TEXLIVE_IMAGE -v /var/run/docker.sock:/var/run/docker.sock -v $(pwd):/app sharelatex/acceptance-test-runner:clsi-6.11.2' - // This is a terrible hack to set the file ownership to jenkins:jenkins so we can cleanup the directory - sh 'docker run -v $(pwd):/app --rm busybox /bin/chown -R 111:119 /app' - sh 'rm -r compiles cache server.log db.sqlite config/settings.defaults.coffee' + sh 'DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" make test_unit' } } - stage('Package') { + + stage('Acceptance Tests') { + steps { + sh 'DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" make test_acceptance' + } + } + + stage('Package and publish build') { steps { sh 'echo ${BUILD_NUMBER} > build_number.txt' sh 'touch build.tar.gz' // Avoid tar warning about files changing during read sh 'tar -czf build.tar.gz --exclude=build.tar.gz --exclude-vcs .' - } - } - stage('Publish') { - steps { withAWS(credentials:'S3_CI_BUILDS_AWS_KEYS', region:"${S3_REGION_BUILD_ARTEFACTS}") { s3Upload(file:'build.tar.gz', bucket:"${S3_BUCKET_BUILD_ARTEFACTS}", path:"${JOB_NAME}/${BUILD_NUMBER}.tar.gz") + } + } + } + + stage('Publish build number') { + steps { + sh 'echo ${BRANCH_NAME}-${BUILD_NUMBER} > build_number.txt' + withAWS(credentials:'S3_CI_BUILDS_AWS_KEYS', region:"${S3_REGION_BUILD_ARTEFACTS}") { // The deployment process uses this file to figure out the latest build s3Upload(file:'build_number.txt', bucket:"${S3_BUCKET_BUILD_ARTEFACTS}", path:"${JOB_NAME}/latest") } @@ -82,6 +75,10 @@ pipeline { } post { + always { + sh 'DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" make test_clean' + } + failure { mail(from: "${EMAIL_ALERT_FROM}", to: "${EMAIL_ALERT_TO}", diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6407885 --- /dev/null +++ b/Makefile @@ -0,0 +1,29 @@ +# This file was auto-generated, do not edit it directly. +# Instead run bin/update_build_scripts from +# https://github.com/sharelatex/sharelatex-dev-environment +# Version: 1.0.1 + +BUILD_NUMBER ?= local +BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) +PROJECT_NAME = clsi +DOCKER_COMPOSE_FLAGS ?= -f docker-compose.yml +DOCKER_COMPOSE := docker-compose ${DOCKER_COMPOSE_FLAGS} + +clean: + rm -f app.js + rm -rf app/js + rm -rf test/unit/js + rm -rf test/acceptance/js + +test: test_unit test_acceptance + +test_unit: + @[ -d test/unit ] && $(DOCKER_COMPOSE) run --rm test_unit -- ${MOCHA_ARGS} || echo "clsi has no unit tests" + +test_acceptance: test_clean # clear the database before each acceptance test run + @[ -d test/acceptance ] && $(DOCKER_COMPOSE) run --rm test_acceptance -- ${MOCHA_ARGS} || echo "clsi has no acceptance tests" + +test_clean: + $(DOCKER_COMPOSE) down + +.PHONY: clean test test_unit test_acceptance test_clean build publish diff --git a/app.coffee b/app.coffee index 5c79b8e..ba7d225 100644 --- a/app.coffee +++ b/app.coffee @@ -132,6 +132,7 @@ resCacher = if Settings.smokeTest do runSmokeTest = -> logger.log("running smoke tests") + console.log(__dirname, __filename) smokeTest.run(require.resolve(__dirname + "/test/smoke/js/SmokeTests.js"))({}, resCacher) setTimeout(runSmokeTest, 30 * 1000) @@ -139,6 +140,10 @@ app.get "/health_check", (req, res)-> res.contentType(resCacher?.setContentType) res.status(resCacher?.code).send(resCacher?.body) +app.get "/smoke_test_force", (req, res)-> + smokeTest.run(require.resolve(__dirname + "/test/smoke/js/SmokeTests.js"))(req, res) + + profiler = require "v8-profiler" app.get "/profile", (req, res) -> time = parseInt(req.query.time || "1000") diff --git a/bin/acceptance_test b/bin/acceptance_test new file mode 100644 index 0000000..fd2e513 --- /dev/null +++ b/bin/acceptance_test @@ -0,0 +1,4 @@ +#!/bin/bash +set -e; +MOCHA="node_modules/.bin/mocha --recursive --reporter spec --timeout 15000" +$MOCHA "$@" diff --git a/config/settings.defaults.coffee b/config/settings.defaults.coffee index 4b39178..448d13b 100644 --- a/config/settings.defaults.coffee +++ b/config/settings.defaults.coffee @@ -16,27 +16,28 @@ module.exports = clsiCacheDir: Path.resolve(__dirname + "/../cache") synctexBaseDir: (project_id) -> Path.join(@compilesDir, project_id) -# clsi: -# commandRunner: "docker-runner-sharelatex" -# docker: -# image: "quay.io/sharelatex/texlive-full:2017.1" -# env: -# HOME: "/tmp" -# socketPath: "/var/run/docker.sock" -# user: "tex" -# expireProjectAfterIdleMs: 24 * 60 * 60 * 1000 -# checkProjectsIntervalMs: 10 * 60 * 1000 - internal: clsi: port: 3013 - host: "localhost" - + host: process.env["LISTEN_ADDRESS"] or "0.0.0.0" apis: clsi: - url: "http://localhost:3013" + url: "http://#{process.env['CLSI_HOST'] or 'localhost'}:3013" smokeTest: false project_cache_length_ms: 1000 * 60 * 60 * 24 parallelFileDownloads:1 + +if process.env["COMMAND_RUNNER"] + module.exports.clsi = + commandRunner: process.env["COMMAND_RUNNER"] + docker: + image: process.env["TEXLIVE_IMAGE"] or "quay.io/sharelatex/texlive-full:2017.1" + env: + HOME: "/tmp" + socketPath: "/var/run/docker.sock" + user: process.env["TEXLIVE_IMAGE_USER"] or "tex" + expireProjectAfterIdleMs: 24 * 60 * 60 * 1000 + checkProjectsIntervalMs: 10 * 60 * 1000 + module.exports.path.sandboxedCompilesHostDir = process.env["COMPILES_HOST_DIR"] diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml new file mode 100644 index 0000000..9f40ba8 --- /dev/null +++ b/docker-compose.ci.yml @@ -0,0 +1,33 @@ +# This file was auto-generated, do not edit it directly. +# Instead run bin/update_build_scripts from +# https://github.com/sharelatex/sharelatex-dev-environment +# Version: 1.0.1 + +version: "2" + +services: + test_unit: + image: node:6.9.5 + volumes: + - .:/app + working_dir: /app + entrypoint: npm run test:unit:_run + + test_acceptance: + image: node:6.9.5 + volumes: + - .:/app + working_dir: /app + environment: + REDIS_HOST: redis + MONGO_HOST: mongo + depends_on: + - redis + - mongo + entrypoint: npm run test:acceptance:_run + + redis: + image: redis + + mongo: + image: mongo:3.4 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..4a25bc3 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,51 @@ +# This file was auto-generated, do not edit it directly. +# Instead run bin/update_build_scripts from +# https://github.com/sharelatex/sharelatex-dev-environment +# Version: 1.0.1 + +version: "2" + +services: + test_unit: + image: node:6.9.5 + volumes: + - .:/app + working_dir: /app + entrypoint: npm run test:unit + + test_acceptance: + image: node:6.9.5 + volumes: + - .:/app + environment: + REDIS_HOST: redis + MONGO_HOST: mongo + CLSI_HOST: clsi + depends_on: + - clsi + - redis + - mongo + working_dir: /app + entrypoint: npm run test:acceptance + + redis: + image: redis + + mongo: + image: mongo:3.4 + + clsi: + image: gcr.io/henry-terraform-admin/clsi + build: . + environment: + TEXLIVE_IMAGE: quay.io/sharelatex/texlive-small:latest + TEXLIVE_IMAGE_USER: root # Not ideal, but makes running in dev very simple + COMMAND_RUNNER: docker-runner-sharelatex + SHARELATEX_CONFIG: /app/config/settings.defaults.coffee + COMPILES_HOST_DIR: $PWD/compiles + volumes: + - .:/app:cached + - /var/run/docker.sock:/var/run/docker.sock + - ./docker-runner:/app/node_modules/docker-runner-sharelatex + ports: + - 3013:3013 \ No newline at end of file diff --git a/docker-runner b/docker-runner new file mode 160000 index 0000000..f861a1c --- /dev/null +++ b/docker-runner @@ -0,0 +1 @@ +Subproject commit f861a1c810ad844a6e00e82f2ebedac26dcad8b7 diff --git a/nodemon.json b/nodemon.json new file mode 100644 index 0000000..9044f92 --- /dev/null +++ b/nodemon.json @@ -0,0 +1,15 @@ +{ + "ignore": [ + ".git", + "node_modules/" + ], + "verbose": true, + "execMap": { + "js": "npm run start" + }, + "watch": [ + "app/coffee/", + "app.coffee" + ], + "ext": "coffee" +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..4c110ea --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3162 @@ +{ + "name": "node-clsi", + "version": "0.1.4", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "JSONStream": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.10.0.tgz", + "integrity": "sha1-dDSdDYlSK3HzDwoD/5vSDKbxKsA=", + "requires": { + "jsonparse": "0.0.5", + "through": "2.3.8" + } + }, + "abbrev": { + "version": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha1-+PLIh60Qv2f2NPAFtph/7TF5qsg=" + }, + "accepts": { + "version": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", + "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", + "requires": { + "mime-types": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", + "negotiator": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz" + } + }, + "ajv": { + "version": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "requires": { + "co": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "fast-deep-equal": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", + "fast-json-stable-stringify": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "json-schema-traverse": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz" + } + }, + "ansi-regex": { + "version": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-0.2.0.tgz", + "integrity": "sha1-NZq0sV3NZLptdHNLcsNjYKmvLBk=", + "dev": true + }, + "aproba": { + "version": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha1-aALmJk79GMeQobDVF/DyYnvyyUo=" + }, + "are-we-there-yet": { + "version": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", + "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", + "requires": { + "delegates": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz" + }, + "dependencies": { + "isarray": { + "version": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha1-No8lEtefnUb9/HE0mueHi7weuVw=", + "requires": { + "core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "isarray": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "process-nextick-args": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "string_decoder": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "util-deprecate": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + } + }, + "string_decoder": { + "version": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", + "requires": { + "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + } + } + } + }, + "argparse": { + "version": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz", + "integrity": "sha1-z9AeD7uj1srtBJ+9dY1A9lGW9Xw=", + "dev": true, + "requires": { + "underscore": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", + "underscore.string": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz" + }, + "dependencies": { + "underscore": { + "version": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", + "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=", + "dev": true + }, + "underscore.string": { + "version": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz", + "integrity": "sha1-jN2PusTi0uoefi6Al8QvRCKA+Fs=", + "dev": true + } + } + }, + "array-flatten": { + "version": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "asn1": { + "version": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + }, + "assert-plus": { + "version": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "assertion-error": { + "version": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.0.tgz", + "integrity": "sha1-x/hUOP3UZrx8oWq5DIFRN5el0js=", + "dev": true + }, + "async": { + "version": "https://registry.npmjs.org/async/-/async-0.2.9.tgz", + "integrity": "sha1-32MGD789Myhqdqr21Vophtn/hhk=" + }, + "asynckit": { + "version": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" + }, + "balanced-match": { + "version": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "bcrypt-pbkdf": { + "version": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "optional": true, + "requires": { + "tweetnacl": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" + } + }, + "bignumber.js": { + "version": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.0.7.tgz", + "integrity": "sha1-husHB89qURCQnSPm6nQ0wU9QDxw=" + }, + "bl": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.1.tgz", + "integrity": "sha1-ysMo977kVzDUBLaSID/LWQ4XLV4=", + "requires": { + "readable-stream": "2.3.3" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "requires": { + "core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "isarray": "1.0.0", + "process-nextick-args": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "string_decoder": "1.0.3", + "util-deprecate": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "requires": { + "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + } + } + } + }, + "block-stream": { + "version": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "requires": { + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + } + }, + "bluebird": { + "version": "https://registry.npmjs.org/bluebird/-/bluebird-2.9.34.tgz", + "integrity": "sha1-L3tOyAIWMoqf3evfacjUlC/v99g=" + }, + "body-parser": { + "version": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", + "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", + "requires": { + "bytes": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "content-type": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "depd": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "http-errors": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "iconv-lite": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "on-finished": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "qs": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "raw-body": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", + "type-is": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz" + } + }, + "boom": { + "version": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", + "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", + "requires": { + "hoek": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz" + } + }, + "brace-expansion": { + "version": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "requires": { + "balanced-match": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "concat-map": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + }, + "bunyan": { + "version": "https://registry.npmjs.org/bunyan/-/bunyan-0.22.3.tgz", + "integrity": "sha1-ehncG0yMZF90AkGnQPIkUUfGfsI=", + "dev": true, + "requires": { + "dtrace-provider": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.2.8.tgz", + "mv": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz" + } + }, + "buster-core": { + "version": "https://registry.npmjs.org/buster-core/-/buster-core-0.6.4.tgz", + "integrity": "sha1-J79rrWdCROpyDzEdkAoMoct4YFA=", + "dev": true + }, + "buster-format": { + "version": "https://registry.npmjs.org/buster-format/-/buster-format-0.5.6.tgz", + "integrity": "sha1-K4bDIuz14bCubm55Bev884fSq5U=", + "dev": true, + "requires": { + "buster-core": "https://registry.npmjs.org/buster-core/-/buster-core-0.6.4.tgz" + } + }, + "bytes": { + "version": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "caseless": { + "version": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "chai": { + "version": "https://registry.npmjs.org/chai/-/chai-1.8.1.tgz", + "integrity": "sha1-zHeGbV5+vKK9dRRLHtw3Coh4X3I=", + "dev": true, + "requires": { + "assertion-error": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.0.tgz", + "deep-eql": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz" + } + }, + "chalk": { + "version": "https://registry.npmjs.org/chalk/-/chalk-0.3.0.tgz", + "integrity": "sha1-HJhDdzfxGZ68wdTEj9Qbn5yOjyM=", + "dev": true, + "requires": { + "ansi-styles": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-0.2.0.tgz", + "has-color": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz" + } + }, + "co": { + "version": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "code-point-at": { + "version": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "coffee-script": { + "version": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz", + "integrity": "sha1-gIs5bhEPU9AhoZpO8fZb4OjjX6M=", + "dev": true + }, + "colors": { + "version": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz", + "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=", + "dev": true + }, + "combined-stream": { + "version": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "requires": { + "delayed-stream": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + } + }, + "commander": { + "version": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz", + "integrity": "sha1-0bhvkB+LZL2UG96tr5JFMDk76Sg=" + }, + "concat-map": { + "version": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", + "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=", + "requires": { + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "readable-stream": "2.0.6", + "typedarray": "0.0.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", + "requires": { + "core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "isarray": "1.0.0", + "process-nextick-args": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "string_decoder": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "util-deprecate": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + } + } + } + }, + "console-control-strings": { + "version": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "content-disposition": { + "version": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + }, + "content-type": { + "version": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=" + }, + "cookie": { + "version": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "cookie-signature": { + "version": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "core-util-is": { + "version": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cryptiles": { + "version": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", + "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", + "requires": { + "boom": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz" + }, + "dependencies": { + "boom": { + "version": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", + "integrity": "sha1-XdnabuOl8wIHdDYpDLcX0/SlTgI=", + "requires": { + "hoek": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz" + } + } + } + }, + "dashdash": { + "version": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + } + }, + "dateformat": { + "version": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz", + "integrity": "sha1-sCIMAt6YYXQztyhRz0fePfLNvuk=", + "dev": true + }, + "debug": { + "version": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "requires": { + "ms": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + } + }, + "deep-eql": { + "version": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", + "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", + "dev": true, + "requires": { + "type-detect": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz" + } + }, + "deep-extend": { + "version": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", + "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=" + }, + "delayed-stream": { + "version": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "delegates": { + "version": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "depd": { + "version": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "detect-libc": { + "version": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" + }, + "diff": { + "version": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz", + "integrity": "sha1-JLuwAcSn1VIhaefKvbLCgU7ZHPQ=" + }, + "docker-modem": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-1.0.4.tgz", + "integrity": "sha512-pkXB9p7KWagegOXm2NsbVDBluQQLCBJzX9uYJzVbL6CHwe4d2sSbcACJ4K8ISX1l1JUUmFSiwNkBKc1uTiU4MA==", + "requires": { + "JSONStream": "0.10.0", + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "readable-stream": "1.0.34", + "split-ca": "1.0.1" + }, + "dependencies": { + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "requires": { + "core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "isarray": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "string_decoder": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + } + } + } + }, + "dockerode": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-2.5.3.tgz", + "integrity": "sha512-LQKXR5jyI+G/+5OhZCi40m0ArY4j46g7Tl71Vtn10Ekt5TiyDzZAoqXOCS6edQpEuGbdFgSDJxleFqLxACpKJg==", + "requires": { + "concat-stream": "1.5.2", + "docker-modem": "1.0.4", + "tar-fs": "1.12.0" + } + }, + "dottie": { + "version": "https://registry.npmjs.org/dottie/-/dottie-0.3.1.tgz", + "integrity": "sha1-QYX3d0iZYKwIQTHeP5oUoXm7dJ8=" + }, + "dtrace-provider": { + "version": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.2.8.tgz", + "integrity": "sha1-4kPxkhmqlfvw2PL/sH9b1k6U/iA=", + "dev": true, + "optional": true + }, + "ecc-jsbn": { + "version": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "optional": true, + "requires": { + "jsbn": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" + } + }, + "ee-first": { + "version": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", + "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=" + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "requires": { + "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + } + }, + "escape-html": { + "version": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "esprima": { + "version": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz", + "integrity": "sha1-n1V+CPw7TSbs6d00+Pv0drYlha0=", + "dev": true + }, + "etag": { + "version": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "eventemitter2": { + "version": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", + "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=", + "dev": true + }, + "exit": { + "version": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "express": { + "version": "https://registry.npmjs.org/express/-/express-4.16.2.tgz", + "integrity": "sha1-41xt/i1kt9ygpc1PIXgb4ymeB2w=", + "requires": { + "accepts": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", + "array-flatten": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "body-parser": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", + "content-disposition": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "content-type": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "cookie": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "cookie-signature": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "depd": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "encodeurl": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", + "escape-html": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "etag": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "finalhandler": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", + "fresh": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "merge-descriptors": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "methods": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "on-finished": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "parseurl": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "path-to-regexp": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "proxy-addr": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz", + "qs": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "range-parser": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "send": "https://registry.npmjs.org/send/-/send-0.16.1.tgz", + "serve-static": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz", + "setprototypeof": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "statuses": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "type-is": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", + "utils-merge": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "vary": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" + }, + "dependencies": { + "setprototypeof": { + "version": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha1-0L2FU2iHtv58DYGMuWLZ2RxU5lY=" + }, + "statuses": { + "version": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" + } + } + }, + "extend": { + "version": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" + }, + "extsprintf": { + "version": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", + "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=" + }, + "fast-json-stable-stringify": { + "version": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "finalhandler": { + "version": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", + "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", + "requires": { + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "encodeurl": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", + "escape-html": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "on-finished": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "parseurl": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "statuses": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "unpipe": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" + }, + "dependencies": { + "statuses": { + "version": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" + } + } + }, + "findup-sync": { + "version": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz", + "integrity": "sha1-fz56l7gjksZTvwZYm9hRkOk8NoM=", + "dev": true, + "requires": { + "glob": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + }, + "dependencies": { + "glob": { + "version": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", + "dev": true, + "requires": { + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz" + } + }, + "lodash": { + "version": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=", + "dev": true + }, + "minimatch": { + "version": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", + "dev": true, + "requires": { + "lru-cache": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "sigmund": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + } + }, + "forever-agent": { + "version": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz", + "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=", + "requires": { + "asynckit": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "combined-stream": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "mime-types": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz" + } + }, + "forwarded": { + "version": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "fs-extra": { + "version": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.16.5.tgz", + "integrity": "sha1-GtZh+myGyWCM0bSe/G/Og0k5p1A=", + "requires": { + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz", + "jsonfile": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz" + } + }, + "fs.realpath": { + "version": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fstream": { + "version": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "requires": { + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz" + }, + "dependencies": { + "graceful-fs": { + "version": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + }, + "mkdirp": { + "version": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + } + } + }, + "fstream-ignore": { + "version": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", + "integrity": "sha1-nDHa40dnAY/h0kmyTa2mfQktoQU=", + "requires": { + "fstream": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" + } + }, + "gauge": { + "version": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "console-control-strings": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "has-unicode": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "signal-exit": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "string-width": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "wide-align": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz" + } + }, + "generic-pool": { + "version": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.2.0.tgz", + "integrity": "sha1-i0ZcGnWI6p3SuxM72gu2a/74pj4=" + }, + "getobject": { + "version": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz", + "integrity": "sha1-BHpEl4n6Fg0Bj1SG7ZEyC27HiFw=", + "dev": true + }, + "getpass": { + "version": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + } + }, + "glob": { + "version": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", + "requires": { + "fs.realpath": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "inflight": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "path-is-absolute": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + } + }, + "graceful-fs": { + "version": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz", + "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=", + "requires": { + "natives": "https://registry.npmjs.org/natives/-/natives-1.1.1.tgz" + } + }, + "growl": { + "version": "https://registry.npmjs.org/growl/-/growl-1.7.0.tgz", + "integrity": "sha1-3i1mE20ALhErpw8/EMMc98NQsto=" + }, + "grunt": { + "version": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz", + "integrity": "sha1-VpN81RlDJK3/bSB2MYMqnWuk5/A=", + "dev": true, + "requires": { + "async": "https://registry.npmjs.org/async/-/async-0.1.22.tgz", + "coffee-script": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz", + "colors": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz", + "dateformat": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz", + "eventemitter2": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", + "exit": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "findup-sync": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz", + "getobject": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz", + "glob": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", + "grunt-legacy-log": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.3.tgz", + "grunt-legacy-util": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz", + "hooker": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", + "iconv-lite": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz", + "js-yaml": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz", + "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "nopt": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", + "underscore.string": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz", + "which": "https://registry.npmjs.org/which/-/which-1.0.9.tgz" + }, + "dependencies": { + "async": { + "version": "https://registry.npmjs.org/async/-/async-0.1.22.tgz", + "integrity": "sha1-D8GqoIig4+8Ovi2IMbqw3PiEUGE=", + "dev": true + }, + "coffee-script": { + "version": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz", + "integrity": "sha1-FQ1rTLUiiUNp7+1qIQHCC8f0pPQ=", + "dev": true + }, + "glob": { + "version": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", + "integrity": "sha1-0p4KBV3qUTj00H7UDomC6DwgZs0=", + "dev": true, + "requires": { + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz", + "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz" + } + }, + "graceful-fs": { + "version": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz", + "integrity": "sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q=", + "dev": true + }, + "iconv-lite": { + "version": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz", + "integrity": "sha1-HOYKOleGSiktEyH/RgnKS7llrcg=", + "dev": true + }, + "inherits": { + "version": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz", + "integrity": "sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js=", + "dev": true + }, + "lodash": { + "version": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz", + "integrity": "sha1-jzSZxSRdNG1oLlsNO0B2fgnxqSw=", + "dev": true + }, + "minimatch": { + "version": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", + "dev": true, + "requires": { + "lru-cache": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "sigmund": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + }, + "nopt": { + "version": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", + "dev": true, + "requires": { + "abbrev": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz" + } + }, + "rimraf": { + "version": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", + "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=", + "dev": true + } + } + }, + "grunt-bunyan": { + "version": "https://registry.npmjs.org/grunt-bunyan/-/grunt-bunyan-0.5.0.tgz", + "integrity": "sha1-aCnXbgGZQ9owQTk2MaNuKsgpsWw=", + "dev": true, + "requires": { + "lodash": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + }, + "dependencies": { + "lodash": { + "version": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=", + "dev": true + } + } + }, + "grunt-contrib-clean": { + "version": "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-0.5.0.tgz", + "integrity": "sha1-9T397ghJsce0Dp67umn0jExgecU=", + "dev": true, + "requires": { + "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" + }, + "dependencies": { + "rimraf": { + "version": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", + "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=", + "dev": true + } + } + }, + "grunt-contrib-coffee": { + "version": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.7.0.tgz", + "integrity": "sha1-ixIme3TnM4sfKcW4txj7n4mYLxM=", + "dev": true, + "requires": { + "coffee-script": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.3.tgz" + }, + "dependencies": { + "coffee-script": { + "version": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.3.tgz", + "integrity": "sha1-Y1XTLPGwTN/2tITl5xF4Ky8MOb4=", + "dev": true + } + } + }, + "grunt-execute": { + "version": "https://registry.npmjs.org/grunt-execute/-/grunt-execute-0.1.5.tgz", + "integrity": "sha1-yX64lDYS/vu3L749Mu+VIzxfouk=", + "dev": true + }, + "grunt-legacy-log": { + "version": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.3.tgz", + "integrity": "sha1-7ClCboAwIa9ZAp+H0vnNczWgVTE=", + "dev": true, + "requires": { + "colors": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz", + "grunt-legacy-log-utils": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-0.1.1.tgz", + "hooker": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "underscore.string": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz" + }, + "dependencies": { + "lodash": { + "version": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=", + "dev": true + }, + "underscore.string": { + "version": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz", + "integrity": "sha1-ccCL9rQosRM/N+ePo6Icgvcymw0=", + "dev": true + } + } + }, + "grunt-legacy-log-utils": { + "version": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-0.1.1.tgz", + "integrity": "sha1-wHBrndkGThFvNvI/5OawSGcsD34=", + "dev": true, + "requires": { + "colors": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "underscore.string": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz" + }, + "dependencies": { + "lodash": { + "version": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=", + "dev": true + }, + "underscore.string": { + "version": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz", + "integrity": "sha1-ccCL9rQosRM/N+ePo6Icgvcymw0=", + "dev": true + } + } + }, + "grunt-legacy-util": { + "version": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz", + "integrity": "sha1-kzJIhNv343qf98Am3/RR2UqeVUs=", + "dev": true, + "requires": { + "async": "https://registry.npmjs.org/async/-/async-0.1.22.tgz", + "exit": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "getobject": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz", + "hooker": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz", + "underscore.string": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz", + "which": "https://registry.npmjs.org/which/-/which-1.0.9.tgz" + }, + "dependencies": { + "async": { + "version": "https://registry.npmjs.org/async/-/async-0.1.22.tgz", + "integrity": "sha1-D8GqoIig4+8Ovi2IMbqw3PiEUGE=", + "dev": true + }, + "lodash": { + "version": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz", + "integrity": "sha1-jzSZxSRdNG1oLlsNO0B2fgnxqSw=", + "dev": true + } + } + }, + "grunt-mkdir": { + "version": "https://registry.npmjs.org/grunt-mkdir/-/grunt-mkdir-1.0.0.tgz", + "integrity": "sha1-c+GiasJKCFljY/TdlUsNMkheWOk=" + }, + "grunt-mocha-test": { + "version": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.8.2.tgz", + "integrity": "sha1-emGEuYhg0Phb3qrWvqob199bvus=", + "dev": true, + "requires": { + "mocha": "https://registry.npmjs.org/mocha/-/mocha-1.14.0.tgz" + }, + "dependencies": { + "glob": { + "version": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", + "integrity": "sha1-4xPusknHr/qlxHUoaw4RW1mDlGc=", + "dev": true, + "requires": { + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz" + } + }, + "graceful-fs": { + "version": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz", + "integrity": "sha1-fNLNsiiko/Nule+mzBQt59GhNtA=", + "dev": true + }, + "minimatch": { + "version": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", + "dev": true, + "requires": { + "lru-cache": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "sigmund": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + }, + "mocha": { + "version": "https://registry.npmjs.org/mocha/-/mocha-1.14.0.tgz", + "integrity": "sha1-cT223FAAGRqdA1gZXQkIeQ7LYVc=", + "dev": true, + "requires": { + "commander": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz", + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "diff": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz", + "glob": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", + "growl": "https://registry.npmjs.org/growl/-/growl-1.7.0.tgz", + "jade": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", + "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + } + } + } + }, + "grunt-shell": { + "version": "https://registry.npmjs.org/grunt-shell/-/grunt-shell-0.6.4.tgz", + "integrity": "sha1-5KbRuSkSd2/ZOimcX2zGTpUlNlw=", + "dev": true, + "requires": { + "chalk": "https://registry.npmjs.org/chalk/-/chalk-0.3.0.tgz" + } + }, + "har-schema": { + "version": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "requires": { + "ajv": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "har-schema": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz" + } + }, + "has-color": { + "version": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz", + "integrity": "sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8=", + "dev": true + }, + "has-unicode": { + "version": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "hawk": { + "version": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", + "integrity": "sha1-r02RTrBl+bXOTZ0RwcshJu7MMDg=", + "requires": { + "boom": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", + "cryptiles": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", + "hoek": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz", + "sntp": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz" + } + }, + "heapdump": { + "version": "https://registry.npmjs.org/heapdump/-/heapdump-0.3.9.tgz", + "integrity": "sha1-A8dOsN9dZ74Jgug0KbqcnSs7f3g=" + }, + "hoek": { + "version": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz", + "integrity": "sha1-ctnQdU9/4lyi0BrY+PmpRJqJUm0=" + }, + "hooker": { + "version": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", + "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=", + "dev": true + }, + "http-errors": { + "version": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "requires": { + "depd": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "setprototypeof": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "statuses": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz" + }, + "dependencies": { + "depd": { + "version": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" + } + } + }, + "http-signature": { + "version": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "jsprim": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "sshpk": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz" + } + }, + "iconv-lite": { + "version": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha1-90aPYBNfXl2tM5nAqBvpoWA6CCs=" + }, + "inflection": { + "version": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz", + "integrity": "sha1-ogCTVlbW9fa8TcdQLhrstwMihBY=" + }, + "inflight": { + "version": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "wrappy": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + } + }, + "inherits": { + "version": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha1-7uJfVtscnsYIXgwid4CD9Zar+Sc=" + }, + "ipaddr.js": { + "version": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.5.2.tgz", + "integrity": "sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A=" + }, + "is-fullwidth-code-point": { + "version": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz" + } + }, + "is-typedarray": { + "version": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isarray": { + "version": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "isstream": { + "version": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jade": { + "version": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", + "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=", + "requires": { + "commander": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", + "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz" + }, + "dependencies": { + "commander": { + "version": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", + "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=" + }, + "mkdirp": { + "version": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", + "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=" + } + } + }, + "js-yaml": { + "version": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz", + "integrity": "sha1-olrmUJmZ6X3yeMZxnaEb0Gh3Q6g=", + "dev": true, + "requires": { + "argparse": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz", + "esprima": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz" + } + }, + "jsbn": { + "version": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true + }, + "json-schema": { + "version": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" + }, + "json-stable-stringify": { + "version": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "requires": { + "jsonify": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz" + } + }, + "json-stringify-safe": { + "version": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsonfile": { + "version": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "requires": { + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz" + }, + "dependencies": { + "graceful-fs": { + "version": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "optional": true + } + } + }, + "jsonify": { + "version": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" + }, + "jsonparse": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz", + "integrity": "sha1-MwVCrT8KZUZlt3jz6y2an6UHrGQ=" + }, + "jsprim": { + "version": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "extsprintf": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "json-schema": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "verror": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz" + } + }, + "lockfile": { + "version": "https://registry.npmjs.org/lockfile/-/lockfile-1.0.3.tgz", + "integrity": "sha1-Jjj8OaAzHpysGgS3F5mTHJxQ33k=" + }, + "lodash": { + "version": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" + }, + "logger-sharelatex": { + "version": "git+https://github.com/sharelatex/logger-sharelatex.git#9ee7b52eb2bbd8fcbb1e2c708587c1e93fd4c733", + "integrity": "sha1-aRMA+7GVHSmsRMbyj5cLhnueM9Q=", + "requires": { + "bunyan": "1.5.1", + "coffee-script": "1.4.0", + "raven": "https://registry.npmjs.org/raven/-/raven-1.2.1.tgz" + }, + "dependencies": { + "bunyan": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.5.1.tgz", + "integrity": "sha1-X259RMQ7lS9WsPQTCeOrEjkbTi0=", + "requires": { + "dtrace-provider": "0.6.0", + "mv": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "safe-json-stringify": "1.0.4" + } + }, + "coffee-script": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.4.0.tgz", + "integrity": "sha1-XjvIqsJsAajie/EHcixWVfWtfTY=" + }, + "dtrace-provider": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.6.0.tgz", + "integrity": "sha1-CweNVReTfYcxAUUtkUZzdVe3XlE=", + "optional": true, + "requires": { + "nan": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz" + } + } + } + }, + "lru-cache": { + "version": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" + }, + "lsmod": { + "version": "https://registry.npmjs.org/lsmod/-/lsmod-1.0.0.tgz", + "integrity": "sha1-mgD3bco26yP6BTUK/htYXUKZ5ks=" + }, + "lynx": { + "version": "https://registry.npmjs.org/lynx/-/lynx-0.0.11.tgz", + "integrity": "sha1-LPoU5EP9LZKlm3efQVZ84cxpZaM=", + "requires": { + "mersenne": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.4.tgz", + "statsd-parser": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz" + } + }, + "media-typer": { + "version": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "mersenne": { + "version": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.4.tgz", + "integrity": "sha1-QB/ex+whzbngPNPTAhOY2iGycIU=" + }, + "methods": { + "version": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "metrics-sharelatex": { + "version": "git+https://github.com/sharelatex/metrics-sharelatex.git#05bd57604115ae5efdca2a419fbef4f25e9a780f", + "integrity": "sha1-ruLAc3Tl1GZrAQjP/K4NeRajdW4=", + "requires": { + "coffee-script": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz", + "lynx": "https://registry.npmjs.org/lynx/-/lynx-0.1.1.tgz", + "underscore": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz" + }, + "dependencies": { + "coffee-script": { + "version": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz", + "integrity": "sha1-gIs5bhEPU9AhoZpO8fZb4OjjX6M=" + }, + "lynx": { + "version": "https://registry.npmjs.org/lynx/-/lynx-0.1.1.tgz", + "integrity": "sha1-Mxjc7xaQi4KG6Bisz9sxzXQkj50=", + "requires": { + "mersenne": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.4.tgz", + "statsd-parser": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz" + } + }, + "underscore": { + "version": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=" + } + } + }, + "mime": { + "version": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha1-Eh+evEnjdm8xGnbh+hyAA8SwOqY=" + }, + "mime-db": { + "version": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", + "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" + }, + "mime-types": { + "version": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", + "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", + "requires": { + "mime-db": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz" + } + }, + "minimatch": { + "version": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "requires": { + "brace-expansion": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz" + } + }, + "minimist": { + "version": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mkdirp": { + "version": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=" + }, + "mocha": { + "version": "https://registry.npmjs.org/mocha/-/mocha-1.10.0.tgz", + "integrity": "sha1-8WrMlQ75Vm+/kIvPttXQQWw50No=", + "dev": true, + "requires": { + "commander": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "diff": "https://registry.npmjs.org/diff/-/diff-1.0.2.tgz", + "glob": "https://registry.npmjs.org/glob/-/glob-3.2.1.tgz", + "growl": "https://registry.npmjs.org/growl/-/growl-1.7.0.tgz", + "jade": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", + "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.3.tgz", + "ms": "https://registry.npmjs.org/ms/-/ms-0.3.0.tgz" + }, + "dependencies": { + "commander": { + "version": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", + "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=", + "dev": true + }, + "diff": { + "version": "https://registry.npmjs.org/diff/-/diff-1.0.2.tgz", + "integrity": "sha1-Suc/Gu6Nb89ITxoc53zmUdm38Mk=", + "dev": true + }, + "glob": { + "version": "https://registry.npmjs.org/glob/-/glob-3.2.1.tgz", + "integrity": "sha1-V69w7HO6IyO/4/KaBndl22TF11g=", + "dev": true, + "requires": { + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz", + "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz" + } + }, + "graceful-fs": { + "version": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz", + "integrity": "sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q=", + "dev": true + }, + "inherits": { + "version": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz", + "integrity": "sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js=", + "dev": true + }, + "minimatch": { + "version": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", + "dev": true, + "requires": { + "lru-cache": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "sigmund": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + }, + "mkdirp": { + "version": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.3.tgz", + "integrity": "sha1-WV4lHBNww6aLqyE20ONIuBBa3xM=", + "dev": true + }, + "ms": { + "version": "https://registry.npmjs.org/ms/-/ms-0.3.0.tgz", + "integrity": "sha1-A+3DSNYT5mpWSGz9rFO8vomcvWE=", + "dev": true + } + } + }, + "moment": { + "version": "https://registry.npmjs.org/moment/-/moment-2.20.1.tgz", + "integrity": "sha1-1usaRsvMFKKy+UNBEsH/iQfzE/0=" + }, + "ms": { + "version": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "mv": { + "version": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", + "optional": true, + "requires": { + "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "ncp": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz" + }, + "dependencies": { + "glob": { + "version": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "optional": true, + "requires": { + "inflight": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "path-is-absolute": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + } + }, + "mkdirp": { + "version": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "optional": true, + "requires": { + "minimist": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + }, + "rimraf": { + "version": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", + "optional": true, + "requires": { + "glob": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz" + } + } + } + }, + "mysql": { + "version": "https://registry.npmjs.org/mysql/-/mysql-2.6.2.tgz", + "integrity": "sha1-k3Gd0yT1fUHET7bX+GDPjmOFk7A=", + "requires": { + "bignumber.js": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.0.7.tgz", + "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "require-all": "https://registry.npmjs.org/require-all/-/require-all-1.0.0.tgz" + } + }, + "nan": { + "version": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", + "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=" + }, + "natives": { + "version": "https://registry.npmjs.org/natives/-/natives-1.1.1.tgz", + "integrity": "sha1-ARrM4ffL2H97prMJPWzZOSvhxXQ=" + }, + "ncp": { + "version": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", + "optional": true + }, + "negotiator": { + "version": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" + }, + "node-pre-gyp": { + "version": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz", + "integrity": "sha1-wA6WhgsjwOFCCse+/FBE4deNhkk=", + "requires": { + "detect-libc": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "hawk": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "nopt": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "npmlog": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "rc": "https://registry.npmjs.org/rc/-/rc-1.2.3.tgz", + "request": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "semver": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "tar": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "tar-pack": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.1.tgz" + }, + "dependencies": { + "ajv": { + "version": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "requires": { + "co": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "json-stable-stringify": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz" + } + }, + "assert-plus": { + "version": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=" + }, + "aws-sign2": { + "version": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=" + }, + "boom": { + "version": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "requires": { + "hoek": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz" + } + }, + "cryptiles": { + "version": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "requires": { + "boom": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz" + } + }, + "form-data": { + "version": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "requires": { + "asynckit": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "combined-stream": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "mime-types": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz" + } + }, + "har-schema": { + "version": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", + "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=" + }, + "har-validator": { + "version": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", + "requires": { + "ajv": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "har-schema": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz" + } + }, + "hawk": { + "version": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "requires": { + "boom": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "cryptiles": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "hoek": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "sntp": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz" + } + }, + "hoek": { + "version": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" + }, + "http-signature": { + "version": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "requires": { + "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "jsprim": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "sshpk": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz" + } + }, + "mkdirp": { + "version": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + }, + "performance-now": { + "version": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=" + }, + "qs": { + "version": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=" + }, + "request": { + "version": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", + "requires": { + "aws-sign2": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "aws4": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "caseless": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "combined-stream": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "extend": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "forever-agent": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "form-data": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "har-validator": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "hawk": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "http-signature": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "is-typedarray": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "isstream": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "json-stringify-safe": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "mime-types": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", + "oauth-sign": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "performance-now": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "qs": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "stringstream": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "tough-cookie": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", + "tunnel-agent": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "uuid": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz" + } + }, + "sntp": { + "version": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "requires": { + "hoek": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz" + } + } + } + }, + "nopt": { + "version": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "requires": { + "abbrev": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "osenv": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz" + } + }, + "npmlog": { + "version": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha1-CKfyqL9zRgR3mp76StXMcXq7lUs=", + "requires": { + "are-we-there-yet": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", + "console-control-strings": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "gauge": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "set-blocking": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" + } + }, + "number-is-nan": { + "version": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "oauth-sign": { + "version": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" + }, + "object-assign": { + "version": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "on-finished": { + "version": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" + } + }, + "once": { + "version": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + } + }, + "os-homedir": { + "version": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "os-tmpdir": { + "version": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", + "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=", + "requires": { + "os-homedir": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "os-tmpdir": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" + } + }, + "parseurl": { + "version": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" + }, + "path-is-absolute": { + "version": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-to-regexp": { + "version": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "performance-now": { + "version": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "process-nextick-args": { + "version": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + }, + "proxy-addr": { + "version": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz", + "integrity": "sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=", + "requires": { + "forwarded": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "ipaddr.js": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.5.2.tgz" + } + }, + "pump": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz", + "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==", + "requires": { + "end-of-stream": "1.4.1", + "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + } + }, + "punycode": { + "version": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "qs": { + "version": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha1-NJzfbu+J7EXBLX1es/wMhwNDptg=" + }, + "range-parser": { + "version": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + }, + "raven": { + "version": "https://registry.npmjs.org/raven/-/raven-1.2.1.tgz", + "integrity": "sha1-lJwTTbAooZC3u/j3kKrlQbfAIL0=", + "requires": { + "cookie": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "json-stringify-safe": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "lsmod": "https://registry.npmjs.org/lsmod/-/lsmod-1.0.0.tgz", + "stack-trace": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz", + "uuid": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz" + } + }, + "raw-body": { + "version": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", + "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", + "requires": { + "bytes": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "http-errors": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "iconv-lite": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "unpipe": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" + } + }, + "rc": { + "version": "https://registry.npmjs.org/rc/-/rc-1.2.3.tgz", + "integrity": "sha1-UVdakA+N1oOBxxC0cSwhVMPiA1s=", + "requires": { + "deep-extend": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", + "ini": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "minimist": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "strip-json-comments": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" + }, + "dependencies": { + "minimist": { + "version": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, + "readable-stream": { + "version": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "isarray": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "string_decoder": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + } + }, + "request": { + "version": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", + "integrity": "sha1-ygtl2gLtYpNYh4COb1EDgQNOM1Y=", + "requires": { + "aws-sign2": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "aws4": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "caseless": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "combined-stream": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "extend": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "forever-agent": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "form-data": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz", + "har-validator": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "hawk": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", + "http-signature": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "is-typedarray": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "isstream": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "json-stringify-safe": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "mime-types": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", + "oauth-sign": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "performance-now": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "qs": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "stringstream": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "tough-cookie": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", + "tunnel-agent": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "uuid": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz" + }, + "dependencies": { + "uuid": { + "version": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha1-PdPT55Crwk17DToDT/q6vijrvAQ=" + } + } + }, + "require-all": { + "version": "https://registry.npmjs.org/require-all/-/require-all-1.0.0.tgz", + "integrity": "sha1-hINwjnzkxt+tmItQgPl4KbktIic=" + }, + "require-like": { + "version": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", + "integrity": "sha1-rW8wwTvs15cBDEaK+ndcDAprR/o=", + "dev": true + }, + "rimraf": { + "version": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha1-LtgVDSShbqhlHm1u8PR8QVjOejY=", + "requires": { + "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz" + } + }, + "safe-buffer": { + "version": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=" + }, + "safe-json-stringify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.0.4.tgz", + "integrity": "sha1-gaCY9Efku8P/MxKiQ1IbwGDvWRE=", + "optional": true + }, + "sandboxed-module": { + "version": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-0.3.0.tgz", + "integrity": "sha1-8fvvvYCaT2kHO9B8rm/H2y6vX2o=", + "dev": true, + "requires": { + "require-like": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", + "stack-trace": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.6.tgz" + }, + "dependencies": { + "stack-trace": { + "version": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.6.tgz", + "integrity": "sha1-HnGb1qJin/CcGJ4Xqe+QKpT8XbA=", + "dev": true + } + } + }, + "semver": { + "version": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha1-4FnAnYVx8FQII3M0M1BdOi8AsY4=" + }, + "send": { + "version": "https://registry.npmjs.org/send/-/send-0.16.1.tgz", + "integrity": "sha1-pw4coh0TgsEdDZ9iMd6ygQgNerM=", + "requires": { + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "depd": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "destroy": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "encodeurl": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", + "escape-html": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "etag": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "fresh": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "http-errors": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "mime": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "ms": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "on-finished": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "range-parser": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "statuses": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz" + }, + "dependencies": { + "statuses": { + "version": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" + } + } + }, + "sequelize": { + "version": "https://registry.npmjs.org/sequelize/-/sequelize-2.1.3.tgz", + "integrity": "sha1-FrpROxNnlY/oIzhYEelRFFHt6tE=", + "requires": { + "bluebird": "https://registry.npmjs.org/bluebird/-/bluebird-2.9.34.tgz", + "dottie": "https://registry.npmjs.org/dottie/-/dottie-0.3.1.tgz", + "generic-pool": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.2.0.tgz", + "inflection": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz", + "lodash": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "moment": "https://registry.npmjs.org/moment/-/moment-2.20.1.tgz", + "node-uuid": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", + "toposort-class": "https://registry.npmjs.org/toposort-class/-/toposort-class-0.3.1.tgz", + "validator": "https://registry.npmjs.org/validator/-/validator-3.43.0.tgz" + }, + "dependencies": { + "node-uuid": { + "version": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", + "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=" + } + } + }, + "serve-static": { + "version": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz", + "integrity": "sha1-TFfVNASnYdjy58HooYpH2/J4pxk=", + "requires": { + "encodeurl": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", + "escape-html": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "parseurl": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "send": "https://registry.npmjs.org/send/-/send-0.16.1.tgz" + } + }, + "set-blocking": { + "version": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "setprototypeof": { + "version": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" + }, + "settings-sharelatex": { + "version": "git+https://github.com/sharelatex/settings-sharelatex.git#cbc5e41c1dbe6789721a14b3fdae05bf22546559", + "integrity": "sha1-RatFGqtZ7wuhO78vGSB60S3H9Rc=", + "requires": { + "coffee-script": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz" + }, + "dependencies": { + "coffee-script": { + "version": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz", + "integrity": "sha1-gIs5bhEPU9AhoZpO8fZb4OjjX6M=" + } + } + }, + "sigmund": { + "version": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" + }, + "signal-exit": { + "version": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "sinon": { + "version": "https://registry.npmjs.org/sinon/-/sinon-1.7.3.tgz", + "integrity": "sha1-emnWnNApRYbHQyVO7/G1g6UJl/I=", + "dev": true, + "requires": { + "buster-format": "https://registry.npmjs.org/buster-format/-/buster-format-0.5.6.tgz" + } + }, + "smoke-test-sharelatex": { + "version": "git+https://github.com/sharelatex/smoke-test-sharelatex.git#bc3e93d18ccee219c0d99e8b02c984ccdd842e1c", + "integrity": "sha1-s6idlk0vuV2Kz+u6rn3ITbaMEQ4=", + "requires": { + "mocha": "1.17.1" + }, + "dependencies": { + "glob": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", + "integrity": "sha1-4xPusknHr/qlxHUoaw4RW1mDlGc=", + "requires": { + "graceful-fs": "2.0.3", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "minimatch": "0.2.14" + } + }, + "graceful-fs": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz", + "integrity": "sha1-fNLNsiiko/Nule+mzBQt59GhNtA=" + }, + "minimatch": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", + "requires": { + "lru-cache": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "sigmund": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + }, + "mocha": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-1.17.1.tgz", + "integrity": "sha1-f3Zx1oUm0HS3uuZgyQmfh+DqHMs=", + "requires": { + "commander": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz", + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "diff": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz", + "glob": "3.2.3", + "growl": "https://registry.npmjs.org/growl/-/growl-1.7.0.tgz", + "jade": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", + "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + } + } + } + }, + "sntp": { + "version": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", + "integrity": "sha1-LGzsFP7cIiJznK+bXD2F0cxaLMg=", + "requires": { + "hoek": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz" + } + }, + "split-ca": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", + "integrity": "sha1-bIOv82kvphJW4M0ZfgXp3hV2kaY=" + }, + "sqlite3": { + "version": "https://registry.npmjs.org/sqlite3/-/sqlite3-3.1.13.tgz", + "integrity": "sha1-2ZCgVic5J2jeYni6/Rox/f6Qfdk=", + "requires": { + "nan": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz", + "node-pre-gyp": "0.6.38" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "are-we-there-yet": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", + "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.3.3" + } + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + }, + "assert-plus": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=" + }, + "aws4": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "requires": { + "inherits": "2.0.3" + } + }, + "boom": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "requires": { + "hoek": "2.16.3" + } + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "combined-stream": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "requires": { + "delayed-stream": "1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cryptiles": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "requires": { + "boom": "2.10.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", + "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=" + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.17" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fstream": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "requires": { + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.6.2" + } + }, + "fstream-ignore": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", + "integrity": "sha1-nDHa40dnAY/h0kmyTa2mfQktoQU=", + "requires": { + "fstream": "1.0.11", + "inherits": "2.0.3", + "minimatch": "3.0.4" + } + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + }, + "har-schema": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", + "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=" + }, + "har-validator": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", + "requires": { + "ajv": "4.11.8", + "har-schema": "1.0.5" + } + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "hawk": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } + }, + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" + }, + "http-signature": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.1", + "sshpk": "1.13.1" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", + "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "requires": { + "jsonify": "0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "mime-db": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", + "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" + }, + "mime-types": { + "version": "2.1.17", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", + "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", + "requires": { + "mime-db": "1.30.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "1.1.8" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "nan": { + "version": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz", + "integrity": "sha1-2Vv3IeyHfgjbJ27T/G63j5CDrUY=" + }, + "node-pre-gyp": { + "version": "0.6.38", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.38.tgz", + "integrity": "sha1-6Sog+DQWQVu0CG9tH7eLPac9ET0=", + "requires": { + "hawk": "3.1.3", + "mkdirp": "0.5.1", + "nopt": "4.0.1", + "npmlog": "4.1.2", + "rc": "1.2.1", + "request": "2.81.0", + "rimraf": "2.6.2", + "semver": "5.4.1", + "tar": "2.2.1", + "tar-pack": "3.4.0" + } + }, + "nopt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "requires": { + "abbrev": "1.1.1", + "osenv": "0.1.4" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1.0.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", + "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=", + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "performance-now": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=" + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "qs": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=" + }, + "rc": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz", + "integrity": "sha1-LgPo5C7kULjLPc5lvhv4l04d/ZU=", + "requires": { + "deep-extend": "0.4.2", + "ini": "1.3.4", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "request": { + "version": "2.81.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "4.2.1", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.17", + "oauth-sign": "0.8.2", + "performance-now": "0.2.0", + "qs": "6.4.0", + "safe-buffer": "5.1.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.3", + "tunnel-agent": "0.6.0", + "uuid": "3.1.0" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "requires": { + "glob": "7.1.2" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "sntp": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "requires": { + "hoek": "2.16.3" + } + }, + "sshpk": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "requires": { + "safe-buffer": "5.1.1" + } + }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, + "tar": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "requires": { + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" + } + }, + "tar-pack": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.0.tgz", + "integrity": "sha1-I74tf2cagzk3bL2wuP4/3r8xeYQ=", + "requires": { + "debug": "2.6.9", + "fstream": "1.0.11", + "fstream-ignore": "1.0.5", + "once": "1.4.0", + "readable-stream": "2.3.3", + "rimraf": "2.6.2", + "tar": "2.2.1", + "uid-number": "0.0.6" + } + }, + "tough-cookie": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", + "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", + "requires": { + "punycode": "1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "5.1.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "optional": true + }, + "uid-number": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", + "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "uuid": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "1.3.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "wide-align": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", + "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", + "requires": { + "string-width": "1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + } + } + }, + "sshpk": { + "version": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "requires": { + "asn1": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "bcrypt-pbkdf": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "dashdash": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "ecc-jsbn": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "getpass": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "jsbn": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "tweetnacl": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" + } + }, + "stack-trace": { + "version": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz", + "integrity": "sha1-qPbq7KkGdMMz58Q5U/J1tFFRBpU=" + }, + "statsd-parser": { + "version": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz", + "integrity": "sha1-y9JDlTzELv/VSLXSI4jtaJ7GOb0=" + }, + "statuses": { + "version": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha1-u3PURtonlhBu/MG2AaJT1sRr0Ic=" + }, + "string-width": { + "version": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "is-fullwidth-code-point": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" + } + }, + "string_decoder": { + "version": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "stringstream": { + "version": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" + }, + "strip-ansi": { + "version": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" + } + }, + "strip-json-comments": { + "version": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, + "tar": { + "version": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "requires": { + "block-stream": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "fstream": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + } + }, + "tar-fs": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.12.0.tgz", + "integrity": "sha1-pqgFU9ilTHPeHQrg553ncDVgXh0=", + "requires": { + "mkdirp": "0.5.1", + "pump": "1.0.3", + "tar-stream": "1.5.5" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + } + } + }, + "tar-pack": { + "version": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.1.tgz", + "integrity": "sha1-4dvAOpudO6B+iWrQJzF+tnmhCh8=", + "requires": { + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "fstream": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "fstream-ignore": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", + "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "tar": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "uid-number": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz" + }, + "dependencies": { + "isarray": { + "version": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha1-No8lEtefnUb9/HE0mueHi7weuVw=", + "requires": { + "core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "isarray": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "process-nextick-args": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "string_decoder": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "util-deprecate": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + } + }, + "string_decoder": { + "version": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", + "requires": { + "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + } + } + } + }, + "tar-stream": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.5.tgz", + "integrity": "sha512-mQdgLPc/Vjfr3VWqWbfxW8yQNiJCbAZ+Gf6GDu1Cy0bdb33ofyiNGBtAY96jHFhDuivCwgW1H9DgTON+INiXgg==", + "requires": { + "bl": "1.2.1", + "end-of-stream": "1.4.1", + "readable-stream": "2.3.3", + "xtend": "4.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "requires": { + "core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "isarray": "1.0.0", + "process-nextick-args": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "string_decoder": "1.0.3", + "util-deprecate": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "requires": { + "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + } + } + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "timekeeper": { + "version": "https://registry.npmjs.org/timekeeper/-/timekeeper-0.0.4.tgz", + "integrity": "sha1-kNt58X2Ni1NiFUOJSSuXJ2LP0nY=", + "dev": true + }, + "toposort-class": { + "version": "https://registry.npmjs.org/toposort-class/-/toposort-class-0.3.1.tgz", + "integrity": "sha1-0TM67XNPhgjO0ShiGL0sRfYO/0g=" + }, + "tough-cookie": { + "version": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", + "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", + "requires": { + "punycode": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" + } + }, + "tunnel-agent": { + "version": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + } + }, + "tweetnacl": { + "version": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "optional": true + }, + "type-detect": { + "version": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", + "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=", + "dev": true + }, + "type-is": { + "version": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", + "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", + "requires": { + "media-typer": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "mime-types": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "uid-number": { + "version": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", + "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=" + }, + "underscore": { + "version": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + }, + "underscore.string": { + "version": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz", + "integrity": "sha1-18D6KvXVoaZ/QlPa7pgTLnM/Dxk=", + "dev": true + }, + "unpipe": { + "version": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "util-deprecate": { + "version": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz", + "integrity": "sha1-Zyj8BFnEUNeWqZwxg3VpvfZy1yg=" + }, + "v8-profiler": { + "version": "https://registry.npmjs.org/v8-profiler/-/v8-profiler-5.7.0.tgz", + "integrity": "sha1-6DgcvrtbX9DKjSsJ9qAYGhWNs00=", + "requires": { + "nan": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", + "node-pre-gyp": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz" + } + }, + "validator": { + "version": "https://registry.npmjs.org/validator/-/validator-3.43.0.tgz", + "integrity": "sha1-lkZLmS1BloM9l6GUv0Cxn/VLrgU=" + }, + "vary": { + "version": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "verror": { + "version": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "extsprintf": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz" + } + }, + "which": { + "version": "https://registry.npmjs.org/which/-/which-1.0.9.tgz", + "integrity": "sha1-RgwdoPgQED0DIam2M6+eV15kSG8=", + "dev": true + }, + "wide-align": { + "version": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", + "integrity": "sha1-Vx4PGwYEY268DfwhsDObvjE0FxA=", + "requires": { + "string-width": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz" + } + }, + "wrappy": { + "version": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "wrench": { + "version": "https://registry.npmjs.org/wrench/-/wrench-1.5.9.tgz", + "integrity": "sha1-QRaRxjqbJTGxcAJnJ5veyiOyFCo=" + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + } + } +} diff --git a/package.json b/package.json index 71711be..efc7aa0 100644 --- a/package.json +++ b/package.json @@ -1,48 +1,67 @@ { - "name": "node-clsi", - "description": "A Node.js implementation of the CLSI LaTeX web-API", - "version": "0.1.4", + "name": "node-clsi", + "description": "A Node.js implementation of the CLSI LaTeX web-API", + "version": "0.1.4", "repository": { - "type": "git", + "type": "git", "url": "https://github.com/sharelatex/clsi-sharelatex.git" - }, - "author": "James Allen ", + }, + "scripts": { + "test:acceptance:wait_for_app": "echo 'Waiting for app to be accessible' && while (! curl -s -o /dev/null localhost:3013/status) do sleep 1; done", + "test:acceptance:run": "mocha --recursive --reporter spec --timeout 15000", + "test:acceptance:dir": "npm -q run test:acceptance:wait_for_app && npm -q run test:acceptance:run -- $@", + "test:acceptance": "npm run compile:app && npm run compile:acceptance_tests && npm run test:acceptance:_run -- $@", + "compile:test:acceptance": "coffee -o test/acceptance/js -c test/acceptance/coffee", + "compile:test:smoke": "coffee -o test/smoke/js -c test/smoke/coffee", + "compile:app": "coffee -o app/js -c app/coffee && coffee -c app.coffee", + "compile": "npm run compile:app && npm run compile:test:acceptance && npm run compile:test:smoke", + "start": "npm run compile:app && node app.js", + "test:acceptance:_run": "mocha --recursive --reporter spec --timeout 15000 $@ test/acceptance/js", + "test:unit:_run": "mocha --recursive --reporter spec $@ test/unit/js", + "test:unit": "npm run compile:app && npm run compile:unit_tests && npm run test:unit:_run -- $@", + "compile:unit_tests": "[ -e test/unit ] && coffee -o test/unit/js -c test/unit/coffee || echo 'No unit tests to compile'", + "compile:acceptance_tests": "[ -e test/acceptance ] && coffee -o test/acceptance/js -c test/acceptance/coffee || echo 'No acceptance tests to compile'", + "compile:all": "npm run compile:app && npm run compile:unit_tests && npm run compile:acceptance_tests && compile:test:smoke", + "nodemon": "nodemon --config nodemon.json" + }, + "author": "James Allen ", "dependencies": { - "async": "0.2.9", - "body-parser": "^1.2.0", - "express": "^4.2.0", - "fs-extra": "^0.16.3", - "grunt-mkdir": "^1.0.0", - "heapdump": "^0.3.5", - "lockfile": "^1.0.3", - "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.4", - "lynx": "0.0.11", - "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.5.0", - "mkdirp": "0.3.5", - "mysql": "2.6.2", - "request": "^2.21.0", - "sequelize": "^2.1.3", - "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", - "smoke-test-sharelatex": "git+https://github.com/sharelatex/smoke-test-sharelatex.git#v0.2.0", - "sqlite3": "~3.1.8", - "underscore": "^1.8.2", - "v8-profiler": "^5.2.4", + "async": "0.2.9", + "body-parser": "^1.2.0", + "dockerode": "^2.5.3", + "express": "^4.2.0", + "fs-extra": "^0.16.3", + "grunt-mkdir": "^1.0.0", + "heapdump": "^0.3.5", + "lockfile": "^1.0.3", + "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.4", + "lynx": "0.0.11", + "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.5.0", + "mkdirp": "0.3.5", + "mysql": "2.6.2", + "request": "^2.21.0", + "sequelize": "^2.1.3", + "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", + "smoke-test-sharelatex": "git+https://github.com/sharelatex/smoke-test-sharelatex.git#v0.2.0", + "sqlite3": "~3.1.8", + "underscore": "^1.8.2", + "v8-profiler": "^5.2.4", "wrench": "~1.5.4" - }, + }, "devDependencies": { - "mocha": "1.10.0", - "coffee-script": "1.6.0", - "chai": "~1.8.1", - "sinon": "~1.7.3", - "grunt": "~0.4.2", - "grunt-contrib-coffee": "~0.7.0", - "grunt-contrib-clean": "~0.5.0", - "grunt-shell": "~0.6.1", - "grunt-mocha-test": "~0.8.1", - "sandboxed-module": "~0.3.0", - "timekeeper": "0.0.4", - "grunt-execute": "^0.1.5", - "bunyan": "^0.22.1", + "mocha": "1.10.0", + "coffee-script": "1.6.0", + "chai": "~1.8.1", + "sinon": "~1.7.3", + "grunt": "~0.4.2", + "grunt-contrib-coffee": "~0.7.0", + "grunt-contrib-clean": "~0.5.0", + "grunt-shell": "~0.6.1", + "grunt-mocha-test": "~0.8.1", + "sandboxed-module": "~0.3.0", + "timekeeper": "0.0.4", + "grunt-execute": "^0.1.5", + "bunyan": "^0.22.1", "grunt-bunyan": "^0.5.0" } } diff --git a/test/acceptance/coffee/helpers/Client.coffee b/test/acceptance/coffee/helpers/Client.coffee index c67b425..7663496 100644 --- a/test/acceptance/coffee/helpers/Client.coffee +++ b/test/acceptance/coffee/helpers/Client.coffee @@ -11,6 +11,7 @@ module.exports = Client = Math.random().toString(16).slice(2) compile: (project_id, data, callback = (error, res, body) ->) -> + console.log("#{@host}/project/#{project_id}/compile") request.post { url: "#{@host}/project/#{project_id}/compile" json: From 017ba3a4ece03788dd9950daa2f613bf73a0a623 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 13 Feb 2018 12:05:10 +0000 Subject: [PATCH 002/118] mvp needs hacked pacth in docker runner wip most tests pass --- .gitignore | 1 + Dockerfile | 16 +- Jenkinsfile | 36 +- Makefile | 5 + app/coffee/CommandRunner.coffee | 2 + app/coffee/CompileController.coffee | 3 + app/coffee/CompileManager.coffee | 12 +- app/coffee/LatexRunner.coffee | 1 + app/coffee/OutputFileFinder.coffee | 2 - app/coffee/ResourceWriter.coffee | 1 + bin/synctex | Bin 0 -> 90472 bytes config/settings.defaults.coffee | 3 + docker-compose.ci.yml | 10 +- docker-compose.yml | 8 +- docker-runner | 2 +- package-lock.json | 4702 ++++++++++------- package.json | 112 +- .../coffee/ExampleDocumentTests.coffee | 3 +- test/acceptance/coffee/helpers/Client.coffee | 1 + 19 files changed, 3008 insertions(+), 1912 deletions(-) create mode 100755 bin/synctex diff --git a/.gitignore b/.gitignore index 99e9760..476f2d1 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ cache db.sqlite config/* bin/synctex +npm-debug.log diff --git a/Dockerfile b/Dockerfile index d98547c..83e452d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,17 +2,21 @@ FROM node:6.9.5 RUN wget -qO- https://get.docker.com/ | sh -# ---- Copy Files/Build ---- -WORKDIR /app +run apt-get install poppler-utils vim ghostscript --yes + +# run git build-essential --yes +# RUN git clone https://github.com/netblue30/firejail.git +# RUN cd firejail && ./configure && make && make install-strip +# run mkdir /data + COPY ./ /app -# Build react/vue/angular bundle static files -# RUN npm run build + +WORKDIR /app + RUN npm install RUN npm run compile -EXPOSE 3013 - ENV SHARELATEX_CONFIG /app/config/settings.production.coffee ENV NODE_ENV production diff --git a/Jenkinsfile b/Jenkinsfile index ab90aaa..bc9ba01 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -9,34 +9,9 @@ pipeline { } stages { - stage('Install') { - agent { - docker { - image 'node:6.9.5' - args "-v /var/lib/jenkins/.npm:/tmp/.npm -e HOME=/tmp" - reuseNode true - } - } + stage('Build') { steps { - // we need to disable logallrefupdates, else git clones - // during the npm install will require git to lookup the - // user id which does not exist in the container's - // /etc/passwd file, causing the clone to fail. - sh 'git config --global core.logallrefupdates false' - sh 'rm -rf node_modules' - sh 'npm install && npm rebuild' - } - } - - stage('Compile') { - agent { - docker { - image 'node:6.9.5' - reuseNode true - } - } - steps { - sh 'npm run compile:all' + sh 'make build' } } @@ -54,12 +29,7 @@ pipeline { stage('Package and publish build') { steps { - sh 'echo ${BUILD_NUMBER} > build_number.txt' - sh 'touch build.tar.gz' // Avoid tar warning about files changing during read - sh 'tar -czf build.tar.gz --exclude=build.tar.gz --exclude-vcs .' - withAWS(credentials:'S3_CI_BUILDS_AWS_KEYS', region:"${S3_REGION_BUILD_ARTEFACTS}") { - s3Upload(file:'build.tar.gz', bucket:"${S3_BUCKET_BUILD_ARTEFACTS}", path:"${JOB_NAME}/${BUILD_NUMBER}.tar.gz") - } + sh 'make publish' } } diff --git a/Makefile b/Makefile index 6407885..6e7fca5 100644 --- a/Makefile +++ b/Makefile @@ -25,5 +25,10 @@ test_acceptance: test_clean # clear the database before each acceptance test run test_clean: $(DOCKER_COMPOSE) down +build: + docker build --pull --tag quay.io/sharelatex/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) . + +publish: + docker push quay.io/sharelatex/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) .PHONY: clean test test_unit test_acceptance test_clean build publish diff --git a/app/coffee/CommandRunner.coffee b/app/coffee/CommandRunner.coffee index f47af00..969b55f 100644 --- a/app/coffee/CommandRunner.coffee +++ b/app/coffee/CommandRunner.coffee @@ -5,7 +5,9 @@ logger.info "using standard command runner" module.exports = CommandRunner = run: (project_id, command, directory, image, timeout, environment, callback = (error) ->) -> + console.log("Command runner", directory) command = (arg.replace('$COMPILE_DIR', directory) for arg in command) + console.log("Command runner 2", command) logger.log project_id: project_id, command: command, directory: directory, "running command" logger.warn "timeouts and sandboxing are not enabled with CommandRunner" diff --git a/app/coffee/CompileController.coffee b/app/coffee/CompileController.coffee index 99973fd..52c0e14 100644 --- a/app/coffee/CompileController.coffee +++ b/app/coffee/CompileController.coffee @@ -34,11 +34,14 @@ module.exports = CompileController = status = "error" code = 500 logger.error err: error, project_id: request.project_id, "error running compile" + else status = "failure" for file in outputFiles if file.path?.match(/output\.pdf$/) status = "success" + if status == "failure" + logger.err project_id: request.project_id, outputFiles:outputFiles, "project failed to compile successfully, no output.pdf generated" timer.done() res.status(code or 200).send { diff --git a/app/coffee/CompileManager.coffee b/app/coffee/CompileManager.coffee index 167a80e..d3d319a 100644 --- a/app/coffee/CompileManager.coffee +++ b/app/coffee/CompileManager.coffee @@ -41,6 +41,7 @@ module.exports = CompileManager = doCompile: (request, callback = (error, outputFiles) ->) -> compileDir = getCompileDir(request.project_id, request.user_id) + console.log("doCompile",compileDir ) timer = new Metrics.Timer("write-to-disk") logger.log project_id: request.project_id, user_id: request.user_id, "syncing resources to disk" @@ -206,9 +207,14 @@ module.exports = CompileManager = file_path = base_dir + "/" + file_name compileDir = getCompileDir(project_id, user_id) synctex_path = Path.join(compileDir, "output.pdf") - CompileManager._runSynctex ["code", synctex_path, file_path, line, column], (error, stdout) -> + command = ["code", synctex_path, file_path, line, column] + CompileManager._runSynctex command, (error, stdout) -> return callback(error) if error? - logger.log project_id: project_id, user_id:user_id, file_name: file_name, line: line, column: column, stdout: stdout, "synctex code output" + if stdout.toLowerCase().indexOf("warning") == -1 + logType = "log" + else + logType = "err" + logger[logType] project_id: project_id, user_id:user_id, file_name: file_name, line: line, column: column, command:command, stdout: stdout, "synctex code output" callback null, CompileManager._parseSynctexFromCodeOutput(stdout) syncFromPdf: (project_id, user_id, page, h, v, callback = (error, filePositions) ->) -> @@ -216,6 +222,7 @@ module.exports = CompileManager = base_dir = Settings.path.synctexBaseDir(compileName) compileDir = getCompileDir(project_id, user_id) synctex_path = Path.join(compileDir, "output.pdf") + logger.log({base_dir, project_id, synctex_path}, "base diiir") CompileManager._runSynctex ["pdf", synctex_path, page, h, v], (error, stdout) -> return callback(error) if error? logger.log project_id: project_id, user_id:user_id, page: page, h: h, v:v, stdout: stdout, "synctex pdf output" @@ -243,6 +250,7 @@ module.exports = CompileManager = return callback(error) if error? if Settings.clsi?.synctexCommandWrapper? [bin_path, args] = Settings.clsi?.synctexCommandWrapper bin_path, args + logger.log({bin_path, args}, "synctex being run") child_process.execFile bin_path, args, timeout: 10 * seconds, (error, stdout, stderr) -> if error? logger.err err:error, args:args, "error running synctex" diff --git a/app/coffee/LatexRunner.coffee b/app/coffee/LatexRunner.coffee index 6a5a4f6..11e71e5 100644 --- a/app/coffee/LatexRunner.coffee +++ b/app/coffee/LatexRunner.coffee @@ -8,6 +8,7 @@ ProcessTable = {} # table of currently running jobs (pids or docker container n module.exports = LatexRunner = runLatex: (project_id, options, callback = (error) ->) -> + console.log("LatexRunner", options.directory) {directory, mainFile, compiler, timeout, image, environment} = options compiler ||= "pdflatex" timeout ||= 60000 # milliseconds diff --git a/app/coffee/OutputFileFinder.coffee b/app/coffee/OutputFileFinder.coffee index 4b07f6e..662440b 100644 --- a/app/coffee/OutputFileFinder.coffee +++ b/app/coffee/OutputFileFinder.coffee @@ -10,8 +10,6 @@ module.exports = OutputFileFinder = for resource in resources incomingResources[resource.path] = true - logger.log directory: directory, "getting output files" - OutputFileFinder._getAllFiles directory, (error, allFiles = []) -> if error? logger.err err:error, "error finding all output files" diff --git a/app/coffee/ResourceWriter.coffee b/app/coffee/ResourceWriter.coffee index 0b6aef5..66cfbfa 100644 --- a/app/coffee/ResourceWriter.coffee +++ b/app/coffee/ResourceWriter.coffee @@ -109,6 +109,7 @@ module.exports = ResourceWriter = callback() _writeResourceToDisk: (project_id, resource, basePath, callback = (error) ->) -> + console.log("_writeResourceToDisk", basePath, resource.path) ResourceWriter.checkPath basePath, resource.path, (error, path) -> return callback(error) if error? mkdirp Path.dirname(path), (error) -> diff --git a/bin/synctex b/bin/synctex new file mode 100755 index 0000000000000000000000000000000000000000..99044b97a0259b30afbf0bfb34365e4f1270479e GIT binary patch literal 90472 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:nwe6m+2D>* zoY{Fj%W}c{FpJ0qQhvldq&OhRhLU!YWRj+C* zr>Q}YSRBOXUeBSlS#^V$>-Rn;Wp|Nrleu=q@RS!Ry94He;sEfJqhz56A+J6U-P;suFi&-TzgYe$<- zGIL(WvacRsBYsH2Q@cHlD--s$fErFgrK;U0!`7zFux~*il*vt|OV&9bx}1_I#qep# zG<4r8d;v-`x2<+^tzC98SyGVPMIkC6-A1Bo=r~xzQ)AV0i#Rk&Q(F`de1i;K5SrG8 z=o1a0Mn$dQU{S|FB#Fh&LF@!6isac0rP(wrJn|Rv+k(PV5g%sM(c>8)iA0Y?-Ga}M zpz-j|ClQ6AlX%vFp&^(+qs;8$CDH8SXyboTx}IF>SmOSR4QKnTO;@Oqa2z~|t|uG8 zY3#e2973#S%OW9pnCy*x(}rAR3v0P#DUcw|4E~lI(Ol@|>b3_^-LYE)zJ&Z6fYQs7QI37T3~o7>1ZXy!>x+fkYrwKH92@S z+jwyRzf>^_Bp9t#j9%rZ-bH0hdjZpe%h}0XxHr%%Qxjb7Ra}~rxzJN9Y!?~eV)~rS z5oTCt%uoed;oeIgr{}=Ckk|SCk zft(z1MUT6k7LI#k=b-4OR%EwuZ5dP>`4;v!bXbC|{?1?WgYa#5*q!5rE; z@m)}#U8KFx0*ll;wtyyg;;=_?*n#Kglr7sD^y^&4HaIfa@55-|%f`jMggA;^HG@)fpw8-_nWlYW9hgm#a%f88fEQ$F4u8dXW0m(&9Exomjxu(1wv$8WIZA+^>EeA= z@u4Y;4@fHBUlqTaix**@?+#^heCHhD+N-P4Z5z~g{sMB->p<*lsC;d}1C%tYagxp9 zbZOO&x-;rd;xP)HKa8it6bUV^_QrNe2W2elC+qkffIA=3gTgv+?c6tlYwFB%Q2gvVQf_autT zI6_H-jtopM^ZdEhQ?R+_$d);Vbe18dofb96^2#pO7h{S0>4=u~hJGaINlVlf+)VBM z^NnIM>nIq(o=f2jPQ?LE;)qbDhe0jvhT%@ET*0ujI(7$)nd{5!yop)sIpIY-UnX6Y zU_H~uI?C%~%{n_ntKoXZdT{Iv&AK;WhMa1P3ASQbFHrD_Xs^i@Wk_x z6oGjf&y=2yBB+BgHFWp(G@X>lQlUCzrErk8hWooAn`ijAHWS~gLUJOX!bPgX$*_3_ z2LwIxak>o~uWn^nHz~^Gr71%u)w6y%5$#sb9G5b5G&)PReFpGw!M+6ygCbx18$0qS z)(F+@2w`7=<6C-k8uPo-b5Mm4$0_HNXb^@GX}h5nN1&nd5WUb@6;w*E}{H>w(kUtvs?-=jjT~E(NlW0!<&4oVTc^#1gIfMG4XMRH~gc|OThO%O`e&-6J zo}0er)`;fxe0G4(-<8woAt2ZFDHV`mYd(#-o-O(G&Bv01wrfIJos*>fOUI|KAzjUbQVBi4MXOWewdK%Z_Xb7vtna9RR3-|2h@rP0Vg5SQyN-9r z)D9|Qb>1jc`Rx(^^JqGS@U4_a{7+Buc5vb-g-Wh(2kuS z9C(GW5l=>R#GKNo%{`jo}TvA+8+5O~Gm^F6Q+E_ETsH!=Fs^7 zdB{1gRE6?rj$TSZmB9Cx!}hn0TJl$hW58a4Rf1+gbIh^nIlhM{s)XZ0!!c|)E+>w;HjWMrkf1PKdT>lw zTxVDW42#o=#cPn8rV{9!cAz(~fa0|K{Ep)OCmQOX$nf0!1ELH2H*kEt4nWIF!}6~U zx}EQysC!6FAbR3igV(v*+kRPH@7N8(l2uCjnB{5u4o@8^v@GiYb&JY$YnG>}fJ$(O zlhY;)dspl|YXfk4Ti_9{Ha#ZJMbVRogO7#3RKj5aZ(RJ`|^QuMai%4UNf0=%rX56OqEd>QZPRl@PH zi#&AVgGrs3?`e84eJ4&4G$|#l7IU=0#4Y;&xMO(rG)yew%g~Yv)eF(-?LDV*>O@e` z%>LOrv)y!7)$&AV{%B{<&O8rz>CDL|pfk_Uadze+Pg9Xh`(%7hhe`$s6Y1ChAE4#+ zG#)JE%HU`Wsrf8qqJ($3uN_gs4}Q<0r3(db)Fkeq;CxNu9tutt61Py0p67(UFLen8 z`zSPd6^b#EqJ(#3v?PLpum47C{A1H2zg?;|{>m^maNJG$oI=6YaE=lTj)`w?C;26W zoBQ-js^`cr>7M&EehFnWW0?aT(de7?y6LYU@3gZ8o~E&MLqeQfN;_+L)L~~``?j+e zn8aac{@?D?&W36dcRP#AK=Rz=Zf84%#Ldp0RA?7F`;J1p*x53LHsQpUrXX!f>S`UIe zC;A$NHiO_5Jo+WE%GpLu_$-Z@-b%gy8wB>lD?OH%acpyZ&VvAtW9QS{VV1CWp^n&l z2f>nOm@azZYTZR!zU;K*`JSdx`>^C?4>>G3Dp6!lXOIX!p6(fZjKXuGH0kT@p(HCl zM4`2rCRy?Ozmn40Op~nmRfRTYI{Z~_rZq+f&n(h9*z~2(*-U%t;s6|DC$>G%fB$xI z#lyW}LJdZJH`ZzOEgRxAq2u>$LXST9IhfFUJJOoaiwf;xLO)b!7ZX~g(8h$KZQ6wH zF*>**sC97d(f>{+v>v;m|Bfa!AK_-pqegva*J|}mILc{4i}!6pz6U-B6M7gAN~ZBb zVTE=vp@j8Esg3ak~Z1uqMe4eU@PB%DsjiyB{WR zAFRcbQer$gz4Z3MsS9p_#E9jLe|~14 zKJ|G`;_g!)(j>MGg`~yQ4MO7PQ|BwRi%$(Gw2M#8S7_r?uYXz(i%F&fjw#n2FevBW zXIT8(EztVnUUkbHqqW$4t+n4_N7!zn*!Zp8hb!N<)?uQ>0nmkBQ>N zHI7mE@V+Msd0yQy3b)}IL=LEI<0omi?LHwf<0mPYYE)=Fev&4LQiayzCn@MYOQFs9 z`A&--KUH;+Iav88wxrKR2U z?&Zb0cQ4F#4gzd9U%8J#kaL@35U2yt>82w36dV>)<@>#sAO3oriQjuNmS|?9#?8JPXs(zqejh zbzH7B{~ETi*(k!Y&c#mkzO1j;s?WjtCT&VZ^&Mm*{7iW@c?de+5O?Mbv_NII>h?cPo--QZ@JS{(4Z^myf(HX zmSC5v`~#}|L45eF^PJelR3P=X_hCOV+uqCQyS2RK-Sia{XnE9B-bYuC$6X5XhBSFU z8a=ay7!*%#oA=`ELgUHq5--A0FL-GuSAcVjGGp_Lt>itP0s;I9IissLwHkI{Vslo#ID%Yih_{M zkLe+^!|)nzcufN(%4O7Lc0QtN<};ty2Coo-BGEgrMAqDoXo(soZ(mGLRPBoOQ!JJh z=n_YvN-2@C%wFO#Rf67wp&rMHJ!9wT6nG(6wV`RNr-uA1 z(EN3HqAQ==Q$zh)Tz*+DSK#t6x>Q}2q6=F|8K`yzJ^s0ap3#g%qu8*5TDK#6J*i~f z5>g4QulIwiy}(4GH&IRo<=m}uuHziM&xW*!yB=Zu2ce&|V0!4t7VklN_?2A38xioZjYQAKwLA?nLZT?>HWKYt)QF}U zTj3v{Zm3^P4|hfU-2YI0E6jDHFy!ZX-h|Ddm+wZ_MjF$%367i_GN=*Z!4KR91+Zzb zoPDTyBqz?2_YM&b@6@O^5`XgoG;fM{4ZQFJ{XMuu7-)a~^;cDdQ zA;z!JAw%Ta0^AQ6JrpE)nlhoiXbH%}7M`664vo&B>l_hpTgyfx#^29}7+WyVQUC5+ z9S!hH{nCIo!Z{kSnM$r87cp%|rWnU_B^HQa9DJ0XnskS-)J=L{`p(v*4U1fxR7b@6 zuzKBBqiaB_n-ikz6wMhyLVW;d75fu=EX>@V#y&C;b&v{2QGI5ZvFb*m9(3uV=on&} zoxtMN*hWvU7NTiRmA^*4q2=>5t%JQr|AKiy%lH;u8yR!5wRNumtJrxY<2fXwFx47b zZfA-{ud^{+tr>zCA7C^LzeWr{*$ajr@Q!e zLmmuCUgL?!%rR8pEuLCz^DPVDTQn4Z%cTWvXTNMb`uL@7Yz$gNj zIvS`Or-Zsm)un$ob4wjn2fRGGvxfSkp?(9pMm>zWfk&dJ@mj(pWJQIM=w5Uw+Rz3L zs5}xapmveRp}XIBUm1ZJWFX;>wxT2SK8x!Aj#M65YzIpdzwY zPveJZHWlC(2DWd5n>_=~A~(>kM%e|#fQDqhjnH&F_97{YZmN?P$d|WLO1P(9VaJbS z>C$_sI+XS_sSttNb2;&BE|k8?XrmS0NkIv?B6t@YkfAN&mO`(;|7akzJ@8SRjlL{m zoup;Z6@^tr!Gku!!s4AL_Uxo~)k&=%1K)2W)zg`Um=E2)An^cr4tl*C+Ud3s_3!1z z(%U_$8-quo(R>X>{<;QY5*Psuse~{HXFxW}04Nsradbw{Dhh$hqqoS0bu1$|e*2g% zyg&jYM`4pEg<+r+2uWec9f=lcp~5~d!TtoCNAM{Lcof0I67XVz2WuGDeR1vnN{*lh z^+r8E^G#^z$Y^Y3`-#R@(vCFe7fxt+He*HM1cD+yUsO#+5#MCXD>M<`)H5ZrkA~&N zQ=!5DjmmKEnrc|`nKP+u*oUMj8ai_ZL2$1%&0dU+RX%obgBJ=UubCTMCQzZ^<_I)H zpi2dsBhYk#76^2%K-B^T1fo|Ep}sQ(S}xE8fmR4~sz9p*8YR$LhFI3KZedwF*ggjP z1futAg6}wix&#^{P`5w@0$IX0PoQjph6_00sGA`px(LdhuvrLXr+e@(1LJK)C`vB#>92 z`vl4p=pKRO`1jy%3p7@6w+Q4DC@hd1To~LWP@&+K2{eNtBf1F6qTdi^t& zA}EXgoDj4M`O^Yz7pO&`Hi3R2P`f}s5vW6;9|#l`==%b73G_XIx&^vRAdCIX;57ot zjrPGe3zQ?ckU+W0BPr7tL0R-1=@ze$_Y){jpk4xv66h0QC69&;?iR==xc3DL2((+E zLV?~EXof%?0?iTV*8(jNXoo=60=+2EVup;?MNk&~H6d6os1^SUd8wC14fi?>CfIzJReOI9E0^K1{n?Sb-)XtC*T?A#(kGhd9szb;J z2^1A*pg>&$Wee0TP^Lh#88vush`D6bDla_vLxFMx>JlhdpmzoG3bae0Jb`{E&?tf0 z1scnc5nTjj(SI)@_=NlofdT@(Do~+7F9#MphNHD20QSy+M@0pUvp=9S%s%j30&MM~^4Ql~kE~YjSFIg7-${y2xIJQXi z?+bKMD##_nzR69k%joUhh-ge06ZZKMxhBqWyxwgD6+vlw-P`+~6Y)Ri;M;KI=zY)S zo8R|b+K#u%wM&pQy^~&N6h&#@^bb6jZtq!e@TF34%ln>7sBi}gcc{WCiu!DI4nDzI zG>W3=8VauHZFXGi4Ak0M<|*i%1=>Z|N&NvZc?N=~66ZN=j-VY{oG$R}5}q8eg?&@g z6u!w>xEqDLrSQy7pG~Bkg9D)a0O|rotBpM=Y9{_1oYw=-(W8oAbwtlmYIoQdz=Dr@ zRedBR89mhnO*d{(dIsLT{()zNe@}X{nfoB4oj-7!-q|PI`<_x-x`Atse~m4b(b7R&=+mOt@&b^9>o;0EsmVj}8%K25hoJ+Jd)k zxSRq}FVcZ?IRd*b{4H4Ow{M$Duck_q-;og@_1WH$fxgsL+24_&cfsJc;%E#O*P)`_ z^wN-_w9M_-vYs??dkIzWl-JzgBwF0f zEe>1b{k!VE1hVdA6m}@Gk=NmQl{Gn)kx>W||ud48J)>7jNdmCyYzJfc&xfsR9 zOh-zY@7fQUMX(&mgI?nuG0R80ox$9Mh%=yQ-NMc(pREO+CiJ-Lw<*-NPGBB-N0f|R zjY#zRE{641&3Ibj(7uGHkr#0d74+o@-iX1Mfn*cK(>R#dL7WRF+_Y`ju!M7=kMsny z@hzPgZTg^{ZEEwVT|9`tqq85r34qT~H26EfnO67MD-Kf6cF5(SlFNES&e$!%7dsE9 zJbG_C@8V!**%N4}029g`?{B*rBSfwi9Sdn;NoHq>-ubEJ!fQA8bQS|C?WmjDiFZz< zXkZ=9rQPa|X`A_!5x4Vq*!XeqJA~C$_cP)x4nKK5z}FebB7F;7T=@|S?-Fm6y7dx*zqZSQpePugb{^jub5l*o$rskkZwQba*4lfPMJ2^8}(-W|$Eu4eY$A z$0%g=k#|%@MkR{ay`%V%AWz`B=1jar@iwPpv8mvTZ4C7OpVOGU|D?u@`cG;M-lzM| zG_&k*@0FKg4MtI?%MhmJ$Ol*g845)8co8<W=L7GlFnUJc57z$}Bq{U0R=JXHuo(k@8~Dy=QPv_ls8#A2`F#OFyH-rf<*~HDm^R z$1bPvw)a;j6+0?Fs0$WLWL=@f}h-ET!2oBW~_`CFXh*@s0wPDgz{N?4k* z$O{t|xtuJrH!QMliHq^uWxP{6jmBtDeg5jXitTI@jzH`~J$HfRv#JMk z&ORELPCYaBS^A4o&v$WtXCLah4kVvdJ($HlgL>xdv-B6Gp5-`l(?dP9qeEvCx?%5T ztqo&~$J7_8aI%lq$yg{+2yEh81M{7_9_!@Ji9GZ|v=pPmfuDkfES5H^x_Ovq*5xKd znZe69e%~mq&`IVLzSngH?OuZRhRDe$kNa#yFOE`J3vsn-;ibO1N_!X7W>4ey#fLyu z*doG0Iu>?uDzatqwH}kx*rZ*Fv5wV5Aukp_`&Pge&>XWe>MMj%Y**(BLv#p>AIL!t zuWO&@@0IM-Mx)M(MEpD0gw&@i>2r#8UTCtF_LSPpp3-p_qB82M*h_tQ)xRCu!8#`r z&A$NK3v}Wy;>%%9bRFiK0H|4g@zeI@Pol+++2i1~j9#BV-v7i>I-$2cTv#3UKatTU zowW?-=3*D1u5i&#%BHg++Y_&!ZRaY(KD>9fJ$YBH3mwvhe#{X`yGY7s=hA`SzMdO; zNl?_4Fcjc14yZ48dYaynuI_=RogN~2!qc=xNT`w7N(%Z|uBYh+Lf~tNw6B4^g+Wgz z)IqA^PWV64R)`y8Q%-^pzZ@5=Q2_50!-7$V$ zO-V>`wMo9@G|i;;jf2>&FvR!F4Oox&fwi9~(H)DwOF zI{HL1#Bj^2xD4o71qXwiNQn~iw(DTU~7tL)|j zw@^oLibbJek?2twVRUA^@g6=yf~PbPH&I{%&GXs!X9m#SAW!3IgipRBXX-#PY7f=X zkvz2MP3e3jyjDJy>f~R}{DZh-fRc$jBHq5JPL5aOd_2YQ?Yate6VSjEKFR4%ZKx*Ny(xSa+qBpy)druQ_`AzxC2`uOw)2EHjC zaihpay|ht6v0Tt^NaSviT^DVM`HQ+w2fayent=V_#1 zuZ7~;=*t1>R&p?8yg8hbp;NPN1p!WRhyC}Twq+?jP_>rF05>Q>DNX~e$?P0zqd`V8 z4fK#{D4JDedc-809@UboWpX&ZSP^t9_Wcc98QG}6z#Q6D4{UUjwA43zTZ&PgbbEt( zsE(*%k^T)Wn;@zx9<2L6pEuvPv-}t z#uk|R7EUS#z;@CAbHRPoj++3fv3Y6v0?XgnNyQhqkJ`gUdo$Xw(1x{*_wTLuAVn4} z4r9M2>!s}OrIOvv*;~oHVH;NL7r1hfN)+OP{NhADA+^XkMr8F+jf*;<2f91IB+o@~ zX%)V#IUL_s^>=p;N2u{pJ9<7HJyhGB;ol3b(nVWn%jS2F$!L2S4EFR?IvzIbEbXau z+?($_m$UcS`+}&yhHTYiMCC4e4hX3O)f(UIT%;H+(`7wLTC$CnGt6ii%cI4m(J17u z5p3n983n+)S}Sk~Yjvr5Mq2K`x^f5ZqxO9Cl7E*P2D|hyn94SVY#s*4mSI2-4Lu4$cm6WRz&AXtOZxY^M+w~ zyLuR2;<0sna2(oehuL6CC6C)KQ4zkcW}1*1o7-vo;7xK#j7xdi%J6OL$rm;2TJ2a? zjSKFh_S3f3_a%vAvt^j17EarFYV5yE^ra+vTkL+Z*;K+M%rZ6BU34}})_SUv34?UO zvesR2AGK%TjP0MF!HsA2-SyqnjuhRO5I;3m#7~Wb-Mbo1Li|)^^>}i@De+%{_-(k` zg$b9A>f(4-fq(a+K4eUtgDGP-GNPQ(#t%zlHDI1zR|oyN5ZH9%U?2jUZYwyRPBw<8 zb%O;Eyzu!5C$14d9@`Vs|8I;@FQjlBr)t>kL(qgVP}h#{i{TUDSV%2H74(4NR9dCU z2(`@yry^Hkr)oVLr0Tm|@_-~>D&)QfxcDfSlH>ttKK1W$;m*#t%LSP*lT_@1b+ty| zsOm1Nnv5`2`Gm2!U|qSRBst$M{xoh^{Y5Zx%sll&0He8o}Jl7+tC@qiwi<50$8g*JAFwmz}D(BfM)<^BO2RuT%{Cs!zptUWyD=`s-Yp zWjYdEhU3s}vP={ffnd+F{)zxBa}%@)Nj`uRiv7vm6Tv*tyL9N0BeGPg6!U^oSiC({ z^&X1Ix?MVk=@EDB$#5kRv*9Lm*PqUs>RKvo?dsXqr65SzF^8jFqKBoYC?p2mKO0YL5*2ZWmbLQVcmP4hJV06Zf8J<5+`s<k}Wn*<4Kso!gvczc$&RiHuA07C~aDJZYr0) zX$|{&;hh*uX-mEpo8Y0sUbxyWwpEzjFl~2c>=_9L6{)95y$>Elyru1Fl!q|+Zb5b; zZUO#+Y}sVXB@HAn5(e{DSkppbF3e2GnkZ|w;d+$U+i;$nSI9W}EIgWai9XWtkZrwG za^!GZZc@+nf(J$VN_$~2CY9HXB^5{L@irdytzlMmD{s@OA~~#keN$oD?u^(pl>TT| zl#0?eb95;kc;}81TCOPv-INgkETf%J!8}h z??4V>1{w&7)g7CL*6h@!bq6iGj+;eAKSk-fX=zLEqf?gBn*g+SNliVRkMVY@^*-sn zoM@0h?73m`c$pscTs&W)SU5Lo-Z!Zeyu=$_np!|O#NBlnr#RE*7q#i?_ z|2Zg__x~Iehi7IZ$sYI}fP=d9uLtCyPNpgPQ#q)=AV^D;boiUp9xXzXl&Q*5!0Di{ zWiSf5%C5r*qIn80+i|Q0vXKoPqI9zO2RJ=W zlyJ8@-7@Gji9{fZ?jTwA(31sN9Z_5#p7uQM`%*s{fg9;;j9%Y(8vjH=i8@{g;n8d- zGkz0P^2)}np@Y@AUoSdcUnd4MvtG^NB&I2}Ln4fI)^UrXYCo*Vbf&UIWR3a&fY z2PM;p+txC;zwU&=(sr&ZA9WG68AmzjX_#sq*(TopmDza~E?`ez2UV9m06T`+hXXX2 z#lVZYHjM;(2+zunzbviBO?^H9+Km$jpSgmsf6*08J+KOtdKQrq&`{iy8)nywK6w?k zHeKRiOhgHrA-}3^a^zRw{%IYdGp-w^j1)$(9M>(EgH!ZZxLRe@=|z9h(C6Ol1P3$l zf`eg;gzDa;hsk)tBSV$Ges>2jfzVH9u#;Xhhm+<7NT!CGsyS(1j0DE*_2m1Pd-A8P z^5hq-^W@Lk;K?s-_2gHydGhNzJoz+3J_);uN18_AFP*rEG>yU4D5{~^53_0-3oy{! zR7gpv$wx`JcivfEYg6`sgz<3uLQPWz-52OU^BUqA2scv&$~!JcQ!+HwAN^pGYiDu` zlqBdd6B&JtVoyf1kEfA+cfz4{LDa;)8GO;ElPCkqfAx5(7yZJWP7f;c6xewZ9hlDO zr1$~bUHq0)^c@tg3*IROWiO29nC?vlis@3p$s`cO+X4JtPFCbzx(xYwNvs+wy5ObY z^0B$S=pee2)9HdWS@vP-G`kr=H}_HIg9*9CiG&$_U~x@DssZTxt|g z`*W92)!2wwbc*4GmqN=^^Qz5t@?<&P>L0|Cg8XX4Xqe_`6H1K++AAAtra9Ux6C>G9 z6Gzg=EM?L_S(rg2iiZoxI(QUv<2;QkWwwcK@~Y0h1#h6>?U5@LhIBsE(>POs#Ah1O z$O|EqfzV6QYe{NkZ?lFn7SsVdwR)K*9pl3LKM50}CU!t{ z6k%~XM~T_wx6CmT9s>#G1(=5k%>YSy)BE_)nNN+?Z@RpGw?&Hl+&o}5nL?x~fCM$c z1?J#NoZ%o`*_>f6Q5t6m9YPUJTwJ?G@sMcvDWu^L`&DjhU^rg~#&bQjN49J*Egyyz z1h+NTi}yQ)FoOxhEKEc9sM_OD71bDCL*j*AfjZ@~VM}(1^Ez?lwn;?99#3cuU&4Tt zYzJjw0#G;&NAY04uUE1K&?hjU7Oc#brMC{T9 zRt?Qy7GH$EA)uBS-X8UlOr1x!WFM?Y=W&o#TRU}vOnesxuHPMCOMh}Z1}F@^m6J96 zBtWQn13?Xp!@T%jKN=-Z5)RPs1tJnaFB24LT8w&$ePX59LHF(Z3P( z#AA2S!RGK9VuDykbV4%-ul8DxMlQW!cx1~QQ}!a1g}-Cpzl;dfF?n&a-(Y@l=V1|r zy=5mnx#(L3Uri{K!Ir&=yA=H)B+SZ<}Z5YBVat z#fv7$cEK;88I+YpPxSw!SSyNcxA<|C?NSWOy}{Q@G0d(0HfOP*6x+}_6OZ7A51zq< zkV|-zZHQD;9qdm6BgOuBlo~3fw)ZG?xZ-Z>{Aw5#0)wXG$R9L3Nsa*HT3(sdByJ%R z-Fzf;lxzunQ&p*z;}jvMKum^GSQ7MAf3750f>@#zbrahjvR%_nb@77Elp$F*=Lb@KL>x8sQNZ0isy-Y~gf^?OTP8QNt zJxE6h=?al`dZ2kt8x&5ms;!9k@t_PWo?cUhtVs0aBY;*9=+Gg0nS96lmoLTB7-}Z= zFBfWmw4rp@Kfrn;ZFm~i8x`wsCs-pZ5`Fz}X3a*MmNlrsdfKi`u!h=ftT!mu6B4YE z6^ULata%1V%No>1>h%uR>lN!i;P$9e2(qvrB&>N-Ny{44Mr!n+P3m=u^$iKu$cjX_ z4Q7R`BdbZv8q`MWwGP&66>EChM@fyWNc2i!y>_3hL2aalG1;VErC8G&Y87i_MWQ*v znrG9rQiIw^y~4qIg<@ToV2!Lu^rr}gVSGG6r)3RlBQ?gLkL?kQx8SMTj_)K^Y}4CX z+KmGrL$WJN4@hOLq$06lVzZH!jLZgBh!vc$!L^;udnPv$%@8(h)nc#2W`kh^i;F}D zNC8~i**s`+BhkkX&O zt73^!i+1TRUJTesVF@<7;Vt8V>z|?!SuG7i_1Ip=?RVM~UPGJ0I1JkGW=7L1!Gj*h z(h4V`*bCDUMjUzHOHaVeDJaJ4@X882$I#qmS*s>qJTVlXc+r&5hZBpYgkGCCeQNWt z?fG7-D9}8T(hH~NzcuBIf#90$BpE!GKS)#%ZY?XVC@mYb;`+?9WEohV#YbCXy7Aj4FF8wkr zYKdhP)fUe$9p|lGQaLYJy4X9fs-)CAK|!<2$`_WN;hjK9WpM>vE-bI4WM0+6x{As( z5XgJwr^{EDlzDrUuSOLpv;hAXp$yewfzX;%RkyIjTUizKRu|XQmU^q|g4K0FFICsi zI`_QE{=UOX`VK2Nxh(0AM%FUE1X5U5fs()~n178`fZvo#@ESL)eB7|we%7#BT=laq z^iMr&+^}lPd*LPLPMYqYF}rZ$1^#oVJF+GPrc9me$hmOJnNz2nd$uF*tP7_2&m}U& zdjwTfj=#w;*E_7%LTUkR_0uw(#2K|L>$Oylgs$hmmoX>dCx4kS5zJzOJa>Y8CRoG- z<85Sy!vb4o67%^B>q_mlicA&7!38~YuPm*pwCCbl$mdqoRhAUjEU_ceL-VhxDJ?GX zme$l%)p*Nlsw%v}1*P28u)W$~X@wV!%6F0>hq=_|Tl0!5PY8NzOG~fhGO!_SI`gWk zmv}2mE2?Uic=JZ*(+(^B5(LEy7go(H4wgEI7M53(2fYw!-h$E+Z(c=tZEbnwd@n>O zuoZRiKACx^;Y$Q!b&mSyp%&C$T*TJc|0t}S<{N=SC*HRqb;0WR9PMzXU*`QS5{VAimOY^Rdr3(+=bBS)#bqj z-m-;N#lZq=l2t61-m8miy`_t*OEKO{3as)<%RycTu24aRRZ(G82d%l)R`ooqw%RHw zvF6RQDod=&c~)UnE&5_!O?h>&u3{Xko7+=i7Z7dYF0Qr{kMO3J!a9KRU|1e3Uj*Z! zmL)aEDpYMJYoi_|D`R;tRPv(J=T+5|)QZ^7#?L@skTSLGj)G!Y4rN`Eo}P*qSD-7# zk*ljjAHm;NYTbKFD@&;FD@$SiCEnTvrKs3g@V@B88u!ibt7=5-i{BW~oe7&X_c<>6?CABAdCMo3Ia zYLZNMwU}ryUlmwtUKhV%9_%J<;C#780-7Ze-gzLiu-u!bq)bg~LWI zqt{PCRTV~DGi|_)X z7K^l`NS)Ma!gK;mq)x!G)Qu?QEvc%6qdFI>;bLARSZoXy+kr*1v$aT1ix$mo0`e8j zTNcekaPq+gmYyao^*jB>Rp*@X-9JwGB=^*SWi3DZ57#U|+p>I9`hU3M>_e`aHf7VT zE6)DX&Z$#=>x-A&yydTXGd*3RRNu|Mny#0JH*Wp?WR&|lvD0?rC%YKuwRmtLpZr>_ zY$D92Ws`XBtLmzPP|NIv#TXRTkPWzsYBUsM#cTI_pQij2Ok*{b#S1YUFw$$w5k^29R=^^V5}U=RPL zgW~ZqNdJ-@kIzJUNZ)w;I;5u{y&LHzNS{Djk8~%}?;+ib^l79+@F>ZEe)0GOq!%Kc ziS$mS*CG7_(z}u7^+$Q6RY-Rt{Tb4|NdJL!$U&Ahc>u~I4I`b2^reGwqz&nz1LN`g zk=7x73h73qyO91JX|HU=@i-oFJknC6laYpymLT1Nv=Qmwk=~E=9PF(+mi6V6EV;07o^>1L9a;XPJv#L9(@kr zBmDqr5b05CAurO2NFPI5j&ujom+nMABK_uFkk^C#`@118(x>l%yhz_ddL7b3zXN%Z zejVu(NEagAiS%Bidy%#y9g<^N1HX&%NPmNLCeromP#$UB_fQ_`nfIbRQqO%Tk90ZG zy-5FpbjTr=b>96bk96$=D3A0H528F$Ydy*%J?0^lM|#G?D35f||Din6vycus)UvKa zIss`I=}e>-K7#T{SN{a%k#-|}0_oGgfWDFb9%-+`EbHJWVHZemXn`Ff-M9tzgmlnW z*b~wlBd{l=4?P3DBR%Iu=pAY2E2wu6^!Y04MS9B))Qj}buc2O~Z@i9rk&bIey-4pu zx)bSMq4^6rH`2S2?m+qo(%nc=CI2$6xxmU;oRe|%fP=DEWgL`4`9tu( zWA7*N6NQBTrlz;7;}!j=zkL#4PFvgqbIux=dyc37)!ECfuOBu3#IGJdlo$}t$@stY zAD_g}q>PCJb8g6-ba0<~R1A>tCHTJ%cu$rMpVcpmD5#!B{J+`4c%XW8d4EGz*3e90 zf^4FD1pjY?U!bu_%a?g33rOvI4EXJ7@H>G2a~k{};1BQR&VLa4;q)~4 zF~FCk!PASI!fEh9;D4M3zZ&>A(%>Hh-om_?N0h#^aZWqg3%W^t z;_*o!GjvN6b}$6?S_b?)J3jMr(+?AXe<)4)nZQ4v27ev!e+HgznVI^tCb5vz&vyfV z1m^pp_WEZfKpfj&%q zSrzP`sQmH3{}_iGBDgmA%q^_QHP_)Cs}f4A39?LRr;zo=acL055PJWgL4Hhk>u zdfQ1)aos6H_MiTF(9?lU`h<0A{t3O2JRBc-_m7_1IRf!u^U!$w7CZkb%~>mZHT2GE z5Z**L9dsWaw{N=ZKsS3>JpL0qzl1&9M;wXY{h)jIE9v=BKRpk+3-jXfGP{1Wij5uY z2L6r{Q}-j4AAq>kH#{Dv?@1f|Wj$xgj{v@FWIUd0mpAJ$Za9^n3VamtWue24zG3FM zg}~<`-mJCrCp#%m^gHoc4Z3+_V6V6~a%A4l_D1-}fd3uv(54x9l-w>@#~uo zIeu&Gc@OYG#Isk}<;W^E?Hq)Od((t?oKJM=@t66SvG*~+Uwk_BY_Ff%F;i@S#HN0m z3A#0B#N)H<^$^{K&i2we;8`EyBs;&XBbXWYKk(ZSXTLVu*50#MW;OK6dS*0BCH2wz zp|&<2A8v0q)psm6oy20fXnnD0Q9ORDy*{E_;gp}&A01aCUh8G6m*_6&rD_#^lR-D= zx_JDfWPa}@_))*n`sI)H@i@JdC(&<*FcINtUGv7$c)S(slLY>k{irg+(|TtK)&!Jce|CR z_57u4$(nEw5zzP>g#Cgptm{i0Tclw@K8Ly#6Evy1=*F598Um0 z4(tDu?d3_1yoA3eId*|=@(>zj z%E&Pp_}6|KkAKfzp5*wpbDmfTx^o{-Pe=8w23=%RJigQ+?-C>LW55r?K1QCyevdQu zy94-eX zzmCyj|JZLj9rz+UKC{r29|L?1@MG-wszmwez~2D;m3Dk)855A*27!MWct`w3_VO-w z0hMAo?grgG+ws(k!|rw&yL$rovgcs`@aJZH1;A%me-TFU*#)}lU&Z666Js6EEf$i= z$m)fCvbDgM68_AAIX7qe2j|-E zZNY`}Z{q+Tr*LWfYyG>A(<246a)z-LTlfY6{}B4|k9`uZauuG&9sM-^ub;%L2%eMa z!(VJS@L$W?(8tGDSW573Ri=+qgcSU{PYFz2Lcd%|ENtaDe&n*~mve&1r**boAxLlZ zBNt7-BP6kM`ug}v=XYcatmPi1=n(4hPuKrRT-vii(Z?06_1mT3=?brkf8j}<(tmPA zAF6)sbFhz5hpY4}Dm_)DXQ}iem6oY=kxFk+>Fp|gP^CXp={A-AN~LeB^h1^Q8K~-4 z=~q;Gs!GpN=|w6nQ|Tg=-k{RkRr;Vxf2PuHD*cs8-&W~|D(&M@^{ezNDm_)DXQ}ie zm9nzFz(4EuiaZag^wU$e2sTY z&G^d|zPkQdc>-6AmNdEE%=CrZ7gW4_q>DO91~carMgGR(*M zv%V&&Za-(@kJi7|gU&xt{cHdEmP`Fbr+u;YYyUXnNU7)5!IJ*`I7xpu;ft+b`|nzp z`m4_PV(ZuQ#p5pZPaFTm*5A*PU&c{VkJkTd#|k{uC+Qbue|p{vsQPbK=@kW1Z~V)W zT1mfTc{Ey&3k|!a3-J0G`qip*iAtBLv{9uusq{9L-lNicRr-CEKB7`x-$!_Yi+&%g zbgxQ3QR&%}CXMsvO)i~Vj)U}L3Qj9HdF1F(`Eq3?A;%Vs89AD8xZo#4DuOt`P=Es< zk}l9!<#d9i+A6>!D5V7x&zv$6C!iEGzp}1iZe97pl9A;lc%}r>P*>FpgH}N~&XQRLc+>`e%K!jZg=0#^Rzc~4*=0329Wr}C z3Ce0nO3W^j5YIVP+lR%pYWVQ^coPh~`tpty$LLR&asF(E@evyP7IRHQgR%OZZ)4C|rI@ns3r2hhy-axCZ=L9*KNt;R`lS3}D&HFJq(5ELC*>Dv`Vmh0d73^cpFR#s^;#pH z^v#++DgS$#Ug>`ZH5R{zkxzQQ@dY2lyf=ocZ7Tn!*Ce0!vXD>j(kA*pcS!zHDra!b8 zG&agBpjI%EAMz+vs6v1$m=Y5xY868?kO(#)<(Z(U5GkVHTI*Z;Jmy|(;z{P7bH2US z+Iz3P_d5Hz#_)HP3-g!6z2B~DzzDmYxc6^#PXzD>6en}$_K4su3zFSTr_b5^q6xj6 zd6#^>^}Uix(KRqUy-0lG4a0RV4EQ19Pv2*FJ21HxVL-Cj9&^9q9={PTdkz}mvahlc zJ`TJ+C)r-MD?Z;2%N_*DS3*6D{gP{E6Zwym{-w)HN~dd?czTZbD-Rj2YX-pIBmV3S zhU=Oo@C6tcO1wS})jbQqPb2=PT}H2K1He0p|LZoxb?p#%pW=8ZT$hpF$G5r{0W_1u zAAY-}96G0nr)!(Y`A!r3ZpyjjXC_D27$E0y;(veHaGiSw{u1z|4eHygq`&DFqt`W9 z(7#Lk&p$SNKQOtD$AD7wwzz;V0p2S1pZ3J4ljIE2d;hv6rbNO`Yq|7e#+>*XCQlb z8`b~6l78QXMz4DmApcklL>tjBRD40QZI98P09v`05`U88qt6#=o=$;(N`8IK%7jXM_H}TirFKJ42jvG%;5dXwV!=KVn>XWDO1iv#GgII@LuBkh(E^ib^HazNtC($jr4ttcPYb; z#6*Ya$LGg9J*N`)c>~AKC+_oNeU!6-__}*bis;@4JmJ7o!*UrXecL*t*F8s|zn1t1 z)Pt_!0>71V#7}be+DUq!r}FgtocM<~8@=wO0pkJS?O6eQew*|@ud?w73pfS~C5`IU z$-o!2v?bpxl<%2M=r2%wK{9?nJ(RPT`1P+E{srRC5dZKB!~f7}0e>Ox^Wm=N_lUpniqQ{J z&Jqmp8tJW5ag4JL8GVKH-A(8(CjEomM(=vNrV0Hv(m(c$(O*IN-zEO74Tj%N{9fX# zcN^~Z;R)ca80X$p_%1(5`hgRSo??>y#M@~1x)|^x@z?J&`g4f?oA?=z81DV9B@3)Q zE-e}E->DUflR0zyBI$je*vsWg;FA7gzoJU|w%?n4-BSR0+lk**=)dhCetqG4x>NB? zLio5B_#*UY_Oe~TEbH|<$~o%clD>O>-zL7J)o|T|4+_}>vREneKgTaL{Y(|o*`|1& zR^Co0eQR4u16@Cz#ODh3b0zUlZ!~)E|6ES|pZrdJS*2m0f#Q70+_sbc@z+ews|?!( zygftbk9(WQkux%4pM4#~)Bh&%S6(lvh>I!cW9SG8{Wo?R{v`2FDbAP7?Q>1=^_1i5 zE#A)`YC?ZC>3_uZbTf_}#J_%7NfF&^iKjhH$cVH5dBW5C!>n(Xa#;G#F5Uw?{$UBrFfU-yP1Y%OtLAMkP+AnxmK{@vV4+}EWX zm%Sts?+5)QjZyc!;%PVWlXe+?j)r1iBJtBNHGKF83phypqMsVRhWHVSOunxl>KPWW ze4MziE!qC7J`J_nY}Taaap94XI{BGrBhSbcg;XMiD`0FMS? zr01IRkQ{#w4M-JlK(A+^^CI-1bRN=U(|IVKpU%T}9=lo_>{#Bld_@wEJJ-ZX0i5{Y zZ1Y4N-VZ9%X;@`TC|4>|%|qpEE()`z6r)&gf3+)ByyfIT&Mw5bdQym-4v-x?fyyKI#MxZfmYE9drK7H>MjmVs0V+z`*tA|aW2&7fD?(6G zMb~wsu0%X5i3`xGIaV1QPKLE&=2JbgbqYQZ>cgpGrzY_pX3<`dtcjsY8gf`p9AYY6 z`%#UB=DB&WIwq=xBguS{VrI&4%Z91}8hB|yyAof2QM@X_oKEI3>OW1k7l;{u)X~xD ze1xWfJQfon^$ae@s$0$ex>j9i`lX2+l406E_3e z;(EqC#N>0G|AsofHB0*7P>p#Tn@!*#5q|BOFaHFoq2Cj3eg(~8u5L-gQWK^YVBYPawF;X}$>dF&5-_}bx=as0! z#Zf6z)4=p;Fh2cJwwCU6r8a|29k~R;{>XNV943}dYPHzRq7BXw*5{@&S}J7h+_Y{L zLeQa$Vo;mXYU-$KLTan*aWQY4DVK!PlsHSJj2@nuwc(F!Gg5zZHOC`sX#GMM8K0c6 z4~ujUJUXv~X3x|Jj}@wGDAy*#D8BCaI-x%2zCNAHqsBdYN}Px1d^06s%|;{!t|G%s ztx}G}THaE|^HXT`CMH}}Cr+Q#4^>KTo~zP#r7Q4?hVs?WzZ~qP6w(^5k&j1FEwM)= zeJHwlE6})>H)CLd0fbb8!C9DO`O=44b!!DRqR{`bFN60)U4Z67eApCJC13P%WlFx} zaF2vs=Q<9mO{sTTG2-G)5|X;$ior^s?IcjNvwLNU9d&pC6Q6QTaxH4s??DLI62-K| z0@TPLQNMqoJ{dIU`LxXDUtvBHm{aMJjTQQPo<#G-mS@Z6XtYut8I{t416EXKakrCC zy$jOhjiP+=9Z1PjZY-p9Lo$LW)H!#NmQRbDYqO=pB0L{|)Q^0Tr!pR#&?wlo4r^z_ zCJst_WpJk>QmGwHL}{YqC@#i;Pef(QOm!B@L}x_2bQM0O-lW}p%{H`1=%rPgL@f4R zt)le##0nQ%k!#!Bg$DGX#Ct48$@Ne}&N3gh*gXiS0W(wHk4=WJp03u584u`E`#{K} zkhGxvRAQI=t;d!U(ZVNW zd0J}dvZ|pNzqkARn75GLWid_K zBt$wJTa;tAZ8YumdIlqhH;=U8dj_Rf7CRKq=Ydi|iN#c?f!DbK=@+2TV5FU|VD4kj z%7sV@brh-{#1yr@GP>@mx}=nIlOSe+0^FhF`v&9Qq-nq~4voSlYPVY^p#Hz>6tK$_ zqwf<+T1t4sMPP}&#a~WG#`iPubS(aye@ROTZ#Y**pk3e6A7B4+q!SNW{?E6RG|1Pz ze126y{1o|D0(%!{iah>-mJ$#-Po=kHncd>KDxc3IoNT}YmcC;8Z)z#w4gV0p)6WR8 ztwHtV{B24FKa02@i9e6u&u#4%23&ql%ic_eKg(U#ALNXb^ZWkUenxaDiI3*p>HK~b za5;13@4nA=H~BsP#q?8LvK0@KMd$bZxCaXS9)B_a*O9-60{z@rE7qyy@^fawkEODB z?#=ag12CymR9D>J`=}7(I@xaVm&Fm^x8o_x@B4rckbj$Jj2x|n`>h53ffGtfF@SXk zxdz^{yZ<`Bzu$(3Z2Z2jc<&*jpcK2", + }, + "author": "James Allen ", "dependencies": { - "async": "0.2.9", - "body-parser": "^1.2.0", - "dockerode": "^2.5.3", - "express": "^4.2.0", - "fs-extra": "^0.16.3", - "grunt-mkdir": "^1.0.0", - "heapdump": "^0.3.5", - "lockfile": "^1.0.3", - "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.4", - "lynx": "0.0.11", - "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.5.0", - "mkdirp": "0.3.5", - "mysql": "2.6.2", - "request": "^2.21.0", - "sequelize": "^2.1.3", - "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", - "smoke-test-sharelatex": "git+https://github.com/sharelatex/smoke-test-sharelatex.git#v0.2.0", - "sqlite3": "~3.1.8", - "underscore": "^1.8.2", - "v8-profiler": "^5.2.4", + "async": "0.2.9", + "body-parser": "^1.2.0", + "dockerode": "^2.5.3", + "express": "^4.2.0", + "fs-extra": "^0.16.3", + "grunt-mkdir": "^1.0.0", + "heapdump": "^0.3.5", + "lockfile": "^1.0.3", + "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.4", + "lynx": "0.0.11", + "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.5.0", + "mkdirp": "0.3.5", + "mysql": "2.6.2", + "request": "^2.21.0", + "sequelize": "^2.1.3", + "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", + "smoke-test-sharelatex": "git+https://github.com/sharelatex/smoke-test-sharelatex.git#v0.2.0", + "sqlite3": "~3.1.8", + "underscore": "^1.8.2", + "v8-profiler": "^5.2.4", "wrench": "~1.5.4" - }, + }, "devDependencies": { - "mocha": "1.10.0", - "coffee-script": "1.6.0", - "chai": "~1.8.1", - "sinon": "~1.7.3", - "grunt": "~0.4.2", - "grunt-contrib-coffee": "~0.7.0", - "grunt-contrib-clean": "~0.5.0", - "grunt-shell": "~0.6.1", - "grunt-mocha-test": "~0.8.1", - "sandboxed-module": "~0.3.0", - "timekeeper": "0.0.4", - "grunt-execute": "^0.1.5", - "bunyan": "^0.22.1", + "mocha": "^4.0.1", + "coffee-script": "1.6.0", + "chai": "~1.8.1", + "sinon": "~1.7.3", + "grunt": "~0.4.2", + "grunt-contrib-coffee": "~0.7.0", + "grunt-contrib-clean": "~0.5.0", + "grunt-shell": "~0.6.1", + "grunt-mocha-test": "~0.8.1", + "sandboxed-module": "~0.3.0", + "timekeeper": "0.0.4", + "grunt-execute": "^0.1.5", + "bunyan": "^0.22.1", "grunt-bunyan": "^0.5.0" } } diff --git a/test/acceptance/coffee/ExampleDocumentTests.coffee b/test/acceptance/coffee/ExampleDocumentTests.coffee index 4d431c2..e9a58b5 100644 --- a/test/acceptance/coffee/ExampleDocumentTests.coffee +++ b/test/acceptance/coffee/ExampleDocumentTests.coffee @@ -11,7 +11,8 @@ try catch e convertToPng = (pdfPath, pngPath, callback = (error) ->) -> - convert = ChildProcess.exec "convert #{fixturePath(pdfPath)} #{fixturePath(pngPath)}" + command = "convert #{fixturePath(pdfPath)} #{fixturePath(pngPath)}" + convert = ChildProcess.exec command stdout = "" convert.stdout.on "data", (chunk) -> console.log "STDOUT", chunk.toString() convert.stderr.on "data", (chunk) -> console.log "STDERR", chunk.toString() diff --git a/test/acceptance/coffee/helpers/Client.coffee b/test/acceptance/coffee/helpers/Client.coffee index 7663496..546c235 100644 --- a/test/acceptance/coffee/helpers/Client.coffee +++ b/test/acceptance/coffee/helpers/Client.coffee @@ -31,6 +31,7 @@ module.exports = Client = express = require("express") app = express() app.use express.static(directory) + console.log("starting test server on", port, host) app.listen(port, host).on "error", (error) -> console.error "error starting server:", error.message process.exit(1) From a2a8b70b744b451d27f71bea2acc036ce33f8afc Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Fri, 16 Feb 2018 11:36:32 +0000 Subject: [PATCH 003/118] acceptence tests pass inside docker container (apart from sync) --- Dockerfile | 15 +- app.coffee | 12 +- config/settings.defaults.coffee | 1 - docker-compose.yml | 35 +- install_deps.sh | 4 + package-lock.json | 531 +++++++++--------- package.json | 2 +- .../coffee/BrokenLatexFileTests.coffee | 5 +- .../coffee/DeleteOldFilesTest.coffee | 6 +- .../coffee/ExampleDocumentTests.coffee | 5 +- .../coffee/SimpleLatexFileTests.coffee | 4 +- test/acceptance/coffee/SynctexTests.coffee | 4 +- test/acceptance/coffee/TimeoutTests.coffee | 5 +- test/acceptance/coffee/UrlCachingTests.coffee | 4 +- test/acceptance/coffee/WordcountTests.coffee | 4 +- test/acceptance/coffee/helpers/ClsiApp.coffee | 24 + 16 files changed, 351 insertions(+), 310 deletions(-) create mode 100755 install_deps.sh create mode 100644 test/acceptance/coffee/helpers/ClsiApp.coffee diff --git a/Dockerfile b/Dockerfile index 83e452d..05be846 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,23 +1,16 @@ -FROM node:6.9.5 - -RUN wget -qO- https://get.docker.com/ | sh - -run apt-get install poppler-utils vim ghostscript --yes - -# run git build-essential --yes -# RUN git clone https://github.com/netblue30/firejail.git -# RUN cd firejail && ./configure && make && make install-strip -# run mkdir /data +FROM node:6.13.0 COPY ./ /app WORKDIR /app + RUN npm install +RUN [ -e ./install_deps.sh ] && ./install_deps.sh + RUN npm run compile -ENV SHARELATEX_CONFIG /app/config/settings.production.coffee ENV NODE_ENV production CMD ["node","/app/app.js"] diff --git a/app.coffee b/app.coffee index ba7d225..527b17e 100644 --- a/app.coffee +++ b/app.coffee @@ -165,8 +165,16 @@ app.use (error, req, res, next) -> logger.error {err: error, url: req.url}, "server error" res.sendStatus(error?.statusCode || 500) -app.listen port = (Settings.internal?.clsi?.port or 3013), host = (Settings.internal?.clsi?.host or "localhost"), (error) -> - logger.info "CLSI starting up, listening on #{host}:#{port}" +port = (Settings.internal?.clsi?.port or 3013) +host = (Settings.internal?.clsi?.host or "localhost") + +if !module.parent # Called directly + app.listen port, host, (error) -> + logger.info "CLSI starting up, listening on #{host}:#{port}" + +module.exports = app + + setInterval () -> ProjectPersistenceManager.clearExpiredProjects() diff --git a/config/settings.defaults.coffee b/config/settings.defaults.coffee index 4835b4f..9b1e4be 100644 --- a/config/settings.defaults.coffee +++ b/config/settings.defaults.coffee @@ -20,7 +20,6 @@ module.exports = clsi: port: 3013 host: process.env["LISTEN_ADDRESS"] or "0.0.0.0" - apis: clsi: url: "http://#{process.env['CLSI_HOST'] or 'localhost'}:3013" diff --git a/docker-compose.yml b/docker-compose.yml index d09ea6e..a272e75 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,16 +14,17 @@ services: entrypoint: npm run test:unit test_acceptance: - image: node:6.9.5 - volumes: - - .:/app + build: . environment: - REDIS_HOST: redis - MONGO_HOST: mongo - depends_on: - - redis - - mongo - working_dir: /app + TEXLIVE_IMAGE: quay.io/sharelatex/texlive-full:2017.1 + TEXLIVE_IMAGE_USER: root # Not ideal, but makes running in dev very simple + SHARELATEX_CONFIG: /app/config/settings.defaults.coffee + COMMAND_RUNNER: docker-runner-sharelatex + COMPILES_HOST_DIR: $PWD/compiles + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - ./docker-runner:/app/node_modules/docker-runner-sharelatex + - ./compiles:/app/compiles entrypoint: npm run test:acceptance redis: @@ -31,19 +32,3 @@ services: mongo: image: mongo:3.4 - - app: - image: gcr.io/henry-terraform-admin/clsi - build: . - environment: - TEXLIVE_IMAGE: quay.io/sharelatex/texlive-full:2017.1 - TEXLIVE_IMAGE_USER: root # Not ideal, but makes running in dev very simple - COMMAND_RUNNER: docker-runner-sharelatex - SHARELATEX_CONFIG: /app/config/settings.defaults.coffee - COMPILES_HOST_DIR: $PWD/compiles - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - ./docker-runner:/app/node_modules/docker-runner-sharelatex - - /Users/henry/Projects/sharelatex-dev-environment/clsi/compiles:/app/compiles - ports: - - 3013:3013 \ No newline at end of file diff --git a/install_deps.sh b/install_deps.sh new file mode 100755 index 0000000..49bdc5c --- /dev/null +++ b/install_deps.sh @@ -0,0 +1,4 @@ +/bin/sh +wget -qO- https://get.docker.com/ | sh +apt-get install poppler-utils vim ghostscript --yes +npm rebuild diff --git a/package-lock.json b/package-lock.json index 5f30ca5..a5b7847 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,12 @@ "resolved": "https://registry.npmjs.org/async/-/async-0.2.9.tgz", "integrity": "sha1-32MGD789Myhqdqr21Vophtn/hhk=" }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "optional": true + }, "bl": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.1.tgz", @@ -208,6 +214,16 @@ } } }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", + "optional": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, "browser-stdout": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", @@ -396,8 +412,18 @@ "coffee-script": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz", - "integrity": "sha1-gIs5bhEPU9AhoZpO8fZb4OjjX6M=", - "dev": true + "integrity": "sha1-gIs5bhEPU9AhoZpO8fZb4OjjX6M=" + }, + "commander": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz", + "integrity": "sha1-0bhvkB+LZL2UG96tr5JFMDk76Sg=" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "optional": true }, "concat-stream": { "version": "1.5.2", @@ -429,6 +455,11 @@ } } }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, "core-util-is": { "version": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" @@ -440,10 +471,15 @@ "ms": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" } }, + "diff": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz", + "integrity": "sha1-JLuwAcSn1VIhaefKvbLCgU7ZHPQ=" + }, "docker-modem": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-1.0.4.tgz", - "integrity": "sha512-pkXB9p7KWagegOXm2NsbVDBluQQLCBJzX9uYJzVbL6CHwe4d2sSbcACJ4K8ISX1l1JUUmFSiwNkBKc1uTiU4MA==", + "integrity": "sha1-JEJUsiax1/YCJfgjlb2FGQz3Ves=", "requires": { "JSONStream": "0.10.0", "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -467,17 +503,26 @@ "dockerode": { "version": "2.5.3", "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-2.5.3.tgz", - "integrity": "sha512-LQKXR5jyI+G/+5OhZCi40m0ArY4j46g7Tl71Vtn10Ekt5TiyDzZAoqXOCS6edQpEuGbdFgSDJxleFqLxACpKJg==", + "integrity": "sha1-Wsw8Bx96JuRDpbWM83U+8vPCvAQ=", "requires": { "concat-stream": "1.5.2", "docker-modem": "1.0.4", "tar-fs": "1.12.0" } }, + "dtrace-provider": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.6.0.tgz", + "integrity": "sha1-CweNVReTfYcxAUUtkUZzdVe3XlE=", + "optional": true, + "requires": { + "nan": "2.8.0" + } + }, "end-of-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "integrity": "sha1-7SljTRm6ukY7bOa4CjchPqtx7EM=", "requires": { "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz" } @@ -773,7 +818,7 @@ "setprototypeof": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + "integrity": "sha1-0L2FU2iHtv58DYGMuWLZ2RxU5lY=" }, "statuses": { "version": "1.3.1", @@ -932,6 +977,52 @@ } } }, + "glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "optional": true, + "requires": { + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "optional": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "optional": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "optional": true + } + } + }, + "graceful-fs": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz", + "integrity": "sha1-fNLNsiiko/Nule+mzBQt59GhNtA=" + }, + "growl": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.7.0.tgz", + "integrity": "sha1-3i1mE20ALhErpw8/EMMc98NQsto=" + }, "grunt": { "version": "0.4.5", "resolved": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz", @@ -1478,6 +1569,32 @@ "resolved": "https://registry.npmjs.org/heapdump/-/heapdump-0.3.9.tgz", "integrity": "sha1-A8dOsN9dZ74Jgug0KbqcnSs7f3g=" }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "optional": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + }, + "dependencies": { + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "optional": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + } + } + }, "inherits": { "version": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" @@ -1486,6 +1603,32 @@ "version": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" }, + "jade": { + "version": "0.26.3", + "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", + "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=", + "requires": { + "commander": "0.6.1", + "mkdirp": "0.3.0" + }, + "dependencies": { + "commander": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", + "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=" + }, + "mkdirp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", + "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=" + } + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, "jsonparse": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz", @@ -1504,22 +1647,6 @@ "raven": "1.2.1" }, "dependencies": { - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "optional": true, - "requires": { - "balanced-match": "1.0.0", - "concat-map": "0.0.1" - } - }, "bunyan": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.5.1.tgz", @@ -1534,165 +1661,19 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.4.0.tgz", "integrity": "sha1-XjvIqsJsAajie/EHcixWVfWtfTY=" - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "optional": true - }, - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" - }, - "dtrace-provider": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.6.0.tgz", - "integrity": "sha1-CweNVReTfYcxAUUtkUZzdVe3XlE=", - "optional": true, - "requires": { - "nan": "2.8.0" - } - }, - "glob": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", - "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", - "optional": true, - "requires": { - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "optional": true, - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "optional": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "lsmod": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-1.0.0.tgz", - "integrity": "sha1-mgD3bco26yP6BTUK/htYXUKZ5ks=" - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "optional": true, - "requires": { - "brace-expansion": "1.1.11" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "optional": true - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "optional": true, - "requires": { - "minimist": "0.0.8" - } - }, - "mv": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", - "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", - "optional": true, - "requires": { - "mkdirp": "0.5.1", - "ncp": "2.0.0", - "rimraf": "2.4.5" - } - }, - "nan": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", - "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=", - "optional": true - }, - "ncp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", - "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", - "optional": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1.0.2" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "optional": true - }, - "raven": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/raven/-/raven-1.2.1.tgz", - "integrity": "sha1-lJwTTbAooZC3u/j3kKrlQbfAIL0=", - "requires": { - "cookie": "0.3.1", - "json-stringify-safe": "5.0.1", - "lsmod": "1.0.0", - "stack-trace": "0.0.9", - "uuid": "3.0.0" - } - }, - "rimraf": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", - "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", - "optional": true, - "requires": { - "glob": "6.0.4" - } - }, - "stack-trace": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz", - "integrity": "sha1-qPbq7KkGdMMz58Q5U/J1tFFRBpU=" - }, - "uuid": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz", - "integrity": "sha1-Zyj8BFnEUNeWqZwxg3VpvfZy1yg=" - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" } } }, + "lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" + }, + "lsmod": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-1.0.0.tgz", + "integrity": "sha1-mgD3bco26yP6BTUK/htYXUKZ5ks=" + }, "lynx": { "version": "0.0.11", "resolved": "https://registry.npmjs.org/lynx/-/lynx-0.0.11.tgz", @@ -1714,6 +1695,11 @@ } } }, + "mersenne": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.4.tgz", + "integrity": "sha1-QB/ex+whzbngPNPTAhOY2iGycIU=" + }, "metrics-sharelatex": { "version": "git+https://github.com/sharelatex/metrics-sharelatex.git#05bd57604115ae5efdca2a419fbef4f25e9a780f", "requires": { @@ -1722,11 +1708,6 @@ "underscore": "1.6.0" }, "dependencies": { - "coffee-script": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz", - "integrity": "sha1-gIs5bhEPU9AhoZpO8fZb4OjjX6M=" - }, "lynx": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/lynx/-/lynx-0.1.1.tgz", @@ -1736,16 +1717,6 @@ "statsd-parser": "0.0.4" } }, - "mersenne": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.4.tgz", - "integrity": "sha1-QB/ex+whzbngPNPTAhOY2iGycIU=" - }, - "statsd-parser": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz", - "integrity": "sha1-y9JDlTzELv/VSLXSI4jtaJ7GOb0=" - }, "underscore": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", @@ -1753,6 +1724,15 @@ } } }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "optional": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, "minimist": { "version": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" @@ -1765,7 +1745,7 @@ "mocha": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.1.0.tgz", - "integrity": "sha512-0RVnjg1HJsXY2YFDoTNzcc1NKhYuXKRrBAG2gDygmJJA136Cs2QlRliZG1mA0ap7cuaT30mw16luAeln+4RiNA==", + "integrity": "sha1-fYbPvPNcuCnidUwy4XNV7AUzh5Q=", "dev": true, "requires": { "browser-stdout": "1.3.0", @@ -1799,7 +1779,7 @@ "commander": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "integrity": "sha1-FXFS/R56bI2YpbcVzzdt+SgARWM=", "dev": true }, "concat-map": { @@ -1811,7 +1791,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", "dev": true, "requires": { "ms": "2.0.0" @@ -1820,7 +1800,7 @@ "diff": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", - "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==", + "integrity": "sha1-qoVnpu7QPFMfyJ0/cRzQ5SWd7HU=", "dev": true }, "fs.realpath": { @@ -1846,7 +1826,7 @@ "growl": { "version": "1.10.3", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", - "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", + "integrity": "sha1-GSa6kM8+3+KttJJ/WIC8IsZseQ8=", "dev": true }, "inflight": { @@ -1922,6 +1902,34 @@ "version": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "mv": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", + "optional": true, + "requires": { + "mkdirp": "0.5.1", + "ncp": "2.0.0", + "rimraf": "2.4.5" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "optional": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "optional": true, + "requires": { + "minimist": "0.0.8" + } + } + } + }, "mysql": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.6.2.tgz", @@ -1975,6 +1983,18 @@ } } }, + "nan": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", + "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=", + "optional": true + }, + "ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", + "optional": true + }, "node-uuid": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", @@ -1987,6 +2007,12 @@ "wrappy": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" } }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "optional": true + }, "process-nextick-args": { "version": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" @@ -1994,16 +2020,28 @@ "pump": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz", - "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==", + "integrity": "sha1-Xf6DEcM7v2/BgmH580cCxHwIqVQ=", "requires": { "end-of-stream": "1.4.1", "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz" } }, + "raven": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/raven/-/raven-1.2.1.tgz", + "integrity": "sha1-lJwTTbAooZC3u/j3kKrlQbfAIL0=", + "requires": { + "cookie": "0.3.1", + "json-stringify-safe": "5.0.1", + "lsmod": "1.0.0", + "stack-trace": "0.0.9", + "uuid": "3.0.0" + } + }, "request": { "version": "2.83.0", "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", - "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", + "integrity": "sha1-ygtl2gLtYpNYh4COb1EDgQNOM1Y=", "requires": { "aws-sign2": "0.7.0", "aws4": "1.6.0", @@ -2361,7 +2399,7 @@ "uuid": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", - "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==" + "integrity": "sha1-EsUou51Y0LkmXZovbw/ovhf/HxQ=" }, "verror": { "version": "1.10.0", @@ -2375,6 +2413,15 @@ } } }, + "rimraf": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", + "optional": true, + "requires": { + "glob": "6.0.4" + } + }, "safe-buffer": { "version": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=" @@ -2471,15 +2518,13 @@ "version": "git+https://github.com/sharelatex/settings-sharelatex.git#cbc5e41c1dbe6789721a14b3fdae05bf22546559", "requires": { "coffee-script": "1.6.0" - }, - "dependencies": { - "coffee-script": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz", - "integrity": "sha1-gIs5bhEPU9AhoZpO8fZb4OjjX6M=" - } } }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" + }, "sinon": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.7.3.tgz", @@ -2512,16 +2557,6 @@ "mocha": "1.17.1" }, "dependencies": { - "commander": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz", - "integrity": "sha1-0bhvkB+LZL2UG96tr5JFMDk76Sg=" - }, - "diff": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz", - "integrity": "sha1-JLuwAcSn1VIhaefKvbLCgU7ZHPQ=" - }, "glob": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", @@ -2532,47 +2567,11 @@ "minimatch": "0.2.14" } }, - "graceful-fs": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz", - "integrity": "sha1-fNLNsiiko/Nule+mzBQt59GhNtA=" - }, - "growl": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.7.0.tgz", - "integrity": "sha1-3i1mE20ALhErpw8/EMMc98NQsto=" - }, "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, - "jade": { - "version": "0.26.3", - "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", - "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=", - "requires": { - "commander": "0.6.1", - "mkdirp": "0.3.0" - }, - "dependencies": { - "commander": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", - "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=" - }, - "mkdirp": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", - "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=" - } - } - }, - "lru-cache": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", - "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" - }, "minimatch": { "version": "0.2.14", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", @@ -2595,11 +2594,6 @@ "jade": "0.26.3", "mkdirp": "0.3.5" } - }, - "sigmund": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", - "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" } } }, @@ -3325,6 +3319,16 @@ } } }, + "stack-trace": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz", + "integrity": "sha1-qPbq7KkGdMMz58Q5U/J1tFFRBpU=" + }, + "statsd-parser": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz", + "integrity": "sha1-y9JDlTzELv/VSLXSI4jtaJ7GOb0=" + }, "string_decoder": { "version": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" @@ -3332,7 +3336,7 @@ "supports-color": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "integrity": "sha1-iD992rwWUUKyphQn8zUt7RldGj4=", "dev": true, "requires": { "has-flag": "2.0.0" @@ -3361,7 +3365,7 @@ "tar-stream": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.5.tgz", - "integrity": "sha512-mQdgLPc/Vjfr3VWqWbfxW8yQNiJCbAZ+Gf6GDu1Cy0bdb33ofyiNGBtAY96jHFhDuivCwgW1H9DgTON+INiXgg==", + "integrity": "sha1-XK2Ed59FyDsfJQjZawnYjHIYr1U=", "requires": { "bl": "1.2.1", "end-of-stream": "1.4.1", @@ -3423,6 +3427,11 @@ "version": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, + "uuid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz", + "integrity": "sha1-Zyj8BFnEUNeWqZwxg3VpvfZy1yg=" + }, "v8-profiler": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/v8-profiler/-/v8-profiler-5.7.0.tgz", diff --git a/package.json b/package.json index e44f2e4..77e9423 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "sequelize": "^2.1.3", "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", "smoke-test-sharelatex": "git+https://github.com/sharelatex/smoke-test-sharelatex.git#v0.2.0", - "sqlite3": "~3.1.8", + "sqlite3": "^3.1.13", "underscore": "^1.8.2", "v8-profiler": "^5.2.4", "wrench": "~1.5.4" diff --git a/test/acceptance/coffee/BrokenLatexFileTests.coffee b/test/acceptance/coffee/BrokenLatexFileTests.coffee index 5a92d5f..a54df55 100644 --- a/test/acceptance/coffee/BrokenLatexFileTests.coffee +++ b/test/acceptance/coffee/BrokenLatexFileTests.coffee @@ -1,9 +1,10 @@ Client = require "./helpers/Client" request = require "request" require("chai").should() +ClsiApp = require "./helpers/ClsiApp" describe "Broken LaTeX file", -> - before -> + before (done)-> @broken_request = resources: [ path: "main.tex" @@ -24,6 +25,7 @@ describe "Broken LaTeX file", -> \\end{document} ''' ] + ClsiApp.ensureRunning done describe "on first run", -> before (done) -> @@ -31,6 +33,7 @@ describe "Broken LaTeX file", -> Client.compile @project_id, @broken_request, (@error, @res, @body) => done() it "should return a failure status", -> + console.log(@error, @res, @body) @body.compile.status.should.equal "failure" describe "on second run", -> diff --git a/test/acceptance/coffee/DeleteOldFilesTest.coffee b/test/acceptance/coffee/DeleteOldFilesTest.coffee index b8a1ff3..1cb6776 100644 --- a/test/acceptance/coffee/DeleteOldFilesTest.coffee +++ b/test/acceptance/coffee/DeleteOldFilesTest.coffee @@ -1,9 +1,10 @@ Client = require "./helpers/Client" request = require "request" require("chai").should() +ClsiApp = require "./helpers/ClsiApp" describe "Deleting Old Files", -> - before -> + before (done)-> @request = resources: [ path: "main.tex" @@ -14,7 +15,8 @@ describe "Deleting Old Files", -> \\end{document} ''' ] - + ClsiApp.ensureRunning done + describe "on first run", -> before (done) -> @project_id = Client.randomId() diff --git a/test/acceptance/coffee/ExampleDocumentTests.coffee b/test/acceptance/coffee/ExampleDocumentTests.coffee index e9a58b5..5ec6111 100644 --- a/test/acceptance/coffee/ExampleDocumentTests.coffee +++ b/test/acceptance/coffee/ExampleDocumentTests.coffee @@ -3,6 +3,7 @@ request = require "request" require("chai").should() fs = require "fs" ChildProcess = require "child_process" +ClsiApp = require "./helpers/ClsiApp" fixturePath = (path) -> __dirname + "/../fixtures/" + path @@ -86,7 +87,9 @@ Client.runServer(4242, fixturePath("examples")) describe "Example Documents", -> before (done) -> - ChildProcess.exec("rm test/acceptance/fixtures/tmp/*").on "exit", () -> done() + ChildProcess.exec("rm test/acceptance/fixtures/tmp/*").on "exit", () -> + ClsiApp.ensureRunning done + for example_dir in fs.readdirSync fixturePath("examples") do (example_dir) -> diff --git a/test/acceptance/coffee/SimpleLatexFileTests.coffee b/test/acceptance/coffee/SimpleLatexFileTests.coffee index 2693f63..95b667b 100644 --- a/test/acceptance/coffee/SimpleLatexFileTests.coffee +++ b/test/acceptance/coffee/SimpleLatexFileTests.coffee @@ -1,6 +1,7 @@ Client = require "./helpers/Client" request = require "request" require("chai").should() +ClsiApp = require "./helpers/ClsiApp" describe "Simple LaTeX file", -> before (done) -> @@ -15,7 +16,8 @@ describe "Simple LaTeX file", -> \\end{document} ''' ] - Client.compile @project_id, @request, (@error, @res, @body) => done() + ClsiApp.ensureRunning => + Client.compile @project_id, @request, (@error, @res, @body) => done() it "should return the PDF", -> pdf = Client.getOutputFile(@body, "pdf") diff --git a/test/acceptance/coffee/SynctexTests.coffee b/test/acceptance/coffee/SynctexTests.coffee index 02b2397..c6adcf2 100644 --- a/test/acceptance/coffee/SynctexTests.coffee +++ b/test/acceptance/coffee/SynctexTests.coffee @@ -2,6 +2,7 @@ Client = require "./helpers/Client" request = require "request" require("chai").should() expect = require("chai").expect +ClsiApp = require "./helpers/ClsiApp" describe "Syncing", -> before (done) -> @@ -16,7 +17,8 @@ describe "Syncing", -> ''' ] @project_id = Client.randomId() - Client.compile @project_id, @request, (@error, @res, @body) => done() + ClsiApp.ensureRunning => + Client.compile @project_id, @request, (@error, @res, @body) => done() describe "from code to pdf", -> it "should return the correct location", (done) -> diff --git a/test/acceptance/coffee/TimeoutTests.coffee b/test/acceptance/coffee/TimeoutTests.coffee index 5e0058d..3861487 100644 --- a/test/acceptance/coffee/TimeoutTests.coffee +++ b/test/acceptance/coffee/TimeoutTests.coffee @@ -1,6 +1,8 @@ Client = require "./helpers/Client" request = require "request" require("chai").should() +ClsiApp = require "./helpers/ClsiApp" + describe "Timed out compile", -> before (done) -> @@ -18,7 +20,8 @@ describe "Timed out compile", -> ''' ] @project_id = Client.randomId() - Client.compile @project_id, @request, (@error, @res, @body) => done() + ClsiApp.ensureRunning => + Client.compile @project_id, @request, (@error, @res, @body) => done() it "should return a timeout error", -> @body.compile.error.should.equal "container timed out" diff --git a/test/acceptance/coffee/UrlCachingTests.coffee b/test/acceptance/coffee/UrlCachingTests.coffee index 9e6f3d6..cef7446 100644 --- a/test/acceptance/coffee/UrlCachingTests.coffee +++ b/test/acceptance/coffee/UrlCachingTests.coffee @@ -2,6 +2,7 @@ Client = require "./helpers/Client" request = require "request" require("chai").should() sinon = require "sinon" +ClsiApp = require "./helpers/ClsiApp" host = "localhost" @@ -46,7 +47,8 @@ describe "Url Caching", -> }] sinon.spy Server, "getFile" - Client.compile @project_id, @request, (@error, @res, @body) => done() + ClsiApp.ensureRunning => + Client.compile @project_id, @request, (@error, @res, @body) => done() afterEach -> Server.getFile.restore() diff --git a/test/acceptance/coffee/WordcountTests.coffee b/test/acceptance/coffee/WordcountTests.coffee index d84ecba..abace06 100644 --- a/test/acceptance/coffee/WordcountTests.coffee +++ b/test/acceptance/coffee/WordcountTests.coffee @@ -4,6 +4,7 @@ require("chai").should() expect = require("chai").expect path = require("path") fs = require("fs") +ClsiApp = require "./helpers/ClsiApp" describe "Syncing", -> before (done) -> @@ -13,7 +14,8 @@ describe "Syncing", -> content: fs.readFileSync(path.join(__dirname,"../fixtures/naugty_strings.txt"),"utf-8") ] @project_id = Client.randomId() - Client.compile @project_id, @request, (@error, @res, @body) => done() + ClsiApp.ensureRunning => + Client.compile @project_id, @request, (@error, @res, @body) => done() describe "wordcount file", -> it "should return wordcount info", (done) -> diff --git a/test/acceptance/coffee/helpers/ClsiApp.coffee b/test/acceptance/coffee/helpers/ClsiApp.coffee new file mode 100644 index 0000000..35be427 --- /dev/null +++ b/test/acceptance/coffee/helpers/ClsiApp.coffee @@ -0,0 +1,24 @@ +app = require('../../../../app') +require("logger-sharelatex").logger.level("error") +logger = require("logger-sharelatex") +Settings = require("settings-sharelatex") + +module.exports = + running: false + initing: false + callbacks: [] + ensureRunning: (callback = (error) ->) -> + if @running + return callback() + else if @initing + @callbacks.push callback + else + @initing = true + @callbacks.push callback + app.listen Settings.internal?.clsi?.port, "localhost", (error) => + throw error if error? + @running = true + logger.log("clsi running in dev mode") + + for callback in @callbacks + callback() \ No newline at end of file From a02adacc987c2acd19d2884ab507db51bc46819a Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Fri, 16 Feb 2018 12:52:12 +0000 Subject: [PATCH 004/118] updated build sripts with 1.0.3 --- .nvmrc | 2 +- Dockerfile | 2 +- Makefile | 9 ++-- base.yml | 14 ++++++ docker-compose.ci.yml | 2 +- docker-compose.yml | 21 ++++---- kube.yaml | 41 ++++++++++++++++ nodemon.json | 1 + package.json | 112 +++++++++++++++++++++--------------------- 9 files changed, 131 insertions(+), 73 deletions(-) create mode 100644 base.yml create mode 100644 kube.yaml diff --git a/.nvmrc b/.nvmrc index e1e5d13..5917993 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -6.9.5 +6.13.0 diff --git a/Dockerfile b/Dockerfile index 05be846..e195f96 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,13 +4,13 @@ COPY ./ /app WORKDIR /app - RUN npm install RUN [ -e ./install_deps.sh ] && ./install_deps.sh RUN npm run compile +ENV SHARELATEX_CONFIG /app/config/settings.production.coffee ENV NODE_ENV production CMD ["node","/app/app.js"] diff --git a/Makefile b/Makefile index 6e7fca5..828e8f8 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,16 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.0.1 +# Version: 1.0.3 BUILD_NUMBER ?= local BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) PROJECT_NAME = clsi DOCKER_COMPOSE_FLAGS ?= -f docker-compose.yml -DOCKER_COMPOSE := docker-compose ${DOCKER_COMPOSE_FLAGS} +DOCKER_COMPOSE := BUILD_NUMBER=$(BUILD_NUMBER) \ + BRANCH_NAME=$(BRANCH_NAME) \ + PROJECT_NAME=$(PROJECT_NAME) \ + docker-compose ${DOCKER_COMPOSE_FLAGS} clean: rm -f app.js @@ -24,7 +27,7 @@ test_acceptance: test_clean # clear the database before each acceptance test run @[ -d test/acceptance ] && $(DOCKER_COMPOSE) run --rm test_acceptance -- ${MOCHA_ARGS} || echo "clsi has no acceptance tests" test_clean: - $(DOCKER_COMPOSE) down + $(DOCKER_COMPOSE) down -t 0 build: docker build --pull --tag quay.io/sharelatex/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) . diff --git a/base.yml b/base.yml new file mode 100644 index 0000000..ef5f44a --- /dev/null +++ b/base.yml @@ -0,0 +1,14 @@ +version: "2" + +services: + base: + environment: + TEXLIVE_IMAGE: quay.io/sharelatex/texlive-full:2017.1 + TEXLIVE_IMAGE_USER: root + SHARELATEX_CONFIG: /app/config/settings.defaults.coffee + COMMAND_RUNNER: docker-runner-sharelatex + COMPILES_HOST_DIR: $PWD/compiles + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - ./docker-runner:/app/node_modules/docker-runner-sharelatex + - ./compiles:/app/compiles diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index f34280d..695ad3c 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.0.1 +# Version: 1.0.3 version: "2" diff --git a/docker-compose.yml b/docker-compose.yml index a272e75..8c612e5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,13 +1,13 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.0.1 +# Version: 1.0.3 version: "2" services: test_unit: - image: node:6.9.5 + image: node:6.13.0 volumes: - .:/app working_dir: /app @@ -15,16 +15,15 @@ services: test_acceptance: build: . + extends: + file: base.yml + service: base environment: - TEXLIVE_IMAGE: quay.io/sharelatex/texlive-full:2017.1 - TEXLIVE_IMAGE_USER: root # Not ideal, but makes running in dev very simple - SHARELATEX_CONFIG: /app/config/settings.defaults.coffee - COMMAND_RUNNER: docker-runner-sharelatex - COMPILES_HOST_DIR: $PWD/compiles - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - ./docker-runner:/app/node_modules/docker-runner-sharelatex - - ./compiles:/app/compiles + REDIS_HOST: redis + MONGO_HOST: mongo + depends_on: + - redis + - mongo entrypoint: npm run test:acceptance redis: diff --git a/kube.yaml b/kube.yaml new file mode 100644 index 0000000..44a562d --- /dev/null +++ b/kube.yaml @@ -0,0 +1,41 @@ +apiVersion: v1 +kind: Service +metadata: + name: clsi + namespace: default +spec: + type: LoadBalancer + ports: + - port: 3009 + protocol: TCP + targetPort: 3009 + selector: + run: clsi +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: clsi + namespace: default +spec: + replicas: 2 + template: + metadata: + labels: + run: clsi + spec: + containers: + - name: clsi + image: gcr.io/henry-terraform-admin/clsi:6e0c79688030117a9c27d0fc01d1271e6ac3dd3e + imagePullPolicy: Always + readinessProbe: + httpGet: + path: status + port: 3009 + periodSeconds: 5 + initialDelaySeconds: 0 + failureThreshold: 3 + successThreshold: 1 + + + diff --git a/nodemon.json b/nodemon.json index 9044f92..9a3be8d 100644 --- a/nodemon.json +++ b/nodemon.json @@ -4,6 +4,7 @@ "node_modules/" ], "verbose": true, + "legacyWatch": true, "execMap": { "js": "npm run start" }, diff --git a/package.json b/package.json index 77e9423..c5d52c9 100644 --- a/package.json +++ b/package.json @@ -1,67 +1,67 @@ { - "name": "node-clsi", - "description": "A Node.js implementation of the CLSI LaTeX web-API", - "version": "0.1.4", + "name": "node-clsi", + "description": "A Node.js implementation of the CLSI LaTeX web-API", + "version": "0.1.4", "repository": { - "type": "git", + "type": "git", "url": "https://github.com/sharelatex/clsi-sharelatex.git" - }, + }, "scripts": { - "test:acceptance:wait_for_app": "echo 'Waiting for app to be accessible' && while (! curl -s -o /dev/null localhost:3013/status) do sleep 1; done", - "test:acceptance:run": "mocha --recursive --reporter spec --timeout 15000", - "test:acceptance:dir": "npm -q run test:acceptance:wait_for_app && npm -q run test:acceptance:run -- $@", - "test:acceptance": "npm run compile:app && npm run compile:acceptance_tests && npm run test:acceptance:_run -- $@", - "compile:test:acceptance": "coffee -o test/acceptance/js -c test/acceptance/coffee", - "compile:test:smoke": "coffee -o test/smoke/js -c test/smoke/coffee", - "compile:app": "coffee -o app/js -c app/coffee && coffee -c app.coffee", - "compile": "npm run compile:app && npm run compile:test:acceptance && npm run compile:test:smoke", - "start": "npm run compile:app && node app.js", - "test:acceptance:_run": "mocha --recursive --reporter spec --timeout 15000 --exit $@ test/acceptance/js", - "test:unit:_run": "mocha --recursive --reporter spec $@ test/unit/js", - "test:unit": "npm run compile:app && npm run compile:unit_tests && npm run test:unit:_run -- $@", - "compile:unit_tests": "[ -e test/unit ] && coffee -o test/unit/js -c test/unit/coffee || echo 'No unit tests to compile'", - "compile:acceptance_tests": "[ -e test/acceptance ] && coffee -o test/acceptance/js -c test/acceptance/coffee || echo 'No acceptance tests to compile'", - "compile:all": "npm run compile:app && npm run compile:unit_tests && npm run compile:acceptance_tests", + "test:acceptance:wait_for_app": "echo 'Waiting for app to be accessible' && while (! curl -s -o /dev/null localhost:3013/status) do sleep 1; done", + "test:acceptance:run": "mocha --recursive --reporter spec --timeout 15000", + "test:acceptance:dir": "npm -q run test:acceptance:wait_for_app && npm -q run test:acceptance:run -- $@", + "test:acceptance": "npm run compile:app && npm run compile:acceptance_tests && npm run test:acceptance:_run -- $@", + "compile:test:acceptance": "coffee -o test/acceptance/js -c test/acceptance/coffee", + "compile:test:smoke": "coffee -o test/smoke/js -c test/smoke/coffee", + "compile:app": "coffee -o app/js -c app/coffee && coffee -c app.coffee", + "compile": "npm run compile:app && npm run compile:test:acceptance && npm run compile:test:smoke", + "start": "npm run compile:app && node app.js", + "test:acceptance:_run": "mocha --recursive --reporter spec --timeout 15000 --exit $@ test/acceptance/js", + "test:unit:_run": "mocha --recursive --reporter spec $@ test/unit/js", + "test:unit": "npm run compile:app && npm run compile:unit_tests && npm run test:unit:_run -- $@", + "compile:unit_tests": "[ -e test/unit ] && coffee -o test/unit/js -c test/unit/coffee || echo 'No unit tests to compile'", + "compile:acceptance_tests": "[ -e test/acceptance ] && coffee -o test/acceptance/js -c test/acceptance/coffee || echo 'No acceptance tests to compile'", + "compile:all": "npm run compile:app && npm run compile:unit_tests && npm run compile:acceptance_tests", "nodemon": "nodemon --config nodemon.json" - }, - "author": "James Allen ", + }, + "author": "James Allen ", "dependencies": { - "async": "0.2.9", - "body-parser": "^1.2.0", - "dockerode": "^2.5.3", - "express": "^4.2.0", - "fs-extra": "^0.16.3", - "grunt-mkdir": "^1.0.0", - "heapdump": "^0.3.5", - "lockfile": "^1.0.3", - "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.4", - "lynx": "0.0.11", - "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.5.0", - "mkdirp": "0.3.5", - "mysql": "2.6.2", - "request": "^2.21.0", - "sequelize": "^2.1.3", - "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", - "smoke-test-sharelatex": "git+https://github.com/sharelatex/smoke-test-sharelatex.git#v0.2.0", - "sqlite3": "^3.1.13", - "underscore": "^1.8.2", - "v8-profiler": "^5.2.4", + "async": "0.2.9", + "body-parser": "^1.2.0", + "dockerode": "^2.5.3", + "express": "^4.2.0", + "fs-extra": "^0.16.3", + "grunt-mkdir": "^1.0.0", + "heapdump": "^0.3.5", + "lockfile": "^1.0.3", + "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.4", + "lynx": "0.0.11", + "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.5.0", + "mkdirp": "0.3.5", + "mysql": "2.6.2", + "request": "^2.21.0", + "sequelize": "^2.1.3", + "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", + "smoke-test-sharelatex": "git+https://github.com/sharelatex/smoke-test-sharelatex.git#v0.2.0", + "sqlite3": "^3.1.13", + "underscore": "^1.8.2", + "v8-profiler": "^5.2.4", "wrench": "~1.5.4" - }, + }, "devDependencies": { - "mocha": "^4.0.1", - "coffee-script": "1.6.0", - "chai": "~1.8.1", - "sinon": "~1.7.3", - "grunt": "~0.4.2", - "grunt-contrib-coffee": "~0.7.0", - "grunt-contrib-clean": "~0.5.0", - "grunt-shell": "~0.6.1", - "grunt-mocha-test": "~0.8.1", - "sandboxed-module": "~0.3.0", - "timekeeper": "0.0.4", - "grunt-execute": "^0.1.5", - "bunyan": "^0.22.1", + "mocha": "^4.0.1", + "coffee-script": "1.6.0", + "chai": "~1.8.1", + "sinon": "~1.7.3", + "grunt": "~0.4.2", + "grunt-contrib-coffee": "~0.7.0", + "grunt-contrib-clean": "~0.5.0", + "grunt-shell": "~0.6.1", + "grunt-mocha-test": "~0.8.1", + "sandboxed-module": "~0.3.0", + "timekeeper": "0.0.4", + "grunt-execute": "^0.1.5", + "bunyan": "^0.22.1", "grunt-bunyan": "^0.5.0" } } From 12b13d61997c78ce7188a29d6be7517d0cf62ad8 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Fri, 16 Feb 2018 15:45:47 +0000 Subject: [PATCH 005/118] mount app as volume in docker container for local tests change to overrides --- base.yml => docker-compose-overrides.yml | 14 +++++++++++++- docker-compose.yml | 7 +++++-- 2 files changed, 18 insertions(+), 3 deletions(-) rename base.yml => docker-compose-overrides.yml (50%) diff --git a/base.yml b/docker-compose-overrides.yml similarity index 50% rename from base.yml rename to docker-compose-overrides.yml index ef5f44a..3d64975 100644 --- a/base.yml +++ b/docker-compose-overrides.yml @@ -1,7 +1,7 @@ version: "2" services: - base: + dev: environment: TEXLIVE_IMAGE: quay.io/sharelatex/texlive-full:2017.1 TEXLIVE_IMAGE_USER: root @@ -12,3 +12,15 @@ services: - /var/run/docker.sock:/var/run/docker.sock - ./docker-runner:/app/node_modules/docker-runner-sharelatex - ./compiles:/app/compiles + + ci: + environment: + TEXLIVE_IMAGE: quay.io/sharelatex/texlive-full:2017.1 + TEXLIVE_IMAGE_USER: root + SHARELATEX_CONFIG: /app/config/settings.defaults.coffee + COMMAND_RUNNER: docker-runner-sharelatex + COMPILES_HOST_DIR: $PWD/compiles + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - ./docker-runner:/app/node_modules/docker-runner-sharelatex + - ./compiles:/app/compiles \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 8c612e5..6126666 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,9 +15,12 @@ services: test_acceptance: build: . + volumes: + - .:/app + working_dir: /app extends: - file: base.yml - service: base + file: docker-compose-overrides.yml + service: dev environment: REDIS_HOST: redis MONGO_HOST: mongo From d698cc318fc7bf2e73e7d1b687487c145313d633 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Wed, 21 Feb 2018 15:42:51 +0000 Subject: [PATCH 006/118] updateded build scripts --- Jenkinsfile | 36 +++++++++++++++++-- Makefile | 7 +--- ...overrides.yml => docker-compose-config.yml | 0 docker-compose.ci.yml | 12 +++++-- docker-compose.yml | 6 ++-- kube.yaml | 8 ++--- 6 files changed, 50 insertions(+), 19 deletions(-) rename docker-compose-overrides.yml => docker-compose-config.yml (100%) diff --git a/Jenkinsfile b/Jenkinsfile index bc9ba01..326dce5 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -9,9 +9,34 @@ pipeline { } stages { - stage('Build') { + stage('Install') { + agent { + docker { + image 'node:6.13.0' + args "-v /var/lib/jenkins/.npm:/tmp/.npm -e HOME=/tmp" + reuseNode true + } + } steps { - sh 'make build' + // we need to disable logallrefupdates, else git clones + // during the npm install will require git to lookup the + // user id which does not exist in the container's + // /etc/passwd file, causing the clone to fail. + sh 'git config --global core.logallrefupdates false' + sh 'rm -rf node_modules' + sh 'npm install && npm rebuild' + } + } + + stage('Compile') { + agent { + docker { + image 'node:6.13.0' + reuseNode true + } + } + steps { + sh 'npm run compile:all' } } @@ -29,7 +54,12 @@ pipeline { stage('Package and publish build') { steps { - sh 'make publish' + sh 'echo ${BUILD_NUMBER} > build_number.txt' + sh 'touch build.tar.gz' // Avoid tar warning about files changing during read + sh 'tar -czf build.tar.gz --exclude=build.tar.gz --exclude-vcs .' + withAWS(credentials:'S3_CI_BUILDS_AWS_KEYS', region:"${S3_REGION_BUILD_ARTEFACTS}") { + s3Upload(file:'build.tar.gz', bucket:"${S3_BUCKET_BUILD_ARTEFACTS}", path:"${JOB_NAME}/${BUILD_NUMBER}.tar.gz") + } } } diff --git a/Makefile b/Makefile index 828e8f8..89066f5 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.0.3 +# Version: 1.1.0 BUILD_NUMBER ?= local BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) @@ -28,10 +28,5 @@ test_acceptance: test_clean # clear the database before each acceptance test run test_clean: $(DOCKER_COMPOSE) down -t 0 -build: - docker build --pull --tag quay.io/sharelatex/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) . - -publish: - docker push quay.io/sharelatex/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) .PHONY: clean test test_unit test_acceptance test_clean build publish diff --git a/docker-compose-overrides.yml b/docker-compose-config.yml similarity index 100% rename from docker-compose-overrides.yml rename to docker-compose-config.yml diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index 695ad3c..00740d4 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -1,17 +1,23 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.0.3 +# Version: 1.1.0 version: "2" services: test_unit: - image: quay.io/sharelatex/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER + image: node:6.13.0 + volumes: + - .:/app + working_dir: /app entrypoint: npm run test:unit:_run test_acceptance: - image: quay.io/sharelatex/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER + image: node:6.13.0 + volumes: + - .:/app + working_dir: /app environment: REDIS_HOST: redis MONGO_HOST: mongo diff --git a/docker-compose.yml b/docker-compose.yml index 6126666..c259abc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.0.3 +# Version: 1.1.0 version: "2" @@ -14,12 +14,12 @@ services: entrypoint: npm run test:unit test_acceptance: - build: . + image: node:6.13.0 volumes: - .:/app working_dir: /app extends: - file: docker-compose-overrides.yml + file: docker-compose-config.yml service: dev environment: REDIS_HOST: redis diff --git a/kube.yaml b/kube.yaml index 44a562d..d3fb042 100644 --- a/kube.yaml +++ b/kube.yaml @@ -6,9 +6,9 @@ metadata: spec: type: LoadBalancer ports: - - port: 3009 + - port: 80 protocol: TCP - targetPort: 3009 + targetPort: 80 selector: run: clsi --- @@ -26,12 +26,12 @@ spec: spec: containers: - name: clsi - image: gcr.io/henry-terraform-admin/clsi:6e0c79688030117a9c27d0fc01d1271e6ac3dd3e + image: gcr.io/henry-terraform-admin/clsi imagePullPolicy: Always readinessProbe: httpGet: path: status - port: 3009 + port: 80 periodSeconds: 5 initialDelaySeconds: 0 failureThreshold: 3 From 8f6db5baff9e4f53fbf06b3053cb63448ddd6c60 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Mon, 26 Feb 2018 11:56:19 +0000 Subject: [PATCH 007/118] tests pass under app user --- Dockerfile | 25 ++++++++++++++++++------- Jenkinsfile | 36 +++--------------------------------- Makefile | 5 +++++ docker-compose.ci.yml | 10 ++-------- docker-compose.yml | 2 +- install_deps.sh | 6 +++++- 6 files changed, 34 insertions(+), 50 deletions(-) diff --git a/Dockerfile b/Dockerfile index e195f96..881852d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:6.13.0 +FROM node:6.13.0 as app COPY ./ /app @@ -6,11 +6,22 @@ WORKDIR /app RUN npm install + +RUN npm run compile:all + +FROM node:6.13.0 + +COPY --from=app /app /app + +WORKDIR /app + + +# All app and node_modules will be owned by root. +# The app will run as the 'app' user, and so not have write permissions +# on any files it doesn't need. +RUN useradd --user-group --create-home --home-dir /app --shell /bin/bash app + RUN [ -e ./install_deps.sh ] && ./install_deps.sh -RUN npm run compile - -ENV SHARELATEX_CONFIG /app/config/settings.production.coffee -ENV NODE_ENV production - -CMD ["node","/app/app.js"] +USER app +CMD ["node","app.js"] diff --git a/Jenkinsfile b/Jenkinsfile index 326dce5..bc9ba01 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -9,34 +9,9 @@ pipeline { } stages { - stage('Install') { - agent { - docker { - image 'node:6.13.0' - args "-v /var/lib/jenkins/.npm:/tmp/.npm -e HOME=/tmp" - reuseNode true - } - } + stage('Build') { steps { - // we need to disable logallrefupdates, else git clones - // during the npm install will require git to lookup the - // user id which does not exist in the container's - // /etc/passwd file, causing the clone to fail. - sh 'git config --global core.logallrefupdates false' - sh 'rm -rf node_modules' - sh 'npm install && npm rebuild' - } - } - - stage('Compile') { - agent { - docker { - image 'node:6.13.0' - reuseNode true - } - } - steps { - sh 'npm run compile:all' + sh 'make build' } } @@ -54,12 +29,7 @@ pipeline { stage('Package and publish build') { steps { - sh 'echo ${BUILD_NUMBER} > build_number.txt' - sh 'touch build.tar.gz' // Avoid tar warning about files changing during read - sh 'tar -czf build.tar.gz --exclude=build.tar.gz --exclude-vcs .' - withAWS(credentials:'S3_CI_BUILDS_AWS_KEYS', region:"${S3_REGION_BUILD_ARTEFACTS}") { - s3Upload(file:'build.tar.gz', bucket:"${S3_BUCKET_BUILD_ARTEFACTS}", path:"${JOB_NAME}/${BUILD_NUMBER}.tar.gz") - } + sh 'make publish' } } diff --git a/Makefile b/Makefile index 89066f5..b20654c 100644 --- a/Makefile +++ b/Makefile @@ -28,5 +28,10 @@ test_acceptance: test_clean # clear the database before each acceptance test run test_clean: $(DOCKER_COMPOSE) down -t 0 +build: + docker build --pull --tag quay.io/sharelatex/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) . + +publish: + docker push quay.io/sharelatex/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) .PHONY: clean test test_unit test_acceptance test_clean build publish diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index 00740d4..cbe15fe 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -7,17 +7,11 @@ version: "2" services: test_unit: - image: node:6.13.0 - volumes: - - .:/app - working_dir: /app + image: quay.io/sharelatex/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER entrypoint: npm run test:unit:_run test_acceptance: - image: node:6.13.0 - volumes: - - .:/app - working_dir: /app + image: quay.io/sharelatex/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER environment: REDIS_HOST: redis MONGO_HOST: mongo diff --git a/docker-compose.yml b/docker-compose.yml index c259abc..cf90742 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,7 +14,7 @@ services: entrypoint: npm run test:unit test_acceptance: - image: node:6.13.0 + build: . volumes: - .:/app working_dir: /app diff --git a/install_deps.sh b/install_deps.sh index 49bdc5c..3caa1c7 100755 --- a/install_deps.sh +++ b/install_deps.sh @@ -1,4 +1,8 @@ /bin/sh wget -qO- https://get.docker.com/ | sh -apt-get install poppler-utils vim ghostscript --yes +apt-get install poppler-utils ghostscript --yes npm rebuild +usermod -aG docker app + +touch /var/run/docker.sock +chown root:docker /var/run/docker.sock From b8c22f4d74e56f03e6897c3cad9a90649e603e62 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Mon, 26 Feb 2018 14:29:30 +0000 Subject: [PATCH 008/118] wip, docker container is correctly created --- app/coffee/CompileManager.coffee | 40 +- config/settings.defaults.coffee | 2 - docker-compose.yml | 11 + .../coffee/BrokenLatexFileTests.coffee | 83 ++-- .../coffee/DeleteOldFilesTest.coffee | 60 +-- .../coffee/ExampleDocumentTests.coffee | 196 ++++----- .../coffee/SimpleLatexFileTests.coffee | 70 ++-- test/acceptance/coffee/SynctexTests.coffee | 16 +- test/acceptance/coffee/TimeoutTests.coffee | 52 +-- test/acceptance/coffee/UrlCachingTests.coffee | 378 +++++++++--------- test/acceptance/coffee/WordcountTests.coffee | 72 ++-- 11 files changed, 495 insertions(+), 485 deletions(-) diff --git a/app/coffee/CompileManager.coffee b/app/coffee/CompileManager.coffee index d3d319a..a6df6dd 100644 --- a/app/coffee/CompileManager.coffee +++ b/app/coffee/CompileManager.coffee @@ -41,7 +41,7 @@ module.exports = CompileManager = doCompile: (request, callback = (error, outputFiles) ->) -> compileDir = getCompileDir(request.project_id, request.user_id) - console.log("doCompile",compileDir ) + # console.log("doCompile", compileDir) timer = new Metrics.Timer("write-to-disk") logger.log project_id: request.project_id, user_id: request.user_id, "syncing resources to disk" @@ -206,9 +206,9 @@ module.exports = CompileManager = base_dir = Settings.path.synctexBaseDir(compileName) file_path = base_dir + "/" + file_name compileDir = getCompileDir(project_id, user_id) - synctex_path = Path.join(compileDir, "output.pdf") + synctex_path = "$COMPILE_DIR/output.pdf" command = ["code", synctex_path, file_path, line, column] - CompileManager._runSynctex command, (error, stdout) -> + CompileManager._runSynctex project_id, user_id, command, (error, stdout) -> return callback(error) if error? if stdout.toLowerCase().indexOf("warning") == -1 logType = "log" @@ -221,9 +221,9 @@ module.exports = CompileManager = compileName = getCompileName(project_id, user_id) base_dir = Settings.path.synctexBaseDir(compileName) compileDir = getCompileDir(project_id, user_id) - synctex_path = Path.join(compileDir, "output.pdf") - logger.log({base_dir, project_id, synctex_path}, "base diiir") - CompileManager._runSynctex ["pdf", synctex_path, page, h, v], (error, stdout) -> + synctex_path = "$COMPILE_DIR/output.pdf" + command = ["pdf", synctex_path, page, h, v] + CompileManager._runSynctex project_id, user_id, command, (error, stdout) -> return callback(error) if error? logger.log project_id: project_id, user_id:user_id, page: page, h: h, v:v, stdout: stdout, "synctex pdf output" callback null, CompileManager._parseSynctexFromPdfOutput(stdout, base_dir) @@ -242,20 +242,22 @@ module.exports = CompileManager = return callback(new Error("not a file")) if not stats?.isFile() callback() - _runSynctex: (args, callback = (error, stdout) ->) -> - bin_path = Path.resolve(__dirname + "/../../bin/synctex") + _runSynctex: (project_id, user_id, command, callback = (error, stdout) ->) -> seconds = 1000 - outputFilePath = args[1] - CompileManager._checkFileExists outputFilePath, (error) -> - return callback(error) if error? - if Settings.clsi?.synctexCommandWrapper? - [bin_path, args] = Settings.clsi?.synctexCommandWrapper bin_path, args - logger.log({bin_path, args}, "synctex being run") - child_process.execFile bin_path, args, timeout: 10 * seconds, (error, stdout, stderr) -> - if error? - logger.err err:error, args:args, "error running synctex" - return callback(error) - callback(null, stdout) + + #this is a hack, only works for docker runner + command.unshift("/opt/synctex") + directory = getCompileDir(project_id, user_id) + timeout = 10 * 1000 + compileName = getCompileName(project_id, user_id) + console.log command, "_runSynctex" + + CommandRunner.run compileName, command, directory, Settings.clsi.docker.image, timeout, {}, (error, stdout) -> + console.log("synctex run", stdout) + if error? + logger.err err:error, command:command, "error running synctex" + return callback(error) + callback(null, stdout) _parseSynctexFromCodeOutput: (output) -> results = [] diff --git a/config/settings.defaults.coffee b/config/settings.defaults.coffee index 9b1e4be..d09784d 100644 --- a/config/settings.defaults.coffee +++ b/config/settings.defaults.coffee @@ -41,5 +41,3 @@ if process.env["COMMAND_RUNNER"] checkProjectsIntervalMs: 10 * 60 * 1000 module.exports.path.sandboxedCompilesHostDir = process.env["COMPILES_HOST_DIR"] - -console.log module.exports \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index cf90742..090c360 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -29,6 +29,17 @@ services: - mongo entrypoint: npm run test:acceptance + synctex: + image: quay.io/sharelatex/texlive-full:2017.1 + volumes: + - ~/Projects/sharelatex-dev-environment/clsi/compiles/564c29f884179:/compile + - ./bin/synctex:/opt/synctex + + command: + /opt/synctex pdf /compile/output.pdf 1 100 200 + + + redis: image: redis diff --git a/test/acceptance/coffee/BrokenLatexFileTests.coffee b/test/acceptance/coffee/BrokenLatexFileTests.coffee index a54df55..755f28e 100644 --- a/test/acceptance/coffee/BrokenLatexFileTests.coffee +++ b/test/acceptance/coffee/BrokenLatexFileTests.coffee @@ -1,49 +1,48 @@ -Client = require "./helpers/Client" -request = require "request" -require("chai").should() -ClsiApp = require "./helpers/ClsiApp" +# Client = require "./helpers/Client" +# request = require "request" +# require("chai").should() +# ClsiApp = require "./helpers/ClsiApp" -describe "Broken LaTeX file", -> - before (done)-> - @broken_request = - resources: [ - path: "main.tex" - content: ''' - \\documentclass{articl % :( - \\begin{documen % :( - Broken - \\end{documen % :( - ''' - ] - @correct_request = - resources: [ - path: "main.tex" - content: ''' - \\documentclass{article} - \\begin{document} - Hello world - \\end{document} - ''' - ] - ClsiApp.ensureRunning done +# describe "Broken LaTeX file", -> +# before (done)-> +# @broken_request = +# resources: [ +# path: "main.tex" +# content: ''' +# \\documentclass{articl % :( +# \\begin{documen % :( +# Broken +# \\end{documen % :( +# ''' +# ] +# @correct_request = +# resources: [ +# path: "main.tex" +# content: ''' +# \\documentclass{article} +# \\begin{document} +# Hello world +# \\end{document} +# ''' +# ] +# ClsiApp.ensureRunning done - describe "on first run", -> - before (done) -> - @project_id = Client.randomId() - Client.compile @project_id, @broken_request, (@error, @res, @body) => done() +# describe "on first run", -> +# before (done) -> +# @project_id = Client.randomId() +# Client.compile @project_id, @broken_request, (@error, @res, @body) => done() - it "should return a failure status", -> - console.log(@error, @res, @body) - @body.compile.status.should.equal "failure" +# it "should return a failure status", -> +# @body.compile.status.should.equal "failure" - describe "on second run", -> - before (done) -> - @project_id = Client.randomId() - Client.compile @project_id, @correct_request, () => - Client.compile @project_id, @broken_request, (@error, @res, @body) => - done() +# describe "on second run", -> +# before (done) -> +# @project_id = Client.randomId() +# Client.compile @project_id, @correct_request, () => +# Client.compile @project_id, @broken_request, (@error, @res, @body) => +# done() - it "should return a failure status", -> - @body.compile.status.should.equal "failure" +# it "should return a failure status", -> +# @body.compile.status.should.equal "failure" diff --git a/test/acceptance/coffee/DeleteOldFilesTest.coffee b/test/acceptance/coffee/DeleteOldFilesTest.coffee index 1cb6776..750f5f9 100644 --- a/test/acceptance/coffee/DeleteOldFilesTest.coffee +++ b/test/acceptance/coffee/DeleteOldFilesTest.coffee @@ -1,36 +1,36 @@ -Client = require "./helpers/Client" -request = require "request" -require("chai").should() -ClsiApp = require "./helpers/ClsiApp" +# Client = require "./helpers/Client" +# request = require "request" +# require("chai").should() +# ClsiApp = require "./helpers/ClsiApp" -describe "Deleting Old Files", -> - before (done)-> - @request = - resources: [ - path: "main.tex" - content: ''' - \\documentclass{article} - \\begin{document} - Hello world - \\end{document} - ''' - ] - ClsiApp.ensureRunning done +# describe "Deleting Old Files", -> +# before (done)-> +# @request = +# resources: [ +# path: "main.tex" +# content: ''' +# \\documentclass{article} +# \\begin{document} +# Hello world +# \\end{document} +# ''' +# ] +# ClsiApp.ensureRunning done - describe "on first run", -> - before (done) -> - @project_id = Client.randomId() - Client.compile @project_id, @request, (@error, @res, @body) => done() +# describe "on first run", -> +# before (done) -> +# @project_id = Client.randomId() +# Client.compile @project_id, @request, (@error, @res, @body) => done() - it "should return a success status", -> - @body.compile.status.should.equal "success" +# it "should return a success status", -> +# @body.compile.status.should.equal "success" - describe "after file has been deleted", -> - before (done) -> - @request.resources = [] - Client.compile @project_id, @request, (@error, @res, @body) => - done() +# describe "after file has been deleted", -> +# before (done) -> +# @request.resources = [] +# Client.compile @project_id, @request, (@error, @res, @body) => +# done() - it "should return a failure status", -> - @body.compile.status.should.equal "failure" +# it "should return a failure status", -> +# @body.compile.status.should.equal "failure" diff --git a/test/acceptance/coffee/ExampleDocumentTests.coffee b/test/acceptance/coffee/ExampleDocumentTests.coffee index 5ec6111..1a1a0a0 100644 --- a/test/acceptance/coffee/ExampleDocumentTests.coffee +++ b/test/acceptance/coffee/ExampleDocumentTests.coffee @@ -1,114 +1,114 @@ -Client = require "./helpers/Client" -request = require "request" -require("chai").should() -fs = require "fs" -ChildProcess = require "child_process" -ClsiApp = require "./helpers/ClsiApp" +# Client = require "./helpers/Client" +# request = require "request" +# require("chai").should() +# fs = require "fs" +# ChildProcess = require "child_process" +# ClsiApp = require "./helpers/ClsiApp" -fixturePath = (path) -> __dirname + "/../fixtures/" + path +# fixturePath = (path) -> __dirname + "/../fixtures/" + path -try - fs.mkdirSync(fixturePath("tmp")) -catch e +# try +# fs.mkdirSync(fixturePath("tmp")) +# catch e -convertToPng = (pdfPath, pngPath, callback = (error) ->) -> - command = "convert #{fixturePath(pdfPath)} #{fixturePath(pngPath)}" - convert = ChildProcess.exec command - stdout = "" - convert.stdout.on "data", (chunk) -> console.log "STDOUT", chunk.toString() - convert.stderr.on "data", (chunk) -> console.log "STDERR", chunk.toString() - convert.on "exit", () -> - callback() +# convertToPng = (pdfPath, pngPath, callback = (error) ->) -> +# command = "convert #{fixturePath(pdfPath)} #{fixturePath(pngPath)}" +# convert = ChildProcess.exec command +# stdout = "" +# convert.stdout.on "data", (chunk) -> console.log "STDOUT", chunk.toString() +# convert.stderr.on "data", (chunk) -> console.log "STDERR", chunk.toString() +# convert.on "exit", () -> +# callback() -compare = (originalPath, generatedPath, callback = (error, same) ->) -> - diff_file = "#{fixturePath(generatedPath)}-diff.png" - proc = ChildProcess.exec "compare -metric mae #{fixturePath(originalPath)} #{fixturePath(generatedPath)} #{diff_file}" - stderr = "" - proc.stderr.on "data", (chunk) -> stderr += chunk - proc.on "exit", () -> - if stderr.trim() == "0 (0)" - fs.unlink diff_file # remove output diff if test matches expected image - callback null, true - else - console.log "compare result", stderr - callback null, false +# compare = (originalPath, generatedPath, callback = (error, same) ->) -> +# diff_file = "#{fixturePath(generatedPath)}-diff.png" +# proc = ChildProcess.exec "compare -metric mae #{fixturePath(originalPath)} #{fixturePath(generatedPath)} #{diff_file}" +# stderr = "" +# proc.stderr.on "data", (chunk) -> stderr += chunk +# proc.on "exit", () -> +# if stderr.trim() == "0 (0)" +# fs.unlink diff_file # remove output diff if test matches expected image +# callback null, true +# else +# console.log "compare result", stderr +# callback null, false -checkPdfInfo = (pdfPath, callback = (error, output) ->) -> - proc = ChildProcess.exec "pdfinfo #{fixturePath(pdfPath)}" - stdout = "" - proc.stdout.on "data", (chunk) -> stdout += chunk - proc.stderr.on "data", (chunk) -> console.log "STDERR", chunk.toString() - proc.on "exit", () -> - if stdout.match(/Optimized:\s+yes/) - callback null, true - else - console.log "pdfinfo result", stdout - callback null, false +# checkPdfInfo = (pdfPath, callback = (error, output) ->) -> +# proc = ChildProcess.exec "pdfinfo #{fixturePath(pdfPath)}" +# stdout = "" +# proc.stdout.on "data", (chunk) -> stdout += chunk +# proc.stderr.on "data", (chunk) -> console.log "STDERR", chunk.toString() +# proc.on "exit", () -> +# if stdout.match(/Optimized:\s+yes/) +# callback null, true +# else +# console.log "pdfinfo result", stdout +# callback null, false -compareMultiplePages = (project_id, callback = (error) ->) -> - compareNext = (page_no, callback) -> - path = "tmp/#{project_id}-source-#{page_no}.png" - fs.stat fixturePath(path), (error, stat) -> - if error? - callback() - else - compare "tmp/#{project_id}-source-#{page_no}.png", "tmp/#{project_id}-generated-#{page_no}.png", (error, same) => - throw error if error? - same.should.equal true - compareNext page_no + 1, callback - compareNext 0, callback +# compareMultiplePages = (project_id, callback = (error) ->) -> +# compareNext = (page_no, callback) -> +# path = "tmp/#{project_id}-source-#{page_no}.png" +# fs.stat fixturePath(path), (error, stat) -> +# if error? +# callback() +# else +# compare "tmp/#{project_id}-source-#{page_no}.png", "tmp/#{project_id}-generated-#{page_no}.png", (error, same) => +# throw error if error? +# same.should.equal true +# compareNext page_no + 1, callback +# compareNext 0, callback -comparePdf = (project_id, example_dir, callback = (error) ->) -> - convertToPng "tmp/#{project_id}.pdf", "tmp/#{project_id}-generated.png", (error) => - throw error if error? - convertToPng "examples/#{example_dir}/output.pdf", "tmp/#{project_id}-source.png", (error) => - throw error if error? - fs.stat fixturePath("tmp/#{project_id}-source-0.png"), (error, stat) => - if error? - compare "tmp/#{project_id}-source.png", "tmp/#{project_id}-generated.png", (error, same) => - throw error if error? - same.should.equal true - callback() - else - compareMultiplePages project_id, (error) -> - throw error if error? - callback() +# comparePdf = (project_id, example_dir, callback = (error) ->) -> +# convertToPng "tmp/#{project_id}.pdf", "tmp/#{project_id}-generated.png", (error) => +# throw error if error? +# convertToPng "examples/#{example_dir}/output.pdf", "tmp/#{project_id}-source.png", (error) => +# throw error if error? +# fs.stat fixturePath("tmp/#{project_id}-source-0.png"), (error, stat) => +# if error? +# compare "tmp/#{project_id}-source.png", "tmp/#{project_id}-generated.png", (error, same) => +# throw error if error? +# same.should.equal true +# callback() +# else +# compareMultiplePages project_id, (error) -> +# throw error if error? +# callback() -downloadAndComparePdf = (project_id, example_dir, url, callback = (error) ->) -> - writeStream = fs.createWriteStream(fixturePath("tmp/#{project_id}.pdf")) - request.get(url).pipe(writeStream) - writeStream.on "close", () => - checkPdfInfo "tmp/#{project_id}.pdf", (error, optimised) => - throw error if error? - optimised.should.equal true - comparePdf project_id, example_dir, callback +# downloadAndComparePdf = (project_id, example_dir, url, callback = (error) ->) -> +# writeStream = fs.createWriteStream(fixturePath("tmp/#{project_id}.pdf")) +# request.get(url).pipe(writeStream) +# writeStream.on "close", () => +# checkPdfInfo "tmp/#{project_id}.pdf", (error, optimised) => +# throw error if error? +# optimised.should.equal true +# comparePdf project_id, example_dir, callback -Client.runServer(4242, fixturePath("examples")) +# Client.runServer(4242, fixturePath("examples")) -describe "Example Documents", -> - before (done) -> - ChildProcess.exec("rm test/acceptance/fixtures/tmp/*").on "exit", () -> - ClsiApp.ensureRunning done +# describe "Example Documents", -> +# before (done) -> +# ChildProcess.exec("rm test/acceptance/fixtures/tmp/*").on "exit", () -> +# ClsiApp.ensureRunning done - for example_dir in fs.readdirSync fixturePath("examples") - do (example_dir) -> - describe example_dir, -> - before -> - @project_id = Client.randomId() + "_" + example_dir +# for example_dir in fs.readdirSync fixturePath("examples") +# do (example_dir) -> +# describe example_dir, -> +# before -> +# @project_id = Client.randomId() + "_" + example_dir - it "should generate the correct pdf", (done) -> - Client.compileDirectory @project_id, fixturePath("examples"), example_dir, 4242, (error, res, body) => - if error || body?.compile?.status is "failure" - console.log "DEBUG: error", error, "body", JSON.stringify(body) - pdf = Client.getOutputFile body, "pdf" - downloadAndComparePdf(@project_id, example_dir, pdf.url, done) +# it "should generate the correct pdf", (done) -> +# Client.compileDirectory @project_id, fixturePath("examples"), example_dir, 4242, (error, res, body) => +# if error || body?.compile?.status is "failure" +# console.log "DEBUG: error", error, "body", JSON.stringify(body) +# pdf = Client.getOutputFile body, "pdf" +# downloadAndComparePdf(@project_id, example_dir, pdf.url, done) - it "should generate the correct pdf on the second run as well", (done) -> - Client.compileDirectory @project_id, fixturePath("examples"), example_dir, 4242, (error, res, body) => - if error || body?.compile?.status is "failure" - console.log "DEBUG: error", error, "body", JSON.stringify(body) - pdf = Client.getOutputFile body, "pdf" - downloadAndComparePdf(@project_id, example_dir, pdf.url, done) +# it "should generate the correct pdf on the second run as well", (done) -> +# Client.compileDirectory @project_id, fixturePath("examples"), example_dir, 4242, (error, res, body) => +# if error || body?.compile?.status is "failure" +# console.log "DEBUG: error", error, "body", JSON.stringify(body) +# pdf = Client.getOutputFile body, "pdf" +# downloadAndComparePdf(@project_id, example_dir, pdf.url, done) diff --git a/test/acceptance/coffee/SimpleLatexFileTests.coffee b/test/acceptance/coffee/SimpleLatexFileTests.coffee index 95b667b..ae21c61 100644 --- a/test/acceptance/coffee/SimpleLatexFileTests.coffee +++ b/test/acceptance/coffee/SimpleLatexFileTests.coffee @@ -1,41 +1,41 @@ -Client = require "./helpers/Client" -request = require "request" -require("chai").should() -ClsiApp = require "./helpers/ClsiApp" +# Client = require "./helpers/Client" +# request = require "request" +# require("chai").should() +# ClsiApp = require "./helpers/ClsiApp" -describe "Simple LaTeX file", -> - before (done) -> - @project_id = Client.randomId() - @request = - resources: [ - path: "main.tex" - content: ''' - \\documentclass{article} - \\begin{document} - Hello world - \\end{document} - ''' - ] - ClsiApp.ensureRunning => - Client.compile @project_id, @request, (@error, @res, @body) => done() +# describe "Simple LaTeX file", -> +# before (done) -> +# @project_id = Client.randomId() +# @request = +# resources: [ +# path: "main.tex" +# content: ''' +# \\documentclass{article} +# \\begin{document} +# Hello world +# \\end{document} +# ''' +# ] +# ClsiApp.ensureRunning => +# Client.compile @project_id, @request, (@error, @res, @body) => done() - it "should return the PDF", -> - pdf = Client.getOutputFile(@body, "pdf") - pdf.type.should.equal "pdf" +# it "should return the PDF", -> +# pdf = Client.getOutputFile(@body, "pdf") +# pdf.type.should.equal "pdf" - it "should return the log", -> - log = Client.getOutputFile(@body, "log") - log.type.should.equal "log" +# it "should return the log", -> +# log = Client.getOutputFile(@body, "log") +# log.type.should.equal "log" - it "should provide the pdf for download", (done) -> - pdf = Client.getOutputFile(@body, "pdf") - request.get pdf.url, (error, res, body) -> - res.statusCode.should.equal 200 - done() +# it "should provide the pdf for download", (done) -> +# pdf = Client.getOutputFile(@body, "pdf") +# request.get pdf.url, (error, res, body) -> +# res.statusCode.should.equal 200 +# done() - it "should provide the log for download", (done) -> - log = Client.getOutputFile(@body, "pdf") - request.get log.url, (error, res, body) -> - res.statusCode.should.equal 200 - done() +# it "should provide the log for download", (done) -> +# log = Client.getOutputFile(@body, "pdf") +# request.get log.url, (error, res, body) -> +# res.statusCode.should.equal 200 +# done() diff --git a/test/acceptance/coffee/SynctexTests.coffee b/test/acceptance/coffee/SynctexTests.coffee index c6adcf2..cbd22a7 100644 --- a/test/acceptance/coffee/SynctexTests.coffee +++ b/test/acceptance/coffee/SynctexTests.coffee @@ -20,14 +20,14 @@ describe "Syncing", -> ClsiApp.ensureRunning => Client.compile @project_id, @request, (@error, @res, @body) => done() - describe "from code to pdf", -> - it "should return the correct location", (done) -> - Client.syncFromCode @project_id, "main.tex", 3, 5, (error, pdfPositions) -> - throw error if error? - expect(pdfPositions).to.deep.equal( - pdf: [ { page: 1, h: 133.77, v: 134.76, height: 6.92, width: 343.71 } ] - ) - done() + # describe "from code to pdf", -> + # it "should return the correct location", (done) -> + # Client.syncFromCode @project_id, "main.tex", 3, 5, (error, pdfPositions) -> + # throw error if error? + # expect(pdfPositions).to.deep.equal( + # pdf: [ { page: 1, h: 133.77, v: 134.76, height: 6.92, width: 343.71 } ] + # ) + # done() describe "from pdf to code", -> it "should return the correct location", (done) -> diff --git a/test/acceptance/coffee/TimeoutTests.coffee b/test/acceptance/coffee/TimeoutTests.coffee index 3861487..2a0f693 100644 --- a/test/acceptance/coffee/TimeoutTests.coffee +++ b/test/acceptance/coffee/TimeoutTests.coffee @@ -1,31 +1,31 @@ -Client = require "./helpers/Client" -request = require "request" -require("chai").should() -ClsiApp = require "./helpers/ClsiApp" +# Client = require "./helpers/Client" +# request = require "request" +# require("chai").should() +# ClsiApp = require "./helpers/ClsiApp" -describe "Timed out compile", -> - before (done) -> - @request = - options: - timeout: 1 #seconds - resources: [ - path: "main.tex" - content: ''' - \\documentclass{article} - \\begin{document} - Hello world - \\input{|"sleep 10"} - \\end{document} - ''' - ] - @project_id = Client.randomId() - ClsiApp.ensureRunning => - Client.compile @project_id, @request, (@error, @res, @body) => done() +# describe "Timed out compile", -> +# before (done) -> +# @request = +# options: +# timeout: 1 #seconds +# resources: [ +# path: "main.tex" +# content: ''' +# \\documentclass{article} +# \\begin{document} +# Hello world +# \\input{|"sleep 10"} +# \\end{document} +# ''' +# ] +# @project_id = Client.randomId() +# ClsiApp.ensureRunning => +# Client.compile @project_id, @request, (@error, @res, @body) => done() - it "should return a timeout error", -> - @body.compile.error.should.equal "container timed out" +# it "should return a timeout error", -> +# @body.compile.error.should.equal "container timed out" - it "should return a timedout status", -> - @body.compile.status.should.equal "timedout" +# it "should return a timedout status", -> +# @body.compile.status.should.equal "timedout" diff --git a/test/acceptance/coffee/UrlCachingTests.coffee b/test/acceptance/coffee/UrlCachingTests.coffee index cef7446..89f67c2 100644 --- a/test/acceptance/coffee/UrlCachingTests.coffee +++ b/test/acceptance/coffee/UrlCachingTests.coffee @@ -1,222 +1,222 @@ -Client = require "./helpers/Client" -request = require "request" -require("chai").should() -sinon = require "sinon" -ClsiApp = require "./helpers/ClsiApp" +# Client = require "./helpers/Client" +# request = require "request" +# require("chai").should() +# sinon = require "sinon" +# ClsiApp = require "./helpers/ClsiApp" -host = "localhost" +# host = "localhost" -Server = - run: () -> - express = require "express" - app = express() +# Server = +# run: () -> +# express = require "express" +# app = express() - staticServer = express.static __dirname + "/../fixtures/" - app.get "/:random_id/*", (req, res, next) => - @getFile(req.url) - req.url = "/" + req.params[0] - staticServer(req, res, next) +# staticServer = express.static __dirname + "/../fixtures/" +# app.get "/:random_id/*", (req, res, next) => +# @getFile(req.url) +# req.url = "/" + req.params[0] +# staticServer(req, res, next) - app.listen 31415, host +# app.listen 31415, host - getFile: () -> +# getFile: () -> - randomId: () -> - Math.random().toString(16).slice(2) +# randomId: () -> +# Math.random().toString(16).slice(2) -Server.run() +# Server.run() -describe "Url Caching", -> - describe "Downloading an image for the first time", -> - before (done) -> - @project_id = Client.randomId() - @file = "#{Server.randomId()}/lion.png" - @request = - resources: [{ - path: "main.tex" - content: ''' - \\documentclass{article} - \\usepackage{graphicx} - \\begin{document} - \\includegraphics{lion.png} - \\end{document} - ''' - }, { - path: "lion.png" - url: "http://#{host}:31415/#{@file}" - }] +# describe "Url Caching", -> +# describe "Downloading an image for the first time", -> +# before (done) -> +# @project_id = Client.randomId() +# @file = "#{Server.randomId()}/lion.png" +# @request = +# resources: [{ +# path: "main.tex" +# content: ''' +# \\documentclass{article} +# \\usepackage{graphicx} +# \\begin{document} +# \\includegraphics{lion.png} +# \\end{document} +# ''' +# }, { +# path: "lion.png" +# url: "http://#{host}:31415/#{@file}" +# }] - sinon.spy Server, "getFile" - ClsiApp.ensureRunning => - Client.compile @project_id, @request, (@error, @res, @body) => done() +# sinon.spy Server, "getFile" +# ClsiApp.ensureRunning => +# Client.compile @project_id, @request, (@error, @res, @body) => done() - afterEach -> - Server.getFile.restore() +# afterEach -> +# Server.getFile.restore() - it "should download the image", -> - Server.getFile - .calledWith("/" + @file) - .should.equal true +# it "should download the image", -> +# Server.getFile +# .calledWith("/" + @file) +# .should.equal true - describe "When an image is in the cache and the last modified date is unchanged", -> - before (done) -> - @project_id = Client.randomId() - @file = "#{Server.randomId()}/lion.png" - @request = - resources: [{ - path: "main.tex" - content: ''' - \\documentclass{article} - \\usepackage{graphicx} - \\begin{document} - \\includegraphics{lion.png} - \\end{document} - ''' - }, @image_resource = { - path: "lion.png" - url: "http://#{host}:31415/#{@file}" - modified: Date.now() - }] +# describe "When an image is in the cache and the last modified date is unchanged", -> +# before (done) -> +# @project_id = Client.randomId() +# @file = "#{Server.randomId()}/lion.png" +# @request = +# resources: [{ +# path: "main.tex" +# content: ''' +# \\documentclass{article} +# \\usepackage{graphicx} +# \\begin{document} +# \\includegraphics{lion.png} +# \\end{document} +# ''' +# }, @image_resource = { +# path: "lion.png" +# url: "http://#{host}:31415/#{@file}" +# modified: Date.now() +# }] - Client.compile @project_id, @request, (@error, @res, @body) => - sinon.spy Server, "getFile" - Client.compile @project_id, @request, (@error, @res, @body) => - done() +# Client.compile @project_id, @request, (@error, @res, @body) => +# sinon.spy Server, "getFile" +# Client.compile @project_id, @request, (@error, @res, @body) => +# done() - after -> - Server.getFile.restore() +# after -> +# Server.getFile.restore() - it "should not download the image again", -> - Server.getFile.called.should.equal false +# it "should not download the image again", -> +# Server.getFile.called.should.equal false - describe "When an image is in the cache and the last modified date is advanced", -> - before (done) -> - @project_id = Client.randomId() - @file = "#{Server.randomId()}/lion.png" - @request = - resources: [{ - path: "main.tex" - content: ''' - \\documentclass{article} - \\usepackage{graphicx} - \\begin{document} - \\includegraphics{lion.png} - \\end{document} - ''' - }, @image_resource = { - path: "lion.png" - url: "http://#{host}:31415/#{@file}" - modified: @last_modified = Date.now() - }] +# describe "When an image is in the cache and the last modified date is advanced", -> +# before (done) -> +# @project_id = Client.randomId() +# @file = "#{Server.randomId()}/lion.png" +# @request = +# resources: [{ +# path: "main.tex" +# content: ''' +# \\documentclass{article} +# \\usepackage{graphicx} +# \\begin{document} +# \\includegraphics{lion.png} +# \\end{document} +# ''' +# }, @image_resource = { +# path: "lion.png" +# url: "http://#{host}:31415/#{@file}" +# modified: @last_modified = Date.now() +# }] - Client.compile @project_id, @request, (@error, @res, @body) => - sinon.spy Server, "getFile" - @image_resource.modified = new Date(@last_modified + 3000) - Client.compile @project_id, @request, (@error, @res, @body) => - done() +# Client.compile @project_id, @request, (@error, @res, @body) => +# sinon.spy Server, "getFile" +# @image_resource.modified = new Date(@last_modified + 3000) +# Client.compile @project_id, @request, (@error, @res, @body) => +# done() - afterEach -> - Server.getFile.restore() +# afterEach -> +# Server.getFile.restore() - it "should download the image again", -> - Server.getFile.called.should.equal true +# it "should download the image again", -> +# Server.getFile.called.should.equal true - describe "When an image is in the cache and the last modified date is further in the past", -> - before (done) -> - @project_id = Client.randomId() - @file = "#{Server.randomId()}/lion.png" - @request = - resources: [{ - path: "main.tex" - content: ''' - \\documentclass{article} - \\usepackage{graphicx} - \\begin{document} - \\includegraphics{lion.png} - \\end{document} - ''' - }, @image_resource = { - path: "lion.png" - url: "http://#{host}:31415/#{@file}" - modified: @last_modified = Date.now() - }] +# describe "When an image is in the cache and the last modified date is further in the past", -> +# before (done) -> +# @project_id = Client.randomId() +# @file = "#{Server.randomId()}/lion.png" +# @request = +# resources: [{ +# path: "main.tex" +# content: ''' +# \\documentclass{article} +# \\usepackage{graphicx} +# \\begin{document} +# \\includegraphics{lion.png} +# \\end{document} +# ''' +# }, @image_resource = { +# path: "lion.png" +# url: "http://#{host}:31415/#{@file}" +# modified: @last_modified = Date.now() +# }] - Client.compile @project_id, @request, (@error, @res, @body) => - sinon.spy Server, "getFile" - @image_resource.modified = new Date(@last_modified - 3000) - Client.compile @project_id, @request, (@error, @res, @body) => - done() +# Client.compile @project_id, @request, (@error, @res, @body) => +# sinon.spy Server, "getFile" +# @image_resource.modified = new Date(@last_modified - 3000) +# Client.compile @project_id, @request, (@error, @res, @body) => +# done() - afterEach -> - Server.getFile.restore() +# afterEach -> +# Server.getFile.restore() - it "should not download the image again", -> - Server.getFile.called.should.equal false +# it "should not download the image again", -> +# Server.getFile.called.should.equal false - describe "When an image is in the cache and the last modified date is not specified", -> - before (done) -> - @project_id = Client.randomId() - @file = "#{Server.randomId()}/lion.png" - @request = - resources: [{ - path: "main.tex" - content: ''' - \\documentclass{article} - \\usepackage{graphicx} - \\begin{document} - \\includegraphics{lion.png} - \\end{document} - ''' - }, @image_resource = { - path: "lion.png" - url: "http://#{host}:31415/#{@file}" - modified: @last_modified = Date.now() - }] +# describe "When an image is in the cache and the last modified date is not specified", -> +# before (done) -> +# @project_id = Client.randomId() +# @file = "#{Server.randomId()}/lion.png" +# @request = +# resources: [{ +# path: "main.tex" +# content: ''' +# \\documentclass{article} +# \\usepackage{graphicx} +# \\begin{document} +# \\includegraphics{lion.png} +# \\end{document} +# ''' +# }, @image_resource = { +# path: "lion.png" +# url: "http://#{host}:31415/#{@file}" +# modified: @last_modified = Date.now() +# }] - Client.compile @project_id, @request, (@error, @res, @body) => - sinon.spy Server, "getFile" - delete @image_resource.modified - Client.compile @project_id, @request, (@error, @res, @body) => - done() +# Client.compile @project_id, @request, (@error, @res, @body) => +# sinon.spy Server, "getFile" +# delete @image_resource.modified +# Client.compile @project_id, @request, (@error, @res, @body) => +# done() - afterEach -> - Server.getFile.restore() +# afterEach -> +# Server.getFile.restore() - it "should download the image again", -> - Server.getFile.called.should.equal true +# it "should download the image again", -> +# Server.getFile.called.should.equal true - describe "After clearing the cache", -> - before (done) -> - @project_id = Client.randomId() - @file = "#{Server.randomId()}/lion.png" - @request = - resources: [{ - path: "main.tex" - content: ''' - \\documentclass{article} - \\usepackage{graphicx} - \\begin{document} - \\includegraphics{lion.png} - \\end{document} - ''' - }, @image_resource = { - path: "lion.png" - url: "http://#{host}:31415/#{@file}" - modified: @last_modified = Date.now() - }] +# describe "After clearing the cache", -> +# before (done) -> +# @project_id = Client.randomId() +# @file = "#{Server.randomId()}/lion.png" +# @request = +# resources: [{ +# path: "main.tex" +# content: ''' +# \\documentclass{article} +# \\usepackage{graphicx} +# \\begin{document} +# \\includegraphics{lion.png} +# \\end{document} +# ''' +# }, @image_resource = { +# path: "lion.png" +# url: "http://#{host}:31415/#{@file}" +# modified: @last_modified = Date.now() +# }] - Client.compile @project_id, @request, (error) => - throw error if error? - Client.clearCache @project_id, (error, res, body) => - throw error if error? - sinon.spy Server, "getFile" - Client.compile @project_id, @request, (@error, @res, @body) => - done() +# Client.compile @project_id, @request, (error) => +# throw error if error? +# Client.clearCache @project_id, (error, res, body) => +# throw error if error? +# sinon.spy Server, "getFile" +# Client.compile @project_id, @request, (@error, @res, @body) => +# done() - afterEach -> - Server.getFile.restore() +# afterEach -> +# Server.getFile.restore() - it "should download the image again", -> - Server.getFile.called.should.equal true +# it "should download the image again", -> +# Server.getFile.called.should.equal true diff --git a/test/acceptance/coffee/WordcountTests.coffee b/test/acceptance/coffee/WordcountTests.coffee index abace06..c41ba9a 100644 --- a/test/acceptance/coffee/WordcountTests.coffee +++ b/test/acceptance/coffee/WordcountTests.coffee @@ -1,38 +1,38 @@ -Client = require "./helpers/Client" -request = require "request" -require("chai").should() -expect = require("chai").expect -path = require("path") -fs = require("fs") -ClsiApp = require "./helpers/ClsiApp" +# Client = require "./helpers/Client" +# request = require "request" +# require("chai").should() +# expect = require("chai").expect +# path = require("path") +# fs = require("fs") +# ClsiApp = require "./helpers/ClsiApp" -describe "Syncing", -> - before (done) -> - @request = - resources: [ - path: "main.tex" - content: fs.readFileSync(path.join(__dirname,"../fixtures/naugty_strings.txt"),"utf-8") - ] - @project_id = Client.randomId() - ClsiApp.ensureRunning => - Client.compile @project_id, @request, (@error, @res, @body) => done() +# describe "Syncing", -> +# before (done) -> +# @request = +# resources: [ +# path: "main.tex" +# content: fs.readFileSync(path.join(__dirname,"../fixtures/naugty_strings.txt"),"utf-8") +# ] +# @project_id = Client.randomId() +# ClsiApp.ensureRunning => +# Client.compile @project_id, @request, (@error, @res, @body) => done() - describe "wordcount file", -> - it "should return wordcount info", (done) -> - Client.wordcount @project_id, "main.tex", (error, result) -> - throw error if error? - expect(result).to.deep.equal( - texcount: { - encode: "utf8" - textWords: 2281 - headWords: 2 - outside: 0 - headers: 2 - elements: 0 - mathInline: 6 - mathDisplay: 0 - errors: 0 - messages: "" - } - ) - done() +# describe "wordcount file", -> +# it "should return wordcount info", (done) -> +# Client.wordcount @project_id, "main.tex", (error, result) -> +# throw error if error? +# expect(result).to.deep.equal( +# texcount: { +# encode: "utf8" +# textWords: 2281 +# headWords: 2 +# outside: 0 +# headers: 2 +# elements: 0 +# mathInline: 6 +# mathDisplay: 0 +# errors: 0 +# messages: "" +# } +# ) +# done() From 70f016af1f48c9f3b4fca8262e9c8d758cdc7918 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Thu, 1 Mar 2018 13:55:55 +0000 Subject: [PATCH 009/118] unit tests pass, acceptence fail uncomment tests --- Dockerfile | 2 +- app/coffee/CommandRunner.coffee | 2 - app/coffee/CompileManager.coffee | 17 +- app/coffee/LatexRunner.coffee | 1 - app/coffee/ResourceWriter.coffee | 1 - config/settings.defaults.coffee | 5 + docker-compose-config.yml | 3 +- docker-compose.yml | 21 +- docker-runner | 2 +- install_deps.sh | 2 +- .../coffee/BrokenLatexFileTests.coffee | 82 ++++---- .../coffee/DeleteOldFilesTest.coffee | 60 +++--- .../coffee/ExampleDocumentTests.coffee | 195 +++++++++--------- .../coffee/SimpleLatexFileTests.coffee | 71 +++---- test/acceptance/coffee/SynctexTests.coffee | 28 +-- test/acceptance/coffee/TimeoutTests.coffee | 1 - test/acceptance/coffee/helpers/ClsiApp.coffee | 2 +- .../unit/coffee/CompileControllerTests.coffee | 2 +- test/unit/coffee/CompileManagerTests.coffee | 56 +++-- 19 files changed, 290 insertions(+), 263 deletions(-) diff --git a/Dockerfile b/Dockerfile index 881852d..f317201 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,5 +23,5 @@ RUN useradd --user-group --create-home --home-dir /app --shell /bin/bash app RUN [ -e ./install_deps.sh ] && ./install_deps.sh -USER app +# USER app CMD ["node","app.js"] diff --git a/app/coffee/CommandRunner.coffee b/app/coffee/CommandRunner.coffee index 969b55f..f47af00 100644 --- a/app/coffee/CommandRunner.coffee +++ b/app/coffee/CommandRunner.coffee @@ -5,9 +5,7 @@ logger.info "using standard command runner" module.exports = CommandRunner = run: (project_id, command, directory, image, timeout, environment, callback = (error) ->) -> - console.log("Command runner", directory) command = (arg.replace('$COMPILE_DIR', directory) for arg in command) - console.log("Command runner 2", command) logger.log project_id: project_id, command: command, directory: directory, "running command" logger.warn "timeouts and sandboxing are not enabled with CommandRunner" diff --git a/app/coffee/CompileManager.coffee b/app/coffee/CompileManager.coffee index a6df6dd..0db15e5 100644 --- a/app/coffee/CompileManager.coffee +++ b/app/coffee/CompileManager.coffee @@ -41,8 +41,6 @@ module.exports = CompileManager = doCompile: (request, callback = (error, outputFiles) ->) -> compileDir = getCompileDir(request.project_id, request.user_id) - # console.log("doCompile", compileDir) - timer = new Metrics.Timer("write-to-disk") logger.log project_id: request.project_id, user_id: request.user_id, "syncing resources to disk" ResourceWriter.syncResourcesToDisk request, compileDir, (error, resourceList) -> @@ -206,7 +204,7 @@ module.exports = CompileManager = base_dir = Settings.path.synctexBaseDir(compileName) file_path = base_dir + "/" + file_name compileDir = getCompileDir(project_id, user_id) - synctex_path = "$COMPILE_DIR/output.pdf" + synctex_path = "#{base_dir}/output.pdf" command = ["code", synctex_path, file_path, line, column] CompileManager._runSynctex project_id, user_id, command, (error, stdout) -> return callback(error) if error? @@ -219,9 +217,9 @@ module.exports = CompileManager = syncFromPdf: (project_id, user_id, page, h, v, callback = (error, filePositions) ->) -> compileName = getCompileName(project_id, user_id) - base_dir = Settings.path.synctexBaseDir(compileName) compileDir = getCompileDir(project_id, user_id) - synctex_path = "$COMPILE_DIR/output.pdf" + base_dir = Settings.path.synctexBaseDir(compileName) + synctex_path = "#{base_dir}/output.pdf" command = ["pdf", synctex_path, page, h, v] CompileManager._runSynctex project_id, user_id, command, (error, stdout) -> return callback(error) if error? @@ -245,19 +243,16 @@ module.exports = CompileManager = _runSynctex: (project_id, user_id, command, callback = (error, stdout) ->) -> seconds = 1000 - #this is a hack, only works for docker runner command.unshift("/opt/synctex") + directory = getCompileDir(project_id, user_id) timeout = 10 * 1000 compileName = getCompileName(project_id, user_id) - console.log command, "_runSynctex" - - CommandRunner.run compileName, command, directory, Settings.clsi.docker.image, timeout, {}, (error, stdout) -> - console.log("synctex run", stdout) + CommandRunner.run compileName, command, directory, Settings.clsi.docker.image, timeout, {}, (error, output) -> if error? logger.err err:error, command:command, "error running synctex" return callback(error) - callback(null, stdout) + callback(null, output.stdout) _parseSynctexFromCodeOutput: (output) -> results = [] diff --git a/app/coffee/LatexRunner.coffee b/app/coffee/LatexRunner.coffee index 11e71e5..6a5a4f6 100644 --- a/app/coffee/LatexRunner.coffee +++ b/app/coffee/LatexRunner.coffee @@ -8,7 +8,6 @@ ProcessTable = {} # table of currently running jobs (pids or docker container n module.exports = LatexRunner = runLatex: (project_id, options, callback = (error) ->) -> - console.log("LatexRunner", options.directory) {directory, mainFile, compiler, timeout, image, environment} = options compiler ||= "pdflatex" timeout ||= 60000 # milliseconds diff --git a/app/coffee/ResourceWriter.coffee b/app/coffee/ResourceWriter.coffee index 66cfbfa..0b6aef5 100644 --- a/app/coffee/ResourceWriter.coffee +++ b/app/coffee/ResourceWriter.coffee @@ -109,7 +109,6 @@ module.exports = ResourceWriter = callback() _writeResourceToDisk: (project_id, resource, basePath, callback = (error) ->) -> - console.log("_writeResourceToDisk", basePath, resource.path) ResourceWriter.checkPath basePath, resource.path, (error, path) -> return callback(error) if error? mkdirp Path.dirname(path), (error) -> diff --git a/config/settings.defaults.coffee b/config/settings.defaults.coffee index d09784d..2f2348b 100644 --- a/config/settings.defaults.coffee +++ b/config/settings.defaults.coffee @@ -40,4 +40,9 @@ if process.env["COMMAND_RUNNER"] expireProjectAfterIdleMs: 24 * 60 * 60 * 1000 checkProjectsIntervalMs: 10 * 60 * 1000 + module.exports.path.synctexBaseDir = -> "/compile" + module.exports.path.sandboxedCompilesHostDir = process.env["COMPILES_HOST_DIR"] + + #TODO this can be deleted once module is merged in + module.exports.path.synctexBinHostPath = process.env["SYNCTEX_BIN_HOST_PATH"] diff --git a/docker-compose-config.yml b/docker-compose-config.yml index 3d64975..16897c4 100644 --- a/docker-compose-config.yml +++ b/docker-compose-config.yml @@ -8,6 +8,7 @@ services: SHARELATEX_CONFIG: /app/config/settings.defaults.coffee COMMAND_RUNNER: docker-runner-sharelatex COMPILES_HOST_DIR: $PWD/compiles + SYNCTEX_BIN_HOST_PATH: $PWD/bin/synctex volumes: - /var/run/docker.sock:/var/run/docker.sock - ./docker-runner:/app/node_modules/docker-runner-sharelatex @@ -21,6 +22,6 @@ services: COMMAND_RUNNER: docker-runner-sharelatex COMPILES_HOST_DIR: $PWD/compiles volumes: - - /var/run/docker.sock:/var/run/docker.sock + - /var/run/docker.sock:/var/run/docker.sock:rw - ./docker-runner:/app/node_modules/docker-runner-sharelatex - ./compiles:/app/compiles \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 090c360..0ed0b54 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -32,12 +32,29 @@ services: synctex: image: quay.io/sharelatex/texlive-full:2017.1 volumes: - - ~/Projects/sharelatex-dev-environment/clsi/compiles/564c29f884179:/compile + - ~/Projects/sharelatex-dev-environment/clsi/compiles/cd749215b3512:/compile - ./bin/synctex:/opt/synctex - command: + entrypoint: /opt/synctex pdf /compile/output.pdf 1 100 200 + # /opt/synctex code -h + # /opt/synctex code /compile/main.tex ./main.tex 3 5 + # ls -al + app: + build: . + volumes: + - .:/app + working_dir: /app + extends: + file: docker-compose-config.yml + service: dev + environment: + REDIS_HOST: redis + MONGO_HOST: mongo + depends_on: + - redis + - mongo redis: diff --git a/docker-runner b/docker-runner index 9a732b2..65ad621 160000 --- a/docker-runner +++ b/docker-runner @@ -1 +1 @@ -Subproject commit 9a732b2594f014a722f0c16150cf848b9512430f +Subproject commit 65ad62116fb1ba4fca978a6d1d6b89bf195e5166 diff --git a/install_deps.sh b/install_deps.sh index 3caa1c7..bde142a 100755 --- a/install_deps.sh +++ b/install_deps.sh @@ -1,6 +1,6 @@ /bin/sh wget -qO- https://get.docker.com/ | sh -apt-get install poppler-utils ghostscript --yes +apt-get install poppler-utils vim ghostscript --yes npm rebuild usermod -aG docker app diff --git a/test/acceptance/coffee/BrokenLatexFileTests.coffee b/test/acceptance/coffee/BrokenLatexFileTests.coffee index 755f28e..8ab4344 100644 --- a/test/acceptance/coffee/BrokenLatexFileTests.coffee +++ b/test/acceptance/coffee/BrokenLatexFileTests.coffee @@ -1,48 +1,48 @@ -# Client = require "./helpers/Client" -# request = require "request" -# require("chai").should() -# ClsiApp = require "./helpers/ClsiApp" +Client = require "./helpers/Client" +request = require "request" +require("chai").should() +ClsiApp = require "./helpers/ClsiApp" -# describe "Broken LaTeX file", -> -# before (done)-> -# @broken_request = -# resources: [ -# path: "main.tex" -# content: ''' -# \\documentclass{articl % :( -# \\begin{documen % :( -# Broken -# \\end{documen % :( -# ''' -# ] -# @correct_request = -# resources: [ -# path: "main.tex" -# content: ''' -# \\documentclass{article} -# \\begin{document} -# Hello world -# \\end{document} -# ''' -# ] -# ClsiApp.ensureRunning done +describe "Broken LaTeX file", -> + before (done)-> + @broken_request = + resources: [ + path: "main.tex" + content: ''' + \\documentclass{articl % :( + \\begin{documen % :( + Broken + \\end{documen % :( + ''' + ] + @correct_request = + resources: [ + path: "main.tex" + content: ''' + \\documentclass{article} + \\begin{document} + Hello world + \\end{document} + ''' + ] + ClsiApp.ensureRunning done -# describe "on first run", -> -# before (done) -> -# @project_id = Client.randomId() -# Client.compile @project_id, @broken_request, (@error, @res, @body) => done() + describe "on first run", -> + before (done) -> + @project_id = Client.randomId() + Client.compile @project_id, @broken_request, (@error, @res, @body) => done() -# it "should return a failure status", -> -# @body.compile.status.should.equal "failure" + it "should return a failure status", -> + @body.compile.status.should.equal "failure" -# describe "on second run", -> -# before (done) -> -# @project_id = Client.randomId() -# Client.compile @project_id, @correct_request, () => -# Client.compile @project_id, @broken_request, (@error, @res, @body) => -# done() + describe "on second run", -> + before (done) -> + @project_id = Client.randomId() + Client.compile @project_id, @correct_request, () => + Client.compile @project_id, @broken_request, (@error, @res, @body) => + done() -# it "should return a failure status", -> -# @body.compile.status.should.equal "failure" + it "should return a failure status", -> + @body.compile.status.should.equal "failure" diff --git a/test/acceptance/coffee/DeleteOldFilesTest.coffee b/test/acceptance/coffee/DeleteOldFilesTest.coffee index 750f5f9..1cb6776 100644 --- a/test/acceptance/coffee/DeleteOldFilesTest.coffee +++ b/test/acceptance/coffee/DeleteOldFilesTest.coffee @@ -1,36 +1,36 @@ -# Client = require "./helpers/Client" -# request = require "request" -# require("chai").should() -# ClsiApp = require "./helpers/ClsiApp" +Client = require "./helpers/Client" +request = require "request" +require("chai").should() +ClsiApp = require "./helpers/ClsiApp" -# describe "Deleting Old Files", -> -# before (done)-> -# @request = -# resources: [ -# path: "main.tex" -# content: ''' -# \\documentclass{article} -# \\begin{document} -# Hello world -# \\end{document} -# ''' -# ] -# ClsiApp.ensureRunning done +describe "Deleting Old Files", -> + before (done)-> + @request = + resources: [ + path: "main.tex" + content: ''' + \\documentclass{article} + \\begin{document} + Hello world + \\end{document} + ''' + ] + ClsiApp.ensureRunning done -# describe "on first run", -> -# before (done) -> -# @project_id = Client.randomId() -# Client.compile @project_id, @request, (@error, @res, @body) => done() + describe "on first run", -> + before (done) -> + @project_id = Client.randomId() + Client.compile @project_id, @request, (@error, @res, @body) => done() -# it "should return a success status", -> -# @body.compile.status.should.equal "success" + it "should return a success status", -> + @body.compile.status.should.equal "success" -# describe "after file has been deleted", -> -# before (done) -> -# @request.resources = [] -# Client.compile @project_id, @request, (@error, @res, @body) => -# done() + describe "after file has been deleted", -> + before (done) -> + @request.resources = [] + Client.compile @project_id, @request, (@error, @res, @body) => + done() -# it "should return a failure status", -> -# @body.compile.status.should.equal "failure" + it "should return a failure status", -> + @body.compile.status.should.equal "failure" diff --git a/test/acceptance/coffee/ExampleDocumentTests.coffee b/test/acceptance/coffee/ExampleDocumentTests.coffee index 1a1a0a0..ec56979 100644 --- a/test/acceptance/coffee/ExampleDocumentTests.coffee +++ b/test/acceptance/coffee/ExampleDocumentTests.coffee @@ -1,114 +1,113 @@ -# Client = require "./helpers/Client" -# request = require "request" -# require("chai").should() -# fs = require "fs" -# ChildProcess = require "child_process" -# ClsiApp = require "./helpers/ClsiApp" +Client = require "./helpers/Client" +request = require "request" +require("chai").should() +fs = require "fs" +ChildProcess = require "child_process" +ClsiApp = require "./helpers/ClsiApp" -# fixturePath = (path) -> __dirname + "/../fixtures/" + path +fixturePath = (path) -> __dirname + "/../fixtures/" + path -# try -# fs.mkdirSync(fixturePath("tmp")) -# catch e +try + fs.mkdirSync(fixturePath("tmp")) +catch e -# convertToPng = (pdfPath, pngPath, callback = (error) ->) -> -# command = "convert #{fixturePath(pdfPath)} #{fixturePath(pngPath)}" -# convert = ChildProcess.exec command -# stdout = "" -# convert.stdout.on "data", (chunk) -> console.log "STDOUT", chunk.toString() -# convert.stderr.on "data", (chunk) -> console.log "STDERR", chunk.toString() -# convert.on "exit", () -> -# callback() +convertToPng = (pdfPath, pngPath, callback = (error) ->) -> + command = "convert #{fixturePath(pdfPath)} #{fixturePath(pngPath)}" + convert = ChildProcess.exec command + stdout = "" + convert.stdout.on "data", (chunk) -> console.log "STDOUT", chunk.toString() + convert.stderr.on "data", (chunk) -> console.log "STDERR", chunk.toString() + convert.on "exit", () -> + callback() -# compare = (originalPath, generatedPath, callback = (error, same) ->) -> -# diff_file = "#{fixturePath(generatedPath)}-diff.png" -# proc = ChildProcess.exec "compare -metric mae #{fixturePath(originalPath)} #{fixturePath(generatedPath)} #{diff_file}" -# stderr = "" -# proc.stderr.on "data", (chunk) -> stderr += chunk -# proc.on "exit", () -> -# if stderr.trim() == "0 (0)" -# fs.unlink diff_file # remove output diff if test matches expected image -# callback null, true -# else -# console.log "compare result", stderr -# callback null, false +compare = (originalPath, generatedPath, callback = (error, same) ->) -> + diff_file = "#{fixturePath(generatedPath)}-diff.png" + proc = ChildProcess.exec "compare -metric mae #{fixturePath(originalPath)} #{fixturePath(generatedPath)} #{diff_file}" + stderr = "" + proc.stderr.on "data", (chunk) -> stderr += chunk + proc.on "exit", () -> + if stderr.trim() == "0 (0)" + fs.unlink diff_file # remove output diff if test matches expected image + callback null, true + else + console.log "compare result", stderr + callback null, false -# checkPdfInfo = (pdfPath, callback = (error, output) ->) -> -# proc = ChildProcess.exec "pdfinfo #{fixturePath(pdfPath)}" -# stdout = "" -# proc.stdout.on "data", (chunk) -> stdout += chunk -# proc.stderr.on "data", (chunk) -> console.log "STDERR", chunk.toString() -# proc.on "exit", () -> -# if stdout.match(/Optimized:\s+yes/) -# callback null, true -# else -# console.log "pdfinfo result", stdout -# callback null, false +checkPdfInfo = (pdfPath, callback = (error, output) ->) -> + proc = ChildProcess.exec "pdfinfo #{fixturePath(pdfPath)}" + stdout = "" + proc.stdout.on "data", (chunk) -> stdout += chunk + proc.stderr.on "data", (chunk) -> console.log "STDERR", chunk.toString() + proc.on "exit", () -> + if stdout.match(/Optimized:\s+yes/) + callback null, true + else + callback null, false -# compareMultiplePages = (project_id, callback = (error) ->) -> -# compareNext = (page_no, callback) -> -# path = "tmp/#{project_id}-source-#{page_no}.png" -# fs.stat fixturePath(path), (error, stat) -> -# if error? -# callback() -# else -# compare "tmp/#{project_id}-source-#{page_no}.png", "tmp/#{project_id}-generated-#{page_no}.png", (error, same) => -# throw error if error? -# same.should.equal true -# compareNext page_no + 1, callback -# compareNext 0, callback +compareMultiplePages = (project_id, callback = (error) ->) -> + compareNext = (page_no, callback) -> + path = "tmp/#{project_id}-source-#{page_no}.png" + fs.stat fixturePath(path), (error, stat) -> + if error? + callback() + else + compare "tmp/#{project_id}-source-#{page_no}.png", "tmp/#{project_id}-generated-#{page_no}.png", (error, same) => + throw error if error? + same.should.equal true + compareNext page_no + 1, callback + compareNext 0, callback -# comparePdf = (project_id, example_dir, callback = (error) ->) -> -# convertToPng "tmp/#{project_id}.pdf", "tmp/#{project_id}-generated.png", (error) => -# throw error if error? -# convertToPng "examples/#{example_dir}/output.pdf", "tmp/#{project_id}-source.png", (error) => -# throw error if error? -# fs.stat fixturePath("tmp/#{project_id}-source-0.png"), (error, stat) => -# if error? -# compare "tmp/#{project_id}-source.png", "tmp/#{project_id}-generated.png", (error, same) => -# throw error if error? -# same.should.equal true -# callback() -# else -# compareMultiplePages project_id, (error) -> -# throw error if error? -# callback() +comparePdf = (project_id, example_dir, callback = (error) ->) -> + convertToPng "tmp/#{project_id}.pdf", "tmp/#{project_id}-generated.png", (error) => + throw error if error? + convertToPng "examples/#{example_dir}/output.pdf", "tmp/#{project_id}-source.png", (error) => + throw error if error? + fs.stat fixturePath("tmp/#{project_id}-source-0.png"), (error, stat) => + if error? + compare "tmp/#{project_id}-source.png", "tmp/#{project_id}-generated.png", (error, same) => + throw error if error? + same.should.equal true + callback() + else + compareMultiplePages project_id, (error) -> + throw error if error? + callback() -# downloadAndComparePdf = (project_id, example_dir, url, callback = (error) ->) -> -# writeStream = fs.createWriteStream(fixturePath("tmp/#{project_id}.pdf")) -# request.get(url).pipe(writeStream) -# writeStream.on "close", () => -# checkPdfInfo "tmp/#{project_id}.pdf", (error, optimised) => -# throw error if error? -# optimised.should.equal true -# comparePdf project_id, example_dir, callback +downloadAndComparePdf = (project_id, example_dir, url, callback = (error) ->) -> + writeStream = fs.createWriteStream(fixturePath("tmp/#{project_id}.pdf")) + request.get(url).pipe(writeStream) + writeStream.on "close", () => + checkPdfInfo "tmp/#{project_id}.pdf", (error, optimised) => + throw error if error? + optimised.should.equal true + comparePdf project_id, example_dir, callback -# Client.runServer(4242, fixturePath("examples")) +Client.runServer(4242, fixturePath("examples")) -# describe "Example Documents", -> -# before (done) -> -# ChildProcess.exec("rm test/acceptance/fixtures/tmp/*").on "exit", () -> -# ClsiApp.ensureRunning done +describe "Example Documents", -> + before (done) -> + ChildProcess.exec("rm test/acceptance/fixtures/tmp/*").on "exit", () -> + ClsiApp.ensureRunning done -# for example_dir in fs.readdirSync fixturePath("examples") -# do (example_dir) -> -# describe example_dir, -> -# before -> -# @project_id = Client.randomId() + "_" + example_dir + for example_dir in fs.readdirSync fixturePath("examples") + do (example_dir) -> + describe example_dir, -> + before -> + @project_id = Client.randomId() + "_" + example_dir -# it "should generate the correct pdf", (done) -> -# Client.compileDirectory @project_id, fixturePath("examples"), example_dir, 4242, (error, res, body) => -# if error || body?.compile?.status is "failure" -# console.log "DEBUG: error", error, "body", JSON.stringify(body) -# pdf = Client.getOutputFile body, "pdf" -# downloadAndComparePdf(@project_id, example_dir, pdf.url, done) + it "should generate the correct pdf", (done) -> + Client.compileDirectory @project_id, fixturePath("examples"), example_dir, 4242, (error, res, body) => + if error || body?.compile?.status is "failure" + console.log "DEBUG: error", error, "body", JSON.stringify(body) + pdf = Client.getOutputFile body, "pdf" + downloadAndComparePdf(@project_id, example_dir, pdf.url, done) -# it "should generate the correct pdf on the second run as well", (done) -> -# Client.compileDirectory @project_id, fixturePath("examples"), example_dir, 4242, (error, res, body) => -# if error || body?.compile?.status is "failure" -# console.log "DEBUG: error", error, "body", JSON.stringify(body) -# pdf = Client.getOutputFile body, "pdf" -# downloadAndComparePdf(@project_id, example_dir, pdf.url, done) + it "should generate the correct pdf on the second run as well", (done) -> + Client.compileDirectory @project_id, fixturePath("examples"), example_dir, 4242, (error, res, body) => + if error || body?.compile?.status is "failure" + console.log "DEBUG: error", error, "body", JSON.stringify(body) + pdf = Client.getOutputFile body, "pdf" + downloadAndComparePdf(@project_id, example_dir, pdf.url, done) diff --git a/test/acceptance/coffee/SimpleLatexFileTests.coffee b/test/acceptance/coffee/SimpleLatexFileTests.coffee index ae21c61..0d3337a 100644 --- a/test/acceptance/coffee/SimpleLatexFileTests.coffee +++ b/test/acceptance/coffee/SimpleLatexFileTests.coffee @@ -1,41 +1,42 @@ -# Client = require "./helpers/Client" -# request = require "request" -# require("chai").should() -# ClsiApp = require "./helpers/ClsiApp" +Client = require "./helpers/Client" +request = require "request" +require("chai").should() +ClsiApp = require "./helpers/ClsiApp" -# describe "Simple LaTeX file", -> -# before (done) -> -# @project_id = Client.randomId() -# @request = -# resources: [ -# path: "main.tex" -# content: ''' -# \\documentclass{article} -# \\begin{document} -# Hello world -# \\end{document} -# ''' -# ] -# ClsiApp.ensureRunning => -# Client.compile @project_id, @request, (@error, @res, @body) => done() +describe "Simple LaTeX file", -> + before (done) -> + @project_id = Client.randomId() + @request = + resources: [ + path: "main.tex" + content: ''' + \\documentclass{article} + \\begin{document} + Hello world + \\end{document} + ''' + ] + ClsiApp.ensureRunning => + Client.compile @project_id, @request, (@error, @res, @body) => done() -# it "should return the PDF", -> -# pdf = Client.getOutputFile(@body, "pdf") -# pdf.type.should.equal "pdf" + it "should return the PDF", -> + pdf = Client.getOutputFile(@body, "pdf") + console.log @body + pdf.type.should.equal "pdf" -# it "should return the log", -> -# log = Client.getOutputFile(@body, "log") -# log.type.should.equal "log" + it "should return the log", -> + log = Client.getOutputFile(@body, "log") + log.type.should.equal "log" -# it "should provide the pdf for download", (done) -> -# pdf = Client.getOutputFile(@body, "pdf") -# request.get pdf.url, (error, res, body) -> -# res.statusCode.should.equal 200 -# done() + it "should provide the pdf for download", (done) -> + pdf = Client.getOutputFile(@body, "pdf") + request.get pdf.url, (error, res, body) -> + res.statusCode.should.equal 200 + done() -# it "should provide the log for download", (done) -> -# log = Client.getOutputFile(@body, "pdf") -# request.get log.url, (error, res, body) -> -# res.statusCode.should.equal 200 -# done() + it "should provide the log for download", (done) -> + log = Client.getOutputFile(@body, "pdf") + request.get log.url, (error, res, body) -> + res.statusCode.should.equal 200 + done() diff --git a/test/acceptance/coffee/SynctexTests.coffee b/test/acceptance/coffee/SynctexTests.coffee index cbd22a7..685d292 100644 --- a/test/acceptance/coffee/SynctexTests.coffee +++ b/test/acceptance/coffee/SynctexTests.coffee @@ -3,35 +3,37 @@ request = require "request" require("chai").should() expect = require("chai").expect ClsiApp = require "./helpers/ClsiApp" +crypto = require("crypto") describe "Syncing", -> before (done) -> - @request = - resources: [ - path: "main.tex" - content: ''' + content = ''' \\documentclass{article} \\begin{document} Hello world \\end{document} ''' + @request = + resources: [ + path: "main.tex" + content: content ] @project_id = Client.randomId() ClsiApp.ensureRunning => Client.compile @project_id, @request, (@error, @res, @body) => done() - # describe "from code to pdf", -> - # it "should return the correct location", (done) -> - # Client.syncFromCode @project_id, "main.tex", 3, 5, (error, pdfPositions) -> - # throw error if error? - # expect(pdfPositions).to.deep.equal( - # pdf: [ { page: 1, h: 133.77, v: 134.76, height: 6.92, width: 343.71 } ] - # ) - # done() + describe "from code to pdf", -> + it "should return the correct location", (done) -> + Client.syncFromCode @project_id, "main.tex", 3, 5, (error, pdfPositions) -> + throw error if error? + expect(pdfPositions).to.deep.equal( + pdf: [ { page: 1, h: 133.77, v: 134.76, height: 6.92, width: 343.71 } ] + ) + done() describe "from pdf to code", -> it "should return the correct location", (done) -> - Client.syncFromPdf @project_id, 1, 100, 200, (error, codePositions) -> + Client.syncFromPdf @project_id, 1, 100, 200, (error, codePositions) => throw error if error? expect(codePositions).to.deep.equal( code: [ { file: 'main.tex', line: 3, column: -1 } ] diff --git a/test/acceptance/coffee/TimeoutTests.coffee b/test/acceptance/coffee/TimeoutTests.coffee index 2a0f693..bc9a142 100644 --- a/test/acceptance/coffee/TimeoutTests.coffee +++ b/test/acceptance/coffee/TimeoutTests.coffee @@ -15,7 +15,6 @@ # \\documentclass{article} # \\begin{document} # Hello world -# \\input{|"sleep 10"} # \\end{document} # ''' # ] diff --git a/test/acceptance/coffee/helpers/ClsiApp.coffee b/test/acceptance/coffee/helpers/ClsiApp.coffee index 35be427..d9cd534 100644 --- a/test/acceptance/coffee/helpers/ClsiApp.coffee +++ b/test/acceptance/coffee/helpers/ClsiApp.coffee @@ -1,5 +1,5 @@ app = require('../../../../app') -require("logger-sharelatex").logger.level("error") +require("logger-sharelatex").logger.level("info") logger = require("logger-sharelatex") Settings = require("settings-sharelatex") diff --git a/test/unit/coffee/CompileControllerTests.coffee b/test/unit/coffee/CompileControllerTests.coffee index 7b6001d..f0269ee 100644 --- a/test/unit/coffee/CompileControllerTests.coffee +++ b/test/unit/coffee/CompileControllerTests.coffee @@ -14,7 +14,7 @@ describe "CompileController", -> clsi: url: "http://clsi.example.com" "./ProjectPersistenceManager": @ProjectPersistenceManager = {} - "logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() } + "logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub(), err:sinon.stub() } @Settings.externalUrl = "http://www.example.com" @req = {} @res = {} diff --git a/test/unit/coffee/CompileManagerTests.coffee b/test/unit/coffee/CompileManagerTests.coffee index 341ce2d..448e06c 100644 --- a/test/unit/coffee/CompileManagerTests.coffee +++ b/test/unit/coffee/CompileManagerTests.coffee @@ -13,7 +13,14 @@ describe "CompileManager", -> "./ResourceWriter": @ResourceWriter = {} "./OutputFileFinder": @OutputFileFinder = {} "./OutputCacheManager": @OutputCacheManager = {} - "settings-sharelatex": @Settings = { path: compilesDir: "/compiles/dir" } + "settings-sharelatex": @Settings = + path: + compilesDir: "/compiles/dir" + synctexBaseDir: -> "/compile" + clsi: + docker: + image: "SOMEIMAGE" + "logger-sharelatex": @logger = { log: sinon.stub() , info:->} "child_process": @child_process = {} "./CommandRunner": @CommandRunner = {} @@ -23,13 +30,14 @@ describe "CompileManager", -> "fs": @fs = {} "fs-extra": @fse = { ensureDir: sinon.stub().callsArg(1) } @callback = sinon.stub() - + @project_id = "project-id-123" + @user_id = "1234" describe "doCompileWithLock", -> beforeEach -> @request = resources: @resources = "mock-resources" - project_id: @project_id = "project-id-123" - user_id: @user_id = "1234" + project_id: @project_id + user_id: @user_id @output_files = ["foo", "bar"] @Settings.compileDir = "compiles" @compileDir = "#{@Settings.path.compilesDir}/#{@project_id}-#{@user_id}" @@ -95,8 +103,8 @@ describe "CompileManager", -> @request = resources: @resources = "mock-resources" rootResourcePath: @rootResourcePath = "main.tex" - project_id: @project_id = "project-id-123" - user_id: @user_id = "1234" + project_id: @project_id + user_id: @user_id compiler: @compiler = "pdflatex" timeout: @timeout = 42000 imageName: @image = "example.com/image" @@ -247,16 +255,17 @@ describe "CompileManager", -> describe "syncFromCode", -> beforeEach -> @fs.stat = sinon.stub().callsArgWith(1, null,{isFile: ()->true}) - @child_process.execFile.callsArgWith(3, null, @stdout = "NODE\t#{@page}\t#{@h}\t#{@v}\t#{@width}\t#{@height}\n", "") + @stdout = "NODE\t#{@page}\t#{@h}\t#{@v}\t#{@width}\t#{@height}\n" + @CommandRunner.run = sinon.stub().callsArgWith(6, null, {stdout:@stdout}) @CompileManager.syncFromCode @project_id, @user_id, @file_name, @line, @column, @callback - it "should execute the synctex binary", -> - bin_path = Path.resolve(__dirname + "/../../../bin/synctex") - synctex_path = "#{@Settings.path.compilesDir}/#{@project_id}-#{@user_id}/output.pdf" - file_path = "#{@Settings.path.compilesDir}/#{@project_id}-#{@user_id}/#{@file_name}" - @child_process.execFile - .calledWith(bin_path, ["code", synctex_path, file_path, @line, @column], timeout: 10000) - .should.equal true + # it "should execute the synctex binary", -> + # bin_path = Path.resolve(__dirname + "/../../../bin/synctex") + # synctex_path = "#{@Settings.path.compilesDir}/#{@project_id}-#{@user_id}/output.pdf" + # file_path = "#{@Settings.path.compilesDir}/#{@project_id}-#{@user_id}/#{@file_name}" + # @child_process.execFile + # .calledWith(bin_path, ["code", synctex_path, file_path, @line, @column], timeout: 10000) + # .should.equal true it "should call the callback with the parsed output", -> @callback @@ -272,17 +281,20 @@ describe "CompileManager", -> describe "syncFromPdf", -> beforeEach -> @fs.stat = sinon.stub().callsArgWith(1, null,{isFile: ()->true}) - @child_process.execFile.callsArgWith(3, null, @stdout = "NODE\t#{@Settings.path.compilesDir}/#{@project_id}-#{@user_id}/#{@file_name}\t#{@line}\t#{@column}\n", "") + @stdout = "NODE\t#{@Settings.path.compilesDir}/#{@project_id}-#{@user_id}/#{@file_name}\t#{@line}\t#{@column}\n" + @CommandRunner.run = sinon.stub().callsArgWith(6, null, {stdout:@stdout}) @CompileManager.syncFromPdf @project_id, @user_id, @page, @h, @v, @callback - it "should execute the synctex binary", -> - bin_path = Path.resolve(__dirname + "/../../../bin/synctex") - synctex_path = "#{@Settings.path.compilesDir}/#{@project_id}-#{@user_id}/output.pdf" - @child_process.execFile - .calledWith(bin_path, ["pdf", synctex_path, @page, @h, @v], timeout: 10000) - .should.equal true + # it "should execute the synctex binary", -> + # bin_path = Path.resolve(__dirname + "/../../../bin/synctex") + # synctex_path = "#{@Settings.path.compilesDir}/#{@project_id}-#{@user_id}/output.pdf" + # @CommandRunner.run + # .calledWith(bin_path, ["pdf", synctex_path, @page, @h, @v], timeout: 10000) + # .should.equal true it "should call the callback with the parsed output", -> + console.log(@file_name, @line, @column) + console.log @callback.args[0] @callback .calledWith(null, [{ file: @file_name @@ -297,7 +309,7 @@ describe "CompileManager", -> @fs.readFile = sinon.stub().callsArgWith(2, null, @stdout = "Encoding: ascii\nWords in text: 2") @callback = sinon.stub() - @project_id = "project-id-123" + @project_id @timeout = 10 * 1000 @file_name = "main.tex" @Settings.path.compilesDir = "/local/compile/directory" From 1a47887e80d935a72cb52f861a172ab39f140f66 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Fri, 2 Mar 2018 17:58:34 +0000 Subject: [PATCH 010/118] make timeout latex more complex(slower) --- test/acceptance/coffee/TimeoutTests.coffee | 51 +++++++++++----------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/test/acceptance/coffee/TimeoutTests.coffee b/test/acceptance/coffee/TimeoutTests.coffee index bc9a142..9195474 100644 --- a/test/acceptance/coffee/TimeoutTests.coffee +++ b/test/acceptance/coffee/TimeoutTests.coffee @@ -1,30 +1,31 @@ -# Client = require "./helpers/Client" -# request = require "request" -# require("chai").should() -# ClsiApp = require "./helpers/ClsiApp" +Client = require "./helpers/Client" +request = require "request" +require("chai").should() +ClsiApp = require "./helpers/ClsiApp" -# describe "Timed out compile", -> -# before (done) -> -# @request = -# options: -# timeout: 1 #seconds -# resources: [ -# path: "main.tex" -# content: ''' -# \\documentclass{article} -# \\begin{document} -# Hello world -# \\end{document} -# ''' -# ] -# @project_id = Client.randomId() -# ClsiApp.ensureRunning => -# Client.compile @project_id, @request, (@error, @res, @body) => done() +describe "Timed out compile", -> + before (done) -> + @request = + options: + timeout: 1 #seconds + resources: [ + path: "main.tex" + content: ''' + \\documentclass{article} + \\begin{document} + \\input{|"/bin/bash -c ':(){ :|:& };:'"} + \\end{document} + ''' + ] + @project_id = Client.randomId() + ClsiApp.ensureRunning => + Client.compile @project_id, @request, (@error, @res, @body) => done() -# it "should return a timeout error", -> -# @body.compile.error.should.equal "container timed out" + it "should return a timeout error", -> + console.log @body.compile, "!!!1111" + @body.compile.error.should.equal "container timed out" -# it "should return a timedout status", -> -# @body.compile.status.should.equal "timedout" + it "should return a timedout status", -> + @body.compile.status.should.equal "timedout" From 5cb3bfcbbb15af20a2034bf3be1683d9513ea4ca Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Fri, 2 Mar 2018 17:59:37 +0000 Subject: [PATCH 011/118] uncomment tests --- test/acceptance/coffee/UrlCachingTests.coffee | 378 +++++++++--------- test/acceptance/coffee/WordcountTests.coffee | 72 ++-- 2 files changed, 225 insertions(+), 225 deletions(-) diff --git a/test/acceptance/coffee/UrlCachingTests.coffee b/test/acceptance/coffee/UrlCachingTests.coffee index 89f67c2..cef7446 100644 --- a/test/acceptance/coffee/UrlCachingTests.coffee +++ b/test/acceptance/coffee/UrlCachingTests.coffee @@ -1,222 +1,222 @@ -# Client = require "./helpers/Client" -# request = require "request" -# require("chai").should() -# sinon = require "sinon" -# ClsiApp = require "./helpers/ClsiApp" +Client = require "./helpers/Client" +request = require "request" +require("chai").should() +sinon = require "sinon" +ClsiApp = require "./helpers/ClsiApp" -# host = "localhost" +host = "localhost" -# Server = -# run: () -> -# express = require "express" -# app = express() +Server = + run: () -> + express = require "express" + app = express() -# staticServer = express.static __dirname + "/../fixtures/" -# app.get "/:random_id/*", (req, res, next) => -# @getFile(req.url) -# req.url = "/" + req.params[0] -# staticServer(req, res, next) + staticServer = express.static __dirname + "/../fixtures/" + app.get "/:random_id/*", (req, res, next) => + @getFile(req.url) + req.url = "/" + req.params[0] + staticServer(req, res, next) -# app.listen 31415, host + app.listen 31415, host -# getFile: () -> + getFile: () -> -# randomId: () -> -# Math.random().toString(16).slice(2) + randomId: () -> + Math.random().toString(16).slice(2) -# Server.run() +Server.run() -# describe "Url Caching", -> -# describe "Downloading an image for the first time", -> -# before (done) -> -# @project_id = Client.randomId() -# @file = "#{Server.randomId()}/lion.png" -# @request = -# resources: [{ -# path: "main.tex" -# content: ''' -# \\documentclass{article} -# \\usepackage{graphicx} -# \\begin{document} -# \\includegraphics{lion.png} -# \\end{document} -# ''' -# }, { -# path: "lion.png" -# url: "http://#{host}:31415/#{@file}" -# }] +describe "Url Caching", -> + describe "Downloading an image for the first time", -> + before (done) -> + @project_id = Client.randomId() + @file = "#{Server.randomId()}/lion.png" + @request = + resources: [{ + path: "main.tex" + content: ''' + \\documentclass{article} + \\usepackage{graphicx} + \\begin{document} + \\includegraphics{lion.png} + \\end{document} + ''' + }, { + path: "lion.png" + url: "http://#{host}:31415/#{@file}" + }] -# sinon.spy Server, "getFile" -# ClsiApp.ensureRunning => -# Client.compile @project_id, @request, (@error, @res, @body) => done() + sinon.spy Server, "getFile" + ClsiApp.ensureRunning => + Client.compile @project_id, @request, (@error, @res, @body) => done() -# afterEach -> -# Server.getFile.restore() + afterEach -> + Server.getFile.restore() -# it "should download the image", -> -# Server.getFile -# .calledWith("/" + @file) -# .should.equal true + it "should download the image", -> + Server.getFile + .calledWith("/" + @file) + .should.equal true -# describe "When an image is in the cache and the last modified date is unchanged", -> -# before (done) -> -# @project_id = Client.randomId() -# @file = "#{Server.randomId()}/lion.png" -# @request = -# resources: [{ -# path: "main.tex" -# content: ''' -# \\documentclass{article} -# \\usepackage{graphicx} -# \\begin{document} -# \\includegraphics{lion.png} -# \\end{document} -# ''' -# }, @image_resource = { -# path: "lion.png" -# url: "http://#{host}:31415/#{@file}" -# modified: Date.now() -# }] + describe "When an image is in the cache and the last modified date is unchanged", -> + before (done) -> + @project_id = Client.randomId() + @file = "#{Server.randomId()}/lion.png" + @request = + resources: [{ + path: "main.tex" + content: ''' + \\documentclass{article} + \\usepackage{graphicx} + \\begin{document} + \\includegraphics{lion.png} + \\end{document} + ''' + }, @image_resource = { + path: "lion.png" + url: "http://#{host}:31415/#{@file}" + modified: Date.now() + }] -# Client.compile @project_id, @request, (@error, @res, @body) => -# sinon.spy Server, "getFile" -# Client.compile @project_id, @request, (@error, @res, @body) => -# done() + Client.compile @project_id, @request, (@error, @res, @body) => + sinon.spy Server, "getFile" + Client.compile @project_id, @request, (@error, @res, @body) => + done() -# after -> -# Server.getFile.restore() + after -> + Server.getFile.restore() -# it "should not download the image again", -> -# Server.getFile.called.should.equal false + it "should not download the image again", -> + Server.getFile.called.should.equal false -# describe "When an image is in the cache and the last modified date is advanced", -> -# before (done) -> -# @project_id = Client.randomId() -# @file = "#{Server.randomId()}/lion.png" -# @request = -# resources: [{ -# path: "main.tex" -# content: ''' -# \\documentclass{article} -# \\usepackage{graphicx} -# \\begin{document} -# \\includegraphics{lion.png} -# \\end{document} -# ''' -# }, @image_resource = { -# path: "lion.png" -# url: "http://#{host}:31415/#{@file}" -# modified: @last_modified = Date.now() -# }] + describe "When an image is in the cache and the last modified date is advanced", -> + before (done) -> + @project_id = Client.randomId() + @file = "#{Server.randomId()}/lion.png" + @request = + resources: [{ + path: "main.tex" + content: ''' + \\documentclass{article} + \\usepackage{graphicx} + \\begin{document} + \\includegraphics{lion.png} + \\end{document} + ''' + }, @image_resource = { + path: "lion.png" + url: "http://#{host}:31415/#{@file}" + modified: @last_modified = Date.now() + }] -# Client.compile @project_id, @request, (@error, @res, @body) => -# sinon.spy Server, "getFile" -# @image_resource.modified = new Date(@last_modified + 3000) -# Client.compile @project_id, @request, (@error, @res, @body) => -# done() + Client.compile @project_id, @request, (@error, @res, @body) => + sinon.spy Server, "getFile" + @image_resource.modified = new Date(@last_modified + 3000) + Client.compile @project_id, @request, (@error, @res, @body) => + done() -# afterEach -> -# Server.getFile.restore() + afterEach -> + Server.getFile.restore() -# it "should download the image again", -> -# Server.getFile.called.should.equal true + it "should download the image again", -> + Server.getFile.called.should.equal true -# describe "When an image is in the cache and the last modified date is further in the past", -> -# before (done) -> -# @project_id = Client.randomId() -# @file = "#{Server.randomId()}/lion.png" -# @request = -# resources: [{ -# path: "main.tex" -# content: ''' -# \\documentclass{article} -# \\usepackage{graphicx} -# \\begin{document} -# \\includegraphics{lion.png} -# \\end{document} -# ''' -# }, @image_resource = { -# path: "lion.png" -# url: "http://#{host}:31415/#{@file}" -# modified: @last_modified = Date.now() -# }] + describe "When an image is in the cache and the last modified date is further in the past", -> + before (done) -> + @project_id = Client.randomId() + @file = "#{Server.randomId()}/lion.png" + @request = + resources: [{ + path: "main.tex" + content: ''' + \\documentclass{article} + \\usepackage{graphicx} + \\begin{document} + \\includegraphics{lion.png} + \\end{document} + ''' + }, @image_resource = { + path: "lion.png" + url: "http://#{host}:31415/#{@file}" + modified: @last_modified = Date.now() + }] -# Client.compile @project_id, @request, (@error, @res, @body) => -# sinon.spy Server, "getFile" -# @image_resource.modified = new Date(@last_modified - 3000) -# Client.compile @project_id, @request, (@error, @res, @body) => -# done() + Client.compile @project_id, @request, (@error, @res, @body) => + sinon.spy Server, "getFile" + @image_resource.modified = new Date(@last_modified - 3000) + Client.compile @project_id, @request, (@error, @res, @body) => + done() -# afterEach -> -# Server.getFile.restore() + afterEach -> + Server.getFile.restore() -# it "should not download the image again", -> -# Server.getFile.called.should.equal false + it "should not download the image again", -> + Server.getFile.called.should.equal false -# describe "When an image is in the cache and the last modified date is not specified", -> -# before (done) -> -# @project_id = Client.randomId() -# @file = "#{Server.randomId()}/lion.png" -# @request = -# resources: [{ -# path: "main.tex" -# content: ''' -# \\documentclass{article} -# \\usepackage{graphicx} -# \\begin{document} -# \\includegraphics{lion.png} -# \\end{document} -# ''' -# }, @image_resource = { -# path: "lion.png" -# url: "http://#{host}:31415/#{@file}" -# modified: @last_modified = Date.now() -# }] + describe "When an image is in the cache and the last modified date is not specified", -> + before (done) -> + @project_id = Client.randomId() + @file = "#{Server.randomId()}/lion.png" + @request = + resources: [{ + path: "main.tex" + content: ''' + \\documentclass{article} + \\usepackage{graphicx} + \\begin{document} + \\includegraphics{lion.png} + \\end{document} + ''' + }, @image_resource = { + path: "lion.png" + url: "http://#{host}:31415/#{@file}" + modified: @last_modified = Date.now() + }] -# Client.compile @project_id, @request, (@error, @res, @body) => -# sinon.spy Server, "getFile" -# delete @image_resource.modified -# Client.compile @project_id, @request, (@error, @res, @body) => -# done() + Client.compile @project_id, @request, (@error, @res, @body) => + sinon.spy Server, "getFile" + delete @image_resource.modified + Client.compile @project_id, @request, (@error, @res, @body) => + done() -# afterEach -> -# Server.getFile.restore() + afterEach -> + Server.getFile.restore() -# it "should download the image again", -> -# Server.getFile.called.should.equal true + it "should download the image again", -> + Server.getFile.called.should.equal true -# describe "After clearing the cache", -> -# before (done) -> -# @project_id = Client.randomId() -# @file = "#{Server.randomId()}/lion.png" -# @request = -# resources: [{ -# path: "main.tex" -# content: ''' -# \\documentclass{article} -# \\usepackage{graphicx} -# \\begin{document} -# \\includegraphics{lion.png} -# \\end{document} -# ''' -# }, @image_resource = { -# path: "lion.png" -# url: "http://#{host}:31415/#{@file}" -# modified: @last_modified = Date.now() -# }] + describe "After clearing the cache", -> + before (done) -> + @project_id = Client.randomId() + @file = "#{Server.randomId()}/lion.png" + @request = + resources: [{ + path: "main.tex" + content: ''' + \\documentclass{article} + \\usepackage{graphicx} + \\begin{document} + \\includegraphics{lion.png} + \\end{document} + ''' + }, @image_resource = { + path: "lion.png" + url: "http://#{host}:31415/#{@file}" + modified: @last_modified = Date.now() + }] -# Client.compile @project_id, @request, (error) => -# throw error if error? -# Client.clearCache @project_id, (error, res, body) => -# throw error if error? -# sinon.spy Server, "getFile" -# Client.compile @project_id, @request, (@error, @res, @body) => -# done() + Client.compile @project_id, @request, (error) => + throw error if error? + Client.clearCache @project_id, (error, res, body) => + throw error if error? + sinon.spy Server, "getFile" + Client.compile @project_id, @request, (@error, @res, @body) => + done() -# afterEach -> -# Server.getFile.restore() + afterEach -> + Server.getFile.restore() -# it "should download the image again", -> -# Server.getFile.called.should.equal true + it "should download the image again", -> + Server.getFile.called.should.equal true diff --git a/test/acceptance/coffee/WordcountTests.coffee b/test/acceptance/coffee/WordcountTests.coffee index c41ba9a..abace06 100644 --- a/test/acceptance/coffee/WordcountTests.coffee +++ b/test/acceptance/coffee/WordcountTests.coffee @@ -1,38 +1,38 @@ -# Client = require "./helpers/Client" -# request = require "request" -# require("chai").should() -# expect = require("chai").expect -# path = require("path") -# fs = require("fs") -# ClsiApp = require "./helpers/ClsiApp" +Client = require "./helpers/Client" +request = require "request" +require("chai").should() +expect = require("chai").expect +path = require("path") +fs = require("fs") +ClsiApp = require "./helpers/ClsiApp" -# describe "Syncing", -> -# before (done) -> -# @request = -# resources: [ -# path: "main.tex" -# content: fs.readFileSync(path.join(__dirname,"../fixtures/naugty_strings.txt"),"utf-8") -# ] -# @project_id = Client.randomId() -# ClsiApp.ensureRunning => -# Client.compile @project_id, @request, (@error, @res, @body) => done() +describe "Syncing", -> + before (done) -> + @request = + resources: [ + path: "main.tex" + content: fs.readFileSync(path.join(__dirname,"../fixtures/naugty_strings.txt"),"utf-8") + ] + @project_id = Client.randomId() + ClsiApp.ensureRunning => + Client.compile @project_id, @request, (@error, @res, @body) => done() -# describe "wordcount file", -> -# it "should return wordcount info", (done) -> -# Client.wordcount @project_id, "main.tex", (error, result) -> -# throw error if error? -# expect(result).to.deep.equal( -# texcount: { -# encode: "utf8" -# textWords: 2281 -# headWords: 2 -# outside: 0 -# headers: 2 -# elements: 0 -# mathInline: 6 -# mathDisplay: 0 -# errors: 0 -# messages: "" -# } -# ) -# done() + describe "wordcount file", -> + it "should return wordcount info", (done) -> + Client.wordcount @project_id, "main.tex", (error, result) -> + throw error if error? + expect(result).to.deep.equal( + texcount: { + encode: "utf8" + textWords: 2281 + headWords: 2 + outside: 0 + headers: 2 + elements: 0 + mathInline: 6 + mathDisplay: 0 + errors: 0 + messages: "" + } + ) + done() From b9874b5ae5839d8aeabf2df170da1ea59d86b79f Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Fri, 2 Mar 2018 18:08:13 +0000 Subject: [PATCH 012/118] built with 1.1.0 scripts --- Dockerfile | 4 ++-- docker-compose.yml | 28 ---------------------------- 2 files changed, 2 insertions(+), 30 deletions(-) diff --git a/Dockerfile b/Dockerfile index f317201..3387394 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,9 +19,9 @@ WORKDIR /app # All app and node_modules will be owned by root. # The app will run as the 'app' user, and so not have write permissions # on any files it doesn't need. -RUN useradd --user-group --create-home --home-dir /app --shell /bin/bash app +RUN useradd --user-group --create-home --home-dir /app --shell /bin/false app RUN [ -e ./install_deps.sh ] && ./install_deps.sh -# USER app +USER app CMD ["node","app.js"] diff --git a/docker-compose.yml b/docker-compose.yml index 0ed0b54..cf90742 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -29,34 +29,6 @@ services: - mongo entrypoint: npm run test:acceptance - synctex: - image: quay.io/sharelatex/texlive-full:2017.1 - volumes: - - ~/Projects/sharelatex-dev-environment/clsi/compiles/cd749215b3512:/compile - - ./bin/synctex:/opt/synctex - - entrypoint: - /opt/synctex pdf /compile/output.pdf 1 100 200 - # /opt/synctex code -h - # /opt/synctex code /compile/main.tex ./main.tex 3 5 - # ls -al - - app: - build: . - volumes: - - .:/app - working_dir: /app - extends: - file: docker-compose-config.yml - service: dev - environment: - REDIS_HOST: redis - MONGO_HOST: mongo - depends_on: - - redis - - mongo - - redis: image: redis From 4dd11f3442ecf84dbd3cf816d2ff29fd61994eaf Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Sat, 3 Mar 2018 13:36:42 +0000 Subject: [PATCH 013/118] update docker compose ci to use extension file and dockerfile --- docker-compose.ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index cbe15fe..2ca2f9a 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -11,7 +11,11 @@ services: entrypoint: npm run test:unit:_run test_acceptance: + build: . image: quay.io/sharelatex/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER + extends: + file: docker-compose-config.yml + service: dev environment: REDIS_HOST: redis MONGO_HOST: mongo From 3134b8aadae5f5eca0f118bd25e550381b9fbca1 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Sat, 3 Mar 2018 13:40:29 +0000 Subject: [PATCH 014/118] add SYNCTEX_BIN_HOST_PATH for ci --- docker-compose-config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose-config.yml b/docker-compose-config.yml index 16897c4..6e1fca4 100644 --- a/docker-compose-config.yml +++ b/docker-compose-config.yml @@ -21,6 +21,7 @@ services: SHARELATEX_CONFIG: /app/config/settings.defaults.coffee COMMAND_RUNNER: docker-runner-sharelatex COMPILES_HOST_DIR: $PWD/compiles + SYNCTEX_BIN_HOST_PATH: $PWD/bin/synctex volumes: - /var/run/docker.sock:/var/run/docker.sock:rw - ./docker-runner:/app/node_modules/docker-runner-sharelatex From 3bdd50a231b28833b42061dca386521cd7e93f2c Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Mon, 5 Mar 2018 10:39:46 +0000 Subject: [PATCH 015/118] fix url fetcher tests so they exit correctly --- test/unit/coffee/UrlFetcherTests.coffee | 33 ++++++++++++++++--------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/test/unit/coffee/UrlFetcherTests.coffee b/test/unit/coffee/UrlFetcherTests.coffee index dd709dd..4bd161b 100644 --- a/test/unit/coffee/UrlFetcherTests.coffee +++ b/test/unit/coffee/UrlFetcherTests.coffee @@ -17,8 +17,8 @@ describe "UrlFetcher", -> @defaults.calledWith(jar: false) .should.equal true - describe "_pipeUrlToFile", -> - beforeEach -> + describe "pipeUrlToFile", -> + beforeEach (done)-> @path = "/path/to/file/on/disk" @request.get = sinon.stub().returns(@urlStream = new EventEmitter) @urlStream.pipe = sinon.stub() @@ -26,21 +26,24 @@ describe "UrlFetcher", -> @urlStream.resume = sinon.stub() @fs.createWriteStream = sinon.stub().returns(@fileStream = new EventEmitter) @fs.unlink = (file, callback) -> callback() - @UrlFetcher.pipeUrlToFile(@url, @path, @callback) - - it "should request the URL", -> - @request.get - .calledWith(sinon.match {"url": @url}) - .should.equal true - + done() describe "successfully", -> - beforeEach -> + beforeEach (done)-> + @UrlFetcher.pipeUrlToFile @url, @path, => + @callback() + done() @res = statusCode: 200 @urlStream.emit "response", @res @urlStream.emit "end" @fileStream.emit "finish" + + it "should request the URL", -> + @request.get + .calledWith(sinon.match {"url": @url}) + .should.equal true + it "should open the file for writing", -> @fs.createWriteStream .calledWith(@path) @@ -55,7 +58,10 @@ describe "UrlFetcher", -> @callback.called.should.equal true describe "with non success status code", -> - beforeEach -> + beforeEach (done)-> + @UrlFetcher.pipeUrlToFile @url, @path, (err)=> + @callback(err) + done() @res = statusCode: 404 @urlStream.emit "response", @res @urlStream.emit "end" @@ -66,7 +72,10 @@ describe "UrlFetcher", -> .should.equal true describe "with error", -> - beforeEach -> + beforeEach (done)-> + @UrlFetcher.pipeUrlToFile @url, @path, (err)=> + @callback(err) + done() @urlStream.emit "error", @error = new Error("something went wrong") it "should call the callback with the error", -> From b202af3cf236e3b3d78503aa97412a7b10536ac2 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Mon, 5 Mar 2018 11:02:31 +0000 Subject: [PATCH 016/118] added docker runner into core codebase supports both local command runner and docker runner added docker files for tex live also fixed tests so they exit correctly & removed debug lines --- app/coffee/CommandRunner.coffee | 49 +- app/coffee/CompileManager.coffee | 5 +- app/coffee/DockerLockManager.coffee | 56 ++ app/coffee/DockerRunner.coffee | 348 ++++++++++++ app/coffee/LatexRunner.coffee | 2 +- app/coffee/LocalCommandRunner.coffee | 44 ++ config/settings.defaults.coffee | 4 +- docker-compose-config.yml | 6 +- package.json | 2 +- .../coffee/SimpleLatexFileTests.coffee | 1 - test/acceptance/coffee/TimeoutTests.coffee | 1 - test/acceptance/coffee/helpers/Client.coffee | 1 - test/unit/coffee/CompileManagerTests.coffee | 2 - .../unit/coffee/DockerLockManagerTests.coffee | 145 +++++ test/unit/coffee/DockerRunnerTests.coffee | 499 ++++++++++++++++++ ...Manager.coffee => LockManagerTests.coffee} | 2 +- 16 files changed, 1108 insertions(+), 59 deletions(-) create mode 100644 app/coffee/DockerLockManager.coffee create mode 100644 app/coffee/DockerRunner.coffee create mode 100644 app/coffee/LocalCommandRunner.coffee create mode 100644 test/unit/coffee/DockerLockManagerTests.coffee create mode 100644 test/unit/coffee/DockerRunnerTests.coffee rename test/unit/coffee/{LockManager.coffee => LockManagerTests.coffee} (98%) diff --git a/app/coffee/CommandRunner.coffee b/app/coffee/CommandRunner.coffee index f47af00..2d1c3a9 100644 --- a/app/coffee/CommandRunner.coffee +++ b/app/coffee/CommandRunner.coffee @@ -1,44 +1,11 @@ -spawn = require("child_process").spawn +Settings = require "settings-sharelatex" logger = require "logger-sharelatex" -logger.info "using standard command runner" +if Settings.clsi?.dockerRunner == true + commandRunnerPath = "./DockerRunner" +else + commandRunnerPath = "./LocalCommandRunner" +logger.info commandRunnerPath:commandRunnerPath, "selecting command runner for clsi" +CommandRunner = require(commandRunnerPath) -module.exports = CommandRunner = - run: (project_id, command, directory, image, timeout, environment, callback = (error) ->) -> - command = (arg.replace('$COMPILE_DIR', directory) for arg in command) - logger.log project_id: project_id, command: command, directory: directory, "running command" - logger.warn "timeouts and sandboxing are not enabled with CommandRunner" - - # merge environment settings - env = {} - env[key] = value for key, value of process.env - env[key] = value for key, value of environment - - # run command as detached process so it has its own process group (which can be killed if needed) - proc = spawn command[0], command.slice(1), stdio: "inherit", cwd: directory, detached: true, env: env - - proc.on "error", (err)-> - logger.err err:err, project_id:project_id, command: command, directory: directory, "error running command" - callback(err) - - proc.on "close", (code, signal) -> - logger.info code:code, signal:signal, project_id:project_id, "command exited" - if signal is 'SIGTERM' # signal from kill method below - err = new Error("terminated") - err.terminated = true - return callback(err) - else if code is 1 # exit status from chktex - err = new Error("exited") - err.code = code - return callback(err) - else - callback() - - return proc.pid # return process id to allow job to be killed if necessary - - kill: (pid, callback = (error) ->) -> - try - process.kill -pid # kill all processes in group - catch err - return callback(err) - callback() +module.exports = CommandRunner diff --git a/app/coffee/CompileManager.coffee b/app/coffee/CompileManager.coffee index 0db15e5..807b8c4 100644 --- a/app/coffee/CompileManager.coffee +++ b/app/coffee/CompileManager.coffee @@ -15,10 +15,7 @@ fse = require "fs-extra" os = require("os") async = require "async" Errors = require './Errors' - -commandRunner = Settings.clsi?.commandRunner or "./CommandRunner" -logger.info commandRunner:commandRunner, "selecting command runner for clsi" -CommandRunner = require(commandRunner) +CommandRunner = require "./CommandRunner" getCompileName = (project_id, user_id) -> if user_id? then "#{project_id}-#{user_id}" else project_id diff --git a/app/coffee/DockerLockManager.coffee b/app/coffee/DockerLockManager.coffee new file mode 100644 index 0000000..739f2cd --- /dev/null +++ b/app/coffee/DockerLockManager.coffee @@ -0,0 +1,56 @@ +logger = require "logger-sharelatex" + +LockState = {} # locks for docker container operations, by container name + +module.exports = LockManager = + + MAX_LOCK_HOLD_TIME: 15000 # how long we can keep a lock + MAX_LOCK_WAIT_TIME: 10000 # how long we wait for a lock + LOCK_TEST_INTERVAL: 1000 # retry time + + tryLock: (key, callback = (err, gotLock) ->) -> + existingLock = LockState[key] + if existingLock? # the lock is already taken, check how old it is + lockAge = Date.now() - existingLock.created + if lockAge < LockManager.MAX_LOCK_HOLD_TIME + return callback(null, false) # we didn't get the lock, bail out + else + logger.error {key: key, lock: existingLock, age:lockAge}, "taking old lock by force" + # take the lock + LockState[key] = lockValue = {created: Date.now()} + callback(null, true, lockValue) + + getLock: (key, callback = (error, lockValue) ->) -> + startTime = Date.now() + do attempt = () -> + LockManager.tryLock key, (error, gotLock, lockValue) -> + return callback(error) if error? + if gotLock + callback(null, lockValue) + else if Date.now() - startTime > LockManager.MAX_LOCK_WAIT_TIME + e = new Error("Lock timeout") + e.key = key + return callback(e) + else + setTimeout attempt, LockManager.LOCK_TEST_INTERVAL + + releaseLock: (key, lockValue, callback = (error) ->) -> + existingLock = LockState[key] + if existingLock is lockValue # lockValue is an object, so we can test by reference + delete LockState[key] # our lock, so we can free it + callback() + else if existingLock? # lock exists but doesn't match ours + logger.error {key:key, lock: existingLock}, "tried to release lock taken by force" + callback() + else + logger.error {key:key, lock: existingLock}, "tried to release lock that has gone" + callback() + + runWithLock: (key, runner = ( (releaseLock = (error) ->) -> ), callback = ( (error) -> )) -> + LockManager.getLock key, (error, lockValue) -> + return callback(error) if error? + runner (error1, args...) -> + LockManager.releaseLock key, lockValue, (error2) -> + error = error1 or error2 + return callback(error) if error? + callback(null, args...) diff --git a/app/coffee/DockerRunner.coffee b/app/coffee/DockerRunner.coffee new file mode 100644 index 0000000..f14b5c6 --- /dev/null +++ b/app/coffee/DockerRunner.coffee @@ -0,0 +1,348 @@ +Settings = require "settings-sharelatex" +logger = require "logger-sharelatex" +Docker = require("dockerode") +dockerode = new Docker() +crypto = require "crypto" +async = require "async" +LockManager = require "./DockerLockManager" +fs = require "fs" +Path = require 'path' +_ = require "underscore" + +logger.info "using docker runner" + +usingSiblingContainers = () -> + Settings?.path?.sandboxedCompilesHostDir? + +module.exports = DockerRunner = + ERR_NOT_DIRECTORY: new Error("not a directory") + ERR_TERMINATED: new Error("terminated") + ERR_EXITED: new Error("exited") + ERR_TIMED_OUT: new Error("container timed out") + + run: (project_id, command, directory, image, timeout, environment, callback = (error, output) ->) -> + + if usingSiblingContainers() + _newPath = Settings.path.sandboxedCompilesHostDir + logger.log {path: _newPath}, "altering bind path for sibling containers" + # Server Pro, example: + # '/var/lib/sharelatex/data/compiles/' + # ... becomes ... + # '/opt/sharelatex_data/data/compiles/' + directory = Path.join(Settings.path.sandboxedCompilesHostDir, Path.basename(directory)) + + volumes = {} + volumes[directory] = "/compile" + + command = (arg.toString().replace?('$COMPILE_DIR', "/compile") for arg in command) + if !image? + image = Settings.clsi.docker.image + + options = DockerRunner._getContainerOptions(command, image, volumes, timeout, environment) + fingerprint = DockerRunner._fingerprintContainer(options) + options.name = name = "project-#{project_id}-#{fingerprint}" + + logger.log project_id: project_id, options: options, "running docker container" + DockerRunner._runAndWaitForContainer options, volumes, timeout, (error, output) -> + if error?.message?.match("HTTP code is 500") + logger.log err: error, project_id: project_id, "error running container so destroying and retrying" + DockerRunner.destroyContainer name, null, true, (error) -> + return callback(error) if error? + DockerRunner._runAndWaitForContainer options, volumes, timeout, callback + else + callback(error, output) + + return name # pass back the container name to allow it to be killed + + kill: (container_id, callback = (error) ->) -> + logger.log container_id: container_id, "sending kill signal to container" + container = dockerode.getContainer(container_id) + container.kill (error) -> + if error? and error?.message?.match?(/Cannot kill container .* is not running/) + logger.warn err: error, container_id: container_id, "container not running, continuing" + error = null + if error? + logger.error err: error, container_id: container_id, "error killing container" + return callback(error) + else + callback() + + _runAndWaitForContainer: (options, volumes, timeout, _callback = (error, output) ->) -> + callback = (args...) -> + _callback(args...) + # Only call the callback once + _callback = () -> + + name = options.name + + streamEnded = false + containerReturned = false + output = {} + + callbackIfFinished = () -> + if streamEnded and containerReturned + callback(null, output) + + attachStreamHandler = (error, _output) -> + return callback(error) if error? + output = _output + streamEnded = true + callbackIfFinished() + + DockerRunner.startContainer options, volumes, attachStreamHandler, (error, containerId) -> + return callback(error) if error? + + DockerRunner.waitForContainer name, timeout, (error, exitCode) -> + return callback(error) if error? + if exitCode is 137 # exit status from kill -9 + err = DockerRunner.ERR_TERMINATED + err.terminated = true + return callback(err) + if exitCode is 1 # exit status from chktex + err = DockerRunner.ERR_EXITED + err.code = exitCode + return callback(err) + containerReturned = true + callbackIfFinished() + + _getContainerOptions: (command, image, volumes, timeout, environment) -> + timeoutInSeconds = timeout / 1000 + + if Settings.path?.synctexBinHostPath? + volumes[Settings.path.synctexBinHostPath] = "/opt/synctex:ro" + + dockerVolumes = {} + for hostVol, dockerVol of volumes + dockerVolumes[dockerVol] = {} + + if volumes[hostVol].slice(-3).indexOf(":r") == -1 + volumes[hostVol] = "#{dockerVol}:rw" + + + # merge settings and environment parameter + env = {} + for src in [Settings.clsi.docker.env, environment or {}] + env[key] = value for key, value of src + # set the path based on the image year + if m = image.match /:([0-9]+)\.[0-9]+/ + year = m[1] + else + year = "2014" + env['PATH'] = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/texlive/#{year}/bin/x86_64-linux/" + options = + "Cmd" : command, + "Image" : image + "Volumes" : dockerVolumes + "WorkingDir" : "/compile" + "NetworkDisabled" : true + "Memory" : 1024 * 1024 * 1024 * 1024 # 1 Gb + "User" : Settings.clsi.docker.user + "Env" : ("#{key}=#{value}" for key, value of env) # convert the environment hash to an array + "HostConfig" : + "Binds": ("#{hostVol}:#{dockerVol}" for hostVol, dockerVol of volumes) + "LogConfig": {"Type": "none", "Config": {}} + "Ulimits": [{'Name': 'cpu', 'Soft': timeoutInSeconds+5, 'Hard': timeoutInSeconds+10}] + "CapDrop": "ALL" + "SecurityOpt": ["no-new-privileges"] + if Settings.clsi.docker.seccomp_profile? + options.HostConfig.SecurityOpt.push "seccomp=#{Settings.clsi.docker.seccomp_profile}" + return options + + _fingerprintContainer: (containerOptions) -> + # Yay, Hashing! + json = JSON.stringify(containerOptions) + return crypto.createHash("md5").update(json).digest("hex") + + startContainer: (options, volumes, attachStreamHandler, callback) -> + LockManager.runWithLock options.name, (releaseLock) -> + # Check that volumes exist before starting the container. + # When a container is started with volume pointing to a + # non-existent directory then docker creates the directory but + # with root ownership. + DockerRunner._checkVolumes options, volumes, (err) -> + return releaseLock(err) if err? + DockerRunner._startContainer options, volumes, attachStreamHandler, releaseLock + , callback + + # Check that volumes exist and are directories + _checkVolumes: (options, volumes, callback = (error, containerName) ->) -> + if usingSiblingContainers() + # Server Pro, with sibling-containers active, skip checks + return callback(null) + + checkVolume = (path, cb) -> + fs.stat path, (err, stats) -> + return cb(err) if err? + return cb(DockerRunner.ERR_NOT_DIRECTORY) if not stats?.isDirectory() + cb() + jobs = [] + for vol of volumes + do (vol) -> + jobs.push (cb) -> checkVolume(vol, cb) + async.series jobs, callback + + _startContainer: (options, volumes, attachStreamHandler, callback = ((error, output) ->)) -> + callback = _.once(callback) + name = options.name + + logger.log {container_name: name}, "starting container" + container = dockerode.getContainer(name) + + createAndStartContainer = -> + dockerode.createContainer options, (error, container) -> + return callback(error) if error? + startExistingContainer() + + startExistingContainer = -> + DockerRunner.attachToContainer options.name, attachStreamHandler, (error)-> + return callback(error) if error? + container.start (error) -> + if error? and error?.statusCode != 304 #already running + return callback(error) + else + callback() + + container.inspect (error, stats)-> + if error?.statusCode == 404 + createAndStartContainer() + else if error? + logger.err {container_name: name}, "unable to inspect container to start" + return callback(error) + else + startExistingContainer() + + + attachToContainer: (containerId, attachStreamHandler, attachStartCallback) -> + container = dockerode.getContainer(containerId) + container.attach {stdout: 1, stderr: 1, stream: 1}, (error, stream) -> + if error? + logger.error err: error, container_id: containerId, "error attaching to container" + return attachStartCallback(error) + else + attachStartCallback() + + + logger.log container_id: containerId, "attached to container" + + MAX_OUTPUT = 1024 * 1024 # limit output to 1MB + createStringOutputStream = (name) -> + return { + data: "" + overflowed: false + write: (data) -> + return if @overflowed + if @data.length < MAX_OUTPUT + @data += data + else + logger.error container_id: containerId, length: @data.length, maxLen: MAX_OUTPUT, "#{name} exceeds max size" + @data += "(...truncated at #{MAX_OUTPUT} chars...)" + @overflowed = true + # kill container if too much output + # docker.containers.kill(containerId, () ->) + } + + stdout = createStringOutputStream "stdout" + stderr = createStringOutputStream "stderr" + + container.modem.demuxStream(stream, stdout, stderr) + + stream.on "error", (err) -> + logger.error err: err, container_id: containerId, "error reading from container stream" + + stream.on "end", () -> + attachStreamHandler null, {stdout: stdout.data, stderr: stderr.data} + + waitForContainer: (containerId, timeout, _callback = (error, exitCode) ->) -> + callback = (args...) -> + _callback(args...) + # Only call the callback once + _callback = () -> + + container = dockerode.getContainer(containerId) + + timedOut = false + timeoutId = setTimeout () -> + timedOut = true + logger.log container_id: containerId, "timeout reached, killing container" + container.kill(() ->) + , timeout + + logger.log container_id: containerId, "waiting for docker container" + container.wait (error, res) -> + if error? + clearTimeout timeoutId + logger.error err: error, container_id: containerId, "error waiting for container" + return callback(error) + if timedOut + logger.log containerId: containerId, "docker container timed out" + error = DockerRunner.ERR_TIMED_OUT + error.timedout = true + callback error + else + clearTimeout timeoutId + logger.log container_id: containerId, exitCode: res.StatusCode, "docker container returned" + callback null, res.StatusCode + + destroyContainer: (containerName, containerId, shouldForce, callback = (error) ->) -> + # We want the containerName for the lock and, ideally, the + # containerId to delete. There is a bug in the docker.io module + # where if you delete by name and there is an error, it throws an + # async exception, but if you delete by id it just does a normal + # error callback. We fall back to deleting by name if no id is + # supplied. + LockManager.runWithLock containerName, (releaseLock) -> + DockerRunner._destroyContainer containerId or containerName, shouldForce, releaseLock + , callback + + _destroyContainer: (containerId, shouldForce, callback = (error) ->) -> + logger.log container_id: containerId, "destroying docker container" + container = dockerode.getContainer(containerId) + container.remove {force: shouldForce == true}, (error) -> + if error? and error?.statusCode == 404 + logger.warn err: error, container_id: containerId, "container not found, continuing" + error = null + if error? + logger.error err: error, container_id: containerId, "error destroying container" + else + logger.log container_id: containerId, "destroyed container" + callback(error) + + # handle expiry of docker containers + + MAX_CONTAINER_AGE: Settings.clsi.docker.maxContainerAge or oneHour = 60 * 60 * 1000 + + examineOldContainer: (container, callback = (error, name, id, ttl)->) -> + name = container.Name or container.Names?[0] + created = container.Created * 1000 # creation time is returned in seconds + now = Date.now() + age = now - created + maxAge = DockerRunner.MAX_CONTAINER_AGE + ttl = maxAge - age + logger.log {containerName: name, created: created, now: now, age: age, maxAge: maxAge, ttl: ttl}, "checking whether to destroy container" + callback(null, name, container.Id, ttl) + + destroyOldContainers: (callback = (error) ->) -> + dockerode.listContainers all: true, (error, containers) -> + return callback(error) if error? + jobs = [] + for container in containers or [] + do (container) -> + DockerRunner.examineOldContainer container, (err, name, id, ttl) -> + if name.slice(0, 9) == '/project-' && ttl <= 0 + jobs.push (cb) -> + DockerRunner.destroyContainer name, id, false, () -> cb() + # Ignore errors because some containers get stuck but + # will be destroyed next time + async.series jobs, callback + + startContainerMonitor: () -> + logger.log {maxAge: DockerRunner.MAX_CONTAINER_AGE}, "starting container expiry" + # randomise the start time + randomDelay = Math.floor(Math.random() * 5 * 60 * 1000) + setTimeout () -> + setInterval () -> + DockerRunner.destroyOldContainers() + , oneHour = 60 * 60 * 1000 + , randomDelay + +DockerRunner.startContainerMonitor() \ No newline at end of file diff --git a/app/coffee/LatexRunner.coffee b/app/coffee/LatexRunner.coffee index 6a5a4f6..3571af2 100644 --- a/app/coffee/LatexRunner.coffee +++ b/app/coffee/LatexRunner.coffee @@ -2,7 +2,7 @@ Path = require "path" Settings = require "settings-sharelatex" logger = require "logger-sharelatex" Metrics = require "./Metrics" -CommandRunner = require(Settings.clsi?.commandRunner or "./CommandRunner") +CommandRunner = require "./CommandRunner" ProcessTable = {} # table of currently running jobs (pids or docker container names) diff --git a/app/coffee/LocalCommandRunner.coffee b/app/coffee/LocalCommandRunner.coffee new file mode 100644 index 0000000..f47af00 --- /dev/null +++ b/app/coffee/LocalCommandRunner.coffee @@ -0,0 +1,44 @@ +spawn = require("child_process").spawn +logger = require "logger-sharelatex" + +logger.info "using standard command runner" + +module.exports = CommandRunner = + run: (project_id, command, directory, image, timeout, environment, callback = (error) ->) -> + command = (arg.replace('$COMPILE_DIR', directory) for arg in command) + logger.log project_id: project_id, command: command, directory: directory, "running command" + logger.warn "timeouts and sandboxing are not enabled with CommandRunner" + + # merge environment settings + env = {} + env[key] = value for key, value of process.env + env[key] = value for key, value of environment + + # run command as detached process so it has its own process group (which can be killed if needed) + proc = spawn command[0], command.slice(1), stdio: "inherit", cwd: directory, detached: true, env: env + + proc.on "error", (err)-> + logger.err err:err, project_id:project_id, command: command, directory: directory, "error running command" + callback(err) + + proc.on "close", (code, signal) -> + logger.info code:code, signal:signal, project_id:project_id, "command exited" + if signal is 'SIGTERM' # signal from kill method below + err = new Error("terminated") + err.terminated = true + return callback(err) + else if code is 1 # exit status from chktex + err = new Error("exited") + err.code = code + return callback(err) + else + callback() + + return proc.pid # return process id to allow job to be killed if necessary + + kill: (pid, callback = (error) ->) -> + try + process.kill -pid # kill all processes in group + catch err + return callback(err) + callback() diff --git a/config/settings.defaults.coffee b/config/settings.defaults.coffee index c29829e..c76b251 100644 --- a/config/settings.defaults.coffee +++ b/config/settings.defaults.coffee @@ -29,9 +29,9 @@ module.exports = project_cache_length_ms: 1000 * 60 * 60 * 24 parallelFileDownloads:1 -if process.env["COMMAND_RUNNER"] +if process.env["DOCKER_RUNNER"] module.exports.clsi = - commandRunner: process.env["COMMAND_RUNNER"] + dockerRunner: process.env["DOCKER_RUNNER"] == "true" docker: image: process.env["TEXLIVE_IMAGE"] or "quay.io/sharelatex/texlive-full:2017.1" env: diff --git a/docker-compose-config.yml b/docker-compose-config.yml index 6e1fca4..55a3f01 100644 --- a/docker-compose-config.yml +++ b/docker-compose-config.yml @@ -6,12 +6,11 @@ services: TEXLIVE_IMAGE: quay.io/sharelatex/texlive-full:2017.1 TEXLIVE_IMAGE_USER: root SHARELATEX_CONFIG: /app/config/settings.defaults.coffee - COMMAND_RUNNER: docker-runner-sharelatex + DOCKER_RUNNER: "true" COMPILES_HOST_DIR: $PWD/compiles SYNCTEX_BIN_HOST_PATH: $PWD/bin/synctex volumes: - /var/run/docker.sock:/var/run/docker.sock - - ./docker-runner:/app/node_modules/docker-runner-sharelatex - ./compiles:/app/compiles ci: @@ -19,10 +18,9 @@ services: TEXLIVE_IMAGE: quay.io/sharelatex/texlive-full:2017.1 TEXLIVE_IMAGE_USER: root SHARELATEX_CONFIG: /app/config/settings.defaults.coffee - COMMAND_RUNNER: docker-runner-sharelatex + DOCKER_RUNNER: "true" COMPILES_HOST_DIR: $PWD/compiles SYNCTEX_BIN_HOST_PATH: $PWD/bin/synctex volumes: - /var/run/docker.sock:/var/run/docker.sock:rw - - ./docker-runner:/app/node_modules/docker-runner-sharelatex - ./compiles:/app/compiles \ No newline at end of file diff --git a/package.json b/package.json index ae37955..6c5361c 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "compile": "npm run compile:app && npm run compile:test:acceptance && npm run compile:test:smoke", "start": "npm run compile:app && node app.js", "test:acceptance:_run": "mocha --recursive --reporter spec --timeout 15000 --exit $@ test/acceptance/js", - "test:unit:_run": "mocha --recursive --reporter spec $@ test/unit/js", + "test:unit:_run": "mocha --recursive --exit --reporter spec $@ test/unit/js", "test:unit": "npm run compile:app && npm run compile:unit_tests && npm run test:unit:_run -- $@", "compile:unit_tests": "[ -e test/unit ] && coffee -o test/unit/js -c test/unit/coffee || echo 'No unit tests to compile'", "compile:acceptance_tests": "[ -e test/acceptance ] && coffee -o test/acceptance/js -c test/acceptance/coffee || echo 'No acceptance tests to compile'", diff --git a/test/acceptance/coffee/SimpleLatexFileTests.coffee b/test/acceptance/coffee/SimpleLatexFileTests.coffee index 0d3337a..95b667b 100644 --- a/test/acceptance/coffee/SimpleLatexFileTests.coffee +++ b/test/acceptance/coffee/SimpleLatexFileTests.coffee @@ -21,7 +21,6 @@ describe "Simple LaTeX file", -> it "should return the PDF", -> pdf = Client.getOutputFile(@body, "pdf") - console.log @body pdf.type.should.equal "pdf" it "should return the log", -> diff --git a/test/acceptance/coffee/TimeoutTests.coffee b/test/acceptance/coffee/TimeoutTests.coffee index 9195474..877223a 100644 --- a/test/acceptance/coffee/TimeoutTests.coffee +++ b/test/acceptance/coffee/TimeoutTests.coffee @@ -23,7 +23,6 @@ describe "Timed out compile", -> Client.compile @project_id, @request, (@error, @res, @body) => done() it "should return a timeout error", -> - console.log @body.compile, "!!!1111" @body.compile.error.should.equal "container timed out" it "should return a timedout status", -> diff --git a/test/acceptance/coffee/helpers/Client.coffee b/test/acceptance/coffee/helpers/Client.coffee index 546c235..3913170 100644 --- a/test/acceptance/coffee/helpers/Client.coffee +++ b/test/acceptance/coffee/helpers/Client.coffee @@ -11,7 +11,6 @@ module.exports = Client = Math.random().toString(16).slice(2) compile: (project_id, data, callback = (error, res, body) ->) -> - console.log("#{@host}/project/#{project_id}/compile") request.post { url: "#{@host}/project/#{project_id}/compile" json: diff --git a/test/unit/coffee/CompileManagerTests.coffee b/test/unit/coffee/CompileManagerTests.coffee index 448e06c..1836c05 100644 --- a/test/unit/coffee/CompileManagerTests.coffee +++ b/test/unit/coffee/CompileManagerTests.coffee @@ -293,8 +293,6 @@ describe "CompileManager", -> # .should.equal true it "should call the callback with the parsed output", -> - console.log(@file_name, @line, @column) - console.log @callback.args[0] @callback .calledWith(null, [{ file: @file_name diff --git a/test/unit/coffee/DockerLockManagerTests.coffee b/test/unit/coffee/DockerLockManagerTests.coffee new file mode 100644 index 0000000..6161bec --- /dev/null +++ b/test/unit/coffee/DockerLockManagerTests.coffee @@ -0,0 +1,145 @@ +SandboxedModule = require('sandboxed-module') +sinon = require('sinon') +require('chai').should() +require "coffee-script" +modulePath = require('path').join __dirname, '../../../app/coffee/DockerLockManager' + +describe "LockManager", -> + beforeEach -> + @LockManager = SandboxedModule.require modulePath, requires: + "settings-sharelatex": @Settings = + clsi: docker: {} + "logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() } + + describe "runWithLock", -> + describe "with a single lock", -> + beforeEach (done) -> + @callback = sinon.stub() + @LockManager.runWithLock "lock-one", (releaseLock) -> + setTimeout () -> + releaseLock(null, "hello", "world") + , 100 + , (err, args...) => + @callback(err,args...) + done() + + it "should call the callback", -> + @callback.calledWith(null,"hello","world").should.equal true + + describe "with two locks", -> + beforeEach (done) -> + @callback1 = sinon.stub() + @callback2 = sinon.stub() + @LockManager.runWithLock "lock-one", (releaseLock) -> + setTimeout () -> + releaseLock(null, "hello", "world","one") + , 100 + , (err, args...) => + @callback1(err,args...) + @LockManager.runWithLock "lock-two", (releaseLock) -> + setTimeout () -> + releaseLock(null, "hello", "world","two") + , 200 + , (err, args...) => + @callback2(err,args...) + done() + + it "should call the first callback", -> + @callback1.calledWith(null,"hello","world","one").should.equal true + + it "should call the second callback", -> + @callback2.calledWith(null,"hello","world","two").should.equal true + + describe "with lock contention", -> + describe "where the first lock is released quickly", -> + beforeEach (done) -> + @LockManager.MAX_LOCK_WAIT_TIME = 1000 + @LockManager.LOCK_TEST_INTERVAL = 100 + @callback1 = sinon.stub() + @callback2 = sinon.stub() + @LockManager.runWithLock "lock", (releaseLock) -> + setTimeout () -> + releaseLock(null, "hello", "world","one") + , 100 + , (err, args...) => + @callback1(err,args...) + @LockManager.runWithLock "lock", (releaseLock) -> + setTimeout () -> + releaseLock(null, "hello", "world","two") + , 200 + , (err, args...) => + @callback2(err,args...) + done() + + it "should call the first callback", -> + @callback1.calledWith(null,"hello","world","one").should.equal true + + it "should call the second callback", -> + @callback2.calledWith(null,"hello","world","two").should.equal true + + describe "where the first lock is held longer than the waiting time", -> + beforeEach (done) -> + @LockManager.MAX_LOCK_HOLD_TIME = 10000 + @LockManager.MAX_LOCK_WAIT_TIME = 1000 + @LockManager.LOCK_TEST_INTERVAL = 100 + @callback1 = sinon.stub() + @callback2 = sinon.stub() + doneOne = doneTwo = false + finish = (key) -> + doneOne = true if key is 1 + doneTwo = true if key is 2 + done() if doneOne and doneTwo + @LockManager.runWithLock "lock", (releaseLock) -> + setTimeout () -> + releaseLock(null, "hello", "world","one") + , 1100 + , (err, args...) => + @callback1(err,args...) + finish(1) + @LockManager.runWithLock "lock", (releaseLock) -> + setTimeout () -> + releaseLock(null, "hello", "world","two") + , 100 + , (err, args...) => + @callback2(err,args...) + finish(2) + + it "should call the first callback", -> + @callback1.calledWith(null,"hello","world","one").should.equal true + + it "should call the second callback with an error", -> + error = sinon.match.instanceOf Error + @callback2.calledWith(error).should.equal true + + describe "where the first lock is held longer than the max holding time", -> + beforeEach (done) -> + @LockManager.MAX_LOCK_HOLD_TIME = 1000 + @LockManager.MAX_LOCK_WAIT_TIME = 2000 + @LockManager.LOCK_TEST_INTERVAL = 100 + @callback1 = sinon.stub() + @callback2 = sinon.stub() + doneOne = doneTwo = false + finish = (key) -> + doneOne = true if key is 1 + doneTwo = true if key is 2 + done() if doneOne and doneTwo + @LockManager.runWithLock "lock", (releaseLock) -> + setTimeout () -> + releaseLock(null, "hello", "world","one") + , 1500 + , (err, args...) => + @callback1(err,args...) + finish(1) + @LockManager.runWithLock "lock", (releaseLock) -> + setTimeout () -> + releaseLock(null, "hello", "world","two") + , 100 + , (err, args...) => + @callback2(err,args...) + finish(2) + + it "should call the first callback", -> + @callback1.calledWith(null,"hello","world","one").should.equal true + + it "should call the second callback", -> + @callback2.calledWith(null,"hello","world","two").should.equal true diff --git a/test/unit/coffee/DockerRunnerTests.coffee b/test/unit/coffee/DockerRunnerTests.coffee new file mode 100644 index 0000000..5a697e2 --- /dev/null +++ b/test/unit/coffee/DockerRunnerTests.coffee @@ -0,0 +1,499 @@ +SandboxedModule = require('sandboxed-module') +sinon = require('sinon') +require('chai').should() +expect = require('chai').expect +require "coffee-script" +modulePath = require('path').join __dirname, '../../../app/coffee/DockerRunner' +Path = require "path" + +describe "DockerRunner", -> + beforeEach -> + @container = container = {} + @DockerRunner = SandboxedModule.require modulePath, requires: + "settings-sharelatex": @Settings = + clsi: docker: {} + path: {} + "logger-sharelatex": @logger = { + log: sinon.stub(), + error: sinon.stub(), + info: sinon.stub(), + warn: sinon.stub() + } + "dockerode": class Docker + getContainer: sinon.stub().returns(container) + createContainer: sinon.stub().yields(null, container) + listContainers: sinon.stub() + "fs": @fs = { stat: sinon.stub().yields(null,{isDirectory:()->true}) } + "./Metrics": + Timer: class Timer + done: () -> + "./LockManager": + runWithLock: (key, runner, callback) -> runner(callback) + @Docker = Docker + @getContainer = Docker::getContainer + @createContainer = Docker::createContainer + @listContainers = Docker::listContainers + + @directory = "/local/compile/directory" + @mainFile = "main-file.tex" + @compiler = "pdflatex" + @image = "example.com/sharelatex/image:2016.2" + @env = {} + @callback = sinon.stub() + @project_id = "project-id-123" + @volumes = + "/local/compile/directory": "/compile" + @Settings.clsi.docker.image = @defaultImage = "default-image" + @Settings.clsi.docker.env = PATH: "mock-path" + + describe "run", -> + beforeEach (done)-> + @DockerRunner._getContainerOptions = sinon.stub().returns(@options = {mockoptions: "foo"}) + @DockerRunner._fingerprintContainer = sinon.stub().returns(@fingerprint = "fingerprint") + + @name = "project-#{@project_id}-#{@fingerprint}" + + @command = ["mock", "command", "--outdir=$COMPILE_DIR"] + @command_with_dir = ["mock", "command", "--outdir=/compile"] + @timeout = 42000 + done() + + describe "successfully", -> + beforeEach (done)-> + @DockerRunner._runAndWaitForContainer = sinon.stub().callsArgWith(3, null, @output = "mock-output") + @DockerRunner.run @project_id, @command, @directory, @image, @timeout, @env, (err, output)=> + @callback(err, output) + done() + + it "should generate the options for the container", -> + @DockerRunner._getContainerOptions + .calledWith(@command_with_dir, @image, @volumes, @timeout) + .should.equal true + + it "should generate the fingerprint from the returned options", -> + @DockerRunner._fingerprintContainer + .calledWith(@options) + .should.equal true + + it "should do the run", -> + @DockerRunner._runAndWaitForContainer + .calledWith(@options, @volumes, @timeout) + .should.equal true + + it "should call the callback", -> + @callback.calledWith(null, @output).should.equal true + + describe 'when path.sandboxedCompilesHostDir is set', -> + + beforeEach -> + @Settings.path.sandboxedCompilesHostDir = '/some/host/dir/compiles' + @directory = '/var/lib/sharelatex/data/compiles/xyz' + @DockerRunner._runAndWaitForContainer = sinon.stub().callsArgWith(3, null, @output = "mock-output") + @DockerRunner.run @project_id, @command, @directory, @image, @timeout, @env, @callback + + it 'should re-write the bind directory', -> + volumes = @DockerRunner._runAndWaitForContainer.lastCall.args[1] + expect(volumes).to.deep.equal { + '/some/host/dir/compiles/xyz': '/compile' + } + + it "should call the callback", -> + @callback.calledWith(null, @output).should.equal true + + describe "when the run throws an error", -> + beforeEach -> + firstTime = true + @output = "mock-output" + @DockerRunner._runAndWaitForContainer = (options, volumes, timeout, callback = (error, output)->) => + if firstTime + firstTime = false + callback new Error("HTTP code is 500 which indicates error: server error") + else + callback(null, @output) + sinon.spy @DockerRunner, "_runAndWaitForContainer" + @DockerRunner.destroyContainer = sinon.stub().callsArg(3) + @DockerRunner.run @project_id, @command, @directory, @image, @timeout, @env, @callback + + it "should do the run twice", -> + @DockerRunner._runAndWaitForContainer + .calledTwice.should.equal true + + it "should destroy the container in between", -> + @DockerRunner.destroyContainer + .calledWith(@name, null) + .should.equal true + + it "should call the callback", -> + @callback.calledWith(null, @output).should.equal true + + describe "with no image", -> + beforeEach -> + @DockerRunner._runAndWaitForContainer = sinon.stub().callsArgWith(3, null, @output = "mock-output") + @DockerRunner.run @project_id, @command, @directory, null, @timeout, @env, @callback + + it "should use the default image", -> + @DockerRunner._getContainerOptions + .calledWith(@command_with_dir, @defaultImage, @volumes, @timeout) + .should.equal true + + describe "_runAndWaitForContainer", -> + beforeEach -> + @options = {mockoptions: "foo", name: @name = "mock-name"} + @DockerRunner.startContainer = (options, volumes, attachStreamHandler, callback) => + attachStreamHandler(null, @output = "mock-output") + callback(null, @containerId = "container-id") + sinon.spy @DockerRunner, "startContainer" + @DockerRunner.waitForContainer = sinon.stub().callsArgWith(2, null, @exitCode = 42) + @DockerRunner._runAndWaitForContainer @options, @volumes, @timeout, @callback + + it "should create/start the container", -> + @DockerRunner.startContainer + .calledWith(@options, @volumes) + .should.equal true + + it "should wait for the container to finish", -> + @DockerRunner.waitForContainer + .calledWith(@name, @timeout) + .should.equal true + + it "should call the callback with the output", -> + @callback.calledWith(null, @output).should.equal true + + describe "startContainer", -> + beforeEach -> + @attachStreamHandler = sinon.stub() + @attachStreamHandler.cock = true + @options = {mockoptions: "foo", name: "mock-name"} + @container.inspect = sinon.stub().callsArgWith(0) + @DockerRunner.attachToContainer = (containerId, attachStreamHandler, cb)=> + attachStreamHandler() + cb() + sinon.spy @DockerRunner, "attachToContainer" + + + + describe "when the container exists", -> + beforeEach -> + @container.inspect = sinon.stub().callsArgWith(0) + @container.start = sinon.stub().yields() + + @DockerRunner.startContainer @options, @volumes, @callback, -> + + it "should start the container with the given name", -> + @getContainer + .calledWith(@options.name) + .should.equal true + @container.start + .called + .should.equal true + + it "should not try to create the container", -> + @createContainer.called.should.equal false + + it "should attach to the container", -> + @DockerRunner.attachToContainer.called.should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + + it "should attach before the container starts", -> + sinon.assert.callOrder(@DockerRunner.attachToContainer, @container.start) + + describe "when the container does not exist", -> + beforeEach ()-> + exists = false + @container.start = sinon.stub().yields() + @container.inspect = sinon.stub().callsArgWith(0, {statusCode:404}) + @DockerRunner.startContainer @options, @volumes, @attachStreamHandler, @callback + + it "should create the container", -> + @createContainer + .calledWith(@options) + .should.equal true + + it "should call the callback and stream handler", -> + @attachStreamHandler.called.should.equal true + @callback.called.should.equal true + + it "should attach to the container", -> + @DockerRunner.attachToContainer.called.should.equal true + + it "should attach before the container starts", -> + sinon.assert.callOrder(@DockerRunner.attachToContainer, @container.start) + + + describe "when the container is already running", -> + beforeEach -> + error = new Error("HTTP code is 304 which indicates error: server error - start: Cannot start container #{@name}: The container MOCKID is already running.") + error.statusCode = 304 + @container.start = sinon.stub().yields(error) + @container.inspect = sinon.stub().callsArgWith(0) + @DockerRunner.startContainer @options, @volumes, @attachStreamHandler, @callback + + it "should not try to create the container", -> + @createContainer.called.should.equal false + + it "should call the callback and stream handler without an error", -> + @attachStreamHandler.called.should.equal true + @callback.called.should.equal true + + describe "when a volume does not exist", -> + beforeEach ()-> + @fs.stat = sinon.stub().yields(new Error("no such path")) + @DockerRunner.startContainer @options, @volumes, @attachStreamHandler, @callback + + it "should not try to create the container", -> + @createContainer.called.should.equal false + + it "should call the callback with an error", -> + @callback.calledWith(new Error()).should.equal true + + describe "when a volume exists but is not a directory", -> + beforeEach -> + @fs.stat = sinon.stub().yields(null, {isDirectory: () -> return false}) + @DockerRunner.startContainer @options, @volumes, @attachStreamHandler, @callback + + it "should not try to create the container", -> + @createContainer.called.should.equal false + + it "should call the callback with an error", -> + @callback.calledWith(new Error()).should.equal true + + describe "when a volume does not exist, but sibling-containers are used", -> + beforeEach -> + @fs.stat = sinon.stub().yields(new Error("no such path")) + @Settings.path.sandboxedCompilesHostDir = '/some/path' + @container.start = sinon.stub().yields() + @DockerRunner.startContainer @options, @volumes, @callback + + afterEach -> + delete @Settings.path.sandboxedCompilesHostDir + + it "should start the container with the given name", -> + @getContainer + .calledWith(@options.name) + .should.equal true + @container.start + .called + .should.equal true + + it "should not try to create the container", -> + @createContainer.called.should.equal false + + it "should call the callback", -> + @callback.called.should.equal true + @callback.calledWith(new Error()).should.equal false + + describe "when the container tries to be created, but already has been (race condition)", -> + + describe "waitForContainer", -> + beforeEach -> + @containerId = "container-id" + @timeout = 5000 + @container.wait = sinon.stub().yields(null, StatusCode: @statusCode = 42) + @container.kill = sinon.stub().yields() + + describe "when the container returns in time", -> + beforeEach -> + @DockerRunner.waitForContainer @containerId, @timeout, @callback + + it "should wait for the container", -> + @getContainer + .calledWith(@containerId) + .should.equal true + @container.wait + .called + .should.equal true + + it "should call the callback with the exit", -> + @callback + .calledWith(null, @statusCode) + .should.equal true + + describe "when the container does not return before the timeout", -> + beforeEach (done) -> + @container.wait = (callback = (error, exitCode) ->) -> + setTimeout () -> + callback(null, StatusCode: 42) + , 100 + @timeout = 5 + @DockerRunner.waitForContainer @containerId, @timeout, (args...) => + @callback(args...) + done() + + it "should call kill on the container", -> + @getContainer + .calledWith(@containerId) + .should.equal true + @container.kill + .called + .should.equal true + + it "should call the callback with an error", -> + error = new Error("container timed out") + error.timedout = true + @callback + .calledWith(error) + .should.equal true + + describe "destroyOldContainers", -> + beforeEach (done) -> + oneHourInSeconds = 60 * 60 + oneHourInMilliseconds = oneHourInSeconds * 1000 + nowInSeconds = Date.now()/1000 + @containers = [{ + Name: "/project-old-container-name" + Id: "old-container-id" + Created: nowInSeconds - oneHourInSeconds - 100 + }, { + Name: "/project-new-container-name" + Id: "new-container-id" + Created: nowInSeconds - oneHourInSeconds + 100 + }, { + Name: "/totally-not-a-project-container" + Id: "some-random-id" + Created: nowInSeconds - (2 * oneHourInSeconds ) + }] + @DockerRunner.MAX_CONTAINER_AGE = oneHourInMilliseconds + @listContainers.callsArgWith(1, null, @containers) + @DockerRunner.destroyContainer = sinon.stub().callsArg(3) + @DockerRunner.destroyOldContainers (error) => + @callback(error) + done() + + it "should list all containers", -> + @listContainers + .calledWith(all: true) + .should.equal true + + it "should destroy old containers", -> + @DockerRunner.destroyContainer + .callCount + .should.equal 1 + @DockerRunner.destroyContainer + .calledWith("/project-old-container-name", "old-container-id") + .should.equal true + + it "should not destroy new containers", -> + @DockerRunner.destroyContainer + .calledWith("/project-new-container-name", "new-container-id") + .should.equal false + + it "should not destroy non-project containers", -> + @DockerRunner.destroyContainer + .calledWith("/totally-not-a-project-container", "some-random-id") + .should.equal false + + it "should callback the callback", -> + @callback.called.should.equal true + + + describe '_destroyContainer', -> + beforeEach -> + @containerId = 'some_id' + @fakeContainer = + remove: sinon.stub().callsArgWith(1, null) + @Docker::getContainer = sinon.stub().returns(@fakeContainer) + + it 'should get the container', (done) -> + @DockerRunner._destroyContainer @containerId, false, (err) => + @Docker::getContainer.callCount.should.equal 1 + @Docker::getContainer.calledWith(@containerId).should.equal true + done() + + it 'should try to force-destroy the container when shouldForce=true', (done) -> + @DockerRunner._destroyContainer @containerId, true, (err) => + @fakeContainer.remove.callCount.should.equal 1 + @fakeContainer.remove.calledWith({force: true}).should.equal true + done() + + it 'should not try to force-destroy the container when shouldForce=false', (done) -> + @DockerRunner._destroyContainer @containerId, false, (err) => + @fakeContainer.remove.callCount.should.equal 1 + @fakeContainer.remove.calledWith({force: false}).should.equal true + done() + + it 'should not produce an error', (done) -> + @DockerRunner._destroyContainer @containerId, false, (err) => + expect(err).to.equal null + done() + + describe 'when the container is already gone', -> + beforeEach -> + @fakeError = new Error('woops') + @fakeError.statusCode = 404 + @fakeContainer = + remove: sinon.stub().callsArgWith(1, @fakeError) + @Docker::getContainer = sinon.stub().returns(@fakeContainer) + + it 'should not produce an error', (done) -> + @DockerRunner._destroyContainer @containerId, false, (err) => + expect(err).to.equal null + done() + + describe 'when container.destroy produces an error', (done) -> + beforeEach -> + @fakeError = new Error('woops') + @fakeError.statusCode = 500 + @fakeContainer = + remove: sinon.stub().callsArgWith(1, @fakeError) + @Docker::getContainer = sinon.stub().returns(@fakeContainer) + + it 'should produce an error', (done) -> + @DockerRunner._destroyContainer @containerId, false, (err) => + expect(err).to.not.equal null + expect(err).to.equal @fakeError + done() + + + describe 'kill', -> + beforeEach -> + @containerId = 'some_id' + @fakeContainer = + kill: sinon.stub().callsArgWith(0, null) + @Docker::getContainer = sinon.stub().returns(@fakeContainer) + + it 'should get the container', (done) -> + @DockerRunner.kill @containerId, (err) => + @Docker::getContainer.callCount.should.equal 1 + @Docker::getContainer.calledWith(@containerId).should.equal true + done() + + it 'should try to force-destroy the container', (done) -> + @DockerRunner.kill @containerId, (err) => + @fakeContainer.kill.callCount.should.equal 1 + done() + + it 'should not produce an error', (done) -> + @DockerRunner.kill @containerId, (err) => + expect(err).to.equal undefined + done() + + describe 'when the container is not actually running', -> + beforeEach -> + @fakeError = new Error('woops') + @fakeError.statusCode = 500 + @fakeError.message = 'Cannot kill container is not running' + @fakeContainer = + kill: sinon.stub().callsArgWith(0, @fakeError) + @Docker::getContainer = sinon.stub().returns(@fakeContainer) + + it 'should not produce an error', (done) -> + @DockerRunner.kill @containerId, (err) => + expect(err).to.equal undefined + done() + + describe 'when container.kill produces a legitimate error', (done) -> + beforeEach -> + @fakeError = new Error('woops') + @fakeError.statusCode = 500 + @fakeError.message = 'Totally legitimate reason to throw an error' + @fakeContainer = + kill: sinon.stub().callsArgWith(0, @fakeError) + @Docker::getContainer = sinon.stub().returns(@fakeContainer) + + it 'should produce an error', (done) -> + @DockerRunner.kill @containerId, (err) => + expect(err).to.not.equal undefined + expect(err).to.equal @fakeError + done() diff --git a/test/unit/coffee/LockManager.coffee b/test/unit/coffee/LockManagerTests.coffee similarity index 98% rename from test/unit/coffee/LockManager.coffee rename to test/unit/coffee/LockManagerTests.coffee index c1071a5..2d0c95a 100644 --- a/test/unit/coffee/LockManager.coffee +++ b/test/unit/coffee/LockManagerTests.coffee @@ -5,7 +5,7 @@ modulePath = require('path').join __dirname, '../../../app/js/LockManager' Path = require "path" Errors = require "../../../app/js/Errors" -describe "LockManager", -> +describe "DockerLockManager", -> beforeEach -> @LockManager = SandboxedModule.require modulePath, requires: "settings-sharelatex": {} From f1df41112b0127b56fd3912e96263539cfe76875 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Mon, 5 Mar 2018 15:29:12 +0000 Subject: [PATCH 017/118] wip for ci --- Jenkinsfile | 2 +- config/settings.defaults.coffee | 2 +- docker-compose-config.yml | 1 + docker-compose.ci.yml | 2 +- install_deps.sh | 2 ++ 5 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index bc9ba01..8607d39 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -23,7 +23,7 @@ pipeline { stage('Acceptance Tests') { steps { - sh 'DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" make test_acceptance' + sh 'DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" test_acceptance' } } diff --git a/config/settings.defaults.coffee b/config/settings.defaults.coffee index c76b251..3100a5d 100644 --- a/config/settings.defaults.coffee +++ b/config/settings.defaults.coffee @@ -9,7 +9,7 @@ module.exports = username: "clsi" password: null dialect: "sqlite" - storage: Path.resolve(__dirname + "/../db.sqlite") + storage: process.env["SQLITE_PATH"] or Path.resolve(__dirname + "/../db.sqlite") path: compilesDir: Path.resolve(__dirname + "/../compiles") diff --git a/docker-compose-config.yml b/docker-compose-config.yml index 55a3f01..f2bb863 100644 --- a/docker-compose-config.yml +++ b/docker-compose-config.yml @@ -21,6 +21,7 @@ services: DOCKER_RUNNER: "true" COMPILES_HOST_DIR: $PWD/compiles SYNCTEX_BIN_HOST_PATH: $PWD/bin/synctex + SQLITE_PATH: /app/compiles/db.sqlite volumes: - /var/run/docker.sock:/var/run/docker.sock:rw - ./compiles:/app/compiles \ No newline at end of file diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index 2ca2f9a..98715c2 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -15,7 +15,7 @@ services: image: quay.io/sharelatex/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER extends: file: docker-compose-config.yml - service: dev + service: ci environment: REDIS_HOST: redis MONGO_HOST: mongo diff --git a/install_deps.sh b/install_deps.sh index bde142a..dce17a4 100755 --- a/install_deps.sh +++ b/install_deps.sh @@ -6,3 +6,5 @@ usermod -aG docker app touch /var/run/docker.sock chown root:docker /var/run/docker.sock + +chown -R app:app /app/cache From 2f96350b7cf31b549f02c54f9877f94ba0399e02 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Mon, 5 Mar 2018 15:30:15 +0000 Subject: [PATCH 018/118] removed unused scripts --- package.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/package.json b/package.json index 6c5361c..771ee96 100644 --- a/package.json +++ b/package.json @@ -7,16 +7,13 @@ "url": "https://github.com/sharelatex/clsi-sharelatex.git" }, "scripts": { - "test:acceptance:wait_for_app": "echo 'Waiting for app to be accessible' && while (! curl -s -o /dev/null localhost:3013/status) do sleep 1; done", - "test:acceptance:run": "mocha --recursive --reporter spec --timeout 15000", - "test:acceptance:dir": "npm -q run test:acceptance:wait_for_app && npm -q run test:acceptance:run -- $@", - "test:acceptance": "npm run compile:app && npm run compile:acceptance_tests && npm run test:acceptance:_run -- $@", "compile:test:acceptance": "coffee -o test/acceptance/js -c test/acceptance/coffee", "compile:test:smoke": "coffee -o test/smoke/js -c test/smoke/coffee", "compile:app": "coffee -o app/js -c app/coffee && coffee -c app.coffee", "compile": "npm run compile:app && npm run compile:test:acceptance && npm run compile:test:smoke", "start": "npm run compile:app && node app.js", "test:acceptance:_run": "mocha --recursive --reporter spec --timeout 15000 --exit $@ test/acceptance/js", + "test:acceptance": "npm run compile:app && npm run compile:acceptance_tests && npm run test:acceptance:_run -- $@", "test:unit:_run": "mocha --recursive --exit --reporter spec $@ test/unit/js", "test:unit": "npm run compile:app && npm run compile:unit_tests && npm run test:unit:_run -- $@", "compile:unit_tests": "[ -e test/unit ] && coffee -o test/unit/js -c test/unit/coffee || echo 'No unit tests to compile'", From 177c46df985b7d194cf7b5771fa166212530d401 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Mon, 5 Mar 2018 15:35:08 +0000 Subject: [PATCH 019/118] add cache dir --- install_deps.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install_deps.sh b/install_deps.sh index dce17a4..078c311 100755 --- a/install_deps.sh +++ b/install_deps.sh @@ -7,4 +7,5 @@ usermod -aG docker app touch /var/run/docker.sock chown root:docker /var/run/docker.sock +mkdir -p /app/cache chown -R app:app /app/cache From 00cf5468d090a86f428067ce10703ccfbd01827c Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Mon, 5 Mar 2018 15:40:33 +0000 Subject: [PATCH 020/118] update jenkins task --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 8607d39..993d22f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -23,7 +23,7 @@ pipeline { stage('Acceptance Tests') { steps { - sh 'DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" test_acceptance' + sh 'DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" up --build test_acceptance' } } From 4e6514b17e6cbf97dc90f53098369fc4bf4c264d Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Mon, 5 Mar 2018 16:02:16 +0000 Subject: [PATCH 021/118] add logging in db.coffee --- app/coffee/db.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/coffee/db.coffee b/app/coffee/db.coffee index a72f61e..cb63359 100644 --- a/app/coffee/db.coffee +++ b/app/coffee/db.coffee @@ -1,6 +1,7 @@ Sequelize = require("sequelize") Settings = require("settings-sharelatex") _ = require("underscore") +logger = require "logger-sharelatex" options = _.extend {logging:false}, Settings.mysql.clsi @@ -32,5 +33,7 @@ module.exports = ] }) - sync: () -> sequelize.sync() + sync: () -> + logger.log dbPath:Settings.mysql.clsi.storage, "syncing db schema" + sequelize.sync() From 96a237fb749e1dfe9d9d264f4f2d66b73f51b17c Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 6 Mar 2018 10:30:05 +0000 Subject: [PATCH 022/118] removed user temporarly, created make ci task --- Dockerfile | 2 +- Jenkinsfile | 22 ++-------------------- Makefile | 12 ++++++++++++ docker-compose.ci.yml | 9 +-------- docker-compose.yml | 6 ------ 5 files changed, 16 insertions(+), 35 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3387394..5826661 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,5 +23,5 @@ RUN useradd --user-group --create-home --home-dir /app --shell /bin/false app RUN [ -e ./install_deps.sh ] && ./install_deps.sh -USER app +# USER app CMD ["node","app.js"] diff --git a/Jenkinsfile b/Jenkinsfile index 993d22f..e92cfbe 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -9,27 +9,9 @@ pipeline { } stages { - stage('Build') { + stage('CI') { steps { - sh 'make build' - } - } - - stage('Unit Tests') { - steps { - sh 'DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" make test_unit' - } - } - - stage('Acceptance Tests') { - steps { - sh 'DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" up --build test_acceptance' - } - } - - stage('Package and publish build') { - steps { - sh 'make publish' + sh 'make ci' } } diff --git a/Makefile b/Makefile index b20654c..450efc8 100644 --- a/Makefile +++ b/Makefile @@ -28,10 +28,22 @@ test_acceptance: test_clean # clear the database before each acceptance test run test_clean: $(DOCKER_COMPOSE) down -t 0 + build: docker build --pull --tag quay.io/sharelatex/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) . publish: docker push quay.io/sharelatex/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) +ci: + # On the CI server, we want to run our tests in the image that we + # have built for deployment, which is what the docker-compose.ci.yml + # override does. + PROJECT_NAME=$(PROJECT_NAME) \ + BRANCH_NAME=$(BRANCH_NAME) \ + BUILD_NUMBER=$(BUILD_NUMBER) \ + DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" \ + $(MAKE) build test + + .PHONY: clean test test_unit test_acceptance test_clean build publish diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index 98715c2..16a5da3 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -12,17 +12,10 @@ services: test_acceptance: build: . - image: quay.io/sharelatex/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER extends: file: docker-compose-config.yml service: ci - environment: - REDIS_HOST: redis - MONGO_HOST: mongo - depends_on: - - redis - - mongo - entrypoint: npm run test:acceptance:_run + entrypoint: npm run test:acceptance redis: image: redis diff --git a/docker-compose.yml b/docker-compose.yml index cf90742..2469482 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,12 +21,6 @@ services: extends: file: docker-compose-config.yml service: dev - environment: - REDIS_HOST: redis - MONGO_HOST: mongo - depends_on: - - redis - - mongo entrypoint: npm run test:acceptance redis: From aca9100c52a5c63511d013eaf8dbfe44f0d143bd Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 6 Mar 2018 10:59:40 +0000 Subject: [PATCH 023/118] set entry point for dockerfile --- Dockerfile | 3 +-- docker-compose.yml | 2 +- entrypoint.sh | 5 +++++ 3 files changed, 7 insertions(+), 3 deletions(-) create mode 100755 entrypoint.sh diff --git a/Dockerfile b/Dockerfile index 5826661..db863cd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,5 +23,4 @@ RUN useradd --user-group --create-home --home-dir /app --shell /bin/false app RUN [ -e ./install_deps.sh ] && ./install_deps.sh -# USER app -CMD ["node","app.js"] +ENTRYPOINT ["/bin/sh", "entrypoint.sh"] diff --git a/docker-compose.yml b/docker-compose.yml index 2469482..e6af23b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,7 +21,7 @@ services: extends: file: docker-compose-config.yml service: dev - entrypoint: npm run test:acceptance + command: npm run test:acceptance redis: image: redis diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100755 index 0000000..588854e --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +echo "Changing permissions of /var/run/docker.sock for sibling containers" +chown root:docker /var/run/docker.sock +exec runuser -u app "$@" \ No newline at end of file From 4ff11213531fea8c21c1fdb63f83d7d81b13c7b8 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 6 Mar 2018 11:26:43 +0000 Subject: [PATCH 024/118] add cmd back in --- Dockerfile | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index db863cd..de5f218 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,6 @@ WORKDIR /app RUN npm install - RUN npm run compile:all FROM node:6.13.0 @@ -15,12 +14,9 @@ COPY --from=app /app /app WORKDIR /app - -# All app and node_modules will be owned by root. -# The app will run as the 'app' user, and so not have write permissions -# on any files it doesn't need. RUN useradd --user-group --create-home --home-dir /app --shell /bin/false app RUN [ -e ./install_deps.sh ] && ./install_deps.sh ENTRYPOINT ["/bin/sh", "entrypoint.sh"] +CMD ["node","app.js"] From 3c4870f68850f77e4a855392c9d84af60c0287f3 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 6 Mar 2018 11:27:51 +0000 Subject: [PATCH 025/118] =?UTF-8?q?remove=20touch=20/var/run/docker.sock?= =?UTF-8?q?=20which=20doesn=E2=80=99t=20work=20robustly?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- install_deps.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/install_deps.sh b/install_deps.sh index 078c311..1883949 100755 --- a/install_deps.sh +++ b/install_deps.sh @@ -4,8 +4,5 @@ apt-get install poppler-utils vim ghostscript --yes npm rebuild usermod -aG docker app -touch /var/run/docker.sock -chown root:docker /var/run/docker.sock - mkdir -p /app/cache chown -R app:app /app/cache From 7dbed15fea2d74f1b5c7f438e6e82c12e834a71f Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 6 Mar 2018 12:07:54 +0000 Subject: [PATCH 026/118] update scripts from latest build scripts 1.1.0 --- Dockerfile | 7 ++++++- Jenkinsfile | 1 - Makefile | 5 ++--- docker-compose.ci.yml | 11 +++++++++-- docker-compose.yml | 8 +++++++- package.json | 2 +- 6 files changed, 25 insertions(+), 9 deletions(-) diff --git a/Dockerfile b/Dockerfile index de5f218..62ef342 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app RUN npm install + RUN npm run compile:all FROM node:6.13.0 @@ -14,9 +15,13 @@ COPY --from=app /app /app WORKDIR /app + +# All app and node_modules will be owned by root. +# The app will run as the 'app' user, and so not have write permissions +# on any files it doesn't need. RUN useradd --user-group --create-home --home-dir /app --shell /bin/false app RUN [ -e ./install_deps.sh ] && ./install_deps.sh - ENTRYPOINT ["/bin/sh", "entrypoint.sh"] + CMD ["node","app.js"] diff --git a/Jenkinsfile b/Jenkinsfile index e92cfbe..7a34ee7 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -14,7 +14,6 @@ pipeline { sh 'make ci' } } - stage('Publish build number') { steps { sh 'echo ${BRANCH_NAME}-${BUILD_NUMBER} > build_number.txt' diff --git a/Makefile b/Makefile index 450efc8..063d96e 100644 --- a/Makefile +++ b/Makefile @@ -28,13 +28,12 @@ test_acceptance: test_clean # clear the database before each acceptance test run test_clean: $(DOCKER_COMPOSE) down -t 0 - build: docker build --pull --tag quay.io/sharelatex/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) . publish: docker push quay.io/sharelatex/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) - + ci: # On the CI server, we want to run our tests in the image that we # have built for deployment, which is what the docker-compose.ci.yml @@ -43,7 +42,7 @@ ci: BRANCH_NAME=$(BRANCH_NAME) \ BUILD_NUMBER=$(BUILD_NUMBER) \ DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" \ - $(MAKE) build test + $(MAKE) build test publish .PHONY: clean test test_unit test_acceptance test_clean build publish diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index 16a5da3..2ca2f9a 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -12,10 +12,17 @@ services: test_acceptance: build: . + image: quay.io/sharelatex/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER extends: file: docker-compose-config.yml - service: ci - entrypoint: npm run test:acceptance + service: dev + environment: + REDIS_HOST: redis + MONGO_HOST: mongo + depends_on: + - redis + - mongo + entrypoint: npm run test:acceptance:_run redis: image: redis diff --git a/docker-compose.yml b/docker-compose.yml index e6af23b..cf90742 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,7 +21,13 @@ services: extends: file: docker-compose-config.yml service: dev - command: npm run test:acceptance + environment: + REDIS_HOST: redis + MONGO_HOST: mongo + depends_on: + - redis + - mongo + entrypoint: npm run test:acceptance redis: image: redis diff --git a/package.json b/package.json index 771ee96..fed3260 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "compile:all": "npm run compile:app && npm run compile:unit_tests && npm run compile:acceptance_tests", "nodemon": "nodemon --config nodemon.json" }, - "author": "James Allen ", + "author": "James Allen ", "dependencies": { "async": "0.2.9", "body-parser": "^1.2.0", From b9d94fb428c08cf8828da271b7074497033a1043 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 6 Mar 2018 13:06:10 +0000 Subject: [PATCH 027/118] fixed commended tests --- test/unit/coffee/CompileManagerTests.coffee | 37 +++++++++++++-------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/test/unit/coffee/CompileManagerTests.coffee b/test/unit/coffee/CompileManagerTests.coffee index 1836c05..93b7f11 100644 --- a/test/unit/coffee/CompileManagerTests.coffee +++ b/test/unit/coffee/CompileManagerTests.coffee @@ -259,13 +259,19 @@ describe "CompileManager", -> @CommandRunner.run = sinon.stub().callsArgWith(6, null, {stdout:@stdout}) @CompileManager.syncFromCode @project_id, @user_id, @file_name, @line, @column, @callback - # it "should execute the synctex binary", -> - # bin_path = Path.resolve(__dirname + "/../../../bin/synctex") - # synctex_path = "#{@Settings.path.compilesDir}/#{@project_id}-#{@user_id}/output.pdf" - # file_path = "#{@Settings.path.compilesDir}/#{@project_id}-#{@user_id}/#{@file_name}" - # @child_process.execFile - # .calledWith(bin_path, ["code", synctex_path, file_path, @line, @column], timeout: 10000) - # .should.equal true + it "should execute the synctex binary", -> + bin_path = Path.resolve(__dirname + "/../../../bin/synctex") + synctex_path = "#{@Settings.path.compilesDir}/#{@project_id}-#{@user_id}/output.pdf" + file_path = "#{@Settings.path.compilesDir}/#{@project_id}-#{@user_id}/#{@file_name}" + @CommandRunner.run + .calledWith( + "#{@project_id}-#{@user_id}", + ['/opt/synctex', 'code', synctex_path, file_path, @line, @column], + "#{@Settings.path.compilesDir}/#{@project_id}-#{@user_id}", + @Settings.clsi.docker.image, + 10000, + {} + ).should.equal true it "should call the callback with the parsed output", -> @callback @@ -285,12 +291,17 @@ describe "CompileManager", -> @CommandRunner.run = sinon.stub().callsArgWith(6, null, {stdout:@stdout}) @CompileManager.syncFromPdf @project_id, @user_id, @page, @h, @v, @callback - # it "should execute the synctex binary", -> - # bin_path = Path.resolve(__dirname + "/../../../bin/synctex") - # synctex_path = "#{@Settings.path.compilesDir}/#{@project_id}-#{@user_id}/output.pdf" - # @CommandRunner.run - # .calledWith(bin_path, ["pdf", synctex_path, @page, @h, @v], timeout: 10000) - # .should.equal true + it "should execute the synctex binary", -> + bin_path = Path.resolve(__dirname + "/../../../bin/synctex") + synctex_path = "#{@Settings.path.compilesDir}/#{@project_id}-#{@user_id}/output.pdf" + @CommandRunner.run + .calledWith( + "#{@project_id}-#{@user_id}", + ['/opt/synctex', "pdf", synctex_path, @page, @h, @v], + "#{@Settings.path.compilesDir}/#{@project_id}-#{@user_id}", + @Settings.clsi.docker.image, + 10000, + {}).should.equal true it "should call the callback with the parsed output", -> @callback From 83c7068bd1e07672d9b0acd859947d1b1d41db72 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 6 Mar 2018 16:03:26 +0000 Subject: [PATCH 028/118] test new scripts on ci --- Jenkinsfile | 23 +++++++++++++++++++++-- Makefile | 10 ---------- docker-compose.ci.yml | 2 +- package.json | 4 ++-- 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 7a34ee7..bc9ba01 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -9,11 +9,30 @@ pipeline { } stages { - stage('CI') { + stage('Build') { steps { - sh 'make ci' + sh 'make build' } } + + stage('Unit Tests') { + steps { + sh 'DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" make test_unit' + } + } + + stage('Acceptance Tests') { + steps { + sh 'DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" make test_acceptance' + } + } + + stage('Package and publish build') { + steps { + sh 'make publish' + } + } + stage('Publish build number') { steps { sh 'echo ${BRANCH_NAME}-${BUILD_NUMBER} > build_number.txt' diff --git a/Makefile b/Makefile index 063d96e..74b78a3 100644 --- a/Makefile +++ b/Makefile @@ -33,16 +33,6 @@ build: publish: docker push quay.io/sharelatex/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) - -ci: - # On the CI server, we want to run our tests in the image that we - # have built for deployment, which is what the docker-compose.ci.yml - # override does. - PROJECT_NAME=$(PROJECT_NAME) \ - BRANCH_NAME=$(BRANCH_NAME) \ - BUILD_NUMBER=$(BUILD_NUMBER) \ - DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" \ - $(MAKE) build test publish .PHONY: clean test test_unit test_acceptance test_clean build publish diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index 2ca2f9a..98715c2 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -15,7 +15,7 @@ services: image: quay.io/sharelatex/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER extends: file: docker-compose-config.yml - service: dev + service: ci environment: REDIS_HOST: redis MONGO_HOST: mongo diff --git a/package.json b/package.json index fed3260..54181da 100644 --- a/package.json +++ b/package.json @@ -9,9 +9,9 @@ "scripts": { "compile:test:acceptance": "coffee -o test/acceptance/js -c test/acceptance/coffee", "compile:test:smoke": "coffee -o test/smoke/js -c test/smoke/coffee", - "compile:app": "coffee -o app/js -c app/coffee && coffee -c app.coffee", + "compile:app": "coffee $COFFEE_OPTIONS -o app/js -c app/coffee && coffee $COFFEE_OPTIONS -c app.coffee", "compile": "npm run compile:app && npm run compile:test:acceptance && npm run compile:test:smoke", - "start": "npm run compile:app && node app.js", + "start": "npm run compile:app && node $NODE_APP_OPTIONS app.js", "test:acceptance:_run": "mocha --recursive --reporter spec --timeout 15000 --exit $@ test/acceptance/js", "test:acceptance": "npm run compile:app && npm run compile:acceptance_tests && npm run test:acceptance:_run -- $@", "test:unit:_run": "mocha --recursive --exit --reporter spec $@ test/unit/js", From dc3cb439d004bf5f172ee90b4ca92251aa9f9740 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Wed, 7 Mar 2018 11:23:52 +0000 Subject: [PATCH 029/118] update build scripts --- Dockerfile | 5 +++-- docker-compose.ci.yml | 4 ++-- docker-compose.yml | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 62ef342..ba3b3e1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,6 +4,8 @@ COPY ./ /app WORKDIR /app +RUN rm -rf node_modules/* && make clean + RUN npm install @@ -20,8 +22,7 @@ WORKDIR /app # The app will run as the 'app' user, and so not have write permissions # on any files it doesn't need. RUN useradd --user-group --create-home --home-dir /app --shell /bin/false app - -RUN [ -e ./install_deps.sh ] && ./install_deps.sh +RUN ./install_deps.sh ENTRYPOINT ["/bin/sh", "entrypoint.sh"] CMD ["node","app.js"] diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index 98715c2..85512cf 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -8,7 +8,7 @@ version: "2" services: test_unit: image: quay.io/sharelatex/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER - entrypoint: npm run test:unit:_run + command: npm run test:unit:_run test_acceptance: build: . @@ -22,7 +22,7 @@ services: depends_on: - redis - mongo - entrypoint: npm run test:acceptance:_run + command: npm run test:acceptance:_run redis: image: redis diff --git a/docker-compose.yml b/docker-compose.yml index cf90742..1850687 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,7 +11,7 @@ services: volumes: - .:/app working_dir: /app - entrypoint: npm run test:unit + command: npm run test:unit test_acceptance: build: . @@ -27,7 +27,7 @@ services: depends_on: - redis - mongo - entrypoint: npm run test:acceptance + command: npm run test:acceptance redis: image: redis From 0c1b699bd525fdafdfb8befd3ff14bd56a69d435 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Wed, 7 Mar 2018 11:41:38 +0000 Subject: [PATCH 030/118] add docker ignore rather than make clean --- .dockerignore | 3 +++ Dockerfile | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..a998353 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +node_modules/* +app.js +**/js/* diff --git a/Dockerfile b/Dockerfile index ba3b3e1..94247ab 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,8 +4,6 @@ COPY ./ /app WORKDIR /app -RUN rm -rf node_modules/* && make clean - RUN npm install From a741a238a8f2ac6007055e33fcedce1be28a0b74 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Thu, 8 Mar 2018 15:38:17 +0000 Subject: [PATCH 031/118] have entrypoint kickoff download off texlive images install script exits without error if auth fails. --- Makefile | 2 +- bin/install_texlive_gce.sh | 13 +++++++++++++ entrypoint.sh | 1 + 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100755 bin/install_texlive_gce.sh diff --git a/Makefile b/Makefile index 74b78a3..5c46c26 100644 --- a/Makefile +++ b/Makefile @@ -28,11 +28,11 @@ test_acceptance: test_clean # clear the database before each acceptance test run test_clean: $(DOCKER_COMPOSE) down -t 0 + build: docker build --pull --tag quay.io/sharelatex/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) . publish: docker push quay.io/sharelatex/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) - .PHONY: clean test test_unit test_acceptance test_clean build publish diff --git a/bin/install_texlive_gce.sh b/bin/install_texlive_gce.sh new file mode 100755 index 0000000..20d2ca0 --- /dev/null +++ b/bin/install_texlive_gce.sh @@ -0,0 +1,13 @@ +#!/bin/sh +METADATA=http://metadata.google.internal./computeMetadata/v1 +SVC_ACCT=$METADATA/instance/service-accounts/default +ACCESS_TOKEN=$(curl -s -H 'Metadata-Flavor: Google' $SVC_ACCT/token | cut -d'"' -f 4) +if [ -z "$ACCESS_TOKEN" ]; then + echo "No acccess token to download texlive-full images from google container, continuing without downloading. This is likely not a google cloud enviroment." + exit 0 +fi +docker login -u '_token' -p $ACCESS_TOKEN https://gcr.io +docker pull --all-tags gcr.io/henry-terraform-admin/texlive-full #TODO NEED TO MAKE THIS AN ENV VAR +echo "Finished downloading texlive-full images" + + diff --git a/entrypoint.sh b/entrypoint.sh index 588854e..f4861a4 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -2,4 +2,5 @@ echo "Changing permissions of /var/run/docker.sock for sibling containers" chown root:docker /var/run/docker.sock +./bin/install_texlive_gce.sh exec runuser -u app "$@" \ No newline at end of file From 52982b8fcd8d856e1312504c5772c4df9a02fe41 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Wed, 14 Mar 2018 15:20:27 +0000 Subject: [PATCH 032/118] remove texlive docker images --- docker-runner | 1 - package.json | 11 +++++------ 2 files changed, 5 insertions(+), 7 deletions(-) delete mode 160000 docker-runner diff --git a/docker-runner b/docker-runner deleted file mode 160000 index 65ad621..0000000 --- a/docker-runner +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 65ad62116fb1ba4fca978a6d1d6b89bf195e5166 diff --git a/package.json b/package.json index 54181da..fdb5030 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,6 @@ "url": "https://github.com/sharelatex/clsi-sharelatex.git" }, "scripts": { - "compile:test:acceptance": "coffee -o test/acceptance/js -c test/acceptance/coffee", - "compile:test:smoke": "coffee -o test/smoke/js -c test/smoke/coffee", "compile:app": "coffee $COFFEE_OPTIONS -o app/js -c app/coffee && coffee $COFFEE_OPTIONS -c app.coffee", "compile": "npm run compile:app && npm run compile:test:acceptance && npm run compile:test:smoke", "start": "npm run compile:app && node $NODE_APP_OPTIONS app.js", @@ -16,10 +14,11 @@ "test:acceptance": "npm run compile:app && npm run compile:acceptance_tests && npm run test:acceptance:_run -- $@", "test:unit:_run": "mocha --recursive --exit --reporter spec $@ test/unit/js", "test:unit": "npm run compile:app && npm run compile:unit_tests && npm run test:unit:_run -- $@", - "compile:unit_tests": "[ -e test/unit ] && coffee -o test/unit/js -c test/unit/coffee || echo 'No unit tests to compile'", - "compile:acceptance_tests": "[ -e test/acceptance ] && coffee -o test/acceptance/js -c test/acceptance/coffee || echo 'No acceptance tests to compile'", - "compile:all": "npm run compile:app && npm run compile:unit_tests && npm run compile:acceptance_tests", - "nodemon": "nodemon --config nodemon.json" + "compile:unit_tests": "[ -e test/unit ] && coffee -o test/unit/js -c test/unit/coffee || echo 'No unit tests to compile, skipping'", + "compile:acceptance_tests": "[ -e test/acceptance ] && coffee -o test/acceptance/js -c test/acceptance/coffee || echo 'No acceptance tests to compile, skipping'", + "compile:all": "npm run compile:app && npm run compile:unit_tests && npm run compile:acceptance_tests && npm run compile:smoke_tests", + "nodemon": "nodemon --config nodemon.json", + "compile:smoke_tests": "[ -e test/smoke ] && coffee -o test/smoke/js -c test/smoke/coffee || echo 'No smoke tests to compile, skipping'" }, "author": "James Allen ", "dependencies": { From 1dce40c61fbdd6a82df69ab9a8bcbd3b567c9958 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Fri, 16 Mar 2018 15:25:36 +0000 Subject: [PATCH 033/118] make compiles dir --- install_deps.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/install_deps.sh b/install_deps.sh index 1883949..511f291 100755 --- a/install_deps.sh +++ b/install_deps.sh @@ -6,3 +6,6 @@ usermod -aG docker app mkdir -p /app/cache chown -R app:app /app/cache + +mkdir -p /app/compiles +chown -R app:app /app/compiles From 9f8a68be3811db3e3c82c530c7195ff650c10956 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Fri, 16 Mar 2018 15:29:35 +0000 Subject: [PATCH 034/118] add log line for connecting to a db --- app/coffee/db.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/coffee/db.coffee b/app/coffee/db.coffee index cb63359..f32cdf7 100644 --- a/app/coffee/db.coffee +++ b/app/coffee/db.coffee @@ -5,6 +5,8 @@ logger = require "logger-sharelatex" options = _.extend {logging:false}, Settings.mysql.clsi +logger.log dbPath:Settings.mysql.clsi.storage, "connecting to db" + sequelize = new Sequelize( Settings.mysql.clsi.database, Settings.mysql.clsi.username, From 5739a2aeca85a1da1a179fe25eb2f0f108ae743e Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Fri, 16 Mar 2018 16:04:26 +0000 Subject: [PATCH 035/118] comment out synctex for moment --- app/coffee/DockerRunner.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/coffee/DockerRunner.coffee b/app/coffee/DockerRunner.coffee index f14b5c6..3ee7f29 100644 --- a/app/coffee/DockerRunner.coffee +++ b/app/coffee/DockerRunner.coffee @@ -108,8 +108,8 @@ module.exports = DockerRunner = _getContainerOptions: (command, image, volumes, timeout, environment) -> timeoutInSeconds = timeout / 1000 - if Settings.path?.synctexBinHostPath? - volumes[Settings.path.synctexBinHostPath] = "/opt/synctex:ro" + # if Settings.path?.synctexBinHostPath? + # volumes[Settings.path.synctexBinHostPath] = "/opt/synctex:ro" dockerVolumes = {} for hostVol, dockerVol of volumes From 63145cc60c347ea0bc6416950ce4c010e56c1329 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Fri, 16 Mar 2018 16:22:39 +0000 Subject: [PATCH 036/118] add synctex back in --- app/coffee/DockerRunner.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/coffee/DockerRunner.coffee b/app/coffee/DockerRunner.coffee index 3ee7f29..f14b5c6 100644 --- a/app/coffee/DockerRunner.coffee +++ b/app/coffee/DockerRunner.coffee @@ -108,8 +108,8 @@ module.exports = DockerRunner = _getContainerOptions: (command, image, volumes, timeout, environment) -> timeoutInSeconds = timeout / 1000 - # if Settings.path?.synctexBinHostPath? - # volumes[Settings.path.synctexBinHostPath] = "/opt/synctex:ro" + if Settings.path?.synctexBinHostPath? + volumes[Settings.path.synctexBinHostPath] = "/opt/synctex:ro" dockerVolumes = {} for hostVol, dockerVol of volumes From 6fbfcfc68b0fd48d66ae2fd97da905674662b33d Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Fri, 16 Mar 2018 16:50:30 +0000 Subject: [PATCH 037/118] move synctex into a directory for simple mounting --- .gitignore | 1 - bin/{ => synctex}/synctex | Bin 2 files changed, 1 deletion(-) rename bin/{ => synctex}/synctex (100%) diff --git a/.gitignore b/.gitignore index 476f2d1..21c1eca 100644 --- a/.gitignore +++ b/.gitignore @@ -13,5 +13,4 @@ cache .vagrant db.sqlite config/* -bin/synctex npm-debug.log diff --git a/bin/synctex b/bin/synctex/synctex similarity index 100% rename from bin/synctex rename to bin/synctex/synctex From f4226ecd0ed128ab8b531d273982ccd5dc4a2b51 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Fri, 16 Mar 2018 17:10:56 +0000 Subject: [PATCH 038/118] try copying synctex betwen directories --- bin/{synctex => }/synctex | Bin entrypoint.sh | 4 ++++ 2 files changed, 4 insertions(+) rename bin/{synctex => }/synctex (100%) diff --git a/bin/synctex/synctex b/bin/synctex similarity index 100% rename from bin/synctex/synctex rename to bin/synctex diff --git a/entrypoint.sh b/entrypoint.sh index f4861a4..d50a70a 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,6 +1,10 @@ #!/bin/sh echo "Changing permissions of /var/run/docker.sock for sibling containers" +cp /var/clsi/bin/synctex /var/clsi/bin/synctex-mount/synctex + chown root:docker /var/run/docker.sock +chown app:app /app/compiles + ./bin/install_texlive_gce.sh exec runuser -u app "$@" \ No newline at end of file From 17c51c2ba0bd2556e8a9d4f3eaf11e75ff9b5c1f Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Fri, 16 Mar 2018 17:30:11 +0000 Subject: [PATCH 039/118] added debugging and new moving commands --- app.coffee | 4 ++++ entrypoint.sh | 8 ++++++-- install_deps.sh | 5 +---- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/app.coffee b/app.coffee index 527b17e..7bd0564 100644 --- a/app.coffee +++ b/app.coffee @@ -144,6 +144,10 @@ app.get "/smoke_test_force", (req, res)-> smokeTest.run(require.resolve(__dirname + "/test/smoke/js/SmokeTests.js"))(req, res) +#TODO delete this +app.get "/settings", (req, res)-> + res.json(Settings) + profiler = require "v8-profiler" app.get "/profile", (req, res) -> time = parseInt(req.query.time || "1000") diff --git a/entrypoint.sh b/entrypoint.sh index d50a70a..ecb28d1 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,10 +1,14 @@ #!/bin/sh echo "Changing permissions of /var/run/docker.sock for sibling containers" -cp /var/clsi/bin/synctex /var/clsi/bin/synctex-mount/synctex chown root:docker /var/run/docker.sock -chown app:app /app/compiles + +mkdir -p /app/cache +chown -R app:app /app/cache + +mkdir -p /app/compiles +chown -R app:app /app/compiles ./bin/install_texlive_gce.sh exec runuser -u app "$@" \ No newline at end of file diff --git a/install_deps.sh b/install_deps.sh index 511f291..20ab946 100755 --- a/install_deps.sh +++ b/install_deps.sh @@ -4,8 +4,5 @@ apt-get install poppler-utils vim ghostscript --yes npm rebuild usermod -aG docker app -mkdir -p /app/cache -chown -R app:app /app/cache +cp /app/bin/synctex /app/bin/synctex-mount/synctex -mkdir -p /app/compiles -chown -R app:app /app/compiles From 3d9a93ad617ba116556409cace1f62dd2b5b0701 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Fri, 16 Mar 2018 17:37:36 +0000 Subject: [PATCH 040/118] add logging of docker options --- app/coffee/DockerRunner.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/app/coffee/DockerRunner.coffee b/app/coffee/DockerRunner.coffee index f14b5c6..8323d6c 100644 --- a/app/coffee/DockerRunner.coffee +++ b/app/coffee/DockerRunner.coffee @@ -146,6 +146,7 @@ module.exports = DockerRunner = "SecurityOpt": ["no-new-privileges"] if Settings.clsi.docker.seccomp_profile? options.HostConfig.SecurityOpt.push "seccomp=#{Settings.clsi.docker.seccomp_profile}" + logger.log options:options, "options for running docker container" return options _fingerprintContainer: (containerOptions) -> From 3c1d7ab264b4369aae4e969e0d27358ba458b25f Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Fri, 16 Mar 2018 17:40:10 +0000 Subject: [PATCH 041/118] mkdir the /app/bin/synctex-mount --- install_deps.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install_deps.sh b/install_deps.sh index 20ab946..76aaf09 100755 --- a/install_deps.sh +++ b/install_deps.sh @@ -4,5 +4,6 @@ apt-get install poppler-utils vim ghostscript --yes npm rebuild usermod -aG docker app +mkdir /app/bin/synctex-mount cp /app/bin/synctex /app/bin/synctex-mount/synctex From 0bd937701802df43b294ceb129f0fffea6623abb Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Fri, 16 Mar 2018 17:48:55 +0000 Subject: [PATCH 042/118] chown synctex and add the creation of directories in --- bin/install_texlive_gce.sh | 1 + install_deps.sh | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/bin/install_texlive_gce.sh b/bin/install_texlive_gce.sh index 20d2ca0..2ea5f99 100755 --- a/bin/install_texlive_gce.sh +++ b/bin/install_texlive_gce.sh @@ -8,6 +8,7 @@ if [ -z "$ACCESS_TOKEN" ]; then fi docker login -u '_token' -p $ACCESS_TOKEN https://gcr.io docker pull --all-tags gcr.io/henry-terraform-admin/texlive-full #TODO NEED TO MAKE THIS AN ENV VAR +cp /app/bin/synctex /app/bin/synctex-mount/synctex echo "Finished downloading texlive-full images" diff --git a/install_deps.sh b/install_deps.sh index 76aaf09..e1f3ce4 100755 --- a/install_deps.sh +++ b/install_deps.sh @@ -4,6 +4,10 @@ apt-get install poppler-utils vim ghostscript --yes npm rebuild usermod -aG docker app -mkdir /app/bin/synctex-mount -cp /app/bin/synctex /app/bin/synctex-mount/synctex +mkdir -p /app/cache +chown -R app:app /app/cache +mkdir -p /app/compiles +chown -R app:app /app/compiles + +chown -R app:app /app/bin/synctex From 8ccbfc7d3263c7ae442806707140830760d00f03 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Fri, 16 Mar 2018 18:11:46 +0000 Subject: [PATCH 043/118] don't put synctex in as a volume --- app/coffee/DockerRunner.coffee | 9 +++++---- bin/install_texlive_gce.sh | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/coffee/DockerRunner.coffee b/app/coffee/DockerRunner.coffee index 8323d6c..635243f 100644 --- a/app/coffee/DockerRunner.coffee +++ b/app/coffee/DockerRunner.coffee @@ -108,9 +108,6 @@ module.exports = DockerRunner = _getContainerOptions: (command, image, volumes, timeout, environment) -> timeoutInSeconds = timeout / 1000 - if Settings.path?.synctexBinHostPath? - volumes[Settings.path.synctexBinHostPath] = "/opt/synctex:ro" - dockerVolumes = {} for hostVol, dockerVol of volumes dockerVolumes[dockerVol] = {} @@ -118,7 +115,6 @@ module.exports = DockerRunner = if volumes[hostVol].slice(-3).indexOf(":r") == -1 volumes[hostVol] = "#{dockerVol}:rw" - # merge settings and environment parameter env = {} for src in [Settings.clsi.docker.env, environment or {}] @@ -144,8 +140,13 @@ module.exports = DockerRunner = "Ulimits": [{'Name': 'cpu', 'Soft': timeoutInSeconds+5, 'Hard': timeoutInSeconds+10}] "CapDrop": "ALL" "SecurityOpt": ["no-new-privileges"] + if Settings.clsi.docker.seccomp_profile? options.HostConfig.SecurityOpt.push "seccomp=#{Settings.clsi.docker.seccomp_profile}" + + if Settings.path?.synctexBinHostPath? + options["HostConfig"]["Binds"].push("#{Settings.path.synctexBinHostPath}:/opt/synctex:ro") + logger.log options:options, "options for running docker container" return options diff --git a/bin/install_texlive_gce.sh b/bin/install_texlive_gce.sh index 2ea5f99..85ab709 100755 --- a/bin/install_texlive_gce.sh +++ b/bin/install_texlive_gce.sh @@ -9,6 +9,7 @@ fi docker login -u '_token' -p $ACCESS_TOKEN https://gcr.io docker pull --all-tags gcr.io/henry-terraform-admin/texlive-full #TODO NEED TO MAKE THIS AN ENV VAR cp /app/bin/synctex /app/bin/synctex-mount/synctex + echo "Finished downloading texlive-full images" From aeb6f48945b68319d0b30dafd581b18c86229f41 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Mon, 19 Mar 2018 09:51:26 +0000 Subject: [PATCH 044/118] try running as root --- entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/entrypoint.sh b/entrypoint.sh index ecb28d1..6722a41 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -11,4 +11,4 @@ mkdir -p /app/compiles chown -R app:app /app/compiles ./bin/install_texlive_gce.sh -exec runuser -u app "$@" \ No newline at end of file +exec "$@" \ No newline at end of file From 0915ac8c606728a1e6d38f97494da6455caad83a Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Mon, 19 Mar 2018 12:56:53 +0000 Subject: [PATCH 045/118] run as app user and chmod 777 compiles dir --- entrypoint.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/entrypoint.sh b/entrypoint.sh index 6722a41..ee1df04 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -9,6 +9,7 @@ chown -R app:app /app/cache mkdir -p /app/compiles chown -R app:app /app/compiles +chmod -R 777 /app/compiles #TODO why do I need this? ./bin/install_texlive_gce.sh -exec "$@" \ No newline at end of file +exec runuser -u app "$@" \ No newline at end of file From 4d955a8d4144e2917d22be211cc208d2b05924c4 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Mon, 19 Mar 2018 14:10:45 +0000 Subject: [PATCH 046/118] try a build with node user --- Dockerfile | 1 - entrypoint.sh | 7 +++---- install_deps.sh | 8 ++++---- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Dockerfile b/Dockerfile index 94247ab..c30764d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,7 +19,6 @@ WORKDIR /app # All app and node_modules will be owned by root. # The app will run as the 'app' user, and so not have write permissions # on any files it doesn't need. -RUN useradd --user-group --create-home --home-dir /app --shell /bin/false app RUN ./install_deps.sh ENTRYPOINT ["/bin/sh", "entrypoint.sh"] diff --git a/entrypoint.sh b/entrypoint.sh index ee1df04..423a5d2 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -5,11 +5,10 @@ echo "Changing permissions of /var/run/docker.sock for sibling containers" chown root:docker /var/run/docker.sock mkdir -p /app/cache -chown -R app:app /app/cache +chown -R node:node /app/cache mkdir -p /app/compiles -chown -R app:app /app/compiles -chmod -R 777 /app/compiles #TODO why do I need this? +chown -R node:node /app/compiles ./bin/install_texlive_gce.sh -exec runuser -u app "$@" \ No newline at end of file +exec runuser -u node "$@" \ No newline at end of file diff --git a/install_deps.sh b/install_deps.sh index e1f3ce4..4b06f22 100755 --- a/install_deps.sh +++ b/install_deps.sh @@ -2,12 +2,12 @@ wget -qO- https://get.docker.com/ | sh apt-get install poppler-utils vim ghostscript --yes npm rebuild -usermod -aG docker app +usermod -aG docker node mkdir -p /app/cache -chown -R app:app /app/cache +chown -R node:node /app/cache mkdir -p /app/compiles -chown -R app:app /app/compiles +chown -R node:node /app/compiles -chown -R app:app /app/bin/synctex +chown -R node:node /app/bin/synctex From dc1ea9d3e96223f4ac6e0c860cae672affec8195 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Mon, 19 Mar 2018 14:22:18 +0000 Subject: [PATCH 047/118] ammend comment --- Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index c30764d..68e1a4a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,9 +15,8 @@ COPY --from=app /app /app WORKDIR /app - # All app and node_modules will be owned by root. -# The app will run as the 'app' user, and so not have write permissions +# The app will run as the 'node' user, and so not have write permissions # on any files it doesn't need. RUN ./install_deps.sh ENTRYPOINT ["/bin/sh", "entrypoint.sh"] From ec75f9fa6732bea223e5aa91e11cc97386514e15 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 20 Mar 2018 13:48:12 +0000 Subject: [PATCH 048/118] add smoke test env var --- config/settings.defaults.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/settings.defaults.coffee b/config/settings.defaults.coffee index 3100a5d..de853f5 100644 --- a/config/settings.defaults.coffee +++ b/config/settings.defaults.coffee @@ -25,7 +25,7 @@ module.exports = clsi: url: "http://#{process.env['CLSI_HOST'] or 'localhost'}:3013" - smokeTest: false + smokeTest: process.env["SMOKE_TEST"] or false project_cache_length_ms: 1000 * 60 * 60 * 24 parallelFileDownloads:1 From b5a7eabaab1cb996dfe9c1e6e7ed72e8f0eabf46 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Thu, 29 Mar 2018 12:12:29 +0100 Subject: [PATCH 049/118] update build script and add load balancer agent --- .viminfo | 35 + Dockerfile | 4 - Makefile | 6 +- app.coffee | 41 + debug | 5 + docker-compose.ci.yml | 6 +- docker-compose.yml | 6 +- entrypoint.sh | 2 + nodemon.json | 3 +- package-lock.json | 3661 +------------------------------------- package.json | 6 +- patch-texlive-dockerfile | 3 + synctex.profile | 34 + 13 files changed, 184 insertions(+), 3628 deletions(-) create mode 100644 .viminfo create mode 100755 debug create mode 100644 patch-texlive-dockerfile create mode 100644 synctex.profile diff --git a/.viminfo b/.viminfo new file mode 100644 index 0000000..78c0129 --- /dev/null +++ b/.viminfo @@ -0,0 +1,35 @@ +# This viminfo file was generated by Vim 7.4. +# You may edit it if you're careful! + +# Value of 'encoding' when this file was written +*encoding=latin1 + + +# hlsearch on (H) or off (h): +~h +# Command Line History (newest to oldest): +:x + +# Search String History (newest to oldest): + +# Expression History (newest to oldest): + +# Input Line History (newest to oldest): + +# Input Line History (newest to oldest): + +# Registers: + +# File marks: +'0 1 0 ~/hello + +# Jumplist (newest first): +-' 1 0 ~/hello + +# History of marks within files (newest to oldest): + +> ~/hello + " 1 0 + ^ 1 1 + . 1 0 + + 1 0 diff --git a/Dockerfile b/Dockerfile index 68e1a4a..ed1f2c6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,10 +14,6 @@ FROM node:6.13.0 COPY --from=app /app /app WORKDIR /app - -# All app and node_modules will be owned by root. -# The app will run as the 'node' user, and so not have write permissions -# on any files it doesn't need. RUN ./install_deps.sh ENTRYPOINT ["/bin/sh", "entrypoint.sh"] diff --git a/Makefile b/Makefile index 5c46c26..2a1196c 100644 --- a/Makefile +++ b/Makefile @@ -21,13 +21,13 @@ clean: test: test_unit test_acceptance test_unit: - @[ -d test/unit ] && $(DOCKER_COMPOSE) run --rm test_unit -- ${MOCHA_ARGS} || echo "clsi has no unit tests" + @[ ! -d test/unit ] && echo "clsi has no unit tests" || $(DOCKER_COMPOSE) run --rm test_unit -- ${MOCHA_ARGS} test_acceptance: test_clean # clear the database before each acceptance test run - @[ -d test/acceptance ] && $(DOCKER_COMPOSE) run --rm test_acceptance -- ${MOCHA_ARGS} || echo "clsi has no acceptance tests" + @[ ! -d test/acceptance ] && echo "clsi has no acceptance tests" || $(DOCKER_COMPOSE) run --rm test_acceptance -- ${MOCHA_ARGS} test_clean: - $(DOCKER_COMPOSE) down -t 0 + $(DOCKER_COMPOSE) down -t -v 0 build: docker build --pull --tag quay.io/sharelatex/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) . diff --git a/app.coffee b/app.coffee index 7bd0564..a3f4ef1 100644 --- a/app.coffee +++ b/app.coffee @@ -169,13 +169,54 @@ app.use (error, req, res, next) -> logger.error {err: error, url: req.url}, "server error" res.sendStatus(error?.statusCode || 500) +net = require "net" +os = require "os" + +STATE = "up" + +server = net.createServer (socket) -> + socket.on "error", (err)-> + if err.code == "ECONNRESET" + # this always comes up, we don't know why + return + logger.err err:err, "error with socket on load check" + socket.destroy() + + if STATE == "up" and settings.load_balancer_agent.report_load + currentLoad = os.loadavg()[0] + + # staging clis's have 1 cpu core only + if os.cpus().length == 1 + availableWorkingCpus = 1 + else + availableWorkingCpus = os.cpus().length - 1 + + freeLoad = availableWorkingCpus - currentLoad + freeLoadPercentage = Math.round((freeLoad / availableWorkingCpus) * 100) + if freeLoadPercentage <= 0 + freeLoadPercentage = 1 # when its 0 the server is set to drain and will move projects to different servers + socket.write("up, #{freeLoadPercentage}%\n", "ASCII") + socket.end() + else + socket.write("#{STATE}\n", "ASCII") + socket.end() + + + + port = (Settings.internal?.clsi?.port or 3013) host = (Settings.internal?.clsi?.host or "localhost") +load_port = settings.internal.clsi.load_port or 3048 + + if !module.parent # Called directly app.listen port, host, (error) -> logger.info "CLSI starting up, listening on #{host}:#{port}" + server.listen load_port, host, (error) -> + throw error if error? + logger.info "Load agent listening on load port #{load_port}" module.exports = app diff --git a/debug b/debug new file mode 100755 index 0000000..fcc371c --- /dev/null +++ b/debug @@ -0,0 +1,5 @@ +#!/bin/bash +echo "hello world" +sleep 3 +echo "awake" +/opt/synctex pdf /compile/output.pdf 1 100 200 diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index 85512cf..370e2a8 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -8,7 +8,8 @@ version: "2" services: test_unit: image: quay.io/sharelatex/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER - command: npm run test:unit:_run + user: node + entrypoint: npm run test:unit:_run test_acceptance: build: . @@ -22,7 +23,8 @@ services: depends_on: - redis - mongo - command: npm run test:acceptance:_run + user: node + entrypoint: npm run test:acceptance:_run redis: image: redis diff --git a/docker-compose.yml b/docker-compose.yml index 1850687..32cbf61 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,7 +11,8 @@ services: volumes: - .:/app working_dir: /app - command: npm run test:unit + user: node + entrypoint: npm run test:unit test_acceptance: build: . @@ -24,10 +25,11 @@ services: environment: REDIS_HOST: redis MONGO_HOST: mongo + user: node depends_on: - redis - mongo - command: npm run test:acceptance + entrypoint: npm run test:acceptance redis: image: redis diff --git a/entrypoint.sh b/entrypoint.sh index 423a5d2..ee5ea47 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -11,4 +11,6 @@ mkdir -p /app/compiles chown -R node:node /app/compiles ./bin/install_texlive_gce.sh +echo "HELOOOo" +echo "$@" exec runuser -u node "$@" \ No newline at end of file diff --git a/nodemon.json b/nodemon.json index 9a3be8d..815f392 100644 --- a/nodemon.json +++ b/nodemon.json @@ -10,7 +10,8 @@ }, "watch": [ "app/coffee/", - "app.coffee" + "app.coffee", + "config/" ], "ext": "coffee" } diff --git a/package-lock.json b/package-lock.json index a5b7847..d00b345 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,70 +4,12 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "JSONStream": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.10.0.tgz", - "integrity": "sha1-dDSdDYlSK3HzDwoD/5vSDKbxKsA=", - "requires": { - "jsonparse": "0.0.5", - "through": "2.3.8" - } - }, - "async": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.9.tgz", - "integrity": "sha1-32MGD789Myhqdqr21Vophtn/hhk=" - }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "optional": true }, - "bl": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.1.tgz", - "integrity": "sha1-ysMo977kVzDUBLaSID/LWQ4XLV4=", - "requires": { - "readable-stream": "2.3.3" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", - "requires": { - "core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "isarray": "1.0.0", - "process-nextick-args": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "string_decoder": "1.0.3", - "util-deprecate": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" - } - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "requires": { - "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" - } - } - } - }, - "block-stream": { - "version": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", - "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", - "requires": { - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" - } - }, "body-parser": { "version": "1.18.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", @@ -224,292 +166,12 @@ "concat-map": "0.0.1" } }, - "browser-stdout": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", - "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", - "dev": true - }, - "bunyan": { - "version": "0.22.3", - "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-0.22.3.tgz", - "integrity": "sha1-ehncG0yMZF90AkGnQPIkUUfGfsI=", - "dev": true, - "requires": { - "mv": "2.1.1" - }, - "dependencies": { - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "optional": true, - "requires": { - "balanced-match": "1.0.0", - "concat-map": "0.0.1" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true, - "optional": true - }, - "glob": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", - "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", - "dev": true, - "optional": true, - "requires": { - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "optional": true, - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "optional": true, - "requires": { - "brace-expansion": "1.1.11" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true, - "optional": true - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "optional": true, - "requires": { - "minimist": "0.0.8" - } - }, - "mv": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", - "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", - "dev": true, - "optional": true, - "requires": { - "mkdirp": "0.5.1", - "ncp": "2.0.0", - "rimraf": "2.4.5" - } - }, - "ncp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", - "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1.0.2" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true, - "optional": true - }, - "rimraf": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", - "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", - "dev": true, - "optional": true, - "requires": { - "glob": "6.0.4" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - } - } - }, - "chai": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/chai/-/chai-1.8.1.tgz", - "integrity": "sha1-zHeGbV5+vKK9dRRLHtw3Coh4X3I=", - "dev": true, - "requires": { - "assertion-error": "1.0.0", - "deep-eql": "0.1.3" - }, - "dependencies": { - "assertion-error": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.0.tgz", - "integrity": "sha1-x/hUOP3UZrx8oWq5DIFRN5el0js=", - "dev": true - }, - "deep-eql": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", - "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", - "dev": true, - "requires": { - "type-detect": "0.1.1" - } - }, - "type-detect": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", - "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=", - "dev": true - } - } - }, - "coffee-script": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz", - "integrity": "sha1-gIs5bhEPU9AhoZpO8fZb4OjjX6M=" - }, - "commander": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz", - "integrity": "sha1-0bhvkB+LZL2UG96tr5JFMDk76Sg=" - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "optional": true }, - "concat-stream": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", - "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=", - "requires": { - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "readable-stream": "2.0.6", - "typedarray": "0.0.6" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", - "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", - "requires": { - "core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "isarray": "1.0.0", - "process-nextick-args": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "string_decoder": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "util-deprecate": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" - } - } - } - }, - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" - }, - "core-util-is": { - "version": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "debug": { - "version": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", - "requires": { - "ms": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" - } - }, - "diff": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz", - "integrity": "sha1-JLuwAcSn1VIhaefKvbLCgU7ZHPQ=" - }, - "docker-modem": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-1.0.4.tgz", - "integrity": "sha1-JEJUsiax1/YCJfgjlb2FGQz3Ves=", - "requires": { - "JSONStream": "0.10.0", - "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "readable-stream": "1.0.34", - "split-ca": "1.0.1" - }, - "dependencies": { - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "requires": { - "core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "isarray": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "string_decoder": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" - } - } - } - }, - "dockerode": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-2.5.3.tgz", - "integrity": "sha1-Wsw8Bx96JuRDpbWM83U+8vPCvAQ=", - "requires": { - "concat-stream": "1.5.2", - "docker-modem": "1.0.4", - "tar-fs": "1.12.0" - } - }, "dtrace-provider": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.6.0.tgz", @@ -519,20 +181,6 @@ "nan": "2.8.0" } }, - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha1-7SljTRm6ukY7bOa4CjchPqtx7EM=", - "requires": { - "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, "express": { "version": "4.16.2", "resolved": "https://registry.npmjs.org/express/-/express-4.16.2.tgz", @@ -851,132 +499,6 @@ } } }, - "fs-extra": { - "version": "0.16.5", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.16.5.tgz", - "integrity": "sha1-GtZh+myGyWCM0bSe/G/Og0k5p1A=", - "requires": { - "graceful-fs": "3.0.11", - "jsonfile": "2.4.0", - "rimraf": "2.6.2" - }, - "dependencies": { - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "1.0.0", - "concat-map": "0.0.1" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "graceful-fs": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz", - "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=", - "requires": { - "natives": "1.1.1" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "jsonfile": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", - "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", - "requires": { - "graceful-fs": "4.1.11" - }, - "dependencies": { - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "optional": true - } - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "1.1.11" - } - }, - "natives": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.1.tgz", - "integrity": "sha512-8eRaxn8u/4wN8tGkhlc2cgwwvOLMLUMUn4IYTexMgWd+LyUDfeXVkk2ygQR0hvIHbJQXgHujia3ieUUDwNGkEA==" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1.0.2" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", - "requires": { - "glob": "7.1.2" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - } - } - }, "glob": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", @@ -1013,562 +535,6 @@ } } }, - "graceful-fs": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz", - "integrity": "sha1-fNLNsiiko/Nule+mzBQt59GhNtA=" - }, - "growl": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.7.0.tgz", - "integrity": "sha1-3i1mE20ALhErpw8/EMMc98NQsto=" - }, - "grunt": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz", - "integrity": "sha1-VpN81RlDJK3/bSB2MYMqnWuk5/A=", - "dev": true, - "requires": { - "async": "0.1.22", - "coffee-script": "1.3.3", - "colors": "0.6.2", - "dateformat": "1.0.2-1.2.3", - "eventemitter2": "0.4.14", - "exit": "0.1.2", - "findup-sync": "0.1.3", - "getobject": "0.1.0", - "glob": "3.1.21", - "grunt-legacy-log": "0.1.3", - "grunt-legacy-util": "0.2.0", - "hooker": "0.2.3", - "iconv-lite": "0.2.11", - "js-yaml": "2.0.5", - "lodash": "0.9.2", - "minimatch": "0.2.14", - "nopt": "1.0.10", - "rimraf": "2.2.8", - "underscore.string": "2.2.1", - "which": "1.0.9" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true - }, - "argparse": { - "version": "0.1.16", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz", - "integrity": "sha1-z9AeD7uj1srtBJ+9dY1A9lGW9Xw=", - "dev": true, - "requires": { - "underscore": "1.7.0", - "underscore.string": "2.4.0" - }, - "dependencies": { - "underscore.string": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz", - "integrity": "sha1-jN2PusTi0uoefi6Al8QvRCKA+Fs=", - "dev": true - } - } - }, - "async": { - "version": "0.1.22", - "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz", - "integrity": "sha1-D8GqoIig4+8Ovi2IMbqw3PiEUGE=", - "dev": true - }, - "coffee-script": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz", - "integrity": "sha1-FQ1rTLUiiUNp7+1qIQHCC8f0pPQ=", - "dev": true - }, - "colors": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz", - "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=", - "dev": true - }, - "dateformat": { - "version": "1.0.2-1.2.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz", - "integrity": "sha1-sCIMAt6YYXQztyhRz0fePfLNvuk=", - "dev": true - }, - "esprima": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz", - "integrity": "sha1-n1V+CPw7TSbs6d00+Pv0drYlha0=", - "dev": true - }, - "eventemitter2": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", - "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=", - "dev": true - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true - }, - "findup-sync": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz", - "integrity": "sha1-fz56l7gjksZTvwZYm9hRkOk8NoM=", - "dev": true, - "requires": { - "glob": "3.2.11", - "lodash": "2.4.2" - }, - "dependencies": { - "glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", - "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "minimatch": "0.3.0" - } - }, - "lodash": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", - "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=", - "dev": true - }, - "minimatch": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", - "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", - "dev": true, - "requires": { - "lru-cache": "2.7.3", - "sigmund": "1.0.1" - } - } - } - }, - "getobject": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz", - "integrity": "sha1-BHpEl4n6Fg0Bj1SG7ZEyC27HiFw=", - "dev": true - }, - "glob": { - "version": "3.1.21", - "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", - "integrity": "sha1-0p4KBV3qUTj00H7UDomC6DwgZs0=", - "dev": true, - "requires": { - "graceful-fs": "1.2.3", - "inherits": "1.0.2", - "minimatch": "0.2.14" - }, - "dependencies": { - "inherits": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz", - "integrity": "sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js=", - "dev": true - } - } - }, - "graceful-fs": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz", - "integrity": "sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q=", - "dev": true - }, - "grunt-legacy-log": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.3.tgz", - "integrity": "sha1-7ClCboAwIa9ZAp+H0vnNczWgVTE=", - "dev": true, - "requires": { - "colors": "0.6.2", - "grunt-legacy-log-utils": "0.1.1", - "hooker": "0.2.3", - "lodash": "2.4.2", - "underscore.string": "2.3.3" - }, - "dependencies": { - "lodash": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", - "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=", - "dev": true - }, - "underscore.string": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz", - "integrity": "sha1-ccCL9rQosRM/N+ePo6Icgvcymw0=", - "dev": true - } - } - }, - "grunt-legacy-log-utils": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-0.1.1.tgz", - "integrity": "sha1-wHBrndkGThFvNvI/5OawSGcsD34=", - "dev": true, - "requires": { - "colors": "0.6.2", - "lodash": "2.4.2", - "underscore.string": "2.3.3" - }, - "dependencies": { - "lodash": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", - "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=", - "dev": true - }, - "underscore.string": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz", - "integrity": "sha1-ccCL9rQosRM/N+ePo6Icgvcymw0=", - "dev": true - } - } - }, - "grunt-legacy-util": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz", - "integrity": "sha1-kzJIhNv343qf98Am3/RR2UqeVUs=", - "dev": true, - "requires": { - "async": "0.1.22", - "exit": "0.1.2", - "getobject": "0.1.0", - "hooker": "0.2.3", - "lodash": "0.9.2", - "underscore.string": "2.2.1", - "which": "1.0.9" - } - }, - "hooker": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", - "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=", - "dev": true - }, - "iconv-lite": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz", - "integrity": "sha1-HOYKOleGSiktEyH/RgnKS7llrcg=", - "dev": true - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "js-yaml": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz", - "integrity": "sha1-olrmUJmZ6X3yeMZxnaEb0Gh3Q6g=", - "dev": true, - "requires": { - "argparse": "0.1.16", - "esprima": "1.0.4" - } - }, - "lodash": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz", - "integrity": "sha1-jzSZxSRdNG1oLlsNO0B2fgnxqSw=", - "dev": true - }, - "lru-cache": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", - "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=", - "dev": true - }, - "minimatch": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", - "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", - "dev": true, - "requires": { - "lru-cache": "2.7.3", - "sigmund": "1.0.1" - } - }, - "nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", - "dev": true, - "requires": { - "abbrev": "1.1.1" - } - }, - "rimraf": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", - "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=", - "dev": true - }, - "sigmund": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", - "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", - "dev": true - }, - "underscore": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", - "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=", - "dev": true - }, - "underscore.string": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz", - "integrity": "sha1-18D6KvXVoaZ/QlPa7pgTLnM/Dxk=", - "dev": true - }, - "which": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz", - "integrity": "sha1-RgwdoPgQED0DIam2M6+eV15kSG8=", - "dev": true - } - } - }, - "grunt-bunyan": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/grunt-bunyan/-/grunt-bunyan-0.5.0.tgz", - "integrity": "sha1-aCnXbgGZQ9owQTk2MaNuKsgpsWw=", - "dev": true, - "requires": { - "lodash": "2.4.2" - }, - "dependencies": { - "lodash": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", - "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=", - "dev": true - } - } - }, - "grunt-contrib-clean": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-0.5.0.tgz", - "integrity": "sha1-9T397ghJsce0Dp67umn0jExgecU=", - "dev": true, - "requires": { - "rimraf": "2.2.8" - }, - "dependencies": { - "rimraf": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", - "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=", - "dev": true - } - } - }, - "grunt-contrib-coffee": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.7.0.tgz", - "integrity": "sha1-ixIme3TnM4sfKcW4txj7n4mYLxM=", - "dev": true, - "requires": { - "coffee-script": "1.6.3" - }, - "dependencies": { - "coffee-script": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.3.tgz", - "integrity": "sha1-Y1XTLPGwTN/2tITl5xF4Ky8MOb4=", - "dev": true - } - } - }, - "grunt-execute": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/grunt-execute/-/grunt-execute-0.1.5.tgz", - "integrity": "sha1-yX64lDYS/vu3L749Mu+VIzxfouk=", - "dev": true - }, - "grunt-mkdir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/grunt-mkdir/-/grunt-mkdir-1.0.0.tgz", - "integrity": "sha1-c+GiasJKCFljY/TdlUsNMkheWOk=" - }, - "grunt-mocha-test": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.8.2.tgz", - "integrity": "sha1-emGEuYhg0Phb3qrWvqob199bvus=", - "dev": true, - "requires": { - "mocha": "1.14.0" - }, - "dependencies": { - "commander": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz", - "integrity": "sha1-0bhvkB+LZL2UG96tr5JFMDk76Sg=", - "dev": true - }, - "diff": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz", - "integrity": "sha1-JLuwAcSn1VIhaefKvbLCgU7ZHPQ=", - "dev": true - }, - "glob": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", - "integrity": "sha1-4xPusknHr/qlxHUoaw4RW1mDlGc=", - "dev": true, - "requires": { - "graceful-fs": "2.0.3", - "inherits": "2.0.3", - "minimatch": "0.2.14" - } - }, - "graceful-fs": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz", - "integrity": "sha1-fNLNsiiko/Nule+mzBQt59GhNtA=", - "dev": true - }, - "growl": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.7.0.tgz", - "integrity": "sha1-3i1mE20ALhErpw8/EMMc98NQsto=", - "dev": true - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "jade": { - "version": "0.26.3", - "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", - "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=", - "dev": true, - "requires": { - "commander": "0.6.1", - "mkdirp": "0.3.0" - }, - "dependencies": { - "commander": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", - "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=", - "dev": true - }, - "mkdirp": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", - "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=", - "dev": true - } - } - }, - "lru-cache": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", - "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=", - "dev": true - }, - "minimatch": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", - "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", - "dev": true, - "requires": { - "lru-cache": "2.7.3", - "sigmund": "1.0.1" - } - }, - "mocha": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-1.14.0.tgz", - "integrity": "sha1-cT223FAAGRqdA1gZXQkIeQ7LYVc=", - "dev": true, - "requires": { - "commander": "2.0.0", - "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "diff": "1.0.7", - "glob": "3.2.3", - "growl": "1.7.0", - "jade": "0.26.3", - "mkdirp": "0.3.5" - } - }, - "sigmund": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", - "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", - "dev": true - } - } - }, - "grunt-shell": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/grunt-shell/-/grunt-shell-0.6.4.tgz", - "integrity": "sha1-5KbRuSkSd2/ZOimcX2zGTpUlNlw=", - "dev": true, - "requires": { - "chalk": "0.3.0" - }, - "dependencies": { - "ansi-styles": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-0.2.0.tgz", - "integrity": "sha1-NZq0sV3NZLptdHNLcsNjYKmvLBk=", - "dev": true - }, - "chalk": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.3.0.tgz", - "integrity": "sha1-HJhDdzfxGZ68wdTEj9Qbn5yOjyM=", - "dev": true, - "requires": { - "ansi-styles": "0.2.0", - "has-color": "0.1.7" - } - }, - "has-color": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz", - "integrity": "sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8=", - "dev": true - } - } - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, - "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", - "dev": true - }, - "heapdump": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/heapdump/-/heapdump-0.3.9.tgz", - "integrity": "sha1-A8dOsN9dZ74Jgug0KbqcnSs7f3g=" - }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -1595,56 +561,12 @@ } } }, - "inherits": { - "version": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "isarray": { - "version": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "jade": { - "version": "0.26.3", - "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", - "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=", + "load-balancer-agent-sharelatex": { + "version": "git+https://github.com/sharelatex/load-balancer-agent-sharelatex.git#241a128c1e9fdec384186061b27704c8891d98ef", "requires": { - "commander": "0.6.1", - "mkdirp": "0.3.0" - }, - "dependencies": { - "commander": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", - "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=" - }, - "mkdirp": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", - "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=" - } - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "jsonparse": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz", - "integrity": "sha1-MwVCrT8KZUZlt3jz6y2an6UHrGQ=" - }, - "lockfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/lockfile/-/lockfile-1.0.3.tgz", - "integrity": "sha1-Jjj8OaAzHpysGgS3F5mTHJxQ33k=" - }, - "logger-sharelatex": { - "version": "git+https://github.com/sharelatex/logger-sharelatex.git#9ee7b52eb2bbd8fcbb1e2c708587c1e93fd4c733", - "requires": { - "bunyan": "1.5.1", - "coffee-script": "1.4.0", - "raven": "1.2.1" + "express": "4.16.2", + "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#405cf1350ca5ae5f7bb1e7091e28d5aa3aaaa72c", + "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#b4fb8404c5de571d029bf4c29e96a60b21206f94" }, "dependencies": { "bunyan": { @@ -1661,66 +583,53 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.4.0.tgz", "integrity": "sha1-XjvIqsJsAajie/EHcixWVfWtfTY=" - } - } - }, - "lru-cache": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", - "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" - }, - "lsmod": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-1.0.0.tgz", - "integrity": "sha1-mgD3bco26yP6BTUK/htYXUKZ5ks=" - }, - "lynx": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/lynx/-/lynx-0.0.11.tgz", - "integrity": "sha1-LPoU5EP9LZKlm3efQVZ84cxpZaM=", - "requires": { - "mersenne": "0.0.4", - "statsd-parser": "0.0.4" - }, - "dependencies": { - "mersenne": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.4.tgz", - "integrity": "sha1-QB/ex+whzbngPNPTAhOY2iGycIU=" }, - "statsd-parser": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz", - "integrity": "sha1-y9JDlTzELv/VSLXSI4jtaJ7GOb0=" - } - } - }, - "mersenne": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.4.tgz", - "integrity": "sha1-QB/ex+whzbngPNPTAhOY2iGycIU=" - }, - "metrics-sharelatex": { - "version": "git+https://github.com/sharelatex/metrics-sharelatex.git#05bd57604115ae5efdca2a419fbef4f25e9a780f", - "requires": { - "coffee-script": "1.6.0", - "lynx": "0.1.1", - "underscore": "1.6.0" - }, - "dependencies": { - "lynx": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/lynx/-/lynx-0.1.1.tgz", - "integrity": "sha1-Mxjc7xaQi4KG6Bisz9sxzXQkj50=", + "cookie": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz", + "integrity": "sha1-kOtGndzpBchm3mh+/EMTHYgB+dA=" + }, + "logger-sharelatex": { + "version": "git+https://github.com/sharelatex/logger-sharelatex.git#405cf1350ca5ae5f7bb1e7091e28d5aa3aaaa72c", "requires": { - "mersenne": "0.0.4", - "statsd-parser": "0.0.4" + "bunyan": "1.5.1", + "coffee-script": "1.4.0", + "raven": "0.8.1" } }, - "underscore": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", - "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=" + "lsmod": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-0.0.3.tgz", + "integrity": "sha1-F+E9ThrpF1DqVlNUjNiecUetAkQ=" + }, + "raven": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/raven/-/raven-0.8.1.tgz", + "integrity": "sha1-UVk7tlnHcnjc00gitlq+d7dRuvU=", + "requires": { + "cookie": "0.1.0", + "lsmod": "0.0.3", + "node-uuid": "1.4.8", + "stack-trace": "0.0.7" + } + }, + "settings-sharelatex": { + "version": "git+https://github.com/sharelatex/settings-sharelatex.git#b4fb8404c5de571d029bf4c29e96a60b21206f94", + "requires": { + "coffee-script": "1.6.0" + }, + "dependencies": { + "coffee-script": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz", + "integrity": "sha1-gIs5bhEPU9AhoZpO8fZb4OjjX6M=" + } + } + }, + "stack-trace": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.7.tgz", + "integrity": "sha1-xy4Il0T8Nln1CM3ONiGvVjTsD/8=" } } }, @@ -1733,175 +642,6 @@ "brace-expansion": "1.1.11" } }, - "minimist": { - "version": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mkdirp": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", - "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=" - }, - "mocha": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.1.0.tgz", - "integrity": "sha1-fYbPvPNcuCnidUwy4XNV7AUzh5Q=", - "dev": true, - "requires": { - "browser-stdout": "1.3.0", - "commander": "2.11.0", - "debug": "3.1.0", - "diff": "3.3.1", - "escape-string-regexp": "1.0.5", - "glob": "7.1.2", - "growl": "1.10.3", - "he": "1.1.1", - "mkdirp": "0.5.1", - "supports-color": "4.4.0" - }, - "dependencies": { - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "1.0.0", - "concat-map": "0.0.1" - } - }, - "commander": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha1-FXFS/R56bI2YpbcVzzdt+SgARWM=", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "diff": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", - "integrity": "sha1-qoVnpu7QPFMfyJ0/cRzQ5SWd7HU=", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "growl": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", - "integrity": "sha1-GSa6kM8+3+KttJJ/WIC8IsZseQ8=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "1.1.11" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1.0.2" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - } - } - }, - "ms": { - "version": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, "mv": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", @@ -1930,59 +670,6 @@ } } }, - "mysql": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.6.2.tgz", - "integrity": "sha1-k3Gd0yT1fUHET7bX+GDPjmOFk7A=", - "requires": { - "bignumber.js": "2.0.7", - "readable-stream": "1.1.14", - "require-all": "1.0.0" - }, - "dependencies": { - "bignumber.js": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.0.7.tgz", - "integrity": "sha1-husHB89qURCQnSPm6nQ0wU9QDxw=" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "0.0.1", - "string_decoder": "0.10.31" - } - }, - "require-all": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/require-all/-/require-all-1.0.0.tgz", - "integrity": "sha1-hINwjnzkxt+tmItQgPl4KbktIic=" - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - } - } - }, "nan": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", @@ -2000,419 +687,12 @@ "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=" }, - "once": { - "version": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" - } - }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "optional": true }, - "process-nextick-args": { - "version": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" - }, - "pump": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz", - "integrity": "sha1-Xf6DEcM7v2/BgmH580cCxHwIqVQ=", - "requires": { - "end-of-stream": "1.4.1", - "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz" - } - }, - "raven": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/raven/-/raven-1.2.1.tgz", - "integrity": "sha1-lJwTTbAooZC3u/j3kKrlQbfAIL0=", - "requires": { - "cookie": "0.3.1", - "json-stringify-safe": "5.0.1", - "lsmod": "1.0.0", - "stack-trace": "0.0.9", - "uuid": "3.0.0" - } - }, - "request": { - "version": "2.83.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", - "integrity": "sha1-ygtl2gLtYpNYh4COb1EDgQNOM1Y=", - "requires": { - "aws-sign2": "0.7.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.6", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.3.2", - "har-validator": "5.0.3", - "hawk": "6.0.2", - "http-signature": "1.2.0", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.17", - "oauth-sign": "0.8.2", - "performance-now": "2.1.0", - "qs": "6.5.1", - "safe-buffer": "5.1.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.3", - "tunnel-agent": "0.6.0", - "uuid": "3.2.1" - }, - "dependencies": { - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "requires": { - "co": "4.6.0", - "fast-deep-equal": "1.0.0", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.3.1" - } - }, - "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" - }, - "aws4": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", - "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", - "optional": true, - "requires": { - "tweetnacl": "0.14.5" - } - }, - "boom": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", - "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", - "requires": { - "hoek": "4.2.0" - } - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" - }, - "combined-stream": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", - "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", - "requires": { - "delayed-stream": "1.0.0" - } - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "cryptiles": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", - "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", - "requires": { - "boom": "5.2.0" - }, - "dependencies": { - "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "requires": { - "hoek": "4.2.0" - } - } - } - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "1.0.0" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, - "fast-deep-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", - "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=" - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, - "form-data": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", - "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.6", - "mime-types": "2.1.17" - } - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { - "assert-plus": "1.0.0" - } - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" - }, - "har-validator": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", - "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", - "requires": { - "ajv": "5.5.2", - "har-schema": "2.0.0" - } - }, - "hawk": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", - "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", - "requires": { - "boom": "4.3.1", - "cryptiles": "3.1.2", - "hoek": "4.2.0", - "sntp": "2.1.0" - } - }, - "hoek": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz", - "integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ==" - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "requires": { - "assert-plus": "1.0.0", - "jsprim": "1.4.1", - "sshpk": "1.13.1" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "optional": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "mime-db": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", - "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" - }, - "mime-types": { - "version": "2.1.17", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", - "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", - "requires": { - "mime-db": "1.30.0" - } - }, - "oauth-sign": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - }, - "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" - }, - "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" - }, - "sntp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", - "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", - "requires": { - "hoek": "4.2.0" - } - }, - "sshpk": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", - "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", - "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" - } - }, - "stringstream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" - }, - "tough-cookie": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", - "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", - "requires": { - "punycode": "1.4.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "5.1.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "optional": true - }, - "uuid": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", - "integrity": "sha1-EsUou51Y0LkmXZovbw/ovhf/HxQ=" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { - "assert-plus": "1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "1.3.0" - } - } - } - }, "rimraf": { "version": "2.4.5", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", @@ -2422,1856 +702,11 @@ "glob": "6.0.4" } }, - "safe-buffer": { - "version": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=" - }, "safe-json-stringify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.0.4.tgz", "integrity": "sha1-gaCY9Efku8P/MxKiQ1IbwGDvWRE=", "optional": true - }, - "sandboxed-module": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-0.3.0.tgz", - "integrity": "sha1-8fvvvYCaT2kHO9B8rm/H2y6vX2o=", - "dev": true, - "requires": { - "require-like": "0.1.2", - "stack-trace": "0.0.6" - }, - "dependencies": { - "require-like": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", - "integrity": "sha1-rW8wwTvs15cBDEaK+ndcDAprR/o=", - "dev": true - }, - "stack-trace": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.6.tgz", - "integrity": "sha1-HnGb1qJin/CcGJ4Xqe+QKpT8XbA=", - "dev": true - } - } - }, - "sequelize": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-2.1.3.tgz", - "integrity": "sha1-FrpROxNnlY/oIzhYEelRFFHt6tE=", - "requires": { - "bluebird": "2.9.34", - "dottie": "0.3.1", - "generic-pool": "2.2.0", - "inflection": "1.12.0", - "lodash": "3.10.1", - "moment": "2.20.1", - "node-uuid": "1.4.8", - "toposort-class": "0.3.1", - "validator": "3.43.0" - }, - "dependencies": { - "bluebird": { - "version": "2.9.34", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.9.34.tgz", - "integrity": "sha1-L3tOyAIWMoqf3evfacjUlC/v99g=" - }, - "dottie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/dottie/-/dottie-0.3.1.tgz", - "integrity": "sha1-QYX3d0iZYKwIQTHeP5oUoXm7dJ8=" - }, - "generic-pool": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.2.0.tgz", - "integrity": "sha1-i0ZcGnWI6p3SuxM72gu2a/74pj4=" - }, - "inflection": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz", - "integrity": "sha1-ogCTVlbW9fa8TcdQLhrstwMihBY=" - }, - "lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" - }, - "moment": { - "version": "2.20.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.20.1.tgz", - "integrity": "sha512-Yh9y73JRljxW5QxN08Fner68eFLxM5ynNOAw2LbIB1YAGeQzZT8QFSUvkAz609Zf+IHhhaUxqZK8dG3W/+HEvg==" - }, - "toposort-class": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-0.3.1.tgz", - "integrity": "sha1-0TM67XNPhgjO0ShiGL0sRfYO/0g=" - }, - "validator": { - "version": "3.43.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-3.43.0.tgz", - "integrity": "sha1-lkZLmS1BloM9l6GUv0Cxn/VLrgU=" - } - } - }, - "settings-sharelatex": { - "version": "git+https://github.com/sharelatex/settings-sharelatex.git#cbc5e41c1dbe6789721a14b3fdae05bf22546559", - "requires": { - "coffee-script": "1.6.0" - } - }, - "sigmund": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", - "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" - }, - "sinon": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.7.3.tgz", - "integrity": "sha1-emnWnNApRYbHQyVO7/G1g6UJl/I=", - "dev": true, - "requires": { - "buster-format": "0.5.6" - }, - "dependencies": { - "buster-core": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/buster-core/-/buster-core-0.6.4.tgz", - "integrity": "sha1-J79rrWdCROpyDzEdkAoMoct4YFA=", - "dev": true - }, - "buster-format": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/buster-format/-/buster-format-0.5.6.tgz", - "integrity": "sha1-K4bDIuz14bCubm55Bev884fSq5U=", - "dev": true, - "requires": { - "buster-core": "0.6.4" - } - } - } - }, - "smoke-test-sharelatex": { - "version": "git+https://github.com/sharelatex/smoke-test-sharelatex.git#bc3e93d18ccee219c0d99e8b02c984ccdd842e1c", - "requires": { - "mocha": "1.17.1" - }, - "dependencies": { - "glob": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", - "integrity": "sha1-4xPusknHr/qlxHUoaw4RW1mDlGc=", - "requires": { - "graceful-fs": "2.0.3", - "inherits": "2.0.3", - "minimatch": "0.2.14" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "minimatch": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", - "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", - "requires": { - "lru-cache": "2.7.3", - "sigmund": "1.0.1" - } - }, - "mocha": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-1.17.1.tgz", - "integrity": "sha1-f3Zx1oUm0HS3uuZgyQmfh+DqHMs=", - "requires": { - "commander": "2.0.0", - "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "diff": "1.0.7", - "glob": "3.2.3", - "growl": "1.7.0", - "jade": "0.26.3", - "mkdirp": "0.3.5" - } - } - } - }, - "split-ca": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", - "integrity": "sha1-bIOv82kvphJW4M0ZfgXp3hV2kaY=" - }, - "sqlite3": { - "version": "3.1.13", - "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-3.1.13.tgz", - "integrity": "sha512-JxXKPJnkZ6NuHRojq+g2WXWBt3M1G9sjZaYiHEWSTGijDM3cwju/0T2XbWqMXFmPqDgw+iB7zKQvnns4bvzXlw==", - "requires": { - "nan": "2.7.0", - "node-pre-gyp": "0.6.38" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true - }, - "ajv": { - "version": "4.11.8", - "bundled": true, - "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" - } - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true - }, - "are-we-there-yet": { - "version": "1.1.4", - "bundled": true, - "requires": { - "delegates": "1.0.0", - "readable-stream": "2.3.3" - } - }, - "asn1": { - "version": "0.2.3", - "bundled": true - }, - "assert-plus": { - "version": "0.2.0", - "bundled": true - }, - "asynckit": { - "version": "0.4.0", - "bundled": true - }, - "aws-sign2": { - "version": "0.6.0", - "bundled": true - }, - "aws4": { - "version": "1.6.0", - "bundled": true - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "bundled": true, - "optional": true, - "requires": { - "tweetnacl": "0.14.5" - } - }, - "block-stream": { - "version": "0.0.9", - "bundled": true, - "requires": { - "inherits": "2.0.3" - } - }, - "boom": { - "version": "2.10.1", - "bundled": true, - "requires": { - "hoek": "2.16.3" - } - }, - "brace-expansion": { - "version": "1.1.8", - "bundled": true, - "requires": { - "balanced-match": "1.0.0", - "concat-map": "0.0.1" - } - }, - "caseless": { - "version": "0.12.0", - "bundled": true - }, - "co": { - "version": "4.6.0", - "bundled": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true - }, - "combined-stream": { - "version": "1.0.5", - "bundled": true, - "requires": { - "delayed-stream": "1.0.0" - } - }, - "concat-map": { - "version": "0.0.1", - "bundled": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true - }, - "cryptiles": { - "version": "2.0.5", - "bundled": true, - "requires": { - "boom": "2.10.1" - } - }, - "dashdash": { - "version": "1.14.1", - "bundled": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true - } - } - }, - "debug": { - "version": "2.6.9", - "bundled": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.4.2", - "bundled": true - }, - "delayed-stream": { - "version": "1.0.0", - "bundled": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true - }, - "ecc-jsbn": { - "version": "0.1.1", - "bundled": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "extend": { - "version": "3.0.1", - "bundled": true - }, - "extsprintf": { - "version": "1.3.0", - "bundled": true - }, - "forever-agent": { - "version": "0.6.1", - "bundled": true - }, - "form-data": { - "version": "2.1.4", - "bundled": true, - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.17" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true - }, - "fstream": { - "version": "1.0.11", - "bundled": true, - "requires": { - "graceful-fs": "4.1.11", - "inherits": "2.0.3", - "mkdirp": "0.5.1", - "rimraf": "2.6.2" - } - }, - "fstream-ignore": { - "version": "1.0.5", - "bundled": true, - "requires": { - "fstream": "1.0.11", - "inherits": "2.0.3", - "minimatch": "3.0.4" - } - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "requires": { - "aproba": "1.2.0", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" - } - }, - "getpass": { - "version": "0.1.7", - "bundled": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true - } - } - }, - "glob": { - "version": "7.1.2", - "bundled": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "graceful-fs": { - "version": "4.1.11", - "bundled": true - }, - "har-schema": { - "version": "1.0.5", - "bundled": true - }, - "har-validator": { - "version": "4.2.1", - "bundled": true, - "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true - }, - "hawk": { - "version": "3.1.3", - "bundled": true, - "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" - } - }, - "hoek": { - "version": "2.16.3", - "bundled": true - }, - "http-signature": { - "version": "1.1.1", - "bundled": true, - "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.1", - "sshpk": "1.13.1" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true - }, - "ini": { - "version": "1.3.4", - "bundled": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-typedarray": { - "version": "1.0.0", - "bundled": true - }, - "isarray": { - "version": "1.0.0", - "bundled": true - }, - "isstream": { - "version": "0.1.2", - "bundled": true - }, - "jsbn": { - "version": "0.1.1", - "bundled": true, - "optional": true - }, - "json-schema": { - "version": "0.2.3", - "bundled": true - }, - "json-stable-stringify": { - "version": "1.0.1", - "bundled": true, - "requires": { - "jsonify": "0.0.0" - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "bundled": true - }, - "jsonify": { - "version": "0.0.0", - "bundled": true - }, - "jsprim": { - "version": "1.4.1", - "bundled": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true - } - } - }, - "mime-db": { - "version": "1.30.0", - "bundled": true - }, - "mime-types": { - "version": "2.1.17", - "bundled": true, - "requires": { - "mime-db": "1.30.0" - } - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "requires": { - "brace-expansion": "1.1.8" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "bundled": true - }, - "nan": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz", - "integrity": "sha1-2Vv3IeyHfgjbJ27T/G63j5CDrUY=" - }, - "node-pre-gyp": { - "version": "0.6.38", - "bundled": true, - "requires": { - "hawk": "3.1.3", - "mkdirp": "0.5.1", - "nopt": "4.0.1", - "npmlog": "4.1.2", - "rc": "1.2.1", - "request": "2.81.0", - "rimraf": "2.6.2", - "semver": "5.4.1", - "tar": "2.2.1", - "tar-pack": "3.4.0" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "requires": { - "abbrev": "1.1.1", - "osenv": "0.1.4" - } - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true - }, - "oauth-sign": { - "version": "0.8.2", - "bundled": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "requires": { - "wrappy": "1.0.2" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true - }, - "osenv": { - "version": "0.1.4", - "bundled": true, - "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true - }, - "performance-now": { - "version": "0.2.0", - "bundled": true - }, - "process-nextick-args": { - "version": "1.0.7", - "bundled": true - }, - "punycode": { - "version": "1.4.1", - "bundled": true - }, - "qs": { - "version": "6.4.0", - "bundled": true - }, - "rc": { - "version": "1.2.1", - "bundled": true, - "requires": { - "deep-extend": "0.4.2", - "ini": "1.3.4", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true - } - } - }, - "readable-stream": { - "version": "2.3.3", - "bundled": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" - } - }, - "request": { - "version": "2.81.0", - "bundled": true, - "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "4.2.1", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.17", - "oauth-sign": "0.8.2", - "performance-now": "0.2.0", - "qs": "6.4.0", - "safe-buffer": "5.1.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.3", - "tunnel-agent": "0.6.0", - "uuid": "3.1.0" - } - }, - "rimraf": { - "version": "2.6.2", - "bundled": true, - "requires": { - "glob": "7.1.2" - } - }, - "safe-buffer": { - "version": "5.1.1", - "bundled": true - }, - "semver": { - "version": "5.4.1", - "bundled": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true - }, - "sntp": { - "version": "1.0.9", - "bundled": true, - "requires": { - "hoek": "2.16.3" - } - }, - "sshpk": { - "version": "1.13.1", - "bundled": true, - "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true - } - } - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - }, - "string_decoder": { - "version": "1.0.3", - "bundled": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "stringstream": { - "version": "0.0.5", - "bundled": true - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true - }, - "tar": { - "version": "2.2.1", - "bundled": true, - "requires": { - "block-stream": "0.0.9", - "fstream": "1.0.11", - "inherits": "2.0.3" - } - }, - "tar-pack": { - "version": "3.4.0", - "bundled": true, - "requires": { - "debug": "2.6.9", - "fstream": "1.0.11", - "fstream-ignore": "1.0.5", - "once": "1.4.0", - "readable-stream": "2.3.3", - "rimraf": "2.6.2", - "tar": "2.2.1", - "uid-number": "0.0.6" - } - }, - "tough-cookie": { - "version": "2.3.3", - "bundled": true, - "requires": { - "punycode": "1.4.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "bundled": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "bundled": true, - "optional": true - }, - "uid-number": { - "version": "0.0.6", - "bundled": true - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true - }, - "uuid": { - "version": "3.1.0", - "bundled": true - }, - "verror": { - "version": "1.10.0", - "bundled": true, - "requires": { - "assert-plus": "1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "1.3.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true - } - } - }, - "wide-align": { - "version": "1.1.2", - "bundled": true, - "requires": { - "string-width": "1.0.2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true - } - } - }, - "stack-trace": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz", - "integrity": "sha1-qPbq7KkGdMMz58Q5U/J1tFFRBpU=" - }, - "statsd-parser": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz", - "integrity": "sha1-y9JDlTzELv/VSLXSI4jtaJ7GOb0=" - }, - "string_decoder": { - "version": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - }, - "supports-color": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha1-iD992rwWUUKyphQn8zUt7RldGj4=", - "dev": true, - "requires": { - "has-flag": "2.0.0" - } - }, - "tar-fs": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.12.0.tgz", - "integrity": "sha1-pqgFU9ilTHPeHQrg553ncDVgXh0=", - "requires": { - "mkdirp": "0.5.1", - "pump": "1.0.3", - "tar-stream": "1.5.5" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" - } - } - } - }, - "tar-stream": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.5.tgz", - "integrity": "sha1-XK2Ed59FyDsfJQjZawnYjHIYr1U=", - "requires": { - "bl": "1.2.1", - "end-of-stream": "1.4.1", - "readable-stream": "2.3.3", - "xtend": "4.0.1" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", - "requires": { - "core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "isarray": "1.0.0", - "process-nextick-args": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "string_decoder": "1.0.3", - "util-deprecate": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" - } - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "requires": { - "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" - } - } - } - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" - }, - "timekeeper": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/timekeeper/-/timekeeper-0.0.4.tgz", - "integrity": "sha1-kNt58X2Ni1NiFUOJSSuXJ2LP0nY=", - "dev": true - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" - }, - "underscore": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", - "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" - }, - "util-deprecate": { - "version": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "uuid": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz", - "integrity": "sha1-Zyj8BFnEUNeWqZwxg3VpvfZy1yg=" - }, - "v8-profiler": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/v8-profiler/-/v8-profiler-5.7.0.tgz", - "integrity": "sha1-6DgcvrtbX9DKjSsJ9qAYGhWNs00=", - "requires": { - "nan": "2.8.0", - "node-pre-gyp": "0.6.39" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", - "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" - } - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "are-we-there-yet": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", - "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", - "requires": { - "delegates": "1.0.0", - "readable-stream": "2.3.4" - } - }, - "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" - }, - "assert-plus": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "aws-sign2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=" - }, - "aws4": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", - "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", - "optional": true, - "requires": { - "tweetnacl": "0.14.5" - } - }, - "boom": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", - "requires": { - "hoek": "2.16.3" - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "1.0.0", - "concat-map": "0.0.1" - } - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, - "combined-stream": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", - "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", - "requires": { - "delayed-stream": "1.0.0" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "cryptiles": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", - "requires": { - "boom": "2.10.1" - } - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - } - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", - "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=" - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" - }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" - }, - "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, - "form-data": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", - "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.6", - "mime-types": "2.1.17" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "fstream": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", - "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", - "requires": { - "graceful-fs": "4.1.11", - "inherits": "2.0.3", - "mkdirp": "0.5.1", - "rimraf": "2.6.2" - } - }, - "fstream-ignore": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", - "integrity": "sha1-nDHa40dnAY/h0kmyTa2mfQktoQU=", - "requires": { - "fstream": "1.0.11", - "inherits": "2.0.3", - "minimatch": "3.0.4" - } - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "requires": { - "aproba": "1.2.0", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" - } - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - } - } - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" - }, - "har-schema": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", - "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=" - }, - "har-validator": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", - "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", - "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" - } - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" - }, - "hawk": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", - "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" - } - }, - "hoek": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" - }, - "http-signature": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", - "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.1", - "sshpk": "1.13.1" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "optional": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", - "requires": { - "jsonify": "0.0.0" - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - } - } - }, - "mime-db": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", - "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" - }, - "mime-types": { - "version": "2.1.17", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", - "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", - "requires": { - "mime-db": "1.30.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "1.1.11" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "nan": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", - "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=" - }, - "node-pre-gyp": { - "version": "0.6.39", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz", - "integrity": "sha512-OsJV74qxnvz/AMGgcfZoDaeDXKD3oY3QVIbBmwszTFkRisTSXbMQyn4UWzUMOtA5SVhrBZOTp0wcoSBgfMfMmQ==", - "requires": { - "detect-libc": "1.0.3", - "hawk": "3.1.3", - "mkdirp": "0.5.1", - "nopt": "4.0.1", - "npmlog": "4.1.2", - "rc": "1.2.5", - "request": "2.81.0", - "rimraf": "2.6.2", - "semver": "5.5.0", - "tar": "2.2.1", - "tar-pack": "3.4.1" - } - }, - "nopt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", - "requires": { - "abbrev": "1.1.1", - "osenv": "0.1.4" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "oauth-sign": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1.0.2" - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, - "osenv": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", - "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=", - "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "performance-now": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", - "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=" - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - }, - "qs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", - "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=" - }, - "rc": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.5.tgz", - "integrity": "sha1-J1zWh/bjs2zHVrqibf7oCnkDAf0=", - "requires": { - "deep-extend": "0.4.2", - "ini": "1.3.5", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - } - } - }, - "readable-stream": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.4.tgz", - "integrity": "sha512-vuYxeWYM+fde14+rajzqgeohAI7YoJcHE7kXDAc4Nk0EbuKnJfqtY9YtRkLo/tqkuF7MsBQRhPnPeyjYITp3ZQ==", - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" - } - }, - "request": { - "version": "2.81.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", - "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", - "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.6", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "4.2.1", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.17", - "oauth-sign": "0.8.2", - "performance-now": "0.2.0", - "qs": "6.4.0", - "safe-buffer": "5.1.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.3", - "tunnel-agent": "0.6.0", - "uuid": "3.2.1" - } - }, - "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", - "requires": { - "glob": "7.1.2" - } - }, - "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" - }, - "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" - }, - "sntp": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", - "requires": { - "hoek": "2.16.3" - } - }, - "sshpk": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", - "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", - "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - } - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "requires": { - "safe-buffer": "5.1.1" - } - }, - "stringstream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "2.1.1" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" - }, - "tar": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", - "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", - "requires": { - "block-stream": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", - "fstream": "1.0.11", - "inherits": "2.0.3" - } - }, - "tar-pack": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.1.tgz", - "integrity": "sha512-PPRybI9+jM5tjtCbN2cxmmRU7YmqT3Zv/UDy48tAh2XRkLa9bAORtSWLkVc13+GJF+cdTh1yEnHEk3cpTaL5Kg==", - "requires": { - "debug": "2.6.9", - "fstream": "1.0.11", - "fstream-ignore": "1.0.5", - "once": "1.4.0", - "readable-stream": "2.3.4", - "rimraf": "2.6.2", - "tar": "2.2.1", - "uid-number": "0.0.6" - } - }, - "tough-cookie": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", - "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", - "requires": { - "punycode": "1.4.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "5.1.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "optional": true - }, - "uid-number": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", - "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=" - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "uuid": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", - "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { - "assert-plus": "1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "1.3.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - } - } - }, - "wide-align": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", - "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", - "requires": { - "string-width": "1.0.2" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - } - } - }, - "wrappy": { - "version": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "wrench": { - "version": "1.5.9", - "resolved": "https://registry.npmjs.org/wrench/-/wrench-1.5.9.tgz", - "integrity": "sha1-QRaRxjqbJTGxcAJnJ5veyiOyFCo=" - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" } } } diff --git a/package.json b/package.json index fdb5030..e0e4fda 100644 --- a/package.json +++ b/package.json @@ -14,9 +14,9 @@ "test:acceptance": "npm run compile:app && npm run compile:acceptance_tests && npm run test:acceptance:_run -- $@", "test:unit:_run": "mocha --recursive --exit --reporter spec $@ test/unit/js", "test:unit": "npm run compile:app && npm run compile:unit_tests && npm run test:unit:_run -- $@", - "compile:unit_tests": "[ -e test/unit ] && coffee -o test/unit/js -c test/unit/coffee || echo 'No unit tests to compile, skipping'", - "compile:acceptance_tests": "[ -e test/acceptance ] && coffee -o test/acceptance/js -c test/acceptance/coffee || echo 'No acceptance tests to compile, skipping'", - "compile:all": "npm run compile:app && npm run compile:unit_tests && npm run compile:acceptance_tests && npm run compile:smoke_tests", + "compile:unit_tests": "[ -e test/unit ] && coffee -o test/unit/js -c test/unit/coffee || echo 'No unit tests to compile'", + "compile:acceptance_tests": "[ -e test/acceptance ] && coffee -o test/acceptance/js -c test/acceptance/coffee || echo 'No acceptance tests to compile'", + "compile:all": "npm run compile:app && npm run compile:unit_tests && npm run compile:acceptance_tests", "nodemon": "nodemon --config nodemon.json", "compile:smoke_tests": "[ -e test/smoke ] && coffee -o test/smoke/js -c test/smoke/coffee || echo 'No smoke tests to compile, skipping'" }, diff --git a/patch-texlive-dockerfile b/patch-texlive-dockerfile new file mode 100644 index 0000000..61cb796 --- /dev/null +++ b/patch-texlive-dockerfile @@ -0,0 +1,3 @@ +FROM quay.io/sharelatex/texlive-full:2017.1 + +# RUN usermod -u 1001 tex diff --git a/synctex.profile b/synctex.profile new file mode 100644 index 0000000..577a901 --- /dev/null +++ b/synctex.profile @@ -0,0 +1,34 @@ +include /etc/firejail/disable-common.inc +include /etc/firejail/disable-devel.inc +# include /etc/firejail/disable-mgmt.inc ## removed in 0.9.40 +# include /etc/firejail/disable-secret.inc ## removed in 0.9.40 + +read-only /bin +blacklist /boot +blacklist /dev +read-only /etc +blacklist /home # blacklisted for synctex +read-only /lib +read-only /lib64 +blacklist /media +blacklist /mnt +blacklist /opt +blacklist /root +read-only /run +blacklist /sbin +blacklist /selinux +blacklist /src +blacklist /sys +read-only /usr + +caps.drop all +noroot +nogroups +net none +private-tmp +private-dev +shell none +seccomp +nonewprivs + + From b330ee2d5b44e277cbc795b4fa4af242a2d8d3c1 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Thu, 29 Mar 2018 13:07:55 +0100 Subject: [PATCH 050/118] grep works with command updated build scripts acceptence tests break, files are written as root when user is node --- Makefile | 7 ++++--- app.coffee | 4 ++-- app/coffee/ResourceWriter.coffee | 6 ++++++ docker-compose-config.yml | 4 +++- docker-compose.ci.yml | 6 ++---- docker-compose.yml | 10 ++++++---- entrypoint.sh | 12 +++++++++--- install_deps.sh | 9 --------- package.json | 4 ++-- .../coffee/ExampleDocumentTests.coffee | 17 +++++++++++++---- 10 files changed, 47 insertions(+), 32 deletions(-) diff --git a/Makefile b/Makefile index 2a1196c..2db377a 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,7 @@ DOCKER_COMPOSE_FLAGS ?= -f docker-compose.yml DOCKER_COMPOSE := BUILD_NUMBER=$(BUILD_NUMBER) \ BRANCH_NAME=$(BRANCH_NAME) \ PROJECT_NAME=$(PROJECT_NAME) \ + MOCHA_GREP=${MOCHA_GREP} \ docker-compose ${DOCKER_COMPOSE_FLAGS} clean: @@ -21,13 +22,13 @@ clean: test: test_unit test_acceptance test_unit: - @[ ! -d test/unit ] && echo "clsi has no unit tests" || $(DOCKER_COMPOSE) run --rm test_unit -- ${MOCHA_ARGS} + @[ ! -d test/unit ] && echo "clsi has no unit tests" || $(DOCKER_COMPOSE) run --rm test_unit test_acceptance: test_clean # clear the database before each acceptance test run - @[ ! -d test/acceptance ] && echo "clsi has no acceptance tests" || $(DOCKER_COMPOSE) run --rm test_acceptance -- ${MOCHA_ARGS} + @[ ! -d test/acceptance ] && echo "clsi has no acceptance tests" || $(DOCKER_COMPOSE) run --rm test_acceptance test_clean: - $(DOCKER_COMPOSE) down -t -v 0 + $(DOCKER_COMPOSE) down -v -t 0 build: docker build --pull --tag quay.io/sharelatex/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) . diff --git a/app.coffee b/app.coffee index a3f4ef1..11981a7 100644 --- a/app.coffee +++ b/app.coffee @@ -182,7 +182,7 @@ server = net.createServer (socket) -> logger.err err:err, "error with socket on load check" socket.destroy() - if STATE == "up" and settings.load_balancer_agent.report_load + if STATE == "up" and Settings.load_balancer_agent.report_load currentLoad = os.loadavg()[0] # staging clis's have 1 cpu core only @@ -206,7 +206,7 @@ server = net.createServer (socket) -> port = (Settings.internal?.clsi?.port or 3013) host = (Settings.internal?.clsi?.host or "localhost") -load_port = settings.internal.clsi.load_port or 3048 +load_port = Settings.internal.clsi.load_port or 3048 diff --git a/app/coffee/ResourceWriter.coffee b/app/coffee/ResourceWriter.coffee index 0b6aef5..90ada04 100644 --- a/app/coffee/ResourceWriter.coffee +++ b/app/coffee/ResourceWriter.coffee @@ -120,7 +120,13 @@ module.exports = ResourceWriter = logger.err err:err, project_id:project_id, path:path, resource_url:resource.url, modified:resource.modified, "error downloading file for resources" callback() #try and continue compiling even if http resource can not be downloaded at this time else + process = require("process") + console.log "writing file out", path, process.getuid() fs.writeFile path, resource.content, callback + try + result = fs.lstatSync(path) + console.log "path stats", result + catch e checkPath: (basePath, resourcePath, callback) -> path = Path.normalize(Path.join(basePath, resourcePath)) diff --git a/docker-compose-config.yml b/docker-compose-config.yml index f2bb863..b6deacf 100644 --- a/docker-compose-config.yml +++ b/docker-compose-config.yml @@ -12,6 +12,8 @@ services: volumes: - /var/run/docker.sock:/var/run/docker.sock - ./compiles:/app/compiles + - ./cache:/app/cache + ci: environment: @@ -24,4 +26,4 @@ services: SQLITE_PATH: /app/compiles/db.sqlite volumes: - /var/run/docker.sock:/var/run/docker.sock:rw - - ./compiles:/app/compiles \ No newline at end of file + - ./cache:/app/cache diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index 370e2a8..85512cf 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -8,8 +8,7 @@ version: "2" services: test_unit: image: quay.io/sharelatex/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER - user: node - entrypoint: npm run test:unit:_run + command: npm run test:unit:_run test_acceptance: build: . @@ -23,8 +22,7 @@ services: depends_on: - redis - mongo - user: node - entrypoint: npm run test:acceptance:_run + command: npm run test:acceptance:_run redis: image: redis diff --git a/docker-compose.yml b/docker-compose.yml index 32cbf61..5f53d69 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,8 +11,9 @@ services: volumes: - .:/app working_dir: /app - user: node - entrypoint: npm run test:unit + environment: + MOCHA_GREP: ${MOCHA_GREP} + command: npm run test:unit test_acceptance: build: . @@ -25,11 +26,12 @@ services: environment: REDIS_HOST: redis MONGO_HOST: mongo - user: node + environment: + MOCHA_GREP: ${MOCHA_GREP} depends_on: - redis - mongo - entrypoint: npm run test:acceptance + command: npm run test:acceptance redis: image: redis diff --git a/entrypoint.sh b/entrypoint.sh index ee5ea47..32776fa 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,7 +1,10 @@ #!/bin/sh echo "Changing permissions of /var/run/docker.sock for sibling containers" - +ls -al /var/run/docker.sock +docker --version +cat /etc/passwd +usermod -aG docker node chown root:docker /var/run/docker.sock mkdir -p /app/cache @@ -10,7 +13,10 @@ chown -R node:node /app/cache mkdir -p /app/compiles chown -R node:node /app/compiles +chown -R node:node /app/bin/synctex +mkdir -p /app/test/acceptance/fixtures/tmp/ +chown -R node:node /app + + ./bin/install_texlive_gce.sh -echo "HELOOOo" -echo "$@" exec runuser -u node "$@" \ No newline at end of file diff --git a/install_deps.sh b/install_deps.sh index 4b06f22..49bdc5c 100755 --- a/install_deps.sh +++ b/install_deps.sh @@ -2,12 +2,3 @@ wget -qO- https://get.docker.com/ | sh apt-get install poppler-utils vim ghostscript --yes npm rebuild -usermod -aG docker node - -mkdir -p /app/cache -chown -R node:node /app/cache - -mkdir -p /app/compiles -chown -R node:node /app/compiles - -chown -R node:node /app/bin/synctex diff --git a/package.json b/package.json index e0e4fda..fe2bd26 100644 --- a/package.json +++ b/package.json @@ -11,9 +11,9 @@ "compile": "npm run compile:app && npm run compile:test:acceptance && npm run compile:test:smoke", "start": "npm run compile:app && node $NODE_APP_OPTIONS app.js", "test:acceptance:_run": "mocha --recursive --reporter spec --timeout 15000 --exit $@ test/acceptance/js", - "test:acceptance": "npm run compile:app && npm run compile:acceptance_tests && npm run test:acceptance:_run -- $@", + "test:acceptance": "npm run compile:app && npm run compile:acceptance_tests && npm run test:acceptance:_run -- --grep=$MOCHA_GREP", "test:unit:_run": "mocha --recursive --exit --reporter spec $@ test/unit/js", - "test:unit": "npm run compile:app && npm run compile:unit_tests && npm run test:unit:_run -- $@", + "test:unit": "npm run compile:app && npm run compile:unit_tests && npm run test:unit:_run -- --grep=$MOCHA_GREP", "compile:unit_tests": "[ -e test/unit ] && coffee -o test/unit/js -c test/unit/coffee || echo 'No unit tests to compile'", "compile:acceptance_tests": "[ -e test/acceptance ] && coffee -o test/acceptance/js -c test/acceptance/coffee || echo 'No acceptance tests to compile'", "compile:all": "npm run compile:app && npm run compile:unit_tests && npm run compile:acceptance_tests", diff --git a/test/acceptance/coffee/ExampleDocumentTests.coffee b/test/acceptance/coffee/ExampleDocumentTests.coffee index ec56979..d4bd19f 100644 --- a/test/acceptance/coffee/ExampleDocumentTests.coffee +++ b/test/acceptance/coffee/ExampleDocumentTests.coffee @@ -4,15 +4,21 @@ require("chai").should() fs = require "fs" ChildProcess = require "child_process" ClsiApp = require "./helpers/ClsiApp" - -fixturePath = (path) -> __dirname + "/../fixtures/" + path - +logger = require("logger-sharelatex") +Path = require("path") +fixturePath = (path) -> Path.normalize(__dirname + "/../fixtures/" + path) +process = require "process" +console.log process.pid, process.ppid, process.getuid(),process.getgroups(), "PID" try + console.log "creating tmp directory", fixturePath("tmp") fs.mkdirSync(fixturePath("tmp")) -catch e +catch err + console.log err, fixturePath("tmp"), "unable to create fixture tmp path" convertToPng = (pdfPath, pngPath, callback = (error) ->) -> command = "convert #{fixturePath(pdfPath)} #{fixturePath(pngPath)}" + console.log "COMMAND" + console.log command convert = ChildProcess.exec command stdout = "" convert.stdout.on "data", (chunk) -> console.log "STDOUT", chunk.toString() @@ -58,6 +64,8 @@ compareMultiplePages = (project_id, callback = (error) ->) -> compareNext 0, callback comparePdf = (project_id, example_dir, callback = (error) ->) -> + console.log "CONVERT" + console.log "tmp/#{project_id}.pdf", "tmp/#{project_id}-generated.png" convertToPng "tmp/#{project_id}.pdf", "tmp/#{project_id}-generated.png", (error) => throw error if error? convertToPng "examples/#{example_dir}/output.pdf", "tmp/#{project_id}-source.png", (error) => @@ -76,6 +84,7 @@ comparePdf = (project_id, example_dir, callback = (error) ->) -> downloadAndComparePdf = (project_id, example_dir, url, callback = (error) ->) -> writeStream = fs.createWriteStream(fixturePath("tmp/#{project_id}.pdf")) request.get(url).pipe(writeStream) + console.log("writing file out", fixturePath("tmp/#{project_id}.pdf")) writeStream.on "close", () => checkPdfInfo "tmp/#{project_id}.pdf", (error, optimised) => throw error if error? From ca23cd42ada5803d6237506572f078e00d354c77 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Mon, 9 Apr 2018 11:06:35 +0100 Subject: [PATCH 051/118] update package.json scripts --- docker-compose.yml | 1 - package.json | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 5f53d69..5a25a01 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -26,7 +26,6 @@ services: environment: REDIS_HOST: redis MONGO_HOST: mongo - environment: MOCHA_GREP: ${MOCHA_GREP} depends_on: - redis diff --git a/package.json b/package.json index fe2bd26..49835cc 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,9 @@ "test:acceptance:_run": "mocha --recursive --reporter spec --timeout 15000 --exit $@ test/acceptance/js", "test:acceptance": "npm run compile:app && npm run compile:acceptance_tests && npm run test:acceptance:_run -- --grep=$MOCHA_GREP", "test:unit:_run": "mocha --recursive --exit --reporter spec $@ test/unit/js", - "test:unit": "npm run compile:app && npm run compile:unit_tests && npm run test:unit:_run -- --grep=$MOCHA_GREP", - "compile:unit_tests": "[ -e test/unit ] && coffee -o test/unit/js -c test/unit/coffee || echo 'No unit tests to compile'", - "compile:acceptance_tests": "[ -e test/acceptance ] && coffee -o test/acceptance/js -c test/acceptance/coffee || echo 'No acceptance tests to compile'", + "test:unit": "npm run compile:app && npm run compile:unit_tests && npm run test:unit:_run -- --grep=$MOCHA_GREP", + "compile:unit_tests": "[ ! -e test/unit/coffee ] && echo 'No unit tests to compile' || coffee -o test/unit/js -c test/unit/coffee", + "compile:acceptance_tests": "[ ! -e test/acceptance/coffee ] && echo 'No acceptance tests to compile' || coffee -o test/acceptance/js -c test/acceptance/coffee", "compile:all": "npm run compile:app && npm run compile:unit_tests && npm run compile:acceptance_tests", "nodemon": "nodemon --config nodemon.json", "compile:smoke_tests": "[ -e test/smoke ] && coffee -o test/smoke/js -c test/smoke/coffee || echo 'No smoke tests to compile, skipping'" From 98a4e60eb7456c859a9075181ee2c5ed2e01d587 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Thu, 24 May 2018 19:03:57 +0100 Subject: [PATCH 052/118] update to 1.1.3 build scripts --- .dockerignore | 6 +++ .nvmrc | 2 +- Dockerfile | 15 +++--- Makefile | 11 +++-- bin/synctex | Bin 90472 -> 92840 bytes docker-compose.ci.yml | 10 ++-- docker-compose.yml | 9 ++-- nodemon.json | 2 + package.json | 104 +++++++++++++++++++++--------------------- 9 files changed, 88 insertions(+), 71 deletions(-) diff --git a/.dockerignore b/.dockerignore index a998353..386f26d 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,9 @@ node_modules/* +gitrev +.git +.gitignore +.npm +.nvmrc +nodemon.json app.js **/js/* diff --git a/.nvmrc b/.nvmrc index 5917993..bbf0c5a 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -6.13.0 +6.14.1 diff --git a/Dockerfile b/Dockerfile index ed1f2c6..1ccc689 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,20 +1,23 @@ -FROM node:6.13.0 as app - -COPY ./ /app +FROM node:6.14.1 as app WORKDIR /app -RUN npm install +#wildcard as some files may not be in all repos +COPY package*.json npm-shrink*.json /app/ + +RUN npm install --quiet + +COPY . /app RUN npm run compile:all -FROM node:6.13.0 +FROM node:6.14.1 COPY --from=app /app /app WORKDIR /app -RUN ./install_deps.sh +RUN chmod 0755 ./install_deps.sh && ./install_deps.sh ENTRYPOINT ["/bin/sh", "entrypoint.sh"] CMD ["node","app.js"] diff --git a/Makefile b/Makefile index 2db377a..63ff8c0 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.0 +# Version: 1.1.3 BUILD_NUMBER ?= local BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) @@ -13,6 +13,7 @@ DOCKER_COMPOSE := BUILD_NUMBER=$(BUILD_NUMBER) \ MOCHA_GREP=${MOCHA_GREP} \ docker-compose ${DOCKER_COMPOSE_FLAGS} + clean: rm -f app.js rm -rf app/js @@ -24,16 +25,18 @@ test: test_unit test_acceptance test_unit: @[ ! -d test/unit ] && echo "clsi has no unit tests" || $(DOCKER_COMPOSE) run --rm test_unit -test_acceptance: test_clean # clear the database before each acceptance test run +test_acceptance: test_clean test_acceptance_pre_run # clear the database before each acceptance test run @[ ! -d test/acceptance ] && echo "clsi has no acceptance tests" || $(DOCKER_COMPOSE) run --rm test_acceptance test_clean: $(DOCKER_COMPOSE) down -v -t 0 +test_acceptance_pre_run: + @[ ! -f test/acceptance/scripts/pre-run ] && echo "clsi has no pre acceptance tests task" || $(DOCKER_COMPOSE) run --rm test_acceptance test/acceptance/scripts/pre-run build: - docker build --pull --tag quay.io/sharelatex/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) . + docker build --pull --tag gcr.io/csh-gcdm-test/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) . publish: - docker push quay.io/sharelatex/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) + docker push gcr.io/csh-gcdm-test/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) .PHONY: clean test test_unit test_acceptance test_clean build publish diff --git a/bin/synctex b/bin/synctex index 99044b97a0259b30afbf0bfb34365e4f1270479e..e793e248bb52d48f28a105fb6dd2ea9ead1659e1 100755 GIT binary patch literal 92840 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:nwe6m+2D>* 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", + }, + "author": "James Allen ", "dependencies": { - "async": "0.2.9", - "body-parser": "^1.2.0", - "dockerode": "^2.5.3", - "express": "^4.2.0", - "fs-extra": "^0.16.3", - "grunt-mkdir": "^1.0.0", - "heapdump": "^0.3.5", - "lockfile": "^1.0.3", - "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.4", - "lynx": "0.0.11", - "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.5.0", - "mkdirp": "0.3.5", - "mysql": "2.6.2", - "request": "^2.21.0", - "sequelize": "^2.1.3", - "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", - "smoke-test-sharelatex": "git+https://github.com/sharelatex/smoke-test-sharelatex.git#v0.2.0", - "sqlite3": "^3.1.13", - "underscore": "^1.8.2", - "v8-profiler": "^5.2.4", + "async": "0.2.9", + "body-parser": "^1.2.0", + "dockerode": "^2.5.3", + "express": "^4.2.0", + "fs-extra": "^0.16.3", + "grunt-mkdir": "^1.0.0", + "heapdump": "^0.3.5", + "lockfile": "^1.0.3", + "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.4", + "lynx": "0.0.11", + "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.5.0", + "mkdirp": "0.3.5", + "mysql": "2.6.2", + "request": "^2.21.0", + "sequelize": "^2.1.3", + "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", + "smoke-test-sharelatex": "git+https://github.com/sharelatex/smoke-test-sharelatex.git#v0.2.0", + "sqlite3": "^3.1.13", + "underscore": "^1.8.2", + "v8-profiler": "^5.2.4", "wrench": "~1.5.4" - }, + }, "devDependencies": { - "mocha": "^4.0.1", - "coffee-script": "1.6.0", - "chai": "~1.8.1", - "sinon": "~1.7.3", - "grunt": "~0.4.2", - "grunt-contrib-coffee": "~0.7.0", - "grunt-contrib-clean": "~0.5.0", - "grunt-shell": "~0.6.1", - "grunt-mocha-test": "~0.8.1", - "sandboxed-module": "~0.3.0", - "timekeeper": "0.0.4", - "grunt-execute": "^0.1.5", - "bunyan": "^0.22.1", + "mocha": "^4.0.1", + "coffee-script": "1.6.0", + "chai": "~1.8.1", + "sinon": "~1.7.3", + "grunt": "~0.4.2", + "grunt-contrib-coffee": "~0.7.0", + "grunt-contrib-clean": "~0.5.0", + "grunt-shell": "~0.6.1", + "grunt-mocha-test": "~0.8.1", + "sandboxed-module": "~0.3.0", + "timekeeper": "0.0.4", + "grunt-execute": "^0.1.5", + "bunyan": "^0.22.1", "grunt-bunyan": "^0.5.0" } } From 1814f1c997c3c0fe4f675c80676b9f070518bfb3 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Thu, 24 May 2018 21:59:02 +0100 Subject: [PATCH 053/118] added --exit to unit tests --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c925708..a0ea4f0 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "start": "npm run compile:app && node $NODE_APP_OPTIONS app.js", "test:acceptance:_run": "mocha --recursive --reporter spec --timeout 15000 --exit $@ test/acceptance/js", "test:acceptance": "npm run compile:app && npm run compile:acceptance_tests && npm run test:acceptance:_run -- --grep=$MOCHA_GREP", - "test:unit:_run": "mocha --recursive --reporter spec $@ test/unit/js", + "test:unit:_run": "mocha --recursive --reporter spec --exit $@ test/unit/js", "test:unit": "npm run compile:app && npm run compile:unit_tests && npm run test:unit:_run -- --grep=$MOCHA_GREP", "compile:unit_tests": "[ ! -e test/unit/coffee ] && echo 'No unit tests to compile' || coffee -o test/unit/js -c test/unit/coffee", "compile:acceptance_tests": "[ ! -e test/acceptance/coffee ] && echo 'No acceptance tests to compile' || coffee -o test/acceptance/js -c test/acceptance/coffee", From e544ad9a238f847258a21e5bcc196ffe7ec1cace Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Fri, 25 May 2018 11:51:34 +0100 Subject: [PATCH 054/118] set user to tex for tests run on ci box --- docker-compose-config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose-config.yml b/docker-compose-config.yml index b6deacf..ce0c502 100644 --- a/docker-compose-config.yml +++ b/docker-compose-config.yml @@ -18,7 +18,7 @@ services: ci: environment: TEXLIVE_IMAGE: quay.io/sharelatex/texlive-full:2017.1 - TEXLIVE_IMAGE_USER: root + TEXLIVE_IMAGE_USER: tex SHARELATEX_CONFIG: /app/config/settings.defaults.coffee DOCKER_RUNNER: "true" COMPILES_HOST_DIR: $PWD/compiles From da814b0e3a7de59c46363957ec8a626eb44ebe68 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Fri, 25 May 2018 12:01:16 +0100 Subject: [PATCH 055/118] log settings on startup --- config/settings.defaults.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/config/settings.defaults.coffee b/config/settings.defaults.coffee index de853f5..ab6e7e6 100644 --- a/config/settings.defaults.coffee +++ b/config/settings.defaults.coffee @@ -46,3 +46,4 @@ if process.env["DOCKER_RUNNER"] module.exports.path.sandboxedCompilesHostDir = process.env["COMPILES_HOST_DIR"] module.exports.path.synctexBinHostPath = process.env["SYNCTEX_BIN_HOST_PATH"] +console.log module.exports \ No newline at end of file From 95486151697ba5f2d2002d2fd41a6acce5c5349d Mon Sep 17 00:00:00 2001 From: henry oswald Date: Fri, 25 May 2018 12:43:12 +0000 Subject: [PATCH 056/118] all but the sync tests should pass --- config/settings.defaults.coffee | 3 ++- docker-compose-config.yml | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/config/settings.defaults.coffee b/config/settings.defaults.coffee index ab6e7e6..2da6834 100644 --- a/config/settings.defaults.coffee +++ b/config/settings.defaults.coffee @@ -46,4 +46,5 @@ if process.env["DOCKER_RUNNER"] module.exports.path.sandboxedCompilesHostDir = process.env["COMPILES_HOST_DIR"] module.exports.path.synctexBinHostPath = process.env["SYNCTEX_BIN_HOST_PATH"] -console.log module.exports \ No newline at end of file +console.log "configggggg" +console.log module.exports diff --git a/docker-compose-config.yml b/docker-compose-config.yml index ce0c502..3b78060 100644 --- a/docker-compose-config.yml +++ b/docker-compose-config.yml @@ -4,7 +4,7 @@ services: dev: environment: TEXLIVE_IMAGE: quay.io/sharelatex/texlive-full:2017.1 - TEXLIVE_IMAGE_USER: root + TEXLIVE_IMAGE_USER: "tex" SHARELATEX_CONFIG: /app/config/settings.defaults.coffee DOCKER_RUNNER: "true" COMPILES_HOST_DIR: $PWD/compiles @@ -18,7 +18,7 @@ services: ci: environment: TEXLIVE_IMAGE: quay.io/sharelatex/texlive-full:2017.1 - TEXLIVE_IMAGE_USER: tex + TEXLIVE_IMAGE_USER: "tex" SHARELATEX_CONFIG: /app/config/settings.defaults.coffee DOCKER_RUNNER: "true" COMPILES_HOST_DIR: $PWD/compiles @@ -27,3 +27,4 @@ services: volumes: - /var/run/docker.sock:/var/run/docker.sock:rw - ./cache:/app/cache + - ./compiles:/app/compiles From 0b3af7d75988e81010f5a82499d6bf4ecaaa91eb Mon Sep 17 00:00:00 2001 From: henry oswald Date: Fri, 25 May 2018 13:45:07 +0000 Subject: [PATCH 057/118] change synctex binary and added it to mounted volumes in docker config --- bin/synctex | Bin 92840 -> 92776 bytes docker-compose-config.yml | 4 +++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/bin/synctex b/bin/synctex index e793e248bb52d48f28a105fb6dd2ea9ead1659e1..89b8cc698827b5e02a1b6053f32098766304b739 100755 GIT binary patch literal 92776 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 literal 92840 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 Date: Fri, 25 May 2018 15:30:26 +0100 Subject: [PATCH 058/118] bumped timeout to 30 seconds --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a0ea4f0..49a259a 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "compile:app": "([ -e app/coffee ] && coffee $COFFEE_OPTIONS -o app/js -c app/coffee || echo 'No CoffeeScript folder to compile') && ( [ -e app.coffee ] && coffee $COFFEE_OPTIONS -c app.coffee || echo 'No CoffeeScript app to compile')", "compile": "npm run compile:app && npm run compile:test:acceptance && npm run compile:test:smoke", "start": "npm run compile:app && node $NODE_APP_OPTIONS app.js", - "test:acceptance:_run": "mocha --recursive --reporter spec --timeout 15000 --exit $@ test/acceptance/js", + "test:acceptance:_run": "mocha --recursive --reporter spec --timeout 30000 --exit $@ test/acceptance/js", "test:acceptance": "npm run compile:app && npm run compile:acceptance_tests && npm run test:acceptance:_run -- --grep=$MOCHA_GREP", "test:unit:_run": "mocha --recursive --reporter spec --exit $@ test/unit/js", "test:unit": "npm run compile:app && npm run compile:unit_tests && npm run test:unit:_run -- --grep=$MOCHA_GREP", From 85aec72206be30020e92f38ac046f16923258daf Mon Sep 17 00:00:00 2001 From: Christopher Hoskin Date: Tue, 12 Jun 2018 10:15:17 +0100 Subject: [PATCH 059/118] Use metadata to determine Google Cloud project dynamically. Fixes: #601 --- bin/install_texlive_gce.sh | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/bin/install_texlive_gce.sh b/bin/install_texlive_gce.sh index 85ab709..4f98fed 100755 --- a/bin/install_texlive_gce.sh +++ b/bin/install_texlive_gce.sh @@ -1,13 +1,19 @@ #!/bin/sh METADATA=http://metadata.google.internal./computeMetadata/v1 SVC_ACCT=$METADATA/instance/service-accounts/default +PROJECT_URL=$METADATA/project/project-id ACCESS_TOKEN=$(curl -s -H 'Metadata-Flavor: Google' $SVC_ACCT/token | cut -d'"' -f 4) -if [ -z "$ACCESS_TOKEN" ]; then - echo "No acccess token to download texlive-full images from google container, continuing without downloading. This is likely not a google cloud enviroment." +#if [ -z "$ACCESS_TOKEN" ]; then +# echo "No acccess token to download texlive-full images from google container, continuing without downloading. This is likely not a google cloud enviroment." +# exit 0 +#fi +PROJECT=$(curl -s -H 'Metadata-Flavor: Google' $PROJECT_URL) +if [ -z "$PROJECT" ]; then + echo "No project name to download texlive-full images from google container, continuing without downloading. This is likely not a google cloud enviroment." exit 0 fi docker login -u '_token' -p $ACCESS_TOKEN https://gcr.io -docker pull --all-tags gcr.io/henry-terraform-admin/texlive-full #TODO NEED TO MAKE THIS AN ENV VAR +docker pull --all-tags gcr.io/$PROJECT/texlive-full cp /app/bin/synctex /app/bin/synctex-mount/synctex echo "Finished downloading texlive-full images" From e6532b5681086db89c3cb54e55f264a9301a2238 Mon Sep 17 00:00:00 2001 From: Christopher Hoskin Date: Tue, 12 Jun 2018 10:22:30 +0100 Subject: [PATCH 060/118] Update build scripts from 1.1.3 to 1.1.6 --- Jenkinsfile | 9 ++++++++- Makefile | 11 ++++++++--- docker-compose.ci.yml | 9 +++++---- docker-compose.yml | 4 +++- package.json | 4 ++-- 5 files changed, 26 insertions(+), 11 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index bc9ba01..eddce50 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -29,7 +29,13 @@ pipeline { stage('Package and publish build') { steps { - sh 'make publish' + + withCredentials([file(credentialsId: 'csh-gcdm-test', variable: 'DOCKER_REPO_KEY_PATH')]) { + sh 'docker login -u _json_key --password-stdin https://csh-gcdm-test < ${DOCKER_REPO_KEY_PATH}' + } + sh 'DOCKER_REPO=csh-gcdm-test make publish' + sh 'docker logout https://csh-gcdm-test' + } } @@ -47,6 +53,7 @@ pipeline { post { always { sh 'DOCKER_COMPOSE_FLAGS="-f docker-compose.ci.yml" make test_clean' + sh 'make clean' } failure { diff --git a/Makefile b/Makefile index 63ff8c0..ae7cf16 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.3 +# Version: 1.1.6 BUILD_NUMBER ?= local BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) @@ -15,6 +15,8 @@ DOCKER_COMPOSE := BUILD_NUMBER=$(BUILD_NUMBER) \ clean: + docker rmi ci/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) + docker rmi csh-gcdm-test/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) rm -f app.js rm -rf app/js rm -rf test/unit/js @@ -34,9 +36,12 @@ test_clean: test_acceptance_pre_run: @[ ! -f test/acceptance/scripts/pre-run ] && echo "clsi has no pre acceptance tests task" || $(DOCKER_COMPOSE) run --rm test_acceptance test/acceptance/scripts/pre-run build: - docker build --pull --tag gcr.io/csh-gcdm-test/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) . + docker build --pull --tag ci/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) \ + --tag csh-gcdm-test/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) \ + . publish: - docker push gcr.io/csh-gcdm-test/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) + + docker push $(DOCKER_REPO)/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) .PHONY: clean test test_unit test_acceptance test_clean build publish diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index 0e14b0f..651b454 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -1,25 +1,27 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.3 +# Version: 1.1.6 version: "2" services: test_unit: - image: gcr.io/csh-gcdm-test/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER + image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER command: npm run test:unit:_run test_acceptance: build: . - image: gcr.io/csh-gcdm-test/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER + image: ci/$PROJECT_NAME:$BRANCH_NAME-$BUILD_NUMBER extends: file: docker-compose-config.yml service: ci environment: + ELASTIC_SEARCH_DSN: es:9200 REDIS_HOST: redis MONGO_HOST: mongo POSTGRES_HOST: postgres + MOCHA_GREP: ${MOCHA_GREP} depends_on: - mongo - redis @@ -30,4 +32,3 @@ services: mongo: image: mongo:3.4 - diff --git a/docker-compose.yml b/docker-compose.yml index 2283746..a0b4261 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.3 +# Version: 1.1.6 version: "2" @@ -24,6 +24,7 @@ services: file: docker-compose-config.yml service: dev environment: + ELASTIC_SEARCH_DSN: es:9200 REDIS_HOST: redis MONGO_HOST: mongo POSTGRES_HOST: postgres @@ -32,6 +33,7 @@ services: - mongo - redis command: npm run test:acceptance + redis: image: redis diff --git a/package.json b/package.json index 49a259a..8bcdba2 100644 --- a/package.json +++ b/package.json @@ -10,9 +10,9 @@ "compile:app": "([ -e app/coffee ] && coffee $COFFEE_OPTIONS -o app/js -c app/coffee || echo 'No CoffeeScript folder to compile') && ( [ -e app.coffee ] && coffee $COFFEE_OPTIONS -c app.coffee || echo 'No CoffeeScript app to compile')", "compile": "npm run compile:app && npm run compile:test:acceptance && npm run compile:test:smoke", "start": "npm run compile:app && node $NODE_APP_OPTIONS app.js", - "test:acceptance:_run": "mocha --recursive --reporter spec --timeout 30000 --exit $@ test/acceptance/js", + "test:acceptance:_run": "mocha --recursive --reporter spec --timeout 15000 --exit $@ test/acceptance/js", "test:acceptance": "npm run compile:app && npm run compile:acceptance_tests && npm run test:acceptance:_run -- --grep=$MOCHA_GREP", - "test:unit:_run": "mocha --recursive --reporter spec --exit $@ test/unit/js", + "test:unit:_run": "mocha --recursive --reporter spec --exit $@ test/unit/js", "test:unit": "npm run compile:app && npm run compile:unit_tests && npm run test:unit:_run -- --grep=$MOCHA_GREP", "compile:unit_tests": "[ ! -e test/unit/coffee ] && echo 'No unit tests to compile' || coffee -o test/unit/js -c test/unit/coffee", "compile:acceptance_tests": "[ ! -e test/acceptance/coffee ] && echo 'No acceptance tests to compile' || coffee -o test/acceptance/js -c test/acceptance/coffee", From da216c52e961a74c31b5111d26384baf51ecf75d Mon Sep 17 00:00:00 2001 From: Christopher Hoskin Date: Tue, 12 Jun 2018 11:17:26 +0100 Subject: [PATCH 061/118] Accidently left warning message commented out :( --- bin/install_texlive_gce.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bin/install_texlive_gce.sh b/bin/install_texlive_gce.sh index 4f98fed..ee6efba 100755 --- a/bin/install_texlive_gce.sh +++ b/bin/install_texlive_gce.sh @@ -3,10 +3,10 @@ METADATA=http://metadata.google.internal./computeMetadata/v1 SVC_ACCT=$METADATA/instance/service-accounts/default PROJECT_URL=$METADATA/project/project-id ACCESS_TOKEN=$(curl -s -H 'Metadata-Flavor: Google' $SVC_ACCT/token | cut -d'"' -f 4) -#if [ -z "$ACCESS_TOKEN" ]; then -# echo "No acccess token to download texlive-full images from google container, continuing without downloading. This is likely not a google cloud enviroment." -# exit 0 -#fi +if [ -z "$ACCESS_TOKEN" ]; then + echo "No acccess token to download texlive-full images from google container, continuing without downloading. This is likely not a google cloud enviroment." + exit 0 +fi PROJECT=$(curl -s -H 'Metadata-Flavor: Google' $PROJECT_URL) if [ -z "$PROJECT" ]; then echo "No project name to download texlive-full images from google container, continuing without downloading. This is likely not a google cloud enviroment." From 4ca8027cb8f7c0e4beda1341c2d49f5de9fe8219 Mon Sep 17 00:00:00 2001 From: Christopher Hoskin Date: Tue, 12 Jun 2018 15:04:14 +0100 Subject: [PATCH 062/118] Increase acceptance test timeout. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8bcdba2..92265eb 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "compile:app": "([ -e app/coffee ] && coffee $COFFEE_OPTIONS -o app/js -c app/coffee || echo 'No CoffeeScript folder to compile') && ( [ -e app.coffee ] && coffee $COFFEE_OPTIONS -c app.coffee || echo 'No CoffeeScript app to compile')", "compile": "npm run compile:app && npm run compile:test:acceptance && npm run compile:test:smoke", "start": "npm run compile:app && node $NODE_APP_OPTIONS app.js", - "test:acceptance:_run": "mocha --recursive --reporter spec --timeout 15000 --exit $@ test/acceptance/js", + "test:acceptance:_run": "mocha --recursive --reporter spec --timeout 30000 --exit $@ test/acceptance/js", "test:acceptance": "npm run compile:app && npm run compile:acceptance_tests && npm run test:acceptance:_run -- --grep=$MOCHA_GREP", "test:unit:_run": "mocha --recursive --reporter spec --exit $@ test/unit/js", "test:unit": "npm run compile:app && npm run compile:unit_tests && npm run test:unit:_run -- --grep=$MOCHA_GREP", From 0a70985ba55dd8afd1933212d5b5bad7850e2486 Mon Sep 17 00:00:00 2001 From: Christopher Hoskin Date: Tue, 12 Jun 2018 15:26:10 +0100 Subject: [PATCH 063/118] Specify repo correctly --- Jenkinsfile | 8 ++++---- Makefile | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index eddce50..b88e3e4 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -30,11 +30,11 @@ pipeline { stage('Package and publish build') { steps { - withCredentials([file(credentialsId: 'csh-gcdm-test', variable: 'DOCKER_REPO_KEY_PATH')]) { - sh 'docker login -u _json_key --password-stdin https://csh-gcdm-test < ${DOCKER_REPO_KEY_PATH}' + withCredentials([file(credentialsId: 'gcr.io_csh-gcdm-test', variable: 'DOCKER_REPO_KEY_PATH')]) { + sh 'docker login -u _json_key --password-stdin https://gcr.io/csh-gcdm-test < ${DOCKER_REPO_KEY_PATH}' } - sh 'DOCKER_REPO=csh-gcdm-test make publish' - sh 'docker logout https://csh-gcdm-test' + sh 'DOCKER_REPO=gcr.io/csh-gcdm-test make publish' + sh 'docker logout https://gcr.io/csh-gcdm-test' } } diff --git a/Makefile b/Makefile index ae7cf16..a1cefa4 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ DOCKER_COMPOSE := BUILD_NUMBER=$(BUILD_NUMBER) \ clean: docker rmi ci/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) - docker rmi csh-gcdm-test/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) + docker rmi gcr.io/csh-gcdm-test/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) rm -f app.js rm -rf app/js rm -rf test/unit/js @@ -37,7 +37,7 @@ test_acceptance_pre_run: @[ ! -f test/acceptance/scripts/pre-run ] && echo "clsi has no pre acceptance tests task" || $(DOCKER_COMPOSE) run --rm test_acceptance test/acceptance/scripts/pre-run build: docker build --pull --tag ci/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) \ - --tag csh-gcdm-test/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) \ + --tag gcr.io/csh-gcdm-test/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) \ . publish: From 926667f365ed988d12f89bac6aaada6eda4f5def Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 12 Jun 2018 17:44:13 +0100 Subject: [PATCH 064/118] update build scripts so smoke tests are compiled --- Makefile | 2 +- docker-compose.ci.yml | 2 +- docker-compose.yml | 2 +- package.json | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index a1cefa4..ca126e8 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.6 +# Version: 1.1.7 BUILD_NUMBER ?= local BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index 651b454..9f3d54d 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.6 +# Version: 1.1.7 version: "2" diff --git a/docker-compose.yml b/docker-compose.yml index a0b4261..4c1cfc7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.6 +# Version: 1.1.7 version: "2" diff --git a/package.json b/package.json index 92265eb..54625fd 100644 --- a/package.json +++ b/package.json @@ -16,9 +16,9 @@ "test:unit": "npm run compile:app && npm run compile:unit_tests && npm run test:unit:_run -- --grep=$MOCHA_GREP", "compile:unit_tests": "[ ! -e test/unit/coffee ] && echo 'No unit tests to compile' || coffee -o test/unit/js -c test/unit/coffee", "compile:acceptance_tests": "[ ! -e test/acceptance/coffee ] && echo 'No acceptance tests to compile' || coffee -o test/acceptance/js -c test/acceptance/coffee", - "compile:all": "npm run compile:app && npm run compile:unit_tests && npm run compile:acceptance_tests", + "compile:all": "npm run compile:app && npm run compile:unit_tests && npm run compile:acceptance_tests && npm run compile:smoke_tests", "nodemon": "nodemon --config nodemon.json", - "compile:smoke_tests": "[ -e test/smoke ] && coffee -o test/smoke/js -c test/smoke/coffee || echo 'No smoke tests to compile, skipping'" + "compile:smoke_tests": "[ ! -e test/smoke/coffee] && echo 'No smoke tests to compile' || coffee -o test/smoke/js -c test/smoke/coffee" }, "author": "James Allen ", "dependencies": { From b30890ef9948030c44e36e16cbc3fb8c318f9131 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 12 Jun 2018 17:48:23 +0100 Subject: [PATCH 065/118] remove the compile npm command, it isn't needed --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 54625fd..79f9c80 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,6 @@ }, "scripts": { "compile:app": "([ -e app/coffee ] && coffee $COFFEE_OPTIONS -o app/js -c app/coffee || echo 'No CoffeeScript folder to compile') && ( [ -e app.coffee ] && coffee $COFFEE_OPTIONS -c app.coffee || echo 'No CoffeeScript app to compile')", - "compile": "npm run compile:app && npm run compile:test:acceptance && npm run compile:test:smoke", "start": "npm run compile:app && node $NODE_APP_OPTIONS app.js", "test:acceptance:_run": "mocha --recursive --reporter spec --timeout 30000 --exit $@ test/acceptance/js", "test:acceptance": "npm run compile:app && npm run compile:acceptance_tests && npm run test:acceptance:_run -- --grep=$MOCHA_GREP", From b3033c16860437892241673cb8a62e35cbe5e31f Mon Sep 17 00:00:00 2001 From: Christopher Hoskin Date: Wed, 13 Jun 2018 15:47:45 +0100 Subject: [PATCH 066/118] Add csh-staging to repos --- Jenkinsfile | 6 ++++++ Makefile | 2 ++ 2 files changed, 8 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index b88e3e4..efe8448 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -36,6 +36,12 @@ pipeline { sh 'DOCKER_REPO=gcr.io/csh-gcdm-test make publish' sh 'docker logout https://gcr.io/csh-gcdm-test' + withCredentials([file(credentialsId: 'gcr.io_csh-staging', variable: 'DOCKER_REPO_KEY_PATH')]) { + sh 'docker login -u _json_key --password-stdin https://gcr.io/csh-staging < ${DOCKER_REPO_KEY_PATH}' + } + sh 'DOCKER_REPO=gcr.io/csh-staging make publish' + sh 'docker logout https://gcr.io/csh-staging' + } } diff --git a/Makefile b/Makefile index ca126e8..b7c96a2 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,7 @@ DOCKER_COMPOSE := BUILD_NUMBER=$(BUILD_NUMBER) \ clean: docker rmi ci/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) docker rmi gcr.io/csh-gcdm-test/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) + docker rmi gcr.io/csh-staging/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) rm -f app.js rm -rf app/js rm -rf test/unit/js @@ -38,6 +39,7 @@ test_acceptance_pre_run: build: docker build --pull --tag ci/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) \ --tag gcr.io/csh-gcdm-test/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) \ + --tag gcr.io/csh-staging/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) \ . publish: From dd93d37460a759571a00c838a85e52b028944bd3 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 26 Jun 2018 12:43:47 +0100 Subject: [PATCH 067/118] added seccomp --- config/settings.defaults.coffee | 1 + seccomp/clsi-profile.json | 792 ++++++++++++++++++++++++++++++++ 2 files changed, 793 insertions(+) create mode 100644 seccomp/clsi-profile.json diff --git a/config/settings.defaults.coffee b/config/settings.defaults.coffee index 2da6834..ced53a9 100644 --- a/config/settings.defaults.coffee +++ b/config/settings.defaults.coffee @@ -40,6 +40,7 @@ if process.env["DOCKER_RUNNER"] user: process.env["TEXLIVE_IMAGE_USER"] or "tex" expireProjectAfterIdleMs: 24 * 60 * 60 * 1000 checkProjectsIntervalMs: 10 * 60 * 1000 + seccomp_profile: JSON.stringify(JSON.parse(require("fs").readFileSync(Path.resolve(__dirname + "/../seccomp/clsi-profile.json")))) module.exports.path.synctexBaseDir = -> "/compile" diff --git a/seccomp/clsi-profile.json b/seccomp/clsi-profile.json new file mode 100644 index 0000000..cab7a44 --- /dev/null +++ b/seccomp/clsi-profile.json @@ -0,0 +1,792 @@ +{ + "defaultAction": "SCMP_ACT_ERRNO", + "architectures": [ + "SCMP_ARCH_X86_64", + "SCMP_ARCH_X86", + "SCMP_ARCH_X32" + ], + "syscalls": [ + { + "name": "access", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "arch_prctl", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "brk", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "chdir", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "chmod", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "clone", + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 2080505856, + "valueTwo": 0, + "op": "SCMP_CMP_MASKED_EQ" + } + ] + }, + { + "name": "close", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "copy_file_range", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "creat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "dup", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "dup2", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "dup3", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "execve", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "execveat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "exit", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "exit_group", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "faccessat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fadvise64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fadvise64_64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fallocate", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fchdir", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fchmod", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fchmodat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fcntl", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fcntl64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fdatasync", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fork", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fstat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fstat64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fstatat64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fstatfs", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fstatfs64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fsync", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "ftruncate", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "ftruncate64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "futex", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "futimesat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getcpu", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getcwd", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getdents", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getdents64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getegid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getegid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "geteuid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "geteuid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getgid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getgid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getgroups", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getgroups32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getpgid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getpgrp", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getpid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getppid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getpriority", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getresgid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getresgid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getresuid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getresuid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getrlimit", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "get_robust_list", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getrusage", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getsid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "gettid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getuid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getuid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "ioctl", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "kill", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "_llseek", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "lseek", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "lstat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "lstat64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "madvise", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mkdir", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mkdirat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mmap", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mmap2", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mprotect", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mremap", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "munmap", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "newfstatat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "open", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "openat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "pause", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "pipe", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "pipe2", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "prctl", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "pread64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "preadv", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "prlimit64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "pwrite64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "pwritev", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "read", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "readlink", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "readlinkat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "readv", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "rename", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "renameat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "renameat2", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "restart_syscall", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "rmdir", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "rt_sigaction", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "rt_sigpending", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "rt_sigprocmask", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "rt_sigqueueinfo", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "rt_sigreturn", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "rt_sigsuspend", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "rt_sigtimedwait", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "rt_tgsigqueueinfo", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_getaffinity", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_getparam", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_get_priority_max", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_get_priority_min", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_getscheduler", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_rr_get_interval", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_yield", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sendfile", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sendfile64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setgroups", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setgroups32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "set_robust_list", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "set_tid_address", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sigaltstack", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "stat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "stat64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "statfs", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "statfs64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sync", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sync_file_range", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "syncfs", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sysinfo", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "tgkill", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "times", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "tkill", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "truncate", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "truncate64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "umask", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "uname", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "unlink", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "unlinkat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "utime", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "utimensat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "utimes", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "vfork", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "vhangup", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "wait4", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "waitid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "write", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "writev", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "pread", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setgid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setuid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "capget", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "capset", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fchown", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "gettimeofday", + "action": "SCMP_ACT_ALLOW", + "args": [] + } + ] +} \ No newline at end of file From 911e1d58f7f863168b7e9d7a4b1d23b939a59d39 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 26 Jun 2018 14:44:03 +0100 Subject: [PATCH 068/118] put seccomp_profile_path into variable and try catch --- config/settings.defaults.coffee | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/config/settings.defaults.coffee b/config/settings.defaults.coffee index ced53a9..901637c 100644 --- a/config/settings.defaults.coffee +++ b/config/settings.defaults.coffee @@ -40,7 +40,12 @@ if process.env["DOCKER_RUNNER"] user: process.env["TEXLIVE_IMAGE_USER"] or "tex" expireProjectAfterIdleMs: 24 * 60 * 60 * 1000 checkProjectsIntervalMs: 10 * 60 * 1000 - seccomp_profile: JSON.stringify(JSON.parse(require("fs").readFileSync(Path.resolve(__dirname + "/../seccomp/clsi-profile.json")))) + + try + seccomp_profile_path = Path.resolve(__dirname + "/../seccomp/clsi-profile.json") + module.exports.clsi.seccomp_profile = JSON.stringify(JSON.parse(require("fs").readFileSync(seccomp_profile_path))) + catch + console.log "could not load seccom profile from #{seccomp_profile_path}" module.exports.path.synctexBaseDir = -> "/compile" From 364c8097c88e4c830400a2ef3deafec2d0a310b5 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 26 Jun 2018 15:04:56 +0100 Subject: [PATCH 069/118] add error catch to settings.defaults --- config/settings.defaults.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/settings.defaults.coffee b/config/settings.defaults.coffee index 901637c..a5b6f1b 100644 --- a/config/settings.defaults.coffee +++ b/config/settings.defaults.coffee @@ -44,8 +44,8 @@ if process.env["DOCKER_RUNNER"] try seccomp_profile_path = Path.resolve(__dirname + "/../seccomp/clsi-profile.json") module.exports.clsi.seccomp_profile = JSON.stringify(JSON.parse(require("fs").readFileSync(seccomp_profile_path))) - catch - console.log "could not load seccom profile from #{seccomp_profile_path}" + catch error + console.log error, "could not load seccom profile from #{seccomp_profile_path}" module.exports.path.synctexBaseDir = -> "/compile" From 4bfc02ef3b16dcf0f6edf2ffa79ba60c804652ab Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 26 Jun 2018 15:38:30 +0100 Subject: [PATCH 070/118] fix seccomp key --- config/settings.defaults.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/settings.defaults.coffee b/config/settings.defaults.coffee index a5b6f1b..da4aa82 100644 --- a/config/settings.defaults.coffee +++ b/config/settings.defaults.coffee @@ -43,7 +43,7 @@ if process.env["DOCKER_RUNNER"] try seccomp_profile_path = Path.resolve(__dirname + "/../seccomp/clsi-profile.json") - module.exports.clsi.seccomp_profile = JSON.stringify(JSON.parse(require("fs").readFileSync(seccomp_profile_path))) + module.exports.clsi.docker.seccomp_profile = JSON.stringify(JSON.parse(require("fs").readFileSync(seccomp_profile_path))) catch error console.log error, "could not load seccom profile from #{seccomp_profile_path}" From ec85957ae490b7439ec82a743c5d9e200e4dbdd6 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Thu, 28 Jun 2018 16:04:34 +0100 Subject: [PATCH 071/118] add load balance http endpoints to shut box down --- app.coffee | 32 ++++++++++++++++++++++++-------- config/settings.defaults.coffee | 9 ++++++--- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/app.coffee b/app.coffee index 11981a7..cf01b47 100644 --- a/app.coffee +++ b/app.coffee @@ -174,7 +174,11 @@ os = require "os" STATE = "up" -server = net.createServer (socket) -> +process.on "SIGHUP", -> + console.log "got SIGHUP event" + STATE = "down" + +loadTcpServer = net.createServer (socket) -> socket.on "error", (err)-> if err.code == "ECONNRESET" # this always comes up, we don't know why @@ -182,7 +186,7 @@ server = net.createServer (socket) -> logger.err err:err, "error with socket on load check" socket.destroy() - if STATE == "up" and Settings.load_balancer_agent.report_load + if STATE == "up" and Settings.internal.load_balancer_agent.report_load currentLoad = os.loadavg()[0] # staging clis's have 1 cpu core only @@ -201,26 +205,38 @@ server = net.createServer (socket) -> socket.write("#{STATE}\n", "ASCII") socket.end() +loadHttpServer = express() +loadHttpServer.post "/state/up", (req, res, next) -> + STATE = "up" + logger.info "getting message to set server to down" + res.sendStatus 204 +loadHttpServer.post "/state/down", (req, res, next) -> + STATE = "down" + logger.info "getting message to set server to down" + res.sendStatus 204 port = (Settings.internal?.clsi?.port or 3013) host = (Settings.internal?.clsi?.host or "localhost") -load_port = Settings.internal.clsi.load_port or 3048 - +load_tcp_port = Settings.internal.load_balancer_agent.load_port +load_http_port = Settings.internal.load_balancer_agent.local_port if !module.parent # Called directly app.listen port, host, (error) -> logger.info "CLSI starting up, listening on #{host}:#{port}" - server.listen load_port, host, (error) -> + loadTcpServer.listen load_tcp_port, host, (error) -> throw error if error? - logger.info "Load agent listening on load port #{load_port}" + logger.info "Load tcp agent listening on load port #{load_tcp_port}" + + loadHttpServer.listen load_http_port, host, (error) -> + throw error if error? + logger.info "Load http agent listening on load port #{load_http_port}" + module.exports = app - - setInterval () -> ProjectPersistenceManager.clearExpiredProjects() , tenMinutes = 10 * 60 * 1000 diff --git a/config/settings.defaults.coffee b/config/settings.defaults.coffee index da4aa82..a64941f 100644 --- a/config/settings.defaults.coffee +++ b/config/settings.defaults.coffee @@ -20,7 +20,11 @@ module.exports = clsi: port: 3013 host: process.env["LISTEN_ADDRESS"] or "localhost" - + + load_balancer_agent: + report_load:true + load_port: 3048 + local_port: 3049 apis: clsi: url: "http://#{process.env['CLSI_HOST'] or 'localhost'}:3013" @@ -29,6 +33,7 @@ module.exports = project_cache_length_ms: 1000 * 60 * 60 * 24 parallelFileDownloads:1 + if process.env["DOCKER_RUNNER"] module.exports.clsi = dockerRunner: process.env["DOCKER_RUNNER"] == "true" @@ -52,5 +57,3 @@ if process.env["DOCKER_RUNNER"] module.exports.path.sandboxedCompilesHostDir = process.env["COMPILES_HOST_DIR"] module.exports.path.synctexBinHostPath = process.env["SYNCTEX_BIN_HOST_PATH"] -console.log "configggggg" -console.log module.exports From 6464aefdb4dd4d7ba358f9791a550ca59d53a009 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 3 Jul 2018 16:41:34 +0100 Subject: [PATCH 072/118] added filestoreDomainOveride --- app/coffee/UrlFetcher.coffee | 6 +++++ config/settings.defaults.coffee | 1 + test/unit/coffee/UrlFetcherTests.coffee | 35 +++++++++++++++++++++++-- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/app/coffee/UrlFetcher.coffee b/app/coffee/UrlFetcher.coffee index 201306c..58645f0 100644 --- a/app/coffee/UrlFetcher.coffee +++ b/app/coffee/UrlFetcher.coffee @@ -1,6 +1,8 @@ request = require("request").defaults(jar: false) fs = require("fs") logger = require "logger-sharelatex" +settings = require("settings-sharelatex") +URL = require('url'); oneMinute = 60 * 1000 @@ -11,6 +13,9 @@ module.exports = UrlFetcher = _callback(error) _callback = () -> + if settings.filestoreDomainOveride? + p = URL.parse(url).path + url = "#{settings.filestoreDomainOveride}#{p}" timeoutHandler = setTimeout () -> timeoutHandler = null logger.error url:url, filePath: filePath, "Timed out downloading file to cache" @@ -31,6 +36,7 @@ module.exports = UrlFetcher = logger.log url:url, filePath: filePath, "finished downloading file into cache" urlStream.on "response", (res) -> + console.log if res.statusCode >= 200 and res.statusCode < 300 fileStream = fs.createWriteStream(filePath) diff --git a/config/settings.defaults.coffee b/config/settings.defaults.coffee index a64941f..081ded8 100644 --- a/config/settings.defaults.coffee +++ b/config/settings.defaults.coffee @@ -32,6 +32,7 @@ module.exports = smokeTest: process.env["SMOKE_TEST"] or false project_cache_length_ms: 1000 * 60 * 60 * 24 parallelFileDownloads:1 + filestoreDomainOveride: process.env["FILESTORE_DOMAIN_OVERRIDE"] if process.env["DOCKER_RUNNER"] diff --git a/test/unit/coffee/UrlFetcherTests.coffee b/test/unit/coffee/UrlFetcherTests.coffee index 4bd161b..e91720e 100644 --- a/test/unit/coffee/UrlFetcherTests.coffee +++ b/test/unit/coffee/UrlFetcherTests.coffee @@ -7,16 +7,47 @@ EventEmitter = require("events").EventEmitter describe "UrlFetcher", -> beforeEach -> @callback = sinon.stub() - @url = "www.example.com/file" + @url = "https://www.example.com/file/here?query=string" @UrlFetcher = SandboxedModule.require modulePath, requires: request: defaults: @defaults = sinon.stub().returns(@request = {}) fs: @fs = {} "logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() } + "settings-sharelatex": @settings = {} it "should turn off the cookie jar in request", -> @defaults.calledWith(jar: false) .should.equal true - + + describe "rewrite url domain if filestoreDomainOveride is set", -> + beforeEach -> + @path = "/path/to/file/on/disk" + @request.get = sinon.stub().returns(@urlStream = new EventEmitter) + @urlStream.pipe = sinon.stub() + @urlStream.pause = sinon.stub() + @urlStream.resume = sinon.stub() + @fs.createWriteStream = sinon.stub().returns(@fileStream = new EventEmitter) + @fs.unlink = (file, callback) -> callback() + + it "should use the normal domain when override not set", (done)-> + @UrlFetcher.pipeUrlToFile @url, @path, => + @request.get.args[0][0].url.should.equal @url + done() + @res = statusCode: 200 + @urlStream.emit "response", @res + @urlStream.emit "end" + @fileStream.emit "finish" + + + it "should use override domain when filestoreDomainOveride is set", (done)-> + @settings.filestoreDomainOveride = "192.11.11.11" + @UrlFetcher.pipeUrlToFile @url, @path, => + @request.get.args[0][0].url.should.equal "192.11.11.11/file/here?query=string" + done() + @res = statusCode: 200 + @urlStream.emit "response", @res + @urlStream.emit "end" + @fileStream.emit "finish" + describe "pipeUrlToFile", -> beforeEach (done)-> @path = "/path/to/file/on/disk" From a75cec7d52d5a0cdde7a17cfae8f04e2a0819d97 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Thu, 5 Jul 2018 15:07:07 +0100 Subject: [PATCH 073/118] added maint down endpoint --- app.coffee | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app.coffee b/app.coffee index cf01b47..88bb74b 100644 --- a/app.coffee +++ b/app.coffee @@ -174,9 +174,6 @@ os = require "os" STATE = "up" -process.on "SIGHUP", -> - console.log "got SIGHUP event" - STATE = "down" loadTcpServer = net.createServer (socket) -> socket.on "error", (err)-> @@ -217,6 +214,12 @@ loadHttpServer.post "/state/down", (req, res, next) -> logger.info "getting message to set server to down" res.sendStatus 204 +loadHttpServer.post "/state/maint", (req, res, next) -> + STATE = "maint" + logger.info "getting message to set server to maint" + res.sendStatus 204 + + port = (Settings.internal?.clsi?.port or 3013) host = (Settings.internal?.clsi?.host or "localhost") From c1277e9f22794b77123c356337046cc3853e0c5b Mon Sep 17 00:00:00 2001 From: Christopher Hoskin Date: Fri, 6 Jul 2018 15:08:38 +0100 Subject: [PATCH 074/118] Use our experimental metrics --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 79f9c80..0edb116 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "lockfile": "^1.0.3", "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.4", "lynx": "0.0.11", - "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.5.0", + "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.8.1-beta", "mkdirp": "0.3.5", "mysql": "2.6.2", "request": "^2.21.0", From 97716365afae71a2cd0603a34154e0faa2c89567 Mon Sep 17 00:00:00 2001 From: Christopher Hoskin Date: Thu, 12 Jul 2018 11:22:02 +0100 Subject: [PATCH 075/118] Depend on metrics v1.8.1 for remote StatsD host --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0edb116..ab34cda 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "lockfile": "^1.0.3", "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.4", "lynx": "0.0.11", - "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.8.1-beta", + "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.8.1", "mkdirp": "0.3.5", "mysql": "2.6.2", "request": "^2.21.0", From a960614eb4bde3667b36363326339e769d2f5643 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Fri, 13 Jul 2018 10:37:22 +0100 Subject: [PATCH 076/118] added texliveImageNameOveride --- app/coffee/RequestParser.coffee | 6 ++++++ config/settings.defaults.coffee | 1 + test/unit/coffee/RequestParserTests.coffee | 19 ++++++++++++++++++- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/app/coffee/RequestParser.coffee b/app/coffee/RequestParser.coffee index 596b529..d375e9e 100644 --- a/app/coffee/RequestParser.coffee +++ b/app/coffee/RequestParser.coffee @@ -1,3 +1,5 @@ +settings = require("settings-sharelatex") + module.exports = RequestParser = VALID_COMPILERS: ["pdflatex", "latex", "xelatex", "lualatex"] MAX_TIMEOUT: 300 @@ -67,6 +69,10 @@ module.exports = RequestParser = sanitizedRootResourcePath = RequestParser._sanitizePath(rootResourcePath) response.rootResourcePath = RequestParser._checkPath(sanitizedRootResourcePath) + if settings.texliveImageNameOveride? + tag = compile.options.imageName.split(":")[1] + response.imageName = "#{settings.texliveImageNameOveride}:#{tag}" + for resource in response.resources if resource.path == originalRootResourcePath resource.path = sanitizedRootResourcePath diff --git a/config/settings.defaults.coffee b/config/settings.defaults.coffee index 081ded8..1f3fe8b 100644 --- a/config/settings.defaults.coffee +++ b/config/settings.defaults.coffee @@ -33,6 +33,7 @@ module.exports = project_cache_length_ms: 1000 * 60 * 60 * 24 parallelFileDownloads:1 filestoreDomainOveride: process.env["FILESTORE_DOMAIN_OVERRIDE"] + texliveImageNameOveride: process.env["TEX_LIVE_IMAGE_NAME_OVERRIDE"] if process.env["DOCKER_RUNNER"] diff --git a/test/unit/coffee/RequestParserTests.coffee b/test/unit/coffee/RequestParserTests.coffee index 0b420b3..04405ac 100644 --- a/test/unit/coffee/RequestParserTests.coffee +++ b/test/unit/coffee/RequestParserTests.coffee @@ -16,10 +16,12 @@ describe "RequestParser", -> compile: token: "token-123" options: + imageName: "basicImageName/here:2017-1" compiler: "pdflatex" timeout: 42 resources: [] - @RequestParser = SandboxedModule.require modulePath + @RequestParser = SandboxedModule.require modulePath, requires: + "settings-sharelatex": @settings = {} afterEach -> tk.reset() @@ -57,6 +59,21 @@ describe "RequestParser", -> it "should set the compiler to pdflatex by default", -> @data.compiler.should.equal "pdflatex" + describe "with imageName set", -> + beforeEach -> + @RequestParser.parse @validRequest, (error, @data) => + + it "should set the imageName", -> + @data.imageName.should.equal "basicImageName/here:2017-1" + + describe "with texliveImageNameOveride set", -> + beforeEach -> + @settings.texliveImageNameOveride = "usethisoveride/overhere" + @RequestParser.parse @validRequest, (error, @data) => + + it "should override the image path", -> + @data.imageName.should.equal "usethisoveride/overhere:2017-1" + describe "without a timeout specified", -> beforeEach -> delete @validRequest.compile.options.timeout From 354585217391bf46cac7ec97b1d8908e39b04ca1 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Fri, 13 Jul 2018 11:46:37 +0100 Subject: [PATCH 077/118] quick hack to overright image name further down stack --- app/coffee/DockerRunner.coffee | 4 ++++ app/coffee/RequestParser.coffee | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/coffee/DockerRunner.coffee b/app/coffee/DockerRunner.coffee index 635243f..f48e904 100644 --- a/app/coffee/DockerRunner.coffee +++ b/app/coffee/DockerRunner.coffee @@ -38,6 +38,10 @@ module.exports = DockerRunner = if !image? image = Settings.clsi.docker.image + if Settings.texliveImageNameOveride? + tag = image.split(":")[1] + image = "#{Settings.texliveImageNameOveride}:#{tag}" + options = DockerRunner._getContainerOptions(command, image, volumes, timeout, environment) fingerprint = DockerRunner._fingerprintContainer(options) options.name = name = "project-#{project_id}-#{fingerprint}" diff --git a/app/coffee/RequestParser.coffee b/app/coffee/RequestParser.coffee index d375e9e..7824616 100644 --- a/app/coffee/RequestParser.coffee +++ b/app/coffee/RequestParser.coffee @@ -69,7 +69,7 @@ module.exports = RequestParser = sanitizedRootResourcePath = RequestParser._sanitizePath(rootResourcePath) response.rootResourcePath = RequestParser._checkPath(sanitizedRootResourcePath) - if settings.texliveImageNameOveride? + if settings.texliveImageNameOveride? and compile.options?.imageName? tag = compile.options.imageName.split(":")[1] response.imageName = "#{settings.texliveImageNameOveride}:#{tag}" From 8d846f64a9c946a606a908d81bfd4fe9dd43ff0d Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Fri, 13 Jul 2018 11:52:49 +0100 Subject: [PATCH 078/118] move texliveImageNameOveride further down request so it works for compile tests --- app/coffee/RequestParser.coffee | 4 ---- test/unit/coffee/DockerRunnerTests.coffee | 10 ++++++++++ test/unit/coffee/RequestParserTests.coffee | 8 -------- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/app/coffee/RequestParser.coffee b/app/coffee/RequestParser.coffee index 7824616..cabbac3 100644 --- a/app/coffee/RequestParser.coffee +++ b/app/coffee/RequestParser.coffee @@ -69,10 +69,6 @@ module.exports = RequestParser = sanitizedRootResourcePath = RequestParser._sanitizePath(rootResourcePath) response.rootResourcePath = RequestParser._checkPath(sanitizedRootResourcePath) - if settings.texliveImageNameOveride? and compile.options?.imageName? - tag = compile.options.imageName.split(":")[1] - response.imageName = "#{settings.texliveImageNameOveride}:#{tag}" - for resource in response.resources if resource.path == originalRootResourcePath resource.path = sanitizedRootResourcePath diff --git a/test/unit/coffee/DockerRunnerTests.coffee b/test/unit/coffee/DockerRunnerTests.coffee index 5a697e2..456f52b 100644 --- a/test/unit/coffee/DockerRunnerTests.coffee +++ b/test/unit/coffee/DockerRunnerTests.coffee @@ -135,6 +135,16 @@ describe "DockerRunner", -> @DockerRunner._getContainerOptions .calledWith(@command_with_dir, @defaultImage, @volumes, @timeout) .should.equal true + + describe "with image override", -> + beforeEach -> + @Settings.texliveImageNameOveride = "overrideimage/here" + @DockerRunner._runAndWaitForContainer = sinon.stub().callsArgWith(3, null, @output = "mock-output") + @DockerRunner.run @project_id, @command, @directory, @image, @timeout, @env, @callback + + it "should use the override and keep the tag", -> + image = @DockerRunner._getContainerOptions.args[0][1] + image.should.equal "overrideimage/here:2016.2" describe "_runAndWaitForContainer", -> beforeEach -> diff --git a/test/unit/coffee/RequestParserTests.coffee b/test/unit/coffee/RequestParserTests.coffee index 04405ac..f63bc55 100644 --- a/test/unit/coffee/RequestParserTests.coffee +++ b/test/unit/coffee/RequestParserTests.coffee @@ -66,14 +66,6 @@ describe "RequestParser", -> it "should set the imageName", -> @data.imageName.should.equal "basicImageName/here:2017-1" - describe "with texliveImageNameOveride set", -> - beforeEach -> - @settings.texliveImageNameOveride = "usethisoveride/overhere" - @RequestParser.parse @validRequest, (error, @data) => - - it "should override the image path", -> - @data.imageName.should.equal "usethisoveride/overhere:2017-1" - describe "without a timeout specified", -> beforeEach -> delete @validRequest.compile.options.timeout From dd015a05cb024ec3f22d407c3bffab524abb188e Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Mon, 16 Jul 2018 15:38:23 +0100 Subject: [PATCH 079/118] remove express header --- app.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/app.coffee b/app.coffee index 88bb74b..7855e09 100644 --- a/app.coffee +++ b/app.coffee @@ -35,6 +35,7 @@ TIMEOUT = 6 * 60 * 1000 app.use (req, res, next) -> req.setTimeout TIMEOUT res.setTimeout TIMEOUT + res.removeHeader("X-Powered-By") next() app.param 'project_id', (req, res, next, project_id) -> From bcb87620b58aea6d7ae5f450bcd56994f3d8817e Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Mon, 16 Jul 2018 17:25:14 +0100 Subject: [PATCH 080/118] change override to leave image name so it works for wl_texlive --- app/coffee/DockerRunner.coffee | 4 ++-- test/unit/coffee/DockerRunnerTests.coffee | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/coffee/DockerRunner.coffee b/app/coffee/DockerRunner.coffee index f48e904..69a3df9 100644 --- a/app/coffee/DockerRunner.coffee +++ b/app/coffee/DockerRunner.coffee @@ -39,8 +39,8 @@ module.exports = DockerRunner = image = Settings.clsi.docker.image if Settings.texliveImageNameOveride? - tag = image.split(":")[1] - image = "#{Settings.texliveImageNameOveride}:#{tag}" + img = image.split("/") + image = "#{Settings.texliveImageNameOveride}/#{img[2]}" options = DockerRunner._getContainerOptions(command, image, volumes, timeout, environment) fingerprint = DockerRunner._fingerprintContainer(options) diff --git a/test/unit/coffee/DockerRunnerTests.coffee b/test/unit/coffee/DockerRunnerTests.coffee index 456f52b..307ffde 100644 --- a/test/unit/coffee/DockerRunnerTests.coffee +++ b/test/unit/coffee/DockerRunnerTests.coffee @@ -138,13 +138,13 @@ describe "DockerRunner", -> describe "with image override", -> beforeEach -> - @Settings.texliveImageNameOveride = "overrideimage/here" + @Settings.texliveImageNameOveride = "overrideimage.com/something" @DockerRunner._runAndWaitForContainer = sinon.stub().callsArgWith(3, null, @output = "mock-output") @DockerRunner.run @project_id, @command, @directory, @image, @timeout, @env, @callback it "should use the override and keep the tag", -> image = @DockerRunner._getContainerOptions.args[0][1] - image.should.equal "overrideimage/here:2016.2" + image.should.equal "overrideimage.com/something/image:2016.2" describe "_runAndWaitForContainer", -> beforeEach -> From fb00098fc025191752bc533069b64a53cf27de41 Mon Sep 17 00:00:00 2001 From: Christopher Hoskin Date: Tue, 17 Jul 2018 12:10:08 +0100 Subject: [PATCH 081/118] Bump build script to 1.1.8, drop csh-gcdm-test and csh-staging repos --- Jenkinsfile | 56 +++++++++++++++++++++++++++++++++++-------- Makefile | 8 +++---- docker-compose.ci.yml | 2 +- docker-compose.yml | 2 +- package.json | 2 +- 5 files changed, 52 insertions(+), 18 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index efe8448..6db5e29 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -3,12 +3,33 @@ String cron_string = BRANCH_NAME == "master" ? "@daily" : "" pipeline { agent any + environment { + GIT_PROJECT = "clsi-sharelatex" + JENKINS_WORKFLOW = "clsi-sharelatex" + TARGET_URL = "${env.JENKINS_URL}blue/organizations/jenkins/${JENKINS_WORKFLOW}/detail/$BRANCH_NAME/$BUILD_NUMBER/pipeline" + GIT_API_URL = "https://api.github.com/repos/sharelatex/${GIT_PROJECT}/statuses/$GIT_COMMIT" + } + triggers { pollSCM('* * * * *') cron(cron_string) } stages { + stage('Install') { + steps { + withCredentials([usernamePassword(credentialsId: 'GITHUB_INTEGRATION', usernameVariable: 'GH_AUTH_USERNAME', passwordVariable: 'GH_AUTH_PASSWORD')]) { + sh "curl $GIT_API_URL \ + --data '{ \ + \"state\" : \"pending\", \ + \"target_url\": \"$TARGET_URL\", \ + \"description\": \"Your build is underway\", \ + \"context\": \"ci/jenkins\" }' \ + -u $GH_AUTH_USERNAME:$GH_AUTH_PASSWORD" + } + } + } + stage('Build') { steps { sh 'make build' @@ -30,17 +51,11 @@ pipeline { stage('Package and publish build') { steps { - withCredentials([file(credentialsId: 'gcr.io_csh-gcdm-test', variable: 'DOCKER_REPO_KEY_PATH')]) { - sh 'docker login -u _json_key --password-stdin https://gcr.io/csh-gcdm-test < ${DOCKER_REPO_KEY_PATH}' + withCredentials([file(credentialsId: 'gcr.io_cr-test2', variable: 'DOCKER_REPO_KEY_PATH')]) { + sh 'docker login -u _json_key --password-stdin https://gcr.io/cr-test2 < ${DOCKER_REPO_KEY_PATH}' } - sh 'DOCKER_REPO=gcr.io/csh-gcdm-test make publish' - sh 'docker logout https://gcr.io/csh-gcdm-test' - - withCredentials([file(credentialsId: 'gcr.io_csh-staging', variable: 'DOCKER_REPO_KEY_PATH')]) { - sh 'docker login -u _json_key --password-stdin https://gcr.io/csh-staging < ${DOCKER_REPO_KEY_PATH}' - } - sh 'DOCKER_REPO=gcr.io/csh-staging make publish' - sh 'docker logout https://gcr.io/csh-staging' + sh 'DOCKER_REPO=gcr.io/cr-test2 make publish' + sh 'docker logout https://gcr.io/cr-test2' } } @@ -62,11 +77,32 @@ pipeline { sh 'make clean' } + success { + withCredentials([usernamePassword(credentialsId: 'GITHUB_INTEGRATION', usernameVariable: 'GH_AUTH_USERNAME', passwordVariable: 'GH_AUTH_PASSWORD')]) { + sh "curl $GIT_API_URL \ + --data '{ \ + \"state\" : \"success\", \ + \"target_url\": \"$TARGET_URL\", \ + \"description\": \"Your build succeeded!\", \ + \"context\": \"ci/jenkins\" }' \ + -u $GH_AUTH_USERNAME:$GH_AUTH_PASSWORD" + } + } + failure { mail(from: "${EMAIL_ALERT_FROM}", to: "${EMAIL_ALERT_TO}", subject: "Jenkins build failed: ${JOB_NAME}:${BUILD_NUMBER}", body: "Build: ${BUILD_URL}") + withCredentials([usernamePassword(credentialsId: 'GITHUB_INTEGRATION', usernameVariable: 'GH_AUTH_USERNAME', passwordVariable: 'GH_AUTH_PASSWORD')]) { + sh "curl $GIT_API_URL \ + --data '{ \ + \"state\" : \"failure\", \ + \"target_url\": \"$TARGET_URL\", \ + \"description\": \"Your build failed\", \ + \"context\": \"ci/jenkins\" }' \ + -u $GH_AUTH_USERNAME:$GH_AUTH_PASSWORD" + } } } diff --git a/Makefile b/Makefile index b7c96a2..68e3c1c 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.7 +# Version: 1.1.8 BUILD_NUMBER ?= local BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) @@ -16,8 +16,7 @@ DOCKER_COMPOSE := BUILD_NUMBER=$(BUILD_NUMBER) \ clean: docker rmi ci/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) - docker rmi gcr.io/csh-gcdm-test/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) - docker rmi gcr.io/csh-staging/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) + docker rmi gcr.io/cr-test2/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) rm -f app.js rm -rf app/js rm -rf test/unit/js @@ -38,8 +37,7 @@ test_acceptance_pre_run: @[ ! -f test/acceptance/scripts/pre-run ] && echo "clsi has no pre acceptance tests task" || $(DOCKER_COMPOSE) run --rm test_acceptance test/acceptance/scripts/pre-run build: docker build --pull --tag ci/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) \ - --tag gcr.io/csh-gcdm-test/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) \ - --tag gcr.io/csh-staging/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) \ + --tag gcr.io/cr-test2/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) \ . publish: diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index 9f3d54d..e8850b9 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.7 +# Version: 1.1.8 version: "2" diff --git a/docker-compose.yml b/docker-compose.yml index 4c1cfc7..f47658d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.7 +# Version: 1.1.8 version: "2" diff --git a/package.json b/package.json index ab34cda..bafda19 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "compile:acceptance_tests": "[ ! -e test/acceptance/coffee ] && echo 'No acceptance tests to compile' || coffee -o test/acceptance/js -c test/acceptance/coffee", "compile:all": "npm run compile:app && npm run compile:unit_tests && npm run compile:acceptance_tests && npm run compile:smoke_tests", "nodemon": "nodemon --config nodemon.json", - "compile:smoke_tests": "[ ! -e test/smoke/coffee] && echo 'No smoke tests to compile' || coffee -o test/smoke/js -c test/smoke/coffee" + "compile:smoke_tests": "[ ! -e test/smoke/coffee ] && echo 'No smoke tests to compile' || coffee -o test/smoke/js -c test/smoke/coffee" }, "author": "James Allen ", "dependencies": { From 3e26efe06fd59bf46c0089872174d0734c1944ec Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 17 Jul 2018 12:50:33 +0100 Subject: [PATCH 082/118] add PRAGMA journal_mode=WAL; --- app/coffee/db.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/coffee/db.coffee b/app/coffee/db.coffee index f32cdf7..c21da6d 100644 --- a/app/coffee/db.coffee +++ b/app/coffee/db.coffee @@ -14,6 +14,8 @@ sequelize = new Sequelize( options ) +sequelize.query("PRAGMA journal_mode=WAL;") + module.exports = UrlCache: sequelize.define("UrlCache", { url: Sequelize.STRING From 2b6032b24960de525e2517064ccdf31fce4eb322 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 17 Jul 2018 12:53:07 +0100 Subject: [PATCH 083/118] only set wal for sqlite --- app/coffee/db.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/coffee/db.coffee b/app/coffee/db.coffee index c21da6d..c8866e0 100644 --- a/app/coffee/db.coffee +++ b/app/coffee/db.coffee @@ -14,7 +14,8 @@ sequelize = new Sequelize( options ) -sequelize.query("PRAGMA journal_mode=WAL;") +if Settings.mysql.clsi.dialect == "sqlite" + sequelize.query("PRAGMA journal_mode=WAL;") module.exports = UrlCache: sequelize.define("UrlCache", { From 465dc31e75fde24df2752cdbe91f501c43f0f539 Mon Sep 17 00:00:00 2001 From: Christopher Hoskin Date: Wed, 18 Jul 2018 11:32:41 +0100 Subject: [PATCH 084/118] Push images to overleaf-ops --- Jenkinsfile | 8 ++++---- Makefile | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 6db5e29..d82360d 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -51,11 +51,11 @@ pipeline { stage('Package and publish build') { steps { - withCredentials([file(credentialsId: 'gcr.io_cr-test2', variable: 'DOCKER_REPO_KEY_PATH')]) { - sh 'docker login -u _json_key --password-stdin https://gcr.io/cr-test2 < ${DOCKER_REPO_KEY_PATH}' + withCredentials([file(credentialsId: 'gcr.io_overleaf-ops', variable: 'DOCKER_REPO_KEY_PATH')]) { + sh 'docker login -u _json_key --password-stdin https://gcr.io/overleaf-ops < ${DOCKER_REPO_KEY_PATH}' } - sh 'DOCKER_REPO=gcr.io/cr-test2 make publish' - sh 'docker logout https://gcr.io/cr-test2' + sh 'DOCKER_REPO=gcr.io/overleaf-ops make publish' + sh 'docker logout https://gcr.io/overleaf-ops' } } diff --git a/Makefile b/Makefile index 68e3c1c..6daee1b 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ DOCKER_COMPOSE := BUILD_NUMBER=$(BUILD_NUMBER) \ clean: docker rmi ci/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) - docker rmi gcr.io/cr-test2/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) + docker rmi gcr.io/overleaf-ops/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) rm -f app.js rm -rf app/js rm -rf test/unit/js @@ -37,7 +37,7 @@ test_acceptance_pre_run: @[ ! -f test/acceptance/scripts/pre-run ] && echo "clsi has no pre acceptance tests task" || $(DOCKER_COMPOSE) run --rm test_acceptance test/acceptance/scripts/pre-run build: docker build --pull --tag ci/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) \ - --tag gcr.io/cr-test2/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) \ + --tag gcr.io/overleaf-ops/$(PROJECT_NAME):$(BRANCH_NAME)-$(BUILD_NUMBER) \ . publish: From 67d34fdaf05804bc718dd7ac42e22c43272067f0 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Thu, 26 Jul 2018 16:12:26 +0100 Subject: [PATCH 085/118] dd wal logging --- app/coffee/db.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/app/coffee/db.coffee b/app/coffee/db.coffee index c8866e0..c0bb1ca 100644 --- a/app/coffee/db.coffee +++ b/app/coffee/db.coffee @@ -15,6 +15,7 @@ sequelize = new Sequelize( ) if Settings.mysql.clsi.dialect == "sqlite" + logger.log "running PRAGMA journal_mode=WAL;" sequelize.query("PRAGMA journal_mode=WAL;") module.exports = From 0eeee4284d35f59b67604a5cd3342ff5478607fc Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Mon, 30 Jul 2018 11:25:28 +0100 Subject: [PATCH 086/118] bump retried and package versions --- config/settings.defaults.coffee | 5 +++++ package.json | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/config/settings.defaults.coffee b/config/settings.defaults.coffee index 1f3fe8b..7a2e7cf 100644 --- a/config/settings.defaults.coffee +++ b/config/settings.defaults.coffee @@ -10,6 +10,11 @@ module.exports = password: null dialect: "sqlite" storage: process.env["SQLITE_PATH"] or Path.resolve(__dirname + "/../db.sqlite") + pool: + max: 1 + min: 0 + retry: + max: 10 path: compilesDir: Path.resolve(__dirname + "/../compiles") diff --git a/package.json b/package.json index bafda19..c34b93c 100644 --- a/package.json +++ b/package.json @@ -35,10 +35,10 @@ "mkdirp": "0.3.5", "mysql": "2.6.2", "request": "^2.21.0", - "sequelize": "^2.1.3", + "sequelize": "4.38.0", "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", "smoke-test-sharelatex": "git+https://github.com/sharelatex/smoke-test-sharelatex.git#v0.2.0", - "sqlite3": "^3.1.13", + "sqlite3": "4.0.2", "underscore": "^1.8.2", "v8-profiler": "^5.2.4", "wrench": "~1.5.4" From f802717cb5802a3ff9d4d643695201fb5db332aa Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Mon, 30 Jul 2018 14:04:33 +0100 Subject: [PATCH 087/118] remove password from clsi for sql sequalise fails when it is set to null --- .gitignore | 2 + app/coffee/db.coffee | 6 + config/settings.defaults.coffee | 3 +- package-lock.json | 2502 +++++++++++++++++++++++++++++-- package.json | 4 +- 5 files changed, 2407 insertions(+), 110 deletions(-) diff --git a/.gitignore b/.gitignore index 21c1eca..7fb78ee 100644 --- a/.gitignore +++ b/.gitignore @@ -12,5 +12,7 @@ app.js cache .vagrant db.sqlite +db.sqlite-wal +db.sqlite-shm config/* npm-debug.log diff --git a/app/coffee/db.coffee b/app/coffee/db.coffee index c0bb1ca..a24377e 100644 --- a/app/coffee/db.coffee +++ b/app/coffee/db.coffee @@ -42,4 +42,10 @@ module.exports = sync: () -> logger.log dbPath:Settings.mysql.clsi.storage, "syncing db schema" sequelize.sync() + .then(-> + logger.log "db sync complete" + ).catch((err)-> + console.log err, "error syncing" + ) + diff --git a/config/settings.defaults.coffee b/config/settings.defaults.coffee index 7a2e7cf..a2bf593 100644 --- a/config/settings.defaults.coffee +++ b/config/settings.defaults.coffee @@ -7,12 +7,11 @@ module.exports = clsi: database: "clsi" username: "clsi" - password: null dialect: "sqlite" storage: process.env["SQLITE_PATH"] or Path.resolve(__dirname + "/../db.sqlite") pool: max: 1 - min: 0 + min: 1 retry: max: 10 diff --git a/package-lock.json b/package-lock.json index d00b345..d58c1d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,11 +4,157 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@types/geojson": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-1.0.6.tgz", + "integrity": "sha512-Xqg/lIZMrUd0VRmSRbCAewtwGZiAk3mEUDvV4op1tGl+LvyPcb/MIOSxTl9z+9+J+R4/vpjiCAT4xeKzH9ji1w==" + }, + "@types/node": { + "version": "10.5.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.5.4.tgz", + "integrity": "sha512-8TqvB0ReZWwtcd3LXq3YSrBoLyXFgBX/sBZfGye9+YS8zH7/g+i6QRIuiDmwBoTzcQ/pk89nZYTYU4c5akKkzw==" + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha1-+PLIh60Qv2f2NPAFtph/7TF5qsg=" + }, + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "requires": { + "co": "4.6.0", + "fast-deep-equal": "1.1.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-0.2.0.tgz", + "integrity": "sha1-NZq0sV3NZLptdHNLcsNjYKmvLBk=", + "dev": true + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha1-aALmJk79GMeQobDVF/DyYnvyyUo=" + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.3.6" + } + }, + "argparse": { + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz", + "integrity": "sha1-z9AeD7uj1srtBJ+9dY1A9lGW9Xw=", + "dev": true, + "requires": { + "underscore": "1.7.0", + "underscore.string": "2.4.0" + }, + "dependencies": { + "underscore": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", + "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=", + "dev": true + }, + "underscore.string": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz", + "integrity": "sha1-jN2PusTi0uoefi6Al8QvRCKA+Fs=", + "dev": true + } + } + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "assertion-error": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.0.tgz", + "integrity": "sha1-x/hUOP3UZrx8oWq5DIFRN5el0js=", + "dev": true + }, + "async": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.9.tgz", + "integrity": "sha1-32MGD789Myhqdqr21Vophtn/hhk=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", + "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==" + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "optional": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "bignumber.js": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.0.7.tgz", + "integrity": "sha1-husHB89qURCQnSPm6nQ0wU9QDxw=" + }, + "bl": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", + "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", + "requires": { + "readable-stream": "2.3.6", + "safe-buffer": "5.1.2" + } + }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "requires": { + "inherits": "2.0.3" + } + }, + "bluebird": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" }, "body-parser": { "version": "1.18.2", @@ -156,21 +302,325 @@ } } }, + "boom": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "requires": { + "hoek": "2.16.3" + } + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", - "optional": true, "requires": { "balanced-match": "1.0.0", "concat-map": "0.0.1" } }, + "browser-stdout": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", + "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", + "dev": true + }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "requires": { + "buffer-alloc-unsafe": "1.1.0", + "buffer-fill": "1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" + }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" + }, + "bunyan": { + "version": "0.22.3", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-0.22.3.tgz", + "integrity": "sha1-ehncG0yMZF90AkGnQPIkUUfGfsI=", + "dev": true, + "requires": { + "mv": "2.1.1" + } + }, + "buster-core": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/buster-core/-/buster-core-0.6.4.tgz", + "integrity": "sha1-J79rrWdCROpyDzEdkAoMoct4YFA=", + "dev": true + }, + "buster-format": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/buster-format/-/buster-format-0.5.6.tgz", + "integrity": "sha1-K4bDIuz14bCubm55Bev884fSq5U=", + "dev": true, + "requires": { + "buster-core": "0.6.4" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "chai": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-1.8.1.tgz", + "integrity": "sha1-zHeGbV5+vKK9dRRLHtw3Coh4X3I=", + "dev": true, + "requires": { + "assertion-error": "1.0.0", + "deep-eql": "0.1.3" + } + }, + "chalk": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.3.0.tgz", + "integrity": "sha1-HJhDdzfxGZ68wdTEj9Qbn5yOjyM=", + "dev": true, + "requires": { + "ansi-styles": "0.2.0", + "has-color": "0.1.7" + } + }, + "chownr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", + "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=" + }, + "cls-bluebird": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cls-bluebird/-/cls-bluebird-2.1.0.tgz", + "integrity": "sha1-N+8eCAqP+1XC9BZPU28ZGeeWiu4=", + "requires": { + "is-bluebird": "1.0.2", + "shimmer": "1.2.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "coffee-script": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz", + "integrity": "sha1-gIs5bhEPU9AhoZpO8fZb4OjjX6M=" + }, + "colors": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz", + "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=", + "dev": true + }, + "combined-stream": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "requires": { + "delayed-stream": "1.0.0" + } + }, + "commander": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz", + "integrity": "sha1-0bhvkB+LZL2UG96tr5JFMDk76Sg=" + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "optional": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", + "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=", + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.0.6", + "typedarray": "0.0.6" + }, + "dependencies": { + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + }, + "readable-stream": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "0.10.31", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cryptiles": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "requires": { + "boom": "2.10.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "1.0.0" + } + }, + "dateformat": { + "version": "1.0.2-1.2.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz", + "integrity": "sha1-sCIMAt6YYXQztyhRz0fePfLNvuk=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "requires": { + "ms": "2.0.0" + } + }, + "deep-eql": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", + "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", + "dev": true, + "requires": { + "type-detect": "0.1.1" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" + }, + "diff": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.7.tgz", + "integrity": "sha1-JLuwAcSn1VIhaefKvbLCgU7ZHPQ=" + }, + "docker-modem": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-1.0.6.tgz", + "integrity": "sha512-kDwWa5QaiVMB8Orbb7nXdGdwEZHKfEm7iPwglXe1KorImMpmGNlhC7A5LG0p8rrCcz1J4kJhq/o63lFjDdj8rQ==", + "requires": { + "debug": "3.1.0", + "JSONStream": "1.3.2", + "readable-stream": "1.0.34", + "split-ca": "1.0.1" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "dockerode": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-2.5.5.tgz", + "integrity": "sha512-H3HX18xKmy51wqpPHvGDwPOotJMy9l/AWfiaVu4imrgBGr384rINEB2FwTwoYU++krkZjseVYyiVK8CnRz2tkw==", + "requires": { + "concat-stream": "1.5.2", + "docker-modem": "1.0.6", + "tar-fs": "1.12.0" + } + }, + "dottie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.0.tgz", + "integrity": "sha1-2hkZgci41xPKARXViYzzl8Lw3dA=" }, "dtrace-provider": { "version": "0.6.0", @@ -178,9 +628,51 @@ "integrity": "sha1-CweNVReTfYcxAUUtkUZzdVe3XlE=", "optional": true, "requires": { - "nan": "2.8.0" + "nan": "2.10.0" } }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "optional": true, + "requires": { + "jsbn": "0.1.1", + "safer-buffer": "2.1.2" + } + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "requires": { + "once": "1.4.0" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "esprima": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz", + "integrity": "sha1-n1V+CPw7TSbs6d00+Pv0drYlha0=", + "dev": true + }, + "eventemitter2": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", + "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=", + "dev": true + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, "express": { "version": "4.16.2", "resolved": "https://registry.npmjs.org/express/-/express-4.16.2.tgz", @@ -499,47 +991,609 @@ } } }, - "glob": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", - "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", - "optional": true, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "findup-sync": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz", + "integrity": "sha1-fz56l7gjksZTvwZYm9hRkOk8NoM=", + "dev": true, "requires": { + "glob": "3.2.11", + "lodash": "2.4.2" + }, + "dependencies": { + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "minimatch": "0.3.0" + } + }, + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=", + "dev": true + }, + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", + "dev": true, + "requires": { + "lru-cache": "2.7.3", + "sigmund": "1.0.1" + } + } + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", + "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.6", + "mime-types": "2.1.19" + } + }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "fs-extra": { + "version": "0.16.5", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.16.5.tgz", + "integrity": "sha1-GtZh+myGyWCM0bSe/G/Og0k5p1A=", + "requires": { + "graceful-fs": "3.0.11", + "jsonfile": "2.4.0", + "rimraf": "2.6.2" + } + }, + "fs-minipass": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", + "integrity": "sha1-BsJ3IYRU7CiN93raVKA7hwKqy50=", + "requires": { + "minipass": "2.3.3" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fstream": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "requires": { + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.6.2" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + } + } + }, + "fstream-ignore": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", + "integrity": "sha1-nDHa40dnAY/h0kmyTa2mfQktoQU=", + "requires": { + "fstream": "1.0.11", + "inherits": "2.0.3", + "minimatch": "3.0.4" + } + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.3" + } + }, + "generic-pool": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.4.2.tgz", + "integrity": "sha512-H7cUpwCQSiJmAHM4c/aFu6fUfrhWXW1ncyh8ftxEPMu6AiYkHw9K8br720TGPZJbk5eOH2bynjZD1yPvdDAmag==" + }, + "getobject": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz", + "integrity": "sha1-BHpEl4n6Fg0Bj1SG7ZEyC27HiFw=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "1.0.0" + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "requires": { + "fs.realpath": "1.0.0", "inflight": "1.0.6", "inherits": "2.0.3", "minimatch": "3.0.4", "once": "1.4.0", "path-is-absolute": "1.0.1" + } + }, + "graceful-fs": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz", + "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=", + "requires": { + "natives": "1.1.4" + } + }, + "growl": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.7.0.tgz", + "integrity": "sha1-3i1mE20ALhErpw8/EMMc98NQsto=" + }, + "grunt": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz", + "integrity": "sha1-VpN81RlDJK3/bSB2MYMqnWuk5/A=", + "dev": true, + "requires": { + "async": "0.1.22", + "coffee-script": "1.3.3", + "colors": "0.6.2", + "dateformat": "1.0.2-1.2.3", + "eventemitter2": "0.4.14", + "exit": "0.1.2", + "findup-sync": "0.1.3", + "getobject": "0.1.0", + "glob": "3.1.21", + "grunt-legacy-log": "0.1.3", + "grunt-legacy-util": "0.2.0", + "hooker": "0.2.3", + "iconv-lite": "0.2.11", + "js-yaml": "2.0.5", + "lodash": "0.9.2", + "minimatch": "0.2.14", + "nopt": "1.0.10", + "rimraf": "2.2.8", + "underscore.string": "2.2.1", + "which": "1.0.9" }, "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "optional": true + "async": { + "version": "0.1.22", + "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz", + "integrity": "sha1-D8GqoIig4+8Ovi2IMbqw3PiEUGE=", + "dev": true }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "optional": true, + "coffee-script": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz", + "integrity": "sha1-FQ1rTLUiiUNp7+1qIQHCC8f0pPQ=", + "dev": true + }, + "glob": { + "version": "3.1.21", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", + "integrity": "sha1-0p4KBV3qUTj00H7UDomC6DwgZs0=", + "dev": true, "requires": { - "wrappy": "1.0.2" + "graceful-fs": "1.2.3", + "inherits": "1.0.2", + "minimatch": "0.2.14" } }, - "wrappy": { + "graceful-fs": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz", + "integrity": "sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q=", + "dev": true + }, + "iconv-lite": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz", + "integrity": "sha1-HOYKOleGSiktEyH/RgnKS7llrcg=", + "dev": true + }, + "inherits": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "optional": true + "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz", + "integrity": "sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js=", + "dev": true + }, + "lodash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz", + "integrity": "sha1-jzSZxSRdNG1oLlsNO0B2fgnxqSw=", + "dev": true + }, + "minimatch": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", + "dev": true, + "requires": { + "lru-cache": "2.7.3", + "sigmund": "1.0.1" + } + }, + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", + "dev": true, + "requires": { + "abbrev": "1.1.1" + } + }, + "rimraf": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", + "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=", + "dev": true } } }, + "grunt-bunyan": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/grunt-bunyan/-/grunt-bunyan-0.5.0.tgz", + "integrity": "sha1-aCnXbgGZQ9owQTk2MaNuKsgpsWw=", + "dev": true, + "requires": { + "lodash": "2.4.2" + }, + "dependencies": { + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=", + "dev": true + } + } + }, + "grunt-contrib-clean": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-0.5.0.tgz", + "integrity": "sha1-9T397ghJsce0Dp67umn0jExgecU=", + "dev": true, + "requires": { + "rimraf": "2.2.8" + }, + "dependencies": { + "rimraf": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", + "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=", + "dev": true + } + } + }, + "grunt-contrib-coffee": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/grunt-contrib-coffee/-/grunt-contrib-coffee-0.7.0.tgz", + "integrity": "sha1-ixIme3TnM4sfKcW4txj7n4mYLxM=", + "dev": true, + "requires": { + "coffee-script": "1.6.3" + }, + "dependencies": { + "coffee-script": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.3.tgz", + "integrity": "sha1-Y1XTLPGwTN/2tITl5xF4Ky8MOb4=", + "dev": true + } + } + }, + "grunt-execute": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/grunt-execute/-/grunt-execute-0.1.5.tgz", + "integrity": "sha1-yX64lDYS/vu3L749Mu+VIzxfouk=", + "dev": true + }, + "grunt-legacy-log": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.3.tgz", + "integrity": "sha1-7ClCboAwIa9ZAp+H0vnNczWgVTE=", + "dev": true, + "requires": { + "colors": "0.6.2", + "grunt-legacy-log-utils": "0.1.1", + "hooker": "0.2.3", + "lodash": "2.4.2", + "underscore.string": "2.3.3" + }, + "dependencies": { + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=", + "dev": true + }, + "underscore.string": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz", + "integrity": "sha1-ccCL9rQosRM/N+ePo6Icgvcymw0=", + "dev": true + } + } + }, + "grunt-legacy-log-utils": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-0.1.1.tgz", + "integrity": "sha1-wHBrndkGThFvNvI/5OawSGcsD34=", + "dev": true, + "requires": { + "colors": "0.6.2", + "lodash": "2.4.2", + "underscore.string": "2.3.3" + }, + "dependencies": { + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=", + "dev": true + }, + "underscore.string": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz", + "integrity": "sha1-ccCL9rQosRM/N+ePo6Icgvcymw0=", + "dev": true + } + } + }, + "grunt-legacy-util": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz", + "integrity": "sha1-kzJIhNv343qf98Am3/RR2UqeVUs=", + "dev": true, + "requires": { + "async": "0.1.22", + "exit": "0.1.2", + "getobject": "0.1.0", + "hooker": "0.2.3", + "lodash": "0.9.2", + "underscore.string": "2.2.1", + "which": "1.0.9" + }, + "dependencies": { + "async": { + "version": "0.1.22", + "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz", + "integrity": "sha1-D8GqoIig4+8Ovi2IMbqw3PiEUGE=", + "dev": true + }, + "lodash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz", + "integrity": "sha1-jzSZxSRdNG1oLlsNO0B2fgnxqSw=", + "dev": true + } + } + }, + "grunt-mkdir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/grunt-mkdir/-/grunt-mkdir-1.0.0.tgz", + "integrity": "sha1-c+GiasJKCFljY/TdlUsNMkheWOk=" + }, + "grunt-mocha-test": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.8.2.tgz", + "integrity": "sha1-emGEuYhg0Phb3qrWvqob199bvus=", + "dev": true, + "requires": { + "mocha": "1.14.0" + }, + "dependencies": { + "glob": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", + "integrity": "sha1-4xPusknHr/qlxHUoaw4RW1mDlGc=", + "dev": true, + "requires": { + "graceful-fs": "2.0.3", + "inherits": "2.0.3", + "minimatch": "0.2.14" + } + }, + "graceful-fs": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz", + "integrity": "sha1-fNLNsiiko/Nule+mzBQt59GhNtA=", + "dev": true + }, + "minimatch": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", + "dev": true, + "requires": { + "lru-cache": "2.7.3", + "sigmund": "1.0.1" + } + }, + "mocha": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-1.14.0.tgz", + "integrity": "sha1-cT223FAAGRqdA1gZXQkIeQ7LYVc=", + "dev": true, + "requires": { + "commander": "2.0.0", + "debug": "2.6.9", + "diff": "1.0.7", + "glob": "3.2.3", + "growl": "1.7.0", + "jade": "0.26.3", + "mkdirp": "0.3.5" + } + } + } + }, + "grunt-shell": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/grunt-shell/-/grunt-shell-0.6.4.tgz", + "integrity": "sha1-5KbRuSkSd2/ZOimcX2zGTpUlNlw=", + "dev": true, + "requires": { + "chalk": "0.3.0" + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "requires": { + "ajv": "5.5.2", + "har-schema": "2.0.0" + } + }, + "has-color": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz", + "integrity": "sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8=", + "dev": true + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "hawk": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, + "heapdump": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/heapdump/-/heapdump-0.3.9.tgz", + "integrity": "sha1-A8dOsN9dZ74Jgug0KbqcnSs7f3g=" + }, + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" + }, + "hooker": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", + "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=", + "dev": true + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "1.0.0", + "jsprim": "1.4.1", + "sshpk": "1.14.2" + } + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "requires": { + "safer-buffer": "2.1.2" + } + }, + "ignore-walk": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", + "integrity": "sha1-qD5i59JyrA47VRqqgoMaGbafgvg=", + "requires": { + "minimatch": "3.0.4" + } + }, + "inflection": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz", + "integrity": "sha1-ogCTVlbW9fa8TcdQLhrstwMihBY=" + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "optional": true, "requires": { "once": "1.4.0", "wrappy": "1.0.2" @@ -549,7 +1603,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "optional": true, "requires": { "wrappy": "1.0.2" } @@ -561,12 +1614,169 @@ } } }, - "load-balancer-agent-sharelatex": { - "version": "git+https://github.com/sharelatex/load-balancer-agent-sharelatex.git#241a128c1e9fdec384186061b27704c8891d98ef", + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha1-7uJfVtscnsYIXgwid4CD9Zar+Sc=" + }, + "is-bluebird": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-bluebird/-/is-bluebird-1.0.2.tgz", + "integrity": "sha1-CWQ5Bg9KpBGr7hkUOoTWpVNG1uI=" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "requires": { - "express": "4.16.2", - "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#405cf1350ca5ae5f7bb1e7091e28d5aa3aaaa72c", - "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#b4fb8404c5de571d029bf4c29e96a60b21206f94" + "number-is-nan": "1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jade": { + "version": "0.26.3", + "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", + "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=", + "requires": { + "commander": "0.6.1", + "mkdirp": "0.3.0" + }, + "dependencies": { + "commander": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", + "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=" + }, + "mkdirp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", + "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=" + } + } + }, + "js-yaml": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz", + "integrity": "sha1-olrmUJmZ6X3yeMZxnaEb0Gh3Q6g=", + "dev": true, + "requires": { + "argparse": "0.1.16", + "esprima": "1.0.4" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "requires": { + "jsonify": "0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "requires": { + "graceful-fs": "4.1.11" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "optional": true + } + } + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" + }, + "JSONStream": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.2.tgz", + "integrity": "sha1-wQI3G27Dp887hHygDCC7D85Mbeo=", + "requires": { + "jsonparse": "1.3.1", + "through": "2.3.8" + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "lockfile": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lockfile/-/lockfile-1.0.4.tgz", + "integrity": "sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA==", + "requires": { + "signal-exit": "3.0.2" + } + }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + }, + "logger-sharelatex": { + "version": "git+https://github.com/sharelatex/logger-sharelatex.git#9ee7b52eb2bbd8fcbb1e2c708587c1e93fd4c733", + "requires": { + "bunyan": "1.5.1", + "coffee-script": "1.4.0", + "raven": "1.2.1" }, "dependencies": { "bunyan": { @@ -576,72 +1786,186 @@ "requires": { "dtrace-provider": "0.6.0", "mv": "2.1.1", - "safe-json-stringify": "1.0.4" + "safe-json-stringify": "1.2.0" } }, "coffee-script": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.4.0.tgz", "integrity": "sha1-XjvIqsJsAajie/EHcixWVfWtfTY=" - }, - "cookie": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz", - "integrity": "sha1-kOtGndzpBchm3mh+/EMTHYgB+dA=" - }, - "logger-sharelatex": { - "version": "git+https://github.com/sharelatex/logger-sharelatex.git#405cf1350ca5ae5f7bb1e7091e28d5aa3aaaa72c", - "requires": { - "bunyan": "1.5.1", - "coffee-script": "1.4.0", - "raven": "0.8.1" - } - }, - "lsmod": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-0.0.3.tgz", - "integrity": "sha1-F+E9ThrpF1DqVlNUjNiecUetAkQ=" - }, - "raven": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/raven/-/raven-0.8.1.tgz", - "integrity": "sha1-UVk7tlnHcnjc00gitlq+d7dRuvU=", - "requires": { - "cookie": "0.1.0", - "lsmod": "0.0.3", - "node-uuid": "1.4.8", - "stack-trace": "0.0.7" - } - }, - "settings-sharelatex": { - "version": "git+https://github.com/sharelatex/settings-sharelatex.git#b4fb8404c5de571d029bf4c29e96a60b21206f94", - "requires": { - "coffee-script": "1.6.0" - }, - "dependencies": { - "coffee-script": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.6.0.tgz", - "integrity": "sha1-gIs5bhEPU9AhoZpO8fZb4OjjX6M=" - } - } - }, - "stack-trace": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.7.tgz", - "integrity": "sha1-xy4Il0T8Nln1CM3ONiGvVjTsD/8=" } } }, + "lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" + }, + "lsmod": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-1.0.0.tgz", + "integrity": "sha1-mgD3bco26yP6BTUK/htYXUKZ5ks=" + }, + "lynx": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/lynx/-/lynx-0.0.11.tgz", + "integrity": "sha1-LPoU5EP9LZKlm3efQVZ84cxpZaM=", + "requires": { + "mersenne": "0.0.4", + "statsd-parser": "0.0.4" + } + }, + "mersenne": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.4.tgz", + "integrity": "sha1-QB/ex+whzbngPNPTAhOY2iGycIU=" + }, + "metrics-sharelatex": { + "version": "git+https://github.com/sharelatex/metrics-sharelatex.git#e5356366b5b83997c8e1645b2e936af453381517", + "requires": { + "coffee-script": "1.6.0", + "lynx": "0.1.1", + "underscore": "1.6.0" + }, + "dependencies": { + "lynx": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/lynx/-/lynx-0.1.1.tgz", + "integrity": "sha1-Mxjc7xaQi4KG6Bisz9sxzXQkj50=", + "requires": { + "mersenne": "0.0.4", + "statsd-parser": "0.0.4" + } + }, + "underscore": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=" + } + } + }, + "mime-db": { + "version": "1.35.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.35.0.tgz", + "integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg==" + }, + "mime-types": { + "version": "2.1.19", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz", + "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==", + "requires": { + "mime-db": "1.35.0" + } + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", - "optional": true, "requires": { "brace-expansion": "1.1.11" } }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "minipass": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.3.tgz", + "integrity": "sha1-p9zIt7gz9dNodZzOVE3MtV9Q8jM=", + "requires": { + "safe-buffer": "5.1.2", + "yallist": "3.0.2" + } + }, + "minizlib": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.0.tgz", + "integrity": "sha1-EeE2WM5GvDpwomeqxYNZ0eDCnOs=", + "requires": { + "minipass": "2.3.3" + } + }, + "mkdirp": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=" + }, + "mocha": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.1.0.tgz", + "integrity": "sha512-0RVnjg1HJsXY2YFDoTNzcc1NKhYuXKRrBAG2gDygmJJA136Cs2QlRliZG1mA0ap7cuaT30mw16luAeln+4RiNA==", + "dev": true, + "requires": { + "browser-stdout": "1.3.0", + "commander": "2.11.0", + "debug": "3.1.0", + "diff": "3.3.1", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.3", + "he": "1.1.1", + "mkdirp": "0.5.1", + "supports-color": "4.4.0" + }, + "dependencies": { + "commander": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "dev": true + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "diff": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", + "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==", + "dev": true + }, + "growl": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", + "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + } + } + }, + "moment": { + "version": "2.22.2", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz", + "integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y=" + }, + "moment-timezone": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.21.tgz", + "integrity": "sha512-j96bAh4otsgj3lKydm3K7kdtA3iKf2m6MY2iSYCzCm5a1zmHo1g+aK3068dDEeocLZQIS9kU8bsdQHLqEvgW0A==", + "requires": { + "moment": "2.22.2" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, "mv": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", @@ -653,11 +1977,18 @@ "rimraf": "2.4.5" }, "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "optional": true + "glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "optional": true, + "requires": { + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } }, "mkdirp": { "version": "0.5.1", @@ -667,14 +1998,60 @@ "requires": { "minimist": "0.0.8" } + }, + "rimraf": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", + "optional": true, + "requires": { + "glob": "6.0.4" + } + } + } + }, + "mysql": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.6.2.tgz", + "integrity": "sha1-k3Gd0yT1fUHET7bX+GDPjmOFk7A=", + "requires": { + "bignumber.js": "2.0.7", + "readable-stream": "1.1.14", + "require-all": "1.0.0" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" } } }, "nan": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", - "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=", - "optional": true + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", + "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==" + }, + "natives": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.4.tgz", + "integrity": "sha512-Q29yeg9aFKwhLVdkTAejM/HvYG0Y1Am1+HUkFQGn5k2j8GS+v60TVmZh6nujpEAj/qql+wGUrlryO8bF+b1jEg==" }, "ncp": { "version": "2.0.0", @@ -682,31 +2059,944 @@ "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", "optional": true }, - "node-uuid": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", - "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=" + "needle": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.2.1.tgz", + "integrity": "sha1-teMlvTqujCZ4kC+ilvcpRV0dOn0=", + "requires": { + "debug": "2.6.9", + "iconv-lite": "0.4.23", + "sax": "1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz", + "integrity": "sha512-d1xFs+C/IPS8Id0qPTZ4bUT8wWryfR/OzzAFxweG+uLN85oPzyo2Iw6bVlLQ/JOdgNonXLCoRyqDzDWq4iw72A==", + "requires": { + "detect-libc": "1.0.3", + "mkdirp": "0.5.1", + "needle": "2.2.1", + "nopt": "4.0.1", + "npm-packlist": "1.1.11", + "npmlog": "4.1.2", + "rc": "1.2.8", + "rimraf": "2.6.2", + "semver": "5.5.0", + "tar": "4.4.4" + }, + "dependencies": { + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "requires": { + "glob": "7.1.2" + } + } + } + }, + "nopt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "requires": { + "abbrev": "1.1.1", + "osenv": "0.1.5" + } + }, + "npm-bundled": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.3.tgz", + "integrity": "sha1-fnFwPZc68zcKlZG6/jpjrKC+Iwg=" + }, + "npm-packlist": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.11.tgz", + "integrity": "sha1-hOjGg8vnhn00sdNX2JPOKeKKAt4=", + "requires": { + "ignore-walk": "3.0.1", + "npm-bundled": "1.0.3" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha1-CKfyqL9zRgR3mp76StXMcXq7lUs=", + "requires": { + "are-we-there-yet": "1.1.5", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1.0.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha1-hc36+uso6Gd/QW4odZK18/SepBA=", + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "optional": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, - "rimraf": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", - "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", - "optional": true, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "pump": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz", + "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==", "requires": { - "glob": "6.0.4" + "end-of-stream": "1.4.1", + "once": "1.4.0" } }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "raven": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/raven/-/raven-1.2.1.tgz", + "integrity": "sha1-lJwTTbAooZC3u/j3kKrlQbfAIL0=", + "requires": { + "cookie": "0.3.1", + "json-stringify-safe": "5.0.1", + "lsmod": "1.0.0", + "stack-trace": "0.0.9", + "uuid": "3.0.0" + }, + "dependencies": { + "uuid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz", + "integrity": "sha1-Zyj8BFnEUNeWqZwxg3VpvfZy1yg=" + } + } + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "requires": { + "deep-extend": "0.6.0", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "request": { + "version": "2.87.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", + "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", + "requires": { + "aws-sign2": "0.7.0", + "aws4": "1.7.0", + "caseless": "0.12.0", + "combined-stream": "1.0.6", + "extend": "3.0.2", + "forever-agent": "0.6.1", + "form-data": "2.3.2", + "har-validator": "5.0.3", + "http-signature": "1.2.0", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.19", + "oauth-sign": "0.8.2", + "performance-now": "2.1.0", + "qs": "6.5.2", + "safe-buffer": "5.1.2", + "tough-cookie": "2.3.4", + "tunnel-agent": "0.6.0", + "uuid": "3.3.2" + } + }, + "require-all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/require-all/-/require-all-1.0.0.tgz", + "integrity": "sha1-hINwjnzkxt+tmItQgPl4KbktIic=" + }, + "require-like": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", + "integrity": "sha1-rW8wwTvs15cBDEaK+ndcDAprR/o=", + "dev": true + }, + "retry-as-promised": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-2.3.2.tgz", + "integrity": "sha1-zZdO5P2bX+A8vzGHHuSCIcB3N7c=", + "requires": { + "bluebird": "3.5.1", + "debug": "2.6.9" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "requires": { + "glob": "7.1.2" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, "safe-json-stringify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.0.4.tgz", - "integrity": "sha1-gaCY9Efku8P/MxKiQ1IbwGDvWRE=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", + "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sandboxed-module": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-0.3.0.tgz", + "integrity": "sha1-8fvvvYCaT2kHO9B8rm/H2y6vX2o=", + "dev": true, + "requires": { + "require-like": "0.1.2", + "stack-trace": "0.0.6" + }, + "dependencies": { + "stack-trace": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.6.tgz", + "integrity": "sha1-HnGb1qJin/CcGJ4Xqe+QKpT8XbA=", + "dev": true + } + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha1-KBYjTiN4vdxOU1T6tcqold9xANk=" + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha1-3Eu8emyp2Rbe5dQ1FvAJK1j3uKs=" + }, + "sequelize": { + "version": "4.38.0", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-4.38.0.tgz", + "integrity": "sha512-ZCcV2HuzU+03xunWgVeyXnPa/RYY5D2U/WUNpq+xF8VmDTLnSDsHl+pEwmiWrpZD7KdBqDczCeTgjToYyVzYQg==", + "requires": { + "bluebird": "3.5.1", + "cls-bluebird": "2.1.0", + "debug": "3.1.0", + "depd": "1.1.2", + "dottie": "2.0.0", + "generic-pool": "3.4.2", + "inflection": "1.12.0", + "lodash": "4.17.10", + "moment": "2.22.2", + "moment-timezone": "0.5.21", + "retry-as-promised": "2.3.2", + "semver": "5.5.0", + "terraformer-wkt-parser": "1.2.0", + "toposort-class": "1.0.1", + "uuid": "3.3.2", + "validator": "10.4.0", + "wkx": "0.4.5" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "settings-sharelatex": { + "version": "git+https://github.com/sharelatex/settings-sharelatex.git#cbc5e41c1dbe6789721a14b3fdae05bf22546559", + "requires": { + "coffee-script": "1.6.0" + } + }, + "shimmer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.0.tgz", + "integrity": "sha512-xTCx2vohXC2EWWDqY/zb4+5Mu28D+HYNSOuFzsyRDRvI/e1ICb69afwaUwfjr+25ZXldbOLyp+iDUZHq8UnTag==" + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "sinon": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.7.3.tgz", + "integrity": "sha1-emnWnNApRYbHQyVO7/G1g6UJl/I=", + "dev": true, + "requires": { + "buster-format": "0.5.6" + } + }, + "smoke-test-sharelatex": { + "version": "git+https://github.com/sharelatex/smoke-test-sharelatex.git#bc3e93d18ccee219c0d99e8b02c984ccdd842e1c", + "requires": { + "mocha": "1.17.1" + }, + "dependencies": { + "glob": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", + "integrity": "sha1-4xPusknHr/qlxHUoaw4RW1mDlGc=", + "requires": { + "graceful-fs": "2.0.3", + "inherits": "2.0.3", + "minimatch": "0.2.14" + } + }, + "graceful-fs": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz", + "integrity": "sha1-fNLNsiiko/Nule+mzBQt59GhNtA=" + }, + "minimatch": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", + "requires": { + "lru-cache": "2.7.3", + "sigmund": "1.0.1" + } + }, + "mocha": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-1.17.1.tgz", + "integrity": "sha1-f3Zx1oUm0HS3uuZgyQmfh+DqHMs=", + "requires": { + "commander": "2.0.0", + "debug": "2.6.9", + "diff": "1.0.7", + "glob": "3.2.3", + "growl": "1.7.0", + "jade": "0.26.3", + "mkdirp": "0.3.5" + } + } + } + }, + "sntp": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "requires": { + "hoek": "2.16.3" + } + }, + "split-ca": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", + "integrity": "sha1-bIOv82kvphJW4M0ZfgXp3hV2kaY=" + }, + "sqlite3": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-4.0.2.tgz", + "integrity": "sha512-51ferIRwYOhzUEtogqOa/y9supADlAht98bF/gbIi6WkzRJX6Yioldxbzj1MV4yV+LgdKD/kkHwFTeFXOG4htA==", + "requires": { + "nan": "2.10.0", + "node-pre-gyp": "0.10.3", + "request": "2.87.0" + }, + "dependencies": { + "nan": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", + "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==" + }, + "request": { + "version": "2.87.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", + "integrity": "sha1-MvACNc0I1IK00NaNuTqCnA7VdW4=", + "requires": { + "aws-sign2": "0.7.0", + "aws4": "1.7.0", + "caseless": "0.12.0", + "combined-stream": "1.0.6", + "extend": "3.0.2", + "forever-agent": "0.6.1", + "form-data": "2.3.2", + "har-validator": "5.0.3", + "http-signature": "1.2.0", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.19", + "oauth-sign": "0.8.2", + "performance-now": "2.1.0", + "qs": "6.5.2", + "safe-buffer": "5.1.2", + "tough-cookie": "2.3.4", + "tunnel-agent": "0.6.0", + "uuid": "3.3.2" + } + } + } + }, + "sshpk": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", + "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.2", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.2", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "safer-buffer": "2.1.2", + "tweetnacl": "0.14.5" + } + }, + "stack-trace": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz", + "integrity": "sha1-qPbq7KkGdMMz58Q5U/J1tFFRBpU=" + }, + "statsd-parser": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz", + "integrity": "sha1-y9JDlTzELv/VSLXSI4jtaJ7GOb0=" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "stringstream": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz", + "integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, + "supports-color": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", + "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + }, + "tar": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.4.tgz", + "integrity": "sha512-mq9ixIYfNF9SK0IS/h2HKMu8Q2iaCuhDDsZhdEag/FHv8fOaYld4vN7ouMgcSSt5WKZzPs8atclTcJm36OTh4w==", + "requires": { + "chownr": "1.0.1", + "fs-minipass": "1.2.5", + "minipass": "2.3.3", + "minizlib": "1.1.0", + "mkdirp": "0.5.1", + "safe-buffer": "5.1.2", + "yallist": "3.0.2" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + } + } + }, + "tar-fs": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.12.0.tgz", + "integrity": "sha1-pqgFU9ilTHPeHQrg553ncDVgXh0=", + "requires": { + "mkdirp": "0.5.1", + "pump": "1.0.3", + "tar-stream": "1.6.1" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + } + } + }, + "tar-pack": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.1.tgz", + "integrity": "sha512-PPRybI9+jM5tjtCbN2cxmmRU7YmqT3Zv/UDy48tAh2XRkLa9bAORtSWLkVc13+GJF+cdTh1yEnHEk3cpTaL5Kg==", + "requires": { + "debug": "2.6.9", + "fstream": "1.0.11", + "fstream-ignore": "1.0.5", + "once": "1.4.0", + "readable-stream": "2.3.6", + "rimraf": "2.6.2", + "tar": "2.2.1", + "uid-number": "0.0.6" + }, + "dependencies": { + "tar": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "requires": { + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" + } + } + } + }, + "tar-stream": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.1.tgz", + "integrity": "sha512-IFLM5wp3QrJODQFPm6/to3LJZrONdBY/otxcvDIQzu217zKye6yVR3hhi9lAjrC2Z+m/j5oDxMPb1qcd8cIvpA==", + "requires": { + "bl": "1.2.2", + "buffer-alloc": "1.2.0", + "end-of-stream": "1.4.1", + "fs-constants": "1.0.0", + "readable-stream": "2.3.6", + "to-buffer": "1.1.1", + "xtend": "4.0.1" + } + }, + "terraformer": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/terraformer/-/terraformer-1.0.9.tgz", + "integrity": "sha512-YlmQ1fsMWTkKGDGibCRWgmLzrpDRUr63Q025LJ/taYQ6j1Yb8q9McKF7NBi6ACAyUXO6F/bl9w6v4MY307y5Ag==", + "requires": { + "@types/geojson": "1.0.6" + } + }, + "terraformer-wkt-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/terraformer-wkt-parser/-/terraformer-wkt-parser-1.2.0.tgz", + "integrity": "sha512-QU3iA54St5lF8Za1jg1oj4NYc8sn5tCZ08aNSWDeGzrsaV48eZk1iAVWasxhNspYBoCqdHuoot1pUTUrE1AJ4w==", + "requires": { + "@types/geojson": "1.0.6", + "terraformer": "1.0.9" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "timekeeper": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/timekeeper/-/timekeeper-0.0.4.tgz", + "integrity": "sha1-kNt58X2Ni1NiFUOJSSuXJ2LP0nY=", + "dev": true + }, + "to-buffer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==" + }, + "toposort-class": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", + "integrity": "sha1-f/0feMi+KMO6Rc1OGj9e4ZO9mYg=" + }, + "tough-cookie": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", + "integrity": "sha1-7GDO44rGdQY//JelwYlwV47oNlU=", + "requires": { + "punycode": "1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "optional": true + }, + "type-detect": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", + "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=", + "dev": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "uid-number": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", + "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=" + }, + "underscore": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", + "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" + }, + "underscore.string": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz", + "integrity": "sha1-18D6KvXVoaZ/QlPa7pgTLnM/Dxk=", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + }, + "v8-profiler": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/v8-profiler/-/v8-profiler-5.7.0.tgz", + "integrity": "sha1-6DgcvrtbX9DKjSsJ9qAYGhWNs00=", + "requires": { + "nan": "2.10.0", + "node-pre-gyp": "0.6.39" + }, + "dependencies": { + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + } + }, + "assert-plus": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=" + }, + "aws-sign2": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=" + }, + "form-data": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.6", + "mime-types": "2.1.19" + } + }, + "har-schema": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", + "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=" + }, + "har-validator": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", + "requires": { + "ajv": "4.11.8", + "har-schema": "1.0.5" + } + }, + "http-signature": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.1", + "sshpk": "1.14.2" + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "node-pre-gyp": { + "version": "0.6.39", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz", + "integrity": "sha512-OsJV74qxnvz/AMGgcfZoDaeDXKD3oY3QVIbBmwszTFkRisTSXbMQyn4UWzUMOtA5SVhrBZOTp0wcoSBgfMfMmQ==", + "requires": { + "detect-libc": "1.0.3", + "hawk": "3.1.3", + "mkdirp": "0.5.1", + "nopt": "4.0.1", + "npmlog": "4.1.2", + "rc": "1.2.8", + "request": "2.81.0", + "rimraf": "2.6.2", + "semver": "5.5.0", + "tar": "2.2.1", + "tar-pack": "3.4.1" + } + }, + "performance-now": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=" + }, + "qs": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=" + }, + "request": { + "version": "2.81.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.7.0", + "caseless": "0.12.0", + "combined-stream": "1.0.6", + "extend": "3.0.2", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "4.2.1", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.19", + "oauth-sign": "0.8.2", + "performance-now": "0.2.0", + "qs": "6.4.0", + "safe-buffer": "5.1.2", + "stringstream": "0.0.6", + "tough-cookie": "2.3.4", + "tunnel-agent": "0.6.0", + "uuid": "3.3.2" + } + }, + "tar": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "requires": { + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" + } + } + } + }, + "validator": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-10.4.0.tgz", + "integrity": "sha512-Q/wBy3LB1uOyssgNlXSRmaf22NxjvDNZM2MtIQ4jaEOAB61xsh1TQxsq1CgzUMBV1lDrVMogIh8GjG1DYW0zLg==" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "1.3.0" + } + }, + "which": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz", + "integrity": "sha1-RgwdoPgQED0DIam2M6+eV15kSG8=", + "dev": true + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "requires": { + "string-width": "1.0.2" + } + }, + "wkx": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.4.5.tgz", + "integrity": "sha512-01dloEcJZAJabLO5XdcRgqdKpmnxS0zIT02LhkdWOZX2Zs2tPM6hlZ4XG9tWaWur1Qd1OO4kJxUbe2+5BofvnA==", + "requires": { + "@types/node": "10.5.4" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "wrench": { + "version": "1.5.9", + "resolved": "https://registry.npmjs.org/wrench/-/wrench-1.5.9.tgz", + "integrity": "sha1-QRaRxjqbJTGxcAJnJ5veyiOyFCo=" + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + }, + "yallist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", + "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=" } } } diff --git a/package.json b/package.json index c34b93c..f2183ef 100644 --- a/package.json +++ b/package.json @@ -35,10 +35,10 @@ "mkdirp": "0.3.5", "mysql": "2.6.2", "request": "^2.21.0", - "sequelize": "4.38.0", + "sequelize": "^4.38.0", "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", "smoke-test-sharelatex": "git+https://github.com/sharelatex/smoke-test-sharelatex.git#v0.2.0", - "sqlite3": "4.0.2", + "sqlite3": "^4.0.2", "underscore": "^1.8.2", "v8-profiler": "^5.2.4", "wrench": "~1.5.4" From c490479a1a353e86f698fa75e1b0ee1cf7baaaef Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Mon, 30 Jul 2018 15:11:41 +0100 Subject: [PATCH 088/118] remove some console.logs --- app.coffee | 1 - app/coffee/ResourceWriter.coffee | 2 -- app/coffee/UrlFetcher.coffee | 1 - 3 files changed, 4 deletions(-) diff --git a/app.coffee b/app.coffee index 7855e09..8b5c779 100644 --- a/app.coffee +++ b/app.coffee @@ -133,7 +133,6 @@ resCacher = if Settings.smokeTest do runSmokeTest = -> logger.log("running smoke tests") - console.log(__dirname, __filename) smokeTest.run(require.resolve(__dirname + "/test/smoke/js/SmokeTests.js"))({}, resCacher) setTimeout(runSmokeTest, 30 * 1000) diff --git a/app/coffee/ResourceWriter.coffee b/app/coffee/ResourceWriter.coffee index 90ada04..0c9f718 100644 --- a/app/coffee/ResourceWriter.coffee +++ b/app/coffee/ResourceWriter.coffee @@ -121,11 +121,9 @@ module.exports = ResourceWriter = callback() #try and continue compiling even if http resource can not be downloaded at this time else process = require("process") - console.log "writing file out", path, process.getuid() fs.writeFile path, resource.content, callback try result = fs.lstatSync(path) - console.log "path stats", result catch e checkPath: (basePath, resourcePath, callback) -> diff --git a/app/coffee/UrlFetcher.coffee b/app/coffee/UrlFetcher.coffee index 58645f0..da10859 100644 --- a/app/coffee/UrlFetcher.coffee +++ b/app/coffee/UrlFetcher.coffee @@ -36,7 +36,6 @@ module.exports = UrlFetcher = logger.log url:url, filePath: filePath, "finished downloading file into cache" urlStream.on "response", (res) -> - console.log if res.statusCode >= 200 and res.statusCode < 300 fileStream = fs.createWriteStream(filePath) From 94a52333f7c7602ce2a1e94bf0667d80eefd8f1a Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Mon, 30 Jul 2018 15:16:06 +0100 Subject: [PATCH 089/118] add sync= off and read_uncommited=true to improve perf --- app/coffee/db.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/coffee/db.coffee b/app/coffee/db.coffee index a24377e..764edeb 100644 --- a/app/coffee/db.coffee +++ b/app/coffee/db.coffee @@ -17,6 +17,8 @@ sequelize = new Sequelize( if Settings.mysql.clsi.dialect == "sqlite" logger.log "running PRAGMA journal_mode=WAL;" sequelize.query("PRAGMA journal_mode=WAL;") + sequelize.query("PRAGMA synchronous=OFF;") + sequelize.query("PRAGMA read_uncommitted = true;") module.exports = UrlCache: sequelize.define("UrlCache", { From 92e124063563b863723dba314d97c31d04984bb5 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Mon, 30 Jul 2018 15:18:25 +0100 Subject: [PATCH 090/118] added some debugging --- app/coffee/UrlCache.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/coffee/UrlCache.coffee b/app/coffee/UrlCache.coffee index b72b78c..06e6012 100644 --- a/app/coffee/UrlCache.coffee +++ b/app/coffee/UrlCache.coffee @@ -94,11 +94,13 @@ module.exports = UrlCache = return callback() _findUrlDetails: (project_id, url, callback = (error, urlDetails) ->) -> + console.log("_findUrlDetails") db.UrlCache.find(where: { url: url, project_id: project_id }) .then((urlDetails) -> callback null, urlDetails) .error callback _updateOrCreateUrlDetails: (project_id, url, lastModified, callback = (error) ->) -> + console.log("_updateOrCreateUrlDetails") db.UrlCache.findOrCreate(where: {url: url, project_id: project_id}) .spread( (urlDetails, created) -> @@ -109,11 +111,13 @@ module.exports = UrlCache = .error callback _clearUrlDetails: (project_id, url, callback = (error) ->) -> + console.log("_clearUrlDetails") db.UrlCache.destroy(where: {url: url, project_id: project_id}) .then(() -> callback null) .error callback _findAllUrlsInProject: (project_id, callback = (error, urls) ->) -> + console.log("_findAllUrlsInProject") db.UrlCache.findAll(where: { project_id: project_id }) .then( (urlEntries) -> From 627bed428e2ac7abdcedfcf7a389fa0742099d68 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Mon, 30 Jul 2018 16:22:04 +0100 Subject: [PATCH 091/118] added a queue with 1 concurency to db queries --- app/coffee/ProjectPersistenceManager.coffee | 3 + app/coffee/UrlCache.coffee | 68 +++++++++++++-------- 2 files changed, 46 insertions(+), 25 deletions(-) diff --git a/app/coffee/ProjectPersistenceManager.coffee b/app/coffee/ProjectPersistenceManager.coffee index 403043f..17bead2 100644 --- a/app/coffee/ProjectPersistenceManager.coffee +++ b/app/coffee/ProjectPersistenceManager.coffee @@ -11,6 +11,7 @@ module.exports = ProjectPersistenceManager = EXPIRY_TIMEOUT: Settings.project_cache_length_ms || oneDay * 2.5 markProjectAsJustAccessed: (project_id, callback = (error) ->) -> + console.log("markProjectAsJustAccessed") db.Project.findOrCreate(where: {project_id: project_id}) .spread( (project, created) -> @@ -53,11 +54,13 @@ module.exports = ProjectPersistenceManager = callback() _clearProjectFromDatabase: (project_id, callback = (error) ->) -> + console.log("_clearProjectFromDatabase") db.Project.destroy(where: {project_id: project_id}) .then(() -> callback()) .error callback _findExpiredProjectIds: (callback = (error, project_ids) ->) -> + console.log("_findExpiredProjectIds") db.Project.findAll(where: ["lastAccessed < ?", new Date(Date.now() - ProjectPersistenceManager.EXPIRY_TIMEOUT)]) .then((projects) -> callback null, projects.map((project) -> project.project_id) diff --git a/app/coffee/UrlCache.coffee b/app/coffee/UrlCache.coffee index 06e6012..e5fc81c 100644 --- a/app/coffee/UrlCache.coffee +++ b/app/coffee/UrlCache.coffee @@ -6,6 +6,15 @@ fs = require("fs") logger = require "logger-sharelatex" async = require "async" +queue = async.queue((task, cb)-> + console.log("running task") + task(cb) +, 1) + +console.log("hi there queue") +queue.drain = ()-> + console.log('HI all items have been processed') + module.exports = UrlCache = downloadUrlToFile: (project_id, url, destPath, lastModified, callback = (error) ->) -> UrlCache._ensureUrlIsInCache project_id, url, lastModified, (error, pathToCachedUrl) => @@ -51,8 +60,9 @@ module.exports = UrlCache = _doesUrlNeedDownloading: (project_id, url, lastModified, callback = (error, needsDownloading) ->) -> if !lastModified? return callback null, true - + console.log "about to get _findUrlDetails" UrlCache._findUrlDetails project_id, url, (error, urlDetails) -> + console.log error, urlDetails, "_findUrlDetails result" return callback(error) if error? if !urlDetails? or !urlDetails.lastModified? or urlDetails.lastModified.getTime() < lastModified.getTime() return callback null, true @@ -94,36 +104,44 @@ module.exports = UrlCache = return callback() _findUrlDetails: (project_id, url, callback = (error, urlDetails) ->) -> - console.log("_findUrlDetails") - db.UrlCache.find(where: { url: url, project_id: project_id }) - .then((urlDetails) -> callback null, urlDetails) - .error callback + job = (cb)-> + db.UrlCache.find(where: { url: url, project_id: project_id }) + .then((urlDetails) -> cb null, urlDetails) + .error cb + queue.push job, callback _updateOrCreateUrlDetails: (project_id, url, lastModified, callback = (error) ->) -> - console.log("_updateOrCreateUrlDetails") - db.UrlCache.findOrCreate(where: {url: url, project_id: project_id}) - .spread( - (urlDetails, created) -> - urlDetails.updateAttributes(lastModified: lastModified) - .then(() -> callback()) - .error(callback) - ) - .error callback + job = (cb)-> + console.log("_updateOrCreateUrlDetails") + db.UrlCache.findOrCreate(where: {url: url, project_id: project_id}) + .spread( + (urlDetails, created) -> + urlDetails.updateAttributes(lastModified: lastModified) + .then(() -> cb()) + .error(cb) + ) + .error cb + queue.push(job, callback) _clearUrlDetails: (project_id, url, callback = (error) ->) -> - console.log("_clearUrlDetails") - db.UrlCache.destroy(where: {url: url, project_id: project_id}) - .then(() -> callback null) - .error callback + job = (cb)-> + console.log("_clearUrlDetails") + db.UrlCache.destroy(where: {url: url, project_id: project_id}) + .then(() -> cb null) + .error cb + queue.push(job, callback) + _findAllUrlsInProject: (project_id, callback = (error, urls) ->) -> - console.log("_findAllUrlsInProject") - db.UrlCache.findAll(where: { project_id: project_id }) - .then( - (urlEntries) -> - callback null, urlEntries.map((entry) -> entry.url) - ) - .error callback + job = (cb)-> + console.log("_findAllUrlsInProject") + db.UrlCache.findAll(where: { project_id: project_id }) + .then( + (urlEntries) -> + cb null, urlEntries.map((entry) -> entry.url) + ) + .error cb + queue.push(job, callback) From d1ce49d6d7558d8d28433ad6b33fd7206b9892c1 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Mon, 30 Jul 2018 16:46:47 +0100 Subject: [PATCH 092/118] add db queue file for global db query queues --- app/coffee/DbQueue.coffee | 13 ++++++ app/coffee/ProjectPersistenceManager.coffee | 46 +++++++++++++-------- app/coffee/UrlCache.coffee | 18 +++----- 3 files changed, 46 insertions(+), 31 deletions(-) create mode 100644 app/coffee/DbQueue.coffee diff --git a/app/coffee/DbQueue.coffee b/app/coffee/DbQueue.coffee new file mode 100644 index 0000000..6d12924 --- /dev/null +++ b/app/coffee/DbQueue.coffee @@ -0,0 +1,13 @@ +async = require "async" + +queue = async.queue((task, cb)-> + console.log("running task") + task(cb) + , 1) + +queue.drain = ()-> + console.log('HI all items have been processed') + +module.exports = + queue: queue + diff --git a/app/coffee/ProjectPersistenceManager.coffee b/app/coffee/ProjectPersistenceManager.coffee index 17bead2..427b44b 100644 --- a/app/coffee/ProjectPersistenceManager.coffee +++ b/app/coffee/ProjectPersistenceManager.coffee @@ -1,6 +1,7 @@ UrlCache = require "./UrlCache" CompileManager = require "./CompileManager" db = require "./db" +dbQueue = require "./DbQueue" async = require "async" logger = require "logger-sharelatex" oneDay = 24 * 60 * 60 * 1000 @@ -11,15 +12,18 @@ module.exports = ProjectPersistenceManager = EXPIRY_TIMEOUT: Settings.project_cache_length_ms || oneDay * 2.5 markProjectAsJustAccessed: (project_id, callback = (error) ->) -> - console.log("markProjectAsJustAccessed") - db.Project.findOrCreate(where: {project_id: project_id}) - .spread( - (project, created) -> - project.updateAttributes(lastAccessed: new Date()) - .then(() -> callback()) - .error callback - ) - .error callback + job = (cb)-> + console.log("markProjectAsJustAccessed") + db.Project.findOrCreate(where: {project_id: project_id}) + .spread( + (project, created) -> + project.updateAttributes(lastAccessed: new Date()) + .then(() -> cb()) + .error cb + ) + .error cb + dbQueue.queue.push(job, callback) + clearExpiredProjects: (callback = (error) ->) -> ProjectPersistenceManager._findExpiredProjectIds (error, project_ids) -> @@ -54,16 +58,22 @@ module.exports = ProjectPersistenceManager = callback() _clearProjectFromDatabase: (project_id, callback = (error) ->) -> - console.log("_clearProjectFromDatabase") - db.Project.destroy(where: {project_id: project_id}) - .then(() -> callback()) - .error callback + job = (cb)-> + console.log("_clearProjectFromDatabase") + db.Project.destroy(where: {project_id: project_id}) + .then(() -> callback()) + .error callback + dbQueue.queue.push(job, callback) + _findExpiredProjectIds: (callback = (error, project_ids) ->) -> - console.log("_findExpiredProjectIds") - db.Project.findAll(where: ["lastAccessed < ?", new Date(Date.now() - ProjectPersistenceManager.EXPIRY_TIMEOUT)]) - .then((projects) -> - callback null, projects.map((project) -> project.project_id) - ).error callback + job = (cb)-> + console.log("_findExpiredProjectIds") + db.Project.findAll(where: ["lastAccessed < ?", new Date(Date.now() - ProjectPersistenceManager.EXPIRY_TIMEOUT)]) + .then((projects) -> + callback null, projects.map((project) -> project.project_id) + ).error callback + dbQueue.queue.push(job, callback) + logger.log {EXPIRY_TIMEOUT: ProjectPersistenceManager.EXPIRY_TIMEOUT}, "project assets kept timeout" diff --git a/app/coffee/UrlCache.coffee b/app/coffee/UrlCache.coffee index e5fc81c..73d9841 100644 --- a/app/coffee/UrlCache.coffee +++ b/app/coffee/UrlCache.coffee @@ -1,4 +1,5 @@ db = require("./db") +dbQueue = require "./DbQueue" UrlFetcher = require("./UrlFetcher") Settings = require("settings-sharelatex") crypto = require("crypto") @@ -6,15 +7,6 @@ fs = require("fs") logger = require "logger-sharelatex" async = require "async" -queue = async.queue((task, cb)-> - console.log("running task") - task(cb) -, 1) - -console.log("hi there queue") -queue.drain = ()-> - console.log('HI all items have been processed') - module.exports = UrlCache = downloadUrlToFile: (project_id, url, destPath, lastModified, callback = (error) ->) -> UrlCache._ensureUrlIsInCache project_id, url, lastModified, (error, pathToCachedUrl) => @@ -108,7 +100,7 @@ module.exports = UrlCache = db.UrlCache.find(where: { url: url, project_id: project_id }) .then((urlDetails) -> cb null, urlDetails) .error cb - queue.push job, callback + dbQueue.queue.push job, callback _updateOrCreateUrlDetails: (project_id, url, lastModified, callback = (error) ->) -> job = (cb)-> @@ -121,7 +113,7 @@ module.exports = UrlCache = .error(cb) ) .error cb - queue.push(job, callback) + dbQueue.queue.push(job, callback) _clearUrlDetails: (project_id, url, callback = (error) ->) -> job = (cb)-> @@ -129,7 +121,7 @@ module.exports = UrlCache = db.UrlCache.destroy(where: {url: url, project_id: project_id}) .then(() -> cb null) .error cb - queue.push(job, callback) + dbQueue.queue.push(job, callback) _findAllUrlsInProject: (project_id, callback = (error, urls) ->) -> @@ -141,7 +133,7 @@ module.exports = UrlCache = cb null, urlEntries.map((entry) -> entry.url) ) .error cb - queue.push(job, callback) + dbQueue.queue.push(job, callback) From 3a9206f1e749ef454b9d69cb8f00ee7548a47ea9 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Mon, 30 Jul 2018 17:01:59 +0100 Subject: [PATCH 093/118] =?UTF-8?q?fix=20missing=20cb=E2=80=99s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/coffee/ProjectPersistenceManager.coffee | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/coffee/ProjectPersistenceManager.coffee b/app/coffee/ProjectPersistenceManager.coffee index 427b44b..f41c14c 100644 --- a/app/coffee/ProjectPersistenceManager.coffee +++ b/app/coffee/ProjectPersistenceManager.coffee @@ -61,8 +61,8 @@ module.exports = ProjectPersistenceManager = job = (cb)-> console.log("_clearProjectFromDatabase") db.Project.destroy(where: {project_id: project_id}) - .then(() -> callback()) - .error callback + .then(() -> cb()) + .error cb dbQueue.queue.push(job, callback) @@ -71,8 +71,8 @@ module.exports = ProjectPersistenceManager = console.log("_findExpiredProjectIds") db.Project.findAll(where: ["lastAccessed < ?", new Date(Date.now() - ProjectPersistenceManager.EXPIRY_TIMEOUT)]) .then((projects) -> - callback null, projects.map((project) -> project.project_id) - ).error callback + cb null, projects.map((project) -> project.project_id) + ).error cb dbQueue.queue.push(job, callback) From ee518c17551a0a5c3ca13fafb9e7c920a51269df Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Mon, 30 Jul 2018 17:37:30 +0100 Subject: [PATCH 094/118] fix expired projects command --- Makefile | 2 +- app/coffee/ProjectPersistenceManager.coffee | 8 ++++++-- app/coffee/db.coffee | 2 ++ docker-compose.ci.yml | 2 +- docker-compose.yml | 2 +- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 6daee1b..b1ce293 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.8 +# Version: 1.1.9 BUILD_NUMBER ?= local BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) diff --git a/app/coffee/ProjectPersistenceManager.coffee b/app/coffee/ProjectPersistenceManager.coffee index f41c14c..2c73c61 100644 --- a/app/coffee/ProjectPersistenceManager.coffee +++ b/app/coffee/ProjectPersistenceManager.coffee @@ -68,11 +68,15 @@ module.exports = ProjectPersistenceManager = _findExpiredProjectIds: (callback = (error, project_ids) ->) -> job = (cb)-> - console.log("_findExpiredProjectIds") - db.Project.findAll(where: ["lastAccessed < ?", new Date(Date.now() - ProjectPersistenceManager.EXPIRY_TIMEOUT)]) + keepProjectsFrom = new Date(Date.now() - ProjectPersistenceManager.EXPIRY_TIMEOUT) + console.log("_findExpiredProjectIds", keepProjectsFrom) + q = {} + q[db.op.gt] = keepProjectsFrom + db.Project.findAll(where:{lastAccessed:q}) .then((projects) -> cb null, projects.map((project) -> project.project_id) ).error cb + dbQueue.queue.push(job, callback) diff --git a/app/coffee/db.coffee b/app/coffee/db.coffee index 764edeb..de48dfd 100644 --- a/app/coffee/db.coffee +++ b/app/coffee/db.coffee @@ -41,6 +41,8 @@ module.exports = ] }) + op: Sequelize.Op + sync: () -> logger.log dbPath:Settings.mysql.clsi.storage, "syncing db schema" sequelize.sync() diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index e8850b9..f6c8a27 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.8 +# Version: 1.1.9 version: "2" diff --git a/docker-compose.yml b/docker-compose.yml index f47658d..371e6e7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ # This file was auto-generated, do not edit it directly. # Instead run bin/update_build_scripts from # https://github.com/sharelatex/sharelatex-dev-environment -# Version: 1.1.8 +# Version: 1.1.9 version: "2" From 9ef9a3b780ccbca16fba6afe958a566c0383d0d9 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 31 Jul 2018 14:38:24 +0100 Subject: [PATCH 095/118] make Settings.parallelSqlQueryLimit a config setting --- app/coffee/DbQueue.coffee | 5 +++-- config/settings.defaults.coffee | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/coffee/DbQueue.coffee b/app/coffee/DbQueue.coffee index 6d12924..79673e4 100644 --- a/app/coffee/DbQueue.coffee +++ b/app/coffee/DbQueue.coffee @@ -1,13 +1,14 @@ async = require "async" +Settings = require "settings-sharelatex" queue = async.queue((task, cb)-> console.log("running task") task(cb) - , 1) + , Settings.parallelSqlQueryLimit) queue.drain = ()-> console.log('HI all items have been processed') - + module.exports = queue: queue diff --git a/config/settings.defaults.coffee b/config/settings.defaults.coffee index a2bf593..cfa6bda 100644 --- a/config/settings.defaults.coffee +++ b/config/settings.defaults.coffee @@ -36,6 +36,7 @@ module.exports = smokeTest: process.env["SMOKE_TEST"] or false project_cache_length_ms: 1000 * 60 * 60 * 24 parallelFileDownloads:1 + parallelSqlQueryLimit:1 filestoreDomainOveride: process.env["FILESTORE_DOMAIN_OVERRIDE"] texliveImageNameOveride: process.env["TEX_LIVE_IMAGE_NAME_OVERRIDE"] From 3e3468d9e9efee15be1768de99609b519e1810f0 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Wed, 1 Aug 2018 13:59:09 +0100 Subject: [PATCH 096/118] reduce logging --- app/coffee/DbQueue.coffee | 1 - app/coffee/DockerRunner.coffee | 12 +++++++----- app/coffee/ProjectPersistenceManager.coffee | 3 --- app/coffee/UrlCache.coffee | 5 ----- 4 files changed, 7 insertions(+), 14 deletions(-) diff --git a/app/coffee/DbQueue.coffee b/app/coffee/DbQueue.coffee index 79673e4..0e02590 100644 --- a/app/coffee/DbQueue.coffee +++ b/app/coffee/DbQueue.coffee @@ -2,7 +2,6 @@ async = require "async" Settings = require "settings-sharelatex" queue = async.queue((task, cb)-> - console.log("running task") task(cb) , Settings.parallelSqlQueryLimit) diff --git a/app/coffee/DockerRunner.coffee b/app/coffee/DockerRunner.coffee index 69a3df9..589a2e0 100644 --- a/app/coffee/DockerRunner.coffee +++ b/app/coffee/DockerRunner.coffee @@ -46,7 +46,9 @@ module.exports = DockerRunner = fingerprint = DockerRunner._fingerprintContainer(options) options.name = name = "project-#{project_id}-#{fingerprint}" - logger.log project_id: project_id, options: options, "running docker container" + logOptions = _.clone(options) + logOptions.HostConfig.SecurityOpt = "secomp used, removed in logging" + logger.log project_id: project_id, options:logOptions, "running docker container" DockerRunner._runAndWaitForContainer options, volumes, timeout, (error, output) -> if error?.message?.match("HTTP code is 500") logger.log err: error, project_id: project_id, "error running container so destroying and retrying" @@ -144,14 +146,14 @@ module.exports = DockerRunner = "Ulimits": [{'Name': 'cpu', 'Soft': timeoutInSeconds+5, 'Hard': timeoutInSeconds+10}] "CapDrop": "ALL" "SecurityOpt": ["no-new-privileges"] - - if Settings.clsi.docker.seccomp_profile? - options.HostConfig.SecurityOpt.push "seccomp=#{Settings.clsi.docker.seccomp_profile}" + if Settings.path?.synctexBinHostPath? options["HostConfig"]["Binds"].push("#{Settings.path.synctexBinHostPath}:/opt/synctex:ro") - logger.log options:options, "options for running docker container" + if Settings.clsi.docker.seccomp_profile? + options.HostConfig.SecurityOpt.push "seccomp=#{Settings.clsi.docker.seccomp_profile}" + return options _fingerprintContainer: (containerOptions) -> diff --git a/app/coffee/ProjectPersistenceManager.coffee b/app/coffee/ProjectPersistenceManager.coffee index 2c73c61..944ea70 100644 --- a/app/coffee/ProjectPersistenceManager.coffee +++ b/app/coffee/ProjectPersistenceManager.coffee @@ -13,7 +13,6 @@ module.exports = ProjectPersistenceManager = markProjectAsJustAccessed: (project_id, callback = (error) ->) -> job = (cb)-> - console.log("markProjectAsJustAccessed") db.Project.findOrCreate(where: {project_id: project_id}) .spread( (project, created) -> @@ -59,7 +58,6 @@ module.exports = ProjectPersistenceManager = _clearProjectFromDatabase: (project_id, callback = (error) ->) -> job = (cb)-> - console.log("_clearProjectFromDatabase") db.Project.destroy(where: {project_id: project_id}) .then(() -> cb()) .error cb @@ -69,7 +67,6 @@ module.exports = ProjectPersistenceManager = _findExpiredProjectIds: (callback = (error, project_ids) ->) -> job = (cb)-> keepProjectsFrom = new Date(Date.now() - ProjectPersistenceManager.EXPIRY_TIMEOUT) - console.log("_findExpiredProjectIds", keepProjectsFrom) q = {} q[db.op.gt] = keepProjectsFrom db.Project.findAll(where:{lastAccessed:q}) diff --git a/app/coffee/UrlCache.coffee b/app/coffee/UrlCache.coffee index 73d9841..d44479a 100644 --- a/app/coffee/UrlCache.coffee +++ b/app/coffee/UrlCache.coffee @@ -52,9 +52,7 @@ module.exports = UrlCache = _doesUrlNeedDownloading: (project_id, url, lastModified, callback = (error, needsDownloading) ->) -> if !lastModified? return callback null, true - console.log "about to get _findUrlDetails" UrlCache._findUrlDetails project_id, url, (error, urlDetails) -> - console.log error, urlDetails, "_findUrlDetails result" return callback(error) if error? if !urlDetails? or !urlDetails.lastModified? or urlDetails.lastModified.getTime() < lastModified.getTime() return callback null, true @@ -104,7 +102,6 @@ module.exports = UrlCache = _updateOrCreateUrlDetails: (project_id, url, lastModified, callback = (error) ->) -> job = (cb)-> - console.log("_updateOrCreateUrlDetails") db.UrlCache.findOrCreate(where: {url: url, project_id: project_id}) .spread( (urlDetails, created) -> @@ -117,7 +114,6 @@ module.exports = UrlCache = _clearUrlDetails: (project_id, url, callback = (error) ->) -> job = (cb)-> - console.log("_clearUrlDetails") db.UrlCache.destroy(where: {url: url, project_id: project_id}) .then(() -> cb null) .error cb @@ -126,7 +122,6 @@ module.exports = UrlCache = _findAllUrlsInProject: (project_id, callback = (error, urls) ->) -> job = (cb)-> - console.log("_findAllUrlsInProject") db.UrlCache.findAll(where: { project_id: project_id }) .then( (urlEntries) -> From 3890cdec373fa52a39990e85870821ae8d6c1b68 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Wed, 1 Aug 2018 14:10:22 +0100 Subject: [PATCH 097/118] null check host options --- app/coffee/DockerRunner.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/coffee/DockerRunner.coffee b/app/coffee/DockerRunner.coffee index 589a2e0..1e2612c 100644 --- a/app/coffee/DockerRunner.coffee +++ b/app/coffee/DockerRunner.coffee @@ -47,7 +47,7 @@ module.exports = DockerRunner = options.name = name = "project-#{project_id}-#{fingerprint}" logOptions = _.clone(options) - logOptions.HostConfig.SecurityOpt = "secomp used, removed in logging" + logOptions?.HostConfig?.SecurityOpt = "secomp used, removed in logging" logger.log project_id: project_id, options:logOptions, "running docker container" DockerRunner._runAndWaitForContainer options, volumes, timeout, (error, output) -> if error?.message?.match("HTTP code is 500") From 95b2e8caae5483faaf52aebeebc847b05808bdb3 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Wed, 1 Aug 2018 14:32:17 +0100 Subject: [PATCH 098/118] comment out erroring log for moment --- app/coffee/DockerRunner.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/coffee/DockerRunner.coffee b/app/coffee/DockerRunner.coffee index 1e2612c..d529b2d 100644 --- a/app/coffee/DockerRunner.coffee +++ b/app/coffee/DockerRunner.coffee @@ -46,9 +46,9 @@ module.exports = DockerRunner = fingerprint = DockerRunner._fingerprintContainer(options) options.name = name = "project-#{project_id}-#{fingerprint}" - logOptions = _.clone(options) - logOptions?.HostConfig?.SecurityOpt = "secomp used, removed in logging" - logger.log project_id: project_id, options:logOptions, "running docker container" + # logOptions = _.clone(options) + # logOptions?.HostConfig?.SecurityOpt = "secomp used, removed in logging" + logger.log project_id: project_id, "running docker container" DockerRunner._runAndWaitForContainer options, volumes, timeout, (error, output) -> if error?.message?.match("HTTP code is 500") logger.log err: error, project_id: project_id, "error running container so destroying and retrying" From 9f7922983584808035acdb9b0436295debfa4fd2 Mon Sep 17 00:00:00 2001 From: Christopher Hoskin Date: Fri, 3 Aug 2018 15:33:53 +0100 Subject: [PATCH 099/118] Read sentry dsn from env --- config/settings.defaults.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/settings.defaults.coffee b/config/settings.defaults.coffee index cfa6bda..6f4f160 100644 --- a/config/settings.defaults.coffee +++ b/config/settings.defaults.coffee @@ -39,6 +39,8 @@ module.exports = parallelSqlQueryLimit:1 filestoreDomainOveride: process.env["FILESTORE_DOMAIN_OVERRIDE"] texliveImageNameOveride: process.env["TEX_LIVE_IMAGE_NAME_OVERRIDE"] + sentry: + dsn: process.env['SENTRY_DSN'] if process.env["DOCKER_RUNNER"] From 95e052d05910680aeeca2521b65b089122c4e249 Mon Sep 17 00:00:00 2001 From: Christopher Hoskin Date: Mon, 13 Aug 2018 12:27:13 +0100 Subject: [PATCH 100/118] Put a guard on sentry dsn --- config/settings.defaults.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/config/settings.defaults.coffee b/config/settings.defaults.coffee index 6f4f160..19fcb32 100644 --- a/config/settings.defaults.coffee +++ b/config/settings.defaults.coffee @@ -39,6 +39,7 @@ module.exports = parallelSqlQueryLimit:1 filestoreDomainOveride: process.env["FILESTORE_DOMAIN_OVERRIDE"] texliveImageNameOveride: process.env["TEX_LIVE_IMAGE_NAME_OVERRIDE"] +if process.env['SENTRY_DSN'] sentry: dsn: process.env['SENTRY_DSN'] From 382f30f810688f2de073658b7026b924d2c1c74e Mon Sep 17 00:00:00 2001 From: Christopher Hoskin Date: Mon, 13 Aug 2018 17:36:53 +0100 Subject: [PATCH 101/118] Revert "Put a guard on sentry dsn" This reverts commit 95e052d05910680aeeca2521b65b089122c4e249. --- config/settings.defaults.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/config/settings.defaults.coffee b/config/settings.defaults.coffee index 19fcb32..6f4f160 100644 --- a/config/settings.defaults.coffee +++ b/config/settings.defaults.coffee @@ -39,7 +39,6 @@ module.exports = parallelSqlQueryLimit:1 filestoreDomainOveride: process.env["FILESTORE_DOMAIN_OVERRIDE"] texliveImageNameOveride: process.env["TEX_LIVE_IMAGE_NAME_OVERRIDE"] -if process.env['SENTRY_DSN'] sentry: dsn: process.env['SENTRY_DSN'] From eec0529ef7575248b09baee58398e8c963794520 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 14 Aug 2018 15:17:56 +0100 Subject: [PATCH 102/118] put FILESTORE_PARALLEL_FILE_DOWNLOADS and FILESTORE_PARALLEL_SQL_QUERY_LIMIT into env vars --- config/settings.defaults.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/settings.defaults.coffee b/config/settings.defaults.coffee index cfa6bda..6dae3fe 100644 --- a/config/settings.defaults.coffee +++ b/config/settings.defaults.coffee @@ -35,8 +35,8 @@ module.exports = smokeTest: process.env["SMOKE_TEST"] or false project_cache_length_ms: 1000 * 60 * 60 * 24 - parallelFileDownloads:1 - parallelSqlQueryLimit:1 + parallelFileDownloads: process.env["FILESTORE_PARALLEL_FILE_DOWNLOADS"] or 1 + parallelSqlQueryLimit: process.env["FILESTORE_PARALLEL_SQL_QUERY_LIMIT"] or 1 filestoreDomainOveride: process.env["FILESTORE_DOMAIN_OVERRIDE"] texliveImageNameOveride: process.env["TEX_LIVE_IMAGE_NAME_OVERRIDE"] From 988f177f79ec47ee862d23e81605cda2d99b0315 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Sun, 19 Aug 2018 11:38:27 +0100 Subject: [PATCH 103/118] added loads of debugging --- app/coffee/LockManager.coffee | 7 +++++-- app/coffee/ProjectPersistenceManager.coffee | 10 +++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/app/coffee/LockManager.coffee b/app/coffee/LockManager.coffee index 5d6fa46..2877d5f 100644 --- a/app/coffee/LockManager.coffee +++ b/app/coffee/LockManager.coffee @@ -2,7 +2,8 @@ Settings = require('settings-sharelatex') logger = require "logger-sharelatex" Lockfile = require('lockfile') # from https://github.com/npm/lockfile Errors = require "./Errors" - +fs = require("fs") +Path = require("path") module.exports = LockManager = LOCK_TEST_INTERVAL: 1000 # 50ms between each test of the lock MAX_LOCK_WAIT_TIME: 15000 # 10s maximum time to spend trying to get the lock @@ -15,7 +16,9 @@ module.exports = LockManager = stale: @LOCK_STALE Lockfile.lock path, lockOpts, (error) -> return callback new Errors.AlreadyCompilingError("compile in progress") if error?.code is 'EEXIST' - return callback(error) if error? + if error? + logger.err error:error, path:path, statLock:fs.lstatSync(path), statDir: fs.lstatSync(path.dirname(path)), "unable to get lock" + return callback(error) runner (error1, args...) -> Lockfile.unlock path, (error2) -> error = error1 or error2 diff --git a/app/coffee/ProjectPersistenceManager.coffee b/app/coffee/ProjectPersistenceManager.coffee index 944ea70..2565af3 100644 --- a/app/coffee/ProjectPersistenceManager.coffee +++ b/app/coffee/ProjectPersistenceManager.coffee @@ -51,12 +51,16 @@ module.exports = ProjectPersistenceManager = clearProjectFromCache: (project_id, callback = (error) ->) -> logger.log project_id: project_id, "clearing project from cache" UrlCache.clearProject project_id, (error) -> - return callback(error) if error? + if error? + logger.err error:error, project_id: project_id, "error clearing project from cache" + return callback(error) ProjectPersistenceManager._clearProjectFromDatabase project_id, (error) -> - return callback(error) if error? - callback() + if error? + logger.err error:error, project_id:project_id, "error clearing project from database" + callback(error) _clearProjectFromDatabase: (project_id, callback = (error) ->) -> + logger.log project_id:project_id, "clearing project from database" job = (cb)-> db.Project.destroy(where: {project_id: project_id}) .then(() -> cb()) From 1990f20dc06c64b117de2f5100e01ea3682eb99b Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Mon, 20 Aug 2018 10:12:32 +0100 Subject: [PATCH 104/118] improve error reporting --- app/coffee/CompileManager.coffee | 3 ++- app/coffee/DockerRunner.coffee | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/coffee/CompileManager.coffee b/app/coffee/CompileManager.coffee index 807b8c4..daa9021 100644 --- a/app/coffee/CompileManager.coffee +++ b/app/coffee/CompileManager.coffee @@ -290,7 +290,8 @@ module.exports = CompileManager = return callback(error) if error? fs.readFile directory + "/" + file_name + ".wc", "utf-8", (err, stdout) -> if err? - logger.err err:err, command:command, directory:directory, project_id:project_id, user_id:user_id, "error reading word count output" + #call it node_err so sentry doesn't use random path error as unique id so it can't be ignored + logger.err node_err:err, command:command, directory:directory, project_id:project_id, user_id:user_id, "error reading word count output" return callback(err) results = CompileManager._parseWordcountFromOutput(stdout) logger.log project_id:project_id, user_id:user_id, wordcount: results, "word count results" diff --git a/app/coffee/DockerRunner.coffee b/app/coffee/DockerRunner.coffee index d529b2d..42c9f9a 100644 --- a/app/coffee/DockerRunner.coffee +++ b/app/coffee/DockerRunner.coffee @@ -214,7 +214,7 @@ module.exports = DockerRunner = if error?.statusCode == 404 createAndStartContainer() else if error? - logger.err {container_name: name}, "unable to inspect container to start" + logger.err {container_name: name, error:error}, "unable to inspect container to start" return callback(error) else startExistingContainer() From 0f179a7c7cbd9553202588da199ea7d6d5fcdf05 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 21 Aug 2018 12:02:12 +0100 Subject: [PATCH 105/118] add log on exited error code --- app/coffee/DockerRunner.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/app/coffee/DockerRunner.coffee b/app/coffee/DockerRunner.coffee index 42c9f9a..d1337d6 100644 --- a/app/coffee/DockerRunner.coffee +++ b/app/coffee/DockerRunner.coffee @@ -107,6 +107,7 @@ module.exports = DockerRunner = if exitCode is 1 # exit status from chktex err = DockerRunner.ERR_EXITED err.code = exitCode + logger.err err:err, exitCode:exitCode, options:options, "docker container has exited" return callback(err) containerReturned = true callbackIfFinished() From 834eeffda4fe4e0e7ba2a0e0ce3833be3b30d886 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 21 Aug 2018 18:56:53 +0100 Subject: [PATCH 106/118] add time secomp --- seccomp/clsi-profile.json | 40 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/seccomp/clsi-profile.json b/seccomp/clsi-profile.json index cab7a44..34fd252 100644 --- a/seccomp/clsi-profile.json +++ b/seccomp/clsi-profile.json @@ -31,6 +31,21 @@ "action": "SCMP_ACT_ALLOW", "args": [] }, + { + "name": "clock_getres", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "clock_gettime", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "clock_nanosleep", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, { "name": "clone", "action": "SCMP_ACT_ALLOW", @@ -668,6 +683,31 @@ "action": "SCMP_ACT_ALLOW", "args": [] }, + { + "name": "timer_create", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "timer_delete", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "timer_getoverrun", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "timer_gettime", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "timer_settime", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, { "name": "times", "action": "SCMP_ACT_ALLOW", From 171ad0329da77c54f9900026f3ff3e85c1ec917c Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Wed, 22 Aug 2018 18:21:15 +0100 Subject: [PATCH 107/118] fix sql query checking last access time --- app/coffee/ProjectPersistenceManager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/coffee/ProjectPersistenceManager.coffee b/app/coffee/ProjectPersistenceManager.coffee index 2565af3..4ea02bf 100644 --- a/app/coffee/ProjectPersistenceManager.coffee +++ b/app/coffee/ProjectPersistenceManager.coffee @@ -72,7 +72,7 @@ module.exports = ProjectPersistenceManager = job = (cb)-> keepProjectsFrom = new Date(Date.now() - ProjectPersistenceManager.EXPIRY_TIMEOUT) q = {} - q[db.op.gt] = keepProjectsFrom + q[db.op.lt] = keepProjectsFrom db.Project.findAll(where:{lastAccessed:q}) .then((projects) -> cb null, projects.map((project) -> project.project_id) From e4d28addf9d617cd569bf86c707b731676af0ebe Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Wed, 22 Aug 2018 21:32:19 +0100 Subject: [PATCH 108/118] change sync to async for lockfile debugging --- app/coffee/LockManager.coffee | 18 ++++++++++-------- test/unit/coffee/LockManagerTests.coffee | 2 ++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/app/coffee/LockManager.coffee b/app/coffee/LockManager.coffee index 2877d5f..069e26b 100644 --- a/app/coffee/LockManager.coffee +++ b/app/coffee/LockManager.coffee @@ -16,11 +16,13 @@ module.exports = LockManager = stale: @LOCK_STALE Lockfile.lock path, lockOpts, (error) -> return callback new Errors.AlreadyCompilingError("compile in progress") if error?.code is 'EEXIST' - if error? - logger.err error:error, path:path, statLock:fs.lstatSync(path), statDir: fs.lstatSync(path.dirname(path)), "unable to get lock" - return callback(error) - runner (error1, args...) -> - Lockfile.unlock path, (error2) -> - error = error1 or error2 - return callback(error) if error? - callback(null, args...) + fs.lstat path, (err, statLock)-> + fs.lstat Path.dirname(path), (err, statDir)-> + if error? + logger.err error:error, path:path, statLock:statLock, statDir: statDir, "unable to get lock" + return callback(error) + runner (error1, args...) -> + Lockfile.unlock path, (error2) -> + error = error1 or error2 + return callback(error) if error? + callback(null, args...) diff --git a/test/unit/coffee/LockManagerTests.coffee b/test/unit/coffee/LockManagerTests.coffee index 2d0c95a..ab9ce93 100644 --- a/test/unit/coffee/LockManagerTests.coffee +++ b/test/unit/coffee/LockManagerTests.coffee @@ -10,6 +10,8 @@ describe "DockerLockManager", -> @LockManager = SandboxedModule.require modulePath, requires: "settings-sharelatex": {} "logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() } + "fs": + lstat:sinon.stub().callsArgWith(1) "lockfile": @Lockfile = {} @lockFile = "/local/compile/directory/.project-lock" From 7b773474d97931dd1e331bd6dfb17c75dfff9ee6 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Wed, 22 Aug 2018 23:54:40 +0100 Subject: [PATCH 109/118] improve error reporting --- app/coffee/LockManager.coffee | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/coffee/LockManager.coffee b/app/coffee/LockManager.coffee index 069e26b..2a8777f 100644 --- a/app/coffee/LockManager.coffee +++ b/app/coffee/LockManager.coffee @@ -16,11 +16,13 @@ module.exports = LockManager = stale: @LOCK_STALE Lockfile.lock path, lockOpts, (error) -> return callback new Errors.AlreadyCompilingError("compile in progress") if error?.code is 'EEXIST' - fs.lstat path, (err, statLock)-> - fs.lstat Path.dirname(path), (err, statDir)-> - if error? - logger.err error:error, path:path, statLock:statLock, statDir: statDir, "unable to get lock" - return callback(error) + if error? + fs.lstat path, (statLockErr, statLock)-> + fs.lstat Path.dirname(path), (statDirErr, statDir)-> + fs.readdir Path.dirname(path), (readdirErr, readdirDir)-> + logger.err error:error, path:path, statLock:statLock, statLockErr:statLockErr, statDir:statDir, statDirErr: statDirErr, readdirErr:readdirErr, readdirDir:readdirDir, "unable to get lock" + return callback(error) + else runner (error1, args...) -> Lockfile.unlock path, (error2) -> error = error1 or error2 From 05ddbd3a18ae882fbc5a0ec5db5dd0d6e89f492a Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Thu, 23 Aug 2018 00:10:06 +0100 Subject: [PATCH 110/118] try changing bin to be owned by node --- entrypoint.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/entrypoint.sh b/entrypoint.sh index 32776fa..cb2580c 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -17,6 +17,7 @@ chown -R node:node /app/bin/synctex mkdir -p /app/test/acceptance/fixtures/tmp/ chown -R node:node /app +chown -R node:node /app/bin ./bin/install_texlive_gce.sh exec runuser -u node "$@" \ No newline at end of file From 5074442702d34d3112ee6504c5aaa3c284294507 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Thu, 23 Aug 2018 00:12:05 +0100 Subject: [PATCH 111/118] fix unit tests --- app/coffee/LockManager.coffee | 23 ++++++++++++----------- test/unit/coffee/LockManagerTests.coffee | 3 ++- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/app/coffee/LockManager.coffee b/app/coffee/LockManager.coffee index 2a8777f..afa3cca 100644 --- a/app/coffee/LockManager.coffee +++ b/app/coffee/LockManager.coffee @@ -15,16 +15,17 @@ module.exports = LockManager = pollPeriod: @LOCK_TEST_INTERVAL stale: @LOCK_STALE Lockfile.lock path, lockOpts, (error) -> - return callback new Errors.AlreadyCompilingError("compile in progress") if error?.code is 'EEXIST' - if error? - fs.lstat path, (statLockErr, statLock)-> - fs.lstat Path.dirname(path), (statDirErr, statDir)-> - fs.readdir Path.dirname(path), (readdirErr, readdirDir)-> + if error?.code is 'EEXIST' + return callback new Errors.AlreadyCompilingError("compile in progress") + else if error? + fs.lstat path, (statLockErr, statLock)-> + fs.lstat Path.dirname(path), (statDirErr, statDir)-> + fs.readdir Path.dirname(path), (readdirErr, readdirDir)-> logger.err error:error, path:path, statLock:statLock, statLockErr:statLockErr, statDir:statDir, statDirErr: statDirErr, readdirErr:readdirErr, readdirDir:readdirDir, "unable to get lock" return callback(error) - else - runner (error1, args...) -> - Lockfile.unlock path, (error2) -> - error = error1 or error2 - return callback(error) if error? - callback(null, args...) + else + runner (error1, args...) -> + Lockfile.unlock path, (error2) -> + error = error1 or error2 + return callback(error) if error? + callback(null, args...) diff --git a/test/unit/coffee/LockManagerTests.coffee b/test/unit/coffee/LockManagerTests.coffee index ab9ce93..9dd1d46 100644 --- a/test/unit/coffee/LockManagerTests.coffee +++ b/test/unit/coffee/LockManagerTests.coffee @@ -9,9 +9,10 @@ describe "DockerLockManager", -> beforeEach -> @LockManager = SandboxedModule.require modulePath, requires: "settings-sharelatex": {} - "logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() } + "logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub(), err:-> } "fs": lstat:sinon.stub().callsArgWith(1) + readdir: sinon.stub().callsArgWith(1) "lockfile": @Lockfile = {} @lockFile = "/local/compile/directory/.project-lock" From b4107b7391d2cd00da152a5253006d2873b85034 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Thu, 23 Aug 2018 08:34:18 +0100 Subject: [PATCH 112/118] fse.ensureDir when running synctex and wordcount --- app/coffee/CompileController.coffee | 2 - app/coffee/CompileManager.coffee | 59 +++++++++++++++++------------ 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/app/coffee/CompileController.coffee b/app/coffee/CompileController.coffee index 91137f6..5d1ee2e 100644 --- a/app/coffee/CompileController.coffee +++ b/app/coffee/CompileController.coffee @@ -82,7 +82,6 @@ module.exports = CompileController = column = parseInt(req.query.column, 10) project_id = req.params.project_id user_id = req.params.user_id - CompileManager.syncFromCode project_id, user_id, file, line, column, (error, pdfPositions) -> return next(error) if error? res.send JSON.stringify { @@ -95,7 +94,6 @@ module.exports = CompileController = v = parseFloat(req.query.v) project_id = req.params.project_id user_id = req.params.user_id - CompileManager.syncFromPdf project_id, user_id, page, h, v, (error, codePositions) -> return next(error) if error? res.send JSON.stringify { diff --git a/app/coffee/CompileManager.coffee b/app/coffee/CompileManager.coffee index daa9021..f40a98b 100644 --- a/app/coffee/CompileManager.coffee +++ b/app/coffee/CompileManager.coffee @@ -203,14 +203,18 @@ module.exports = CompileManager = compileDir = getCompileDir(project_id, user_id) synctex_path = "#{base_dir}/output.pdf" command = ["code", synctex_path, file_path, line, column] - CompileManager._runSynctex project_id, user_id, command, (error, stdout) -> - return callback(error) if error? - if stdout.toLowerCase().indexOf("warning") == -1 - logType = "log" - else - logType = "err" - logger[logType] project_id: project_id, user_id:user_id, file_name: file_name, line: line, column: column, command:command, stdout: stdout, "synctex code output" - callback null, CompileManager._parseSynctexFromCodeOutput(stdout) + fse.ensureDir compileDir, (error) -> + if error? + logger.err {error, project_id, user_id, file_name}, "error ensuring dir for sync from code" + return callback(error) + CompileManager._runSynctex project_id, user_id, command, (error, stdout) -> + return callback(error) if error? + if stdout.toLowerCase().indexOf("warning") == -1 + logType = "log" + else + logType = "err" + logger[logType] project_id: project_id, user_id:user_id, file_name: file_name, line: line, column: column, command:command, stdout: stdout, "synctex code output" + callback null, CompileManager._parseSynctexFromCodeOutput(stdout) syncFromPdf: (project_id, user_id, page, h, v, callback = (error, filePositions) ->) -> compileName = getCompileName(project_id, user_id) @@ -218,10 +222,14 @@ module.exports = CompileManager = base_dir = Settings.path.synctexBaseDir(compileName) synctex_path = "#{base_dir}/output.pdf" command = ["pdf", synctex_path, page, h, v] - CompileManager._runSynctex project_id, user_id, command, (error, stdout) -> - return callback(error) if error? - logger.log project_id: project_id, user_id:user_id, page: page, h: h, v:v, stdout: stdout, "synctex pdf output" - callback null, CompileManager._parseSynctexFromPdfOutput(stdout, base_dir) + fse.ensureDir compileDir, (error) -> + if error? + logger.err {error, project_id, user_id, file_name}, "error ensuring dir for sync to code" + return callback(error) + CompileManager._runSynctex project_id, user_id, command, (error, stdout) -> + return callback(error) if error? + logger.log project_id: project_id, user_id:user_id, page: page, h: h, v:v, stdout: stdout, "synctex pdf output" + callback null, CompileManager._parseSynctexFromPdfOutput(stdout, base_dir) _checkFileExists: (path, callback = (error) ->) -> synctexDir = Path.dirname(path) @@ -282,20 +290,23 @@ module.exports = CompileManager = logger.log project_id:project_id, user_id:user_id, file_name:file_name, image:image, "running wordcount" file_path = "$COMPILE_DIR/" + file_name command = [ "texcount", '-nocol', '-inc', file_path, "-out=" + file_path + ".wc"] - directory = getCompileDir(project_id, user_id) + compileDir = getCompileDir(project_id, user_id) timeout = 10 * 1000 compileName = getCompileName(project_id, user_id) - - CommandRunner.run compileName, command, directory, image, timeout, {}, (error) -> - return callback(error) if error? - fs.readFile directory + "/" + file_name + ".wc", "utf-8", (err, stdout) -> - if err? - #call it node_err so sentry doesn't use random path error as unique id so it can't be ignored - logger.err node_err:err, command:command, directory:directory, project_id:project_id, user_id:user_id, "error reading word count output" - return callback(err) - results = CompileManager._parseWordcountFromOutput(stdout) - logger.log project_id:project_id, user_id:user_id, wordcount: results, "word count results" - callback null, results + fse.ensureDir compileDir, (error) -> + if error? + logger.err {error, project_id, user_id, file_name}, "error ensuring dir for sync from code" + return callback(error) + CommandRunner.run compileName, command, compileDir, image, timeout, {}, (error) -> + return callback(error) if error? + fs.readFile compileDir + "/" + file_name + ".wc", "utf-8", (err, stdout) -> + if err? + #call it node_err so sentry doesn't use random path error as unique id so it can't be ignored + logger.err node_err:err, command:command, compileDir:compileDir, project_id:project_id, user_id:user_id, "error reading word count output" + return callback(err) + results = CompileManager._parseWordcountFromOutput(stdout) + logger.log project_id:project_id, user_id:user_id, wordcount: results, "word count results" + callback null, results _parseWordcountFromOutput: (output) -> results = { From 607bb74ffa070793bc900a1dbe9b72487ea3b17e Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Thu, 23 Aug 2018 11:16:28 +0100 Subject: [PATCH 113/118] reduce log level --- app/coffee/DockerRunner.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/coffee/DockerRunner.coffee b/app/coffee/DockerRunner.coffee index d1337d6..e71a933 100644 --- a/app/coffee/DockerRunner.coffee +++ b/app/coffee/DockerRunner.coffee @@ -107,9 +107,10 @@ module.exports = DockerRunner = if exitCode is 1 # exit status from chktex err = DockerRunner.ERR_EXITED err.code = exitCode - logger.err err:err, exitCode:exitCode, options:options, "docker container has exited" return callback(err) containerReturned = true + options.SecurityOpt = null #small log line + logger.log err:err, exitCode:exitCode, options:options, "docker container has exited" callbackIfFinished() _getContainerOptions: (command, image, volumes, timeout, environment) -> From 6299832a13ed56aa3cc56e799714782d0180df9b Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Thu, 23 Aug 2018 11:18:05 +0100 Subject: [PATCH 114/118] don't error on a bad synctex call --- app/coffee/CompileManager.coffee | 6 +----- app/coffee/DockerRunner.coffee | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/app/coffee/CompileManager.coffee b/app/coffee/CompileManager.coffee index f40a98b..0436a89 100644 --- a/app/coffee/CompileManager.coffee +++ b/app/coffee/CompileManager.coffee @@ -209,11 +209,7 @@ module.exports = CompileManager = return callback(error) CompileManager._runSynctex project_id, user_id, command, (error, stdout) -> return callback(error) if error? - if stdout.toLowerCase().indexOf("warning") == -1 - logType = "log" - else - logType = "err" - logger[logType] project_id: project_id, user_id:user_id, file_name: file_name, line: line, column: column, command:command, stdout: stdout, "synctex code output" + logger.log project_id: project_id, user_id:user_id, file_name: file_name, line: line, column: column, command:command, stdout: stdout, "synctex code output" callback null, CompileManager._parseSynctexFromCodeOutput(stdout) syncFromPdf: (project_id, user_id, page, h, v, callback = (error, filePositions) ->) -> diff --git a/app/coffee/DockerRunner.coffee b/app/coffee/DockerRunner.coffee index e71a933..3c2ed9c 100644 --- a/app/coffee/DockerRunner.coffee +++ b/app/coffee/DockerRunner.coffee @@ -109,7 +109,7 @@ module.exports = DockerRunner = err.code = exitCode return callback(err) containerReturned = true - options.SecurityOpt = null #small log line + options?.HostConfig?.SecurityOpt = null #small log line logger.log err:err, exitCode:exitCode, options:options, "docker container has exited" callbackIfFinished() From d3bb863d0a989d0df74e6a613c701314733b552f Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 11 Sep 2018 09:51:20 +0100 Subject: [PATCH 115/118] improve synctex logging --- app/coffee/CompileManager.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/coffee/CompileManager.coffee b/app/coffee/CompileManager.coffee index 0436a89..64f2665 100644 --- a/app/coffee/CompileManager.coffee +++ b/app/coffee/CompileManager.coffee @@ -251,7 +251,7 @@ module.exports = CompileManager = compileName = getCompileName(project_id, user_id) CommandRunner.run compileName, command, directory, Settings.clsi.docker.image, timeout, {}, (error, output) -> if error? - logger.err err:error, command:command, "error running synctex" + logger.err err:error, command:command, project_id:project_id, user_id:user_id, "error running synctex" return callback(error) callback(null, output.stdout) From 5f9fb85613857dbfb8db6ba6524156788378b9bd Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 11 Sep 2018 09:54:44 +0100 Subject: [PATCH 116/118] bump wordcount timeouts, taken from 82b996b145196711e439d7d7045f53498c1afa1a --- app/coffee/CompileManager.coffee | 2 +- test/unit/coffee/CompileManagerTests.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/coffee/CompileManager.coffee b/app/coffee/CompileManager.coffee index 64f2665..893d461 100644 --- a/app/coffee/CompileManager.coffee +++ b/app/coffee/CompileManager.coffee @@ -247,7 +247,7 @@ module.exports = CompileManager = command.unshift("/opt/synctex") directory = getCompileDir(project_id, user_id) - timeout = 10 * 1000 + timeout = 60 * 1000 # increased to allow for large projects compileName = getCompileName(project_id, user_id) CommandRunner.run compileName, command, directory, Settings.clsi.docker.image, timeout, {}, (error, output) -> if error? diff --git a/test/unit/coffee/CompileManagerTests.coffee b/test/unit/coffee/CompileManagerTests.coffee index 93b7f11..26524fb 100644 --- a/test/unit/coffee/CompileManagerTests.coffee +++ b/test/unit/coffee/CompileManagerTests.coffee @@ -319,7 +319,7 @@ describe "CompileManager", -> @callback = sinon.stub() @project_id - @timeout = 10 * 1000 + @timeout = 60 * 1000 @file_name = "main.tex" @Settings.path.compilesDir = "/local/compile/directory" @image = "example.com/image" From b07b7a84be650575ec8fdab1f7787a1d04126d19 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 11 Sep 2018 10:21:37 +0100 Subject: [PATCH 117/118] fix unit tests --- app/coffee/CompileManager.coffee | 5 +++-- test/unit/coffee/CompileManagerTests.coffee | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/coffee/CompileManager.coffee b/app/coffee/CompileManager.coffee index 9c28535..eff20eb 100644 --- a/app/coffee/CompileManager.coffee +++ b/app/coffee/CompileManager.coffee @@ -282,12 +282,13 @@ module.exports = CompileManager = } return results + wordcount: (project_id, user_id, file_name, image, callback = (error, pdfPositions) ->) -> logger.log project_id:project_id, user_id:user_id, file_name:file_name, image:image, "running wordcount" file_path = "$COMPILE_DIR/" + file_name command = [ "texcount", '-nocol', '-inc', file_path, "-out=" + file_path + ".wc"] - directory = getCompileDir(project_id, user_id) - timeout = 60 * 1000 # increased to allow for large projects + compileDir = getCompileDir(project_id, user_id) + timeout = 60 * 1000 compileName = getCompileName(project_id, user_id) fse.ensureDir compileDir, (error) -> if error? diff --git a/test/unit/coffee/CompileManagerTests.coffee b/test/unit/coffee/CompileManagerTests.coffee index 26524fb..608a3e5 100644 --- a/test/unit/coffee/CompileManagerTests.coffee +++ b/test/unit/coffee/CompileManagerTests.coffee @@ -269,7 +269,7 @@ describe "CompileManager", -> ['/opt/synctex', 'code', synctex_path, file_path, @line, @column], "#{@Settings.path.compilesDir}/#{@project_id}-#{@user_id}", @Settings.clsi.docker.image, - 10000, + 60000, {} ).should.equal true @@ -300,7 +300,7 @@ describe "CompileManager", -> ['/opt/synctex', "pdf", synctex_path, @page, @h, @v], "#{@Settings.path.compilesDir}/#{@project_id}-#{@user_id}", @Settings.clsi.docker.image, - 10000, + 60000, {}).should.equal true it "should call the callback with the parsed output", -> From a781c7f60085f0546b3f9e253c1643c0e2501ff6 Mon Sep 17 00:00:00 2001 From: Henry Oswald Date: Tue, 11 Sep 2018 11:34:25 +0100 Subject: [PATCH 118/118] change timeout test latex code --- test/acceptance/coffee/TimeoutTests.coffee | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/acceptance/coffee/TimeoutTests.coffee b/test/acceptance/coffee/TimeoutTests.coffee index 877223a..c3acd8f 100644 --- a/test/acceptance/coffee/TimeoutTests.coffee +++ b/test/acceptance/coffee/TimeoutTests.coffee @@ -8,13 +8,14 @@ describe "Timed out compile", -> before (done) -> @request = options: - timeout: 1 #seconds + timeout: 10 #seconds resources: [ path: "main.tex" content: ''' \\documentclass{article} \\begin{document} - \\input{|"/bin/bash -c ':(){ :|:& };:'"} + \\def\\x{Hello!\\par\\x} + \\x \\end{document} ''' ]