Making it easier to setup a CI/CD stack for Plone

For persons writing Plone add-ons and integrations, what's the easy way to spin up a useful continuous integration (CI) and continuous delivery (CD) stack?
I'm looking for examples of workflows persons are using now.

I'm now looking seriously at Gitlab CI for this kind of thing, I've done something similar on Gitlab but for a non-Plone project, I feel like I need to pay attention to a few more moving parts with Plone.

Okay... I'm reading this now:

http://cosent.nl/en/blog/plone-gitlab-continuous-integration

I'm using circleci with its remote docker and the selenium hub docker image and robot framework to run my test suite for a private project for free. Intergrates to github. UI is a bit better than travis.

Here is my docker compose file

version: '3'
services:
  hub:
    image: elgalu/selenium:2
    ports:
      - 4444:4444
      # We need a fixed port range to expose VNC
      # due to a bug in Docker for Mac beta (1.12)
      # https://forums.docker.com/t/docker-for-mac-beta-not-forwarding-ports/8658/6
      - ${VNC_FROM_PORT-40650}-${VNC_TO_PORT-40700}:${VNC_FROM_PORT-40650}-${VNC_TO_PORT-40700}
      # e.g. (hard-coded)
      # - 40650-40700:40650-40700

    volumes:
      - /dev/shm:/dev/shm
    privileged: true
    environment:
      - PICK_ALL_RANDOM_PORTS=true
      - SELENIUM_HUB_HOST=hub
      - SELENIUM_HUB_PORT=4444
      - GRID=true
      - CHROME=false
      - FIREFOX=false
      - MOCK_SERVER_HOST=mock
  chrome:
    image: elgalu/selenium:2
    depends_on:
      - hub
    volumes:
      - /dev/shm:/dev/shm
    privileged: true
    environment:
      - PICK_ALL_RANDOM_PORTS=true
      - SELENIUM_HUB_HOST=hub
      - SELENIUM_HUB_PORT=4444
      - SELENIUM_NODE_HOST={{CONTAINER_IP}}
      - VNC_FROM_PORT=${VNC_FROM_PORT-40650}
      - VNC_TO_PORT=${VNC_TO_PORT-40700}
      - SCREEN_WIDTH=1300
      - SCREEN_HEIGHT=999
      - VIDEO=${VIDEO-false}
      - GRID=false
      - CHROME=true
      - FIREFOX=false

  firefox:
    image: elgalu/selenium:2
    depends_on:
      - hub
    volumes:
      - /dev/shm:/dev/shm
    privileged: true
    environment:
      - PICK_ALL_RANDOM_PORTS=true
      - SELENIUM_HUB_HOST=hub
      - SELENIUM_HUB_PORT=4444
      - SELENIUM_NODE_HOST={{CONTAINER_IP}}
      - VNC_FROM_PORT=${VNC_FROM_PORT-40650}
      - VNC_TO_PORT=${VNC_TO_PORT-40700}
      - SCREEN_WIDTH=1300
      - SCREEN_HEIGHT=999
      - VIDEO=${VIDEO-false}
      - GRID=false
      - CHROME=false
      - FIREFOX=true
  plominotest:
    image: robot_tests
    ports:
      - 8080:8080
    environment:
      - ROBOT_REMOTE_URL=http://hub:4444/wd/hub
      - ROBOT_PLONE_URL=http://plominotest:55001/plone
    volumes:
      - /dev/shm:/dev/shm
# doesn't work with remote docker
#      - ./test/testoutput:/buildout/parts/test
#      - ./src/Products/CMFPlomino/tests/:/buildout/src/Products/CMFPlomino/tests
    command: bin/test --all -vv
    depends_on:
      - "hub"

  robot-server:
    image: robot_tests
    depends_on:
       - hub
    command: bin/robot-server -v Products.CMFPlomino.testing.PRODUCTS_CMFPLOMINO_ACCEPTANCE_TESTING
    volumes:
      - /dev/shm:/dev/shm
#      - ./src/Products/CMFPlomino:/buildout/src/Products/CMFPlomino
    privileged: true
    environment:
      - ZSERVER_HOST=robot-server
      - ZSERVER_PORT=55001
      - LISTENER_PORT=49999
      - LISTENER_HOST=robot-server
  robot:
    image: robot_tests
    depends_on:
       - hub
       - robot-server
    environment:
# Not sure why this doesn't work as advertised
#       - ROBOT_OPTIONS="--outputdir=/buildout/parts/test --variable=REMOTE_URL:http://hub:4444/wd/hub --variable=PLONE_URL:http://robot-server:55001/plone "
       - LISTENER_PORT=49999
       - LISTENER_HOST=robot-server
    command: bin/robot --outputdir=/buildout/parts/test --variable=REMOTE_URL:http://hub:4444/wd/hub --variable=PLONE_URL:http://robot-server:55001/plone  /buildout/src/Products/CMFPlomino/tests/robot/test_*.robot
    volumes:
      - /dev/shm:/dev/shm
# doesn't work with remote docker
      - ./test/testoutput:/buildout/parts/test
      - ./src/Products/CMFPlomino/tests/:/buildout/src/Products/CMFPlomino/tests
    privileged: true

and my .circleci/config.xml

version: 2
general:
  branches:
    ignore:
      - master # list of branches to ignore
      - /release\/.*/ # or ignore regexes
jobs:
  build:
    working_directory: /app
    docker:
      - image: docker:17.09.0-ce-git
    steps:
      - checkout
      - setup_remote_docker
      - run:
          name: Install dependencies
          command: |
            apk add --no-cache \
              py-pip
            pip install \
              docker-compose==1.16.1 \
              awscli==1.11.76
# loading and saving cache seems to take longer than building. better off speeding up build
#      - restore_cache:
#          keys:
#            - v1-{{ .Branch }}
#          paths:
#            - /caches/app.tar
#      - run:
#          name: Load Docker image layer cache
#          command: |
#            set +o pipefail
#            docker load -i /caches/app.tar | true
      - run:
          name: Build application Docker image
          command: |
            docker build --cache-from=robot_tests -t robot_tests . || (docker build --cache-from=robot_tests -t robot_tests -f Dockerfile-bootstrap .; docker build --cache-from=robot_tests -t robot_tests .)
#      - run:
#          name: Save Docker image layer cache
#          command: |
#            docker rm $(docker ps -a -f status=exited -q) || true
#            docker rmi $(docker images -f dangling=true -q) || true
#            mkdir -p /caches
#            docker save -o /caches/app.tar robot_tests
#      - save_cache:
#          key: v1-{{ .Branch }}-{{ epoch }}
#          paths:
#            - /caches/app.tar
      - run:
          name: Run tests
          no_output_timeout: 1200
          command: |
            export NODES=1 VIDEO=false
            mkdir -p test; chown 1000:1000 test; ls -al test
            docker-compose -f docker-compose.test.yml -p grid up -d hub chrome
            docker exec grid_hub_1 wait_all_done 30s
            docker exec grid_chrome_1 wait_all_done 30s
            docker-compose -f docker-compose.test.yml -p grid up --abort-on-container-exit --exit-code-from=plominotest plominotest
      - run:
          name: Clean up tests
          when: always
          command: |
            docker cp  grid_plominotest_1:/buildout/parts/test /app
            ls -al /app/test
            docker-compose -f docker-compose.test.yml -p grid down
      - store_artifacts:
          path: /app/test

Note this runs all tests, not just robot tests.
It doesn't currently run multiple browsers but it could easily.
@datakurre if you have any idea why the ROBOT_OPTIONS env variable is not working, that would be helpful.

1 Like

No, unfortunately. “bin/robot” is our own entry point, but it does not touch env variables.

@datakurre https://github.com/robotframework/robotframework/blob/master/src/robot/run.py#L389