diff --git a/scripts/git-incremental-deploy-macos.mjs b/scripts/git-incremental-deploy-macos.mjs index dd590bb..0e0e553 100755 --- a/scripts/git-incremental-deploy-macos.mjs +++ b/scripts/git-incremental-deploy-macos.mjs @@ -155,6 +155,9 @@ function sshArguments(identityFile, knownHostsFile) { '-o', 'StrictHostKeyChecking=yes', '-o', `UserKnownHostsFile=${knownHostsFile}`, '-o', 'ConnectTimeout=10', + '-o', 'ServerAliveInterval=3', + '-o', 'ServerAliveCountMax=10', + '-o', 'TCPKeepAlive=yes', '-i', identityFile, ] } diff --git a/scripts/git-incremental-deploy.ps1 b/scripts/git-incremental-deploy.ps1 index 81f2bcc..888dcef 100644 --- a/scripts/git-incremental-deploy.ps1 +++ b/scripts/git-incremental-deploy.ps1 @@ -819,7 +819,9 @@ actual_prod_host_key="$(ssh-keygen -F "$PROD_SERVER_IP" -f "$PROD_KNOWN_HOSTS_FI ok "Build server tools are available" prod_ssh() { - ssh -o BatchMode=yes -o StrictHostKeyChecking=yes -o "UserKnownHostsFile=$PROD_KNOWN_HOSTS_FILE" -o ConnectTimeout=8 "$PROD_SERVER_USER@$PROD_SERVER_IP" "$@" + ssh -o BatchMode=yes -o StrictHostKeyChecking=yes -o "UserKnownHostsFile=$PROD_KNOWN_HOSTS_FILE" \ + -o ConnectTimeout=8 -o ServerAliveInterval=3 -o ServerAliveCountMax=10 -o TCPKeepAlive=yes \ + "$PROD_SERVER_USER@$PROD_SERVER_IP" "$@" } prod_ssh_script() { diff --git a/scripts/git-incremental-deploy.test.mjs b/scripts/git-incremental-deploy.test.mjs index 3a27e55..fab6188 100644 --- a/scripts/git-incremental-deploy.test.mjs +++ b/scripts/git-incremental-deploy.test.mjs @@ -40,3 +40,15 @@ test('Mac entry requires an exact commit outside DryRun', () => { assert.equal(parseArguments(['--dry-run']).dryRun, true) assert.equal(parseArguments(['--commit', 'b'.repeat(40)]).commit, 'b'.repeat(40)) }) + +test('deployment SSH hops keep long builds and cutovers alive', () => { + const macSource = readFileSync(new URL('./git-incremental-deploy-macos.mjs', import.meta.url), 'utf8') + const powerShellSource = readFileSync(new URL('./git-incremental-deploy.ps1', import.meta.url), 'utf8') + const payload = extractRemoteTemplate(powerShellSource) + + for (const source of [macSource, payload]) { + assert.match(source, /ServerAliveInterval=3/) + assert.match(source, /ServerAliveCountMax=10/) + assert.match(source, /TCPKeepAlive=yes/) + } +})