Reaching new heights in standardizationGithub rulesets are a great way to ensure that your pull requests (PRs) meet certain criteria before they are allowed to merge. However, while these rulesets provide several options, they are not all encompassing. Status checks serve as a way to enhance rulesets; requiring them can add strictness that’s not available by default.One way a status check can be created is through a Github Action workflow; a Github Action is an automation process whose logic can be triggered in several ways, with a workflow being the piece where the main business logic fires off. Workflows, through their jobs, can return passing or failing codes. These codes can be utilized to rule a status check as successful or not. In this article, we’ll go over how to build a workflow …
Reaching new heights in standardizationGithub rulesets are a great way to ensure that your pull requests (PRs) meet certain criteria before they are allowed to merge. However, while these rulesets provide several options, they are not all encompassing. Status checks serve as a way to enhance rulesets; requiring them can add strictness that’s not available by default.One way a status check can be created is through a Github Action workflow; a Github Action is an automation process whose logic can be triggered in several ways, with a workflow being the piece where the main business logic fires off. Workflows, through their jobs, can return passing or failing codes. These codes can be utilized to rule a status check as successful or not. In this article, we’ll go over how to build a workflow that returns a status code based on a branch naming convention, as well as how we incorporate the workflow as a status check to a ruleset.You can follow along with the org-mushroom-kingdom/bash-git-script repository here, specifically looking at the .github/workflows directory and the sc-team-luigi-naming.yml within.Note that you will jump around a bit in this article due to the way this particular workflow and ruleset were developed. The article makes this clear when this is the case. When referring to other sections in this article, , with a colon (:) indicating subsections is used (ex. If referring to the subsection regarding event triggers in the workflow, this would be written as Creating the Workflow: Adding Event Triggers)Lastly, there are several links throughout the article. These are listed in the section in a citation-like format, roughly in order of appearance.Imagine you are part of an organization named org-mushroom-kingdom and you are on the team team-luigi. team-luigi has an application they are working on: any release for this application is associated with a branch titled team-luigi/* (where * is a wildcard representing more characters, so release branches could be things like team-luigi/v1.0, team-luigi-v1.1.1, etc.).team-luigi has a requirement regarding branches that get merged into a release branch. Specifically, such branches need to follow the naming convention feature/team-luigi/* for standardization purposes. If the branch does not follow this naming convention, it should not be allowed to merge into the release branch.A branch ruleset doesn’t have an option for this particular scenario. However, we can encapsulate this logic into a status check, whose logic is stored in a Github Action workflow. In our ruleset, we can require that status check to pass to allow merging.We create the workflow first for two reasons:The workflow is going to contain the logic that performs the naming convention check, so working on the ruleset to enforce the check doesn’t make sense since the check isn’t completed yet.When a ruleset is specified to require passing status checks, it needs at least one of those status checks explicitly listed. Assuming you don’t have any other status checks, you won’t be able to save the ruleset with “required status checks” enabled if you haven’t yet made the check referenceable.Let’s first start by creating a workflow file in the .github/workflows directory. We will name it sc-team-luigi-naming.yml.We should define this workflow to fire off when a pull request is opened and the target branch for that pull request is a branch that begins with team-luigi/. Thus, the on syntax should look like:We also want to be able to test this workflow without having to manually open a pull request each time. For the moment, we’ll just add workflow_dispatch to indicate this action can be run manually. We’ll come back to workflow_dispatch later and flesh it out a bit to assist in manual testing (see ).Adding Environmental VariablesNext, we’ll establish several environmental variables.: In the workflow file, I have these defined at the top-level (workflow-level), but it’s probably more appropriate to put them at job-level. However, this workflow only has one job, so it’s not really an issue in this case.Let’s build the job next:The name key is essential here — it’s needed to properly reference the job in our ruleset. runs-on indicates we’re using a Github-hosted runner, ubuntu-latest, a Linux virtual machine. We also need to give permissions to the GITHUB_TOKEN to write to pull requests. This is so we can inform the user via a pull request comment their branch naming convention is incorrect.Next, let’s get to the steps that will perform the naming convention check and a follow-up logic in the event the source branch doesn’t follow this convention. Observe the following Github gist:Note: If you’re following along with the workflow file sc-team-luigi-naming.yml you’ll notice I am skipping two steps; one just performs print statements, and the other is for manually testing this action (it will be covered in the In the first step in the above gist, we use a simple Bash string comparison to assess if the source branch begins with the specified naming convention:If it does, we exit with a passing status code (0). This status code gets passed to the PR, and since it’s successful, the source branch is allowed to be merged.If it does not, we exit with a failing status code (1).The PR will receive this status code and allow or prevent the branch from being merged when incorporated with the ruleset.As a user-friendly endeavor, we will save the pull request audience a click by automatically commenting on the PR why the status check failed. We set up the next step with an if: failure() clause so the step will only be performed if a previous step failed. Then, we use the Github CLI gh pr comment command, which in turn makes a call to the Github API to perform this action. (This is why we include GH_TOKEN in our environment variables and specify the permissions in our job.)At this point, we are at a state where the logic in this workflow can be incorporated as a status check in the ruleset. However, let’s not jump the gun — it’s time to perform some simple preliminary testing.Technically, you could skip to incorporating the workflow/job logic into a ruleset and test it by opening pull requests with varying source branch names. But rushing to do so is foolish, because when you inevitably run into an issue there’s some overhead depending on what that issue is — will you have to disable the ruleset temporarily, check your spelling of your source branch, or something else? It makes more sense to perform some simpler tests first to save yourself time in the long run.The brunt of our tests during development can be performed manually via the Github Actions UI. We will have to create some pull requests towards the end of manual testing, but for this particular scenario we need only create two. The sections below detail the overall testing process.We’ll also make these branches in the repo itself. In each branch, we’ll make a dummy commit so some sort of difference shows up. Then, we’ll actually make pull requests to the team-luigi/v1.0 branch in the repo, and close them. This is important — the numbers of those pull requests are part of the string values in the choice input. It’s more important for the negative test case; when you begin to manually test this a bunch, all of the commenting activity will stay on that single PR. since this particular workflow can be triggered by reopened pull requests, you can just reopen these PRs to test the workflow more organically later on.We still need to actually evaluate the value of the input, however. This is why we set up the PR_NUMBER, SOURCE_BRANCH, and TARGET_BRANCH as environmental variables. We’ll create a step before the evaluation step to set those variables to specific values, if we’re running the workflow manually:Having the step set up like this makes it so the logic is unchanged in the steps that perform core logic. All we’ve done is reassigned the value of the environmental variables those steps reference.Running Preliminary TestsThe Github Actions UI makes it very easy to run our tests manually. All we need to do is navigate to the Actions page, find our workflow, hit the button, and choose which of our inputs to use. All we’re looking for at the moment is to see if our workflow is passing/failing as expected.Preliminary Happy Path TestingIf we run our first test with the first option (the source branch matches the naming convention), we should see our run is successful as indicated by the green check icon (this is a graphical representation of exit code 0).Clicking on the run opens a page where we can see more details. Note the green text that indicates the run was successful.Preliminary Negative TestingNext, we’ll do a run with the second option (the source branch does NOT match the naming convention, AKA our negative test case). This time, we should see the run failed, as indicated by the red “X” icon (this is a graphical representation of exit code 1).Once again, click on the run to see more details. Note the red text that indicates the run failed, as well as the subsequent step that makes the API call to comment on the PR.If we navigate over to the corresponding pull request (#90 in this case), we should see a comment from the github-actions bot explaining why the pull request cannot be merged.Now that confidence in our manual tests has been assured, we should incorporate the workflow into a ruleset. After that, we can start testing with actual pull requests. (Go to the Incorporation Into the Ruleset section below, then come back up to the More Organic Tests — Pull Requests section)More Organic Tests - Pull RequestsAfter the workflow has been incorporated into the ruleset (see Incorporation Into the Ruleset below), we need to test the workflow with actual pull requests.Since we already made (and closed) pull requests to a team-luigi target branch, testing how this workflow will fire when a pull request is opened (or reopened, in our case) is almost effortless. Simply reopen the pull requests: A dynamic window towards the bottom of the pull request should appear showing the status check as pending, followed by a result the the check passed or failed (see screenshot below for an example of each result).You are free to make more pull requests as you see fit, but as long as you don’t merge the ones you already made, you should be able to keep testing them via closing and reopening.After this testing is complete, we have determined that our workflow and its functionality in our ruleset is working properly. Take the last jump to the section!Incorporation Into the RulesetAfter initial manual testing has been performed and proven the workflow functions as expected, we can incorporate it into a ruleset. , you will need admin permissions or to be assigned a custom role with the “edit repository rules” permission to do this. Also, this process assumes you are incorporating the workflow into a branch ruleset as opposed to a classic branch protection rule. The two are similar, but not the same.We’ll create a ruleset named team-luigi-rules. In the ruleset settings, under , we will input team-luigi/ to indicate we want this ruleset to apply to any branch that begins with team-luigi/. This accounts for when the team has a branch for a newer release (ex team-luigi/v1.1.1).Next we’ll check the checkbox Require a pull request before merging so that users can’t just push to this branch whenever they want. Having a pull request be required also makes pushes to the release branch more standardized, and more obvious when a status check fails (see Testing: More Organic Tests — Pull Requests for details). We don’t need to change anything else here.We then check the Require status checks to pass checkbox. Click the dropdown to reveal the Status checks that are required area. Hit the button. This reveals a text box; go to the workflow file and copy the value of the -level name key (Check source branch naming convention) and paste it in the text box (you can also try to search for the job via a word it includes). This should result in a hit, with a selectable checkbox appearing next to the value. Checking the box and clicking anywhere else after adds our workflow job as a status check.Lastly, double check the rule is enabled by scrolling to the top of the page and confirming is set to . Then, scroll to the bottom of the page and hit . Now, you can begin to test your workflow with actual pull requests — jump to the Testing: More Organic Tests — Pull Requests section.By the end of this article, you should be able to grasp how to configure a simple workflow job into a status check for a Github ruleset and test it rigourously. This logic can be greatly expanded upon to fit your particular repository’s needs, adding further scrutiny to what’s getting pushed. The versatility of Github Actions allows you to trigger these workflows/status checks on virtually any repository related event, automating processes that might be tedious or prone to error otherwise. The world is literally at your fingertips — try working with these processes in your repo today!