GitHub Action: GHCR Containers & Helm Manifests For Preview
Hey guys! Today, we're diving deep into creating a super cool GitHub Action. This action will automatically build and publish containers to GitHub Container Registry (GHCR), update Helm manifests, and generate clear instructions on how to run these previews. The best part? It does this for every pull request and provides a live preview for the main branch. How awesome is that? This is super handy, especially when you're working on projects like OWASP's WrongSecrets CTF Party, where quick and easy previews can make a huge difference. Let's get started!
Why a GitHub Preview Action?
Before we jump into the how-to, let’s talk about the why. Imagine you're part of a team working on a complex application. Every pull request brings in new changes, and it’s crucial to see how these changes play out in a real environment. Manually building containers, updating manifests, and sharing instructions can be a real pain. This is where our GitHub Preview Action shines. It automates all these steps, saving you tons of time and reducing the chances of errors. Plus, it gives everyone on the team a straightforward way to preview changes, making collaboration smoother and more efficient. Think about it – no more “Does this work?” questions; just a clear, concise preview link. How cool is that?
Core Components of the Action
So, what will this action actually do? Let’s break it down:
-
Container Building and Publishing: First off, our action will take the code changes in a pull request and build a container image. This isn’t just any build; it’s a fully automated process that ensures every commit gets a fresh container. Once built, the container is pushed to GHCR, GitHub’s own container registry. This keeps everything neat and tidy within the GitHub ecosystem.
-
Helm Manifest Updates: Next up, we need to update our Helm charts. Helm is a fantastic tool for managing Kubernetes applications, and our action will automatically tweak the manifests to point to the newly built container image. This means that every preview environment is running the exact code from the pull request. How neat is that?
-
Instruction Generation: Now, for the magic touch – clear, concise instructions. Our action will generate a message within the pull request that tells you exactly how to run the preview. No more guessing or digging through documentation; it’s all right there. These instructions will likely include Helm commands or other relevant steps to get the preview environment up and running. Think of it as a user manual, but for your previews.
-
Main Branch Preview: Last but not least, we’ll set up a live preview for the main branch. This means that whenever changes are merged into the main branch, a new preview environment is automatically deployed. This is perfect for showcasing the latest features and ensuring everything is running smoothly. It’s like having a demo environment that’s always up-to-date. Awesome, right?
Step-by-Step Implementation
Alright, let's get our hands dirty and build this thing. Here’s a step-by-step guide to creating our GitHub Preview Action:
1. Setting Up the GitHub Action Workflow
First, we need to create a new workflow file in our repository. This file tells GitHub Actions what to do. Head over to your repository, and create a new file under .github/workflows
. Let’s call it preview-action.yml
. Inside this file, we'll define the workflow:
name: Preview Environment Action
on:
pull_request:
types: [opened, synchronize]
push:
branches: [main]
jobs:
preview:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Docker
uses: docker/setup-docker@v2
- name: Login to GHCR
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push container
id: build-container
run: |
IMAGE_NAME=ghcr.io/${{ github.repository }}/my-app:${{ github.sha }}
docker build -t $IMAGE_NAME .
docker push $IMAGE_NAME
echo "IMAGE_NAME=$IMAGE_NAME" >> $GITHUB_OUTPUT
- name: Update Helm manifest
id: update-helm
run: |
# Your script to update Helm manifest here
# This might involve using `sed` or `yq` to modify the image tag
echo "MANIFEST_UPDATED=true" >> $GITHUB_OUTPUT
- name: Generate instructions
id: generate-instructions
run: |
IMAGE_NAME=${{ steps.build-container.outputs.IMAGE_NAME }}
# Your script to generate instructions here
# This might involve creating a Helm command or providing other setup steps
echo "INSTRUCTIONS='To run the preview, use: helm upgrade --install my-app ./helm --set image=$IMAGE_NAME'" >> $GITHUB_OUTPUT
- name: Post instructions to PR
if: github.event_name == 'pull_request'
uses: actions/github-script@v6
with:
script: |
const INSTRUCTIONS = '${{ steps.generate-instructions.outputs.INSTRUCTIONS }}';
const issue_number = context.payload.pull_request.number;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue_number,
body: `Preview Instructions:\n${INSTRUCTIONS}`
});
Let’s break this down:
-
name
: A friendly name for our workflow. -
on
: This tells GitHub when to run the workflow. We’re triggering it onpull_request
(when a pull request is opened or synchronized) andpush
events on themain
branch. -
jobs
: This section defines the actual tasks. We have one job calledpreview
. -
runs-on
: Specifies the type of machine to run our job on. -
steps
: These are the individual steps in our job.actions/checkout@v3
: Checks out our code.docker/setup-docker@v2
: Sets up Docker.docker/login-action@v2
: Logs into GHCR using the GitHub token.Build and push container
: Builds our Docker image and pushes it to GHCR. We also set an output variableIMAGE_NAME
.Update Helm manifest
: This is where you’ll add your script to update the Helm manifest. You might use tools likesed
oryq
to modify the image tag.Generate instructions
: Here, we generate the instructions for running the preview. We use theIMAGE_NAME
from the previous step and create a Helm command.Post instructions to PR
: Finally, we post the instructions as a comment in the pull request using theactions/github-script@v6
action.
2. Building and Pushing the Container
The Build and push container step is crucial. We’re using Docker commands to build and push our container image. Here’s a closer look:
IMAGE_NAME=ghcr.io/${{ github.repository }}/my-app:${{ github.sha }}
docker build -t $IMAGE_NAME .
docker push $IMAGE_NAME
echo "IMAGE_NAME=$IMAGE_NAME" >> $GITHUB_OUTPUT
IMAGE_NAME
: We construct the image name using the repository name and the commit SHA. This ensures each commit gets a unique image.docker build
: Builds the Docker image.docker push
: Pushes the image to GHCR.echo "IMAGE_NAME=$IMAGE_NAME" >> $GITHUB_OUTPUT
: Sets theIMAGE_NAME
as an output variable so we can use it in later steps.
3. Updating the Helm Manifest
The Update Helm manifest step is where we modify our Helm chart to use the new container image. This usually involves changing the image tag in the values.yaml
file or a similar configuration. You can use tools like sed
or yq
to do this. Here’s an example using sed
:
sed -i "s|image: .*|image: $IMAGE_NAME|g" helm/values.yaml
This command replaces the image:
line in your helm/values.yaml
file with the new IMAGE_NAME
. Make sure to adjust the path and the command to match your specific Helm setup.
4. Generating Instructions
The Generate instructions step creates the message that will be posted in the pull request. This message should tell users how to run the preview environment. Here’s an example:
IMAGE_NAME=${{ steps.build-container.outputs.IMAGE_NAME }}
echo "INSTRUCTIONS='To run the preview, use: helm upgrade --install my-app ./helm --set image=$IMAGE_NAME'" >> $GITHUB_OUTPUT
We’re constructing a Helm command that users can copy and paste to deploy the preview. You can customize this message to include any other relevant information, such as environment variables or database setup steps.
5. Posting Instructions to the Pull Request
The Post instructions to PR step uses the actions/github-script@v6
action to post the instructions as a comment in the pull request. Here’s the code:
const INSTRUCTIONS = '${{ steps.generate-instructions.outputs.INSTRUCTIONS }}';
const issue_number = context.payload.pull_request.number;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue_number,
body: `Preview Instructions:\n${INSTRUCTIONS}`
});
This script retrieves the instructions from the output variable, gets the pull request number, and uses the GitHub API to create a comment with the instructions.
6. Testing the Action
Now that we’ve set up our action, it’s time to test it out. Create a new pull request in your repository, and you should see the action running. If everything is set up correctly, you’ll get a comment in the pull request with the instructions on how to run the preview. How cool is that?
Best Practices and Considerations
Before we wrap up, let’s cover some best practices and things to keep in mind when using this action:
- Security: Make sure to store any sensitive information, such as API keys or passwords, as GitHub Secrets. This keeps them safe and secure.
- Resource Management: Preview environments can consume resources, so it’s essential to have a strategy for managing them. You might want to automatically delete old previews or set resource limits.
- Customization: This action is a starting point. You can customize it to fit your specific needs. For example, you might want to add steps to run tests or set up databases.
- Error Handling: Implement proper error handling to catch and report any issues. This can help you quickly identify and fix problems.
Real-World Applications
Let's talk about real-world applications. This GitHub Preview Action isn’t just a cool tech demo; it’s a practical tool that can make a huge difference in your development workflow. Think about projects like OWASP’s WrongSecrets CTF Party. This is a hands-on project that teaches developers about security vulnerabilities. With our preview action, contributors can quickly test their changes and see how they affect the application’s security. No more waiting for manual deployments or struggling with complex setups. Just a simple, automated preview that makes collaboration and testing a breeze. For projects like this, where quick feedback and easy testing are crucial, this action is a game-changer.
Troubleshooting Common Issues
Even with the best setup, things can sometimes go wrong. Let’s look at some common issues and how to troubleshoot them:
- Container Build Failures: If your container fails to build, check your Dockerfile for errors. Make sure all dependencies are correctly specified and that the build process is working as expected.
- GHCR Login Issues: If you’re having trouble logging into GHCR, double-check your GitHub token and ensure it has the necessary permissions. Also, verify that your
username
andregistry
are correctly configured in thedocker/login-action
step. - Helm Manifest Errors: If your Helm manifest updates are failing, review your
sed
oryq
commands. Ensure they are correctly targeting the image tag and that the syntax is correct. - Instruction Generation Problems: If the instructions are not being generated correctly, check the script in the Generate instructions step. Make sure you’re correctly retrieving the
IMAGE_NAME
and constructing the Helm command. - Pull Request Comment Issues: If the comment is not being posted in the pull request, verify that your GitHub Script has the necessary permissions and that the
issue_number
is being correctly retrieved.
Conclusion
So there you have it! We’ve walked through the process of creating a GitHub Preview Action that builds GHCR containers, updates Helm manifests, and generates clear instructions for running previews. This action can significantly streamline your development workflow, making it easier to collaborate, test changes, and deploy previews. Whether you’re working on a personal project or a large-scale application like OWASP’s WrongSecrets CTF Party, this action can help you ship code faster and with more confidence. Now, go ahead and give it a try. Happy coding, guys!