Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions deploy/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,16 @@ chmod 700 "$cache"
trap 'echo "Interrupted; rerun the installer to continue." >&2; exit 130' INT
# Resume after resets, fail only when a download stalls (no total time cap), and give up after five attempts.
download() {
local name="$1" file="$2" progress="$3" attempt code
local name="$1" file="$2" progress="$3" attempt code error
for attempt in 1 2 3 4 5; do
if code="$(curl --fail "$progress" --location --proto '=https' --proto-redir '=https' --connect-timeout 15 \
--speed-limit 1024 --speed-time 60 --continue-at - --write-out '%{http_code}' \
--output "$cache/$file" "$base/$name")"; then return 0; fi
--output "$cache/$file" "$base/$name")"; then return 0; else error=$?; fi
[[ "$code" != 404 ]] || { echo "Release $version not found." >&2; exit 1; }
# The partial file already holds the whole download; the checksum decides.
[[ "$code" != 416 ]] || return 0
# The server cannot resume; start the file again.
[[ "$error" != 33 ]] || rm -f "$cache/$file"
if (( attempt < 5 )); then echo "Download interrupted; retrying ($((attempt + 1))/5)..." >&2; sleep 3; fi
done
echo "Could not download $name from GitHub. Check the network (set HTTPS_PROXY if a proxy is required) and rerun the installer." >&2
Expand Down
17 changes: 15 additions & 2 deletions deploy/test_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@


class LauncherTests(unittest.TestCase):
def run_launcher(self, system='Darwin', machine='arm64', corrupt=False, arguments=('--install-dir', '/path with spaces/core', '--version', 'v1.2.3'), missing=False, outage=False, seed=None, interrupt=False):
def run_launcher(self, system='Darwin', machine='arm64', corrupt=False, arguments=('--install-dir', '/path with spaces/core', '--version', 'v1.2.3'), missing=False, outage=False, seed=None, interrupt=False, resumable=True):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
binary = b'#!/bin/sh\nprintf "%s\\n" "$@"\n'
Expand All @@ -26,6 +26,10 @@ def run_launcher(self, system='Darwin', machine='arm64', corrupt=False, argument
os.kill(os.getppid(), signal.SIGINT); time.sleep(0.2); sys.exit(2)
if asset.endswith('.sha256'):
target.write_text(DIGEST + ' ' + asset.removesuffix('.sha256') + '\\n')
elif target.exists() and not RESUMABLE:
# Like curl against a server that ignores Range: it answers 200, exits 33 and leaves the file alone.
with open(HERE + '/curl.log', 'a') as log: log.write(str(target.stat().st_size) + '\\n')
sys.stdout.write('200'); sys.exit(33)
elif target.exists():
# Like curl --continue-at -: append the rest, or answer 416 when the file is already complete.
have = target.stat().st_size
Expand All @@ -38,7 +42,7 @@ def run_launcher(self, system='Darwin', machine='arm64', corrupt=False, argument
if OUTAGE: sys.exit(22)
if MISSING:
sys.stdout.write('404'); sys.exit(22)
'''.replace('OUTAGE', repr(outage)).replace('MISSING', repr(missing)).replace('DIGEST', repr('0' * 64 if corrupt else digest)).replace('BINARY', repr(binary)).replace('INTERRUPT', repr(interrupt)).replace('HERE', repr(str(root))),
'''.replace('OUTAGE', repr(outage)).replace('MISSING', repr(missing)).replace('DIGEST', repr('0' * 64 if corrupt else digest)).replace('BINARY', repr(binary)).replace('INTERRUPT', repr(interrupt)).replace('RESUMABLE', repr(resumable)).replace('HERE', repr(str(root))),
}
scripts['sleep'] = '#!/bin/sh\n'
for name, contents in scripts.items():
Expand Down Expand Up @@ -96,6 +100,15 @@ def test_rerun_resumes_the_unfinished_download(self):
self.assertEqual(result.resumed, [10])
self.assertEqual(result.cache, [])

def test_unfinished_download_restarts_when_the_server_cannot_resume(self):
binary = b'#!/bin/sh\nprintf "%s\\n" "$@"\n'
name = 'oac-linux-amd64.' + hashlib.sha256(binary).hexdigest()
result = self.run_launcher('Linux', 'x86_64', seed={name: binary[:10]}, resumable=False)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(result.stdout.splitlines(), ['install', '--install-dir', '/path with spaces/core', '--version', 'v1.2.3'])
self.assertEqual(result.resumed, [10])
self.assertEqual(result.cache, [])

def test_complete_cached_binary_runs_without_downloading_again(self):
binary = b'#!/bin/sh\nprintf "%s\\n" "$@"\n'
name = 'oac-linux-amd64.' + hashlib.sha256(binary).hexdigest()
Expand Down