如何使用

点击这里这个仓库提交一个 issue.

issue 的标题是你要执行的 docker pull 命令, 内容不需要写。

几分钟后,在这个 issue 下会收到一个包含下载链接的评论。

点击这个链接即可下载镜像到本地。

执行 docker load < xxx.tar.gz 即可加载镜像。

How to Use

Click here to submit an issue to this repository.

The issue title is the docker pull command you want to run; no body content is needed.

A few minutes later, a comment containing a download link will appear under this issue.

Click the link to download the image locally.

Run docker load < xxx.tar.gz to load the image.

如何实现

issue 标题作为下载镜像的命令,Actions 获取到 issue 的标题后执行命令下载 Docker 镜像,然后上传到 Onedrive 网盘上。

上传工具使用 rclone,这样就可以避免自己写代码和 Onedrive API 交互。

直接 rclone copy 即可。

最后,使用 GitHub API 为 issue 发送一个评论,包含下载链接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
name: Docker Pull
on:
issues:
types: [opened] # 只有 issue 被打开时触发

jobs:
new-issue-job:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v2

- name: Docker Pull
run: |
sudo -v; curl https://rclone.org/install.sh | sudo bash # 安装 rclone
rclone version
issue_title="${{ github.event.issue.title }}" # 获取 issue 标题
issue_number="${{ github.event.issue.number }}" # 获取 issue 编号
if [[ $issue_title =~ ^docker\ pull\ (.+)$ ]]; then # 正则匹配 issue 标题,应该以 docker pull 开头
echo "Issue title is valid."
$issue_title # 执行 docker pull 命令
image_name="${BASH_REMATCH[1]}" # 获取镜像名
save_image_name=${image_name//[:\/]/_} # 替换镜像名中的 : 和 / 为 _ , 用于保存文件名
docker save $image_name | gzip > ./$save_image_name.tar.gz # 保存镜像
rclone copy ./$save_image_name.tar.gz E5:/DockerPull/$issue_number/ # 上传到 Onedrive
gh issue comment ${{ github.event.issue.number }} -b "Docker image has been pulled and uploaded to E5. Please download it from the following link: https://e5.1881997.xyz/DockerPull/$issue_number/$save_image_name.tar.gz" # 为 issue 发送评论
else
echo "Issue title is invalid."
exit 1
fi
shell: bash
env:
RCLONE_CONFIG_PASS: ${{ secrets.RCLONE_CONFIG_PASS }} # rclone 配置文件的密码
GH_TOKEN: ${{ secrets.PAT }} # GitHub token, 用于发送评论。GitHub Actions 的默认 token 无法发送评论,需要自己生成一个有权限的 token
RCLONE_CONFIG: ./config/rclone.conf # rclone 配置文件路径

整体实现非常简单,只需要几行代码。

再简单解释一下这里面可能会令人疑惑的两个地方:

  1. rclone copy 是如何上传到我的 Onedrive 的?

这里需要首先在本地配置好 rclone,然后将配置文件进行加密,上传到 GitHub 仓库中。

https://github.com/WANG-Guangxin/DockerPull/blob/master/config/rclone.conf

这里是我的 rclone 配置文件,里面包含了我 Onedrive 的配置信息。

RCLONE_CONFIG 环境变量用于指定 rclone 配置文件的路径。

RCLONE_CONFIG_PASS 环境变量用于指定 rclone 配置文件的密码。这个环境变量需要用 secrets 存储。

这两个环境变量在 Actions 的 yaml 中,当执行 rclone copy 时,会自动解密配置文件,然后上传。

2. https://e5.1881997.xyz/DockerPull/$issue_number/$save_image_name.tar.gz 是如何生成的?

https://e5.1881997.xyz 其实是我搭建的一个网盘列表程序,我用的是 OneManager-php

我曾经在这篇文章 中介绍过这个程序。

至于后面的路径,就是 issue 编号和镜像名,这样就可以保证每个 issue 的镜像都是唯一的。

只要保证上传和下载的路径一致,就可以了。

可能的扩展

  1. 只有 star 了这个仓库的人才能使用

下面是一个可能的方案,但还未经测试。

1
2
3
4
5
6
7
8
9
- name: Check if user starred the repo
run: |
user="${{ github.event.issue.user.login }}"
repo="${{ github.repository }}"
starred=$(curl -H 'Authorization: token ${{ secrets.GITHUB_TOKEN }}' https://api.github.com/user/starred/$repo | grep -c "$user")
if [ "$starred" -eq "0" ]; then
echo "User has not starred the repo, aborting."
exit 1
fi
  1. 删除过早的镜像

同样也是未经测试的代码。

1
2
3
4
5
6
7
8
9

issue_number="${{ github.event.issue.number }}"
issue_number_minus_ten=$((issue_number - 10))

if rclone lsf E5:/DockerPull/$issue_number_minus_ten/ > /dev/null 2>&1; then
rclone delete remote:path
else
echo "Path does not exist, skipping."
fi

最后

目前只有两个人使用过这个机器人,有一个人 fork 了这个仓库,但实际上仅仅 fork 这个仓库是无法使用的。

有兴趣的话可以参考这篇文章自己实现一个。

但实际上用我的就够了,希望使用的用户给个 star ⭐

How It Works

The issue title is used as the image-pulling command. After the Actions workflow gets the issue title, it executes the command to pull the Docker image, then uploads it to Onedrive.

The upload tool uses rclone, so we don’t need to write our own code to interact with the Onedrive API.

Just rclone copy.

Finally, use the GitHub API to post a comment on the issue with the download link.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
name: Docker Pull
on:
issues:
types: [opened] # Only triggered when an issue is opened

jobs:
new-issue-job:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v2

- name: Docker Pull
run: |
sudo -v; curl https://rclone.org/install.sh | sudo bash # Install rclone
rclone version
issue_title="${{ github.event.issue.title }}" # Get the issue title
issue_number="${{ github.event.issue.number }}" # Get the issue number
if [[ $issue_title =~ ^docker\ pull\ (.+)$ ]]; then # Regex match the issue title; it should start with docker pull
echo "Issue title is valid."
$issue_title # Execute the docker pull command
image_name="${BASH_REMATCH[1]}" # Get the image name
save_image_name=${image_name//[:\/]/_} # Replace : and / in the image name with _, used for the saved filename
docker save $image_name | gzip > ./$save_image_name.tar.gz # Save the image
rclone copy ./$save_image_name.tar.gz E5:/DockerPull/$issue_number/ # Upload to Onedrive
gh issue comment ${{ github.event.issue.number }} -b "Docker image has been pulled and uploaded to E5. Please download it from the following link: https://e5.1881997.xyz/DockerPull/$issue_number/$save_image_name.tar.gz" # Comment on the issue
else
echo "Issue title is invalid."
exit 1
fi
shell: bash
env:
RCLONE_CONFIG_PASS: ${{ secrets.RCLONE_CONFIG_PASS }} # Password for the rclone config file
GH_TOKEN: ${{ secrets.PAT }} # GitHub token for commenting. The default GitHub Actions token cannot comment; you need to generate a token with permission yourself
RCLONE_CONFIG: ./config/rclone.conf # rclone config file path

The overall implementation is very simple — just a few lines of code.

Let me briefly explain the two places that might be confusing:

  1. How does rclone copy upload to my Onedrive?

You first configure rclone locally, then encrypt the config file and upload it to the GitHub repository.

https://github.com/WANG-Guangxin/DockerPull/blob/master/config/rclone.conf

This is my rclone config file, which contains my Onedrive configuration info.

The RCLONE_CONFIG environment variable specifies the path of the rclone config file.

The RCLONE_CONFIG_PASS environment variable specifies the password of the rclone config file. This environment variable needs to be stored in secrets.

These two environment variables are in the Actions yaml; when rclone copy runs, it automatically decrypts the config file and uploads.

2. How is https://e5.1881997.xyz/DockerPull/$issue_number/$save_image_name.tar.gz generated?

https://e5.1881997.xyz is actually a network drive listing program I set up, using OneManager-php

I introduced this program in this article.

As for the path after it, it’s just the issue number and image name — this ensures each issue’s image is unique.

As long as the upload and download paths match, it works.

Possible Extensions

  1. Only people who starred this repo can use it

Here’s a possible approach, but not yet tested.

1
2
3
4
5
6
7
8
9
- name: Check if user starred the repo
run: |
user="${{ github.event.issue.user.login }}"
repo="${{ github.repository }}"
starred=$(curl -H 'Authorization: token ${{ secrets.GITHUB_TOKEN }}' https://api.github.com/user/starred/$repo | grep -c "$user")
if [ "$starred" -eq "0" ]; then
echo "User has not starred the repo, aborting."
exit 1
fi
  1. Delete old images

Also untested code.

1
2
3
4
5
6
7
8
9

issue_number="${{ github.event.issue.number }}"
issue_number_minus_ten=$((issue_number - 10))

if rclone lsf E5:/DockerPull/$issue_number_minus_ten/ > /dev/null 2>&1; then
rclone delete remote:path
else
echo "Path does not exist, skipping."
fi

Finally

So far only two people have used this bot, and one person forked the repo — but just forking the repo doesn’t make it usable.

If you’re interested, you can implement your own by referring to this article.

But honestly, just using mine is enough — if you do, please give a star ⭐