From 772dd9dd97915690ae867f21ce098b1eb6ceb671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Fern=C3=A1ndez?= Date: Wed, 8 Sep 2021 14:12:33 +0200 Subject: [PATCH 1/3] Add DockerHub -> GHCR sync workflow --- .github/workflows/sync.yml | 50 +++++++++++++++++++++++++++ sync_image_ghcr.sh | 69 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 .github/workflows/sync.yml create mode 100644 sync_image_ghcr.sh diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml new file mode 100644 index 0000000..026785f --- /dev/null +++ b/.github/workflows/sync.yml @@ -0,0 +1,50 @@ +name: Sync DockerHub images with GHCR 🔁 + +## This workflow is a temporal solution for having all our docker images synced to GHCR. However, +## as soon as all the repo's CI is migrated to GitHub Actions, this can be removed. +## +## Project tracking the progress: https://github.com/orgs/jellyfin/projects/31 + +on: + schedule: + - cron: "0 0 * * *" + workflow_dispatch: + +jobs: + sync: + runs-on: ubuntu-latest + name: Image sync 🔁 + # Errors will probably be caused by excesses in API quota, so we can safely continue. + # Remaining workflows will be removed in the next scheduled run. + continue-on-error: true + strategy: + fail-fast: false + matrix: + image: + - 'jellyfin/jellyfin' + + steps: + - name: Clone repository + - uses: actions/checkout@v2.3.4 + + ## Logging in to DockerHub allows for more pulls without hitting DockerHub's quota (100 vs 200). + - name: Login to Docker Hub + uses: docker/login-action@v1.10.0 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Login to GitHub Container Registry + uses: docker/login-action@v1.10.0 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.JF_BOT_TOKEN }} + + - name: Set correct permissions + run: chmod +x sync_image_ghcr.sh + + - name: Run syncing script + run: ./sync_image_ghcr.sh ${{ matrix.image }} + env: + GITHUB_TOKEN: ${{ secrets.JF_BOT_TOKEN }} diff --git a/sync_image_ghcr.sh b/sync_image_ghcr.sh new file mode 100644 index 0000000..9a266b3 --- /dev/null +++ b/sync_image_ghcr.sh @@ -0,0 +1,69 @@ +#!/bin/bash +set -e +# Simple script that pushes missing/outdated tags from an image in DockerHub to GHCR, skipping up-to-date tags +# GitHub package registry. You need to be logged in using 'docker login' first: https://docs.github.com/es/packages/working-with-a-github-packages-registry/working-with-the-container-registry +target_repo="ghcr.io" +tag_file="tag_list.txt" +original_image="$1" +rm -rf $tag_file +url="https://hub.docker.com/v2/repositories/${original_image}/tags" +tag_count=$(wget -q "$url" -O - | jq -r '.count') + +echo "Fetching tags from DockerHub..." +while true; do + results=$(wget -q "$url" -O - | jq -r '.') + url=$(echo "$results" | jq -r '.next') + echo "$results" | jq -r '.results[] | {name: .name, last_pushed: .tag_last_pushed, digests: [.images[].digest]}' >> $tag_file + if [ "${url}" = "null" ] + then + break + else + continue + fi +done; +unset results, url + +sorted=$(cat "$tag_file" | jq -s 'sort_by(.last_pushed)') +echo "$sorted" > $tag_file +file_tag_count=$(jq length "$tag_file") + +if [ $tag_count = $file_tag_count ] +then + echo -e "All the data was retrieved correctly. Pushing missing/modified tags to DockerHub...\n" +else + echo "The retrieved data doesn't match the amount of tags expected by Docker API. Exiting script..." + exit 1 +fi + +unset sorted, file_tag_count, tag_count + +## This token is that GitHub provides is used to access the registry in read-only, so users are able to +## use GHCR without signing up to GitHub. By using this token for checking for the published images, we don't consume +## our own API quota. +dest_token=$(wget -q https://ghcr.io/token\?scope\="repository:${original_image}:pull" -O - | jq -r '.token') +tag_names=$(cat "$tag_file" | jq -r '.[] | .name') + +while read -r line; do + tag="$line" + source_digests=$(cat "$tag_file" | jq -r --arg TAG_NAME "$tag" '.[] | select(.name == $TAG_NAME) | .digests | sort | .[]' | cat) + target_manifest=$(wget --header="Authorization: Bearer ${dest_token}" -q https://${target_repo}/v2/${original_image}/manifests/${tag} -O - | cat) + target_digests=$(echo "$target_manifest" | jq '.manifests | .[] | .digest' | jq -s '. | sort' | jq -r '.[]' | cat) + if [ "$source_digests" = "$target_digests" ] + then + echo The tag $tag is fully updated in $target_repo + continue + else + echo Updating $tag in $target_repo + docker pull $original_image:$tag + docker tag $original_image:$tag $target_repo/$original_image:$tag + docker push $target_repo/$original_image:$tag + + # Delete pushed images from local system + docker image rm $original_image:$tag + docker image rm $target_repo/$original_image:$tag + fi +done <<< $tag_names + +rm -rf $tag_file +echo -e "\nAll the tags have been updated successfully" +exit 0 From 8e33dbaa7341f3ef98c3bcc8751cae4d1ab6fd96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Fern=C3=A1ndez?= Date: Thu, 9 Sep 2021 00:09:57 +0200 Subject: [PATCH 2/3] Use the variable when fetching the guest token for GHCR pull Co-authored-by: K3rnelPan1c <33120068+h1dden-da3m0n@users.noreply.github.com> --- sync_image_ghcr.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sync_image_ghcr.sh b/sync_image_ghcr.sh index 9a266b3..689ed78 100644 --- a/sync_image_ghcr.sh +++ b/sync_image_ghcr.sh @@ -40,7 +40,7 @@ unset sorted, file_tag_count, tag_count ## This token is that GitHub provides is used to access the registry in read-only, so users are able to ## use GHCR without signing up to GitHub. By using this token for checking for the published images, we don't consume ## our own API quota. -dest_token=$(wget -q https://ghcr.io/token\?scope\="repository:${original_image}:pull" -O - | jq -r '.token') +dest_token=$(wget -q https://${target_repo}/token\?scope\="repository:${original_image}:pull" -O - | jq -r '.token') tag_names=$(cat "$tag_file" | jq -r '.[] | .name') while read -r line; do From 57fe868cecd0e9b1fd1a0cada879658c332d5ef1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Fern=C3=A1ndez?= Date: Thu, 9 Sep 2021 00:20:46 +0200 Subject: [PATCH 3/3] Mask GITHUB_TOKEN variable --- .github/workflows/sync.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml index 026785f..7e46b17 100644 --- a/.github/workflows/sync.yml +++ b/.github/workflows/sync.yml @@ -27,6 +27,10 @@ jobs: - name: Clone repository - uses: actions/checkout@v2.3.4 + # The 'echo' command masks the environment variable + - name: Prepare environment + run: chmod +x sync_image_ghcr.sh && echo "::add-mask::${GITHUB_TOKEN}" + ## Logging in to DockerHub allows for more pulls without hitting DockerHub's quota (100 vs 200). - name: Login to Docker Hub uses: docker/login-action@v1.10.0 @@ -41,9 +45,6 @@ jobs: username: ${{ github.repository_owner }} password: ${{ secrets.JF_BOT_TOKEN }} - - name: Set correct permissions - run: chmod +x sync_image_ghcr.sh - - name: Run syncing script run: ./sync_image_ghcr.sh ${{ matrix.image }} env: