# 从 CircleCI 迁移到 GitHub Actions

GitHub Actions和 CircleCI 在配置中共享多个相似之处，这使得迁移到GitHub Actions相对简单。

## 简介

CircleCI 和 GitHub Actions 两者都允许你创建可自动生成、测试、发布、发布和部署代码的工作流。 CircleCI 并在 GitHub Actions 工作流配置中共享一些相似之处：

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

有关详细信息，请参阅“[了解GitHub Actions](/zh/actions/get-started/understand-github-actions)”。

## 主要差异

从 CircleCI 迁移时，考虑以下差异：

* CircleCI 的自动测试并行性根据用户指定的规则或历史计时信息自动对测试进行分组。 此功能不内置于 GitHub Actions.
* 在 Docker 容器中执行的操作对权限问题很敏感，因为容器具有不同的用户映射。 可以通过不使用 Dockerfile 中的 `USER` 说明来避免其中许多问题。 有关 GitHub 托管的运行器上的 Docker 文件系统的详细信息，请参阅 [GitHub 托管的运行器参考](/zh/actions/reference/runners/github-hosted-runners#docker-container-filesystem)。

## 迁移工作流程和作业

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

CircleCI 以及 GitHub Actions 使用类似语法在配置文件中配置 `jobs` 。 如果你在 CircleCI 工作流中使用 `requires` 配置了作业之间的任何依赖关系，则可以使用等效的 GitHub Actions`needs` 语法。 有关详细信息，请参阅“[GitHub Actions 的工作流语法](/zh/actions/reference/workflows-and-actions/workflow-syntax#jobsjob_idneeds)”。

## 将 orbs 迁移到操作

CircleCI 和 GitHub Actions 都提供了一种在工作流中重用和共享任务的机制。 CircleCI 使用称为 orbs 的概念，这些概念以 YAML 编写，用于提供人们可以在工作流程中重复使用的任务。
GitHub Actions 具有功能强大的灵活可重用组件，称为操作，可以使用 JavaScript 文件或 Docker 映像进行生成。 您可以通过编写自定义代码来创建操作，以任何您喜欢的方式与您的存储库交互，包括将其与 GitHub 的 API 以及任何公开可用的第三方 API 集成。 例如，操作可以发布 npm 模块、在创建紧急问题时发送短信提醒，或者部署可用于生产的代码。 有关详细信息，请参阅“[重用自动化](/zh/actions/how-tos/reuse-automations)”。

CircleCI 可以使用 YAML 锚点和别名来重复使用工作流程的组件。
GitHub Actions 支持 YAML 锚点和别名，以实现复用，还提供矩阵功能来运行不同配置的作业。 有关矩阵的详细信息，请参阅“[在工作流中运行作业变体](/zh/actions/how-tos/write-workflows/choose-what-workflows-do/run-job-variations)”。

## 使用 Docker 映像

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

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

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

有关 Docker 文件系统的详细信息，请参阅“[GitHub 托管的运行器参考](/zh/actions/reference/runners/github-hosted-runners#docker-container-filesystem)”。

若要详细了解 GitHub 托管的运行器映像中可用的工具和包，请参阅“[GitHub 托管的运行程序](/zh/actions/concepts/runners/github-hosted-runners#preinstalled-software-for-github-owned-images)”。

## 使用变量和密码

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

有关详细信息，请参阅 [变量参考](/zh/actions/reference/workflows-and-actions/variables#default-environment-variables) 和 [在 GitHub Actions 中使用机密](/zh/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets)。

## 缓存

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

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

### CircleCI 的缓存语法

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

### 用于缓存的GitHub Actions语法

```yaml
- 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 层缓存（DLC）对应的功能。

## 在作业之间保持数据

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

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

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

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

...

- attach_workspace:
    at: /tmp/workspace
```

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

```yaml
- 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
```

有关详细信息，请参阅“[使用工作流工件存储和共享数据](/zh/actions/tutorials/store-and-share-data)”。

## 使用数据库和服务容器

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

在 CircleCI 中，config.yaml 中列出的第一个映像是用于运行命令的主要映像。
GitHub Actions 使用明确分区：将 `container` 用作主容器，并在 `services` 中列出其他容器。

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

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

```yaml
---
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语法

<!-- markdownlint-disable search-replace -->

```yaml
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
```

<!-- markdownlint-enable search-replace -->

有关详细信息，请参阅“[与 Docker 服务容器通信](/zh/actions/tutorials/use-containerized-services/use-docker-service-containers)”。

## 完整的示例

下面是一个真实的示例。 左边显示用于 *thoughtbot/administrator* 存储库的实际 CircleCI [config.yml](https://github.com/thoughtbot/administrate)。 右侧显示 GitHub Actions 的对应项。

### CircleCI 的完整示例

```yaml
---
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的完整示例

```yaml
# 此工作流使用未经 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@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
```