无需第三方应用程序即可从 Github 自动部署到您的服务器
说到设置部署管道,我相信市面上有无数的解决方案。如果我们使用 AWS,我们可以使用 Code Deploy,Heroku、Zeit Now 和 Netlify 也提供了各自的解决方案,当然,其中一种流行的方法是将任务委托给 CI/CD 服务器(Travis、Circle CI 等)来处理。如果您使用的是 Docker 容器,那么最好的编排工具是 AWS ECS、Swarm 和 Kubernetes。如果您与一个更大的团队合作,您很可能会有 Dev-Ops 团队成员来处理,您可能会认为您的基础设施是理所当然的!😐 但是,如果像我一样,您加入了一家初创公司,并且部署过程是手动的(SSH 到服务器、git pull 等),并且您编写了一些 bash 脚本来为他们完成这些操作,那么您可能需要拥抱内心的书呆子精神🤓,并升级您的部署游戏。在这种情况下,在 GitHub 上合并 Pull 请求时自动运行您的 bash 脚本会让每个人都很高兴,而且这不是什么高深的科学,所以让我们开始吧!
目标
将拉取请求合并到分支 dev 和 master 后自动部署代码。
我们使用的东西
- 节点 JS
- Bash 脚本
- Github Webhooks
- SSH 命令行
入门
假设我们需要自动部署两个版本的网站。其中一个版本名为“prod” stage
,代表最新的合并提交。这些更改通常存在错误且不可靠,因此我们希望只有内部团队才能访问。“stage”代表我们dev
在 Github 上的分支。网站的第二个版本名为“prod”,代表master
Github 上的分支。这个分支(希望)比较稳定,并且已经通过了 QA 团队的测试,被认为对最终用户来说是安全的。这个版本就是公司外部所有人所知的网站 URL。
步骤 1:克隆你的 Git 存储库
如果您尚未在服务器中克隆 github 存储库,则必须执行此操作。
创建两个目录,分别名为:prod
和stage
。
mkdir live stage
git clone git@github.com:p0o/your_repo.git
cp -a your_repo/. ./prod/your_repo
cp -a your_repo/. ./stage/your_repo
rm -rf ./your_repo
确保.
在 your_repo 后添加 extra,这是一种特殊cp
语法,允许复制文件夹内的隐藏文件和文件夹(我们也需要它来复制 .git 文件夹)。
大胆假设:我假设您熟悉管理服务器的基础知识,并且可以使用合适的 SSL 证书在所需的 URL 中运行您的网站。我使用 Nginx 来实现此目的,但我不会在文章中解释这些步骤。如果您不确定,可以自行搜索。
第 2 步:编写 bash 脚本
我们需要两个 Bash 脚本来处理每种情况的部署。如果您需要构建文件,那么让我们在服务器的主目录中创建一个目录,并从那里开始。我将此目录命名为scripts
:
cd ~/
mkdir scripts
cd scripts
好的,让我们继续制作 bash 文件:
touch ./deploy_stage
touch ./deploy_prod
授予他们执行权限:
chmod +x ./deploy_stage
chmod +x ./deploy_prod
(感谢darksmile92指出这一点)
我将为其中一个放置示例代码,另一个只是一个不同的文件夹,并且根据您的项目依赖关系可能具有不同的环境变量。
#!/bin/bash
echo "Deploying stage your_repo"
cd ~/stage/your_repo \
&& git checkout dev \
&& git pull \
&& npm i \
&& npm run build \
&& (pm2 stop your_repo_stage || true) \
&& echo 'Installing: done.' \
&& (pm2 delete your_repo_stage || true) \
&& NODE_ENV=development pm2 --name your_repo_stage start npm -- start \
&& echo "your_repo deployed successfully"
这个 bash 脚本基本上会从 GitHub 获取最新代码,安装依赖项,构建脚本(如果需要)并使用PM2运行它。如果您不熟悉,PM2 是一个非常有用的进程管理工具,您可以使用 NPM 轻松安装它。
另外值得一提的是,我用逻辑 AND (&&) 链接了整个过程,因为如果其中一个过程失败,我想退出执行。
步骤 3:编写代码来处理 webhook 事件
为了在 Github 发生任何事件时都能收到通知,我们必须订阅他们的 Webhook API,这实际上意味着需要向 Github 提供一些 URL,以便他们向 Github 发送一些信息。这些 URL 必须是公开的,并且 Github 可以运行脚本来部署你的代码,因此,如果 Github 服务器以外的任何人可以访问这些 URL,将会带来严重的安全隐患(例如,拒绝服务攻击)。
Github 使用 SH1 HMAC 签名来验证发送给您的 JSON 对象。我们会将此签名哈希值保存在X-Hub-Signature
header 的值中。由于处理这些操作比较复杂,我们可以使用github-webhook-handler 包,该包就是为此目的而创建的。
我们还需要从 Node 运行我们的 Bash 脚本文件。我们可以使用原生函数来实现,但为了简单起见,我更喜欢使用ShellJs 。
好了,废话不多说,下面是您需要的代码:
const http = require('http');
const createHandler = require('github-webhook-handler');
const shell = require('shelljs');
// We avoid to hardcode the secret in the code, you should provide it with an ENV variable before running this script
const { MY_SECRET } = process.env;
// You might use the same script for multiple repositories, this is only one of them
const REPO_NAME = 'my_repo';
// port is default on 6767
const PORT = process.env.PORT || 6767;
var handler = createHandler({ path: '/', secret: MY_SECRET })
http.createServer(function (req, res) {
handler(req, res, function (err) {
res.statusCode = 404
res.end('no such location')
})
}).listen(PORT);
handler.on('error', function (err) {
console.error('Error:', err.message)
})
handler.on('pull_request', function (event) {
const repository = event.payload.repository.name;
const action = event.payload.action;
console.log('Received a Pull Request for %s to %s', repository, action);
// the action of closed on pull_request event means either it is merged or declined
if (repository === REPO_NAME && action === 'closed') {
// we should deploy now
shell.cd('..');
shell.exec('~/scripts/deploy_stage');
}
});
将其保存在服务器的某个文件夹中并安装依赖项:
npm init
npm i github-webhook-handler shelljs --save
然后使用 PM2 永远使用环境变量运行它:
MY_SECRET=MyGithubWebhookSecret pm2 --name github-deployer start node -- ./index.js
就这样!
步骤4:配置github webhook
现在我们只需要访问 Github 并将我们的 webhook 引入到 GitHub 即可。但请注意,上一步中我们在 6767 端口上运行了 webhook,并且没有启用 HTTPS。因此,您需要设置 nginx 并为其指定一个启用 HTTPS 的域名。您可以直接将其放在主域名的路径下,但本文不打算详细解释这个过程。幸运的是,网络上有很多文章可供您查找。
前往你的代码库的“设置”选项卡,点击“Webhook”。在页面右侧,点击“添加 Webhook”按钮。
输入你在 Nginx 中为运行的 Node.js 应用引入的 URL。假设它是https://yourdomain.com/webhook
选择application/json
内容类型 (content-type),并输入我们运行服务时使用的密钥。在我的示例中,它是“MyGithubWebhookSecret”。
在“您希望哪些事件触发此 webhook?”部分中,单击“让我选择单个事件”,找到 Pull 请求并检查:
确保其他所有选项都未勾选,然后点击“添加 Webhook”保存。现在一切就绪🦸
步骤 5:测试并验证
使用 PM2 监控我们刚刚创建的 Node.js 应用的日志。输入:
pm2 log github_deployer
现在您可以查看是否有任何更改!前往您的代码库,在新分支中进行一些更改。提交拉取请求并合并。您应该在日志中看到您的 Bash 脚本将执行部署,之后您的更改应该会反映到网站上。如果您遇到任何错误,您可以在日志中看到它,然后……采取一些措施 😂
结论
虽然我认为本文提出的解决方案相当简单,但它并非解决此特定问题的最佳方案。就连我的个人博客也使用 Zeit Now Github Integration 进行部署!然而,其他解决方案依赖于第三方应用程序,有时某些团队根据其资源限制无法访问。就我而言,部署脚本已经存在,代码库没有使用 Docker,而且我处理这个问题的时间非常有限。如果你也遇到同样的情况,不妨继续学习!
本文最初发布在我的博客上,标题为《从 Github 自动部署到你的服务器》,欢迎查看更多文章!👀
你也可以在 Twitter 上找到我 => @p0oker