Skip to content
intisy-composePublic

About

Shared GitHub Actions workflows for the intisy-compose stacks

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

47 Commits

Folders and files

Repository files navigation

workflows

Shared GitHub Actions workflows for the intisy-compose stacks: reusable workflows that every stack repository calls instead of carrying its own logic.

The reusable workflows of the intisy-compose stacks. A stack repository carries only a thin caller that uses: a workflow here; anything shared with other organisations lives in intisy/workflows instead.

Workflows

workflow what it checks or does
check-access.yml every port the calling stack publishes or exposes on the tailnet is in access/PORTS.md and listens only where its kind (web, api, protocol, public) allows, the stack's gateway is the shared one in gateway/ and every service in its namespace declares where it listens, its project, container and volume names follow the org's rules, its docker-compose.sh has the commands of its docker-compose.ps1 less those it declares windows_only (cli/check-cli.sh) and is committed LF and executable, its docker-compose.sh, scripts/*.sh, tests/cli/*.sh and tests/cli/fakes/* are clean under shellcheck -x of the version pinned in cli/shellcheck.pin (cli/check-shell.sh; cli/install-shellcheck.sh installs it), and its tests/cli/run.sh passes when it has one
self-test.yml this repository's own suites, called by test.yml on every push: each check's fixture test, and the data-on-disk helper's bash twin and Pester suite over the same cases

The data-on-disk helper

data-on-disk/ holds the restore decision every stack with a dumped database uses, twice: data-on-disk.ps1 for docker-compose.ps1 and data-on-disk.sh, a function-for-function bash twin, for docker-compose.sh. A stack vendors them byte for byte as scripts/data-on-disk.ps1 and scripts/data-on-disk.sh (the sh once it has a docker-compose.sh), and check-volumes.sh fails on any copy that differs. The decision cases live once, as data, in data-on-disk/tests/cases/; restore-decision.Tests.ps1 (Pester 3.4 syntax) and restore-decision.test.sh both read them, so a case added for one runs against both.

The shared gateway

gateway/ holds the gateway every stack with a web UI or a tailnet port uses: Caddy owns the network namespace and serves each UI on the tailnet name, with certificates only from the Tailscale socket, and on a http://:<port> site published on 127.0.0.1; the tailscale sidecar joins Caddy's namespace. A stack copies Caddyfile, gateway.yml, gateway.sh, namespace-address.sh, tailscale.sh, tailnet-node.sh and tailnet-node.ps1 into its own gateway/ folder byte for byte, extends both services from its compose file, and adds only its own values: gateway/sites.caddy (its sites, each tailnet site with import tailnet), gateway/serve.json (the tailnet ports, each a TCPForward to the same port on 127.0.0.1), container names, TAILNET_HOST, TS_HOSTNAME and the gateway's loopback ports. A change to the template is copied into every stack in the same step; the check fails on any copy that differs.

Nothing in the namespace listens on 127.0.0.1 or every address, except a tailnet port. The tailscale sidecar runs in userspace mode and hands every tailnet connection, TCP or UDP, on a port serve.json does not forward to 127.0.0.1:<port> of the namespace, so a listener there answers on the tailnet. Caddy's local sites bind the namespace's Docker address (gateway.sh), and so does every app that shares the namespace: it reads the address with namespace-address.sh --single (mounted from gateway/), and the gateway proxies to it as gateway:<port>, which Docker resolves to the same address. The stack lists each such service's ports under a top-level x-namespace-listeners block, as gateway (bound to that address), tailnet (a tailnet port in access/PORTS.md, forwarded in serve.json when TCP) or unbound (the app cannot choose its address; the reason sits beside the entry, and the check prints a note), and the check fails on an undeclared service, an address setting on loopback or every address, and a loopback upstream in sites.caddy.

A deleted stack leaves the tailnet. tailnet-node.sh and tailnet-node.ps1, twins with the same messages, are what a stack's delete command calls after its down: they read the stable node ID of the current profile from the stack's tailscaled.state (never the name, which another device may share), remove that device through the Tailscale API (DELETE /api/v2/device/<node ID>) with the optional TS_API_TOKEN from the stack's config file, then remove the state folder, so a later up joins fresh. The token travels only on curl's config on stdin or in a PowerShell header, never on a command line. Without a token they print the node's name and ID and the admin console's Machines page instead; on an API error, or a state that names no node, they keep the folder so delete can run again. gateway/tests/tailnet-node.test.sh and gateway/tests/tailnet-node.Tests.ps1 run them against a fake API.

Public access is one switch, the same in every stack: PUBLIC_ACCESS=true in the stack's config.env (default false, and anything but true, 1, yes or on counts as off) makes tailscale.sh add Tailscale Funnel to the serve config for every forwarded port Funnel carries (443, 8443, 10000), so those sites answer from the internet, without a login of their own, until the switch is off again and the stack restarted. It needs the funnel node attribute in the tailnet policy. A stack never opens Funnel any other way, and the ports Funnel carries are only ever web ports in access/PORTS.md.

Using it

name: Check dashboard access

on:
  workflow_dispatch:
  push:
  pull_request:

permissions:
  contents: read

jobs:
  check:
    uses: intisy-compose/workflows/.github/workflows/check-access.yml@main

Running the check locally

With the stack repositories cloned next to this one:

bash workflows/access/check-access.sh

It checks every *-compose directory beside workflows and exits non-zero, naming each violation, when one breaks the standard or when it finds no stack at all.

bash workflows/access/tests/check-access.test.sh plants each violation in a scratch stack and asserts that the check names it; cli/tests/check-cli.test.sh, cli/tests/check-shell.test.sh (it needs the pinned shellcheck, or SHELLCHECK naming a wrapper around it) and data-on-disk/tests/check-volumes.test.sh do the same for their checks, and data-on-disk/tests/restore-decision.test.sh and gateway/tests/tailnet-node.test.sh run the bash twins of the two helpers, data-on-disk/tests/run-pester.ps1 (Windows PowerShell 5.1) their PowerShell twins. CI runs all of them through self-test.yml.

About

Shared GitHub Actions workflows for the intisy-compose stacks

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages