Skip to content

BYU-ODH/fetch-runner

Repository files navigation

fetch-runner

Run scripts when git fetch finds new commits to specified git branches.

Overview

Two users matter:

  • [general].user — the user defined in the general section of the jobs.toml file. This is the user that the fetch-runner process runs under (also User= in the systemd unit). The fetch-runner script refuses to start if the running uid doesn't match this user.
  • [[jobs]].run_as — defined for each job described in jobs.toml. This user owns the job's repo and runs its git ops and deploy script. If this value is not defined, it will default to [general].user. When run_as is defined, fetch-runner dispatches everything via sudo -n -u <run_as> and a sudoers rule must allow it. A sudoers rule can be generated for each run_as user by running fetch-runner --print-sudoers <jobs.toml>.

Convention assumed throughout the docs is that each repo lives at /srv/<run_as>/<repo_name>/, owned <run_as>:<run_as> mode 0755. That gives every path under /srv/<run_as>/ a single owner and lets [general].user traverse with just search permission.

Setup

1. Install fetch-runner

As [general].user (we use fetch-runner below):

uv tool install git+https://github.com/BYU-ODH/fetch-runner

Note the installed executable path (typically /home/[general].user/.local/bin/fetch-runner).

2. Add a deploy script to each app

As a user with sudo capability:

Clone the fetch-runner repository

git clone https://github.com/BYU-ODH/fetch-runner.git

Now, as the run_as user, copy the deploy.sh script into the application directory. For a job with run_as = "app1" deploying the api repo:

sudo -u app1 cp /path/to/fetch-runner/examples/deploy.sh /srv/app1/api/deploy.sh
sudo -u app1 chmod +x /srv/app1/api/deploy.sh

Replace every deploy-user in the script with the job's run_as user (app1 here). The guard block at the top refuses to run as any other user. Regenerate it for a different user with:

fetch-runner --print-guard app1

Commit the script to the app's repo so deploys are version-controlled.

3. Create the jobs config

cp /path/to/fetch-runner/examples/jobs.toml /home/[general].user/jobs.toml

Per [[jobs]]:

  • name — label shown in logs
  • path — absolute repo path, owned and writable by run_as
  • branch — branch to watch
  • script — absolute script path
  • args — optional list of argv strings passed to the script (each a single token; no whitespace or shell metacharacters). Included in the generated sudoers rule, so regenerate after any change.
  • run_as — optional; defaults to [general].user
  • timeout_seconds — optional script timeout

Validate without starting:

fetch-runner --check /home/fetch-runner/jobs.toml

4. Install the systemd service

sudo cp /path/to/fetch-runner/examples/fetch-runner.service \
    /etc/systemd/system/fetch-runner.service

In the CUSTOMIZE block of the new fetch-runner system unit service, set:

  • User / Group to [general].user
  • ExecStart to the binary path from step 1 and the path to your jobs.toml config file
  • ReadWritePaths to every directory any child process (such as git) writes to — including the root directory of each repo (sudo'd git is still inside the fetch-runner unit service's filesystem sandbox). If all of your repos are inside a single directory, (/srv, for example) you can set ReadWritePaths to only this directory and all child directories will be readable/writeable as well.

The example unit omits NoNewPrivileges= and RestrictSUIDSGID= because they block sudo's setuid. The sudoers fragment (step 5) is what bounds the privilege. If every job uses run_as = [general].user, you can re-enable both.

5. Install the sudoers fragment (only if any job sets a run_as user that is different from [general].user)

fetch-runner --print-sudoers /home/fetch-runner/jobs.toml \
    | sudo tee /etc/sudoers.d/fetch-runner > /dev/null
sudo chmod 0440 /etc/sudoers.d/fetch-runner
sudo visudo -cf /etc/sudoers.d/fetch-runner  # syntax check

Note: You may need to run these steps separately in order to create the /etc/sudoers.d/fetch-runner file. This can be done by copying the output of the fetch-runner --print-sudoers command and pasting the result into the /etc/sudoers.d/fetch-runner file. The other commands can be followed as written.

Re-run after any jobs.toml change. The git rule is intentionally not arg-restricted: running git as run_as is no broader than what the deploy-script rule already grants.

6. Enable and start

sudo systemctl daemon-reload
sudo systemctl enable --now fetch-runner

Migrating from single-user mode

Existing configs without run_as keep working unchanged — sudo is skipped entirely. To split, add run_as per job, update each script's guard for the new user, regenerate the sudoers fragment, reload.

Debugging

sudo systemctl status fetch-runner
sudo journalctl -u fetch-runner -f
sudo journalctl -u fetch-runner -b
  • sudo: a password is required → sudoers fragment is missing or stale; re-run step 5.
  • fetch-runner-guard: refusing to run as <user> → the script's guard names a user that doesn't match the job's run_as; regenerate with fetch-runner --print-guard <run_as>.

About

Run scripts when `git fetch` finds new commits to specified git branches

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages