步骤 11: 对代码进行分支

5.2 version
Maintained Unmaintained
5.0

对代码进行分支

在项目中有很多方式来组织代码修改的流程。但是直接工作在 Git 的 master 分支上,并且不经测试直接部署到生产环境中,这很可能不是最好的方案。

测试并不仅仅关于单元测试或功能测试,它也应该包括可以用生产数据来检查应用程序的行为。如果你或你周围的 利益相关者 可以像部署完毕后普通用户那样试用应用程序,这会是一个巨大的优势,它可以让你充满信心地进行部署。当非技术人员也能去确认新功能时,这一点就显得尤为强大。

出于简化和避免重复的考虑,在下面的步骤中,我们仍然会在 Git 的 master 分支上进行所有工作,但是我们先来看看如何使用一个更好的方案。

采用一个 Git 工作流

一个可行的工作流是对每个新功能或问题修复都新建一个分支。这个方案简单有效。

创建分支

这个工作流从创建一个 Git 分支开始:

1
$ git branch -D sessions-in-db || true
1
$ git checkout -b sessions-in-db

这个命令从 master 分支创建了名为 sessions-in-db 的分支。代码和软件环境的配置从此 分为 两个分支。

把会话数据存放在数据库里

你可能从分支名已经猜到了,我们想要把会话数据的存储从文件系统切换为数据库存储(我们这里的 PostgreSQL 数据库)。

实现这个切换的步骤很典型:

  1. 创建一个 Git 分支;
  2. 如有需要,更新 Symfony 配置;
  3. 如有需要,增加或修改一些代码;
  4. 如果需要的话,更新 PHP 配置(比如增加 PHP 的 PostgreSQL 扩展);
  5. 如果需要的话,更新 Docker 和 SymfonyCloud 的软件环境配置(增加 PostgreSQL 服务);
  6. 在本地测试;
  7. 在远程测试;
  8. 把这个分支合并到 master;
  9. 部署到生产环境;
  10. 删除这个分支;

为了把会话存储在数据库中,要修改 session.handler_id 配置项,把它指向数据库的 DSN:

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
--- a/config/packages/framework.yaml
+++ b/config/packages/framework.yaml
@@ -7,7 +7,7 @@ framework:
     # Enables session support. Note that the session will ONLY be started if you read or write from it.
     # Remove or comment this section to explicitly disable session support.
     session:
-        handler_id: null
+        handler_id: '%env(DATABASE_URL)%'
         cookie_secure: auto
         cookie_samesite: lax

为了把会话存储在数据库中,我们需要创建 sessions 表。用 Doctrine 的数据库迁移来实现:

1
$ symfony console make:migration

编辑文件,在 up() 方法中加入表的创建。

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
--- a/migrations/Version00000000000000.php
+++ b/migrations/Version00000000000000.php
@@ -21,6 +21,14 @@ final class Version00000000000000 extends AbstractMigration
     {
         // this up() migration is auto-generated, please modify it to your needs

+        $this->addSql('
+            CREATE TABLE sessions (
+                sess_id VARCHAR(128) NOT NULL PRIMARY KEY,
+                sess_data BYTEA NOT NULL,
+                sess_lifetime INTEGER NOT NULL,
+                sess_time INTEGER NOT NULL
+            )
+        ');
     }

     public function down(Schema $schema): void

迁移数据库:

1
$ symfony console doctrine:migrations:migrate

通过浏览网站在本地进行测试。因为我们没有做页面展示上的改动,而且还没有开始用会话功能,所以一切应该像之前一样正常运行。

注解

由于我们在会话存储里重用了数据库,我们不需要第 3 到第 5 步,但在关于 Redis 的那一章里,我们会看到在 Docker 和 SymfonyCloud 里增加、测试和部署一个新服务是很直接明了的。

由于这个新表并不是由 Doctrine 来“管理”的,所以我们必须配置 Doctrine,以防它在下次数据库迁移时将该表移除:

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
--- a/config/packages/doctrine.yaml
+++ b/config/packages/doctrine.yaml
@@ -5,6 +5,8 @@ doctrine:
         # IMPORTANT: You MUST configure your server version,
         # either here or in the DATABASE_URL env var (see .env file)
         #server_version: '13'
+
+        schema_filter: ~^(?!session)~
     orm:
         auto_generate_proxy_classes: true
         naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware

把你的修改提交到这条新分支:

1
2
$ git add .
$ git commit -m'Configure database sessions'

部署一个分支

在部署到生成环境之前,我们必须在和生成环境一样的软件配置中测试这个分支。我们也需要确保在 Symfony 的 prod 环境中一切正常运行(本地网站则使用了 Symfony 的 dev 环境)。

现在,让我们来根据 Git 分支 创建一个 SymfonyCloud 环境

1
$ symfony env:delete sessions-in-db --no-interaction
1
$ symfony env:create

这个命令创建了一个新的环境,如下所示:

  • 这个分支从当前的 Git 分支(sessions-in-db)继承了代码和软件环境配置;
  • 这个分支的数据来自主分支环境(也就是生产环境)所有服务数据的快照,包括文件(比如用户上传的文件)和数据库内容;
  • 一个专用的集群会被创建,用来部署代码,数据还有软件环境。

这里的部署和部署到生产环境的步骤是一样的,所以数据库结构迁移也会被执行。这是验证数据库结构迁移是否可以在生产数据上工作的好方法。

这些非 master 环境和 master 环境非常相似,除了一些小的差异:比如,默认情况下非 master 环境不发送邮件。

当部署完成后,用浏览器打开这个新分支的页面:

1
$ symfony open:remote

请注意所有的 SymfonyCloud 命令在当前 Git 分支上都可以运行。上面的操作会打开 sessions-in-db 分支上部署的 URL,它的形式类似 https://sessions-in-db-xxx.eu.s5y.io/

在新环境中测试下网站,你应该会看到在主环境中添加的所有数据。

如果你在 master 环境下新建更多的会议,它们不会出现在 sessions-in-db 环境下,反过来也一样。这些环境是互相独立和隔离的。

如果 master 分支上的代码继续不断更新,你总是可以对 Git 分支进行 rebase,然后部署更新的版本,从而解决代码和软件环境配置的冲突。

你甚至可以把 master 上的数据同步回 sessions-in-db 环境:

1
$ symfony env:sync

在部署前调试生产部署

默认情况下,所有 SymfonyCloud 上的环境设置和 master/prod 环境是一样的(也就是 Symfony 的 prod 环境)。这样你才能在真实条件下测试应用程序。你会觉得像是在生产服务器上直接开发和测试,但此时你却没有直接操作生产服务器的风险。这让我想起来了过去用 FTP 部署时的美好时光。

出问题的时候,你可以切换到 Symfony 的 dev 环境:

1
$ symfony env:debug

当排查完错误后,返回到生产环境的设置:

1
$ symfony env:debug --off

警告

绝对不要master 分支上使用 dev 环境或 Symfony 分析器;它会让你的应用变得很慢,而且会导致很多严重的安全问题。

在部署前测试生产部署

用生产数据预览即将发布的网站版本,这带来了很多可能性,无论是对于用户界面/前端的回归测试,还是性能测试。Blackfire 是用来做性能测试的完美工具。

参考关于“性能”的那个步骤,从而更多了解如何使用 Blackfire 在部署前测试你的代码。

合并到生产环境

当你对分支上的改动满意后,把代码和软件环境配置合并到 Git 的 master 分支:

1
2
$ git checkout master
$ git merge sessions-in-db

然后部署:

1
$ symfony deploy

部署的时候,只有代码和软件环境配置的更新会被推送到 SymfonyCloud;数据不会受到任何影响。

清理

最后,移除 Git 分支和 SymfonyCloud 环境:

1
2
$ git branch -d sessions-in-db
$ symfony env:delete --env=sessions-in-db --no-interaction

This work, including the code samples, is licensed under a Creative Commons BY-NC-SA 4.0 license.