Continuous Integration (CI) means you create a very tight loop to get feedback for changes made to the code.

A good CI system will run every time you make some changes to the source code (or precisely every time you push it out to the hosted copy of your source code.)

It can then run your reguler test, your release tests, your author tests and even more. (You don't wait for it, it does not take up CPU on your computer.)

A CI can be testing your code on multiple platforms (GitHub Actions support Ubuntu Linux, MacOS, MS Windows), on multiple Perl versions, and as many configurations as you like. For every change you make. Long before you release to the public.

If you set it up to run on pull-requests as well, it will give automated feedback to your contributors event while you are at sleep. Soon after they sent the pull-request. So they know their code change does not break the code on any of the platforms and any of the Perl versions you'd like to support.

It can periodically check if updates to the dependencies have not broken your otherwise unchanged code.

It can make it easy to test that your code does not break any of the (public) packages that depend on your code. Long before you release your new version.

Minimal requirement

If you already use GitHub to store the source code of your project the only thing you need is to create a directory called .github/workflows/ and put one (or more) appropriately formatted YAML file in the directory. Once you push out the change GitHub will start running the tests you configured.

It will send you e-mails if something is broken, but you can also follow it via the Actions link on your repository.

The following are a few sample YAML files.

GitHub Actions for projects with Makefile.PL

  • Run on every push and on every pull_request.
  • Create a strategy listing the versions of Perl we would like to use.
  • Use the perl-tester docker image that already contains a bunch of testing modules for Perl. Use the version numbers from the matrix to generate the tags used to identify the Docker container.
  • Make the system check out the source code of your prokeject with the pre-defined checkout action.
  • Using cpanm install all the prerequisites and then run the regular steps starting with perl Makefile.PL
  • As a separate step, run the same tests, but this time RELEASE_TESTING enabled.

examples/workflows/makefile_tester.yml

name: CI Makefile

on:
    push:
        branches: '*'
    pull_request:
        branches: '*'
    schedule:
        - cron: '42 5 * * 0'

jobs:
  perl-job:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        perl-version:
          - '5.30'
#          - '5.32'
#          - 'latest'
    container:
      image: perldocker/perl-tester:${{ matrix.perl-version }}     # https://hub.docker.com/r/perldocker/perl-tester
    name: Perl ${{ matrix.perl-version }}
    steps:
      - uses: actions/checkout@v2
      - name: Regular tests
        run: |
            cpanm --installdeps --notest .
            perl Makefile.PL
            make
            make test


      - name: Prepare for release tests
        run: |
            cpanm --installdep .
            cpanm --notest Test::CheckManifest Test::Pod::Coverage Pod::Coverage Test::Pod
      - name: Release tests
        env:
          RELEASE_TESTING: 1
        run: |
            perl Makefile.PL
            make
            make test

GitHub Actions for projects using Dist::Zilla

  • Similar to the one with Makefile.PL, this one runs the Dist::Zilla commands

examples/workflows/dzil_tester.yml

name: CI Dzil

on:
    push:
        branches: '*'
    pull_request:
        branches: '*'
    schedule:
        - cron: '42 5 * * 0'

jobs:
  perl-job:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        perl-version:
          - '5.30'
#          - '5.32'
#          - 'latest'
    container:
      image: perldocker/perl-tester:${{ matrix.perl-version }}     # https://hub.docker.com/r/perldocker/perl-tester
    name: Perl ${{ matrix.perl-version }}
    steps:
      - uses: actions/checkout@v2
      - name: Regular tests
        run: |
          dzil authordeps --missing | cpanm --notest
          dzil listdeps --author --missing | cpanm --notest
          dzil test --author --release

GitHub Actions for projects using Build.PL

examples/workflows/build_tester.yml

name: CI Build

on:
    push:
        branches: '*'
    pull_request:
        branches: '*'
    schedule:
        - cron: '42 5 * * 0'

jobs:
  perl-job:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        perl-version:
          - '5.30'
#          - '5.32'
#          - 'latest'
    container:
      image: perldocker/perl-tester:${{ matrix.perl-version }}     # https://hub.docker.com/r/perldocker/perl-tester
    name: Perl ${{ matrix.perl-version }}
    steps:
      - uses: actions/checkout@v2
      - name: Regular tests
        run: |
          perl Build.PL
          perl Build test

Dist::Zilla Native

examples/workflows/dzil_native.yml

name: CI

on:
    push:
        branches: '*'
    pull_request:
        branches: '*'
#    schedule:
#        - cron: '42 5 * * 0'

jobs:
  test:
    strategy:
      fail-fast: false
      matrix:
        runner: [ubuntu-latest, macos-latest, windows-latest]
        perl: [ '5.32' ]

    runs-on: ${{matrix.runner}}
    name: OS ${{matrix.runner}} Perl ${{matrix.perl}}

    steps:
    - uses: actions/checkout@v2

    - name: Set up perl
      if: ${{ startsWith( matrix.runner, 'windows-' )  }}
      uses: shogo82148/actions-setup-perl@v1
      with:
          perl-version: ${{ matrix.perl }}
          distribution: strawberry

    - name: Set up perl
      if: ${{ ! startsWith( matrix.runner, 'windows-' )  }}
      uses: shogo82148/actions-setup-perl@v1
      with:
          perl-version: ${{ matrix.perl }}

    - name: Install dependencies
      run: |
        cpanm --notest Dist::Zilla
        dzil authordeps --missing | cpanm --notest
        dzil listdeps --author --missing | cpanm --notest

    - name: Show content of log files on Linux
      if: ${{ failure() && startsWith( matrix.runner, 'ubuntu-' )  }}
      run: cat /home/runner/.cpanm/work/*/build.log

    - name: Show content of log files on Mac
      if: ${{ failure() && startsWith( matrix.runner, 'macos-' )  }}
      run: cat /Users/runner/.cpanm/work/*/build.log

    - name: Show content of log files on Windows
      if: ${{ failure() && startsWith( matrix.runner, 'windows-' )  }}
      run: cat C:/Users/RUNNER~1/.cpanm/work/*/build.log

    - name: Regular tests
      run: |
        dzil test --author --release

Makefile.PL Native

examples/workflows/makefile_native.yml

name: CI

on:
    push:
        branches: '*'
    pull_request:
        branches: '*'

jobs:
  perl-job:
    strategy:
      fail-fast: false
      matrix:
        runner: [ubuntu-latest, macos-latest, windows-latest]
        perl: [ '5.32', '5.30' ]

    runs-on: ${{matrix.runner}}
    name: OS ${{matrix.runner}} Perl ${{matrix.perl}}

    steps:
    - uses: actions/checkout@v2

    - name: Set up perl
      uses: shogo82148/actions-setup-perl@v1
      with:
          perl-version: ${{ matrix.perl }}
          distribution: ${{ ( startsWith( matrix.runner, 'windows-' ) && 'strawberry' ) || 'default' }}

    - name: Install dependencies
      run: |
          cpanm --notest Module::Install
          cpanm --installdeps --notest .

    - name: Show content of log files on Linux
      if: ${{ failure() && startsWith( matrix.runner, 'ubuntu-' )  }}
      run: cat /home/runner/.cpanm/work/*/build.log

    - name: Show content of log files on Mac
      if: ${{ failure() && startsWith( matrix.runner, 'macos-' )  }}
      run: cat /Users/runner/.cpanm/work/*/build.log

    - name: Show content of log files on Windows
      if: ${{ failure() && startsWith( matrix.runner, 'windows-' )  }}
      run: cat C:/Users/RUNNER~1/.cpanm/work/*/build.log

    - name: Regular Tests
      run: |
          perl Makefile.PL
          make
          make test