Files
bob-builder-1/bob/cli.py
T
David Zuelke f8b002de47 Correctly handle Ctrl+C
When receiving a signal, a process must kill itself using the same signal
sys.exit()ing 0, 1, 130, whatever will not signal to the calling program that we terminated in response to the signal

Best example: `for f in a b c; do bob deploy $f; done`, hitting Ctrl+C should interrupt Bob and stop the bash loop, but does not with `sys.exit()`:

    # for x in php-7.3.13 php-7.3.13 php-7.3.13; do bob build $x; done
    Fetching dependencies... found 1:
      - libraries/libc-client-2007f

    Building formula php-7.3.13 in /tmp/bob-35s7cr5z:

    -----> Building php-7.3.13...
      % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                     Dload  Upload   Total   Spent    Left  Speed
    100 18.7M  100 18.7M    0     0  7435k      0  0:00:02  0:00:02 --:--:-- 7434k
    ^Cool.
    Fetching dependencies... found 1:
      - libraries/libc-client-2007f

    Building formula php-7.3.13 in /tmp/bob-vmuko2ra:

    -----> Building php-7.3.13...
      % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                     Dload  Upload   Total   Spent    Left  Speed
     20 18.7M   20 3887k    0     0  7479k      0  0:00:02 --:--:--  0:00:02 7476k^Cool.
    Fetching dependencies... found 1:
      - libraries/libc-client-2007f

    Building formula php-7.3.13 in /tmp/bob-p2t8as81:

    -----> Building php-7.3.13...
      % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                     Dload  Upload   Total   Spent    Left  Speed
      4 18.7M    4  943k    0     0  3599k      0  0:00:05 --:--:--  0:00:05 3588k^Cool.

We want instead to have the loop end upon the first Ctrl+C.

That's only possible if Bash knows that we exited in response to Ctrl+C (=SIGINT), then it'll also terminate the loop
Bash will report the exit status as 128+$signal, so 130 for SIGINT, but sys.exit(130) does not to the same thing - the value of 130 is simply bash's representation
Killing ourselves with the signal number that we are aborting in response to does all this correctly, and bash will see the right WIFSIGNALED() status of our program, not WIFEXITED()
2020-01-15 02:01:38 +01:00

81 lines
2.5 KiB
Python

# -*- coding: utf-8 -*-
"""Usage: bob build <formula> [--name=FILE]
bob deploy <formula> [--overwrite] [--name=<FILE>]
Build formula and optionally deploy it.
Options:
-h --help
--overwrite allow overwriting of deployed archives.
--name=<path> allow separate name for the archived output
Configuration:
Environment Variables: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, S3_BUCKET, S3_PREFIX (optional), UPSTREAM_S3_BUCKET (optional), UPSTREAM_S3_PREFIX (optional)
"""
import os
import signal
import sys
from docopt import docopt
from .models import Formula
from .utils import print_stderr
def build(formula, name=None):
f = Formula(path=formula, override_path=name)
try:
assert f.exists
except AssertionError:
print_stderr("Formula {} doesn't exist.".format(formula), title='ERROR')
sys.exit(1)
# CLI lies ahead.
f.build()
return f
def deploy(formula, overwrite, name):
f = build(formula, name)
print_stderr('Archiving.')
f.archive()
print_stderr('Deploying.')
f.deploy(allow_overwrite=overwrite)
def main():
args = docopt(__doc__)
formula = args['<formula>']
do_build = args['build']
do_deploy = args['deploy']
do_overwrite = args['--overwrite']
do_name = args['--name']
if do_build:
build(formula, name=do_name)
if do_deploy:
deploy(formula, overwrite=do_overwrite, name=do_name)
def sigint_handler(signo, frame):
# when receiving a signal, a process must kill itself using the same signal
# sys.exit()ing 0, 1, 130, whatever will not signal to the calling program that we terminated in response to the signal
# best example: `for f in a b c; do bob deploy $f; done`, hitting Ctrl+C should interrupt Bob and stop the bash loop
# that's only possible if Bash knows that we exited in response to Ctrl+C (=SIGINT), then it'll also terminate the loop
# bash will report the exit status as 128+$signal, so 130 for SIGINT, but sys.exit(130) does not to the same thing - the value of 130 is simply bash's representation
# killing ourselves with the signal number that we are aborting in response to does all this correctly, and bash will see the right WIFSIGNALED() status of our program, not WIFEXITED()
# and finally, before we send ourselves the right signal, we must first restore the handler for it to the default
signal.signal(signo, signal.SIG_DFL)
os.kill(os.getpid(), signo)
def dispatch():
signal.signal(signal.SIGINT, sigint_handler)
main()