Skip to content

Releases

v0.4.0

Choose a tag to compare

@github-actions github-actions released this 10 Nov 23:47

jcloud can now watch a server, not only check a tree. A new command gathers the observations on the host, jcloud.yml declares what the machine itself must be, and survey tells a new failure from one already known. Templates under contrib/ run all of it on a schedule. jcloud still reaches no network, keeps no state and alerts nobody: the transport, the last run and the alert stay the platform's.

observe: the collector, shipped with jcloud

  • jcloud observe [--root <dir>] runs on the host, in the deployed tree, and prints the lines survey reads: each container docker has up and how it stands, the repo digest it runs, each service's pin from pin_file, and each compose profile a manifest declares (on when COMPOSE_PROFILES names it or one of its services is up). Names come from jcloud.yml, so a platform no longer needs a collector of its own. A docker that cannot be asked is an error, never an empty answer.

host: in jcloud.yml

Absent, nothing about the host is asserted. A platform opts in by declaring:

host:
  timers:
    - japode-healthwatch                  # must be active, last run succeeded
    - {name: japode-backup, max_age: 7h}  # …and finished within 7h
  paths:
    - {path: ., mode: "750", owner: runner:runner}
  firewall: ufw                           # installed and active
  disks: [/, /var/lib/docker]
  disk_free: 15%
  certs: [caddy/certificates/*/*.crt]     # a glob is allowed
  cert_days: 14
  • Timers are read with systemctl: active, how the unit they fire last ended, and when. An active timer whose job fails every night is reported, as is a backup older than its max_age.
  • Disks are held to a free-space floor (df), and certificates to a number of days left, read from the file with crypto/x509, never by connecting to the host name.
  • A fact that could not be read (ufw status without root, a failed stat) is reported as unread, never as a pass.

survey

  • --since <previous observations> marks each finding new or standing and lists the ones gone. Findings are matched by a stable code and subject, so a reworded sentence, or a disk going from 9% to 8% free, is still the same finding. The exit code is still this run's verdict.
  • --json prints {"findings": [...]}, each with code (container.missing, digest.moved, timer.failed, backup.stale, disk.low, cert.expiring, …), subject, says and, with --since, state. Nothing else goes to stdout in this mode.

contrib/: a watch that runs itself

Templates a platform copies; jcloud never runs them.

  • contrib/systemd/: a script, service and timer (every 15 minutes) that observe, survey against the last run and leave report.json and report.txt. The unit fails when the server diverges, so it shows in systemctl --failed.
  • contrib/github/jcloud-watch.yml: a workflow for a self-hosted runner on the server that keeps one GitHub issue per finding: opened when new, updated only when its wording changes, closed when gone.

agree

  • A compose environment value that is a number or boolean (PORT: 2700) no longer stops agree --compose at the decode. It is rendered as compose renders it and compared.
  • A file it could not read is named once, with slashes, on every OS. The message no longer repeats the absolute path from the OS error, and the Windows build no longer mixes services\paste\… and services/paste/… in one report.

Not yet

  • A manifest's backup implying its own timer waits on Viglet Cloud's manifests (JCT23). Until then, list each timer under host.timers.

Install

Download a binary below, or go install github.com/Japode/jcloud@v0.4.0.

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 10 Nov 21:12

jcloud can now check a platform laid out differently from Japode. Everything it used to assume about where files sit and how services are reached is now something the platform declares. The motivating case is Viglet Cloud: an nginx edge, one shared compose file split by profiles, shared postgres/redis/Keycloak, and per-client copies of each product.

A platform describes itself

  • jcloud.yml at the platform root names the manifest path, route file and kind, container prefix, image variable, pin file, deploy command, addon path, backup kind, whether images are built on the host, and where .env.example lives. Without the file, every value is Japode's, so an existing Japode tree is read exactly as before.

agree

  • nginx edges. --routes takes nginx -T output when route.kind: nginx. Hosts come from server_name, the upstream from proxy_pass (with set $var and upstream blocks resolved), plus client_max_body_size and proxy_set_header (an empty value counts as a strip). Caddy's caddy adapt JSON still works, and --adapted is kept as an alias.
  • The resolved compose model. --compose takes docker compose --profile '*' config --no-interpolate --format json and finds each service by name, so one shared compose file split by profiles is checked correctly. A manifest may declare profile:.
  • Shared needs. needs: [{name, type, shared: postgres}] points at a platform service several products use. jcloud checks that service against the addon and checks that the client waits on it and reaches it.
  • Backups. backup: none skips the backup.conf comparison and says so in a non-failing note. Each sqlite file or database the platform claims nothing for is named.
  • Variables. env_prefix: declares a service's key prefix (e.g. VIGLET_TURING_). shared: true on a variable makes it a bare key, such as OPENAI_API_KEY, that every manifest declaring it must agree on.
  • Per-client copies. instances: names copies by template (turing-{client}). Each copy found in the compose model and the edge is held to the product's port, image variable, healthcheck and route.
  • Auth gates. A route may state gate: /oauth2/auth, compared with nginx auth_request or Caddy forward_auth in both directions. A declared gate the route doesn't run is reported as open to anyone.
  • TCP probes. health.probe: tcp needs no path. A service with no expose: and no ports: may show its port through its healthcheck; one that declares an expose: must still list the port.

up and survey

  • Host-built images. build: <Dockerfile> replaces image: where jcloud.yml says builds: true. Such a service is ready without a pin, and survey compares no digest for it.
  • Moving tags. A pin that is a tag rather than a digest is named, along with the digest to pin from --published.
  • Opt-in services. survey doesn't report a service missing when its compose profile wasn't reported on.

Output changes

  • ::error/::warning annotations are printed only when GITHUB_ACTIONS=true. A terminal now shows each problem once. If you run jcloud in a container from an Actions step, pass the variable through (docker run -e GITHUB_ACTIONS …), or the annotations disappear.
  • compare reports only the lines that differ, each named by its field (port:, env[2]:), instead of every line between two distant differences.

Contract tests

  • JCLOUD_PLATFORM takes a :-separated list of platform trees. Each is checked through its own jcloud.yml.

Install

Download a binary below, or go install github.com/Japode/jcloud@v0.3.0.

v0.2.0

Choose a tag to compare

@github-actions github-actions released this 10 Nov 19:43

The first release with prebuilt binaries. jcloud behaves the same as in v0.1.0; what changed is how you get it.

  • Binaries on every tag. GoReleaser builds jcloud for Linux, macOS and Windows on amd64 and arm64, and publishes the archives with a checksums.txt. The release workflow runs the same ci/check.sh gate as every push first, so a tag can't publish what CI would refuse.
  • The version comes from the tag. jcloud version prints the release, the schema version and the Go compiler that built it. There is no version constant to bump.

Install

Download a binary below and check it against checksums.txt, or go install github.com/Japode/jcloud@v0.2.0.

v0.1.0

Choose a tag to compare

@alegauss alegauss released this 10 Nov 21:15

jcloud as a module of its own, extracted from tools/jcloud in the Japode platform repository (Japode/cloud). It checks a service's japode.yml manifest against the files that have to agree with it. It renders nothing and deploys nothing.

  • check refuses a manifest that doesn't state what a service must declare. With --repo, it also compares the manifest against the service's own Dockerfile, Caddyfile and publishing workflows.
  • agree compares the manifest against the platform files deployed beside it: the compose file, the Caddy route (read from caddy adapt output, so a commented-out block can't pass), backup.conf, .env.example, and the addons a needs: entry brings.
  • compare reports where the platform's copy of a manifest and the service's own copy disagree.
  • survey compares a server, as observed by the platform's collector script, against what the manifests declare.
  • up names what stands between each service and a deploy: missing variables, a missing pin, and the digest its CI published.
  • env manages .env.age, a configuration store whose keys are readable and whose values are age-encrypted.

This tag has no prebuilt binaries; those start with v0.2.0. Install it with go install github.com/Japode/jcloud@v0.1.0.