diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml new file mode 100644 index 0000000000..57214707f5 --- /dev/null +++ b/.github/workflows/cicd.yml @@ -0,0 +1,200 @@ +name: CI/CD Back & Front + +on: + push: + paths: + - '**.kt' + - '**.png' + - '**.js' + - '**.json' + - '**.ts' + - '**.yml' + - '.github/workflows/cicd.yml' + - 'infra/configurations' + - 'infra/docker' + - '**.sql' + - 'backend/pom.xml' + - 'Makefile' + - 'frontend' + schedule: + - cron: "38 11 */3 * *" + +jobs: + version: + name: Set application version and env profile + runs-on: ubuntu-18.04 + outputs: + ENV_PROFILE: ${{ steps.env_profile.outputs.ENV_PROFILE }} + VERSION: ${{ steps.version.outputs.VERSION }} + steps: + - name: Get last release version + id: lastrelease + uses: pozetroninc/github-action-get-latest-release@master + with: + repository: mtes-mct/monitorenv + + - id: step1 + name: Set ENV profile as dev by default + run: echo "ENV_PROFILE=dev" >> $GITHUB_ENV + + - name: Set ENV profile as PROD when it is a release + if: startsWith(github.ref, 'refs/tags/v') || startsWith(github.ref, 'refs/heads/v') + run: echo "ENV_PROFILE=prod" >> $GITHUB_ENV + + - id: env_profile + name: Set ENV profile output + run: ENV_PROFILE=${{ env.ENV_PROFILE }} && echo "::set-output name=ENV_PROFILE::$ENV_PROFILE" + + - id: version + name: Set VERSION env + run: | + if [ "${ENV_PROFILE}" != "prod" ]; then\ + export VERSION=${{ steps.lastrelease.outputs.release }}_snapshot + else\ + export VERSION=${{ steps.lastrelease.outputs.release }} + fi + echo "::set-output name=VERSION::$VERSION" + + build: + name: Run unit tests, build and package + needs: version + runs-on: ubuntu-18.04 + env: + ENV_PROFILE: ${{needs.version.outputs.ENV_PROFILE}} + VERSION: ${{needs.version.outputs.VERSION}} + ACTIONS_ALLOW_UNSECURE_COMMANDS: true + steps: + - name: Show version + run: echo "VERSION:${{ env.VERSION }} ENV_PROFILE:${{ env.ENV_PROFILE }}" + + - name: Setup Java JDK + uses: actions/setup-java@v1.4.2 + with: + java-version: 11 + + - uses: actions/checkout@v2 + + - name: NPM install with caching + uses: bahmutov/npm-install@v1.6.0 + with: + # Working directory to specify subfolder in which dependencies are defined + working-directory: frontend/ + useLockFile: true + # Custom install command to use + install-command: npm install + + - name: Unit test + run: make test + + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@master + + - name: Cache Docker layers + uses: actions/cache@v2 + with: + path: /tmp/.buildx-cache-app + key: ${{ runner.os }}-single-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-single-buildx + + - name: Build image + uses: docker/build-push-action@v2 + with: + context: . + load: true + builder: ${{ steps.buildx.outputs.name }} + file: infra/configuration/app/Dockerfile + push: false + tags: monitorfish-app:${{ env.VERSION }} + cache-from: type=local,src=/tmp/.buildx-cache-app + cache-to: type=local,mode=max,dest=/tmp/.buildx-cache-app-new + build-args: | + VERSION=${{ env.VERSION }} + ENV_PROFILE=${{ env.ENV_PROFILE }} + GITHUB_SHA=${{ github.sha }} + + # Temp fix + # https://github.com/docker/build-push-action/issues/252 + # https://github.com/moby/buildkit/issues/1896 + + - name: Move cache + run: | + rm -rf /tmp/.buildx-cache-app + mv /tmp/.buildx-cache-app-new /tmp/.buildx-cache-app + + - name: Upload image to artifacts + uses: ishworkh/docker-image-artifact-upload@v1 + with: + image: monitorenv-app:${{ env.VERSION }} + + e2e_test: + name: Run E2E tests + needs: [version, build] + runs-on: ubuntu-18.04 + strategy: + # when one test fails, DO NOT cancel the other + # containers, because this will kill Cypress processes + # leaving the Dashboard hanging ... + # https://github.com/cypress-io/github-action/issues/48 + fail-fast: false + matrix: + # run 4 copies of the current job in parallel + containers: [1, 2, 3, 4] + env: + ACTIONS_ALLOW_UNSECURE_COMMANDS: true + REACT_APP_CYPRESS_PORT: 8880 + CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} + CYPRESS_PROJECT_ID: ${{ secrets.PROJECT_ID }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ENV_PROFILE: ${{needs.version.outputs.ENV_PROFILE}} + VERSION: ${{needs.version.outputs.VERSION}} + steps: + - uses: actions/checkout@v2 + + - name: Download image + uses: ishworkh/docker-image-artifact-download@v1 + with: + image: monitorenv-app:${{ env.VERSION }} + + - name: Run docker images + run: make docker-compose-up + + - name: Curl stubbed geoserver + run: until $(curl --output /dev/null --silent --fail "http://localhost:8081/geoserver/wfs?service=WFS&version=1.1.0&request=GetFeature&typename=monitorenv:regulations&outputFormat=application/json&CQL_FILTER=topic=%27Ouest%20Cotentin%20Bivalves%27%20AND%20zone=%27Praires%20Ouest%20cotentin%27"); do printf '.'; sleep 5; done; + + - name: Run Cypress tests + uses: cypress-io/github-action@v2 + with: + # we have already installed all dependencies above + install: true + install-command: npm i + working-directory: frontend + record: true + parallel: true + wait-on: 'http://localhost:8880' + env: PORT=8880 + browser: firefox + + push_to_registry: + name: Push to registry + needs: [version, e2e_test] + runs-on: ubuntu-18.04 + env: + ACTIONS_ALLOW_UNSECURE_COMMANDS: true + ENV_PROFILE: ${{needs.version.outputs.ENV_PROFILE}} + VERSION: ${{needs.version.outputs.VERSION}} + steps: + - uses: actions/checkout@v2 + + - name: Download image + uses: ishworkh/docker-image-artifact-download@v1 + with: + image: monitorenv-app:${{ env.VERSION }} + + - name: Push docker image to registry + if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/v') || startsWith(github.ref, 'refs/heads/v') + run: | + echo "${{ secrets.GITHUB_TOKEN }}" | docker login docker.pkg.github.com -u ${GITHUB_ACTOR} --password-stdin + make docker-tag + make docker-push diff --git a/infra/configurations/app/Dockerfile b/infra/configurations/app/Dockerfile new file mode 100644 index 0000000000..b3e4ea153a --- /dev/null +++ b/infra/configurations/app/Dockerfile @@ -0,0 +1,126 @@ + +##################### +# Multi stage build # +##################### + +ARG GITHUB_SHA=NO_COMMIT +ARG VERSION=NO_VERSION + +######################################## +# Build monitorfish backend with maven # +######################################## +FROM maven:3.6.0-jdk-11-slim as buildBack + +ARG GITHUB_SHA +ARG VERSION + +WORKDIR /tmp/ +COPY backend/pom.xml /tmp/pom.xml +RUN mvn dependency:go-offline -B + +COPY backend/ /tmp/ +COPY backend/pom.xml /tmp/POM_WITH_ENV_VARS +RUN sed -e 's/COMMIT_TO_CHANGE/${GITHUB_SHA}/' \ + -e 's/VERSION_TO_CHANGE/${VERSION}/' \ + POM_WITH_ENV_VARS > pom.xml + +RUN mvn clean package -DskipTests=true + +########################### +# Build frontend with npm # +########################### +FROM ubuntu:18.04 as buildFront + +ENV DEBIAN_FRONTEND=noninteractive + +ARG ENV_PROFILE +ENV REACT_APP_ENV $ENV_PROFILE + +RUN echo ${REACT_APP_ENV} + +RUN apt-get update &&\ + apt-get install --no-install-recommends -y \ + curl \ + zip \ + unzip \ + ca-certificates &&\ + apt-get clean +USER root + +RUN curl -sL https://deb.nodesource.com/setup_10.x | bash &&\ + apt-get install -y nodejs + +COPY frontend/ /tmp/frontend/ +COPY infra/ /tmp/infra/ +WORKDIR /tmp/frontend + +# Files are expected to be in /tmp/frontend/dist/monitorfish-frontend +RUN npm install ci &&\ + npm run build + +###################### +# Create final image # +###################### +# There is no more JRE with Java 11 : https://stackoverflow.com/a/53733414 +FROM azul/zulu-openjdk-alpine:11 + +ARG VERSION +ENV VERSION $VERSION + +ARG ENV_PROFILE +ENV ENV_PROFILE $ENV_PROFILE + +ENV REACT_APP_ENV $ENV_PROFILE +ENV ENV_DB_URL= + +# Add bash +RUN apk add --no-cache bash + +RUN adduser -D monitorenv +USER monitorenv + +EXPOSE 8880 +EXPOSE 5000 +EXPOSE 5001 +WORKDIR /home/monitorenv + +ENV JAVA_TOOL_OPTIONS "-Dcom.sun.management.jmxremote.ssl=false \ + -Dcom.sun.management.jmxremote.authenticate=false \ + -Dcom.sun.management.jmxremote.port=5000 \ + -Dcom.sun.management.jmxremote.rmi.port=5001 \ + -Dcom.sun.management.jmxremote.registry.ssl=false \ + -Dcom.sun.management.jmxremote.host=0.0.0.0 \ + -Djava.rmi.server.hostname=0.0.0.0" + +# Copy files for the back +COPY --from=buildBack /tmp/target/monitorenv-${VERSION}-exec.jar /home/monitorenv +COPY infra/configurations /home/monitorenv/configurations/ + +USER monitorenv +# Copy files for the front +RUN mkdir /home/monitorenv/public +COPY --from=buildFront /tmp/frontend/build /home/monitorenv/public/ +COPY --from=buildFront /tmp/infra/docker/env.sh /home/monitorenv/ + +# Add logs folder to be mounted as volume +RUN mkdir /home/monitorenv/logs + +# Set up environement variable that define the root folder use for serving static files +# It must point to the front (React) files +ENV STATIC_FILES_PATH /home/monitorenv/public + +# Default profile is for local. Can be overiden at start : docker run -e "SPRING_PROFILES_ACTIVE=prod" +ENV SPRING_PROFILES_ACTIVE ${ENV_PROFILE} + +RUN echo ${ENV_PROFILE} + +USER root +RUN chown monitorenv /home/monitorenv/env.sh +RUN chmod +x /home/monitorenv/env.sh +USER monitorenv + +ENV REACT_APP_GEOSERVER_LOCAL_URL= + +ENTRYPOINT ["/home/monitorenv/env.sh"] + +CMD exec java -Dspring.config.additional-location="/home/monitorenv/configurations/" -jar "monitorenv-${VERSION}-exec.jar" diff --git a/infra/configurations/app/env.sh b/infra/configurations/app/env.sh new file mode 100644 index 0000000000..a870e4019d --- /dev/null +++ b/infra/configurations/app/env.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +sed -i 's#__REACT_APP_GEOSERVER_LOCAL_URL__#'"$REACT_APP_GEOSERVER_LOCAL_URL"'#g' /home/monitorenv/public/env.js +sed -i 's#__REACT_APP_GEOSERVER_REMOTE_URL__#'"$REACT_APP_GEOSERVER_REMOTE_URL"'#g' /home/monitorenv/public/env.js +sed -i 's#__REACT_APP_CYPRESS_TEST__#'"$REACT_APP_CYPRESS_TEST"'#g' /home/monitorenv/public/env.js + +exec "$@"