构建 CI/CD 工作流——使用 GitHub Actions 自动部署 WordPress 主题
已发表: 2022-11-17介绍
在现代 Web 开发中,通常需要执行多个步骤来构建代码并将其部署到生产环境中。 对于 WordPress 主题或插件,这可能意味着安装 Composer 和/或 Node.js 依赖项、编译 CSS、转译 JavaScript 并将文件上传到您的服务器。
在本文中,我们将探讨如何使用 GitHub Actions 简化 WordPress 部署过程。 我们将创建一个 GitHub Actions 工作流程,以自动构建 WordPress 主题并将其部署到您的 Pressidium WordPress 站点。
如果您只是点击了工作流程,请滚动到这篇文章的底部并立即开始。 但是,我们鼓励您阅读整篇文章,我们在其中详细解释了一切是如何工作的!
先决条件
- 对 Git 有基本的了解(创建存储库、提交和推送代码、创建分支等)
- 熟悉 GitHub 的界面
Web 开发中的“部署”是什么?
Web 开发中的部署是将更改推送到远程环境,使网站或应用程序可供使用的过程。
有时我们使用术语“部署”来指代一组活动——包括构建、测试和传输文件——而其他时候我们将它用作文件传输的同义词。 在本文中,我们始终区分构建和部署。
有很多方法可以将您网站的文件推送到托管服务提供商。 在我们的例子中,我们将使用安全文件传输协议 (SFTP),顾名思义,它是一种通过安全通道(例如 SSH)传输文件的网络协议。
什么是 GitHub 操作?
GitHub Actions 是一个持续集成和持续交付 (CI/CD) 平台,可让您自动化构建和部署管道。
在接下来的段落中,我们将探讨如何创建 GitHub Actions 工作流程,以利用支持暂存环境的托管服务提供商来构建和部署 WordPress 主题。
暂存是用于测试的预生产环境,它几乎是生产环境的精确复制品。 它试图尽可能地反映实际的生产环境,因此可以在将更改应用到生产环境之前对其进行测试。
如果您已经在使用 Pressidium,则所有计划都免费提供暂存环境。 阅读这篇知识库文章了解更多信息。
什么是 GitHub Actions 工作流程?
工作流是由一个或多个事件触发并运行一个或多个作业的自动化过程。 每个作业包含一个或多个步骤。 最后,每个步骤都可以运行脚本或 GitHub 操作。 一个存储库可以有多个工作流,执行不同的任务。
使用 GitHub Actions 工作流有很多好处。
- 你花更少的时间做手工、劳动密集型、重复性的工作; 更多时间增值
- 通过实施特定的部署过程,更容易在不同环境中保持一致
- 它与您的 GitHub 存储库集成,允许您跟踪更改、访问部署日志等。
- 它是可重用的,这意味着您可以在所有存储库中使用相同的工作流程
入门
让我们通过在 GitHub 存储库的.github/workflows/
目录中创建一个新的 YAML 文件来开始您的第一个工作流程。 我们将从一个简单的工作流开始,自动部署到生产环境,因此我们将此文件命名为deploy.yml
。
# .github/workflows/deploy.yml name: deploy on: push: branches: # Pushing to the `main` branch # will trigger our workflow - main
我们使用on
关键字来定义哪些事件可以触发工作流。 在此示例中,工作流将在向main
分支进行推送时运行。
当某些文件发生变化时,我们可能根本不需要部署,比如README.md
。 我们可以使用on.push.paths-ignore
来排除文件路径模式。
name: deploy on: push: branches: - main paths-ignore: - 'bin/**' - 'README.m
创建你的第一份工作
一个工作流由一个或多个作业组成。 在此示例中,您将使用单个deploy
作业将文件上传到您网站的生产环境。
name: deploy on: [...] jobs: deploy: runs-on: ubuntu-latest steps: [...]
每个作业都在运行器环境中运行,由runs-on
指定。 在上面的 YAML 块中,我们使用ubuntu-latest
,这是一个 Ubuntu Linux 虚拟机 (VM),由 GitHub 托管,预装了运行器应用程序和其他工具。
您可以使用 GitHub 托管的运行器或托管您自己的运行器并自定义用于运行作业的环境。 但是,后者超出了本文的范围。
检查您的 Git 存储库
在您对代码执行任何有意义的操作之前,您必须检查您的存储库,以便您的工作流可以访问它。 您可以为此使用结帐操作。
jobs: deploy: runs-on: ubuntu-latest steps: - name: Checkout # Checkout our repository under `${GITHUB_WORKSPACE}`, # so our workflow can access it uses: actions/checkout@v3 with: # Fetch the entire Git history fetch-depth: 0
我们将fetch-depth
指定为0
,这将导致获取整个 Git 历史记录。 我们需要它来仅上传在后续运行中已更改的文件。
创建 SFTP 用户
要将文件上传到托管服务提供商,您需要 SFTP 连接详细信息(即主机、网络端口和路径)和 SFTP 用户。
大多数时候,您可以找到这些详细信息并通过托管服务提供商的仪表板创建 SFTP 用户。 一些网络托管服务商还会通过电子邮件向您发送这些详细信息。
如果您已经在使用 Pressidium,请按照以下步骤操作:
- 登录到您的 Pressidium 仪表板
- 从仪表板侧边栏中选择网站菜单选项
- 点击您网站的名称
- 通过单击导航栏上的链接导航到SFTP选项卡
- 记下您的SFTP 连接详细信息
- 创建一个新的SFTP 用户
要创建新的 SFTP 用户:
- 点击新建
- 选择环境(生产或登台)
- 提供用户名和密码(建议使用强密码、混合大小写拉丁字符、数字和特殊字符)
- 记下您输入的用户名和密码
- 点击创建创建用户
在第二步,您应该选择要部署到的环境。 对于此示例,我们将为每个环境创建一个用户。
有关通过 SFTP 访问 Pressidium WordPress 站点的更多信息,请参阅此知识库文章。
存储敏感信息
您可以直接在 GitHub Actions 工作流程中输入 SFTP 连接详细信息和 SFTP 用户凭据。 但是,将敏感信息存储在您的存储库中并不是一个好主意。
GitHub 提供加密的秘密作为在您的组织、存储库或存储库环境中存储敏感信息的一种方式。
为存储库创建加密的秘密:
- 登录您的 GitHub 帐户
- 导航到存储库的主页
- 在您的存储库名称下,单击设置
- 选择Secrets并单击Actions ,在边栏的 Security 部分下
- 单击新建存储库密钥按钮
- 输入机密名称及其值
- 单击添加秘密
你应该得到一个类似于这个的秘密列表:
-
SFTP_HOST
SFTP 服务器的主机名 SFTP_PORT
SFTP 服务器的端口SFTP_USER
用于身份验证的用户名SFTP_PASS
用于身份验证的密码
通过 SFTP 上传文件
要通过 SFTP 上传文件,您可以使用—您猜对了—另一个 GitHub 操作。
有多个 SFTP 客户端和 GitHub Actions 可供选择。 我们使用了自己的lftp-mirror-action ,它在底层使用lftp
。 一个支持SFTP的文件传输工具,可以并行传输多个文件。
- name: Deploy via SFTP uses: pressidium/lftp-mirror-action@v1 with: host: ${{ secrets.SFTP_HOST }} port: ${{ secrets.SFTP_PORT }} user: ${{ secrets.SFTP_USER }} pass: ${{ secrets.SFTP_PASS }} remoteDir: '/demo-www/wp-content/themes/my-theme' options: '--verbose'
配置lftp-mirror-action的输入非常简单:
- 您的 SFTP 连接详细信息和 SFTP 用户凭据可以通过
secrets
上下文访问(例如${{ secrets.SFTP_HOST }}
) -
remoteDir
是主题目录的路径 '--verbose'
选项将启用详细输出,这将记录所有文件传输(对故障排除很有用)
在 Pressidium 上,路径格式如下:
-
YOUR_INSTALLATION_NAME-www/
作为生产环境的根路径 YOUR_INSTALLATION_NAME-dev-www/
作为暂存环境的根路径
其中YOUR_INSTALLATION_NAME
是您的安装名称。 请注意,帐户所有者有一个 SFTP 帐户,显示为“主”帐户,可以访问所有网站,因此它们的路径将与上述路径不同。 建议避免使用此帐户,而是为您要访问的每个网站创建一个单独的帐户。
或者,您可以在您的存储库中创建一个.lftp_ignore
文件,包括您希望从部署中排除的任何文件模式。
这是一个可能看起来像的示例:
## Directories to ignore .vscode/** .env.** .git/ .github/ ## Files to ignore .gitignore package.json package-lock.json composer.json composer.lock
把它们放在一起
name: deploy on: push: branches: - main paths-ignore: - 'bin/**' - 'README.md' jobs: deploy: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 with: fetch-depth: 0 - name: Deploy via SFTP uses: pressidium/lftp-mirror-action@v1 with: host: ${{ secrets.SFTP_HOST }} port: ${{ secrets.SFTP_PORT }} user: ${{ secrets.SFTP_USER }} pass: ${{ secrets.SFTP_PASS }} remoteDir: '/demo-www/wp-content/themes/my-theme' options: '--verbose'
而已! 您的工作流程现在可以自动部署您的 WordPress 主题。
构建和部署您的 WordPress 主题
到目前为止,我们通过只关注部署文件而忽略了可能需要安装的任何依赖项、构建可能需要运行的脚本等等来使事情变得简单。
作为示例设置,我们将使用具有两个分支的 GitHub 存储库:
- 稳定的、生产就绪的
main
分支,将部署到生产环境 - 未经测试的
preview
分支,作为功能的集成分支,将部署到暂存环境
是时候引入build
作业并将我们的工作流程重命名为build-deploy
,因为它将负责构建和部署我们的代码。
name: build-deploy on: push: branches: - main paths-ignore: - 'bin/**' - 'README.md' jobs: build: runs-on: ubuntu-latest steps: [...] deploy: [...]
检查您的 Git 存储库
每个作业都在运行器图像的新实例中运行,因此您必须在build
作业中再次检查您的 GitHub 存储库。
build: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3
您不必在构建作业中获取整个 Git 历史记录,因此您可以坚持使用操作输入的默认值。
安装依赖项
一些主题使用第三方包和库。 如果您的主题需要任何 PHP 和/或 JavaScript 包,您可能需要使用包管理器,例如 Composer、npm 或 yarn。
为了这个示例,我们假设您需要安装 Composer 和 Node.js 依赖项。 对我们来说幸运的是,有现成的操作。
steps: - name: Checkout uses: actions/checkout@v3 - name: Install Composer dependencies uses: php-actions/composer@v6 - name: Install Node.js LTS uses: actions/setup-node@v3 with: node-version: 'lts/*' cache: 'yarn' - name: Install Node.js dependencies run: yarn install
composer操作将默认运行composer install
,因此您不必配置其任何输入参数。
对于设置节点操作,我们为node-version
和cache
输入设置自定义值以指定我们想要:
- 获得 Node.js 的长期支持(或 LTS)版本
- 缓存通过 yarn 包管理器获取的任何依赖项
然后,下一步将运行yarn install
来安装 Node.js 依赖项。 请记住,一个步骤可以运行脚本或 GitHub 操作。
请注意,缓存可以显着加快您的工作流程。 每次工作流运行时都下载依赖项会导致运行时间更长。 您可以使用cache操作缓存作业的依赖项(这也是setup-node
操作在后台执行的操作),从而加快重新创建文件所需的时间。
运行构建过程
再一次,我们假设您需要执行一个“构建”过程——您可能需要运行一个预处理器来编译您的样式表、转换您的 ES6+ 脚本等。这通常意味着您已经在您的文件中定义了一个build
脚本package.json
文件。
因此,您将需要另一个步骤来运行该构建过程。
- name: Build theme run: yarn run build
如果您需要main
分支和preview
分支运行不同的脚本(例如, main
分支build
, preview
staging
),您可以这样做:
- name: Build theme run: | if [[ "${{ github.base_ref }}" == "main" || "${{ github.ref }}" == "refs/heads/main" ]]; then yarn run build else yarn run staging fi
最后,由于每个作业都在运行器图像的新实例中运行,因此工作流程中的作业是完全隔离的。 这意味着,您需要一种方法来临时存储刚刚构建的文件,以便deploy
作业可以访问它们。 输入工件。
神器
工件允许您在作业完成后保留数据,因此您可以在工作流中的作业之间共享数据。
让我们为您的build
作业引入一个额外的步骤,以使用Upload-Artifact操作将构建步骤中生成的数据保留 1 天。 我们假设 Composer 在vendor/
目录中安装它的依赖项,并且我们的build
脚本将文件导出到dist/
目录中。
- name: Upload artifact uses: actions/upload-artifact@v3 with: name: my-theme-build path: | dist/ vendor/ retention-days: 1
根据存储库的大小和推送的频率,您可能需要查看 GitHub 的使用限制、计费和管理。
在撰写本文时,默认情况下,GitHub 将构建日志和工件存储 90 天,并在“GitHub 免费”计划中提供 500 MB 的存储空间。
按顺序运行作业
工作流由一个或多个作业组成,默认情况下它们并行运行。 在我们的例子中,我们必须先构建我们的主题,然后才能部署它。 要按顺序运行build
和deploy
作业,您必须使用jobs.<job_id>.needs
关键字定义依赖项。
deploy: runs-on: ubuntu-latest needs: build
在下面的示例中,我们声明build
作业必须在deploy
作业运行之前成功完成。
name: build-deploy on: [...] jobs: build: runs-on: ubuntu-latest steps: [...] deploy: runs-on: ubuntu-latest needs: build steps: [...]
下载工件
在上传构建步骤中构建的任何数据之前,您必须先下载它们。 让我们重新审视deploy
作业并引入一个额外的步骤。
- name: Download artifact uses: actions/download-artifact@v3 with: name: my-theme-build path: .
您可以使用类似于Upload-Artifact的Download -Arti fact操作。 确保为这两个操作指定相同的名称——在本例中为my-theme-build
。
把它们放在一起
name: build-deploy on: push: branches: - main paths-ignore: - 'bin/**' - 'README.md' jobs: build: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 - name: Install Composer dependencies uses: php-actions/composer@v6 - name: Install Node.js LTS uses: actions/setup-node@v3 with: node-version: 'lts/*' cache: 'yarn' - name: Install Node.js dependencies run: yarn install - name: Build theme run: | if [[ "${{ github.base_ref }}" == "main" || "${{ github.ref }}" == "refs/heads/main" ]]; then yarn run build else yarn run staging fi - name: Upload artifact uses: actions/upload-artifact@v3 with: name: my-theme-build path: | dist/ vendor/ retention-days: 1 deploy: runs-on: ubuntu-latest needs: build steps: - name: Checkout uses: actions/checkout@v3 with: fetch-depth: 0 - name: Download artifact uses: actions/download-artifact@v3 with: name: my-theme-build path: . - name: Deploy via SFTP uses: pressidium/lftp-mirror-action@v1 with: host: ${{ secrets.SFTP_HOST }} port: ${{ secrets.SFTP_PORT }} user: ${{ secrets.SFTP_USER }} pass: ${{ secrets.SFTP_PASS }} remoteDir: '/demo-www/wp-content/themes/my-theme' options: '--verbose'
现在你有了一个 GitHub Actions 工作流程,当你推送到main
分支时,它可以自动构建代码并将其部署到生产环境! 但是,在本文开头,我们描述了一个能够部署到生产环境和暂存环境的工作流,具体取决于您要推送到的分支。 如果您仍然愿意,请继续阅读!
将主题部署到多个环境
部署到多个环境可能需要稍微更改您的工作流程。 例如,通常建议为每个环境设置单独的 SFTP 用户,并将其视为最佳实践。 在 Pressidium 中,SFTP 用户对于网站的生产环境和登台环境是不同的。
因此,让我们为每个用户的用户名/密码创建不同的秘密。 这意味着,我们更新的加密秘密列表应该如下所示:
-
SFTP_HOST
-
SFTP_PORT
-
SFTP_PROD_USER
-
SFTP_PROD_PASS
-
SFTP_STAG_USER
-
SFTP_STAG_PASS
您可能还必须更新主机和网络端口。 不过,在这种情况下,无需更改它们,因为它们对于两种环境都是相同的。
设置环境变量
环境变量是一个变量,由名称/值对组成,是进程运行环境的一部分。
在 GitHub Actions 工作流程中,您可以使用env
关键字来设置范围为以下的自定义环境变量:
- 整个工作流程,通过在工作流程的顶层使用
env
- 工作流中的作业,通过在该作业级别使用
env
- 作业中的特定步骤,通过在该步骤级别使用
env
您还可以将环境变量附加到$GITHUB_ENV
,这使得该变量可用于工作流作业中的任何后续步骤。
如您所见,有多种方法可以构建您的工作流。 因此,请随意使用对您来说最有意义的那个。
在我们的例子中,我们在作业的一个步骤中设置环境变量以临时存储值并从稍后在我们的工作流中运行的另一个步骤访问它们。 我们将在将事件push
送到特定分支的步骤中将环境变量附加到$GITHUB_ENV
以展示如何有条件地设置自定义变量。
- name: Set environment variables (main) if: github.ref == 'refs/heads/main' run: | echo "SFTP_USER=${{ secrets.SFTP_PROD_USER }}" >> $GITHUB_ENV echo "SFTP_PASS=${{ secrets.SFTP_PROD_PASS }}" >> $GITHUB_ENV echo "DEPLOY_PATH=/demo-www/wp-content/themes/my-theme" >> $GITHUB_ENV - name: Set environment variables (preview) if: github.ref == 'refs/heads/preview' run: | echo "SFTP_USER=${{ secrets.SFTP_STAG_USER }}" >> $GITHUB_ENV echo "SFTP_PASS=${{ secrets.SFTP_STAG_PASS }}" >> $GITHUB_ENV echo "DEPLOY_PATH=/demo-dev-www/wp-content/themes/my-theme" >> $GITHUB_ENV
我们使用if
关键字将每个步骤限制到特定分支。 这样, Set environment variables (main)将仅在将更改推送到main
分支时运行。
每个环境的$DEPLOY_PATH
也可能不同。
例如,在 Pressidium 上:
- 生产环境的路径遵循
/<WEBSITE_NAME>-www/
格式 - 暂存环境的路径遵循
/<WEBSITE_NAME>-dev-www/
格式
设置输出
我们想使用我们刚刚设置的环境变量作为 GitHub Action 的输入,它将通过 SFTP 传输文件。
不幸的是,目前似乎无法将环境变量引用为 GitHub 操作的输入。 您可以通过创建一个额外的步骤来解决这个问题,该步骤将输出您稍后需要用作输入的值。
- name: Set outputs # Workaround to reference environment variables as inputs # using step outputs, since we can't pass environment # variables as inputs at the moment. id: sftp_details run: | echo "user=${SFTP_USER}" >> $GITHUB_OUTPUT echo "pass=${SFTP_PASS}" >> $GITHUB_OUTPUT echo "deploy_path=${DEPLOY_PATH}" >> $GITHUB_OUTPUT
您现在拥有sftp_details
步骤的user
、 pass
和deploy_path
输出,您可以使用它们将这些值引用为下一步的输入。
上传文件到不同的环境
通过 SFTP 上传文件与以前几乎相同,但您将使用上一步的输出,而不是引用secrets
上下文和硬编码remoteDir
。
- name: Deploy via SFTP uses: pressidium/lftp-mirror-action@v1 with: host: ${{ secrets.SFTP_HOST }} port: ${{ secrets.SFTP_PORT }} user: ${{ steps.sftp_details.outputs.user }} pass: ${{ steps.sftp_details.outputs.pass }} remoteDir: ${{ steps.sftp_details.outputs.deploy_path }} options: '--verbose'
使用${{ steps.<step_id>.outputs.<output_name> }}
访问步骤的输出。 例如, ${{ steps.sftp_details.outputs.user }}
访问sftp_details
步骤的user
输出。
呼,终于! 您的工作流程现在可以构建您的 WordPress 主题并将其部署到您的生产和暂存环境。
整合完整的工作流程
name: build-deploy on: push: branches: # Pushing to any of the following # branches will trigger our workflow - main - preview paths-ignore: # When all the path names match patterns in `paths-ignore` # the workflow will not run. We don't want to do anything # if we have changed *only* (some of) these files - 'bin/**' - 'README.md' jobs: build: runs-on: ubuntu-latest steps: - name: Checkout # Checkout our repository under `${GITHUB_WORKSPACE}`, # so our workflow can access it uses: actions/checkout@v3 - name: Install Composer dependencies # This will run `composer install` # since that's its default command uses: php-actions/composer@v6 - name: Install Node.js LTS # We use the LTS version of Node.js # and cache packages installed via yarn uses: actions/setup-node@v3 with: node-version: 'lts/*' cache: 'yarn' - name: Install Node.js dependencies run: yarn install - name: Build theme # Run the `build` script for production, # and the `staging` script for staging run: | if [[ "${{ github.base_ref }}" == "main" || "${{ github.ref }}" == "refs/heads/main" ]]; then yarn run build else yarn run staging fi - name: Upload artifact # Persist data produced during the build steps # with a retention period of 1 day uses: actions/upload-artifact@v3 with: name: my-theme-build path: | dist/ vendor/ retention-days: 1 deploy: runs-on: ubuntu-latest needs: build steps: - name: Checkout uses: actions/checkout@v3 with: # Fetch the entire Git history fetch-depth: 0 - name: Download artifact uses: actions/download-artifact@v3 with: name: my-theme-build path: . - name: Set environment variables (main) if: github.ref == 'refs/heads/main' run: | echo "SFTP_USER=${{ secrets.SFTP_PROD_USER }}" >> $GITHUB_ENV echo "SFTP_PASS=${{ secrets.SFTP_PROD_PASS }}" >> $GITHUB_ENV echo "DEPLOY_PATH=/demo-www/wp-content/themes/my-theme" >> $GITHUB_ENV - name: Set environment variables (preview) if: github.ref == 'refs/heads/preview' run: | echo "SFTP_USER=${{ secrets.SFTP_STAG_USER }}" >> $GITHUB_ENV echo "SFTP_PASS=${{ secrets.SFTP_STAG_PASS }}" >> $GITHUB_ENV echo "DEPLOY_PATH=/demo-dev-www/wp-content/themes/my-theme" >> $GITHUB_ENV - name: Set outputs # Workaround to reference environment variables as inputs # using step outputs, since we can't pass environment # variables as inputs at the moment. id: sftp_details run: | echo "user=${SFTP_USER}" >> $GITHUB_OUTPUT echo "pass=${SFTP_PASS}" >> $GITHUB_OUTPUT echo "deploy_path=${DEPLOY_PATH}" >> $GITHUB_OUTPUT - name: Deploy via SFTP uses: pressidium/lftp-mirror-action@v1 with: host: ${{ secrets.SFTP_HOST }} port: ${{ secrets.SFTP_PORT }} user: ${{ steps.sftp_details.outputs.user }} pass: ${{ steps.sftp_details.outputs.pass }} remoteDir: ${{ steps.sftp_details.outputs.deploy_path }} options: '--verbose'
您还可以在此 GitHub 存储库上找到示例 WordPress 主题和 GitHub Actions 工作流程。
结论
你有它! GitHub Actions 是一个强大的工具,可以轻松地自动构建和部署您的 WordPress 主题和插件。
我们仅仅触及了您可以使用 GitHub Actions 实现的目标的皮毛。 您的后续步骤可能是自动运行您可能拥有的任何测试、打开问题或在部署完成时在 Slack 上通知,等等。
查看 GitHub Marketplace,在撰写本文时,您可以在其中找到超过 15,000 个可在 GitHub Actions 工作流程中使用的操作。
那你还在等什么?
- 在 GitHub 上查看此存储库的工作流程
- 在 Git 存储库中的
.github/workflows/
下创建一个新的 YAML 文件 - 享受自动化构建和部署
部署愉快!