DEV Community

Cover image for Create cloud-native buildpacks using Github Actions
Oscar Nevárez
Oscar Nevárez

Posted on

Create cloud-native buildpacks using Github Actions

I've been working with CNB technology a lot during the last few months particularly with pack and PackIt library. Overall it's been a lot of fun and learning along the way, I'm not going to lie here, the learning curve seemed propelled by a rocket but turned out to be quite simple once you grab your head around the new terminology and concepts . Just like anything in tech.

Once I understood the hows and whys then I was buildpacking this and buildpacking that, pretty soon I found myself doing repetitive jobs that were begging to be automized. I'm talking about simple script bundled into buildpacks for better distribution, organization, and versioning plus all the benefits CNB brings to the table.

I decided to invest some time in the making of a more generic tool to help me with this problem.

I needed a little something that took a list of commands and turn them into a valid build pack. In my mind, I could see and almost touch the form and shapes of this new toy, how the sequence of events was executed to coordinate the creation of this on the fly _buildpack.


The easiest way to explain the process is with the diagram below.

# From a working PackIt project, parametrize and map input commands 
# taken from a YML file
│      Base file        │ 
# Compile the modified project so once the build process kicks 
# our buildpack executes one by one the **commands**
│      Build.go          │ 
# Pack it for distribution and release at will
│    buildpack.cnb       │
Enter fullscreen mode Exit fullscreen mode

Whiteboard time

What I first did was draft a very simple definition that allowed me to see how a regular user such as me, in this case, would like to define a list of commands. I decided to use YAML for this. Here's how a command file looks like.

This isn't a Github Action file although I borrowed some of the key names (name and run)

    - name: Init
      run: |
        echo "Init 🔥"
    - name: NoOps
      run: |
        echo "Hello 🗺️"
        ls -ltah
    - name: Install deps
      run: |
        echo "installing 🤖"
Enter fullscreen mode Exit fullscreen mode

This list could as well be defined as a list of "strings" but I foresee probably as a next step eventually I'd like to be able to pass extra parameters, environment variables, etc. Just like Github allows it. So the final definition looks something like the one below.

// type Commander struct {
//  WorkingDir string `yaml:"directory"`
//  Commands   []struct {
//      Name string `yaml:"name"`
//      Run  string `yaml:"run"`
//  } `yaml:"commands"`
// }
Enter fullscreen mode Exit fullscreen mode

Then I thought, what would be the inputs I'd like to accept for the creation of buildpacks from Gh pov? That was a question easy to answer. Being familiar with pack cli I decided to ask just for name and version.


For the scope of the project, let's agree the detection will always occur and the buildpack will always participate in the build plan.


The build part was quick because honestly executing a bash command or script in GO might be the easiest thing to do. Nevertheless, I found some blocks along the road that I didn't foresee in my planning.

Such as this one:

ERROR: failed to build: the launch, cache and build flags should be in the types table of /layers/xxx. Basically, if we're authoring a buildpack from a template then allowing the user to provide a name for it, the name provided has to be considered during the build process. We need to grab the name of a buildpack directly from the buildpack.toml file.

Also, the constant unknown of where I am stood (what context or path) and why it fails while running:

  • Locally
  • Locally with WSDL & ubuntu
  • Same repo remotely with Github actions
  • Another repository (consumer) using the Github action

At the end, I can say that for this being a quick prototype it actually works really good and might even be a more robust thing down the road.


Ok, how do we use this Github Action that creates buildpack for us? I'm glad you ask, couldn't be simpler.

First, create your commander file. It can contain several or just one command.

    - name: Init
      run: |
        echo "Hello World"
Enter fullscreen mode Exit fullscreen mode

As a part of my workflow I will use 3 actions:

  • The setup-pack action. This will set up pack cli and other util tools. Check out the repo if you feel interested.
  • Our commander action
  • actions/upload-artifact@v2
# .github/workflows/worklfow.yml
      - uses: buildpacks/github-actions/setup-pack@v4.1.0
          pack-version: 0.20.0

      - name: Gh action
        uses: laraboot-io/laraboot-commander/actions/commander@master
          name: my-buildpack
          version: 0.0.2
          file: commander.yml

      - name: Upload dist
        uses: actions/upload-artifact@v2
          name: dist
          path: dist
Enter fullscreen mode Exit fullscreen mode

And if everything works fine it will look like this:

    'create'  buildpack.toml
    'create'  bin/build
    'create'  bin/detect
Successfully created 'my-buildpack'
Successfully created package 'dist/my-buildpack.cnb'

Enter fullscreen mode Exit fullscreen mode


Then as a part of your workflow or in another project you might use this buildpack as follows:

mkdir -p app
pack build sample-app --path app --buildpack my-buildpack.cnb --builder cnbs/sample-builder:bionic
Enter fullscreen mode Exit fullscreen mode


We've seen how to create a buildpack from a YML file using a custom GitHub action but the same can be replicated for any other CI runner easily.

We're going to hear more and more about buildpacks in the years to come. It's a game-changer for developers and operators in an era of automation and continuous integration. The source code of this post is available here

Where to go from here?

Discussion (0)