关注 Running Page 项目很久了,今天终于把它部署在我博客上了。Running Page 是一个将运动数据可视化的开源项目,它支持汇总运动记录、生成热力图,并与MapBox结合展示路线。 本文将分享如何通过GitLab CI/CD自动化构建静态页面,并集成苹果快捷指令一键触发部署。
搭建准备
官方支持了 Vercel(推荐)和 GitHub Pages 自动部署,但个人还是比较喜欢跑在自己的服务器上,正好自己搭建了Gitlab, 于是就小折腾了一番。
准备的环境有:
Gitlab CE 仓库:之前在家搭建了一个私有的,用来托管日常自己写的代码和自动部署博客;
Gitlab Runner: 安装一个 Gitlab Runn 来跑工作流;
服务器:Racknerd上买了个小机来跑我的博客;
跑步数据: 官方支持了很多平台的跑步数据导入,我主力用的是 keep, 准备好账号/密码就好了。
构建页面
导入仓库
Gitlab 有个很好用的功能,可以直接导入Github的项目,利用这个功能,就能将 Running Page 的仓库导入到私有的仓库了。
我在导入的时候出现了两次导入失败的情况,可能是网络的问题,重试后就成功导入了
自定义修改
Running Page 是支持自定义站点标题等信息的,官方文档写的很细致的,这里不展开了。
不过修改的内容,建议切换到一个自己的分支,进行修改,方便后面的维护与更新。
最近 ChatGPT 生图的功能非常👍,我按照原来的风格我重新做了个 Logo 😄
另外注意一点,如果想放在站点的子目录(如 /running/
),需要设置一下环境变量PATH_PREFIX
, 或直接修改vite.config.ts
的base
为 base: process.env.PATH_PREFIX || '/running/'
。
构建与部署
接下来就是使用 CI/CD 自动构建了。直接使用官方仓库的 Dockerfile 就可以完成静态页面的构建,那这部分步骤就很清晰了。
graph LR B[使用Docker构建] --> C[从Docker中复制静态页面] C --> D[静态页面保存构建产物] D --> E[rsync推送页面到服务器]
为了安全,我们先将我们的密码/服务器私钥设置到项目下的 Setting > CI/CD > Variables
中,我这里加了三个
- keep 登陆手机号
KEEP_LOGIN_PHONE
- keep 登陆密码
KEEP_LOGIN_PASSWORD
- 博客服务器的私钥
BLOG_SSH_PRIVATE_KEY
(可以生成一个专用的私钥用来部署, 注意提前在服务器配置好公钥)
在添加变量的时候可以将 Masked
钩上,Protect variable
不要随便勾选,除非你讲你自己的分支设置成保护分支,不然构建的时候获取不到值。
到这里就写好这个简易的.gitlab-ci.yml
了。
image: docker:latest
stages:
- build
- deploy
build:
stage: build
tags:
- linux01
script:
# 构建 Docker 镜像
- docker build --build-arg app=Keep --build-arg keep_phone_number=$KEEP_LOGIN_PHONE --build-arg keep_password=$KEEP_LOGIN_PASSWORD --build-arg YOUR_NAME="Razeen" -t running-page:$CI_COMMIT_SHA .
# 创建临时容器来获取构建产物
- docker create --name temp_container running-page:$CI_COMMIT_SHA
- docker cp temp_container:/usr/share/nginx/html ./dist
- docker rm temp_container
# 保存构建产物
- mkdir -p artifacts
- cp -r dist/* artifacts/
artifacts:
paths:
- artifacts/
expire_in: 1 week
allow_failure: false
deploy:
stage: deploy
tags:
- homelab01
script:
- mkdir -p ~/.ssh
- echo "$BLOG_SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- ls -la ~/.ssh
- echo -e "Host *\n\tStrictHostKeyChecking no\n" > ~/.ssh/config
- chmod 600 ~/.ssh/config
# 使用 rsync 同步文件
# 'ssh -p 2222' 如果SSH是非常规端口
- rsync -avz -e 'ssh -p 2222' --delete artifacts/ root@118.93.29.15:/www/running/
dependencies:
- build
allow_failure: false
同时,服务器配置好的nginx
路径。
location /running {
root /www/;
index index.html index.htm;
}
快捷方式触发
上面的 CI/CD
配置写好了,但没加触发条件。为了方便记录,我这里使用了苹果的快捷方式来触发。
在项目下 Settings > C1/CD > Pipeline trigger tokens
中,可以添加一个工作流触发的 Token, 然后可以通过 POST 对应的URL触发了,如
# https://git.isw.app/ => gitlab 地址
# 25 => 项目ID
# REF_NAME => 分支 或 tag (我这里触发的分支)
# glptt-xxxxx => 工作流触发 token
https://git.razeen.app/api/v4/projects/25/ref/REF_NAME/trigger/pipeline?token=glptt-xxxxx
接着,我们使用 获取URL内容
的组件来触发工作流,注意请求方式使用POST
。再加个弹窗
展示返回内容。给快捷方式选个LOGO,保存,设置快捷方式到桌面。这样一键触发就制作好了。
再在 CI/CD
配置中加上对应的触发条件。这里加上了页面触发和触发器触发。
# 简化触发器配置
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "trigger"
when: always
- if: $CI_PIPELINE_SOURCE == "web"
when: always
自动同步代码
之前的步骤已经完成自动构建和部署了,但我还希望我本地的代码能和 GitHub 的保持持续的同步,需要时和自己的分支合并就可更新自己的Page了。
Gitlab 自带 Mirroring repositories
功能,能自动将本地代码与指定远程保持一致,但 CE 版本只能同步推送,这里就需要用CI
实现一下。
sync-upstream:
stage: sync
tags:
- m1max
script:
# 配置 Git
- git config --global user.name "GitLab CI"
- git config --global user.email "gitlab-ci@isw.app"
# 克隆仓库
# REPO_TOKEN 在项目下的 Setting > Access Tokens 中添加一个Token, 并设置到CI/CD的环境变量中去
# 需要 read_repository, write_repository 的权限
# CI 开头的环境变量是CI自带的,不用设置
- git clone https://oauth2:${REPO_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git repo
- cd repo
# 确保在 master 分支
- git checkout master
# 添加上游仓库
- git remote add upstream https://github.com/yihong0618/running_page.git
# 获取上游更新
- git fetch upstream
# 合并上游更新到本地 master 分支
- git merge upstream/master -m "Merge upstream changes"
# 推送到 GitLab 仓库
- git push https://oauth2:${REPO_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git master
此外,为了持续更新,在后台再加个订单调度。为了不和build/deploy
冲突,添加两个条件来区分构建和同步的场景。
rules:
- if: $CI_PIPELINE_SOURCE == "schedule" && $SYNC_ONLY == "true"
when: always
- if: $CI_PIPELINE_SOURCE == "web" && $CI_JOB_NAME == "sync-upstream"
when: always
总结
至此,基本全部实现了。以后每次跑完步就可以触发一下更新上去了。为了身体健康,为了热力图效果,运动起来!!
详细配置
老习惯,最后贴一下全部的配置。.gitlab-ci.yml
。防止多个构建和并发同时执行,加了一个resource_group
。
image: docker:latest
stages:
- sync
- build
- deploy
variables:
GIT_STRATEGY: clone
# 简化触发器配置
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "trigger"
when: always
- if: $CI_PIPELINE_SOURCE == "web"
when: always
- if: $CI_PIPELINE_SOURCE == "schedule"
when: always
.build_template: &build_template
resource_group: running-page-pipeline
interruptible: false
deploy:
<<: *build_template
stage: deploy
tags:
- homelab01
script:
- mkdir -p ~/.ssh
- echo "$BLOG_SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- ls -la ~/.ssh
- echo -e "Host *\n\tStrictHostKeyChecking no\n" > ~/.ssh/config
- chmod 600 ~/.ssh/config
- rsync -avz -e 'ssh -p 2222' --delete artifacts/ root@118.93.29.15:/www/running/
dependencies:
- build
rules:
- if: $CI_PIPELINE_SOURCE == "schedule" && $SYNC_ONLY == "true"
when: never
- if: $CI_PIPELINE_SOURCE == "web" && $CI_JOB_NAME == "sync-upstream"
when: never
- when: always
allow_failure: false
build:
<<: *build_template
stage: build
tags:
- linux01
script:
- docker build --build-arg app=Keep --build-arg keep_phone_number=$KEEP_LOGIN_PHONE --build-arg keep_password=$KEEP_LOGIN_PASSWORD --build-arg YOUR_NAME="Razeen" -t running-page:$CI_COMMIT_SHA .
- docker create --name temp_container running-page:$CI_COMMIT_SHA
- docker cp temp_container:/usr/share/nginx/html ./dist
- docker rm temp_container
- mkdir -p artifacts
- cp -r dist/* artifacts/
artifacts:
paths:
- artifacts/
expire_in: 1 week
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- activities/
- assets/
- GPX_OUT/
- TCX_OUT/
- FIT_OUT/
- Workouts/
- run_page/data.db
- src/static/activities.json
- imported.json
policy: pull-push
rules:
- if: $CI_PIPELINE_SOURCE == "schedule" && $SYNC_ONLY == "true"
when: never
- if: $CI_PIPELINE_SOURCE == "web" && $CI_JOB_NAME == "sync-upstream"
when: never
- when: always
allow_failure: false
sync-upstream:
stage: sync
resource_group: repo-sync
tags:
- m1max
script:
- git config --global user.name "GitLab CI"
- git config --global user.email "gitlab-ci@isw.app"
- git clone https://oauth2:${REPO_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git repo
- cd repo
- git checkout master
- git remote add upstream https://github.com/yihong0618/running_page.git
- git fetch upstream
- git merge upstream/master -m "Merge upstream changes"
- git push https://oauth2:${REPO_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git master
rules:
- if: $CI_PIPELINE_SOURCE == "schedule" && $SYNC_ONLY == "true"
when: always
- if: $CI_PIPELINE_SOURCE == "web" && $CI_JOB_NAME == "sync-upstream"
when: always
allow_failure: true
配置中用了三个runner的 tag, homelab01
和 linux01
跑在家里服务器上,m1max
跑在本地电脑上,需要按照好对应的 git 等命令。