In the last blog post github actions - containerized services we described how to use containerized services to run tests against a fresh phpipam installation in each test run. This is a great step forward for automated testing but we can go a step further and use matrix builds to run the tests against multiple phpipam versions.

The build matrix feature

With the build matrix feature you are able to create a matrix for different variations of your jobs. E.g. you can run jobs against multiple versions of your used programing languages or like in our case against different phpipam versions.

We espacially want to run our test job against head version of v1.4 and v1.5 of phpipam. To define the matrix we add the following to the test job in our github workflow definition:

jobs:
  test:
    strategy:
      matrix:
        phpipam: ['1.4x','1.5x']

The matrix values can be accessed by using the variable ${{ matrix.phpipam-version }}. In our case in the service container definition for the phpipam container.

phpipam:
  image: phpipam/phpipam-www:${{ matrix.phpipam-version }}
  ports:
    - "443:443"

From now for each variation of the matrix a job will be created according to your job definition in your github workflow.

full workflow

To make the workflow more clear and readable we put some recurring data in environment variables and apply them to the container or step definitions.

The full workflow definition looks like this:

See the code!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
name: CI

on: [push]

jobs:
  test:
    name: end to end tests
    runs-on: ubuntu-latest
    env:
      DATABASE_HOST: "database"
      DATABASE_USER: "phpipam"
      DATABASE_PASS: "phpipamadmin"
      DATABASE_NAME: "phpipam"
    strategy:
      matrix:
        phpipam-version: ['1.4x', '1.5x']
    services:
      database:
        image: mariadb:10.3.18
        ports:
          - "3306:3306"
        env:
          MYSQL_ROOT_PASSWORD: "rootpw"
          MYSQL_USER: ${{ env.DATABASE_USER }}
          MYSQL_PASSWORD: ${{ env.DATABASE_PASS }}
          MYSQL_DATABASE: ${{ env.DATABASE_NAME }}
      phpipam:
        image: phpipam/phpipam-www:${{ matrix.phpipam-version }}
        ports:
          - "443:443"
        env:
          IPAM_DATABASE_HOST: ${{ env.DATABASE_HOST }}
          IPAM_DATABASE_USER: ${{ env.DATABASE_USER }}
          IPAM_DATABASE_PASS: ${{ env.DATABASE_PASS }}
          IPAM_DATABASE_NAME: ${{ env.DATABASE_NAME }}
        options: >-
          --label "phpipam-cnt"
    steps:
      - uses: actions/checkout@v2
      - name: Set up Python
        uses: actions/setup-python@v2
        with:
          python-version: '3.x'
      - name: setup test environment
        run: |
          make test-setup
        env:
          PHPIPAM_URL: "https://localhost"
          PHPIPAM_APPID: "ansible"
          PHPIPAM_USERNAME: "admin"
          PHPIPAM_PASSWORD: "ipamadmin"
      - name: "waiting for database to come online"
        run: |
          for i in `seq 1 10`;
          do
            nc -z 127.0.0.1 3306 && echo Success && exit 0
            echo -n .
            sleep 1
          done
          echo Failed waiting for MySQL && exit 1
      - name: setup phpipam
        run: |
          export PHPIPAM_CONTAINER=$(docker container list --filter=label=phpipam-cnt --format={{.Names}})
          docker exec ${PHPIPAM_CONTAINER} sh -c 'mysql -h ${{ env.DATABASE_HOST }} -u ${{ env.DATABASE_USER }} -p${{ env.DATABASE_PASS}} phpipam < phpipam/db/SCHEMA.sql'
          docker exec ${PHPIPAM_CONTAINER} sh -c 'mysql -h ${{ env.DATABASE_HOST }} -u ${{ env.DATABASE_USER }} -p${{ env.DATABASE_PASS}} phpipam --execute="UPDATE settings SET api=1 WHERE id=1;"'
          docker exec ${PHPIPAM_CONTAINER} sh -c 'mysql -h ${{ env.DATABASE_HOST }} -u ${{ env.DATABASE_USER }} -p${{ env.DATABASE_PASS}} phpipam --execute="INSERT INTO api (app_id, app_code, app_permissions, app_security, app_lock_wait) VALUES (\"ansible\",\"aAbBcCdDeEfF00112233445566778899\",2,\"ssl_token\",0);"'
      - name: run example setup
        run: |
          make test-example_setup
        env:
          PHPIPAM_VALIDATE_CERTS: false
      - name: run playbook tests
        run: |
          make test-all
        env:
          PHPIPAM_VALIDATE_CERTS: "false"

conclusion and next steps

We try to make the workflow as simple as possible and increase the readability.

As already mentioned we move some parameters such as database name, user, password and host to job scoped environment variables we can reuse theses values in different places.

As we run all database related tasks inside the phpipam container we don’t need to checkout phpipam repository to get the current schema. We also guarantee in that way that the database is always initialized with the schema for the current used phpipam version.

In the next iteration we will try to make the workflow more generic by moving the service setup to an custom action. We will put all of our findings and the approach we choose to develop the custom action in a new blogpost.