name: Elixir on: workflow_call: # Cancel old workflow runs if new code is pushed concurrency: group: "elixir-${{ github.workflow }}-${{ github.ref }}" cancel-in-progress: true jobs: unit-test: runs-on: ubuntu-22.04-firezone-4c defaults: run: working-directory: ./elixir env: MIX_ENV: test POSTGRES_HOST: localhost GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} services: postgres: image: postgres:15.2 ports: - 5432:5432 env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - uses: erlef/setup-beam@v1 id: setup-beam with: otp-version: "26.1.1" elixir-version: "1.15.6" - uses: actions/checkout@v4 - uses: actions/cache/restore@v3 name: Restore Elixir Deps Cache env: cache-name: cache-elixir-deps-${{ env.MIX_ENV }} with: path: elixir/deps key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }} restore-keys: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }} ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}- - uses: actions/cache/restore@v3 name: Restore Elixir Build Cache env: cache-name: cache-elixir-build-${{ env.MIX_ENV }} with: path: elixir/_build key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }} restore-keys: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }} ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}- - name: Install Dependencies run: mix deps.get --only $MIX_ENV - name: Compile Dependencies run: mix deps.compile --skip-umbrella-children - name: Compile Application run: mix compile --warnings-as-errors - name: Setup Database run: | mix ecto.create mix ecto.migrate - name: Run Tests env: E2E_MAX_WAIT_SECONDS: 20 run: | mix test --warnings-as-errors - name: Test Report uses: dorny/test-reporter@v1 if: ${{ github.event.pull_request.head.repo.full_name == github.repository && (success() || failure()) }} with: name: Elixir Unit Test Report path: elixir/_build/test/lib/*/test-junit-report.xml reporter: java-junit - uses: actions/cache/save@v3 if: ${{ github.ref == 'refs/heads/main' }} name: Save Elixir Deps Cache env: cache-name: cache-elixir-deps-${{ env.MIX_ENV }} with: path: elixir/deps key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }} - uses: actions/cache/save@v3 if: ${{ github.ref == 'refs/heads/main' }} name: Save Elixir Build Cache env: cache-name: cache-elixir-build-${{ env.MIX_ENV }} with: path: elixir/_build key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }} type-check: runs-on: ubuntu-latest defaults: run: working-directory: ./elixir env: MIX_ENV: dev GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: erlef/setup-beam@v1 id: setup-beam with: otp-version: "26.1.1" elixir-version: "1.15.6" - uses: actions/checkout@v4 - uses: actions/cache/restore@v3 name: Restore Elixir Deps Cache env: cache-name: cache-elixir-deps-${{ env.MIX_ENV }} with: path: elixir/deps key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }} restore-keys: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }} ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}- - uses: actions/cache/restore@v3 name: Restore Elixir Build Cache env: cache-name: cache-elixir-build-${{ env.MIX_ENV }} with: path: elixir/_build key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }} restore-keys: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }} ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}- - name: Install Dependencies run: mix deps.get --only $MIX_ENV - name: Compile Dependencies run: mix deps.compile --skip-umbrella-children - name: Compile Application run: mix compile # Don't cache PLTs based on mix.lock hash, as Dialyzer can incrementally update even old ones # Cache key based on Elixir & Erlang version (also useful when running in matrix) - uses: actions/cache/restore@v3 name: Restore PLT cache id: plt_cache env: cache-name: cache-erlang-plt-${{ env.MIX_ENV }} with: key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-plt path: elixir/priv/plts - name: Create PLTs if: steps.plt_cache.outputs.cache-hit != 'true' run: mix dialyzer --plt - name: Run Dialyzer run: mix dialyzer --format dialyxir - uses: actions/cache/save@v3 if: ${{ github.ref == 'refs/heads/main' }} name: Save Elixir Deps Cache env: cache-name: cache-elixir-deps-${{ env.MIX_ENV }} with: path: elixir/deps key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }} - uses: actions/cache/save@v3 if: ${{ github.ref == 'refs/heads/main' }} name: Save Elixir Build Cache env: cache-name: cache-elixir-build-${{ env.MIX_ENV }} with: path: elixir/_build key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }} - uses: actions/cache/save@v3 if: ${{ github.ref == 'refs/heads/main' }} name: Save PLT cache env: cache-name: cache-erlang-plt-${{ env.MIX_ENV }} with: key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-plt path: elixir/priv/plts static-analysis: runs-on: ubuntu-latest defaults: run: working-directory: ./elixir env: MIX_ENV: test GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: erlef/setup-beam@v1 id: setup-beam with: otp-version: "26.1.1" elixir-version: "1.15.6" - uses: actions/checkout@v4 - uses: actions/cache/restore@v3 name: Restore Elixir Deps Cache env: cache-name: cache-elixir-deps-${{ env.MIX_ENV }} with: path: elixir/deps key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }} restore-keys: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }} ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}- - uses: actions/cache/restore@v3 name: Restore Elixir Build Cache env: cache-name: cache-elixir-build-${{ env.MIX_ENV }} with: path: elixir/_build key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }} restore-keys: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }} ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}- - name: Install Dependencies run: mix deps.get --only $MIX_ENV - name: Compile Dependencies run: mix deps.compile --skip-umbrella-children - name: Compile Application run: mix compile --force --warnings-as-errors - name: Check For Retired Packages run: mix hex.audit - name: Check For Vulnerable Packages run: mix deps.audit - name: Run Sobelow vulnerability scanner for web app working-directory: ./elixir/apps/web run: mix sobelow --skip - name: Check Formatting run: mix format --check-formatted - name: Run Credo run: mix credo --strict - uses: actions/cache/save@v3 if: ${{ github.ref == 'refs/heads/main' }} name: Save Elixir Deps Cache env: cache-name: cache-elixir-deps-${{ env.MIX_ENV }} with: path: elixir/deps key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }} - uses: actions/cache/save@v3 if: ${{ github.ref == 'refs/heads/main' }} name: Save Elixir Build Cache env: cache-name: cache-elixir-build-${{ env.MIX_ENV }} with: path: elixir/_build key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }} migrations-and-seed-test: runs-on: ubuntu-latest defaults: run: working-directory: ./elixir env: MIX_ENV: dev POSTGRES_HOST: localhost GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} MAIN_BRANCH: main services: postgres: image: postgres:15.2 ports: - 5432:5432 env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - uses: actions/checkout@v4 - name: Install package dependencies run: | sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' wget -qO- https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo tee /etc/apt/trusted.gpg.d/pgdg.asc &>/dev/null sudo apt update sudo apt-get install -q -y postgresql-client - uses: erlef/setup-beam@v1 id: setup-beam with: otp-version: "26.1.1" elixir-version: "1.15.6" - uses: actions/cache/restore@v3 name: Restore Elixir Deps Cache env: cache-name: cache-elixir-deps-${{ env.MIX_ENV }} with: path: elixir/deps key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }} restore-keys: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }} ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}- - uses: actions/cache/restore@v3 name: Restore Elixir Build Cache env: cache-name: cache-elixir-build-${{ env.MIX_ENV }} with: path: elixir/_build key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }} restore-keys: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }} ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}- - name: Install Dependencies run: mix deps.get --only $MIX_ENV - name: Compile run: mix compile - name: Download main branch DB dump id: download-artifact uses: dawidd6/action-download-artifact@v2 if: ${{ !contains(github.ref, env.MAIN_BRANCH) }} with: branch: ${{ env.MAIN_BRANCH }} name: db-dump path: elixir/apps/domain/priv/repo/ search_artifacts: false workflow: ci.yml workflow_conclusion: completed if_no_artifact_found: fail - name: Create Database run: | mix ecto.create - name: Restore DB dump if: ${{ !contains(github.ref, env.MAIN_BRANCH) }} env: PGPASSWORD: postgres run: | mix ecto.load - name: Run new migrations run: | mix ecto.migrate - name: Dump DB if: contains(github.ref, env.MAIN_BRANCH) env: PGPASSWORD: postgres run: | pg_dump firezone_dev \ -U postgres -h localhost \ --file apps/domain/priv/repo/structure.sql \ --no-acl \ --no-owner - name: Upload main branch DB dump if: contains(github.ref, env.MAIN_BRANCH) uses: actions/upload-artifact@v3 with: name: db-dump path: elixir/apps/domain/priv/repo/structure.sql - name: Run Seed run: mix ecto.seed - uses: actions/cache/save@v3 if: ${{ github.ref == 'refs/heads/main' }} name: Save Elixir Deps Cache env: cache-name: cache-elixir-deps-${{ env.MIX_ENV }} with: path: elixir/deps key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }} - uses: actions/cache/save@v3 if: ${{ github.ref == 'refs/heads/main' }} name: Save Elixir Build Cache env: cache-name: cache-elixir-build-${{ env.MIX_ENV }} with: path: elixir/_build key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }} acceptance-test: runs-on: ubuntu-22.04-firezone-4c defaults: run: working-directory: ./elixir env: MIX_ENV: test POSTGRES_HOST: localhost GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} MIX_TEST_PARTITIONS: 1 strategy: fail-fast: false matrix: MIX_TEST_PARTITION: [1] services: postgres: image: postgres:15.2 ports: - 5432:5432 env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 vault: image: vault:1.12.2 env: VAULT_ADDR: "http://127.0.0.1:8200" VAULT_DEV_ROOT_TOKEN_ID: "firezone" ports: - 8200:8200/tcp options: --cap-add=IPC_LOCK steps: - uses: nanasess/setup-chromedriver@v2 - uses: erlef/setup-beam@v1 id: setup-beam with: otp-version: "26.1.1" elixir-version: "1.15.6" - uses: actions/setup-node@v3 with: node-version: 18 - uses: pnpm/action-setup@v2 with: version: 8 - uses: actions/checkout@v4 - uses: actions/cache/restore@v3 name: Restore Elixir Deps Cache env: cache-name: cache-elixir-deps-${{ env.MIX_ENV }} with: path: elixir/deps key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }} restore-keys: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }} ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}- - uses: actions/cache/restore@v3 name: Restore Elixir Build Cache env: cache-name: cache-elixir-build-${{ env.MIX_ENV }} with: path: elixir/_build key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }} restore-keys: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }} ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}- - uses: actions/cache/restore@v3 name: pnpm Deps Cache env: cache-name: cache-pnpm-build-${{ env.MIX_ENV }} with: path: elixir/apps/web/assets/node_modules key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/pnpm-lock.yaml') }} - uses: actions/cache/restore@v3 name: Assets Cache env: cache-name: cache-assets-build-${{ env.MIX_ENV }} with: path: elixir/apps/web/priv/static/dist key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/pnpm-lock.yaml') }} restore-keys: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/pnpm-lock.yaml') }} ${{ runner.os }}-${{ env.cache-name }}- - run: | export DISPLAY=:99 chromedriver --url-base=/wd/hub & sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 & - name: Install Dependencies run: mix deps.get --only $MIX_ENV - name: Compile Dependencies run: mix deps.compile --skip-umbrella-children - name: Compile Application run: mix compile - name: Install Front-End Dependencies run: | cd apps/web mix assets.setup - name: Build Assets run: | cd apps/web mix assets.build - name: Setup Database run: | mix ecto.create mix ecto.migrate - name: Run Acceptance Tests env: MIX_TEST_PARTITION: ${{ matrix.MIX_TEST_PARTITION }} E2E_MAX_WAIT_SECONDS: 5 run: | mix test --only acceptance:true \ --partitions=${{ env.MIX_TEST_PARTITIONS }} \ --no-compile \ --no-archives-check \ --no-deps-check \ || pkill -f chromedriver \ || mix test --only acceptance:true --failed \ || pkill -f chromedriver \ || mix test --only acceptance:true --failed - name: Save Screenshots if: ${{ github.event.pull_request.head.repo.full_name == github.repository && always() }} uses: actions/upload-artifact@v3 with: name: screenshots path: elixir/apps/web/screenshots - name: Test Report uses: dorny/test-reporter@v1 if: ${{ github.event.pull_request.head.repo.full_name == github.repository && (success() || failure()) }} with: name: Elixir Acceptance Test Report path: elixir/_build/test/lib/*/test-junit-report.xml reporter: java-junit - uses: actions/cache/save@v3 if: ${{ github.ref == 'refs/heads/main' }} name: Save Elixir Deps Cache env: cache-name: cache-elixir-deps-${{ env.MIX_ENV }} with: path: elixir/deps key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }} - uses: actions/cache/save@v3 if: ${{ github.ref == 'refs/heads/main' }} name: Save Elixir Build Cache env: cache-name: cache-elixir-build-${{ env.MIX_ENV }} with: path: elixir/_build key: ${{ runner.os }}-${{ steps.setup-beam.outputs.elixir-version }}-${{ env.cache-name }}-${{ hashFiles('**/elixir/mix.lock') }} - uses: actions/cache/save@v3 if: ${{ github.ref == 'refs/heads/main' }} name: Save pnpm Deps Cache env: cache-name: cache-pnpm-build-${{ env.MIX_ENV }} with: path: elixir/apps/web/assets/node_modules key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/pnpm-lock.yaml') }} - uses: actions/cache/save@v3 if: ${{ github.ref == 'refs/heads/main' }} name: Save Assets Cache env: cache-name: cache-assets-build-${{ env.MIX_ENV }} with: path: elixir/apps/web/priv/static/dist key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/pnpm-lock.yaml') }}