listens for git webhooks and runs bash scripts when they arrive
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
AJ ONeal c2085858a7 bugfix: remove extraneous return 1 month ago
.gitdeploy add .gitdeploy 6 months ago
assets add init to initialize ./scripts/ with examples 5 months ago
examples support pytest results 1 month ago
html Re: #12: update package-lock.json 1 month ago
internal bugfix: remove extraneous return 1 month ago
public git-deploy static server with basic options 6 months ago
vendor go-github v32 -> v33 1 month ago
.gitignore ignore test logs 1 month ago
.gitmodules work 7 months ago
.goreleaser.yml releaser: use -mod=vendor, not go mod download 6 months ago
.ignore add vendor/ to .ignore 6 months ago
.prettierignore build /api/admin/repos by walking scripts/ 6 months ago
.prettierrc run deploy scripts from within trusted repos 6 months ago
AUTHORS add exec script 6 months ago
LICENSE add exec script 6 months ago support pytest results 1 month ago
bitbucket.go rename to gitdeploy 6 months ago
gitea.go rename to gitdeploy 6 months ago
github.go rename to gitdeploy 6 months ago
go.mod go-github v32 -> v33 1 month ago
go.sum go-github v32 -> v33 1 month ago
main.go add max job runtime 1 month ago
staticcheck.conf add staticcheck.conf 1 month ago


gitdeploy is an app for continuous deployment of static websites.

  1. Point a domain to a server running gitdeploy
  2. Set git webhooks (Github, Gitea, Bitbucket, etc)
  3. Create build & deploy scripts from the examples
  4. Profit (when you push, the build will kick off)


gitdeploy is intended for use with static websites that are generated after changes are pushed to a Git repository. This works with sites that are being edited in code and tracked in Git. Sites that have their content managed with a headless CMS that pushes to Git are also very well-suited.

gitdeploy supports verified webhooks from Github, Bitbucket, and Gitea.

gitdeploy is written in Go. This means that it's a standalone binary available on all major operating systems and architectures. It provides an API with endpoints that handle webhooks, allow for initiation of builds, and getting the status of builds and build jobs.

gitdeploy comes with a simple interface. The interface be disabled if you don't want to use it.


gitdeploy init
gitdeploy run --listen :3000 --scripts ./scripts/
Usage of gitdeploy run:
  -listen string
    	the address and port on which to listen (default :4483)
  -github-secret string
    	secret for github webhooks (same as GITHUB_SECRET=)
  -bitbucket-secret string
    	secret for bitbucket webhooks (same as BITBUCKET_SECRET=)
  -gitea-secret string
    	secret for gitea webhooks (same as GITEA_SECRET=)
  -scripts string
    	path to ./scripts/{,,etc}
  -trust-repos string
    	list of repos (ex: '', or '*' for all) for which to run '.gitdeploy/'
    	enable compression for text,html,js,css,etc (default true)
  -promotions string
    	a list of promotable branches in descending order (default 'production,staging,master')
  -serve-path string
    	path to serve, falls back to built-in web app
    	trust X-Forwarded-For header


You can download gitdeploy from the Github Releases API and place it in your PATH, or install it with Webi (

Mac, Linux:

curl -sS | bash

Windows 10:

curl -A MS | powershell

Manual Install


Git SSH Deploy Keys and Tokens

All of the clone URLs are HTTPS clone URLs.

To use SSH clone URLs, you should update your git credentials:

git config --global url."ssh://".insteadOf ""

This will add an entry like this to your .gitconfig:

[url "ssh://"]
    insteadOf =

For more info see The Git Credentials Cheat Sheet at

Setup with Deploy Scripts

Start by initializing your .env and ./scripts directory.

gitdeploy init

The default is sensible - if another exists in a directory with the same repo name as an incoming webhook, it runs it.

The example deploy scripts are a good start, but you'll probably need to update them to suit your build process for your project.

In-repo .gitdeploy scripts

A repo my have its own .gitdeploy/ at its root, but by default these are ignored.

You can set --trust-repos (or TRUST_REPOS) to allow deploy scripts to be run directly from a repository.

  • matches are case-insensitive (foo matches Foo)
  • a wildcard * may be used (at the end of a string) to define a prefix
  • the list may be space ' ' or comman , delimited
# trust a few repos to run their own deploy scripts
gitdeploy run --listen :3000 --trust-repos ''

# trust an organization
gitdeploy run --listen :3000 --trust-repos '*'

# trust all repos
gitdeploy run --listen :3000 --trust-repos '*'

Git Info

These ENVs are set before each script is run:




GET  /api/admin/jobs?since=1577881845.999

      "success": true,
      "jobs": [
            "id": "XXXXXXXXXXXX",
            "_job_id": "XXXXXXXXXXXX",                   // replaced with jobs[].id
            "started_at": "2020-01-01T12:30:45.999Z",
            "_created_at": "2020-01-01T12:30:45.999Z",   // replaced with jobs[].ref.timestamp
            "ref": {
                "repo_id": "",
                "timestamp": "2001-02-03T16:30:00.999Z",
                "https_url": "",
                "ssh_url": "",
                "rev": "abcdef7",
                "ref": "refs/heads/master",
                "ref_type": "branch",
                "ref_name": "master",
                "repo_owner": "example-org",
                "repo_name": "example-project"
            "ended_at": "2001-02-03T16:30:04.999Z",
            "exit_code": 0

POST /local/jobs/{job_id}?format=gitdeploy

    { "report":
        { "name": "sleep test",
          "status": "PASS",
          "message": "the top level result",
          "results": [
            { "name": "a sub test",
              "status": "PASS",
              "message": "a sub group",
              "_detail": { "foo": "bar" }

POST /local/jobs/{job_id}?format=pytest

    { "exitcode": 0,
      "root": "/home/app/srv/",
      "tests": [
        { "nodeid": "pytest::idthing",
          "outcome": "passed"

POST /api/admin/jobs
    { "job_id": "xxxx", "kill": true }

    { "success": true }

GET /api/admin/logs/{job_id}?since=1577881845.999

      "success": true,
      "started_at": "2001-02-03T16:30:01.999Z",
      "id": "",
      "ref": {
        "repo_id": "",
        "timestamp": "2001-02-03T16:30:00.999Z",
        "https_url": "",
        "ssh_url": "",
        "rev": "abcdef7",
        "ref": "refs/heads/master",
        "ref_type": "branch",
        "ref_name": "master",
        "repo_owner": "org",
        "repo_name": "repo"
      "ended_at": "2001-02-03T16:30:04.999Z",
      "exit_code": 0,
      "logs": [
          "timestamp": "2001-02-03T16:30:02.999Z",
          "stderr": false,
          "text": "Cloning into '/tmp/tmp.vksS1ln6Wv/repo'...\n"
          "timestamp": "2001-02-03T16:30:03.999Z",
          "stderr": false,
          "text": "Nothing to do for\n"

# note: see --help for how to use --promotions
POST /api/admin/promote

    { "clone_url": "https://...", "ref_name": "development" }

    { "success": true, "promote_to": "staging" }

# note: each webhook is different, but the result is to run a
POST /api/admin/webhooks/{github,gitea,bitbucket}



pushd html/
  npm ci


With GoReleaser:

goreleaser --snapshot --skip-publish --rm-dist

With Golang:

export GOFLAGS="-mod=vendor"

go run -mod=vendor
go generate -mod=vendor ./...
go build -mod=vendor .

You can use build tags to remove providers from the build:

go build -mod=vendor -tags nobitbucket,nogithub .

Supported tags are:

  • nogithub
  • nogitea
  • nobitbucket

Run as a System Service

sudo env PATH="$PATH" \
  serviceman add --name gitdeploy --system \
    --username app -path "$PATH" -- \
    gitdeploy run --scripts ./scripts/

Add Webhooks

To add a webhook you'll first need a secret

with node.js:


Then you'll need to set up the webhook in your platform of choice.


New Webhook:

Payload URL: https://YOUR_DOMAIN/api/webhooks/github
Content-Type: application/json
Which events would you like to trigger this webhook?
Just the `push` event.
Active: ✅


Sometimes Bitbucket does not give you the option to specify the (X-Hub-Signature) secret, so you'll have to append an access_token instead. Example:

Title: gitdeploy
URL: https://YOUR_DOMAIN/api/webhooks/bitbucket?access_token=YOUR_SECRET
Triggers: Repository push

Securing the Webook with HTTPS

I recommend using caddy to HTTPS: {
    log {
        output stdout
        format console
    encode gzip zstd
    reverse_proxy /* localhost:4483

How to Generate a Base64 Secret

in your browser:

(async function () {
  var rnd = new Uint8Array(16);
  await crypto.getRandomValues(rnd);
  var b64 = [].slice
    .map(function (ch) {
      return String.fromCharCode(ch);
  var secret = btoa(b64)
    .replace(/\//g, "_")
    .replace(/\+/g, "-")
    .replace(/=/g, "");;

with node.js:

  .replace(/\+/g, "-")
  .replace(/\//g, "_")
  .replace(/=/g, "");


Copyright 2020 The gitdeploy Authors

This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at