Sign in
Log inSign up
Create cloud-native buildpacks using Github Actions

Create cloud-native buildpacks using Github Actions

A small trip to automation road

Oscar Nevarez's photo
Oscar Nevarez
·Oct 23, 2021·

5 min read

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.

Idea

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 
# buildpacks.io/docs/concepts/components/lif…
# our buildpack executes one by one the **commands**
┌────────────────────────┐
│      Build.go          │ 
└────────────────────────┘
          ▼
# Pack it for distribution and release at will
┌────────────────────────┐
│    buildpack.cnb       │
└────────────────────────┘

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)

laraboot-commander:
  commands:
    - name: Init
      run: |
        echo "Init 🔥"
        pwd
    - name: NoOps
      run: |
        echo "Hello 🗺️"
        ls -ltah
    - name: Install deps
      run: |
        echo "installing 🤖"

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"`
// }

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.

Detect

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

Build

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.

Usage

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.

#commander.yml
laraboot-commander:
  commands:
    - name: Init
      run: |
        echo "Hello World"

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
        with:
          pack-version: 0.20.0

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

      - name: Upload dist
        uses: actions/upload-artifact@v2
        with:
          name: dist
          path: dist

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'

image.png

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

Conclusion

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?