413 lines
10 KiB
Markdown
413 lines
10 KiB
Markdown
# [gitdeploy](https://git.rootprojects.org/root/gitdeploy)
|
|
|
|
**gitdeploy** is an app for continuous deployment of static websites.
|
|
|
|
1. Point a domain to a server running `gitdeploy`
|
|
2. [Set git webhooks](#add-webhooks) (Github, Gitea, Bitbucket, etc)
|
|
3. Create [build & deploy scripts](#setup-with-deploy-scripts) from the examples
|
|
4. Profit (when you push, the build will kick off)
|
|
|
|
## Features
|
|
|
|
**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.
|
|
|
|
## Usage
|
|
|
|
```bash
|
|
gitdeploy init
|
|
gitdeploy run --listen :3000 --scripts ./scripts/
|
|
```
|
|
|
|
```txt
|
|
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/{deploy.sh,promote.sh,etc}
|
|
-trust-repos string
|
|
list of repos (ex: 'github.com/org/repo', or '*' for all) for which to run '.gitdeploy/deploy.sh'
|
|
-compress
|
|
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-proxy
|
|
trust X-Forwarded-For header
|
|
```
|
|
|
|
## Install
|
|
|
|
You can download `gitdeploy` from the Github Releases API and place it in your PATH,
|
|
or install it with [Webi](https://webinstall.dev) ([webinstall.dev/gitdeploy](https://webinstall.dev/gitdeploy)):
|
|
|
|
**Mac**, **Linux**:
|
|
|
|
```bash
|
|
curl -sS https://webinstall.dev/gitdeploy | bash
|
|
```
|
|
|
|
**Windows 10**:
|
|
|
|
```bash
|
|
curl -A MS https://webinstall.dev/gitdeploy | powershell
|
|
```
|
|
|
|
**Manual Install**
|
|
|
|
See <https://github.com/therootcompany/gitdeploy/releases>.
|
|
|
|
## 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:
|
|
|
|
```bash
|
|
git config --global url."ssh://git@github.com/example-org/".insteadOf "https://github.com/example-org/"
|
|
```
|
|
|
|
This will add an entry like this to your `.gitconfig`:
|
|
|
|
```ini
|
|
[url "ssh://git@github.com/example-org/"]
|
|
insteadOf = https://github.com/example-org/
|
|
```
|
|
|
|
For more info see
|
|
[The Git Credentials Cheat Sheet](https://coolaj86.com/articles/vanilla-devops-git-credentials-cheatsheet/)
|
|
at <https://coolaj86.com/articles/vanilla-devops-git-credentials-cheatsheet/>.
|
|
|
|
## Setup with Deploy Scripts
|
|
|
|
Start by initializing your `.env` and `./scripts` directory.
|
|
|
|
```bash
|
|
gitdeploy init
|
|
```
|
|
|
|
```txt
|
|
.env
|
|
scripts/
|
|
├── deploy.sh
|
|
├── git.example.com/org/go-project/deploy.sh
|
|
├── git.example.com/org/node-project/deploy.sh
|
|
├── git.example.com/org/mirror-project/deploy.sh
|
|
└── promote.sh
|
|
```
|
|
|
|
The default `deploy.sh` is sensible -
|
|
if another `deploy.sh` 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/deploy.sh` 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
|
|
|
|
```bash
|
|
# trust a few repos to run their own deploy scripts
|
|
gitdeploy run --listen :3000 --trust-repos 'github.com/org/one github.com/org/two'
|
|
|
|
# trust an organization
|
|
gitdeploy run --listen :3000 --trust-repos 'github.com/org/*'
|
|
|
|
# trust all repos
|
|
gitdeploy run --listen :3000 --trust-repos '*'
|
|
```
|
|
|
|
### Git Info
|
|
|
|
These ENVs are set before each script is run:
|
|
|
|
```bash
|
|
GIT_REPO_ID=github.com/my-org/my-project
|
|
|
|
GIT_CLONE_URL=https://github.com/my-org/my-project.git
|
|
|
|
GIT_DEPLOY_JOB_ID=xxxxxx
|
|
GIT_REF_NAME=master
|
|
GIT_REF_TYPE=branch
|
|
GIT_REPO_OWNER=my-org
|
|
GIT_REPO_NAME=my-project
|
|
GIT_REPO_TRUSTED=true
|
|
```
|
|
|
|
## API
|
|
|
|
```txt
|
|
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": "github.com/example-org/example-project",
|
|
"timestamp": "2001-02-03T16:30:00.999Z",
|
|
"https_url": "https://github.com/example-org/example-project.git",
|
|
"ssh_url": "git@github.com:example-org/example-project.git",
|
|
"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/status.example.com/e2e-selenium",
|
|
"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": "github.com/org/repo#abcdef7",
|
|
"ref": {
|
|
"repo_id": "github.com/org/repo",
|
|
"timestamp": "2001-02-03T16:30:00.999Z",
|
|
"https_url": "https://github.com/org/repo.git",
|
|
"ssh_url": "git@github.com:org/repo.git",
|
|
"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 github.com/org/repo#master\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 deploy.sh
|
|
POST /api/admin/webhooks/{github,gitea,bitbucket}
|
|
```
|
|
|
|
## Build
|
|
|
|
**Frontend**:
|
|
|
|
```bash
|
|
pushd html/
|
|
npm ci
|
|
scripts/build
|
|
popd
|
|
```
|
|
|
|
**API**:
|
|
|
|
With [GoReleaser](https://webinstall.dev/goreleaser):
|
|
|
|
```bash
|
|
goreleaser --snapshot --skip-publish --rm-dist
|
|
```
|
|
|
|
With [Golang](https://webinstall.dev/golang):
|
|
|
|
```bash
|
|
export GOFLAGS="-mod=vendor"
|
|
|
|
go run -mod=vendor git.rootprojects.org/root/go-gitver/v2
|
|
go generate -mod=vendor ./...
|
|
go build -mod=vendor .
|
|
```
|
|
|
|
You can use build tags to remove providers from the build:
|
|
|
|
```bash
|
|
go build -mod=vendor -tags nobitbucket,nogithub .
|
|
```
|
|
|
|
Supported tags are:
|
|
|
|
- nogithub
|
|
- nogitea
|
|
- nobitbucket
|
|
|
|
## Run as a System Service
|
|
|
|
```bash
|
|
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**:
|
|
|
|
```js
|
|
crypto.randomBytes(16).toString("hex");
|
|
```
|
|
|
|
Then you'll need to set up the webhook in your platform of choice.
|
|
|
|
### Github
|
|
|
|
New Webhook: `https://github.com/YOUR_ORG/YOUR_REPO/settings/hooks/new`
|
|
|
|
```txt
|
|
Payload URL: https://YOUR_DOMAIN/api/webhooks/github
|
|
Content-Type: application/json
|
|
Secret: YOUR_SECRET
|
|
Which events would you like to trigger this webhook?
|
|
Just the `push` event.
|
|
Active: ✅
|
|
```
|
|
|
|
### Bitbucket
|
|
|
|
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:
|
|
|
|
```txt
|
|
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](https://webinstall.dev/caddy) to HTTPS:
|
|
|
|
```caddyfile
|
|
gitdeploy.example.com {
|
|
log {
|
|
output stdout
|
|
format console
|
|
}
|
|
encode gzip zstd
|
|
reverse_proxy /* localhost:4483
|
|
}
|
|
```
|
|
|
|
## How to Generate a Base64 Secret
|
|
|
|
**in your browser**:
|
|
|
|
```js
|
|
(async function () {
|
|
var rnd = new Uint8Array(16);
|
|
await crypto.getRandomValues(rnd);
|
|
var b64 = [].slice
|
|
.apply(rnd)
|
|
.map(function (ch) {
|
|
return String.fromCharCode(ch);
|
|
})
|
|
.join("");
|
|
var secret = btoa(b64)
|
|
.replace(/\//g, "_")
|
|
.replace(/\+/g, "-")
|
|
.replace(/=/g, "");
|
|
console.info(secret);
|
|
})();
|
|
```
|
|
|
|
**with node.js**:
|
|
|
|
```js
|
|
crypto
|
|
.randomBytes(16)
|
|
.toString("base64")
|
|
.replace(/\+/g, "-")
|
|
.replace(/\//g, "_")
|
|
.replace(/=/g, "");
|
|
```
|
|
|
|
## License
|
|
|
|
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 https://mozilla.org/MPL/2.0/.
|