How to Dockerize VueJS app for production and skip installation of Dev dependencies

Viewed 556

I have the following Dockerfile

FROM node:lts-alpine

WORKDIR /app

COPY package*.json ./

RUN export NODE_ENV=production
RUN npm config set strict-ssl false

RUN npm install --only=prod
RUN npm i @vue/cli-service

COPY . ./

RUN npm run build:prod

RUN npm install -g http-server

EXPOSE 8080

CMD ["http-server" "dist"]

My dev environment includes Cypress for E2E testing, but when the npm i @vue/cli-service command runs, it fails with the following error

Cypress Version: 3.8.3
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@^1.2.7 (node_modules/webpack-dev-server/node_modules/chokidar/node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.13: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.13 (node_modules/watchpack-chokidar2/node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.13: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.13 (node_modules/mochapack/node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.13: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@2.3.2 (node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.3.2: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! cypress@3.8.3 postinstall: `node index.js --exec install`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the cypress@3.8.3 postinstall script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! A complete log of this run can be found in:
npm ERR!     /root/.npm/_logs/2021-03-27T11_53_21_406Z-debug.log
The command '/bin/sh -c npm i @vue/cli-service' returned a non-zero code: 1
ERROR: Job failed: exit status 1

here is my package.json file

{
  "name": "todo-app",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build:dev": "vue-cli-service build --mode development",
    "build:prod": "vue-cli-service build --mode production",
    "test:unit": "vue-cli-service test:unit",
    "test:e2e": "vue-cli-service test:e2e",
    "test:e2e:ci": "vue-cli-service test:e2e --headless",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "core-js": "^3.6.5",
    "vue": "^2.6.11",
    "vue-router": "^3.2.0",
    "vuex": "^3.4.0"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "~4.5.0",
    "@vue/cli-plugin-e2e-cypress": "~4.5.0",
    "@vue/cli-plugin-eslint": "~4.5.0",
    "@vue/cli-plugin-router": "~4.5.0",
    "@vue/cli-plugin-unit-mocha": "~4.5.0",
    "@vue/cli-plugin-vuex": "~4.5.0",
    "@vue/cli-service": "~4.5.0",
    "@vue/eslint-config-prettier": "^6.0.0",
    "@vue/test-utils": "^1.0.3",
    "babel-eslint": "^10.1.0",
    "chai": "^4.1.2",
    "eslint": "^6.7.2",
    "eslint-plugin-prettier": "^3.3.1",
    "eslint-plugin-vue": "^6.2.2",
    "node-sass": "^4.12.0",
    "prettier": "^2.2.1",
    "sass-loader": "^8.0.2",
    "vue-template-compiler": "^2.6.11"
  }
}

I seem to be in a catch 22. I can't build my VueJS App for production with out having the vue-cli-service, but vue-cli-service needs the dev dependencies installed and I can only install the dev dependencies if I include a lot of extra dependencies for testing etc in my Production Docker container that I don't want or need.

I am building my containers in a GitLab CI Runner.

How do other people get around this issue?

1 Answers

GitLab CI was able to help me solve my problem.

What I did was to build the app in the same stage that I run the tests (The build only runs if all the tests pass). I then made the dist folder available as an artifact that I could use in the docker_build_dev stage, which is then available as a folder I can simply copy into the Docker image file.

You can read more about GitLab Artifacts here https://docs.gitlab.com/ee/ci/pipelines/job_artifacts.html

This has not only speeded up my test and build but also made it simpler.

Here is my .gitlab-ci.yml file

stages:
  - test
  - build

test:
  stage: test
  tags:
    - docker-test
  script:
    - npm config set strict-ssl false
    - export CYPRESS_INSTALL_BINARY=/cypress/cypress.zip
    - npm install
    - npm run test:e2e:ci
    - npm run test:unit
    - npm run build:prod
  artifacts:
    paths:
      - tests/e2e/videos/
      - dist
    expire_in: 20 minutes

docker_build_dev:
  only:
    - development
  stage: build
  tags:
    - docker
  dependencies: 
    - test
  script:
    - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
    - docker build --tag $CI_REGISTRY_IMAGE:DEV .
    - docker push $CI_REGISTRY_IMAGE:DEV
  needs: ["test"]

Here is my simplified Dockerfile (I switched to Nginx, but that has nothing todo with the solution).

FROM nginx:stable-alpine

COPY ./dist/ /usr/share/nginx/html/

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]
Related