Skip to main content

从 CircleCI 迁移到 GitHub Actions

GitHub Actions 和 CircleCI 在配置上具有若干相似之处,这使得迁移到 GitHub Actions 相对简单。

Note

GitHub Enterprise Server 目前不支持 GitHub 托管的运行器。 可以在 GitHub public roadmap 上查看有关未来支持计划的更多信息。

简介

CircleCI 和 GitHub Actions 都允许您创建能自动构建、测试、发布、发行和部署代码的工作流程。 CircleCI 和 GitHub Actions 的工作流程配置有一些相似之处:

  • 工作流程配置文件以 YAML 编写并存储在仓库中。
  • 工作流程包括一项或多项作业。
  • 作业包括一个或多个步骤或单个命令。
  • 步骤或任务可以重复使用并与社区共享。

有关详细信息,请参阅“了解 GitHub Actions”。

主要差异

从 CircleCI 迁移时,考虑以下差异:

  • CircleCI 的自动测试并行性根据用户指定的规则或历史计时信息自动对测试进行分组。 此功能未内置于 GitHub Actions。
  • 在 Docker 容器中执行的操作对权限问题很敏感,因为容器具有不同的用户映射。 可以通过不使用 Dockerfile 中的 USER 说明来避免其中许多问题。 要详细了解在 GitHub Enterprise Server 托管的运行器上的 Docker 文件系统,请参阅“使用 GitHub 托管的运行器”。

迁移工作流程和作业

CircleCI 在 config.yml 文件中定义 workflows,可以通过它配置多个工作流。 GitHub Enterprise Server 要求每个工作流有一个工作流文件,因此不要求你声明 workflows。 需要为 config.yml 中配置的每个工作流创建一个新的工作流文件。

CircleCI 和 GitHub Actions 在配置文件中使用相似的语法配置 jobs。 如果在 CircleCI 工作流程中使用 requires 配置作业之间的任何依赖项,那么可以使用等效的 GitHub Actions needs 语法。 有关详细信息,请参阅“GitHub Actions 的工作流语法”。

将 orbs 迁移到操作

CircleCI 和 GitHub Actions 都提供在工作流程中重复使用和共享任务的机制。 CircleCI 使用以 YAML 编写的概念 orbs 来提供人们可以在工作流程中重复使用的任务。 GitHub Actions 具有强大而灵活的可重复使用的组件,称为“操作”,您可以使用 JavaScript 文件或 Docker 映像来构建操作。 您可以编写自定义代码来创建操作,以您喜欢的方式与仓库交互,包括使用 GitHub Enterprise Server 的 API 以及任何公开的第三方 API 进行交互。 例如,操作可以发布 npm 模块、在创建紧急问题时发送短信提醒,或者部署可用于生产的代码。 有关详细信息,请参阅“共享自动化”。

CircleCI 可以使用 YAML 锚点和别名来重复使用工作流程的组件。 GitHub Actions 支持对于重复使用矩阵的最常见需求。 有关矩阵的详细信息,请参阅“在工作流中运行作业的变体”。

使用 Docker 映像

CircleCI 和 GitHub Actions 都支持在 Docker 映像中运行步骤。

CircleCI 提供一套具有共同依赖项的预建映像。 这些映像的 USER 设置为 circleci,会导致权限与 GitHub Actions 冲突。

建议在迁移到 GitHub Actions 时不使用 CircleCI 的预建映像。 在许多情况下,您可以使用操作来安装需要的附加依赖项。

有关 Docker 文件系统的详细信息,请参阅“使用 GitHub 托管的运行器”。

若要详细了解 GitHub 托管的运行器映像中可用的工具和包,请参阅“使用 GitHub 托管的运行器”。

使用变量和密码

CircleCI 和 GitHub Actions 支持在配置文件中设置变量,并支持使用 CircleCI 或 GitHub Enterprise Server UI 创建机密。

有关详细信息,请参阅“在变量中存储信息”和“在 GitHub Actions 中使用机密”。

缓存

CircleCI 和 GitHub Actions 提供在配置文件中手动缓存文件的方法。

下面是每个系统的语法示例:

CircleCI 的缓存语法

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

GitHub Actions 的缓存语法

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

GitHub Actions 没有 CircleCI 的 Docker 层缓存(或 DLC)的等效项。

在作业之间保持数据

CircleCI 和 GitHub Actions 提供在作业之间保持数据的机制。

下面是 CircleCI 和 GitHub Actions 配置语法中的示例。

CircleCI 用于在作业之间保留数据的语法

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

...

- attach_workspace:
    at: /tmp/workspace

GitHub Actions 用于在作业之间保留数据的语法

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

...

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

有关详细信息,请参阅“从工作流存储和共享数据”。

使用数据库和服务容器

这两个系统都允许您包括用于数据库、缓存或其他依赖项的其他容器。

在 CircleCI 中,config.yaml 中列出的第一个映像是用于运行命令的主要映像。 GitHub Actions 使用显式部分:使用 container 作为主容器,并在 services 中列出其他容器。

下面是 CircleCI 和 GitHub Actions 配置语法中的示例。

CircleCI 用于使用数据库和服务容器的语法

---
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
...

- attach_workspace:
    at: /tmp/workspace

GitHub Actions 用于使用数据库和服务容器的语法

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@v4
      - 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

有关详细信息,请参阅“关于服务容器”。

完整的示例

下面是一个真实的示例。 左边显示用于 thoughtbot/administrator 存储库的实际 CircleCI config.yml。 右边显示 GitHub Actions 等效项。

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

GitHub Actions 的完整示例

# 此工作流使用未经 GitHub 认证的操作。
# 它们由第三方提供,并受
# 单独的服务条款、隐私政策和支持
# 文档。

# GitHub 建议将操作固定到提交 SHA。
# 若要获取较新版本,需要更新 SHA。
# 还可以引用标记或分支,但该操作可能会更改而不发出警告。

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@v4
      - name: Setup Ruby
        uses: eregon/use-ruby-action@ec02537da5712d66d4d50a0f33b7eb52773b5ed1
        with:
          ruby-version: ${{ matrix.ruby }}
      - name: Cache dependencies
        uses: actions/cache@v3
        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