Github Actions all the way down

Using Github Actions to create new repos that also run their own Github Actions.

March 30, 2022

The lore

I spent a while exploring how Python packages are made. It’s the wild west out there. There’s too many setup files, testing frameworks, no agreed-upon directory structure, and generally enough information to make your head spin. Luckily, you can use tools like cookiecutter to quickly get started (once you figure out which cookiecutter you want to use). For my use case, I wanted to make a simple cookiecutter, but wanted to run some tests to make sure my configuration of files did all the things I expected it to do.

The workflow was:

1. Edit my `cookiecutter`. 
2. Run my cookiecutter to make a new package. 
3. Push my new package to Github. 
4. Run Github Actions. 
5. Realize I messed up in step 1.

But what if, Github Actions could use the cookie cutter repo to create ANOTHER repo to automatically generate your package? And what if that freshly cookie-cut repo would run its own Github Actions to ensure everything was properly set up? Useless? Most likely. But, I was going to do it anyway.

See it in (Github) action

GitHub Action that pushes to a different repo.

GitHub Actions in receiving repo that runs once a push has been made.

Set up

You will need two different repos, a sending repo and receiving repo. In my case, the sending repo generates a cookiecutter template for a Python package, and then the receiving repo is the output package. You don’t have to be familiar with cookiecutter templates to understand this gist, but they’re worth a quick click if organization brings you joy.

Before running your chain reaction of actions, a few pieces need to be set up. The receiving repo cannot be empty, so I just initialized it with an empty The next part is probably the most difficult if you’re unfamiliar, which is generating an SSH key, click here for instructions. Once you’ve gone into the terminal and generated the key, add the PUBLIC KEY to your SENDING repo and the PRIVATE KEY to your RECEIVING repo.

Ready, ACTION!

The GitHub Action that sets off this package creation is below, or you can see the action in the repo, for context. Note: there are difference between the code below and the code in the repo, these changes were made for clarity and should not impact performance.

# Creates cookie cutter and pushes to new repo
name: Generate Cookiecutter
    branches: [ main ]
    branches: [ main ]
    runs-on: ubuntu-latest
      - uses: actions/checkout@v2
      - uses: actions/setup-python@v2
      - name: Install requirements
        run: pip install cookiecutter
      - name: Make cookiecutter with default inputs
        run: cookiecutter --no-input .
      - name: Send to receiving repo
        uses: peaceiris/actions-gh-pages@v3
          deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }}
          external_repository: isabelizimm/making-cookies
          publish_branch: dev-branch
          publish_dir: ./my_new_package
          exclude_assets: ''

Really, the most important part here is the Send to receiving repo step. You see here that your sender deploy key is being utilized and the external repository and branch is specified. This Action will normally exclude certain files (in particular, we need .github/ from the template) on push, which is we have to manually set exclude_assets: ' ' so all files are being pushed.

Once this action is running successfully, you will see a new branch, dev-branch, on the receiving repo, making-cookies. Congratulations! The hard part is finished.

The second set of actions is less magical. This template package has built-in GitHub Actions to jump-start running tests and building docs. This second set of actions runs on push to any branch named “dev-*”, so they automatically start after the initial push to the branch from the sending repo.

Hopefully this helps (or at least gives a starting point) others who are also exploring the capabilites of CI! Feel free to contact me if you have any suggestions or struggles. :)

cheers, iz