| name | push-changes | ||||
|---|---|---|---|---|---|
| description | Guide for pushing commits in the torrust-tracker project. Covers the push workflow, pre-push hook setup, and the SSH idle-timeout problem that can interrupt pushes when the pre-push hook runs long. Triggers on "push changes", "git push", "how to push", "push branch", "SSH timeout on push", or "Connection closed by remote host". | ||||
| metadata |
|
This skill guides you through the complete push process for the Torrust Tracker project.
# One-time setup: install the pre-push Git hook
./contrib/dev-tools/git/install-git-hooks.sh
# Push the current branch to its upstream remote
git push <remote> <branch>The repository ships a pre-push Git hook that runs
./contrib/dev-tools/git/hooks/pre-push.sh automatically on every git push. Install
it once after cloning:
./contrib/dev-tools/git/install-git-hooks.shAfter installation the hook fires automatically; you do not need to invoke the script manually before each push.
For AI agents: before invoking the script manually, check whether the hook is installed:
./contrib/dev-tools/git/check-git-hooks.shIf installed, skip the manual run —
git pushwill trigger it automatically. Running both would execute every check twice.
⏱️ Expected runtime: ~5 minutes on a modern developer machine with warm caches. AI agents should set a command timeout of at least 15 minutes before invoking
./contrib/dev-tools/git/hooks/pre-push.sh.
When the pre-push hook is installed, git push itself becomes a long-running command
because it executes the full pre-push suite before uploading objects. On cold caches,
runtime can exceed the warm-cache expectation.
Recommended for AI-agent terminal execution:
- Prefer running
git pushwith a generous timeout (at least 20 minutes). - Do not treat sparse output as a hang too quickly; some phases can be quiet.
- Do not start a second
git pushwhile one is still running. - Wait for terminal completion (exit code + final output) before retrying.
The pre-push script runs these steps in order:
cargo +nightly fmt --check— nightly format checkcargo +nightly check ...— nightly workspace checkcargo +nightly doc ...— nightly documentation buildcargo +stable test --tests --benches --examples --workspace --all-targets --all-features— all tests
Steps already covered by pre-commit (machete, linters, doc tests) are intentionally omitted — they always run before each commit. E2E tests are excluded because they are slow and run in CI, which is the merge authority.
Check ownership is intentionally split by gate:
- Pre-commit: fast local gate (
cargo machete,linter all,cargo test --doc --workspace) - Pre-push: nightly toolchain checks + full stable test suite (no duplicates of pre-commit; no E2E)
- CI: merge authority with full validation and E2E matrix jobs
When running git push, you may see a connection error like:
Connection to ssh.github.com closed by remote host.
fatal: the remote end hung up unexpectedly
Git opens an SSH connection to GitHub before running the pre-push hook. If the hook takes longer than GitHub's SSH idle timeout (~300 seconds), the connection is torn down while the hook is still running. When Git tries to use the connection after the hook exits, the push fails.
Not every quiet terminal indicates an SSH failure. Pre-push checks can run for several minutes, especially on cold caches. Confirm failure from actual error output (for example, "Connection to ssh.github.com closed by remote host") before concluding the push is broken.
Add the following to ~/.ssh/config on your developer machine:
Host ssh.github.com
ServerAliveInterval 60
ServerAliveCountMax 10
ServerAliveInterval 60 sends a keep-alive packet every 60 seconds.
ServerAliveCountMax 10 allows up to 10 unanswered keep-alives before
the client declares the connection dead (10 × 60 s = 600 s extra tolerance).
⚠️ Warning: This fix is a local machine configuration change. It is not reproducible in automated or AI-agent environments (CI, GitHub Actions, remote codespaces) because those environments do not read your personal~/.ssh/config. In those environments the only reliable remedy is to ensure the pre-push hook completes well within 300 seconds.
| Environment | Recommended approach |
|---|---|
| Personal developer machine | Fix 1 (SSH keep-alive in ~/.ssh/config) |
| CI / GitHub Actions | No fix needed — CI does not run the pre-push hook |
| AI agent / automated environment | Keep hook runtime < 300 s; do not rely on Fix 1 |
The pre-push script supports concise human output, verbose human output, and JSON output for automation.
# Default: text + concise
./contrib/dev-tools/git/hooks/pre-push.sh
# Explicit text + concise
./contrib/dev-tools/git/hooks/pre-push.sh --format=text --verbosity=concise
# Text + verbose streaming command output
./contrib/dev-tools/git/hooks/pre-push.sh --format=text --verbosity=verbose
# Compatibility alias
./contrib/dev-tools/git/hooks/pre-push.sh --format=text --verbose
# Structured output (single JSON document to stdout)
./contrib/dev-tools/git/hooks/pre-push.sh --format=jsonFlag behavior:
--format=<text|json>defaults totext--verbosity=<concise|verbose>defaults toconcise--verboseis an alias for--verbosity=verbose- Duplicate
--format/--verbosityflags: last value wins - Invalid values or unknown flags exit with code
2and print usage guidance to stderr - In
--format=json, structured output remains JSON regardless of verbosity value - Per-step logs are written to
TORRUST_GIT_HOOKS_LOG_DIR(default:/tmp)
For restricted agent environments that cannot write outside the workspace, run with:
TORRUST_GIT_HOOKS_LOG_DIR=.tmp ./contrib/dev-tools/git/hooks/pre-push.shThe .tmp/ directory is git-ignored.
Because .tmp/ is workspace-local, clean stale pre-push-*.log files periodically.
- Concise mode shows high-signal per-step summaries only. On failure, it prints the log path and a short failure tail.
- Verbose mode streams full command output to the terminal. Use this for deep local debugging.
- JSON mode emits one structured document to stdout; diagnostics and usage errors go to stderr.
- If concise output is too short for debugging, re-run the same command with
--format=text --verbosity=verbose.