Answer
Jobs and Steps in GitHub Actions
Jobs — Parallel Units of Work
By default, all jobs run in parallel. Each job gets its own fresh runner.
YAML
name: CI Pipeline
on: push
jobs:
build: # ← Job 1
runs-on: ubuntu-latest
steps:
- run: echo "Building..."
test: # ← Job 2 (runs IN PARALLEL with build by default)
runs-on: ubuntu-latest
steps:
- run: echo "Testing..."
lint: # ← Job 3 (also runs in parallel)
runs-on: ubuntu-latest
steps:
- run: echo "Linting..."
needs: — Job Dependencies (Sequential)
YAML
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: mvn clean package -DskipTests
test:
needs: build # ← waits for build to succeed
runs-on: ubuntu-latest
steps:
- run: mvn test
deploy:
needs: [build, test] # ← waits for BOTH build AND test
runs-on: ubuntu-latest
steps:
- run: ./deploy.sh
notify:
needs: deploy # ← runs after deploy
if: always() # ← runs even if deploy failed
runs-on: ubuntu-latest
steps:
- run: echo "Sending notification..."
Steps — Sequential Tasks Inside a Job
Steps run one after another in the order defined. If a step fails, subsequent steps are skipped by default.
YAML
jobs:
test:
runs-on: ubuntu-latest
steps:
# Step 1: Checkout repo
- name: Checkout code
uses: actions/checkout@v4
# Step 2: Set up Java
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
# Step 3: Cache Maven dependencies
- name: Cache Maven packages
uses: actions/cache@v4
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
# Step 4: Run tests
- name: Run TestNG tests
run: mvn test -Dbrowser=chrome -Denv=staging
# Step 5: Always upload report (even if tests fail)
- name: Upload test report
if: always()
uses: actions/upload-artifact@v4
with:
name: test-report
path: target/surefire-reports/
Step Conditions (if:)
YAML
steps:
- name: Run tests
run: mvn test
- name: Upload on failure only
if: failure() # only if previous step failed
uses: actions/upload-artifact@v4
with:
name: failure-screenshots
path: screenshots/
- name: Deploy on main branch only
if: github.ref == 'refs/heads/main' && success()
run: ./deploy.sh
- name: Always notify
if: always() # success OR failure
run: echo "Done"
Pass Data Between Steps Using outputs
YAML
jobs:
build:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.get-version.outputs.version }}
steps:
- id: get-version
run: |
VERSION=$(mvn help:evaluate -q -Dexpression=project.version -DforceStdout)
echo "version=$VERSION" >> $GITHUB_OUTPUT
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- run: echo "Deploying version ${{ needs.build.outputs.version }}"
