]> ruderich.org/simon Gitweb - punyci/punyci.git/blob - README.adoc
e22572d891761e7c2e685eb4f48ff47e2fe76abd
[punyci/punyci.git] / README.adoc
1 = README
2
3 punyci is a smaller than tiny continuous integration (CI) tool for Git and
4 Podman. It runs as post-receive or post-commit hook and executes the jobs with
5 Podman in the background. If a job fails it sends a mail to the current user.
6 The container is kept alive for an hour to debug issues "live" without needing
7 multiple runs. To debug issues with punyci the job can also be run in the
8 foreground.
9
10 punyci is free software and licensed under GPL version 3 or later.
11
12
13 == Setup
14
15 Compile `punyci` with `go build` or `make`.
16
17 Setup `podman` for an unprivileged user. To send mails `/usr/bin/sendmail`
18 needs to be installed (provided by e.g. Postfix, nullmailer, etc.). Create a
19 bare Git repository and call `punyci post-receive` from the post-receive hook.
20 Alternatively, setup a regular Git repository and call `punyci post-commit`
21 from the post-commit hook.
22
23
24 == Usage
25
26 Example (post-receive):
27
28     $ git init --bare repo.git
29     $ echo 'punyci post-receive' > repo.git/hooks/post-receive
30     $ chmod +x repo.git/hooks/post-receive
31
32     # Optional, see below
33     $ git -C repo.git config receive.advertisePushOptions true
34
35     $ git clone user@example.org:repo.git
36     $ cd repo
37     $ cat > .punyci.toml <<EOF
38     [[Jobs]]
39     Image = "debian:trixie"
40     Script = "exit 1"
41     EOF
42     $ git add .punyci.toml
43     $ git commit -m punyci
44     $ git push
45     [...]
46     remote: 2026/02/22 14:24:52 punyci: queued 1 jobs
47
48 This will create an email reporting the failure of the job. It's send to the
49 current user the CI is running as (adapt with `~/.forward` or similar). The
50 container continues to run for an hour. To debug the issue simply run `podman
51 exec -it $uuid-from-email sh` on the host where the CI is running.
52
53 If a job succeeds then no mail is generated. The only exception is if the
54 previous run of the job failed. Then a "fixed" mail is generated. Failed jobs
55 are marked with an empty file `punyci-failed-$job` in the bare repository.
56
57 The build log of the last job is stored in `punyci-log-$job` in the bare
58 repository. It's written "live" during the run and can be used with `tail -f`
59 to watch the output during the run.
60
61 To run the CI in the foreground use `git push -o wait`. This needs the option
62 `receive.advertisePushOptions = true` in the remote repository. If a job fails
63 the command will hang until the timeout. Simply press Ctrl-C or kill the
64 containers manually.
65
66 Example (post-commit):
67
68     $ git init repo
69     $ echo 'punyci post-commit' > repo/.git/hooks/post-commit
70     $ chmod +x repo/.git/hooks/post-commit
71
72     $ cd repo
73     $ cat > .punyci.toml <<EOF
74     [[Jobs]]
75     Image = "debian:trixie"
76     Script = "exit 1"
77     EOF
78     $ git add .punyci.toml
79     $ git commit -m punyci
80     2026/02/26 06:41:15 punyci: queued 1 jobs
81     [...]
82
83 The behavior is the same as above. To run the CI in the foreground use
84 `PUNYCI=wait git commit`.
85
86
87 == Configuration
88
89 punyci is configured through `.punyci.toml` in the top-level of the
90 repository.
91
92     [[Jobs]]
93     Image = "golang:1.26"
94     Script = "./ci/run"
95
96     [[Jobs]]
97     Image = "golang:1.25"
98     Script = "./ci/run"
99     Cache = true
100
101 An unlimited number of jobs can be configured. They are run in sequence. A
102 failing job doesn't stop the following jobs from starting.
103
104 `Script` can be an arbitrary script (multiple lines with TOML's `"""`
105 strings). If no shebang is present then `#!/bin/sh` is prepended. Otherwise
106 the existing shebang is used.
107
108 `Cache` can be used to configure a persistent cache for each job. The cache is
109 mounted in the container at `/punyci-cache` and must be manually used. For
110 example by symlinking into it (e.g. `ln -s /punyci-cache /root/.cache`) at the
111 beginning of the CI script. The cache is stored as `punyci-cache-$job` in the
112 (bare) repository.
113
114
115 == Architecture
116
117 === Execution
118
119 For each job the following steps are executed:
120
121 - Clone the repository
122 - Start given image as detached container in Podman which waits forever,
123   mounting the repository and cache (optional)
124 - Execute the script to run the CI job
125   . If the CI runs in the foreground write the output to stdout/stderr
126   . Otherwise write it to the log file
127 - If the script exits with a non-zero exit code wait for the container to
128   exit with a timeout of one hour
129 - Kill the container
130 - Send email if running in the background and the job either failed or was
131   fixed
132
133 === Container
134
135 The following files and directories are mounted in the container:
136
137 - `/punyci-cache/`: persistent cache for this job, only if `Cache` is true
138 - `/punyci-repo/`: clone of the repository, separate for each job
139 - `/punyci-script`: configured `Script` of the job (read-only); executed with
140   `/punyci-repo/` as working directory
141
142 === Repository
143
144 The following files and directories are stored in the Git repository (either
145 in the bare repository itself or in `.git` for non-bare repositories); `$job`
146 is the index of the job (starting from zero):
147
148 - `punyci-cache-$job/`: persistent cache
149 - `punyci-log-$job`: log of the last run of this job
150 - `punyci-failed-$job`: empty file created if the last job failed, used to
151   report "fixed" jobs (gets deleted when a job is fixed)
152
153
154 == Licenses
155
156 punyci is licensed under GPL 3 or later.
157
158 Copyright (C) 2026  Simon Ruderich
159
160 This program is free software: you can redistribute it and/or modify
161 it under the terms of the GNU General Public License as published by
162 the Free Software Foundation, either version 3 of the License, or
163 (at your option) any later version.
164
165 This program is distributed in the hope that it will be useful,
166 but WITHOUT ANY WARRANTY; without even the implied warranty of
167 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
168 GNU General Public License for more details.
169
170 You should have received a copy of the GNU General Public License
171 along with this program.  If not, see <http://www.gnu.org/licenses/>.