chore: replace self-hosted deploy runner with SSH from GitHub-hosted runners - #3
Merged
Merged
Conversation
added 4 commits
July 30, 2026 23:07
…runners soy-chrislo/queres is a personal-account repo, not an org member, so the org's self-hosted runner-group mechanism can never apply here (verified via the GitHub API, not assumed). Rewrites deploy.yml's deploy job to run on ubuntu-latest and reach the VPS over SSH as the existing deploy system user (the same convention four other projects on that VPS already use), makes the GHCR package public to drop docker login entirely, fixes the GHCR image reference in all three compose files, rewrites deploy/README.md's runner section, and records the reversal in ADR-0027.
…ploy diff - Stop naming the real VPS hostname and other, unrelated projects' SSH key identifiers in the new public docs (deploy.yml header, deploy/README.md, ADR-0027) — those details don't belong in a public repo and weren't this project's to disclose. - Converge all three ssh/scp remote-command constructions in deploy.yml on the same safe pattern (a single, already-quoted command string) instead of relying on ssh's unquoted joining of trailing arguments for one of them; the prior comment claimed a safety property that pattern didn't actually provide, even though today's fixed values aren't exploitable. - Pre-clean the remote frontends temp dir before every scp, since scp -r merges into an existing directory rather than mirroring it. - Document the new SCP-to-temp-dir frontend transfer mechanism in ADR-0027, which the prior version omitted.
TMP_DIR was still /home/deploy/tmp-frontends-<env> after the previous redaction pass -- flagged by a re-review as still disclosing the real deploy account name via a standard /home/<user> convention, and it was also being restated in prose in ADR-0027. Make TMP_DIR a path relative to the deploy account's home directory instead (ssh/scp both resolve relative remote paths against $HOME by convention). The remote script resolves it back to an absolute path via $HOME before using it in `docker run -v`, since Docker treats a bind-mount source with no leading / as a named-volume name to create, not a bind mount -- silently mounting an empty volume instead of the scp'd files if left relative there.
A third review pass found the previous redaction (hostname, sibling project key labels, /home/<user> path) missed three prose sentences that still spelled out the real account name directly (backticked `deploy`, distinct from the GitHub Actions job also named `deploy:`, which is fine to disclose since it's defined in this same public file). Reworded to refer to it generically, consistent with the rest of the redaction.
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The deploy pipeline in this repo (
.github/workflows/deploy.yml) was copied verbatim from the privatetunodo-organization/auth-gatewayrepo and has never worked here: thedeployjob requires a[self-hosted, linux, deploy-host]runner, and this repo can never register one — it's a personal-account repo, not a member oftunodo-organization, and org-scoped runner groups (the only safe mechanism to expose a self-hosted runner to a public repo) don't extend outside org membership. Verified live viagh api orgs/tunodo-organization/actions/runner-groups, not assumed.This PR replaces the self-hosted
deployjob with one that runs on GitHub-hostedubuntu-latestrunners and reaches the target VPS over SSH as an existing dedicated deploy account — the same convention already used by other, unrelated projects on that same VPS. Seedocs/adr/ADR-0027-public-repo-ssh-deploy.mdfor the full decision record.deploy.yml:deployjob nowruns-on: ubuntu-latest; rawssh/scp(not a third-party composite action) with a pinnedknown_hostssecret for real host-key verification; nodocker loginon the VPS at all (the GHCR package is made public instead);if: always()cleanup of the remote temp dir.deploy/{prod,staging,dev}/docker-compose.yml: fixed the staleghcr.io/CHANGEME_GITHUB_ORG/auth-gatewayplaceholder to the realghcr.io/soy-chrislo/queresnamespace this repo actually publishes to.deploy/README.md: replaced the org-only "dedicated runner group" mitigation section (inapplicable to a personal-account repo) with the actual SSH deploy-credential provisioning steps.docs/adr/ADR-0027-public-repo-ssh-deploy.md: new — records why self-hosted is structurally unavailable here and the alternatives that were ruled out (docker loginover SSH,appleboy/ssh-action, editing the live compose file before the new image is provably pullable).Real production VPS details (hostname, deploy account name, other tenants' SSH key identifiers sharing that account) are deliberately not spelled out anywhere in this public repo — the workflow resolves what it needs at runtime from GitHub Environment secrets instead.
This is infrastructure/CI-only: no application code changed.
Test plan
.github/workflows/deploy.ymlparses as valid YAML (python3 -c "import yaml; yaml.safe_load(open('.github/workflows/deploy.yml'))")run:block in the file passesbash -n(syntax check), including the heredoc-based remote scriptprintf %q-based remote-command construction was traced by hand and simulated locally to confirm it round-tripsDEPLOY_PATH/IMAGE_TAG/TMP_DIRcorrectly through the remote shellcode-reviewerandsecurity-reviewersubagents run 3 times against the actual diff (not just this description); each round's findings were fixed and re-verified before the nextworkflow_dispatchagainststagingonce the SSH secrets exist (tracked as follow-up work, not part of this PR — secrets don't exist yet)