{
"$type": "site.standard.document",
"content": {
"$type": "pub.leaflet.content",
"pages": [
{
"$type": "pub.leaflet.pages.linearDocument",
"blocks": [
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"features": [
{
"$type": "pub.leaflet.richtext.facet#link",
"uri": "https://github.com/github"
}
],
"index": {
"byteEnd": 91,
"byteStart": 85
}
}
],
"plaintext": "In today's post, we'll take a quick look at how to set up continuous deployment in a GitHub repository. We're pretty sure that this setup also works for other Git registries, but if you use another, keep in mind that this post is designed for GitHub only."
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"features": [
{
"$type": "pub.leaflet.richtext.facet#link",
"uri": "https://github.com/github"
}
],
"index": {
"byteEnd": 42,
"byteStart": 36
}
},
{
"features": [
{
"$type": "pub.leaflet.richtext.facet#link",
"uri": "/blog/tags/deployment"
}
],
"index": {
"byteEnd": 159,
"byteStart": 143
}
}
],
"plaintext": "This post also assumes that you use GitHub Actions combined with Argo CD for deploying your applications on a Kubernetes cluster. Follow other deployment posts of us for further instructions on how to set up both technologies on your personal server."
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.header",
"level": 2,
"plaintext": "Preparations"
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"features": [
{
"$type": "pub.leaflet.richtext.facet#link",
"uri": "https://hub.docker.com/"
}
],
"index": {
"byteEnd": 34,
"byteStart": 24
}
}
],
"plaintext": "We recommend creating a Docker Hub account or choose another Docker registry if you want."
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"plaintext": "Your GitHub repository must fulfil these conditions:"
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.unorderedList",
"children": [
{
"$type": "pub.leaflet.blocks.unorderedList#listItem",
"content": {
"$type": "pub.leaflet.blocks.text",
"plaintext": "Has a Dockerfile (ideally in the root folder)"
}
},
{
"$type": "pub.leaflet.blocks.unorderedList#listItem",
"children": [
{
"$type": "pub.leaflet.blocks.unorderedList#listItem",
"content": {
"$type": "pub.leaflet.blocks.text",
"plaintext": "DOCKER_USERNAME: Your docker username"
}
},
{
"$type": "pub.leaflet.blocks.unorderedList#listItem",
"content": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"features": [
{
"$type": "pub.leaflet.richtext.facet#link",
"uri": "https://docs.docker.com/security/for-developers/access-tokens/"
}
],
"index": {
"byteEnd": 54,
"byteStart": 42
}
}
],
"plaintext": "DOCKER_PASSWORD: Your docker password (or access token)"
}
}
],
"content": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"features": [
{
"$type": "pub.leaflet.richtext.facet#link",
"uri": "https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-a-repository"
}
],
"index": {
"byteEnd": 44,
"byteStart": 24
}
}
],
"plaintext": "Has two GitHub Secrets (create GitHub Secret):"
}
}
]
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.header",
"level": 2,
"plaintext": "Create workflow file(s)"
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"features": [
{
"$type": "pub.leaflet.richtext.facet#code"
}
],
"index": {
"byteEnd": 114,
"byteStart": 110
}
},
{
"features": [
{
"$type": "pub.leaflet.richtext.facet#code"
}
],
"index": {
"byteEnd": 155,
"byteStart": 138
}
},
{
"features": [
{
"$type": "pub.leaflet.richtext.facet#code"
}
],
"index": {
"byteEnd": 346,
"byteStart": 342
}
},
{
"features": [
{
"$type": "pub.leaflet.richtext.facet#code"
}
],
"index": {
"byteEnd": 509,
"byteStart": 494
}
}
],
"plaintext": "GitHub Actions are special jobs in GitHub which mostly run on Linux servers and can be controlled by creating yaml files in the directory .github/workflows. These special files can controll after which events these jobs should run and they give you a lot of freedom. As a regular GitHub Action user I can tell you, get used to rewriting your yaml files pretty often because you often forget to think about the small details. But without further ado, let's jump straight into creating a fitting deployment.yaml file, which will do some jobs for us:"
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.unorderedList",
"children": [
{
"$type": "pub.leaflet.blocks.unorderedList#listItem",
"content": {
"$type": "pub.leaflet.blocks.text",
"plaintext": "Push a new docker image to Docker Hub (with the newest version)."
}
},
{
"$type": "pub.leaflet.blocks.unorderedList#listItem",
"content": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"features": [
{
"$type": "pub.leaflet.richtext.facet#code"
}
],
"index": {
"byteEnd": 35,
"byteStart": 11
}
}
],
"plaintext": "Update the manifest/deployment.yaml file, so Argo CD gets notified about the new tagged image."
}
},
{
"$type": "pub.leaflet.blocks.unorderedList#listItem",
"content": {
"$type": "pub.leaflet.blocks.text",
"plaintext": "(optional) Create a new release on GitHub, so the times of releases are documented where they should be."
}
}
]
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.code",
"language": "yaml",
"plaintext": "# deployment.yaml\nname: Deployment\non:\n push:\n branches: [main]\n merge_group:\n pull_request:\n branches: [main]\n # Allows you to run this workflow manually from the Actions tab\n workflow_dispatch:\n\n# Automatically cancel in-progress actions on the same branch\nconcurrency:\n group: ${{ github.workflow }}-${{ github.event_name == 'pull_request_target' && github.head_ref || github.ref }}\n cancel-in-progress: true\n\nenv:\n REGISTRY: docker.io\n IMAGE_NAME: trueberryless/blog\n NODE_VERSION: 20\n\njobs:\n deployment:\n if: contains(github.event.head_commit.message, 'deploy') || github.event_name == 'workflow_dispatch'\n runs-on: ubuntu-latest\n permissions:\n contents: write\n steps:\n - name: Check out the repo\n uses: actions/checkout@v4\n with:\n fetch-depth: 0\n\n - name: Create tag\n run: echo \"IMAGE_TAG=$(echo $GITHUB_REF_NAME-$GITHUB_SHA)\" >> $GITHUB_ENV\n\n - name: Set up Docker Buildx\n uses: docker/setup-buildx-action@v2\n\n - name: Log in to Docker Hub\n uses: docker/login-action@v2\n with:\n username: ${{ secrets.DOCKER_USERNAME }}\n password: ${{ secrets.DOCKER_PASSWORD }}\n\n - name: Extract metadata (tags, labels) for Docker\n id: meta\n uses: docker/metadata-action@v4\n with:\n images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}\n\n - name: Build and push Docker image\n uses: docker/build-push-action@v5\n with:\n context: .\n push: true\n tags: |\n ${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}\n ${{ env.IMAGE_NAME }}:latest\n labels: ${{ steps.meta.outputs.labels }}\n\n - name: Update deployment.yaml file\n run: |\n yq eval '.spec.template.spec.containers[0].image = \"${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}\"' -i manifest/deployment.yaml\n\n - uses: stefanzweifel/git-auto-commit-action@v4\n with:\n commit_message: update deployment.json container image (automated)\n\n - uses: ncipollo/release-action@v1\n with:\n tag: ${{ env.IMAGE_TAG }}\n makeLatest: true\n body: \"A docker image has been deployed to [Docker Hub](https://hub.docker.com/r/${{ env.IMAGE_NAME }}/tags).\"",
"syntaxHighlightingTheme": "catppuccin-mocha"
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"features": [
{
"$type": "pub.leaflet.richtext.facet#code"
}
],
"index": {
"byteEnd": 49,
"byteStart": 34
}
},
{
"features": [
{
"$type": "pub.leaflet.richtext.facet#code"
}
],
"index": {
"byteEnd": 105,
"byteStart": 88
}
},
{
"features": [
{
"$type": "pub.leaflet.richtext.facet#bold"
}
],
"index": {
"byteEnd": 161,
"byteStart": 143
}
}
],
"plaintext": "After copying the contents of our deployment.yaml file and creating the new file in the .github/workflows folder, you need to do a bit of very important adapting:"
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.unorderedList",
"children": [
{
"$type": "pub.leaflet.blocks.unorderedList#listItem",
"content": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"features": [
{
"$type": "pub.leaflet.richtext.facet#code"
}
],
"index": {
"byteEnd": 21,
"byteStart": 11
}
}
],
"plaintext": "Change the IMAGE_NAME to your personal Docker Hub repository. The image name consists of your account name and the repository name. If your not sure what your image name is, you can take a look at the URL of the Docker Hub repository, it should be in there somewhere."
}
}
]
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"features": [
{
"$type": "pub.leaflet.richtext.facet#code"
}
],
"index": {
"byteEnd": 54,
"byteStart": 48
}
}
],
"plaintext": "Now you should be good to go to add the keyword deploy into any commit message to the main branch of your repository and it should automatically push a docker image to Docker Hub and update the manifest for Argo CD."
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.header",
"level": 2,
"plaintext": "Celebrate with a Coffee!"
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"features": [
{
"$type": "pub.leaflet.richtext.facet#link",
"uri": "https://github.com/cilium"
}
],
"index": {
"byteEnd": 71,
"byteStart": 65
}
},
{
"features": [
{
"$type": "pub.leaflet.richtext.facet#link",
"uri": "https://ko-fi.com/trueberryless"
}
],
"index": {
"byteEnd": 216,
"byteStart": 211
}
}
],
"plaintext": "Congratulations, you've successfully set up Argo CD with k3s and Cilium! You deserve a coffee break. Enjoy a well-earned cup, and if you'd like to share a virtual coffee with me, feel free to support my work on Ko-fi. Thank you!"
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"plaintext": ""
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.header",
"level": 2,
"plaintext": "Appendix"
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.text",
"facets": [
{
"features": [
{
"$type": "pub.leaflet.richtext.facet#code"
}
],
"index": {
"byteEnd": 41,
"byteStart": 26
}
}
],
"plaintext": "Here you have an obsolete docker-hub.yaml which we used to use because it has nice versioning strategies:"
}
},
{
"$type": "pub.leaflet.pages.linearDocument#block",
"block": {
"$type": "pub.leaflet.blocks.code",
"language": "yaml",
"plaintext": "# docker-hub.yaml\nname: Docker Image Push\non:\n push:\n branches: [main]\n merge_group:\n pull_request:\n branches: [main]\n # Allows you to run this workflow manually from the Actions tab\n workflow_dispatch:\n\n# Automatically cancel in-progress actions on the same branch\nconcurrency:\n group: ${{ github.workflow }}-${{ github.event_name == 'pull_request_target' && github.head_ref || github.ref }}\n cancel-in-progress: true\n\nenv:\n REGISTRY: docker.io\n IMAGE_NAME: trueberryless/blog\n NODE_VERSION: 18\n\njobs:\n docker-push-image:\n if: contains(github.event.head_commit.message, 'version') || github.event_name == 'workflow_dispatch'\n runs-on: ubuntu-latest\n permissions:\n contents: write\n steps:\n - name: Check out the repo\n uses: actions/checkout@v4\n with:\n fetch-depth: 0\n\n - name: Check if file exists\n run: |\n if [ -f .github/artifacts/version.json ]; then\n echo \"File exists\"\n echo \"FILE_EXISTS=true\" >> $GITHUB_ENV\n else\n echo \"File does not exist\"\n echo \"FILE_EXISTS=false\" >> $GITHUB_ENV\n fi\n\n - name: read_json\n if: ${{ env.FILE_EXISTS == 'true' }}\n id: version\n uses: zoexx/github-action-json-file-properties@release\n with:\n file_path: \".github/artifacts/version.json\"\n\n - name: save environment variables\n if: ${{ env.FILE_EXISTS == 'true' }}\n run: |\n echo \"MAJOR=${{steps.version.outputs.major}}\" >> $GITHUB_ENV\n echo \"MINOR=${{steps.version.outputs.minor}}\" >> $GITHUB_ENV\n echo \"PATCH=${{steps.version.outputs.patch}}\" >> $GITHUB_ENV\n\n - name: create environment variables\n if: ${{ env.FILE_EXISTS == 'false' }}\n run: |\n echo \"MAJOR=0\" >> $GITHUB_ENV\n echo \"MINOR=0\" >> $GITHUB_ENV\n echo \"PATCH=0\" >> $GITHUB_ENV\n\n - name: echo environment variables\n run: |\n echo ${{ env.MINOR }}\n echo ${{ env.MINOR }}\n echo ${{ env.MINOR }}\n\n - name: Major version\n if: contains(github.event.head_commit.message, 'major')\n run: |\n echo \"New major version\"\n echo \"MAJOR=$((${{ env.MAJOR }}+1))\" >> $GITHUB_ENV\n echo \"MINOR=0\" >> $GITHUB_ENV\n echo \"PATCH=0\" >> $GITHUB_ENV\n\n - name: Minor version\n if: contains(github.event.head_commit.message, 'minor')\n run: |\n echo \"New minor version\"\n echo \"MINOR=$((${{ env.MINOR }}+1))\" >> $GITHUB_ENV\n echo \"PATCH=0\" >> $GITHUB_ENV\n\n - name: Patch version\n if: contains(github.event.head_commit.message, 'patch') || github.event_name == 'workflow_dispatch'\n run: |\n echo \"New patch version\"\n echo \"PATCH=$((${{ env.PATCH }}+1))\" >> $GITHUB_ENV\n\n - name: Set up Docker Buildx\n uses: docker/setup-buildx-action@v2\n\n - name: Log in to Docker Hub\n uses: docker/login-action@v2\n with:\n username: ${{ secrets.DOCKER_USERNAME }}\n password: ${{ secrets.DOCKER_PASSWORD }}\n\n - name: Extract metadata (tags, labels) for Docker\n id: meta\n uses: docker/metadata-action@v4\n with:\n images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}\n\n - name: Build and push Docker image\n uses: docker/build-push-action@v5\n with:\n context: .\n push: true\n tags: |\n ${{ env.IMAGE_NAME }}:${{ env.MAJOR }}.${{ env.MINOR }}.${{ env.PATCH }}\n ${{ env.IMAGE_NAME }}:latest\n labels: ${{ steps.meta.outputs.labels }}\n\n - name: Check out the repo\n uses: actions/checkout@v4\n with:\n fetch-depth: 0\n\n - name: Create folder if necessary\n if: ${{ env.FILE_EXISTS == 'false' }}\n run: mkdir -p .github/artifacts\n\n - name: write_json\n id: create-json\n uses: jsdaniell/create-json@v1.2.2\n with:\n name: \"version.json\"\n json: '{ \"major\": ${{ env.MAJOR }}, \"minor\": ${{ env.MINOR }}, \"patch\": ${{ env.PATCH }} }'\n dir: \".github/artifacts/\"\n\n - uses: stefanzweifel/git-auto-commit-action@v4\n with:\n commit_message: update version.json (automated)\n\n - name: Update deployment.yaml file\n run: |\n yq eval '.spec.template.spec.containers[0].image = \"${{ env.IMAGE_NAME }}:${{ env.MAJOR }}.${{ env.MINOR }}.${{ env.PATCH }}\"' -i manifest/deployment.yaml\n\n - uses: stefanzweifel/git-auto-commit-action@v4\n with:\n commit_message: update deployment.json container image (automated)",
"syntaxHighlightingTheme": "catppuccin-mocha"
}
}
],
"id": "019f2eeb-a025-7ffe-800f-b583787c9de6"
}
]
},
"coverImage": {
"$type": "blob",
"ref": {
"$link": "bafkreibz2mnptifzcdi6oedk53ly3lztexmzlu54jogrjt5hpl6z3rmv44"
},
"mimeType": "image/webp",
"size": 675880
},
"description": "Today we'll take a look at how to set up a GitHub repository which will be deployed to a k3s cluster via Argo CD.",
"path": "/3mptxljhmn22z",
"publishedAt": "2024-07-28T18:11:00.000Z",
"site": "at://did:plc:pbjvqaziagcyv2vqodldn5op/site.standard.publication/3mpts4vzcqc2e",
"tags": [
"automation",
"deployment",
"github",
"education"
],
"title": "Setting up Continuous deployment in a GitHub repository"
}