Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Remove the container deployment

9bce37233413e6a56c38d5cf90e79dfa496649b2 · Keenan Nemetz · 3d ago · parent c1edfd3c

Remove the container deployment

A Vole instance is one process whose state is a directory on local disk. A
container therefore adds a layer without removing one, and it costs
something real: the service unit's hardening, and the CI half, which needs
an OCI runtime the container does not have without mounting the host's
socket — the thing the runner docs refuse.

It was also the weakest path in practice. No image is published, so an
upgrade meant a source checkout and a Go build where the package is one
`apt install`. CI never ran the container; it was tested by hand once, which
is a surface that rots quietly.

So the Dockerfile, the compose file, the entrypoint, the image's
configuration, and the guide go. The deployment checker loses its compose
and COPY checks, and the render script is the only shipped script left.

Every other mention of Docker in the repository means the CI container
runtime, and those stay.

Users who manage their machines with Compose or Kubernetes are told where
they stand: give the forge a small virtual machine of its own, and the
first-boot file works there unchanged.

scripts/check-deploy.py +3−37

@@ -2,9 +2,8 @@
2 2 """Fail when a file under deploy/ would break someone's first deployment.
3 3
4 4 The deployment files are the ones nobody runs before a user does: a cloud-init
5 template with a stray placeholder, a boot script with a shell syntax error, or a
6 compose file pointing at a path that is not there all fail on somebody's first
7 boot, minutes after they spent money on a machine.
5 template with a stray placeholder, or a boot script with a shell syntax error,
6 fails on somebody's first boot, minutes after they spent money on a machine.
8 7
9 8 What is checked:
10 9
@@ -13,7 +12,6 @@ What is checked:
13 12 deploy/cloud-init/render.sh uses for it, and no placeholder is left behind;
14 13 - the rendered user data is a cloud-config document with the files it promises,
15 14 and its boot script is valid `sh`;
16 - deploy/docker/compose.yaml builds a Dockerfile whose COPY sources exist;
17 15 - the packaged runner unit, its argument file, and the template that configures
18 16 them agree on the names, because three files agreeing by memory is how a
19 17 runner ends up enabled but idle.
@@ -249,7 +247,7 @@ def check_sh(text, where):
249 247
250 248 def check_scripts():
251 249 bad = 0
252 for rel in ("cloud-init/render.sh", "docker/entrypoint.sh"):
250 for rel in ("cloud-init/render.sh",):
253 251 path = os.path.join(DEPLOY, rel)
254 252 if not os.path.exists(path):
255 253 bad |= fail("missing", f"deploy/{rel}")
@@ -261,37 +259,6 @@ def check_scripts():
261 259 return bad
262 260
263 261
264 def check_container():
265 bad = 0
266 compose_path = os.path.join(DEPLOY, "docker", "compose.yaml")
267 with open(compose_path, encoding="utf-8") as f:
268 compose = yaml.safe_load(f)
269 service = (compose.get("services") or {}).get("vole") or {}
270 build = service.get("build") or {}
271 if not build:
272 return fail("no vole service with a build", "deploy/docker/compose.yaml")
273
274 context = os.path.normpath(os.path.join(os.path.dirname(compose_path), build.get("context", ".")))
275 dockerfile = os.path.normpath(os.path.join(context, build.get("dockerfile", "Dockerfile")))
276 if not os.path.exists(dockerfile):
277 bad |= fail(f"the build names {dockerfile}, which does not exist", "deploy/docker/compose.yaml")
278
279 # Every COPY in the Dockerfile has to name something in the build context,
280 # because a missing file fails the build minutes in.
281 with open(dockerfile, encoding="utf-8") as f:
282 for line in f:
283 match = re.match(r"\s*COPY\s+(?!--from)(\S+)\s+\S+", line)
284 if not match:
285 continue
286 source = os.path.join(context, match.group(1))
287 if "*" not in match.group(1) and not os.path.exists(source):
288 bad |= fail(f"COPY {match.group(1)} is not in the build context", "deploy/docker/Dockerfile")
289
290 if not (service.get("volumes") or []):
291 bad |= fail("no volume; the instance would not survive a restart", "deploy/docker/compose.yaml")
292 return bad
293
294
295 262 def main():
296 263 bad = 0
297 264 bad |= check_yaml_files()
@@ -309,7 +276,6 @@ def main():
309 276 bad |= check_runner_template(lists["runner.yaml"])
310 277
311 278 bad |= check_scripts()
312 bad |= check_container()
313 279 return 1 if bad else 0
314 280
315 281