Estimated read time: 15 minutes
This tutorial will show you how to publish containers along with your code.
At the end of the tutorial, you will have
- A GitHub Package configured with a GitHub Repository
- A container accessible from GitHub Container Registry
ghcr.io
- A semantically versioned container
- A GitHub Action that automatically increments tags on your repo as new merges to the
main
branch happen
Table of contents
Open Table of contents
What are GitHub Packages?
It’s a GitHub feature that allows code to packaged as a docker/OCI containers among other things.
Prerequisites
- install docker for your distribution
- Create a github user and repo
Create an Image
ARG BASE_IMAGE=python:3.10.14
CMD ["python", "-c", "print('Hello World!')"]
save this to a file named Dockerfile
and follow that with this command
docker build . -t ghcr.io/reinthal/swapi:latest
If you are coding along switch reinthal
for you own username and swapi
for your own repo.
Create a Classic PAT
Press the Profile image of the organization or individual for which you are configuring packages for in the top right corner of github, then:
- Go To Profile > Settings
- In the left pane, scroll all the way to the bottom to find Developer Settings
- Press Token (Classic)
- Give the token a name like
GH_TOKEN
and choosewrite:packages
privileges
copy your token! You will only see it once! Write it down in your editor/terminal
For example, save it in your .bashrc
like so
echo 'export GH_TOKEN=ghp_GKHASGDAKSGDAKLSDG' >> ~/.bashrc && source ~/.bashrc
Authenticate to ghcr.io
echo $GH_TOKEN | docker login ghcr.io -u reinthal --password-stdin
WARNING! Your password will be stored unencrypted in /home/kog/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
Docker warns that token is saved in plain text. Thats OK as long as you do not share this machine with someone else who has access to the root account, docker or your user’s files.
Build image, Tag and Push
Run the following commands from the directory where you saved your docker file:
docker build .
...
ARG BASE_IMAGE=python:3.10.14
...
Step 2/2 : CMD ["python", "-c", "print('Hello World!')"]
---> Running in 0229b7c89a0d
---> Removed intermediate container 0229b7c89a0d
---> 5b00cb6d911a
Successfully built 5b00cb6d911a
Then, it’s time to tag the image id 5b00cb6d911a
! A tag can be anything you like! Me and my son like icecream for example:
docker tag 0ebf5e30330b ghcr.io/reinthal/swapi:icecream
Since this is the latest build of the docker container, lets also tag it with the latest
tag:
docker tag 0ebf5e30330b ghcr.io/reinthal/swapi:latest
Then we push the image to the GitHub container registry ghcr.io
:
docker push ghcr.io/reinthal/swapi:latest
The push refers to repository [ghcr.io/reinthal/swapi]
ad48ec53d13d: Layer already exists
ce9cc1eccce8: Layer already exists
4f93c48cb46d: Layer already exists
b43c1d914be6: Layer already exists
0a1f19c0d90d: Layer already exists
a8273e3b1197: Layer already exists
cbe4fb5e267b: Layer already exists
734c0f0b65c2: Layer already exists
8845ab872c1c: Layer already exists
d7d4c2f9d26b: Layer already exists
bbe1a212f7e9: Layer already exists
latest: digest: sha256:935410f51b1ebaebdac668fcc0b0d81332473789d1c7b76a3447050ec045a6ab size: 2635
We have pushed a container to GitHub container registry!
In GitHub, the container registry is part of the Packages feature. The container image with the tag ghcr.io/reinthal/swapi:latest
is now a GitHub Package.
Configure Packages with A GitHub Repo
This next part was unintuitive for me. GitHub does not provide a link between the github package ghcr.io/USERNAME/PACKAGENAME
and the repository github.com/USERNAME/REPONAME
out-of-the-box and it has to be configured. To configure linking between the package and the repository, first go to the organization/individual account for which the github token was configured, in my case reinthal
, then
- Press Packages
- Press the package name, in my case
swapi
- Then press Package settings
- Press Add Repository and choose the desired repository to link with this package, in my case it was
reinthal/swapi
- Configure appropriate roles for the repository, I gave it full admin access to manage packages. Note that your configuration might differ here depending on how to plan administrate packages using GitHub Actions.
- Then, copy the Repository source string and paste it into the
Dockerfile
that built the container
ARG BASE_IMAGE=python:3.10.14
CMD ["python", "-c", "print('Hello World!')"]
LABEL org.opencontainers.image.source = "https://github.com/reinthal/swapi"
# ^ ⊂(◉‿◉)つ Add the above line
and pair the desired repo with this package. In my case, I named the repo and the package the same, i.e swapi
.
Automate Using GitHub Actions
Add the following file to your github repo to push semantically versioned container images to GitHub Packages/GitHub Container Registry:
.github/workflow/tag-and-push.yaml
name: Auto Tag Application
on:
push:
branches:
- main
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
concurrency: production
jobs:
Patch:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: "0"
- name: Minor version for each merge
id: taggerDryRun
uses: anothrNick/github-tag-action@1.36.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
WITH_V: true
DRY_RUN: true
- name: echo new tag
run: |
echo "The next tag version will be: ${{ steps.taggerDryRun.outputs.new_tag }}"
- name: echo tag
run: |
echo "The current tag is: ${{ steps.taggerDryRun.outputs.tag }}"
- name: echo part
run: |
echo "The version increment was: ${{ steps.taggerDryRun.outputs.part }}"
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v2
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Build and push Docker images
# You may pin to the exact commit or the version.
uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0
with:
# List of a customs host-to-IP mapping (e.g., docker:10.180.0.1)
# Build's context is the set of files located in the specified PATH or URL
context: . # optional
push: true
# Path to the Dockerfile
# List of tags
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.taggerDryRun.outputs.new_tag }}, ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}, ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest # optional
labels: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.labels}}
- name: Minor version for each merge
id: taggerFinal
uses: anothrNick/github-tag-action@1.36.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
WITH_V: true
The above GitHub action will automatically tag the container image with semantic versioning, i.e v1.0.0, v.1.3.0 etc.
Summary
We have done the following
- Create an image
- Create a classic PAT GitHub Token
- Authenticate to
ghcr.io
- Tag and push the image
- Configure packages in profile
- Automate using github actions
You have just learned how to publish packages in the form of container images. Go ahead and deploy these to your favorite OCI environment now! :)