Service Containers in Composite Actions

Context

I am setting up CI tests for a Ruby on Rails project and need to run a series of RSpec tests. For this to work we need to spin up three services: Postgres, Redis, and Elasticsearch.

Luckily this is easy with service containers. However, as we are running many different specs, I wanted to use a composite action to reduce duplicate code.

My composite action looked like this:

name: "Run RSpec Tests"

inputs:
  spec_name:
    required: true

services:
  postgres:
    image: postgres:13.3
    env:
      POSTGRES_USER: example
      POSTGRES_DB: example
      POSTGRES_PASSWORD: "example"
    ports: ["5432:5432"]
    options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
  
  redis:
    image: redis 
    ports:
      - 6379:6379
    options: >-
      --health-cmd "redis-cli ping"
      --health-interval 10s
      --health-timeout 5s
      --health-retries 5

  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:6.8.1
    ports:
    - 9200:9200
    options: >-
      --health-cmd "curl http://localhost:9200/_cluster/health"
      --health-interval 10s
      --health-timeout 5s
      --health-retries 10
      
runs:
  using: "composite"

  steps:
    - name: Checkout repo
      uses: actions/checkout@v2

    - name: Restore Ruby Cache
      uses: ruby/setup-ruby@v1
      with:
        ruby-version: 2.7.4 
        bundler-cache: true 

    - name: Setup DB 
      run: bundle exec rake db:setup 
      shell: bash

    - name: RSpec - model tests
      run: bundle exec rake spec:${{ inputs.spec }}
      shell: bash

And I would want to call it like this from my main workflow yml:

jobs:
  setup_and_lint:
    [...]

  rspec_tests_example1:
    name: RSpec Tests (example1)
    runs-on: ubuntu-latest
    needs: setup_and_lint
    steps:
      - name: Checkout repo
        uses: actions/checkout@v2

      - name: RSpec Tests
        uses: ./.github/actions/rspec_test
        with:
          spec: example1

  rspec_tests_example2:
    name: RSpec Tests (example2)
    runs-on: ubuntu-latest
    needs: setup_and_lint
    steps:
      - name: Checkout repo
        uses: actions/checkout@v2

      - name: RSpec Tests
        uses: ./.github/actions/rspec_test
        with:
          spec: example2

My expectation would be that each rspec_tests_exampleX job would call the composite action, and as the 3 services are defined in that action.yml, the service containers should initialise.

Unfortunately that doesn’t seem to happen, it’s like the services block is simply skipped or not recognised, despite not throwing any errors. Thus when my rspec_tests_exampleX jobs run, they error as there is nothing running at the ports.

I am able to get this to work by defining the services in the main workflow every single time, eg:

  rspec_tests_models:
    name: RSpec Tests (models)
    runs-on: ubuntu-latest
    needs: setup_and_lint
    services:
      postgres:
        image: postgres:13.3
        env:
          POSTGRES_USER: example
          POSTGRES_DB: example
          POSTGRES_PASSWORD: "example"
        ports: ["5432:5432"]
        options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
      
      redis:
        image: redis 
        ports:
          - 6379:6379
        options: >-
          --health-cmd "redis-cli ping"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

      elasticsearch:
        image: docker.elastic.co/elasticsearch/elasticsearch:6.8.1
        ports:
        - 9200:9200
        options: >-
          --health-cmd "curl http://localhost:9200/_cluster/health"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 10
    steps:
      - name: Checkout repo
        uses: actions/checkout@v2

      - name: RSpec Tests
        uses: ./.github/actions/rspec_test
        with:
          spec: models

However this means an extra duplicated 29 lines of code for every Spec job.

I have also tried an approach using a matrix, however the run time for the tests was far too long due to the way matrix steps run vs separate parallel jobs.

So: Are Service Container definitions not supported in composite actions? Or am I missing something/doing something silly?

Note: I since realised that the reason this doesn’t work is likely because Service Container Initialisation usually takes place as the job is starting (hence they are usually defined at the job level)

So I would argue that my trying to define them in the Composite action should result in an error, or it would be great for there to be a way to do this properly.

I have raised an issue in GitHub as well: Service Containers can be defined in Composite Action but don't initialise · Issue #1526 · actions/runner · GitHub