GitHub Actions cơ bản: workflow, job và step

1. Tổng quan

GitHub Actions là công cụ CI/CD tích hợp sẵn trong GitHub, giúp bạn tự động hoá build, test, deploy mà không cần server CI riêng. Bài này giải thích ba khái niệm cốt lõi: workflow, job và step, kèm ví dụ cụ thể cho một project Spring Boot dùng Maven.

Đọc xong, bạn sẽ viết được một file workflow chạy test tự động mỗi khi push code, hiểu cách các job chạy song song hoặc tuần tự, và biết cách debug khi workflow fail.

2. Workflow là gì?

Workflow là một file YAML nằm trong thư mục .github/workflows/ của repository. Mỗi workflow định nghĩa khi nào chạy (trigger) và làm gì (jobs).

# .github/workflows/ci.yml
name: CI Pipeline

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Set up JDK 21
        uses: actions/setup-java@v4
        with:
          java-version: '21'
          distribution: 'temurin'
      - name: Build with Maven
        run: mvn -B package

Giải thích:

  • name ở đầu file là tên hiển thị trên tab Actions của GitHub.
  • on khai báo trigger: ở đây workflow chạy khi có push hoặc pull_request vào branch main.
  • jobs.build là một job duy nhất tên build, chạy trên máy ảo ubuntu-latest.

Lưu ý: File phải nằm đúng đường dẫn .github/workflows/*.yml, sai thư mục GitHub sẽ không nhận diện workflow.

3. Job: đơn vị thực thi độc lập

Một workflow có thể có nhiều job. Mỗi job chạy trên một runner (máy ảo) riêng, mặc định các job chạy song song trừ khi bạn khai báo needs.

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: mvn -B test

  deploy:
    runs-on: ubuntu-latest
    needs: test
    steps:
      - run: echo "Deploying application..."

Giải thích:

  • test và deploy là hai job riêng biệt, mỗi job có môi trường runner sạch, không chia sẻ file hệ thống với nhau.
  • needs: test buộc deploy phải chờ test thành công rồi mới chạy — tạo quan hệ tuần tự.
  • Nếu không có needs, GitHub Actions sẽ chạy test và deploy cùng lúc.

3.1. Chia sẻ dữ liệu giữa các job

Vì mỗi job có runner riêng, muốn truyền file giữa job này sang job khác bạn cần dùng artifacts.

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: mvn -B package
      - uses: actions/upload-artifact@v4
        with:
          name: app-jar
          path: target/*.jar

  deploy:
    runs-on: ubuntu-latest
    needs: build
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: app-jar

Giải thích:

  • upload-artifact lưu file .jar từ job build lên GitHub storage tạm thời.
  • download-artifact trong job deploy lấy lại file đó về runner mới.
  • Đây là cách duy nhất để truyền dữ liệu giữa job (không dùng được biến môi trường thông thường).

4. Step: đơn vị thao tác nhỏ nhất

Step là một hành động cụ thể trong job, chạy tuần tự từ trên xuống. Step có hai loại chính: dùng uses (gọi action có sẵn) hoặc dùng run (chạy lệnh shell).

steps:
  - name: Checkout code
    uses: actions/checkout@v4

  - name: Cache Maven dependencies
    uses: actions/cache@v4
    with:
      path: ~/.m2/repository
      key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}

  - name: Run tests
    run: mvn -B test

  - name: Print result
    run: echo "Test step finished with status ${{ job.status }}"

Giải thích:

  • Step Checkout code dùng action có sẵn actions/checkout để lấy source code vào runner.
  • Step Cache Maven dependencies cache thư mục ~/.m2/repository, giúp build sau nhanh hơn vì không phải tải lại dependency.
  • key của cache dựa trên hash của pom.xml — khi pom.xml đổi, cache cũ bị bỏ, cache mới được tạo.
  • Step cuối dùng run để chạy lệnh shell thông thường, có thể tham chiếu context như ${{ job.status }}.

5. Biến môi trường và secrets

Bạn thường cần truyền config nhạy cảm (API key, token) vào workflow mà không hard-code trong file YAML.

jobs:
  deploy:
    runs-on: ubuntu-latest
    env:
      SPRING_PROFILES_ACTIVE: prod
    steps:
      - uses: actions/checkout@v4
      - name: Deploy to server
        env:
          DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
        run: |
          echo "Deploying with profile $SPRING_PROFILES_ACTIVE"
          curl -H "Authorization: Bearer $DEPLOY_TOKEN" https://api.example.com/deploy

Giải thích:

  • env ở cấp job áp dụng cho mọi step trong job đó (SPRING_PROFILES_ACTIVE).
  • env ở cấp step chỉ áp dụng riêng cho step đó (DEPLOY_TOKEN).
  • secrets.DEPLOY_TOKEN được lấy từ Settings → Secrets and variables → Actions của repository, không bao giờ hiện giá trị thật trong log.

Lưu ý: Không bao giờ in trực tiếp giá trị secret bằng echo $DEPLOY_TOKEN ra log — GitHub sẽ tự động mask nhưng tốt nhất vẫn tránh in secret.

6. Matrix: chạy nhiều phiên bản cùng lúc

Khi cần test trên nhiều phiên bản Java hoặc OS, matrix giúp bạn không phải viết lặp job.

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        java: [17, 21]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with:
          java-version: ${{ matrix.java }}
          distribution: 'temurin'
      - run: mvn -B test

Giải thích:

  • strategy.matrix.java khai báo danh sách giá trị [17, 21].
  • GitHub Actions tự động tạo 2 job song song, mỗi job chạy với một phiên bản Java tương ứng.
  • Cách này tiết kiệm công viết YAML lặp, phù hợp khi cần đảm bảo code chạy tốt trên nhiều môi trường.

7. Kiểm tra workflow bằng test đơn giản

Workflow không test trực tiếp bằng JUnit, nhưng bạn nên đảm bảo bước mvn test trong workflow thực sự chạy đúng test suite của project. Ví dụ một test đơn giản cho OrderService mà CI sẽ chạy:

class OrderServiceTest {

    private final OrderService orderService = new OrderService();

    @Test
    void shouldCalculateTotalPriceCorrectly() {
        Order order = new Order("ORD-001", List.of(
                new OrderItem("Keyboard", 2, BigDecimal.valueOf(50)),
                new OrderItem("Mouse", 1, BigDecimal.valueOf(20))
        ));

        BigDecimal total = orderService.calculateTotal(order);

        assertThat(total).isEqualByComparingTo(BigDecimal.valueOf(120));
    }
}

Giải thích:

  • Test này nằm trong src/test/java, sẽ được mvn -B test trong step của workflow chạy tự động.
  • Nếu test fail, step Run tests báo lỗi (exit code khác 0), job dừng lại và job phụ thuộc (needs) không chạy tiếp.
  • Đây là lý do CI hữu ích: code lỗi bị chặn trước khi merge vào main.

Kết quả khi chạy trên GitHub Actions UI (tab Actions):

✓ Checkout code
✓ Set up JDK 21
✓ Build with Maven
✓ Run tests (shouldCalculateTotalPriceCorrectly PASSED)

8. Lỗi thường gặp

  • Sai indent YAML: GitHub Actions dùng YAML, thụt lề sai 1 khoảng trắng khiến job hoặc step không được nhận diện, thường báo lỗi “Invalid workflow file”.
  • Quên runs-on: mỗi job bắt buộc phải có runs-on, thiếu sẽ khiến workflow fail ngay khi parse.
  • Dùng biến môi trường job này ở job khác: env và output biến thường không tự động truyền qua job, phải dùng outputs hoặc artifact.
  • Cache key không đổi khi dependency đổi: nếu key cache quá chung (ví dụ chỉ maven-cache), bạn sẽ dùng nhầm cache cũ chứa dependency lỗi thời.
  • Chạy workflow nặng trên mọi push: nếu không giới hạn paths hoặc branches, mỗi commit nhỏ (sửa README) cũng trigger toàn bộ pipeline build + test, tốn phút chạy Actions (có giới hạn miễn phí).

Lưu ý: Nếu project chỉ cần build đơn giản và không có nhiều môi trường, không cần dùng matrix hay artifact — thêm phức tạp không cần thiết sẽ làm workflow khó maintain.

9. Kết luận

Workflow là file YAML định nghĩa toàn bộ pipeline, job là các khối thực thi độc lập chạy trên runner riêng, và step là từng hành động cụ thể bên trong job. Hiểu rõ ba tầng này giúp bạn tổ chức pipeline CI/CD rõ ràng: job nào chạy song song, job nào phải chờ nhau qua needs, và dùng artifact khi cần chia sẻ dữ liệu.

Khi mới bắt đầu, bạn nên giữ workflow đơn giản (một job, vài step cơ bản: checkout, setup Java, test). Khi project lớn hơn, hãy thêm matrix, cache, và tách build/test/deploy thành các job riêng để dễ quản lý và debug.

github-actionsci-cddevopsjavaautomation