Skip to content

Commit fe05051

Browse files
committed
ci: mirror ubuntu:22.04 to ghcr
1 parent 00ded39 commit fe05051

File tree

2 files changed

+112
-0
lines changed

2 files changed

+112
-0
lines changed

.github/workflows/ghcr.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Mirror DockerHub images used by the Rust project to ghcr.io.
2+
#
3+
# In some CI jobs, we pull images from ghcr.io instead of Docker Hub because
4+
# Docker Hub has a rate limit, while ghcr.io doesn't.
5+
# Those images are pushed to ghcr.io by this job.
6+
#
7+
# Note that authenticating to DockerHub or other registries isn't possible
8+
# for PR jobs, because forks can't access secrets.
9+
# That's why we use ghcr.io: it has no rate limit and doesn't require authentication.
10+
11+
name: GHCR
12+
13+
on:
14+
schedule:
15+
# Run daily at midnight UTC
16+
- cron: '0 0 * * *'
17+
18+
jobs:
19+
mirror:
20+
name: DockerHub mirror
21+
runs-on: ubuntu-24.04
22+
permissions:
23+
# Needed to write to the ghcr.io registry
24+
packages: write
25+
steps:
26+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
27+
with:
28+
persist-credentials: false
29+
30+
- uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
31+
with:
32+
registry: ghcr.io
33+
username: ${{ github.repository_owner }}
34+
password: ${{ github.token }}
35+
36+
- name: Mirror DockerHub
37+
run: python3 src/ci/github-actions/ghcr.py
38+
shell: bash

src/ci/github-actions/ghcr.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
"""
2+
Use crane to mirror images from DockerHub to GHCR.
3+
Learn more about crane at
4+
https://github.com/google/go-containerregistry/blob/main/cmd/crane/README.md
5+
"""
6+
import os
7+
import requests
8+
import tarfile
9+
import shutil
10+
import subprocess
11+
from io import BytesIO
12+
from tempfile import TemporaryDirectory
13+
14+
15+
def crane_gh_release_url() -> str:
16+
version = "v0.20.2"
17+
os_name = "Linux"
18+
arch = "x86_64"
19+
base_url = "https://github.com/google/go-containerregistry/releases/download"
20+
return f"{base_url}/{version}/go-containerregistry_{os_name}_{arch}.tar.gz"
21+
22+
23+
def download_crane():
24+
"""Download the crane executable from the GitHub releases in the current directory."""
25+
26+
try:
27+
# Download the GitHub release tar.gz file
28+
response = requests.get(crane_gh_release_url(), stream=True)
29+
response.raise_for_status()
30+
31+
with TemporaryDirectory() as tmp_dir:
32+
# Extract the tar.gz file to temp dir
33+
with tarfile.open(fileobj=BytesIO(response.content), mode="r:gz") as tar:
34+
tar.extractall(path=tmp_dir)
35+
36+
# The tar.gz file contains multiple files.
37+
# Copy crane executable to current directory.
38+
# We don't need the other files.
39+
crane_path = os.path.join(tmp_dir, "crane")
40+
shutil.copy2(crane_path, "./crane")
41+
42+
print("Successfully downloaded and extracted crane")
43+
44+
except requests.RequestException as e:
45+
raise RuntimeError(f"Failed to download crane: {e}") from e
46+
except (tarfile.TarError, OSError) as e:
47+
raise RuntimeError(f"Failed to extract crane: {e}") from e
48+
49+
50+
def mirror_dockerhub():
51+
# Images from DockerHub that we want to mirror
52+
images = ["ubuntu:22.04"]
53+
for img in images:
54+
repo_owner = "rust-lang"
55+
# Command to mirror images from DockerHub to GHCR
56+
command = ["./crane", "copy", f"docker.io/{img}", f"ghcr.io/{repo_owner}/{img}"]
57+
try:
58+
subprocess.run(
59+
command,
60+
# if the process exits with a non-zero exit code,
61+
# raise the CalledProcessError exception
62+
check=True,
63+
# open stdout and stderr in text mode
64+
text=True,
65+
)
66+
print(f"Successfully mirrored {img}")
67+
except subprocess.CalledProcessError as e:
68+
raise RuntimeError(f"Failed to mirror {img}: {e}") from e
69+
print("Successfully mirrored all images")
70+
71+
72+
if __name__ == "__main__":
73+
download_crane()
74+
mirror_dockerhub()

0 commit comments

Comments
 (0)