Can a GitHub Actions job continue after a step fails while still marking the job as failed?

I’m working on a GitHub Actions workflow for my project. I want to run tests and then upload the results as an artifact. But I’m running into a problem.

Here’s what I’m trying to do:

  1. Run tests
  2. Zip and upload test results as an artifact
  3. Mark the job as failed if tests fail

Right now, if the tests pass, everything works fine. But if they fail, the job stops and doesn’t upload the results. I tried using continue-on-error: true, but that makes the whole job pass even when tests fail.

Is there a way to make sure the artifact gets uploaded even if tests fail, but still have the job marked as failed? Here’s a simplified version of my current workflow:

name: Test and Upload
jobs:
  main-job:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Run Tests
      run: ./run-tests.sh
    - name: Upload Results
      uses: actions/upload-artifact@v2
      with:
        name: test-results
        path: test-output

Any ideas on how to achieve this? I’m a bit stuck and could use some help.

I’ve faced a similar challenge in my projects, and I found a solution that might work for you. Instead of using ‘continue-on-error’, you can use a combination of ‘if’ conditions and the ‘failure()’ function.

Here’s how you can modify your workflow:

name: Test and Upload
jobs:
  main-job:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Run Tests
      id: run_tests
      run: ./run-tests.sh
      continue-on-error: true
    - name: Upload Results
      if: always()
      uses: actions/upload-artifact@v2
      with:
        name: test-results
        path: test-output
    - name: Check Test Status
      if: failure() || steps.run_tests.outcome == 'failure'
      run: exit 1

This approach allows the job to continue after test failure, uploads the artifact, and still marks the job as failed. The ‘if: always()’ ensures the upload step runs regardless of test results, while the final step checks for failures and exits with an error if needed. This has worked well for me in maintaining both artifact uploads and accurate job status reporting.

Another approach you might consider is using a custom composite action. I’ve found this method particularly useful for complex scenarios like yours. Here’s a basic outline:

Create a composite action in your repository:

name: 'Test and Upload'
description: 'Run tests and upload results'
runs:
  using: 'composite'
  steps:
    - run: ./run-tests.sh
      shell: bash
    - uses: actions/upload-artifact@v2
      if: always()
      with:
        name: test-results
        path: test-output
    - run: exit 1
      if: failure()
      shell: bash

Then in your main workflow:

steps:
  - uses: actions/checkout@v2
  - uses: ./.github/actions/test-and-upload

This encapsulates the logic, keeps your main workflow clean, and achieves your goal of uploading results while still marking the job as failed when tests fail. It’s been a reliable solution in my projects.

You might want to consider using a composite step for this scenario. Here’s an approach I’ve found effective:

Create a separate action that encapsulates both running the tests and uploading results. This action can handle the logic of continuing after a failure while still reporting the correct status.

In your main workflow, you’d use it like this:

steps:
  - uses: actions/checkout@v2
  - uses: ./.github/actions/test-and-upload
    id: test_step
  - name: Check test status
    if: steps.test_step.outputs.test_passed != 'true'
    run: exit 1

This method keeps your main workflow clean and allows for more complex logic in the composite action. It’s been a game-changer for managing similar situations in my projects, offering both flexibility and maintainability.

I’ve encountered this issue in my CI/CD pipelines before. One effective approach I’ve used is to leverage the ‘continue-on-error’ flag combined with a custom shell script. Here’s what worked for me:

steps:
  - uses: actions/checkout@v2
  - name: Run Tests and Upload Results
    run: |
      ./run-tests.sh
      test_exit_code=$?
      zip -r test-results.zip test-output
      exit $test_exit_code
    continue-on-error: true
  - name: Upload Results
    uses: actions/upload-artifact@v2
    if: always()
    with:
      name: test-results
      path: test-results.zip

This approach allows the test script to run, captures its exit code, zips the results, and then exits with the original code. The ‘if: always()’ ensures the upload happens regardless of test outcome. It’s a bit more hands-on, but it gives you precise control over the process and has served me well in complex workflows.

hey, i’ve run into this before. try using the if: always() condition on your upload step. it’ll make sure it runs even if the tests fail. then add a final step to check the test status and fail if needed. something like:

- name: Upload Results
  if: always()
  uses: actions/upload-artifact@v2
  with:
    name: test-results
    path: test-output
- name: Check Test Status
  if: failure()
  run: exit 1

this should do what you want. good luck!