CICD with Deno
Intro
A lot of us just want to work on our core project and not have to worry about things like documentation, workflows, status checks, and a readme file, right? I can empathize with that!! Though I'm passionate about making sure those things get done and done well, I also understand that everybody's time is limited and you just want to work on your core project.
Here's the thing. An investment in those things will pay off in the long run, especially when it comes to a good CICD pipeline. You're swapping an upfront time investment for long-term time savings. Running status checks for your pull requests, performing reliable releases and minimizing human error are just a few benefits of a good CICD pipeline.
I have used quite a few technologies in the CICD space: Azure DevOps, GitHub Actions, Jenkins, PowerShell scripts, YAML files, NUKE for dotnet projects, and more. However, one thing that I've used to help with CICD recently is Deno and I think it's a game changer!
What is Deno?
If you know what NodeJS is, then the concept is the same. Deno is a secure runtime for JavaScript and TypeScript built with Rust and is the predecessor to NodeJS. It's built by the same person who created NodeJS, Ryan Dayl. As stated by Ryan Dayl himself, his experience and past NodeJS mistakes have helped drive the design of Deno. I love it and I think you will too!!
Check out Deno at https://deno.land.
Some Deno benefits
One thing that makes Deno awesome is the ability to execute scripts directly from the web using a URL. That's right! You can execute a script directly from the web without downloading NodeJS and using npm to install a package into the notoriously bloated node_modules folder. Just give Deno a URL and if the content returned is a valid script, it executes.
Deno also takes your imports into account. Executing one file that imports another file will work as expected without the worry of dependencies. Deno will download the dependencies and execute them as needed.
After using other technologies, then researching Deno I thought, "Why can't I just use this to help with my CICD pipeline?" I can and I have!
Here are some of the things you can use Deno scripts for:
- Perform validation on branches and versions
- Pull information like issues and pull requests about your GitHub projects
- Improve and automate tasks when performing releases
- Improve pull request status checks
- Use the same scripts across multiple repositories for repeatability
Using these scripts hasn't only made my pipelines reusable and reliable but has also made them quick and easy to develop and maintain.
So how do you do use Deno for CICD?
There are different ways to go about it depending on your needs. You can host the Deno scripts alongside your project or in a central location like another repository. I have done both with no problems and great success.
I currently centralize all of my GitHub Organization's CICD needs like Deno scripts and reusable workflows in a single repository named Infrastructure. Having a centralized repo is also nice because I can version my scripts so I can methodically roll out changes to the consuming projects when I need to.
If you want to check out some of my scripts or GitHub workflows that are used across my entire GitHub organization, you can check them out here.
Now that I've sold you on Deno 😉, let's create a status check workflow that executes a Deno script and validates that your pull request head branch is valid.
Project setup
1. Create or choose repo
Create a new or choose an already existing GitHub repository.
2. Create new folder & script file
In the root of your repository (or location of your choice), create a folder called cicd, then inside that folder, create a file called validate-feature-branch.ts.
It does not matter where you put your scripts. That is up to you and your needs.
Add arg validation
Add the following code to check that a single argument is being passed into the script from the workflow.
if (Deno.args.length != 1) {
console.log("::error:: The script requires exactly one argument that is the name of a GIT branch.");
Deno.exit(1);
}
The Deno eco-system has a runtime API for various tasks just like NodeJS does.
One of those tasks is being able to get the arguments that you pass into any Deno script
using Deno.args
array.
In the code, we added a check to verify whether or not a single argument was passed in.
If zero or more than one argument has been passed in, the script will exit with an error code of 1.
Using the Deno.exit()
function with any number argument greater than zero will fail the GitHub workflow.
Branch name and regex
We can use some handy regex to validate whether or not the name of the branch meets our branch naming requirements. Add the following lines of code to get the argument value and some regex to use for validation.
Example branch name for the following regex: feature/123-my-branch
.
A great resource for learning regex is Regex101!!
Add the following constants to the script which will be used for the validation and the name of the branch itself.
const branchNameRegEx = /^feature\/[1-9]+-(?!-)[a-z-]+$/gm;
const branchName = Deno.args[0].trim();
Checking the branch name
Now that we have our branch name and some regex to use for validation, add the following if..else
block.
The workflow will pass or fail based on whether or not the branch name meets the validation requirements dictated by
the regex.
// If the branch name is valid
if (branchNameRegEx.test(branchName)) {
console.log(`::notice::The branch '${branchName}' is valid!! 🎉.`);
} else {
let errorMsg = `::error::The branch '${branchName}' is invalid.😤`;
errorMsg += `\nIt should follow the regex pattern: ${branchNameRegEx}`;
console.log(errorMsg);
Deno.exit(1);
}
The pieces of text ::error::
and ::notice::
are called workflow commands. These will be used by the GitHub system
to display the text in a particular visual format in the GitHub console.
Click here for more info.
Creating a workflow
If you are unfamiliar with GitHub workflows, check out the GitHub Workflow Docs.
Now that we have our script, we can create a workflow that will execute the script. Follow the steps below:
1. Create workflow folders
If these folders don't already exist, create a folder with the name .github in the root of the repository and then create a folder named workflows inside the .github folder.
2. Create workflow file
In the .github/workflows folder, create a file with the name branch-status-check.yml.
3. Basic workflow structure
We'll give our workflow some basic structure like the workflow name, a pull request trigger, and a job with a step. The first step will be the checkout action which is required to get access to the script file in the repository.
name: ✅Branch Status Check
on:
workflow_dispatch:
pull_request:
branches: master
jobs:
branch_status_check:
name: Branch Status Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
I like to use emojis with some of my workflow names. I find it makes it easier to visualize and manage in the GitHub web interface.
I use '✅' for status checks and '🚀' for releases. You can of course can do whatever you want!!
4. Setup Deno
The Deno team has an action in the marketplace for making it easy to install Deno in the GitHub runner which is required before you execute any Deno commands. The good thing about Deno is that it's super small, cross-platform, and has no dependencies.
Add the following step to the workflow below the checkout step to set up Deno.
- name: Setup Deno
uses: denoland/setup-deno@v1
with:
deno-version: v1.x.x
5. Add script execution step
Now that we have Deno set up, we can execute our script. Add the following step to the workflow below the Deno setup step.
- name: Disable Testing Env
run: deno run "${{ github.workspace }}/cicd/branch-status-check.yml.ts" "${{ github.head_ref }}";
The ${{ github.head_ref }}
is a GitHub workflow expression that will get the name of the branch
that is being merged into the master branch. In this case, it would be the feature branch that you
want to validate.
Go to the workflow expressions GitHub documentation for more info.
The deno run
command executes the TypeScript file we created earlier. Any string
values separated by a space
after the script file path is treated as arguments.
The run command syntax is as follows:
deno run [options] [script file path | script url] [arg1, arg2, ...argN]
Deno has a very nice security model that requires you to explicitly allow access to certain kinds of operations like the network, environment variables, file system, and more. In this case, we don't need to provide and allow permission switches/options because we aren't doing anything that requires permission.
If you try to execute a script without explicitly allowing access to certain operations, you will get an error with a descriptive message explaining exactly what permission is needed and why.
Go to the Deno Permissions documentation for more info.
That's it!! Very simple and the only limit is your imagination!!
Summary
Deno is a great option for creating utility scripts for whatever your needs are. It can be local scripts on your machine to help automate your development workflow, scripts to do simple tasks in a project such as cleanup, or this case, for GitHub workflows to help automate your CICD process. I have personally been using this all over my organization and it's been a wonderful experience!!
Resources
Full TypeScript Source
if (Deno.args.length != 1) {
console.log("::error:: The script requires exactly one argument that is the name of a GIT branch.");
Deno.exit(1);
}
const branchNameRegEx = /^feature\/[1-9]+-(?!-)[a-z-]+$/gm;
const branchName = Deno.args[0].trim();
// If the branch name is valid
if (branchNameRegEx.test(branchName)) {
console.log(`::notice::The branch '${branchName}' is valid!! 🎉.`);
} else {
let errorMsg = `::error::The branch '${branchName}' is invalid.😤`;
errorMsg += `\nIt should follow the regex pattern: ${branchNameRegEx}`;
console.log(errorMsg);
Deno.exit(1);
}
Full Workflow Source
name: ✅Branch Status Check
on:
workflow_dispatch:
pull_request:
branches: master
jobs:
branch_status_check:
name: Branch Status Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Deno
uses: denoland/setup-deno@v1
with:
deno-version: v1.x.x
- name: Disable Testing Env
run: deno run "${{ github.workspace }}/cicd/branch-status-check.yml.ts" "${{ github.head_ref }}";
If you have any questions, want to chat, or contribute to any of my open-source projects, feel free to reach out to me on our discord channel or X located at the top right.
Join Our Community
We believe that everyone has something unique to offer, and we invite you to contribute to Velaptor and the KinsonDigital organization.
Open for more information
-
Contribute Your Skills: Whether you're a seasoned developer or just starting, your unique skills can truly make a difference. Your power to contribute is immense, and you can do so by:
- Picking up issues and submitting pull requests with code improvements, bug fixes, or new features.
- Providing feedback, reporting issues, or suggesting enhancements through GitHub issues.
-
Support Us Financially: Consider supporting us financially if you can. Your contributions can help us cover expenses like hosting and infrastructure costs, and support the ongoing development and maintenance of all KinsonDigital projects. You can donate or sponsor us on:
Remember, every contribution, no matter how small, is valuable and appreciated. By contributing, you're not just helping us; you're helping the entire community by making game development better and more efficient. Join us on this and be involved in making something amazing and unique together!