CircleCI에서 GitHub Actions로 마이그레이션하기

CircleCI에서 GitHub Actions로 마이그레이션하기

GitHub Actions와 CircleCI는 구성상 몇 가지 유사점을 공유하기 때문에 GitHub Actions로의 마이그레이션이 비교적 간단할 수 있어요. 이 가이드에서는 두 시스템의 차이점과 마이그레이션 시 유의할 점을 알려드릴게요.

출처: 문서

본문

Introduction

CircleCI와 GitHub Actions는 모두 코드를 자동으로 빌드, 테스트, 게시, 릴리스, 배포하는 워크플로우를 만들 수 있게 해 줘요. CircleCI와 GitHub Actions는 워크플로우 구성에서 몇 가지 유사점을 공유해요.

  • 워크플로우 구성 파일은 YAML로 작성되고 저장소에 저장돼요.
  • 워크플로우에는 하나 이상의 job이 포함돼요.
  • job에는 하나 이상의 단계 또는 개별 명령이 포함돼요.
  • 단계나 작업은 재사용되고 커뮤니티와 공유될 수 있어요.

자세한 내용은 Understanding GitHub Actions 문서를 참고하세요.

Key differences

CircleCI에서 마이그레이션할 때 다음 차이점을 고려하세요.

  • CircleCI의 자동 테스트 병렬화는 사용자 지정 규칙 또는 이전 타이밍 정보에 따라 테스트를 자동으로 그룹화해요. 이 기능은 GitHub Actions에 내장되어 있지 않아요.
  • Docker 컨테이너에서 실행되는 액션은 컨테이너에 사용자 매핑이 다르기 때문에 권한 문제에 민감해요. Dockerfile에서 USER 지시문을 사용하지 않으면 이러한 문제의 대부분을 피할 수 있어요. GitHub 호스팅 러너의 Docker 파일 시스템에 대한 자세한 내용은 GitHub-hosted runners reference 문서를 참고하세요.

Migrating workflows and jobs

CircleCI는 config.yml 파일에서 workflows를 정의해서 둘 이상의 워크플로우를 구성할 수 있게 해 줘요. GitHub는 워크플로우당 하나의 워크플로우 파일을 요구하므로 workflows를 선언할 필요가 없어요. config.yml에 구성된 각 워크플로우에 대해 새 워크플로우 파일을 만들어야 해요.

CircleCI와 GitHub Actions 모두 구성 파일에서 jobs를 유사한 문법으로 구성해요. CircleCI 워크플로우에서 requires를 사용해서 job 간 의존성을 구성했다면, GitHub Actions의 동등한 needs 문법을 사용할 수 있어요. 자세한 내용은 Workflow syntax for GitHub Actions 문서를 참고하세요.

Migrating orbs to actions

CircleCI와 GitHub Actions 모두 워크플로우에서 작업을 재사용하고 공유하는 메커니즘을 제공해요. CircleCI는 YAML로 작성된 orb라는 개념을 사용해서 사람들이 워크플로우에서 재사용할 수 있는 작업을 제공해요. GitHub Actions는 JavaScript 파일이나 Docker 이미지로 빌드하는 강력하고 유연한 재사용 가능 구성 요소인 액션을 보유해요. GitHub의 API와 공개적으로 사용 가능한 제3자 API를 포함해서 원하는 어떤 방식으로든 저장소와 상호작용하는 사용자 지정 코드를 작성해서 액션을 만들 수 있어요. 예를 들어 액션은 npm 모듈을 게시하고, 긴급 이슈가 생성될 때 SMS 알림을 보내고, 프로덕션 준비 완료 코드를 배포할 수 있어요. 자세한 내용은 Reusing automations 문서를 참고하세요.

CircleCI는 YAML 앵커와 별명(aliases)으로 워크플로우 조각을 재사용할 수 있어요. GitHub Actions는 재사용을 위해 YAML 앵커와 별명을 지원하고, 다양한 구성으로 job을 실행하기 위한 매트릭스도 제공해요. 매트릭스에 대한 자세한 내용은 Running variations of jobs in a workflow 문서를 참고하세요.

Using Docker images

CircleCI와 GitHub Actions 모두 Docker 이미지 내부에서 단계를 실행하는 것을 지원해요.

CircleCI는 공통 의존성이 있는 사전 빌드 이미지 세트를 제공해요. 이러한 이미지는 USERcircleci로 설정되어 있어서 GitHub Actions와 권한 충돌을 일으켜요.

GitHub Actions로 마이그레이션할 때 CircleCI의 사전 빌드 이미지에서 벗어나는 것이 좋아요. 대부분의 경우 액션을 사용해서 필요한 추가 의존성을 설치할 수 있어요.

Docker 파일 시스템에 대한 자세한 내용은 GitHub-hosted runners reference 문서를 참고하세요.

GitHub 호스팅 러너 이미지에서 사용할 수 있는 도구와 패키지에 대한 자세한 내용은 GitHub-hosted runners 문서를 참고하세요.

Using variables and secrets

CircleCI와 GitHub Actions는 모두 구성 파일에서 변수를 설정하고 CircleCI 또는 GitHub UI를 사용해서 secret을 만들 수 있어요.

자세한 내용은 Variables referenceUsing secrets in GitHub Actions 문서를 참고하세요.

Caching

CircleCI와 GitHub Actions는 구성 파일에서 파일을 수동으로 캐시하는 방법을 제공해요.

다음은 각 시스템의 문법 예시예요.

CircleCI syntax for caching

- restore_cache:
    keys:
      - v1-npm-deps-{{ checksum "package-lock.json" }}
      - v1-npm-deps-

GitHub Actions syntax for caching

- name: Cache node modules
  uses: actions/cache@v4
  with:
    path: ~/.npm
    key: v1-npm-deps-${{ hashFiles('**/package-lock.json') }}
    restore-keys: v1-npm-deps-

GitHub Actions에는 CircleCI의 Docker Layer Caching(DLC)에 해당하는 기능이 없어요.

Persisting data between jobs

CircleCI와 GitHub Actions 모두 job 간에 데이터를 유지하는 메커니즘을 제공해요.

다음은 CircleCI와 GitHub Actions 구성 문법의 예시예요.

CircleCI syntax for persisting data between jobs

- persist_to_workspace:
    root: workspace
    paths:
      - math-homework.txt

...

- attach_workspace:
    at: /tmp/workspace

GitHub Actions syntax for persisting data between jobs

- name: Upload math result for job 1
  uses: actions/upload-artifact@v4
  with:
    name: homework
    path: math-homework.txt

...

- name: Download math result for job 1
  uses: actions/download-artifact@v5
  with:
    name: homework

자세한 내용은 Store and share data with workflow artifacts 문서를 참고하세요.

Using databases and service containers

두 시스템 모두 데이터베이스, 캐싱 또는 기타 의존성을 위한 추가 컨테이너를 포함시킬 수 있어요.

CircleCI에서 config.yaml에 나열된 첫 번째 이미지는 명령을 실행하는 기본 이미지예요. GitHub Actions는 명시적 섹션을 사용해요: 기본 컨테이너에 container를 사용하고 추가 컨테이너를 services에 나열해요.

다음은 CircleCI와 GitHub Actions 구성 문법의 예시예요.

CircleCI syntax for using databases and service containers

---
version: 2.1

jobs:

  ruby-26:
    docker:
      - image: circleci/ruby:2.6.3-node-browsers-legacy
        environment:
          PGHOST: localhost
          PGUSER: administrate
          RAILS_ENV: test
      - image: postgres:10.1-alpine
        environment:
          POSTGRES_USER: administrate
          POSTGRES_DB: ruby26
          POSTGRES_PASSWORD: ""

    working_directory: ~/administrate

    steps:
      - checkout

      # Bundle install dependencies
      - run: bundle install --path vendor/bundle

      # Wait for DB
      - run: dockerize -wait tcp://localhost:5432 -timeout 1m

      # Setup the environment
      - run: cp .sample.env .env

      # Setup the database
      - run: bundle exec rake db:setup

      # Run the tests
      - run: bundle exec rake

workflows:
  version: 2
  build:
    jobs:
      - ruby-26
...

GitHub Actions syntax for using databases and service containers

name: Containers

on: [push]

jobs:
  build:

    runs-on: ubuntu-latest
    container: circleci/ruby:2.6.3-node-browsers-legacy

    env:
      PGHOST: postgres
      PGUSER: administrate
      RAILS_ENV: test

    services:
      postgres:
        image: postgres:10.1-alpine
        env:
          POSTGRES_USER: administrate
          POSTGRES_DB: ruby25
          POSTGRES_PASSWORD: ""
        ports:
          - 5432:5432
        # Add a health check
        options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5

    steps:
      # This Docker file changes sets USER to circleci instead of using the default user, so we need to update file permissions for this image to work on GH Actions.
      # See https://docs.github.com/actions/using-github-hosted-runners/about-github-hosted-runners#docker-container-filesystem

      - name: Setup file system permissions
        run: sudo chmod -R 777 $GITHUB_WORKSPACE /github /__w/_temp
      - uses: actions/checkout@v6
      - name: Install dependencies
        run: bundle install --path vendor/bundle
      - name: Setup environment configuration
        run: cp .sample.env .env
      - name: Setup database
        run: bundle exec rake db:setup
      - name: Run tests
        run: bundle exec rake

자세한 내용은 Communicating with Docker service containers 문서를 참고하세요.

Complete Example

다음은 실제 세계의 예시예요. 왼쪽은 thoughtbot/administrator 저장소의 실제 CircleCI config.yml이고, 오른쪽은 GitHub Actions 등가물이에요.

Complete example for CircleCI

---
version: 2.1

commands:
  shared_steps:
    steps:
      - checkout

      # Restore Cached Dependencies
      - restore_cache:
          name: Restore bundle cache
          key: administrate-{{ checksum "Gemfile.lock" }}

      # Bundle install dependencies
      - run: bundle install --path vendor/bundle

      # Cache Dependencies
      - save_cache:
          name: Store bundle cache
          key: administrate-{{ checksum "Gemfile.lock" }}
          paths:
            - vendor/bundle

      # Wait for DB
      - run: dockerize -wait tcp://localhost:5432 -timeout 1m

      # Setup the environment
      - run: cp .sample.env .env

      # Setup the database
      - run: bundle exec rake db:setup

      # Run the tests
      - run: bundle exec rake

default_job: &default_job
  working_directory: ~/administrate
  steps:
    - shared_steps
    # Run the tests against multiple versions of Rails
    - run: bundle exec appraisal install
    - run: bundle exec appraisal rake

jobs:
  ruby-25:
    <<: *default_job
    docker:
      - image: circleci/ruby:2.5.0-node-browsers
        environment:
          PGHOST: localhost
          PGUSER: administrate
          RAILS_ENV: test
      - image: postgres:10.1-alpine
        environment:
          POSTGRES_USER: administrate
          POSTGRES_DB: ruby25
          POSTGRES_PASSWORD: ""

  ruby-26:
    <<: *default_job
    docker:
      - image: circleci/ruby:2.6.3-node-browsers-legacy
        environment:
          PGHOST: localhost
          PGUSER: administrate
          RAILS_ENV: test
      - image: postgres:10.1-alpine
        environment:
          POSTGRES_USER: administrate
          POSTGRES_DB: ruby26
          POSTGRES_PASSWORD: ""

workflows:
  version: 2
  multiple-rubies:
    jobs:
      - ruby-26
      - ruby-25

Complete example for GitHub Actions

# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.

# GitHub recommends pinning actions to a commit SHA.
# To get a newer version, you will need to update the SHA.
# You can also reference a tag or branch, but the action may change without warning.

name: Containers

on: [push]

jobs:
  build:

    strategy:
      matrix:
        ruby: ['2.5', '2.6.3']

    runs-on: ubuntu-latest

    env:
      PGHOST: localhost
      PGUSER: administrate
      RAILS_ENV: test

    services:
      postgres:
        image: postgres:10.1-alpine
        env:
          POSTGRES_USER: administrate
          POSTGRES_DB: ruby25
          POSTGRES_PASSWORD: ""
        ports:
          - 5432:5432
        # Add a health check
        options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5

    steps:
      - uses: actions/checkout@v6
      - name: Setup Ruby
        uses: eregon/use-ruby-action@ec02537da5712d66d4d50a0f33b7eb52773b5ed1
        with:
          ruby-version: ${{ matrix.ruby }}
      - name: Cache dependencies
        uses: actions/cache@v4
        with:
          path: vendor/bundle
          key: administrate-${{ matrix.image }}-${{ hashFiles('Gemfile.lock') }}
      - name: Install postgres headers
        run: |
          sudo apt-get update
          sudo apt-get install libpq-dev
      - name: Install dependencies
        run: bundle install --path vendor/bundle
      - name: Setup environment configuration
        run: cp .sample.env .env
      - name: Setup database
        run: bundle exec rake db:setup
      - name: Run tests
        run: bundle exec rake
      - name: Install appraisal
        run: bundle exec appraisal install
      - name: Run appraisal
        run: bundle exec appraisal rake

더 알아보기 (Learn more)