Table of contents
Open Table of contents
Docker镜像构建通用Actions脚本
创建.github/workflows/build-image.yml内容如下:
name: Build and Push Docker Image
on:
push:
branches: [ main ]
workflow_dispatch: # 支持手动点击按钮构建
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write # 必须赋予写入 Packages 的权限
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Log in to the Container registry
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v6
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=raw,value=latest
- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: . # 指定根目录的Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
这样每次push之后都会自动构建根目录下的Dockerfile并形成ghcr.io的镜像。
自动更新pnpm的Lockfile
添加.github/workflows/update-lockfile.yml如下:
name: Auto Update pnpm Lockfile
on:
push:
branches:
- main # 如果你的默认分支是 master,请改为 master
paths:
- 'package.json' # 只有当 package.json 改变时才触发
workflow_dispatch:
permissions:
contents: write # 必须赋予写权限,否则无法推代码
jobs:
update-lockfile:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Node.js
uses: actions/setup-node@v6.4.0
- name: Install pnpm
uses: pnpm/action-setup@v6.0.8
with:
run_install: false
- name: Update Lockfile
run: pnpm install --no-frozen-lockfile
- name: Commit and Push changes
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@://github.com"
# 检查是否有文件变动
if [ -n "$(git status --porcelain pnpm-lock.yaml)" ]; then
git add pnpm-lock.yaml
git commit -m "chore: auto update pnpm-lock.yaml"
git push
else
echo "Lockfile is already up to date."
fi
这样,我们只管更新package.json(比如AstroPaper的),然后直接push就行了。彻底免除本地调试。
定期执行Actions
可以设置cron如下:
on:
workflow_dispatch:
schedule:
- cron: '0 22 * * 5' # UTC+0时区下,每周五的22:00运行
Job运行于容器内
首先,声明容器:
jobs:
build:
runs-on: ubuntu-latest
# 核心配置:指定该 Job 运行在你自定义的 Docker 容器中
container:
image: ghcr.io/user/repo:latest
# 如果你的镜像是【私有】的,需要取消下方 credentials 的注释,并确保有权限读取镜像
# credentials:
# username: ${{ github.actor }}
# password: ${{ secrets.GITHUB_TOKEN }} # 或者使用具备 read:packages 权限的个人访问令牌 (PAT)
其次,需要注意任何命令都要在Docker中存在。
向另一个仓库推送
首先,创建一个仓库PAT:
- 在 GitHub 右上角头像 -> Settings -> Developer settings -> Personal access tokens (Fine-grained)。
- 点击 Generate new token,给它起个名字。
- Repository access 选择 Only select repositories,并选择你的目标仓库。
- Permissions 中找到 Repository permissions -> 在添加的搜索框中输入 Contents 并勾选 -> 设置为 Read and write。
- 生成令牌后复制它(它只会显示一次)。
- 回到你当前运行 Actions 的源仓库 -> Settings -> Secrets and variables -> Actions -> 点击 New repository secret。
- Name 填入 TARGET_REPO_PAT (或者其他名称),Secret 填入刚才复制的令牌。
然后,Actions先checkout目标仓库:
- name: Checkout Target Repo
uses: actions/checkout@v6
with:
repository: 'user/目标仓库名'
token: ${{ secrets.TARGET_REPO_PAT }} # 使用刚才配置的密钥
path: 'target-repo' # 将目标仓库克隆到该子目录下
执行完操作后,最终push目标仓库:
- name: Push to Target Repo
run: |
# 进入目标仓库
cd target-repo
# 配置 Git 用户信息(用于提交显示)
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@://github.com"
# 检查是否有文件变动,防止没有变动时报错
if [ -n "$(git status --porcelain)" ]; then
git add .
git commit -m "[skip ci]"
git push origin main # 推送到目标仓库的 main 分支
else
echo "No changes to commit"
fi
Docker环境下的仓库推送
如果在Docker容器环境中,需要多加一句:
- name: Commit and Push changes
run: |
git config --global --add safe.directory /__w/[repo]/[repo] // [!code ++]
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@://github.com"
# 检查是否有json文件变动
if [ -n "$(git status --porcelain)" ]; then
git add --all
git commit -m "[skip ci]"
git push
else
echo "No changes to commit"
fi
如果推向自己的仓库,需要多加一句:
permissions:
contents: write # 必须赋予写权限,否则无法推
以yt-dlp为例,GitHub内部可执行文件下载
如代码所示:
- name: Download yt-dlp
uses: robinraju/release-downloader@v1
with:
repository: 'yt-dlp/yt-dlp'
latest: true
fileName: 'yt-dlp_linux'
- name: Make the yt-dlp_linux executable
run: chmod +x yt-dlp_linux
// ...
- name: Delete the yt-dlp_linux executable
run: rm -f yt-dlp_linux
以yt-dlp为例,Docker内直接通过API解析下载Latest的Release文件
在Dockerfile中,以部署yt-dlp本体可执行文件为例
ARG ORG_NAME=yt-dlp
ARG REPO_NAME=yt-dlp
ARG FILE_NAME=yt-dlp_linux
RUN LATEST_VERSION=$(curl -s https://api.github.com/repos/${ORG_NAME}/${REPO_NAME}/releases | grep -m 1 "tag_name" | cut -d'"' -f4) && \
echo "${ORG_NAME}/${REPO_NAME} 最新版本是: ${LATEST_VERSION}" && \
curl -L "https://github.com/${ORG_NAME}/${REPO_NAME}/releases/download/${LATEST_VERSION}/${FILE_NAME}" -o /usr/local/bin/${FILE_NAME} && \
chmod a+rx /usr/local/bin/${FILE_NAME}
由于ARG LATEST_VERSION=$(curl ……)在Actions里面这样不行,只能写进RUN里面了。
在bash脚本中,以部署yt-dlp插件zip文件为例
ORG_NAME=Brainicism
REPO_NAME=bgutil-ytdlp-pot-provider
LATEST_VERSION=$(curl -s https://api.github.com/repos/${ORG_NAME}/${REPO_NAME}/releases | grep -m 1 "tag_name" | cut -d'"' -f4)
FILE_NAME=bgutil-ytdlp-pot-provider.zip
echo "${ORG_NAME}/${REPO_NAME} 最新版本是: ${LATEST_VERSION}"
mkdir -p ~/yt-dlp-plugins
curl -L "https://github.com/${ORG_NAME}/${REPO_NAME}/releases/download/${LATEST_VERSION}/${FILE_NAME}" -o ~/yt-dlp-plugins/${FILE_NAME}
echo "${FILE_NAME} ok!"
Docker环境下的actions/checkout分析
如果Docker里不安装git,就会导致只能通过API下载压缩包然后解压的方式获取仓库内的文件。
所以,给Docker镜像里安装git是必要的。其他的actions/checkout操作本质上都一样,除了:
- name: Check out repository ERROR in docker env
uses: actions/checkout@v6
with:
repository: 'Brainicism/bgutil-ytdlp-pot-provider'
path: '/git/home/bgutil-ytdlp-pot-provider'
fetch-depth: 1
上面这类操作是禁止的。只能把目录放到工作目录下。
比如:
- name: Check out repository
uses: actions/checkout@v6
with:
repository: 'Brainicism/bgutil-ytdlp-pot-provider'
path: 'bgutil-ytdlp-pot-provider'
fetch-depth: 1
实际跑下来,fetch-depth: 1在Github Actions环境下,其实提速并不明显,除非仓库真的臃肿。
Actions中使用Docker的动静结合
虽然Docker在Actions中并非最佳的运行选择,但是,当自动安装ffmpeg的Actions调用的API接口暂时把Github IP给封禁的时候,真的就直接死在环境安装上。
借助Github内部网络的便捷,一边打包环境成Docker镜像,一边运行脚本反而更加稳定。
以yt-dlp为例,ffmpeg和deno几乎是必装的环境依赖。而实际上,yt-dlp并不需要ffmpeg和deno一直保持最新,只要版本不是特别老旧就可以了。
而yt-dlp几乎需要nightly版本来保证及时的更新与可靠性。yt-dlp_linux --update-to nightly这条命令在Actions环境下执行反而异常便利。
所以,不能因为yt-dlp需要保持最新而不采用Docker:提前固定ffmpeg和deno的环境以便稳定运行。