# CI: every push runs the checks of the pre-push hook (tool/check.sh); a push to main is # then deployed to learn.halkyon.eu (deploy/redeploy.sh). Both jobs run on the runner on # sahaquiel, directly on the host as an unprivileged user (deploy/runner/). # # Pushes only, no pull requests: a pull request from a fork would run its own code on # the production server's runner. name: CI on: push: # Runs the workflow again from the web UI, e.g. to retry a failed deploy. workflow_dispatch: jobs: check: runs-on: sahaquiel timeout-minutes: 45 env: # The Flutter the app is developed with; move it together with the local one. FLUTTER_VERSION: 3.47.5 steps: # Before anything else runs, the job proves it can neither see nor reach production # (the runner's sandbox, deploy/runner/). If it can, nothing else should run. - name: Sandbox run: | fail() { echo "The runner's sandbox leaks: $*" >&2; exit 1; } # Only the runner's own processes are visible. A container's process shows up # (in a process namespace of its own) if it runs under the runner's user ID, # and then its environment and files are readable too. [ "$(ps -e -o uid= | sort -u | wc -l)" -eq 1 ] || fail "other users' processes are visible" namespaces=$(for p in /proc/[0-9]*; do readlink "$p/ns/pid"; done 2>/dev/null | sort -u | wc -l) [ "$namespaces" -le 1 ] || fail "processes of $namespaces process namespaces are visible" # deploy's home (.env, the checkout, the dumps) and the Docker socket are out of # reach. [ ! -e /home/deploy ] || fail "/home/deploy is visible" { [ ! -r /run/docker.sock ] && [ ! -w /run/docker.sock ]; } || fail "the Docker socket is usable" # Docker's networks, where the database listens, are unreachable. Caddy listens on # every address of the host, the Docker bridge's included. if timeout 5 bash -c 'exec 3<>/dev/tcp/172.17.0.1/443' 2>/dev/null; then fail "Docker's network is reachable" fi echo "This job can neither see nor reach production." - name: Check out env: TOKEN: ${{ github.token }} run: | git init -q . # The job's token as a header from the environment, so it never shows in a # command line; plain git, so the host needs no Node.js for actions/checkout. export GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=http.extraHeader \ GIT_CONFIG_VALUE_0="Authorization: Basic $(printf 'ci:%s' "$TOKEN" | base64 -w0)" git fetch -q --depth 1 "${{ github.server_url }}/${{ github.repository }}.git" "${{ github.sha }}" git checkout -q -f FETCH_HEAD git clean -q -ffdx - name: Flutter run: | sdk="$HOME/sdk/flutter-$FLUTTER_VERSION" if [ ! -x "$sdk/bin/flutter" ]; then rm -rf "$sdk" git clone -q --depth 1 --branch "$FLUTTER_VERSION" https://github.com/flutter/flutter.git "$sdk" "$sdk/bin/flutter" config --no-analytics >/dev/null "$sdk/bin/dart" --disable-analytics >/dev/null fi echo "$sdk/bin" >> "$FORGEJO_PATH" "$sdk/bin/flutter" --version - name: Dependencies run: flutter pub get - name: Checks run: tool/check.sh deploy: needs: check if: github.ref == 'refs/heads/main' runs-on: sahaquiel timeout-minutes: 30 steps: # The key can run nothing but deploy/redeploy.sh, and only from sahaquiel itself. - name: Deploy to learn.halkyon.eu run: >- ssh -i "$HOME/.ssh/deploy_trigger" -o IdentitiesOnly=yes -o BatchMode=yes -o StrictHostKeyChecking=yes -o UserKnownHostsFile="$HOME/.ssh/known_hosts" deploy@127.0.0.1 "${{ github.sha }}"