This repository provides a set of GitHub Actions that let you land, rebase, close, backport, and update pull requests using Sapling, ghstack, and jujutsu directly from the GitHub Pull Request web interface. Each command is triggered from PR comments and handled by a dedicated action.
Required permissions depend on the action being executed. When using a GitHub App token, configure repository permissions accordingly:
- Land: contents: write, pull-requests: write, issues: write
- Rebase: contents: write, issues: write
- Close: pull-requests: write, contents: write, issues: write
- Backport: contents: write, pull-requests: write, issues: write
- Update: contents: write, pull-requests: write, issues: write
- Cleanup: contents: write
Permissions can be configured at the workflow level or per job. The examples below set them at the workflow level and request matching scopes from the GitHub App via actions/create-github-app-token.
Add a workflow like the following to .github/workflows/commands.yaml to wire
comment-driven commands to their corresponding actions. This example reflects
the current setup used in .github/workflows/commands.yaml:
name: Command
'on':
issue_comment:
types:
- created
permissions:
contents: write
issues: write
pull-requests: write
jobs:
land:
if: >-
github.event.issue.pull_request != null &&
contains(github.event.comment.body, '.land')
runs-on: ubuntu-slim
steps:
- continue-on-error: true
id: createGithubAppToken
uses: actions/create-github-app-token@v3.1.1
with:
client-id: ${{ vars.OPERATOR_APP_CLIENT_ID }}
permission-contents: write
permission-issues: write
permission-pull-requests: write
private-key: ${{ secrets.OPERATOR_PRIVATE_KEY }}
- uses: actions/checkout@v6
with:
fetch-depth: 0
token: >-
${{ steps.createGithubAppToken.outputs.token
|| secrets.GITHUB_TOKEN }}
- uses: cachix/install-nix-action@v31
with:
github_access_token: >-
${{ steps.createGithubAppToken.outputs.token
|| secrets.GITHUB_TOKEN }}
- uses: shikanime-studio/actions/command/land@main
with:
email: operator6o@shikanime.studio
fullname: Operator 6O
github-token: >-
${{ steps.createGithubAppToken.outputs.token
|| secrets.GITHUB_TOKEN }}
gpg-passphrase: ${{ secrets.GPG_PASSPHRASE }}
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
sign-commits: true
username: operator6o
rebase:
if: >-
github.event.issue.pull_request != null &&
contains(github.event.comment.body, '.rebase')
runs-on: ubuntu-slim
steps:
- continue-on-error: true
id: createGithubAppToken
uses: actions/create-github-app-token@v3.1.1
with:
client-id: ${{ vars.OPERATOR_APP_CLIENT_ID }}
permission-contents: write
permission-issues: write
permission-pull-requests: write
private-key: ${{ secrets.OPERATOR_PRIVATE_KEY }}
- uses: actions/checkout@v6
with:
fetch-depth: 0
token: >-
${{ steps.createGithubAppToken.outputs.token
|| secrets.GITHUB_TOKEN }}
- uses: cachix/install-nix-action@v31
with:
github_access_token: >-
${{ steps.createGithubAppToken.outputs.token
|| secrets.GITHUB_TOKEN }}
- uses: shikanime-studio/actions/command/rebase@main
with:
email: operator6o@shikanime.studio
fullname: Operator 6O
github-token: >-
${{ steps.createGithubAppToken.outputs.token
|| secrets.GITHUB_TOKEN }}
gpg-passphrase: ${{ secrets.GPG_PASSPHRASE }}
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
sign-commits: true
username: operator6o
close:
if: >-
github.event.issue.pull_request != null &&
contains(github.event.comment.body, '.close')
runs-on: ubuntu-slim
steps:
- continue-on-error: true
id: createGithubAppToken
uses: actions/create-github-app-token@v3.1.1
with:
client-id: ${{ vars.OPERATOR_APP_CLIENT_ID }}
permission-contents: write
permission-issues: write
permission-pull-requests: write
private-key: ${{ secrets.OPERATOR_PRIVATE_KEY }}
- uses: actions/checkout@v6
with:
fetch-depth: 0
token: >-
${{ steps.createGithubAppToken.outputs.token
|| secrets.GITHUB_TOKEN }}
- uses: cachix/install-nix-action@v31
with:
github_access_token: >-
${{ steps.createGithubAppToken.outputs.token
|| secrets.GITHUB_TOKEN }}
- uses: shikanime-studio/actions/command/close@main
with:
github-token: >-
${{ steps.createGithubAppToken.outputs.token
|| secrets.GITHUB_TOKEN }}
username: operator6o
backport:
if: >-
github.event.issue.pull_request != null &&
contains(github.event.comment.body, '.backport')
runs-on: ubuntu-slim
steps:
- continue-on-error: true
id: createGithubAppToken
uses: actions/create-github-app-token@v3.1.1
with:
client-id: ${{ vars.OPERATOR_APP_CLIENT_ID }}
permission-contents: write
permission-issues: write
permission-pull-requests: write
private-key: ${{ secrets.OPERATOR_PRIVATE_KEY }}
- uses: actions/checkout@v6
with:
fetch-depth: 0
token: >-
${{ steps.createGithubAppToken.outputs.token
|| secrets.GITHUB_TOKEN }}
- uses: cachix/install-nix-action@v31
with:
github_access_token: >-
${{ steps.createGithubAppToken.outputs.token
|| secrets.GITHUB_TOKEN }}
- uses: shikanime-studio/actions/command/backport@main
with:
github-token: >-
${{ steps.createGithubAppToken.outputs.token
|| secrets.GITHUB_TOKEN }}
gpg-passphrase: ${{ secrets.GPG_PASSPHRASE }}
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
sign-commits: trueUse the composite action to install Nix, optionally configure Cachix, and (on Linux) enable QEMU for additional platforms.
extra-platformsacceptsamd64andarm64(comma-separated). It configures both QEMU and Nixextra-platformsaccordingly.
steps:
- uses: shikanime-studio/actions/nix/setup@main
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
cachix-name: my-cache
cachix-auth-token: ${{ secrets.CACHIX_AUTH_TOKEN }}
extra-platforms: arm64
extra-config: |
experimental-features = nix-command flakesUse the composite actions to generate matrices for checks and packages.
jobs:
setup-checks-jobs:
runs-on: ubuntu-latest
outputs:
continue: ${{ steps.matrix.outputs.continue }}
matrix: ${{ steps.matrix.outputs.matrix }}
steps:
- id: matrix
uses: shikanime-studio/actions/nix/setup-checks-jobs@v9
with:
systems: >-
{
"ubuntu-latest": ["x86_64-linux"],
"ubuntu-24.04-arm": [
"aarch64-linux",
"armv6l-linux",
"armv7l-linux"
]
}
checks:
needs: [setup-checks-jobs]
if: needs.setup-checks-jobs.outputs.continue == 'true'
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include: >-
${{ fromJSON(needs.setup-checks-jobs.outputs.matrix) }}
steps:
- run: nix flake check \
--accept-flake-config \
--no-pure-eval \
--system "${{ matrix.system }}"
setup-packages-jobs:
runs-on: ubuntu-latest
outputs:
continue: ${{ steps.matrix.outputs.continue }}
matrix: ${{ steps.matrix.outputs.matrix }}
steps:
- id: matrix
uses: shikanime-studio/actions/nix/setup-packages-jobs@v9
with:
systems: >-
{
"ubuntu-latest": ["x86_64-linux"],
"ubuntu-24.04-arm": ["aarch64-linux"]
}
excludes: '["devenv-up","devenv-test"]'
packages:
needs: [setup-packages-jobs]
if: needs.setup-packages-jobs.outputs.continue == 'true'
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include: >-
${{ fromJSON(needs.setup-packages-jobs.outputs.matrix) }}
steps:
- run: nix build \
--accept-flake-config \
--no-pure-eval \
".#packages.${{ matrix.system }}.${{ matrix.name }}"Trigger a workflow dispatch from a PR comment:
- Comment format:
.run | <workflow> <workflow>can be a workflow name or a workflow file path.- The target workflow must have
workflow_dispatchenabled. - The job token needs
actions: write.
Example job:
run:
if: >-
github.event.issue.pull_request != null &&
contains(github.event.comment.body, '.run')
permissions:
actions: write
issues: write
pull-requests: read
runs-on: ubuntu-slim
steps:
- continue-on-error: true
id: createGithubAppToken
uses: actions/create-github-app-token@v3.1.1
with:
client-id: ${{ vars.OPERATOR_APP_CLIENT_ID }}
permission-actions: write
permission-issues: write
permission-pull-requests: read
private-key: ${{ secrets.OPERATOR_PRIVATE_KEY }}
- uses: shikanime-studio/actions/command/run@main
with:
github-token: >-
${{ steps.createGithubAppToken.outputs.token
|| secrets.GITHUB_TOKEN }}To automate dependency updates and repository hygiene, you can also add a
scheduled workflow for updates that uses the update action:
name: Update
'on':
schedule:
- cron: 0 4 * * 0
workflow_dispatch:
jobs:
dependencies:
runs-on: ubuntu-slim
steps:
- continue-on-error: true
id: createGithubAppToken
uses: actions/create-github-app-token@v3.1.1
with:
client-id: ${{ vars.OPERATOR_APP_CLIENT_ID }}
permission-contents: write
permission-issues: write
permission-pull-requests: write
private-key: ${{ secrets.OPERATOR_PRIVATE_KEY }}
- uses: actions/checkout@v6
with:
fetch-depth: 0
token: >-
${{ steps.createGithubAppToken.outputs.token
|| secrets.GITHUB_TOKEN }}
- uses: cachix/install-nix-action@v31
with:
github_access_token: >-
${{ steps.createGithubAppToken.outputs.token
|| secrets.GITHUB_TOKEN }}
- uses: shikanime-studio/actions/update@v7
with:
gpg-passphrase: ${{ secrets.GPG_PASSPHRASE }}
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
sign-commits: true
stale:
runs-on: ubuntu-slim
steps:
- continue-on-error: true
id: createGithubAppToken
uses: actions/create-github-app-token@v3.1.1
with:
client-id: ${{ vars.OPERATOR_APP_CLIENT_ID }}
permission-contents: write
permission-issues: write
permission-pull-requests: write
private-key: ${{ secrets.OPERATOR_PRIVATE_KEY }}
- uses: actions/stale@v10
with:
days-before-close: 14
days-before-stale: 30
repo-token: >-
${{ steps.createGithubAppToken.outputs.token
|| secrets.GITHUB_TOKEN }}To automatically delete branches after a pull request is merged or closed, add a
cleanup workflow to .github/workflows/cleanup.yaml:
name: Cleanup
'on':
pull_request:
types:
- closed
jobs:
cleanup:
runs-on: ubuntu-slim
steps:
- continue-on-error: true
id: createGithubAppToken
uses: actions/create-github-app-token@v3.1.1
with:
client-id: ${{ vars.OPERATOR_APP_CLIENT_ID }}
permission-contents: write
private-key: ${{ secrets.OPERATOR_PRIVATE_KEY }}
- uses: actions/checkout@v6
with:
fetch-depth: 0
token: >-
${{ steps.createGithubAppToken.outputs.token
|| secrets.GITHUB_TOKEN }}
- uses: shikanime-studio/actions/cleanup@v7