GitHub Workflow
Allow Tusk to auto-address feedback from automated checks
If you are creating the workflow on your own, please reach out to support@usetusk.ai once you’ve merged the workflow so we can enable the auto-iteration feature for your agent.
Overview
Tusk uses a custom GitHub workflow for our customers to ensure that any pull request created passes your sanity checks.
Do this by creating a GitHub workflow at .github/workflows/tusk-sanity-check.yml
. This workflow should:
- Auto-fix any formatting/lint errors
- Commit those changes (if any) to the branch
- This commit message must start with
fix(${{ github.run_id }}):
- This commit message must start with
- Run a type check/build step
Tusk will run this workflow automatically after every commit it creates. If there’s an error in any of the above, it will get provided back to Tusk as feedback and Tusk will iterate on it automatically.
Example workflow
Here’s an example workflow for a company that uses Node.js, has a frontend app and backend app, and has pre-existing lint/build commands.
name: Tusk Sanity Check
on:
workflow_dispatch: # Use workflow_dispatch because Tusk will trigger this on its own
inputs:
taskId:
description: "Tusk Task ID"
required: true
runType:
description: "Tusk Run Type"
required: true
runId:
description: "Tusk Run ID"
required: true
jobs:
sanity_check:
runs-on: ubuntu-latest
steps:
- name: Log inputs
run: |
echo "Tusk Task ID: ${{ github.event.inputs.taskId }}"
echo "Tusk Run Type: ${{ github.event.inputs.runType }}"
echo "Tusk Run ID: ${{ github.event.inputs.runId }}"
echo "Current Branch: ${{ github.ref }}"
echo "Repository default branch: ${{ github.event.repository.default_branch }}"
- uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
# We use paths-filter to determine what directories were modified.
- uses: dorny/paths-filter@v3
id: filter
with:
base: ${{ github.event.repository.default_branch }} # or provide branch name, e.g. 'main'
# Space delimited list usable as command-line argument list in Linux shell. If needed, it uses single or double quotes to wrap filename with unsafe characters.
list-files: "shell"
# Using added|modified so we don't run prettier/eslint on deleted files
filters: |
frontend:
- added|modified: 'frontend/**'
backend:
- added|modified: 'backend/**'
- name: Set Node.js v18.16
uses: actions/setup-node@v3
with:
node-version: 18.16
- name: (Frontend) Install dependencies
if: steps.filter.outputs.frontend == 'true' # You can use the output of paths-filter to decide which steps to run
run: npm install
working-directory: ./frontend
- name: (Backend) Install dependencies
if: steps.filter.outputs.backend == 'true'
run: npm install
working-directory: ./backend
- name: (Frontend) Lint fix
if: steps.filter.outputs.frontend == 'true'
run: npm run lint:fix # This lint:fix commands runs prettier and eslint --fix
working-directory: ./frontend
- name: (Backend) Lint fix
if: steps.filter.outputs.backend == 'true'
run: npm run lint:fix # This lint:fix commands runs prettier and eslint --fix
working-directory: ./backend
# You must include this step after running all auto-fixing steps
- uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "fix(${{ github.run_id }}): auto linting" # The commit message MUST start with "fix(${{ github.run_id }}):"
skip_fetch: true
skip_checkout: true
- name: (Frontend) Check build
if: steps.filter.outputs.frontend == 'true'
run: npm run build:check
working-directory: ./frontend
- name: (Backend) Check build
if: steps.filter.outputs.backend == 'true'
run: npm run build
working-directory: ./backend
Tips and Tricks
Running lint/formatting steps
Make sure that you auto-fix as many errors as possible. This results in higher quality code because Tusk doesn’t have to “think” about issues that are auto-fixed.
If you don’t use something like lint-staged
(see below section if you do), you may only want to auto-lint or auto-format files that are modified. You can use the output of dorny/paths-filter
to do this. Some notes:
- We need to strip out the sub-directory of the file so it becomes the local path (
paths-filter
returns the full file path) "${file#frontend/}"
removesfrontend/
fromfile
(e.g.frontend/app.tsx
becomesapp.tsx
)
- uses: dorny/paths-filter@v3
id: filter
with:
base: ${{ github.event.repository.default_branch }} # or provide branch name, e.g. 'main'
# Space delimited list usable as command-line argument list in Linux shell. If needed, it uses single or double quotes to wrap filename with unsafe characters.
list-files: "shell"
filters: |
frontend:
- 'frontend/**'
backend:
- 'backend/**'
- name: (frontend) Run prettier
if: steps.filter.outputs.frontend == 'true'
working-directory: frontend
run: |
for file in ${{ steps.filter.outputs.frontend_files }}; do
npx prettier --write "${file#frontend/}"
done
Using lint-staged
If you already use lint-staged, you need to modify the checkout
step to include fetch-depth
- uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
fetch-depth: 40
- Set
fetch-depth: 40
. 40 commits should be more than enough for all the new Tusk commits on that branch.lint-staged
will use these commits to determine the files that are changed. - Pass in a diff command to
lint-staged
(e.g.npx lint-staged --diff=origin/master
ornpx lint-staged --diff=origin/${{ github.event.repository.default_branch }}
Using dorny/paths-filter
Use dorny/paths-filter to figure out the sub-directories that are modified (the example below is for a repo with frontend
and backend
).
- uses: dorny/paths-filter@v3
id: filter
with:
base: ${{ github.event.repository.default_branch }} # or provide branch name, e.g. 'main'
# Space delimited list usable as command-line argument list in Linux shell. If needed, it uses single or double quotes to wrap filename with unsafe characters.
list-files: "shell"
filters: |
frontend:
- 'frontend/**'
backend:
- 'backend/**'
- You need to set the base branch, for most repos this can just be the default branch but if you’ve asked Tusk to use a different default branch for Tusk make sure to specify that.
- After this, you can then use something like
if: steps.filter.outputs.frontend == 'true'
to determine whether to run certain future steps. - This will also list the files/that are modified as part of the output (in case we need to pass to any lint/buld commands). You can use this like
${{ steps.filter.outputs.frontend_files }}
.
Committing any auto changes
We use the stefanzweifel/git-auto-commit-action to auto-commit any changes that are made to the branch based off of your formatting/lint steps.
- uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "fix(${{ github.run_id }}): auto linting"
skip_fetch: true
skip_checkout: true
Testing your workflow
- Make sure that your workflow is merged into your main branch on GitHub (if it doesn’t exist there you won’t be able to manually dispatch it to test it)
- Once your workflow is merged on the main branch, if you test the workflow on a different branch it will use the workflow file on the branch you are testing it on. This allows you to test changes to the workflow without merging to main.
- We also recommend testing a branch that has a change in it that should be auto-fixed based on your workflow, and ensuring that this workflow auto-fixes it.
- Make sure your “Workflow permissions” are set to “Read and write permissions”