2 minute read

Usually, in many projects, we need to create different repositories with a similar folder structure. In this scenario, you can use GitHub Templates, which I will cover in another blog post. For now, in this post, I’d like to discuss how to use GitHub Actions to scaffold monorepo repositories using CookieCutter in a self-service manner.

CookieCutter

CookieCutter is a tool that help us creating templates using Jinja2 as templating language. Some examples are:

apiVersion: v2
name: {{cookiecutter.service}}
description: The helm chart for {{cookiecutter.service}}
type: application
version: 0.1.1
appVersion: 0.1.1
dependencies:
  - name: ecs-service-generic
    version: ~1
    repository: "oci://my-ecr-repo/helm-charts"

In addition, this tool uses an answer file called cookiecutter.json to set up the default values for each variable in the templates. We can take advantage of this file to configure default settings for our environments, such as:

{
    "tenant": "tenantName",
    "service": "serviceName",
    "environments": {
        "dev": {
            "accountId": "11111",
            "vpcId": "11111",
            "albSubnets":"11111",
            "serviceSubnets":"11111",
            "permissionsBoundary": "11111",
            "kmsARN": "11111",
            "albDns": "11111",
            "domain": "11111",
            "zoneId": "11111",
            "securityGroupECS": "11111",
            "certificateArn": "11111"
        }
    }
}

Later, by running the following command, we can create the template. Please note that we are passing the tenant and service as parameters in the command and using –no-input to avoid prompting for values, relying solely on the responses from the cookiecutter.json file.

cookiecutter -s -f --no-input _template tenant=tenant1 service=myservice1

We are using -f to overwrite the content if folder exists, it allow us to creates new files if needed, and -s to skip render files again if already exists to don’t overwrite if the user has made any change.

You can find more information about it in the official documentation.

Github Actions

Now, let’s use GitHub Actions to establish a self-service process, allowing users to automatically create a pull request with their requirements for new tenants or services.

First, let’s create the user input interface, define the parameters, and configure it as a manually triggered pipeline:

name: Create new service or tenant
run-name: $ is creating new service or tenant 🚀
on:
    workflow_dispatch:      # Manual execution
        inputs:
            tenant:         # Static parameters
                description: 'Tenant'
                type: choice
                options:
                    - tenant1
                    - tenant2
                    - tenant3
                    - tenant4
                required: true
            serviceName:    # Dynamic parameters
                description: 'Service Name'
                type: string
                required: true

And now, we use these parameters in the workflow execution and automatize the process.

jobs:
    scaffolding:
        runs-on: ubuntu-latest
        steps:
        - uses: actions/checkout@v4
        - run: |
            echo "Building ${{ inputs.serviceName }} on ${{ inputs.tenant }}"
            python3 -m pip install --user cookiecutter
            python3 -m cookiecutter -s -f --no-input ${{ github.workspace }}/_template tenant=${{ inputs.tenant }} service=${{ inputs.serviceName }}
        - name: Create Pull Request
          uses: peter-evans/create-pull-request@v6
          with:
            token: ${{ secrets.TOKEN }}
            title: "Adding service ${{ inputs.serviceName }} to ${{ inputs.tenant }}"
            branch: feat/add-${{ inputs.serviceName }}

We can create a CODEOWNERS file set up the PR’s approvers for each tenant. Here more information.

You can find the full code in the repository