This little post describes how to move ("transfer") a GitLab project to another subgroup, when that project includes a container registry with lots of container images. GitLab does not support moving projects with container images.
- Maintainer privileges on SOURCE_PROJECT and TARGET_PROJECT
SOURCE_PROJECT and TARGET_PROJECT are the same project, but before and after the move (at different URLs). TEMP_PROJECT is a separate project which only holds the container images for a while.
- Copy all container images from SOURCE_PROJECT to a TEMP_PROJECT
- Delete container images from SOURCE_PROJECT
- Transfer SOURCE_PROJECT to TARGET_PROJECT via "Transfer Project" button
- Copy all container images from TEMP_PROJECT back to TARGET_PROJECT
- Remove TEMP_PROJECT
This procedure has the advantage that all network traffic is within GitLab CI, and the downlink to your local machine is not needed.
- Create temporary project TEMP_PROJECT
- Enable container registry on TEMP_PROJECT. Pretty much all other features of TEMP_PROJECT except "Repository" can be disabled.
- Add variable
TEMP_PWD
to CI variables of SOURCE_PROJECT containing an access token that allows pushing to TEMP_PROJECT's container registry. When in doubt, just use a universal api token - Add the following job to SOURCE_PROJECT's .gitlab-ci.yml:
tj:move: stage: build image: 'debian:bookworm' variables: TARGET_IMAGE: 'registry.example.com/path/to/tjmove' script: - apt-get update - apt-get install -y jq ca-certificates skopeo - 'skopeo login --username "${CI_REGISTRY_USER}" --password "${CI_REGISTRY_PASSWORD}" ${CI_REGISTRY}' - | for tag in $(skopeo list-tags docker://${CI_REGISTRY_IMAGE} | jq -r .Tags[]); do if [[ -z $(skopeo inspect docker://${TARGET_IMAGE}:$tag 2>&1 >/dev/null) ]]; then echo "Skipping ${CI_REGISTRY_IMAGE}:$tag (already present at target)." else echo "Copying ${CI_REGISTRY_IMAGE}:$tag ..." skopeo copy --dest-creds "oauth2:${TEMP_PWD}" --retry-times 3 docker://${CI_REGISTRY_IMAGE}:$tag docker://${TARGET_IMAGE}:$tag fi done rules: - if: $TRIGGERVAR == "true"
- Cancel the pipeline that was started for this commit.
- Run the master pipeline with variable
TRIGGERVAR
set totrue
. This will run the above job. - Cancel any other jobs that were started along with the above job.
- Keep an eye on the job. If it fails with weird I/O errors, just restart it until it completes successfully.
- Remove
TEMP_PWD
from CI variables of SOURCE_PROJECT. - Remove the container registry from SOURCE_PROJECT.
- TADAA Move SOURCE_PROJECT to TARGET_PROJECT.
- Create a new container registry in TARGET_PROJECT.
- Add variable
TEMP_PWD
again, but this time to CI variables of TARGET_PROJECT. - Modify the move job added above in TARGET_PROJECT's .gitlab-ci.yml to look like this:
Make sure that the stage (here,
tj:move: stage: build image: 'debian:bookworm' variables: SOURCE_IMAGE: 'registry.example.com/path/to/tjmove' script: - apt-get update - apt-get install -y jq ca-certificates skopeo - 'skopeo login --username "${CI_REGISTRY_USER}" --password "${CI_REGISTRY_PASSWORD}" ${CI_REGISTRY}' - | for tag in $(skopeo list-tags --creds "oauth2:${TEMP_PWD}" docker://${SOURCE_IMAGE} | jq -r .Tags[]); do if [[ -z $(skopeo inspect docker://${CI_REGISTRY_IMAGE}:$tag 2>&1 >/dev/null) ]]; then echo "Skipping ${SOURCE_IMAGE}:$tag (already present at target)." else echo "Copying ${SOURCE_IMAGE}:$tag ..." skopeo copy --src-creds "oauth2:${TEMP_PWD}" --retry-times 3 docker://${SOURCE_IMAGE}:$tag docker://${CI_REGISTRY_IMAGE}:$tag fi done rules: - if: $TRIGGERVAR == "true"
build
) really exists. Will cause "yaml invalid" if it doesn't. - Cancel the pipeline that was started for this commit.
- Run the master pipeline with variable
TRIGGERVAR
set totrue
. This will run the above job. - Cancel any other jobs that were started along with the above job.
- Keep an eye on the job. If it fails with weird I/O errors, just restart it until it completes successfully.
- Check that the container registry in TARGET_PROJECT contains all the right content.
- Remove
TEMP_PWD
from CI variables of TARGET_PROJECT. - Remove TEMP_PROJECT
Done!
Thanks for the great gist!
Slightly adapted to our self hosted GitLab instance:
IIRC the else branch still does not trigger even if images exist, but skopeo worked well and fast every time so who cares. :D