Define a Python Script Inside a GH Actions Workflow File
Janne Kemppainen |GitHub Actions gives you lots of freedom to define custom workflows by combining different actions and running command line programs. Sometimes you might want to run small snippets of code, and that is already possible by running scripts from the command line with the run
keyword. What if you could write your Python script inside the workflow YAML file instead?
This post started as an experiment when I thought if I could write Python within the YAML file, and as I didn’t find any existing implementations I decided to build one myself. As it turns out this was a very straight forward task, as you can see by inspecting my run-python-script-action.
Running Python from YAML
I got inspiration for this action from the GitHub Actions run command that supports multiline commands with the |
character. So why not create an action that gets the Python code as a multiline input parameter, then stores it to a temporary file and runs the code?
This is exactly what my action does. Take a look at this configuration example:
name: Run Script
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- uses: jannekem/run-python-script-action@v1
with:
script: |
import os
print("Directory contents:")
for f in os.listdir():
print(f)
The workflow uses actions/checkout to clone the repository to the actions runner, then actions/setup-python to setup the Python 3 environment, and finally the custom jannekem/run-python-script-action to run a small Python script.
The script can be defined with the script
input argument, and if you start it with the |
character you can write on multiple lines normally, including indentation. This example prints the repository root directory contents to the workflow logs.
The action lets you run pretty much any code you want as long as it is on a “single file”. If you need non standard packages then be sure to install them with pip first. For larger logic I still recommend separate files but for simpler needs I think this can be a neat way to organize things.
Internal implementation
The actual implementation of the action is dead simple, just 14 lines of JavaScript:
const core = require('@actions/core');
const exec = require('@actions/exec');
const fs = require('fs');
const tmp = require('tmp');
async function run() {
tmp.setGracefulCleanup();
const script = core.getInput('script');
const filename = tmp.tmpNameSync({postfix: '.py'});
fs.writeFileSync(filename, script);
exec.exec('python', [filename])
}
run();
If you’re interested in creating your own GitHub actions you should check my Gentle Introduction to GitHub Actions to get started! And if you think that the action could be improved somehow then open an issue or create a pull request in the repository.
I would also like to hear if and how you are using the action, for example via Twitter.
Previous post
Dynamic Attributes in Python