From 3c658ab833eb4c37a32c1dce814ab3d9ce9b5b06 Mon Sep 17 00:00:00 2001 From: Nick Santos Date: Wed, 27 Jul 2022 14:27:46 -0400 Subject: [PATCH] remove tilt_modules, which are now obsolete (#5906) Signed-off-by: Nick Santos --- .../tilt_modules/extensions.json | 9 - .../tilt_modules/restart_process/.gitignore | 1 - .../tilt_modules/restart_process/Dockerfile | 15 -- .../tilt_modules/restart_process/README.md | 181 ------------------ .../tilt_modules/restart_process/Tiltfile | 143 -------------- .../tilt_modules/restart_process/release.sh | 24 --- .../restart_process/test/Dockerfile | 5 - .../restart_process/test/Tiltfile | 5 - .../restart_process/test/custom.Tiltfile | 9 - .../tilt_modules/restart_process/test/fail.sh | 9 - .../restart_process/test/job.yaml | 12 -- .../restart_process/test/test-custom.sh | 21 -- .../restart_process/test/test-docker.sh | 27 --- .../tilt_modules/restart_process/test/test.sh | 7 - .../restart_process/tilt-restart-wrapper.go | 73 ------- .../tilt_modules/extensions.json | 9 - .../tilt_modules/restart_process/.gitignore | 1 - .../tilt_modules/restart_process/Dockerfile | 15 -- .../tilt_modules/restart_process/README.md | 181 ------------------ .../tilt_modules/restart_process/Tiltfile | 143 -------------- .../tilt_modules/restart_process/release.sh | 24 --- .../restart_process/test/Dockerfile | 5 - .../restart_process/test/Tiltfile | 5 - .../restart_process/test/custom.Tiltfile | 9 - .../tilt_modules/restart_process/test/fail.sh | 9 - .../restart_process/test/job.yaml | 12 -- .../restart_process/test/test-custom.sh | 21 -- .../restart_process/test/test-docker.sh | 27 --- .../tilt_modules/restart_process/test/test.sh | 7 - .../restart_process/tilt-restart-wrapper.go | 73 ------- .../tilt_modules/extensions.json | 9 - .../tilt_modules/restart_process/.gitignore | 1 - .../tilt_modules/restart_process/Dockerfile | 15 -- .../tilt_modules/restart_process/README.md | 181 ------------------ .../tilt_modules/restart_process/Tiltfile | 143 -------------- .../tilt_modules/restart_process/release.sh | 24 --- .../restart_process/test/Dockerfile | 5 - .../restart_process/test/Tiltfile | 5 - .../restart_process/test/custom.Tiltfile | 9 - .../tilt_modules/restart_process/test/fail.sh | 9 - .../restart_process/test/job.yaml | 12 -- .../restart_process/test/test-custom.sh | 21 -- .../restart_process/test/test-docker.sh | 27 --- .../tilt_modules/restart_process/test/test.sh | 7 - .../restart_process/tilt-restart-wrapper.go | 73 ------- web/tilt_modules/extensions.json | 9 - web/tilt_modules/uibutton/README.md | 180 ----------------- web/tilt_modules/uibutton/Tiltfile | 151 --------------- .../uibutton/assets/button_with_input.png | Bin 37299 -> 0 bytes web/tilt_modules/uibutton/test/Tiltfile | 17 -- web/tilt_modules/uibutton/test/goose.svg | 4 - web/tilt_modules/uibutton/test/test.sh | 7 - 52 files changed, 1991 deletions(-) delete mode 100644 integration/live_update_after_crash_rebuild/tilt_modules/extensions.json delete mode 100644 integration/live_update_after_crash_rebuild/tilt_modules/restart_process/.gitignore delete mode 100644 integration/live_update_after_crash_rebuild/tilt_modules/restart_process/Dockerfile delete mode 100644 integration/live_update_after_crash_rebuild/tilt_modules/restart_process/README.md delete mode 100644 integration/live_update_after_crash_rebuild/tilt_modules/restart_process/Tiltfile delete mode 100755 integration/live_update_after_crash_rebuild/tilt_modules/restart_process/release.sh delete mode 100644 integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/Dockerfile delete mode 100644 integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/Tiltfile delete mode 100644 integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/custom.Tiltfile delete mode 100755 integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/fail.sh delete mode 100644 integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/job.yaml delete mode 100755 integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/test-custom.sh delete mode 100755 integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/test-docker.sh delete mode 100755 integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/test.sh delete mode 100644 integration/live_update_after_crash_rebuild/tilt_modules/restart_process/tilt-restart-wrapper.go delete mode 100644 integration/restart_process_different_user/tilt_modules/extensions.json delete mode 100644 integration/restart_process_different_user/tilt_modules/restart_process/.gitignore delete mode 100644 integration/restart_process_different_user/tilt_modules/restart_process/Dockerfile delete mode 100644 integration/restart_process_different_user/tilt_modules/restart_process/README.md delete mode 100644 integration/restart_process_different_user/tilt_modules/restart_process/Tiltfile delete mode 100755 integration/restart_process_different_user/tilt_modules/restart_process/release.sh delete mode 100644 integration/restart_process_different_user/tilt_modules/restart_process/test/Dockerfile delete mode 100644 integration/restart_process_different_user/tilt_modules/restart_process/test/Tiltfile delete mode 100644 integration/restart_process_different_user/tilt_modules/restart_process/test/custom.Tiltfile delete mode 100755 integration/restart_process_different_user/tilt_modules/restart_process/test/fail.sh delete mode 100644 integration/restart_process_different_user/tilt_modules/restart_process/test/job.yaml delete mode 100755 integration/restart_process_different_user/tilt_modules/restart_process/test/test-custom.sh delete mode 100755 integration/restart_process_different_user/tilt_modules/restart_process/test/test-docker.sh delete mode 100755 integration/restart_process_different_user/tilt_modules/restart_process/test/test.sh delete mode 100644 integration/restart_process_different_user/tilt_modules/restart_process/tilt-restart-wrapper.go delete mode 100644 integration/same_img_multi_container/tilt_modules/extensions.json delete mode 100644 integration/same_img_multi_container/tilt_modules/restart_process/.gitignore delete mode 100644 integration/same_img_multi_container/tilt_modules/restart_process/Dockerfile delete mode 100644 integration/same_img_multi_container/tilt_modules/restart_process/README.md delete mode 100644 integration/same_img_multi_container/tilt_modules/restart_process/Tiltfile delete mode 100755 integration/same_img_multi_container/tilt_modules/restart_process/release.sh delete mode 100644 integration/same_img_multi_container/tilt_modules/restart_process/test/Dockerfile delete mode 100644 integration/same_img_multi_container/tilt_modules/restart_process/test/Tiltfile delete mode 100644 integration/same_img_multi_container/tilt_modules/restart_process/test/custom.Tiltfile delete mode 100755 integration/same_img_multi_container/tilt_modules/restart_process/test/fail.sh delete mode 100644 integration/same_img_multi_container/tilt_modules/restart_process/test/job.yaml delete mode 100755 integration/same_img_multi_container/tilt_modules/restart_process/test/test-custom.sh delete mode 100755 integration/same_img_multi_container/tilt_modules/restart_process/test/test-docker.sh delete mode 100755 integration/same_img_multi_container/tilt_modules/restart_process/test/test.sh delete mode 100644 integration/same_img_multi_container/tilt_modules/restart_process/tilt-restart-wrapper.go delete mode 100644 web/tilt_modules/extensions.json delete mode 100644 web/tilt_modules/uibutton/README.md delete mode 100644 web/tilt_modules/uibutton/Tiltfile delete mode 100644 web/tilt_modules/uibutton/assets/button_with_input.png delete mode 100644 web/tilt_modules/uibutton/test/Tiltfile delete mode 100644 web/tilt_modules/uibutton/test/goose.svg delete mode 100755 web/tilt_modules/uibutton/test/test.sh diff --git a/integration/live_update_after_crash_rebuild/tilt_modules/extensions.json b/integration/live_update_after_crash_rebuild/tilt_modules/extensions.json deleted file mode 100644 index 62d934aa22..0000000000 --- a/integration/live_update_after_crash_rebuild/tilt_modules/extensions.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Extensions": [ - { - "Name": "restart_process", - "ExtensionRegistry": "https://github.com/tilt-dev/tilt-extensions", - "TimeFetched": "2021-05-07T13:29:21.681511-04:00" - } - ] -} \ No newline at end of file diff --git a/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/.gitignore b/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/.gitignore deleted file mode 100644 index 3758a00fca..0000000000 --- a/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/.gitignore +++ /dev/null @@ -1 +0,0 @@ -tilt-restart-wrapper diff --git a/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/Dockerfile b/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/Dockerfile deleted file mode 100644 index 2ad4b08e0e..0000000000 --- a/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -FROM alpine/git - -RUN apk update && apk add make -RUN apk add build-base - -RUN git clone https://github.com/eradman/entr.git /entr -WORKDIR /entr -RUN git checkout c564e6bdca1dfe2177d1224363cad734158863ad -RUN ./configure; CFLAGS="-static" make install - -FROM scratch - -COPY --from=0 /usr/local/bin/entr / - -ADD tilt-restart-wrapper / diff --git a/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/README.md b/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/README.md deleted file mode 100644 index b47f82ef58..0000000000 --- a/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/README.md +++ /dev/null @@ -1,181 +0,0 @@ -# Restart Process - -This extension helps create images that can restart on `live_update`: - -- `docker_build_with_restart`: wraps a `docker_build` call -- `custom_build_with_restart`: wraps a `custom_build` call - -At the end of a `live_update`, the container's process will rerun itself. - -(Use it in place of the `restart_container()` Live Update step, which has been deprecated for Kubernetes resources.) - -## When to Use -Use this extension when you have an image and you want to re-execute its entrypoint/command as part of a `live_update`. - -E.g. if your app is a static binary, you'll probably need to re-execute the binary for any changes you made to take effect. - -(If your app has hot reloading capabilities--i.e. it can detect and incorporate changes to its source code without needing to restart--you probably don't need this extension.) - -### Unsupported Cases -This extension does NOT support process restarts for: -- Images built with `custom_build` using any of the `skips_local_docker`, `disable_push`, or `tag` parameters. -- Images run in Docker Compose resources (use the [`restart_container()`](https://docs.tilt.dev/api.html#api.restart_container) builtin instead) -- Images without a shell (e.g. `scratch`, `distroless`) -- Container commands specified as `command` in Kubernetes YAML will be overridden by this extension. - - However, the `args` field is still available; [reach out](https://tilt.dev/contact) if you need help navigating the interplay between Tilt and these YAML values -- CRDs - -If this extension doesn't work for your use case, [see our docs for alternatives](https://docs.tilt.dev/live_update_reference.html#restarting-your-process). - -Run into a bug? Need a use case that we don't yet support? Let us know--[open an issue](https://github.com/tilt-dev/tilt-extensions/issues) or [contact us](https://tilt.dev/contact). - -## How to Use - -Import this extension by putting the following at the top of your Tiltfile: -```python -load('ext://restart_process', 'docker_build_with_restart') -``` - -For the image that needs the process restart, replace your existing `docker_build` call: -```python -docker_build( - 'foo-image', - './foo', - arg1=val1, - arg2=val2, - live_update=[x, y, z...] -) -``` -with a `docker_build_with_restart` call: -```python -docker_build_with_restart( - 'foo-image', - './foo', - entrypoint='/go/bin/foo', - arg1=val1, - arg2=val2, - live_update=[x, y, z...] -) -``` -The call above looks just like the initial `docker_build` call except for one added parameter, `entrypoint` (in this example, `/go/bin/foo`). This is the command that you want to run on container start and _re-run_ on Live Update. - -A custom_build call looks similar: - -```python -load('ext://restart_process', 'custom_build_with_restart') - -custom_build_with_restart( - 'foo-image', - 'docker build -t $EXPECTED_REF ./foo', - deps=['./foo'], - live_update=[sync(...)] -) -``` - -### Troubleshooting -#### `failed running [touch /tmp/.restart-proc']` -If you see an error of the form: -``` -ERROR: Build Failed: ImageBuild: executor failed running [touch /tmp/.restart-proc']: exit code: 1 -``` -this often means that your Dockerfile user ([see docs](https://docs.docker.com/engine/reference/builder/#user)) doesn't have permission to write to the file we use to signal a process restart. Use the `restart_file` parameter to specify a file that your Dockerfile user definitely has write access to. - -### API -```python -def docker_build_with_restart(ref: str, context: str, - entrypoint: Union[str, List[str]], - live_update: List[LiveUpdateStep], - base_suffix: str = '-base', - restart_file: str = '/.restart-proc', - trigger: Union[str, List[str]] = [], - **kwargs -): - """Args: - ref: name for this image (e.g. 'myproj/backend' or 'myregistry/myproj/backend'); as the parameter of the same name in docker_build - context: path to use as the Docker build context; as the parameter of the same name in docker_build - entrypoint: the command to be (re-)executed when the container starts or when a live_update is run - live_update: set of steps for updating a running container; as the parameter of the same name in docker_build - base_suffix: suffix for naming the base image, applied as {ref}{base_suffix} - restart_file: file that Tilt will update during a live_update to signal the entrypoint to rerun - trigger: (optional) list of local paths. If specified, the process will ONLY be restarted when there are changes - to the given file(s); as the parameter of the same name in the LiveUpdate `run` step. - **kwargs: will be passed to the underlying `docker_build` call - """ - - -def custom_build_with_restart(ref: str, command: str, deps: List[str], entrypoint, - - entrypoint: Union[str, List[str]], - live_update: List[LiveUpdateStep], - base_suffix: str = '-base', - restart_file: str = '/.restart-proc', - trigger: Union[str, List[str]] = [], - , **kwargs -): - """ - Args: - ref: name for this image (e.g. 'myproj/backend' or 'myregistry/myproj/backend'); as the parameter of the same name in custom_build - command: build command for building your image - deps: source dependencies of the custom build - entrypoint: the command to be (re-)executed when the container starts or when a live_update is run - live_update: set of steps for updating a running container; as the parameter of the same name in custom_build - base_suffix: suffix for naming the base image, applied as {ref}{base_suffix} - restart_file: file that Tilt will update during a live_update to signal the entrypoint to rerun - trigger: (optional) list of local paths. If specified, the process will ONLY be restarted when there are changes - to the given file(s); as the parameter of the same name in the LiveUpdate `run` step. - **kwargs: will be passed to the underlying `custom_build` call - """ -``` - -## What's Happening Under the Hood -*If you're a casual user/just want to get your app running, you can stop reading now. However, if you want to dig deep and know exactly what's going on, or are trying to debug weird behavior, read on.* - -This extension wraps commands in `tilt-restart-wrapper`, which makes use of [`entr`](https://github.com/eradman/entr/) -to run arbitrary commands whenever a specified file changes. Specifically, we override the container's entrypoint with the following: - -``` -/tilt-restart-wrapper --watch_file='/.restart-proc' -``` - -This invocation says: -- when the container starts, run -- whenever the `/.restart-proc` file changes, re-execute - -We also set the following as the last `live_update` step: -```python -run('date > /.restart-proc') -``` - -Because `tilt-restart-wrapper` will re-execute the entrypoint whenever `/.restart-proc'` changes, the above `run` step will cause the entrypoint to re-run. - -#### Provide `tilt-restart-wrapper` -For this all to work, the `entr` binary must be available on the Docker image. The easiest solution would be to call e.g. `apt-get install entr` in the Dockerfile, but different base images will have different package managers; rather than grapple with that, we've made a statically linked binary available on Docker image: [`tiltdev/entr`](https://hub.docker.com/repository/docker/tiltdev/entr). - -To build `image-foo`, this extension will: -- build your image as normal (via `docker_build`, with all of your specified args/kwargs) but with the name `image-foo-base` -- build `image-foo` (the actual image that will be used in your resource) as a _child_ of `image-foo-base`, with the `tilt-process-wrapper` and its dependencies available - -Thus, the final image produced is tagged `image-foo` and has all the properties of your original `docker_build`, plus access to the `tilt-restart-wrapper` binary. - -#### Why a Wrapper? -Why bother with `tilt-restart-wrapper` rather than just calling `entr` directly? - -Because in its canonical invocation, `entr` requires that the file(s) to watch be piped via stdin, i.e. it is invoked like: -``` -echo "/.restart-proc" | entr -rz /bin/my-app -``` - -When specified as a `command` in Kubernetes or Docker Compose YAML (this is how Tilt overrides entrypoints), the above would therefore need to be executed as shell: -``` -/bin/sh -c 'echo "/.restart-proc" | entr -rz /bin/my-app' -``` -Any `args` specified in Kubernetes/Docker Compose are attached to the end of this call, and therefore in this case would apply TO THE `/bin/sh -c` CALL, rather than to the actual command run by `entr`; that is, any `args` specified by the user would be effectively ignored. - -In order to make `entr` usable without a shell, this extension uses [a simple binary](/restart_process/tilt-restart-wrapper.go) that invokes `entr` and writes to its stdin. - -Note: ideally `entr` could accept files-to-watch via flag instead of stdin, but (for a number of good reasons) this feature isn't likely to be added any time soon (see [entr#33](https://github.com/eradman/entr/issues/33)). - -## For Maintainers: Releasing -If you have push access to the `tiltdev` repository on DockerHub, you can release a new version of the binaries used by this extension like so: -1. run `release.sh` (builds `tilt-restart-wrapper` from source, builds and pushes a Docker image with the new binary and a fresh binary of `entr` also installed from source) -2. update the image tag in the [Tiltfile](/restart_process/Tiltfile) with the tag you just pushed (you'll find the image referenced in the Dockerfile contents of the child image--look for "FROM tiltdev/restart-helper") diff --git a/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/Tiltfile b/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/Tiltfile deleted file mode 100644 index 6a4138285c..0000000000 --- a/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/Tiltfile +++ /dev/null @@ -1,143 +0,0 @@ -RESTART_FILE = '/tmp/.restart-proc' -TYPE_RESTART_CONTAINER_STEP = 'live_update_restart_container_step' - -KWARGS_BLACKLIST = [ - # since we'll be passing `dockerfile_contents` when building the - # child image, remove any kwargs that might conflict - 'dockerfile', 'dockerfile_contents', - - # 'target' isn't relevant to our child build--if we pass this arg, - # Docker will just fail to find the specified stage and error out - 'target', -] - -# Arguments to custom_build that don't apply to the docker_build. -_CUSTOM_BUILD_KWARGS_BLACKLIST = [ - 'tag', - 'command_bat', - 'outputs_image_ref_to', - 'disable_push', -] - -_ext_dir = os.getcwd() - -# shared code between the two restart functions -def _helper(base_ref, ref, entrypoint, live_update, restart_file=RESTART_FILE, trigger=None, **kwargs): - if not trigger: - trigger = [] - - # declare a new docker build that adds a static binary of tilt-restart-wrapper - # (which makes use of `entr` to watch files and restart processes) to the user's image - df = ''' - FROM tiltdev/restart-helper:2020-10-16 as restart-helper - - FROM {} - RUN ["touch", "{}"] - COPY --from=restart-helper /tilt-restart-wrapper / - COPY --from=restart-helper /entr / - '''.format(base_ref, restart_file) - - # Change the entrypoint to use `tilt-restart-wrapper`. - # `tilt-restart-wrapper` makes use of `entr` (https://github.com/eradman/entr/) to - # re-execute $entrypoint whenever $restart_file changes - if type(entrypoint) == type(""): - entrypoint_with_entr = ["/tilt-restart-wrapper", "--watch_file={}".format(restart_file), "sh", "-c", entrypoint] - elif type(entrypoint) == type([]): - entrypoint_with_entr = ["/tilt-restart-wrapper", "--watch_file={}".format(restart_file)] + entrypoint - else: - fail("`entrypoint` must be a string or list of strings: got {}".format(type(entrypoint))) - - # last live_update step should always be to modify $restart_file, which - # triggers the process wrapper to rerun $entrypoint - # NB: write `date` instead of just `touch`ing because `entr` doesn't respond - # to timestamp changes, only writes (see https://github.com/eradman/entr/issues/32) - live_update = live_update + [run('date > {}'.format(restart_file), trigger=trigger)] - - # We don't need a real context. See: - # https://github.com/tilt-dev/tilt/issues/3897 - context = _ext_dir - - docker_build(ref, context, entrypoint=entrypoint_with_entr, dockerfile_contents=df, - live_update=live_update, **kwargs) - -def docker_build_with_restart(ref, context, entrypoint, live_update, - base_suffix='-tilt_docker_build_with_restart_base', restart_file=RESTART_FILE, - trigger=None, **kwargs): - """Wrap a docker_build call and its associated live_update steps so that the last step - of any live update is to rerun the given entrypoint. - - Args: - ref: name for this image (e.g. 'myproj/backend' or 'myregistry/myproj/backend'); as the parameter of the same name in docker_build - context: path to use as the Docker build context; as the parameter of the same name in docker_build - entrypoint: the command to be (re-)executed when the container starts or when a live_update is run - live_update: set of steps for updating a running container; as the parameter of the same name in docker_build - base_suffix: suffix for naming the base image, applied as {ref}{base_suffix} - restart_file: file that Tilt will update during a live_update to signal the entrypoint to rerun - trigger: (optional) list of local paths. If specified, the process will ONLY be restarted when there are changes - to the given file(s); as the parameter of the same name in the LiveUpdate `run` step. - **kwargs: will be passed to the underlying `docker_build` call - """ - - # first, validate the given live_update steps - if len(live_update) == 0: - fail("`docker_build_with_restart` requires at least one live_update step") - for step in live_update: - if type(step) == TYPE_RESTART_CONTAINER_STEP: - fail("`docker_build_with_restart` is not compatible with live_update step: " + - "`restart_container()` (this extension is meant to REPLACE restart_container() )") - - # rename the original image to make it a base image and declare a docker_build for it - base_ref = '{}{}'.format(ref, base_suffix) - docker_build(base_ref, context, **kwargs) - - # Clean kwargs for building the child image (which builds on user's specified - # image and copies in Tilt's restart wrapper). In practice, this means removing - # kwargs that were relevant to building the user's specified image but are NOT - # relevant to building the child image / may conflict with args we specifically - # pass for the child image. - cleaned_kwargs = {k: v for k, v in kwargs.items() if k not in KWARGS_BLACKLIST} - _helper(base_ref, ref, entrypoint, live_update, restart_file, trigger, **cleaned_kwargs) - - -def custom_build_with_restart(ref, command, deps, entrypoint, live_update, - base_suffix='-tilt_docker_build_with_restart_base', restart_file=RESTART_FILE, - trigger=None, **kwargs): - """Wrap a custom_build call and its associated live_update steps so that the last step - of any live update is to rerun the given entrypoint. - - Args: - ref: name for this image (e.g. 'myproj/backend' or 'myregistry/myproj/backend'); as the parameter of the same name in custom_build - command: build command for building your image - deps: source dependencies of the custom build - entrypoint: the command to be (re-)executed when the container starts or when a live_update is run - live_update: set of steps for updating a running container; as the parameter of the same name in custom_build - base_suffix: suffix for naming the base image, applied as {ref}{base_suffix} - restart_file: file that Tilt will update during a live_update to signal the entrypoint to rerun - trigger: (optional) list of local paths. If specified, the process will ONLY be restarted when there are changes - to the given file(s); as the parameter of the same name in the LiveUpdate `run` step. - **kwargs: will be passed to the underlying `custom_build` call - """ - - # first, validate the given live_update steps - if len(live_update) == 0: - fail("`custom_build_with_restart` requires at least one live_update step") - for step in live_update: - if type(step) == TYPE_RESTART_CONTAINER_STEP: - fail("`custom_build_with_restart` is not compatible with live_update step: "+ - "`restart_container()` (this extension is meant to REPLACE restart_container() )") - - for k, v in kwargs.items(): - if k == 'skips_local_docker': - fail("`custom_build_with_restart` is not compatible with `skips_local_docker`, because it needs access to the image") - if k == 'disable_push': - fail("`custom_build_with_restart` is not compatible with `disable_push`") - if k == 'tag': - fail("`custom_build_with_restart` renames your base image, so is not compatible with `tag`") - - # rename the original image to make it a base image and declare a custom_build for it - base_ref = '{}{}'.format(ref, base_suffix) - custom_build(base_ref, command=command, deps=deps, **kwargs) - - # A few arguments aren't applicable to the docker_build, so remove them. - cleaned_kwargs = {k: v for k, v in kwargs.items() if k not in _CUSTOM_BUILD_KWARGS_BLACKLIST} - _helper(base_ref, ref, entrypoint, live_update, restart_file, trigger, **cleaned_kwargs) diff --git a/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/release.sh b/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/release.sh deleted file mode 100755 index f342acf03b..0000000000 --- a/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/release.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -set -ex - -TIMESTAMP=$(date +'%Y-%m-%d') -IMAGE_NAME='tiltdev/restart-helper' -IMAGE_WITH_TAG=$IMAGE_NAME:$TIMESTAMP - -# build binary for tilt-restart-wrapper -env GOOS=linux GOARCH=amd64 go build tilt-restart-wrapper.go - -# build Docker image with static binaries of: -# - tilt-restart-wrapper (compiled above) -# - entr (dependency of tilt-restart-wrapper) -docker build . -t $IMAGE_NAME -docker push $IMAGE_NAME - -docker tag $IMAGE_NAME $IMAGE_WITH_TAG -docker push $IMAGE_WITH_TAG - -echo "Successfully built and pushed $IMAGE_WITH_TAG" - - - diff --git a/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/Dockerfile b/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/Dockerfile deleted file mode 100644 index 95934f1b4d..0000000000 --- a/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/Dockerfile +++ /dev/null @@ -1,5 +0,0 @@ -FROM busybox - -COPY fail.sh / - -ENTRYPOINT /fail.sh diff --git a/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/Tiltfile b/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/Tiltfile deleted file mode 100644 index 5a943f9305..0000000000 --- a/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/Tiltfile +++ /dev/null @@ -1,5 +0,0 @@ -load('../Tiltfile', 'docker_build_with_restart') - -k8s_yaml('job.yaml') -docker_build_with_restart('failing_job', '.', '/fail.sh', - live_update=[sync('./fail.sh', '/fail.sh')]) diff --git a/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/custom.Tiltfile b/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/custom.Tiltfile deleted file mode 100644 index 692a9bcedb..0000000000 --- a/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/custom.Tiltfile +++ /dev/null @@ -1,9 +0,0 @@ -load('../Tiltfile', 'custom_build_with_restart') - -k8s_yaml('job.yaml') -custom_build_with_restart( - 'failing_job', - command='docker build -t $EXPECTED_REF .', - deps=['fail.sh'], - entrypoint='/fail.sh', - live_update=[sync('./fail.sh', '/fail.sh')]) diff --git a/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/fail.sh b/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/fail.sh deleted file mode 100755 index 1e51f5e0a2..0000000000 --- a/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/fail.sh +++ /dev/null @@ -1,9 +0,0 @@ -#! /bin/sh - -echo "Are you there, pod?" -sleep 1 - -# Exit with a non-zero status code; we check that docker_build_with_restart -# surfaces this error code to k8s, so k8s knows that the job failed. -echo "Exiting with status code 123 😱" -exit 123 diff --git a/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/job.yaml b/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/job.yaml deleted file mode 100644 index 09e5445b6e..0000000000 --- a/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/job.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - name: failing-job -spec: - template: - spec: - containers: - - name: failing-job - image: failing_job - restartPolicy: Never - backoffLimit: 4 diff --git a/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/test-custom.sh b/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/test-custom.sh deleted file mode 100755 index de1f832b1d..0000000000 --- a/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/test-custom.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -cd $(dirname $0) - -set -x -tilt ci -f custom.Tiltfile > tilt.log 2>&1 -CI_EXIT=$? - -tilt down - -if [ $CI_EXIT -eq 0 ]; then - echo "Expected 'tilt ci' to fail, but succeeded." - exit 1 -fi - -cat tilt.log | grep -q "Are you there, pod?" -GREP_EXIT=$? - -rm tilt.log - -exit $GREP_EXIT diff --git a/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/test-docker.sh b/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/test-docker.sh deleted file mode 100755 index 66509fbf3d..0000000000 --- a/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/test-docker.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash - -# Test case for https://github.com/tilt-dev/tilt-extensions/issues/92 -# -# This job will always exit with a non-zero status code; make sure -# that docker_build_with_restart surfaces this error code to k8s, -# so k8s knows that the job failed. (Thus, we expect the `tilt ci` -# call to fail.) -cd $(dirname $0) - -set -x -tilt ci > tilt.log 2>&1 -CI_EXIT=$? - -tilt down - -if [ $CI_EXIT -eq 0 ]; then - echo "Expected 'tilt ci' to fail, but succeeded." - exit 1 -fi - -cat tilt.log | grep -q "Are you there, pod?" -GREP_EXIT=$? - -rm tilt.log - -exit $GREP_EXIT diff --git a/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/test.sh b/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/test.sh deleted file mode 100755 index 47141f20e8..0000000000 --- a/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/test/test.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -set -ex - -cd $(dirname $0) -./test-docker.sh -./test-custom.sh diff --git a/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/tilt-restart-wrapper.go b/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/tilt-restart-wrapper.go deleted file mode 100644 index 12d5a9fe9e..0000000000 --- a/integration/live_update_after_crash_rebuild/tilt_modules/restart_process/tilt-restart-wrapper.go +++ /dev/null @@ -1,73 +0,0 @@ -// `tilt-restart-wrapper` wraps `entr` (http://eradman.com/entrproject/) to easily -// rerun a user-specified command when a given file changes. -// -// This is Tilt's recommended way of restarting a process as part of a live_update: -// if your container invokes your app via the restart wrapper (e.g. `tilt-restart-wrapper /bin/my-app`), -// you can trigger re-execution of your app with a live_update `run` step that makes -// a trivial change to the file watched by `entr` (e.g. `run('date > /.restart-proc')`) -// -// This script exists (i.e. we're wrapping `entr` in a binary instead of invoking -// it directly) because in its canonical invocation, `entr` requires that the -// file(s) to watch be piped via stdin, i.e. it is invoked like: -// echo "/.restart-proc" | entr -rz /bin/my-app -// -// When specified as a `command` in Kubernetes or Docker Compose YAML (this is how -// Tilt overrides entrypoints), the above would therefore need to be executed as shell: -// /bin/sh -c 'echo "/.restart-proc" | entr -rz /bin/my-app' - -// Any args specified in Kubernetes or Docker Compose YAML are attached to the end -// of this call, and therefore in this case apply TO THE `/bin/sh -c` CALL, rather -// than to the actual command run by `entr`; that is, any `args` specified by the -// user would be effectively ignored. -// -// In order to make `entr` executable as ARGV rather than as shell, we have wrapped it -// in a binary that can be called directly and takes care of the piping under the hood. -// -// Note: ideally `entr` could accept files-to-watch via flag instead of stdin, -// but (for a number of good reasons) this feature isn't likely to be added any -// time soon (see https://github.com/eradman/entr/issues/33). - -package main - -import ( - "flag" - "fmt" - "log" - "os" - "os/exec" - "strings" - "syscall" -) - -var watchFile = flag.String("watch_file", "/.restart-proc", "File that entr will watch for changes; changes to this file trigger `entr` to rerun the command(s) passed") -var entrPath = flag.String("entr_path", "/entr", "Path to `entr` executable") - -func main() { - flag.Parse() - - cmd := exec.Command(*entrPath, "-rz") - cmd.Stdin = strings.NewReader(fmt.Sprintf("%s\n", *watchFile)) - cmd.Args = append(cmd.Args, flag.Args()...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - if err := cmd.Run(); err != nil { - if exiterr, ok := err.(*exec.ExitError); ok { - // The program has exited with an exit code != 0 - if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { - if len(flag.Args()) == 0 { - log.Println("`tilt-restart-wrapper` requires at least one positional arg " + - "(a command or set of args to be executed / rerun whenever `watch_file` changes)") - } - os.Exit(status.ExitStatus()) - } - } else { - log.Fatalf("error running command: %v", err) - } - } - - if len(flag.Args()) == 0 { - log.Fatal("`tilt-restart-wrapper` requires at least one positional arg " + - "(will be passed to `entr` and executed / rerun whenever `watch_file` changes)") - } -} diff --git a/integration/restart_process_different_user/tilt_modules/extensions.json b/integration/restart_process_different_user/tilt_modules/extensions.json deleted file mode 100644 index ea3227de24..0000000000 --- a/integration/restart_process_different_user/tilt_modules/extensions.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Extensions": [ - { - "Name": "restart_process", - "ExtensionRegistry": "https://github.com/tilt-dev/tilt-extensions", - "TimeFetched": "2021-05-07T13:32:22.639113-04:00" - } - ] -} \ No newline at end of file diff --git a/integration/restart_process_different_user/tilt_modules/restart_process/.gitignore b/integration/restart_process_different_user/tilt_modules/restart_process/.gitignore deleted file mode 100644 index 3758a00fca..0000000000 --- a/integration/restart_process_different_user/tilt_modules/restart_process/.gitignore +++ /dev/null @@ -1 +0,0 @@ -tilt-restart-wrapper diff --git a/integration/restart_process_different_user/tilt_modules/restart_process/Dockerfile b/integration/restart_process_different_user/tilt_modules/restart_process/Dockerfile deleted file mode 100644 index 2ad4b08e0e..0000000000 --- a/integration/restart_process_different_user/tilt_modules/restart_process/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -FROM alpine/git - -RUN apk update && apk add make -RUN apk add build-base - -RUN git clone https://github.com/eradman/entr.git /entr -WORKDIR /entr -RUN git checkout c564e6bdca1dfe2177d1224363cad734158863ad -RUN ./configure; CFLAGS="-static" make install - -FROM scratch - -COPY --from=0 /usr/local/bin/entr / - -ADD tilt-restart-wrapper / diff --git a/integration/restart_process_different_user/tilt_modules/restart_process/README.md b/integration/restart_process_different_user/tilt_modules/restart_process/README.md deleted file mode 100644 index b47f82ef58..0000000000 --- a/integration/restart_process_different_user/tilt_modules/restart_process/README.md +++ /dev/null @@ -1,181 +0,0 @@ -# Restart Process - -This extension helps create images that can restart on `live_update`: - -- `docker_build_with_restart`: wraps a `docker_build` call -- `custom_build_with_restart`: wraps a `custom_build` call - -At the end of a `live_update`, the container's process will rerun itself. - -(Use it in place of the `restart_container()` Live Update step, which has been deprecated for Kubernetes resources.) - -## When to Use -Use this extension when you have an image and you want to re-execute its entrypoint/command as part of a `live_update`. - -E.g. if your app is a static binary, you'll probably need to re-execute the binary for any changes you made to take effect. - -(If your app has hot reloading capabilities--i.e. it can detect and incorporate changes to its source code without needing to restart--you probably don't need this extension.) - -### Unsupported Cases -This extension does NOT support process restarts for: -- Images built with `custom_build` using any of the `skips_local_docker`, `disable_push`, or `tag` parameters. -- Images run in Docker Compose resources (use the [`restart_container()`](https://docs.tilt.dev/api.html#api.restart_container) builtin instead) -- Images without a shell (e.g. `scratch`, `distroless`) -- Container commands specified as `command` in Kubernetes YAML will be overridden by this extension. - - However, the `args` field is still available; [reach out](https://tilt.dev/contact) if you need help navigating the interplay between Tilt and these YAML values -- CRDs - -If this extension doesn't work for your use case, [see our docs for alternatives](https://docs.tilt.dev/live_update_reference.html#restarting-your-process). - -Run into a bug? Need a use case that we don't yet support? Let us know--[open an issue](https://github.com/tilt-dev/tilt-extensions/issues) or [contact us](https://tilt.dev/contact). - -## How to Use - -Import this extension by putting the following at the top of your Tiltfile: -```python -load('ext://restart_process', 'docker_build_with_restart') -``` - -For the image that needs the process restart, replace your existing `docker_build` call: -```python -docker_build( - 'foo-image', - './foo', - arg1=val1, - arg2=val2, - live_update=[x, y, z...] -) -``` -with a `docker_build_with_restart` call: -```python -docker_build_with_restart( - 'foo-image', - './foo', - entrypoint='/go/bin/foo', - arg1=val1, - arg2=val2, - live_update=[x, y, z...] -) -``` -The call above looks just like the initial `docker_build` call except for one added parameter, `entrypoint` (in this example, `/go/bin/foo`). This is the command that you want to run on container start and _re-run_ on Live Update. - -A custom_build call looks similar: - -```python -load('ext://restart_process', 'custom_build_with_restart') - -custom_build_with_restart( - 'foo-image', - 'docker build -t $EXPECTED_REF ./foo', - deps=['./foo'], - live_update=[sync(...)] -) -``` - -### Troubleshooting -#### `failed running [touch /tmp/.restart-proc']` -If you see an error of the form: -``` -ERROR: Build Failed: ImageBuild: executor failed running [touch /tmp/.restart-proc']: exit code: 1 -``` -this often means that your Dockerfile user ([see docs](https://docs.docker.com/engine/reference/builder/#user)) doesn't have permission to write to the file we use to signal a process restart. Use the `restart_file` parameter to specify a file that your Dockerfile user definitely has write access to. - -### API -```python -def docker_build_with_restart(ref: str, context: str, - entrypoint: Union[str, List[str]], - live_update: List[LiveUpdateStep], - base_suffix: str = '-base', - restart_file: str = '/.restart-proc', - trigger: Union[str, List[str]] = [], - **kwargs -): - """Args: - ref: name for this image (e.g. 'myproj/backend' or 'myregistry/myproj/backend'); as the parameter of the same name in docker_build - context: path to use as the Docker build context; as the parameter of the same name in docker_build - entrypoint: the command to be (re-)executed when the container starts or when a live_update is run - live_update: set of steps for updating a running container; as the parameter of the same name in docker_build - base_suffix: suffix for naming the base image, applied as {ref}{base_suffix} - restart_file: file that Tilt will update during a live_update to signal the entrypoint to rerun - trigger: (optional) list of local paths. If specified, the process will ONLY be restarted when there are changes - to the given file(s); as the parameter of the same name in the LiveUpdate `run` step. - **kwargs: will be passed to the underlying `docker_build` call - """ - - -def custom_build_with_restart(ref: str, command: str, deps: List[str], entrypoint, - - entrypoint: Union[str, List[str]], - live_update: List[LiveUpdateStep], - base_suffix: str = '-base', - restart_file: str = '/.restart-proc', - trigger: Union[str, List[str]] = [], - , **kwargs -): - """ - Args: - ref: name for this image (e.g. 'myproj/backend' or 'myregistry/myproj/backend'); as the parameter of the same name in custom_build - command: build command for building your image - deps: source dependencies of the custom build - entrypoint: the command to be (re-)executed when the container starts or when a live_update is run - live_update: set of steps for updating a running container; as the parameter of the same name in custom_build - base_suffix: suffix for naming the base image, applied as {ref}{base_suffix} - restart_file: file that Tilt will update during a live_update to signal the entrypoint to rerun - trigger: (optional) list of local paths. If specified, the process will ONLY be restarted when there are changes - to the given file(s); as the parameter of the same name in the LiveUpdate `run` step. - **kwargs: will be passed to the underlying `custom_build` call - """ -``` - -## What's Happening Under the Hood -*If you're a casual user/just want to get your app running, you can stop reading now. However, if you want to dig deep and know exactly what's going on, or are trying to debug weird behavior, read on.* - -This extension wraps commands in `tilt-restart-wrapper`, which makes use of [`entr`](https://github.com/eradman/entr/) -to run arbitrary commands whenever a specified file changes. Specifically, we override the container's entrypoint with the following: - -``` -/tilt-restart-wrapper --watch_file='/.restart-proc' -``` - -This invocation says: -- when the container starts, run -- whenever the `/.restart-proc` file changes, re-execute - -We also set the following as the last `live_update` step: -```python -run('date > /.restart-proc') -``` - -Because `tilt-restart-wrapper` will re-execute the entrypoint whenever `/.restart-proc'` changes, the above `run` step will cause the entrypoint to re-run. - -#### Provide `tilt-restart-wrapper` -For this all to work, the `entr` binary must be available on the Docker image. The easiest solution would be to call e.g. `apt-get install entr` in the Dockerfile, but different base images will have different package managers; rather than grapple with that, we've made a statically linked binary available on Docker image: [`tiltdev/entr`](https://hub.docker.com/repository/docker/tiltdev/entr). - -To build `image-foo`, this extension will: -- build your image as normal (via `docker_build`, with all of your specified args/kwargs) but with the name `image-foo-base` -- build `image-foo` (the actual image that will be used in your resource) as a _child_ of `image-foo-base`, with the `tilt-process-wrapper` and its dependencies available - -Thus, the final image produced is tagged `image-foo` and has all the properties of your original `docker_build`, plus access to the `tilt-restart-wrapper` binary. - -#### Why a Wrapper? -Why bother with `tilt-restart-wrapper` rather than just calling `entr` directly? - -Because in its canonical invocation, `entr` requires that the file(s) to watch be piped via stdin, i.e. it is invoked like: -``` -echo "/.restart-proc" | entr -rz /bin/my-app -``` - -When specified as a `command` in Kubernetes or Docker Compose YAML (this is how Tilt overrides entrypoints), the above would therefore need to be executed as shell: -``` -/bin/sh -c 'echo "/.restart-proc" | entr -rz /bin/my-app' -``` -Any `args` specified in Kubernetes/Docker Compose are attached to the end of this call, and therefore in this case would apply TO THE `/bin/sh -c` CALL, rather than to the actual command run by `entr`; that is, any `args` specified by the user would be effectively ignored. - -In order to make `entr` usable without a shell, this extension uses [a simple binary](/restart_process/tilt-restart-wrapper.go) that invokes `entr` and writes to its stdin. - -Note: ideally `entr` could accept files-to-watch via flag instead of stdin, but (for a number of good reasons) this feature isn't likely to be added any time soon (see [entr#33](https://github.com/eradman/entr/issues/33)). - -## For Maintainers: Releasing -If you have push access to the `tiltdev` repository on DockerHub, you can release a new version of the binaries used by this extension like so: -1. run `release.sh` (builds `tilt-restart-wrapper` from source, builds and pushes a Docker image with the new binary and a fresh binary of `entr` also installed from source) -2. update the image tag in the [Tiltfile](/restart_process/Tiltfile) with the tag you just pushed (you'll find the image referenced in the Dockerfile contents of the child image--look for "FROM tiltdev/restart-helper") diff --git a/integration/restart_process_different_user/tilt_modules/restart_process/Tiltfile b/integration/restart_process_different_user/tilt_modules/restart_process/Tiltfile deleted file mode 100644 index 6a4138285c..0000000000 --- a/integration/restart_process_different_user/tilt_modules/restart_process/Tiltfile +++ /dev/null @@ -1,143 +0,0 @@ -RESTART_FILE = '/tmp/.restart-proc' -TYPE_RESTART_CONTAINER_STEP = 'live_update_restart_container_step' - -KWARGS_BLACKLIST = [ - # since we'll be passing `dockerfile_contents` when building the - # child image, remove any kwargs that might conflict - 'dockerfile', 'dockerfile_contents', - - # 'target' isn't relevant to our child build--if we pass this arg, - # Docker will just fail to find the specified stage and error out - 'target', -] - -# Arguments to custom_build that don't apply to the docker_build. -_CUSTOM_BUILD_KWARGS_BLACKLIST = [ - 'tag', - 'command_bat', - 'outputs_image_ref_to', - 'disable_push', -] - -_ext_dir = os.getcwd() - -# shared code between the two restart functions -def _helper(base_ref, ref, entrypoint, live_update, restart_file=RESTART_FILE, trigger=None, **kwargs): - if not trigger: - trigger = [] - - # declare a new docker build that adds a static binary of tilt-restart-wrapper - # (which makes use of `entr` to watch files and restart processes) to the user's image - df = ''' - FROM tiltdev/restart-helper:2020-10-16 as restart-helper - - FROM {} - RUN ["touch", "{}"] - COPY --from=restart-helper /tilt-restart-wrapper / - COPY --from=restart-helper /entr / - '''.format(base_ref, restart_file) - - # Change the entrypoint to use `tilt-restart-wrapper`. - # `tilt-restart-wrapper` makes use of `entr` (https://github.com/eradman/entr/) to - # re-execute $entrypoint whenever $restart_file changes - if type(entrypoint) == type(""): - entrypoint_with_entr = ["/tilt-restart-wrapper", "--watch_file={}".format(restart_file), "sh", "-c", entrypoint] - elif type(entrypoint) == type([]): - entrypoint_with_entr = ["/tilt-restart-wrapper", "--watch_file={}".format(restart_file)] + entrypoint - else: - fail("`entrypoint` must be a string or list of strings: got {}".format(type(entrypoint))) - - # last live_update step should always be to modify $restart_file, which - # triggers the process wrapper to rerun $entrypoint - # NB: write `date` instead of just `touch`ing because `entr` doesn't respond - # to timestamp changes, only writes (see https://github.com/eradman/entr/issues/32) - live_update = live_update + [run('date > {}'.format(restart_file), trigger=trigger)] - - # We don't need a real context. See: - # https://github.com/tilt-dev/tilt/issues/3897 - context = _ext_dir - - docker_build(ref, context, entrypoint=entrypoint_with_entr, dockerfile_contents=df, - live_update=live_update, **kwargs) - -def docker_build_with_restart(ref, context, entrypoint, live_update, - base_suffix='-tilt_docker_build_with_restart_base', restart_file=RESTART_FILE, - trigger=None, **kwargs): - """Wrap a docker_build call and its associated live_update steps so that the last step - of any live update is to rerun the given entrypoint. - - Args: - ref: name for this image (e.g. 'myproj/backend' or 'myregistry/myproj/backend'); as the parameter of the same name in docker_build - context: path to use as the Docker build context; as the parameter of the same name in docker_build - entrypoint: the command to be (re-)executed when the container starts or when a live_update is run - live_update: set of steps for updating a running container; as the parameter of the same name in docker_build - base_suffix: suffix for naming the base image, applied as {ref}{base_suffix} - restart_file: file that Tilt will update during a live_update to signal the entrypoint to rerun - trigger: (optional) list of local paths. If specified, the process will ONLY be restarted when there are changes - to the given file(s); as the parameter of the same name in the LiveUpdate `run` step. - **kwargs: will be passed to the underlying `docker_build` call - """ - - # first, validate the given live_update steps - if len(live_update) == 0: - fail("`docker_build_with_restart` requires at least one live_update step") - for step in live_update: - if type(step) == TYPE_RESTART_CONTAINER_STEP: - fail("`docker_build_with_restart` is not compatible with live_update step: " + - "`restart_container()` (this extension is meant to REPLACE restart_container() )") - - # rename the original image to make it a base image and declare a docker_build for it - base_ref = '{}{}'.format(ref, base_suffix) - docker_build(base_ref, context, **kwargs) - - # Clean kwargs for building the child image (which builds on user's specified - # image and copies in Tilt's restart wrapper). In practice, this means removing - # kwargs that were relevant to building the user's specified image but are NOT - # relevant to building the child image / may conflict with args we specifically - # pass for the child image. - cleaned_kwargs = {k: v for k, v in kwargs.items() if k not in KWARGS_BLACKLIST} - _helper(base_ref, ref, entrypoint, live_update, restart_file, trigger, **cleaned_kwargs) - - -def custom_build_with_restart(ref, command, deps, entrypoint, live_update, - base_suffix='-tilt_docker_build_with_restart_base', restart_file=RESTART_FILE, - trigger=None, **kwargs): - """Wrap a custom_build call and its associated live_update steps so that the last step - of any live update is to rerun the given entrypoint. - - Args: - ref: name for this image (e.g. 'myproj/backend' or 'myregistry/myproj/backend'); as the parameter of the same name in custom_build - command: build command for building your image - deps: source dependencies of the custom build - entrypoint: the command to be (re-)executed when the container starts or when a live_update is run - live_update: set of steps for updating a running container; as the parameter of the same name in custom_build - base_suffix: suffix for naming the base image, applied as {ref}{base_suffix} - restart_file: file that Tilt will update during a live_update to signal the entrypoint to rerun - trigger: (optional) list of local paths. If specified, the process will ONLY be restarted when there are changes - to the given file(s); as the parameter of the same name in the LiveUpdate `run` step. - **kwargs: will be passed to the underlying `custom_build` call - """ - - # first, validate the given live_update steps - if len(live_update) == 0: - fail("`custom_build_with_restart` requires at least one live_update step") - for step in live_update: - if type(step) == TYPE_RESTART_CONTAINER_STEP: - fail("`custom_build_with_restart` is not compatible with live_update step: "+ - "`restart_container()` (this extension is meant to REPLACE restart_container() )") - - for k, v in kwargs.items(): - if k == 'skips_local_docker': - fail("`custom_build_with_restart` is not compatible with `skips_local_docker`, because it needs access to the image") - if k == 'disable_push': - fail("`custom_build_with_restart` is not compatible with `disable_push`") - if k == 'tag': - fail("`custom_build_with_restart` renames your base image, so is not compatible with `tag`") - - # rename the original image to make it a base image and declare a custom_build for it - base_ref = '{}{}'.format(ref, base_suffix) - custom_build(base_ref, command=command, deps=deps, **kwargs) - - # A few arguments aren't applicable to the docker_build, so remove them. - cleaned_kwargs = {k: v for k, v in kwargs.items() if k not in _CUSTOM_BUILD_KWARGS_BLACKLIST} - _helper(base_ref, ref, entrypoint, live_update, restart_file, trigger, **cleaned_kwargs) diff --git a/integration/restart_process_different_user/tilt_modules/restart_process/release.sh b/integration/restart_process_different_user/tilt_modules/restart_process/release.sh deleted file mode 100755 index f342acf03b..0000000000 --- a/integration/restart_process_different_user/tilt_modules/restart_process/release.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -set -ex - -TIMESTAMP=$(date +'%Y-%m-%d') -IMAGE_NAME='tiltdev/restart-helper' -IMAGE_WITH_TAG=$IMAGE_NAME:$TIMESTAMP - -# build binary for tilt-restart-wrapper -env GOOS=linux GOARCH=amd64 go build tilt-restart-wrapper.go - -# build Docker image with static binaries of: -# - tilt-restart-wrapper (compiled above) -# - entr (dependency of tilt-restart-wrapper) -docker build . -t $IMAGE_NAME -docker push $IMAGE_NAME - -docker tag $IMAGE_NAME $IMAGE_WITH_TAG -docker push $IMAGE_WITH_TAG - -echo "Successfully built and pushed $IMAGE_WITH_TAG" - - - diff --git a/integration/restart_process_different_user/tilt_modules/restart_process/test/Dockerfile b/integration/restart_process_different_user/tilt_modules/restart_process/test/Dockerfile deleted file mode 100644 index 95934f1b4d..0000000000 --- a/integration/restart_process_different_user/tilt_modules/restart_process/test/Dockerfile +++ /dev/null @@ -1,5 +0,0 @@ -FROM busybox - -COPY fail.sh / - -ENTRYPOINT /fail.sh diff --git a/integration/restart_process_different_user/tilt_modules/restart_process/test/Tiltfile b/integration/restart_process_different_user/tilt_modules/restart_process/test/Tiltfile deleted file mode 100644 index 5a943f9305..0000000000 --- a/integration/restart_process_different_user/tilt_modules/restart_process/test/Tiltfile +++ /dev/null @@ -1,5 +0,0 @@ -load('../Tiltfile', 'docker_build_with_restart') - -k8s_yaml('job.yaml') -docker_build_with_restart('failing_job', '.', '/fail.sh', - live_update=[sync('./fail.sh', '/fail.sh')]) diff --git a/integration/restart_process_different_user/tilt_modules/restart_process/test/custom.Tiltfile b/integration/restart_process_different_user/tilt_modules/restart_process/test/custom.Tiltfile deleted file mode 100644 index 692a9bcedb..0000000000 --- a/integration/restart_process_different_user/tilt_modules/restart_process/test/custom.Tiltfile +++ /dev/null @@ -1,9 +0,0 @@ -load('../Tiltfile', 'custom_build_with_restart') - -k8s_yaml('job.yaml') -custom_build_with_restart( - 'failing_job', - command='docker build -t $EXPECTED_REF .', - deps=['fail.sh'], - entrypoint='/fail.sh', - live_update=[sync('./fail.sh', '/fail.sh')]) diff --git a/integration/restart_process_different_user/tilt_modules/restart_process/test/fail.sh b/integration/restart_process_different_user/tilt_modules/restart_process/test/fail.sh deleted file mode 100755 index 1e51f5e0a2..0000000000 --- a/integration/restart_process_different_user/tilt_modules/restart_process/test/fail.sh +++ /dev/null @@ -1,9 +0,0 @@ -#! /bin/sh - -echo "Are you there, pod?" -sleep 1 - -# Exit with a non-zero status code; we check that docker_build_with_restart -# surfaces this error code to k8s, so k8s knows that the job failed. -echo "Exiting with status code 123 😱" -exit 123 diff --git a/integration/restart_process_different_user/tilt_modules/restart_process/test/job.yaml b/integration/restart_process_different_user/tilt_modules/restart_process/test/job.yaml deleted file mode 100644 index 09e5445b6e..0000000000 --- a/integration/restart_process_different_user/tilt_modules/restart_process/test/job.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - name: failing-job -spec: - template: - spec: - containers: - - name: failing-job - image: failing_job - restartPolicy: Never - backoffLimit: 4 diff --git a/integration/restart_process_different_user/tilt_modules/restart_process/test/test-custom.sh b/integration/restart_process_different_user/tilt_modules/restart_process/test/test-custom.sh deleted file mode 100755 index de1f832b1d..0000000000 --- a/integration/restart_process_different_user/tilt_modules/restart_process/test/test-custom.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -cd $(dirname $0) - -set -x -tilt ci -f custom.Tiltfile > tilt.log 2>&1 -CI_EXIT=$? - -tilt down - -if [ $CI_EXIT -eq 0 ]; then - echo "Expected 'tilt ci' to fail, but succeeded." - exit 1 -fi - -cat tilt.log | grep -q "Are you there, pod?" -GREP_EXIT=$? - -rm tilt.log - -exit $GREP_EXIT diff --git a/integration/restart_process_different_user/tilt_modules/restart_process/test/test-docker.sh b/integration/restart_process_different_user/tilt_modules/restart_process/test/test-docker.sh deleted file mode 100755 index 66509fbf3d..0000000000 --- a/integration/restart_process_different_user/tilt_modules/restart_process/test/test-docker.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash - -# Test case for https://github.com/tilt-dev/tilt-extensions/issues/92 -# -# This job will always exit with a non-zero status code; make sure -# that docker_build_with_restart surfaces this error code to k8s, -# so k8s knows that the job failed. (Thus, we expect the `tilt ci` -# call to fail.) -cd $(dirname $0) - -set -x -tilt ci > tilt.log 2>&1 -CI_EXIT=$? - -tilt down - -if [ $CI_EXIT -eq 0 ]; then - echo "Expected 'tilt ci' to fail, but succeeded." - exit 1 -fi - -cat tilt.log | grep -q "Are you there, pod?" -GREP_EXIT=$? - -rm tilt.log - -exit $GREP_EXIT diff --git a/integration/restart_process_different_user/tilt_modules/restart_process/test/test.sh b/integration/restart_process_different_user/tilt_modules/restart_process/test/test.sh deleted file mode 100755 index 47141f20e8..0000000000 --- a/integration/restart_process_different_user/tilt_modules/restart_process/test/test.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -set -ex - -cd $(dirname $0) -./test-docker.sh -./test-custom.sh diff --git a/integration/restart_process_different_user/tilt_modules/restart_process/tilt-restart-wrapper.go b/integration/restart_process_different_user/tilt_modules/restart_process/tilt-restart-wrapper.go deleted file mode 100644 index 12d5a9fe9e..0000000000 --- a/integration/restart_process_different_user/tilt_modules/restart_process/tilt-restart-wrapper.go +++ /dev/null @@ -1,73 +0,0 @@ -// `tilt-restart-wrapper` wraps `entr` (http://eradman.com/entrproject/) to easily -// rerun a user-specified command when a given file changes. -// -// This is Tilt's recommended way of restarting a process as part of a live_update: -// if your container invokes your app via the restart wrapper (e.g. `tilt-restart-wrapper /bin/my-app`), -// you can trigger re-execution of your app with a live_update `run` step that makes -// a trivial change to the file watched by `entr` (e.g. `run('date > /.restart-proc')`) -// -// This script exists (i.e. we're wrapping `entr` in a binary instead of invoking -// it directly) because in its canonical invocation, `entr` requires that the -// file(s) to watch be piped via stdin, i.e. it is invoked like: -// echo "/.restart-proc" | entr -rz /bin/my-app -// -// When specified as a `command` in Kubernetes or Docker Compose YAML (this is how -// Tilt overrides entrypoints), the above would therefore need to be executed as shell: -// /bin/sh -c 'echo "/.restart-proc" | entr -rz /bin/my-app' - -// Any args specified in Kubernetes or Docker Compose YAML are attached to the end -// of this call, and therefore in this case apply TO THE `/bin/sh -c` CALL, rather -// than to the actual command run by `entr`; that is, any `args` specified by the -// user would be effectively ignored. -// -// In order to make `entr` executable as ARGV rather than as shell, we have wrapped it -// in a binary that can be called directly and takes care of the piping under the hood. -// -// Note: ideally `entr` could accept files-to-watch via flag instead of stdin, -// but (for a number of good reasons) this feature isn't likely to be added any -// time soon (see https://github.com/eradman/entr/issues/33). - -package main - -import ( - "flag" - "fmt" - "log" - "os" - "os/exec" - "strings" - "syscall" -) - -var watchFile = flag.String("watch_file", "/.restart-proc", "File that entr will watch for changes; changes to this file trigger `entr` to rerun the command(s) passed") -var entrPath = flag.String("entr_path", "/entr", "Path to `entr` executable") - -func main() { - flag.Parse() - - cmd := exec.Command(*entrPath, "-rz") - cmd.Stdin = strings.NewReader(fmt.Sprintf("%s\n", *watchFile)) - cmd.Args = append(cmd.Args, flag.Args()...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - if err := cmd.Run(); err != nil { - if exiterr, ok := err.(*exec.ExitError); ok { - // The program has exited with an exit code != 0 - if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { - if len(flag.Args()) == 0 { - log.Println("`tilt-restart-wrapper` requires at least one positional arg " + - "(a command or set of args to be executed / rerun whenever `watch_file` changes)") - } - os.Exit(status.ExitStatus()) - } - } else { - log.Fatalf("error running command: %v", err) - } - } - - if len(flag.Args()) == 0 { - log.Fatal("`tilt-restart-wrapper` requires at least one positional arg " + - "(will be passed to `entr` and executed / rerun whenever `watch_file` changes)") - } -} diff --git a/integration/same_img_multi_container/tilt_modules/extensions.json b/integration/same_img_multi_container/tilt_modules/extensions.json deleted file mode 100644 index 7468c371e1..0000000000 --- a/integration/same_img_multi_container/tilt_modules/extensions.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Extensions": [ - { - "Name": "restart_process", - "ExtensionRegistry": "https://github.com/tilt-dev/tilt-extensions", - "TimeFetched": "2021-05-07T13:29:27.756644-04:00" - } - ] -} \ No newline at end of file diff --git a/integration/same_img_multi_container/tilt_modules/restart_process/.gitignore b/integration/same_img_multi_container/tilt_modules/restart_process/.gitignore deleted file mode 100644 index 3758a00fca..0000000000 --- a/integration/same_img_multi_container/tilt_modules/restart_process/.gitignore +++ /dev/null @@ -1 +0,0 @@ -tilt-restart-wrapper diff --git a/integration/same_img_multi_container/tilt_modules/restart_process/Dockerfile b/integration/same_img_multi_container/tilt_modules/restart_process/Dockerfile deleted file mode 100644 index 2ad4b08e0e..0000000000 --- a/integration/same_img_multi_container/tilt_modules/restart_process/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -FROM alpine/git - -RUN apk update && apk add make -RUN apk add build-base - -RUN git clone https://github.com/eradman/entr.git /entr -WORKDIR /entr -RUN git checkout c564e6bdca1dfe2177d1224363cad734158863ad -RUN ./configure; CFLAGS="-static" make install - -FROM scratch - -COPY --from=0 /usr/local/bin/entr / - -ADD tilt-restart-wrapper / diff --git a/integration/same_img_multi_container/tilt_modules/restart_process/README.md b/integration/same_img_multi_container/tilt_modules/restart_process/README.md deleted file mode 100644 index b47f82ef58..0000000000 --- a/integration/same_img_multi_container/tilt_modules/restart_process/README.md +++ /dev/null @@ -1,181 +0,0 @@ -# Restart Process - -This extension helps create images that can restart on `live_update`: - -- `docker_build_with_restart`: wraps a `docker_build` call -- `custom_build_with_restart`: wraps a `custom_build` call - -At the end of a `live_update`, the container's process will rerun itself. - -(Use it in place of the `restart_container()` Live Update step, which has been deprecated for Kubernetes resources.) - -## When to Use -Use this extension when you have an image and you want to re-execute its entrypoint/command as part of a `live_update`. - -E.g. if your app is a static binary, you'll probably need to re-execute the binary for any changes you made to take effect. - -(If your app has hot reloading capabilities--i.e. it can detect and incorporate changes to its source code without needing to restart--you probably don't need this extension.) - -### Unsupported Cases -This extension does NOT support process restarts for: -- Images built with `custom_build` using any of the `skips_local_docker`, `disable_push`, or `tag` parameters. -- Images run in Docker Compose resources (use the [`restart_container()`](https://docs.tilt.dev/api.html#api.restart_container) builtin instead) -- Images without a shell (e.g. `scratch`, `distroless`) -- Container commands specified as `command` in Kubernetes YAML will be overridden by this extension. - - However, the `args` field is still available; [reach out](https://tilt.dev/contact) if you need help navigating the interplay between Tilt and these YAML values -- CRDs - -If this extension doesn't work for your use case, [see our docs for alternatives](https://docs.tilt.dev/live_update_reference.html#restarting-your-process). - -Run into a bug? Need a use case that we don't yet support? Let us know--[open an issue](https://github.com/tilt-dev/tilt-extensions/issues) or [contact us](https://tilt.dev/contact). - -## How to Use - -Import this extension by putting the following at the top of your Tiltfile: -```python -load('ext://restart_process', 'docker_build_with_restart') -``` - -For the image that needs the process restart, replace your existing `docker_build` call: -```python -docker_build( - 'foo-image', - './foo', - arg1=val1, - arg2=val2, - live_update=[x, y, z...] -) -``` -with a `docker_build_with_restart` call: -```python -docker_build_with_restart( - 'foo-image', - './foo', - entrypoint='/go/bin/foo', - arg1=val1, - arg2=val2, - live_update=[x, y, z...] -) -``` -The call above looks just like the initial `docker_build` call except for one added parameter, `entrypoint` (in this example, `/go/bin/foo`). This is the command that you want to run on container start and _re-run_ on Live Update. - -A custom_build call looks similar: - -```python -load('ext://restart_process', 'custom_build_with_restart') - -custom_build_with_restart( - 'foo-image', - 'docker build -t $EXPECTED_REF ./foo', - deps=['./foo'], - live_update=[sync(...)] -) -``` - -### Troubleshooting -#### `failed running [touch /tmp/.restart-proc']` -If you see an error of the form: -``` -ERROR: Build Failed: ImageBuild: executor failed running [touch /tmp/.restart-proc']: exit code: 1 -``` -this often means that your Dockerfile user ([see docs](https://docs.docker.com/engine/reference/builder/#user)) doesn't have permission to write to the file we use to signal a process restart. Use the `restart_file` parameter to specify a file that your Dockerfile user definitely has write access to. - -### API -```python -def docker_build_with_restart(ref: str, context: str, - entrypoint: Union[str, List[str]], - live_update: List[LiveUpdateStep], - base_suffix: str = '-base', - restart_file: str = '/.restart-proc', - trigger: Union[str, List[str]] = [], - **kwargs -): - """Args: - ref: name for this image (e.g. 'myproj/backend' or 'myregistry/myproj/backend'); as the parameter of the same name in docker_build - context: path to use as the Docker build context; as the parameter of the same name in docker_build - entrypoint: the command to be (re-)executed when the container starts or when a live_update is run - live_update: set of steps for updating a running container; as the parameter of the same name in docker_build - base_suffix: suffix for naming the base image, applied as {ref}{base_suffix} - restart_file: file that Tilt will update during a live_update to signal the entrypoint to rerun - trigger: (optional) list of local paths. If specified, the process will ONLY be restarted when there are changes - to the given file(s); as the parameter of the same name in the LiveUpdate `run` step. - **kwargs: will be passed to the underlying `docker_build` call - """ - - -def custom_build_with_restart(ref: str, command: str, deps: List[str], entrypoint, - - entrypoint: Union[str, List[str]], - live_update: List[LiveUpdateStep], - base_suffix: str = '-base', - restart_file: str = '/.restart-proc', - trigger: Union[str, List[str]] = [], - , **kwargs -): - """ - Args: - ref: name for this image (e.g. 'myproj/backend' or 'myregistry/myproj/backend'); as the parameter of the same name in custom_build - command: build command for building your image - deps: source dependencies of the custom build - entrypoint: the command to be (re-)executed when the container starts or when a live_update is run - live_update: set of steps for updating a running container; as the parameter of the same name in custom_build - base_suffix: suffix for naming the base image, applied as {ref}{base_suffix} - restart_file: file that Tilt will update during a live_update to signal the entrypoint to rerun - trigger: (optional) list of local paths. If specified, the process will ONLY be restarted when there are changes - to the given file(s); as the parameter of the same name in the LiveUpdate `run` step. - **kwargs: will be passed to the underlying `custom_build` call - """ -``` - -## What's Happening Under the Hood -*If you're a casual user/just want to get your app running, you can stop reading now. However, if you want to dig deep and know exactly what's going on, or are trying to debug weird behavior, read on.* - -This extension wraps commands in `tilt-restart-wrapper`, which makes use of [`entr`](https://github.com/eradman/entr/) -to run arbitrary commands whenever a specified file changes. Specifically, we override the container's entrypoint with the following: - -``` -/tilt-restart-wrapper --watch_file='/.restart-proc' -``` - -This invocation says: -- when the container starts, run -- whenever the `/.restart-proc` file changes, re-execute - -We also set the following as the last `live_update` step: -```python -run('date > /.restart-proc') -``` - -Because `tilt-restart-wrapper` will re-execute the entrypoint whenever `/.restart-proc'` changes, the above `run` step will cause the entrypoint to re-run. - -#### Provide `tilt-restart-wrapper` -For this all to work, the `entr` binary must be available on the Docker image. The easiest solution would be to call e.g. `apt-get install entr` in the Dockerfile, but different base images will have different package managers; rather than grapple with that, we've made a statically linked binary available on Docker image: [`tiltdev/entr`](https://hub.docker.com/repository/docker/tiltdev/entr). - -To build `image-foo`, this extension will: -- build your image as normal (via `docker_build`, with all of your specified args/kwargs) but with the name `image-foo-base` -- build `image-foo` (the actual image that will be used in your resource) as a _child_ of `image-foo-base`, with the `tilt-process-wrapper` and its dependencies available - -Thus, the final image produced is tagged `image-foo` and has all the properties of your original `docker_build`, plus access to the `tilt-restart-wrapper` binary. - -#### Why a Wrapper? -Why bother with `tilt-restart-wrapper` rather than just calling `entr` directly? - -Because in its canonical invocation, `entr` requires that the file(s) to watch be piped via stdin, i.e. it is invoked like: -``` -echo "/.restart-proc" | entr -rz /bin/my-app -``` - -When specified as a `command` in Kubernetes or Docker Compose YAML (this is how Tilt overrides entrypoints), the above would therefore need to be executed as shell: -``` -/bin/sh -c 'echo "/.restart-proc" | entr -rz /bin/my-app' -``` -Any `args` specified in Kubernetes/Docker Compose are attached to the end of this call, and therefore in this case would apply TO THE `/bin/sh -c` CALL, rather than to the actual command run by `entr`; that is, any `args` specified by the user would be effectively ignored. - -In order to make `entr` usable without a shell, this extension uses [a simple binary](/restart_process/tilt-restart-wrapper.go) that invokes `entr` and writes to its stdin. - -Note: ideally `entr` could accept files-to-watch via flag instead of stdin, but (for a number of good reasons) this feature isn't likely to be added any time soon (see [entr#33](https://github.com/eradman/entr/issues/33)). - -## For Maintainers: Releasing -If you have push access to the `tiltdev` repository on DockerHub, you can release a new version of the binaries used by this extension like so: -1. run `release.sh` (builds `tilt-restart-wrapper` from source, builds and pushes a Docker image with the new binary and a fresh binary of `entr` also installed from source) -2. update the image tag in the [Tiltfile](/restart_process/Tiltfile) with the tag you just pushed (you'll find the image referenced in the Dockerfile contents of the child image--look for "FROM tiltdev/restart-helper") diff --git a/integration/same_img_multi_container/tilt_modules/restart_process/Tiltfile b/integration/same_img_multi_container/tilt_modules/restart_process/Tiltfile deleted file mode 100644 index 6a4138285c..0000000000 --- a/integration/same_img_multi_container/tilt_modules/restart_process/Tiltfile +++ /dev/null @@ -1,143 +0,0 @@ -RESTART_FILE = '/tmp/.restart-proc' -TYPE_RESTART_CONTAINER_STEP = 'live_update_restart_container_step' - -KWARGS_BLACKLIST = [ - # since we'll be passing `dockerfile_contents` when building the - # child image, remove any kwargs that might conflict - 'dockerfile', 'dockerfile_contents', - - # 'target' isn't relevant to our child build--if we pass this arg, - # Docker will just fail to find the specified stage and error out - 'target', -] - -# Arguments to custom_build that don't apply to the docker_build. -_CUSTOM_BUILD_KWARGS_BLACKLIST = [ - 'tag', - 'command_bat', - 'outputs_image_ref_to', - 'disable_push', -] - -_ext_dir = os.getcwd() - -# shared code between the two restart functions -def _helper(base_ref, ref, entrypoint, live_update, restart_file=RESTART_FILE, trigger=None, **kwargs): - if not trigger: - trigger = [] - - # declare a new docker build that adds a static binary of tilt-restart-wrapper - # (which makes use of `entr` to watch files and restart processes) to the user's image - df = ''' - FROM tiltdev/restart-helper:2020-10-16 as restart-helper - - FROM {} - RUN ["touch", "{}"] - COPY --from=restart-helper /tilt-restart-wrapper / - COPY --from=restart-helper /entr / - '''.format(base_ref, restart_file) - - # Change the entrypoint to use `tilt-restart-wrapper`. - # `tilt-restart-wrapper` makes use of `entr` (https://github.com/eradman/entr/) to - # re-execute $entrypoint whenever $restart_file changes - if type(entrypoint) == type(""): - entrypoint_with_entr = ["/tilt-restart-wrapper", "--watch_file={}".format(restart_file), "sh", "-c", entrypoint] - elif type(entrypoint) == type([]): - entrypoint_with_entr = ["/tilt-restart-wrapper", "--watch_file={}".format(restart_file)] + entrypoint - else: - fail("`entrypoint` must be a string or list of strings: got {}".format(type(entrypoint))) - - # last live_update step should always be to modify $restart_file, which - # triggers the process wrapper to rerun $entrypoint - # NB: write `date` instead of just `touch`ing because `entr` doesn't respond - # to timestamp changes, only writes (see https://github.com/eradman/entr/issues/32) - live_update = live_update + [run('date > {}'.format(restart_file), trigger=trigger)] - - # We don't need a real context. See: - # https://github.com/tilt-dev/tilt/issues/3897 - context = _ext_dir - - docker_build(ref, context, entrypoint=entrypoint_with_entr, dockerfile_contents=df, - live_update=live_update, **kwargs) - -def docker_build_with_restart(ref, context, entrypoint, live_update, - base_suffix='-tilt_docker_build_with_restart_base', restart_file=RESTART_FILE, - trigger=None, **kwargs): - """Wrap a docker_build call and its associated live_update steps so that the last step - of any live update is to rerun the given entrypoint. - - Args: - ref: name for this image (e.g. 'myproj/backend' or 'myregistry/myproj/backend'); as the parameter of the same name in docker_build - context: path to use as the Docker build context; as the parameter of the same name in docker_build - entrypoint: the command to be (re-)executed when the container starts or when a live_update is run - live_update: set of steps for updating a running container; as the parameter of the same name in docker_build - base_suffix: suffix for naming the base image, applied as {ref}{base_suffix} - restart_file: file that Tilt will update during a live_update to signal the entrypoint to rerun - trigger: (optional) list of local paths. If specified, the process will ONLY be restarted when there are changes - to the given file(s); as the parameter of the same name in the LiveUpdate `run` step. - **kwargs: will be passed to the underlying `docker_build` call - """ - - # first, validate the given live_update steps - if len(live_update) == 0: - fail("`docker_build_with_restart` requires at least one live_update step") - for step in live_update: - if type(step) == TYPE_RESTART_CONTAINER_STEP: - fail("`docker_build_with_restart` is not compatible with live_update step: " + - "`restart_container()` (this extension is meant to REPLACE restart_container() )") - - # rename the original image to make it a base image and declare a docker_build for it - base_ref = '{}{}'.format(ref, base_suffix) - docker_build(base_ref, context, **kwargs) - - # Clean kwargs for building the child image (which builds on user's specified - # image and copies in Tilt's restart wrapper). In practice, this means removing - # kwargs that were relevant to building the user's specified image but are NOT - # relevant to building the child image / may conflict with args we specifically - # pass for the child image. - cleaned_kwargs = {k: v for k, v in kwargs.items() if k not in KWARGS_BLACKLIST} - _helper(base_ref, ref, entrypoint, live_update, restart_file, trigger, **cleaned_kwargs) - - -def custom_build_with_restart(ref, command, deps, entrypoint, live_update, - base_suffix='-tilt_docker_build_with_restart_base', restart_file=RESTART_FILE, - trigger=None, **kwargs): - """Wrap a custom_build call and its associated live_update steps so that the last step - of any live update is to rerun the given entrypoint. - - Args: - ref: name for this image (e.g. 'myproj/backend' or 'myregistry/myproj/backend'); as the parameter of the same name in custom_build - command: build command for building your image - deps: source dependencies of the custom build - entrypoint: the command to be (re-)executed when the container starts or when a live_update is run - live_update: set of steps for updating a running container; as the parameter of the same name in custom_build - base_suffix: suffix for naming the base image, applied as {ref}{base_suffix} - restart_file: file that Tilt will update during a live_update to signal the entrypoint to rerun - trigger: (optional) list of local paths. If specified, the process will ONLY be restarted when there are changes - to the given file(s); as the parameter of the same name in the LiveUpdate `run` step. - **kwargs: will be passed to the underlying `custom_build` call - """ - - # first, validate the given live_update steps - if len(live_update) == 0: - fail("`custom_build_with_restart` requires at least one live_update step") - for step in live_update: - if type(step) == TYPE_RESTART_CONTAINER_STEP: - fail("`custom_build_with_restart` is not compatible with live_update step: "+ - "`restart_container()` (this extension is meant to REPLACE restart_container() )") - - for k, v in kwargs.items(): - if k == 'skips_local_docker': - fail("`custom_build_with_restart` is not compatible with `skips_local_docker`, because it needs access to the image") - if k == 'disable_push': - fail("`custom_build_with_restart` is not compatible with `disable_push`") - if k == 'tag': - fail("`custom_build_with_restart` renames your base image, so is not compatible with `tag`") - - # rename the original image to make it a base image and declare a custom_build for it - base_ref = '{}{}'.format(ref, base_suffix) - custom_build(base_ref, command=command, deps=deps, **kwargs) - - # A few arguments aren't applicable to the docker_build, so remove them. - cleaned_kwargs = {k: v for k, v in kwargs.items() if k not in _CUSTOM_BUILD_KWARGS_BLACKLIST} - _helper(base_ref, ref, entrypoint, live_update, restart_file, trigger, **cleaned_kwargs) diff --git a/integration/same_img_multi_container/tilt_modules/restart_process/release.sh b/integration/same_img_multi_container/tilt_modules/restart_process/release.sh deleted file mode 100755 index f342acf03b..0000000000 --- a/integration/same_img_multi_container/tilt_modules/restart_process/release.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -set -ex - -TIMESTAMP=$(date +'%Y-%m-%d') -IMAGE_NAME='tiltdev/restart-helper' -IMAGE_WITH_TAG=$IMAGE_NAME:$TIMESTAMP - -# build binary for tilt-restart-wrapper -env GOOS=linux GOARCH=amd64 go build tilt-restart-wrapper.go - -# build Docker image with static binaries of: -# - tilt-restart-wrapper (compiled above) -# - entr (dependency of tilt-restart-wrapper) -docker build . -t $IMAGE_NAME -docker push $IMAGE_NAME - -docker tag $IMAGE_NAME $IMAGE_WITH_TAG -docker push $IMAGE_WITH_TAG - -echo "Successfully built and pushed $IMAGE_WITH_TAG" - - - diff --git a/integration/same_img_multi_container/tilt_modules/restart_process/test/Dockerfile b/integration/same_img_multi_container/tilt_modules/restart_process/test/Dockerfile deleted file mode 100644 index 95934f1b4d..0000000000 --- a/integration/same_img_multi_container/tilt_modules/restart_process/test/Dockerfile +++ /dev/null @@ -1,5 +0,0 @@ -FROM busybox - -COPY fail.sh / - -ENTRYPOINT /fail.sh diff --git a/integration/same_img_multi_container/tilt_modules/restart_process/test/Tiltfile b/integration/same_img_multi_container/tilt_modules/restart_process/test/Tiltfile deleted file mode 100644 index 5a943f9305..0000000000 --- a/integration/same_img_multi_container/tilt_modules/restart_process/test/Tiltfile +++ /dev/null @@ -1,5 +0,0 @@ -load('../Tiltfile', 'docker_build_with_restart') - -k8s_yaml('job.yaml') -docker_build_with_restart('failing_job', '.', '/fail.sh', - live_update=[sync('./fail.sh', '/fail.sh')]) diff --git a/integration/same_img_multi_container/tilt_modules/restart_process/test/custom.Tiltfile b/integration/same_img_multi_container/tilt_modules/restart_process/test/custom.Tiltfile deleted file mode 100644 index 692a9bcedb..0000000000 --- a/integration/same_img_multi_container/tilt_modules/restart_process/test/custom.Tiltfile +++ /dev/null @@ -1,9 +0,0 @@ -load('../Tiltfile', 'custom_build_with_restart') - -k8s_yaml('job.yaml') -custom_build_with_restart( - 'failing_job', - command='docker build -t $EXPECTED_REF .', - deps=['fail.sh'], - entrypoint='/fail.sh', - live_update=[sync('./fail.sh', '/fail.sh')]) diff --git a/integration/same_img_multi_container/tilt_modules/restart_process/test/fail.sh b/integration/same_img_multi_container/tilt_modules/restart_process/test/fail.sh deleted file mode 100755 index 1e51f5e0a2..0000000000 --- a/integration/same_img_multi_container/tilt_modules/restart_process/test/fail.sh +++ /dev/null @@ -1,9 +0,0 @@ -#! /bin/sh - -echo "Are you there, pod?" -sleep 1 - -# Exit with a non-zero status code; we check that docker_build_with_restart -# surfaces this error code to k8s, so k8s knows that the job failed. -echo "Exiting with status code 123 😱" -exit 123 diff --git a/integration/same_img_multi_container/tilt_modules/restart_process/test/job.yaml b/integration/same_img_multi_container/tilt_modules/restart_process/test/job.yaml deleted file mode 100644 index 09e5445b6e..0000000000 --- a/integration/same_img_multi_container/tilt_modules/restart_process/test/job.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - name: failing-job -spec: - template: - spec: - containers: - - name: failing-job - image: failing_job - restartPolicy: Never - backoffLimit: 4 diff --git a/integration/same_img_multi_container/tilt_modules/restart_process/test/test-custom.sh b/integration/same_img_multi_container/tilt_modules/restart_process/test/test-custom.sh deleted file mode 100755 index de1f832b1d..0000000000 --- a/integration/same_img_multi_container/tilt_modules/restart_process/test/test-custom.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -cd $(dirname $0) - -set -x -tilt ci -f custom.Tiltfile > tilt.log 2>&1 -CI_EXIT=$? - -tilt down - -if [ $CI_EXIT -eq 0 ]; then - echo "Expected 'tilt ci' to fail, but succeeded." - exit 1 -fi - -cat tilt.log | grep -q "Are you there, pod?" -GREP_EXIT=$? - -rm tilt.log - -exit $GREP_EXIT diff --git a/integration/same_img_multi_container/tilt_modules/restart_process/test/test-docker.sh b/integration/same_img_multi_container/tilt_modules/restart_process/test/test-docker.sh deleted file mode 100755 index 66509fbf3d..0000000000 --- a/integration/same_img_multi_container/tilt_modules/restart_process/test/test-docker.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash - -# Test case for https://github.com/tilt-dev/tilt-extensions/issues/92 -# -# This job will always exit with a non-zero status code; make sure -# that docker_build_with_restart surfaces this error code to k8s, -# so k8s knows that the job failed. (Thus, we expect the `tilt ci` -# call to fail.) -cd $(dirname $0) - -set -x -tilt ci > tilt.log 2>&1 -CI_EXIT=$? - -tilt down - -if [ $CI_EXIT -eq 0 ]; then - echo "Expected 'tilt ci' to fail, but succeeded." - exit 1 -fi - -cat tilt.log | grep -q "Are you there, pod?" -GREP_EXIT=$? - -rm tilt.log - -exit $GREP_EXIT diff --git a/integration/same_img_multi_container/tilt_modules/restart_process/test/test.sh b/integration/same_img_multi_container/tilt_modules/restart_process/test/test.sh deleted file mode 100755 index 47141f20e8..0000000000 --- a/integration/same_img_multi_container/tilt_modules/restart_process/test/test.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -set -ex - -cd $(dirname $0) -./test-docker.sh -./test-custom.sh diff --git a/integration/same_img_multi_container/tilt_modules/restart_process/tilt-restart-wrapper.go b/integration/same_img_multi_container/tilt_modules/restart_process/tilt-restart-wrapper.go deleted file mode 100644 index 12d5a9fe9e..0000000000 --- a/integration/same_img_multi_container/tilt_modules/restart_process/tilt-restart-wrapper.go +++ /dev/null @@ -1,73 +0,0 @@ -// `tilt-restart-wrapper` wraps `entr` (http://eradman.com/entrproject/) to easily -// rerun a user-specified command when a given file changes. -// -// This is Tilt's recommended way of restarting a process as part of a live_update: -// if your container invokes your app via the restart wrapper (e.g. `tilt-restart-wrapper /bin/my-app`), -// you can trigger re-execution of your app with a live_update `run` step that makes -// a trivial change to the file watched by `entr` (e.g. `run('date > /.restart-proc')`) -// -// This script exists (i.e. we're wrapping `entr` in a binary instead of invoking -// it directly) because in its canonical invocation, `entr` requires that the -// file(s) to watch be piped via stdin, i.e. it is invoked like: -// echo "/.restart-proc" | entr -rz /bin/my-app -// -// When specified as a `command` in Kubernetes or Docker Compose YAML (this is how -// Tilt overrides entrypoints), the above would therefore need to be executed as shell: -// /bin/sh -c 'echo "/.restart-proc" | entr -rz /bin/my-app' - -// Any args specified in Kubernetes or Docker Compose YAML are attached to the end -// of this call, and therefore in this case apply TO THE `/bin/sh -c` CALL, rather -// than to the actual command run by `entr`; that is, any `args` specified by the -// user would be effectively ignored. -// -// In order to make `entr` executable as ARGV rather than as shell, we have wrapped it -// in a binary that can be called directly and takes care of the piping under the hood. -// -// Note: ideally `entr` could accept files-to-watch via flag instead of stdin, -// but (for a number of good reasons) this feature isn't likely to be added any -// time soon (see https://github.com/eradman/entr/issues/33). - -package main - -import ( - "flag" - "fmt" - "log" - "os" - "os/exec" - "strings" - "syscall" -) - -var watchFile = flag.String("watch_file", "/.restart-proc", "File that entr will watch for changes; changes to this file trigger `entr` to rerun the command(s) passed") -var entrPath = flag.String("entr_path", "/entr", "Path to `entr` executable") - -func main() { - flag.Parse() - - cmd := exec.Command(*entrPath, "-rz") - cmd.Stdin = strings.NewReader(fmt.Sprintf("%s\n", *watchFile)) - cmd.Args = append(cmd.Args, flag.Args()...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - if err := cmd.Run(); err != nil { - if exiterr, ok := err.(*exec.ExitError); ok { - // The program has exited with an exit code != 0 - if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { - if len(flag.Args()) == 0 { - log.Println("`tilt-restart-wrapper` requires at least one positional arg " + - "(a command or set of args to be executed / rerun whenever `watch_file` changes)") - } - os.Exit(status.ExitStatus()) - } - } else { - log.Fatalf("error running command: %v", err) - } - } - - if len(flag.Args()) == 0 { - log.Fatal("`tilt-restart-wrapper` requires at least one positional arg " + - "(will be passed to `entr` and executed / rerun whenever `watch_file` changes)") - } -} diff --git a/web/tilt_modules/extensions.json b/web/tilt_modules/extensions.json deleted file mode 100644 index 91de8bc311..0000000000 --- a/web/tilt_modules/extensions.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Extensions": [ - { - "Name": "uibutton", - "ExtensionRegistry": "https://github.com/tilt-dev/tilt-extensions", - "TimeFetched": "2021-11-19T11:39:32.114401-05:00" - } - ] -} \ No newline at end of file diff --git a/web/tilt_modules/uibutton/README.md b/web/tilt_modules/uibutton/README.md deleted file mode 100644 index d1d00c1779..0000000000 --- a/web/tilt_modules/uibutton/README.md +++ /dev/null @@ -1,180 +0,0 @@ -# UIButton - -Authors: - * [Matt Landis](https://github.com/landism) - * [Milas Bowman](https://github.com/milas) - -Extend the Tilt UI with custom actions for your resources. - -## Functions - -### `cmd_button(name, resource, argv, text=None, location=location.RESOURCE, icon_name=None, icon_svg=None)` - -Creates a button for a resource that runs the given command when clicked. - -| Argument | Type | Description | -| -------- | ---- | ----------- | -| `name` | `str` | Unique ID for button | -| `resource` | `str` | Resource to associate button with (required if `location=location.RESOURCE`) | -| `argv` | `List[str]` | Local command to run when button is clicked | -| `text` | `str` | Text to display on button (optional: defaults to `name`) | -| `location` | `str` (enum) | Button placement in UI (see `location` section below) | -| `icon_name` | `str` | Name of [Material Icons font ligature][material-icons-font] to use as icon (at most one of `icon_name` or `icon_svg` should be specified) | -| `icon_svg` | `str` or `Blob` | `` to use as icon; should have 24x24px viewport (at most one of `icon_name` or `icon_svg` should be specified) | - -### `location` -To specify button location, you can bind the `location` helper type or pass a string, e.g. `location=location.NAV` and `location='nav'` are equivalent. - -| Location | `str` Value | `location` Value | -| ---------- | ----------- | ------------------- | -| Resource | `resource` | `location.RESOURCE` | -| Global nav | `nav` | `location.NAV` | - -### `text_input(name, label='', default='', placeholder='')` -Specifies that the button's UI will include a text field the user can enter. -The field's current value will be set in the command's env when it is run. - -| Argument | Type | Description | -| ------------- | ----- | ----------- | -| `name` | `str` | The text input's name. Also the name of the environment variable to be set when running the command. | -| `label` | `str` | Text to display next to the text input in the UI. | -| `default` | `str` | Default initial value for this field. | -| `placeholder` | `str` | A short hint that describes the expected input of this field. | - -### `bool_input(name, label='', default=False, true_string=None, false_string=None)` -Specifies that the button's UI will include a checkbox to toggle this value. -When the command is run, an environment variable will be set based on the -checkbox's state. By default, the variable will be set to the string `"true"` or `"false"`, as appropriate. Those values can be configured with the `true_string` and `false_string` parameters. - -| Argument | Type | Description | -| -------------- | ----- | ----------- | -| `name` | `str` | The input's name. Also the name of the environment variable to be set when running the command. | -| `label` | `str` | Text to display next to the input in the UI. | -| `default` | `bool` | Default initial value for this field. | -| `true_string` | `str` | If not None, when the checkbox is checked, the environment variable will be set to this string instead of "true". | -| `false_string` | `str` | If not None, when the checkbox is checked, the environment variable will be set to this string instead of "false". | - -## Example Usage - -```python -load('ext://uibutton', 'cmd_button', 'location', 'text_input') - -# define resource 'my-resource' -# k8s_resource('my-resource') - -# create a button on resource 'my-resource' -cmd_button(name='my-resource-hello-world', - resource='my-resource', - argv=['echo', 'Hello my-resource!'], - text='Hello World', - icon_name='travel_explore') - -cmd_button(name='nav-hello-world', - argv=['echo', 'Hello nav!'], - text='Hello World', - location=location.NAV, - icon_name='waving_hand') - -cmd_button(name='foo', - resource='my-resource', - text='Reseed database', - inputs=[ - text_input('SHARD'), - ], - # If you need env var expansion *within the command itself* - # you'll need to run it via a shell. - argv=['/bin/sh', '-c', './reseed_database.sh --shard="$SHARD"'], - ) -``` - -## Button Placement -Currently, you can create buttons for a specific resource, which will be shown with other resource contextual actions such as "Copy Pod ID" or as part of the global nav next to the help and account buttons. - -### Resource -To create a resource button, pass the resource name via `resource='my-resource'` and omit the `location` argument or explicitly pass `location=location.RESOURCE`. - -Any command output will appear interleaved with the associated resource's logs. - -Providing an icon is optional. - -### Global Nav -To create a global nav button, pass `location=location.NAV` and omit the `resource` argument or explicitly pass `resource=None`. - -Any command output will appear in the "All Resources" log view under `(global)`. - -Global nav buttons SHOULD specify an icon via either the `icon_name` or `icon_svg` arguments. The `text` value will appear on hover. - -## Icons -Button icons can either come from the set of built-in icons that ship with Tilt or a custom SVG. - -Navbar buttons SHOULD include an icon as the button text is only visible on hover. -For resource buttons, icons are optional and will appear within the button if specified. - -If both `icon_name` and `icon_svg` are specified, `icon_svg` will take precedence. - -### Built-in Icons (Material Icons) -Tilt includes the [Material Icons][material-icons-font] by default. -Use the `icon_name` argument and pass the "font ligature" value for your desired icon. -The font ligatures are visible in the sidebar after clicking on an icon on the Material Fonts site. -Tip: They are `lower_snake_case` values, e.g. the "Check Circle" icon has a font ligature value of `check_circle`. - -### Custom Icons (SVG) -Use the `icon_svg` argument and pass a full `` element. -The SVG viewport should be 24x24 for best results. - -To avoid string quoting issues, it's often easiest to load the SVG from disk rather than storing it directly in your Tiltfile: -```python -load('ext://uibutton', 'cmd_button', 'location') - -icon = read_file('./icon.svg') - -cmd_button('svg-btn', - argv=['echo', '✨ Hello from SVG ✨'], - location=location.NAV, - icon_svg=icon, - text='SVG Nav Button') # text will appear on hover -``` - -## Inputs -If a button has inputs, the UI will attach an arrow to the button, allowing the -user to set those inputs' values. -When the button is clicked, those input's values will be set as environment -variables in the executed process. -For example: -```python -cmd_button('hello', - argv=['sh', '-c', 'echo Hello, $NAME'], - location=location.NAV, - icon_name='front_hand', - text='Hello!', - inputs=[ - text_input('NAME', placeholder='Enter your name'), - ] -) -``` - -This will create a button (top right) with an options menu (opened by the little arrow): -![screenshot of button with options menu](assets/button_with_input.png) - -When the user clicks the button to run its command, `$NAME` will be set -to the field's value, e.g.: - -``` -Running cmd: echo Hello, $NAME -Hello, there -``` - -Note that if the command needs an env var expanded inside the command itself (e.g., it directly uses `$NAME`, rather than simply invoking a program that uses `$NAME`), it will need to be wrapped in a shell call, e.g. ['sh', '-c', 'mycommand $NAME']. - -## Other notes - -Commands are executed locally on the host running `tilt up` (similar to `local_resource`). - -The `argv` argument only accepts a list, e.g. `['echo', 'Hello World']` but not `echo 'Hello World'`. - -To run a script, invoke the interpreter and then pass the script as an argument, e.g. `['bash', '-c', 'echo "Hello from bash ${BASH_VERSION}"']`. - -## Known Issues -* Renamed/deleted buttons will not be removed until Tilt is restarted ([#193](https://github.com/tilt-dev/tilt-extensions/issues/193)) - -[material-icons-font]: https://fonts.google.com/icons diff --git a/web/tilt_modules/uibutton/Tiltfile b/web/tilt_modules/uibutton/Tiltfile deleted file mode 100644 index c955c3222a..0000000000 --- a/web/tilt_modules/uibutton/Tiltfile +++ /dev/null @@ -1,151 +0,0 @@ -LOCATION_RESOURCE = 'resource' -LOCATION_NAV = 'nav' - -location = struct( - RESOURCE=LOCATION_RESOURCE, - NAV=LOCATION_NAV, -) - - -def _button(name, location, text='', icon=None, annotations={}, inputs=[]): - text = text or name - btn = { - "apiVersion": "tilt.dev/v1alpha1", - "kind": "UIButton", - "metadata": { - "name": name, - "annotations": annotations - }, - "spec": { - "text": text, - "location": { - "componentType": location.type, - "componentID": location.id, - } - } - } - - if len(inputs): - btn["spec"]["inputs"] = inputs - - if icon: - if icon.svg: - # convert to str to handle str + Blob - btn['spec']['iconSVG'] = str(icon.svg) - elif icon.name: - btn['spec']['iconName'] = icon.name - - return btn - - -def cmd_button(name, resource='', argv=[], text=None, - location=LOCATION_RESOURCE, icon_name=None, icon_svg=None, - inputs=[]): - if config.tilt_subcommand == 'down': - return - - if config.tilt_subcommand == 'docker-prune': - return - - if not location: - fail('location is required') - - if location == LOCATION_RESOURCE: - if not resource: - fail('Must provide a resource name') - elif resource: - fail('Cannot specify resource for location type {}'.format(location)) - - if not argv: - fail('argv cannot be empty') - - btn_annotations = {} - - if location == LOCATION_NAV: - location = struct(type='Global', id='nav') - elif location == LOCATION_RESOURCE: - location = struct(type='Resource', id=resource) - btn_annotations['tilt.dev/resource'] = resource - else: - # fallback to simplify experimenting with new locations in the future - loc_type, sep, loc_id = location.partition('/') - if not sep: - fail('Unsupported location {}'.format(location)) - location = struct(type=loc_type, id=loc_id) - - button = _button( - name=name, - location=location, - text=text, - icon=struct(name=icon_name, svg=icon_svg), - annotations=btn_annotations, - inputs=inputs, - ) - cmd = { - "apiVersion": "tilt.dev/v1alpha1", - "kind": "Cmd", - "metadata": { - "name": "btn-" + name, - "annotations": { - "tilt.dev/resource": resource, - "tilt.dev/log-span-id": 'cmd:' + name, - } - }, - "spec": { - "args": argv, - "dir": config.main_dir, - "startOn": { - "startAfter": _now(), - "uiButtons": [name], - }, - } - } - - local( - command='echo "${TILT_APPLY_YAML}"\ - | %s apply -f -' % (sys.executable,), - command_bat='(cmd /v:on /c echo !TILT_APPLY_YAML!)\ - | %s apply -f -' % (sys.executable,), - env={'TILT_APPLY_YAML': str(encode_yaml_stream([button, cmd]))}, - echo_off=True) - - -def _now(): - return str(local( - # this is portable across coreutils/busybox/BSD - # note: it's missing fractional seconds because that's not available - # from strftime - command='date -u +"%Y-%m-%dT%H:%M:%S.000000Z"', - command_bat="powershell Get-Date (Get-Date).ToUniversalTime()\ - -UFormat '+%Y-%m-%dT%H:%M:%S.000000Z'", - echo_off=True, - quiet=True) - ).rstrip('\r\n') - - -def text_input(name, label='', default='', placeholder=''): - return { - "name": name, - "label": label, - "text": { - "defaultValue": default, - "placeholder": placeholder, - } - } - - -def bool_input(name, label='', default=False, true_string=None, - false_string=None): - result = { - "name": name, - "label": label, - "bool": { - "defaultValue": default, - }, - } - if true_string != None: - result["bool"]["trueString"] = true_string - if false_string != None: - result["bool"]["falseString"] = false_string - - return result diff --git a/web/tilt_modules/uibutton/assets/button_with_input.png b/web/tilt_modules/uibutton/assets/button_with_input.png deleted file mode 100644 index 1fa5853e66a4fe00a87706c2de25a7329c2760f9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37299 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 D(KE1H diff --git a/web/tilt_modules/uibutton/test/Tiltfile b/web/tilt_modules/uibutton/test/Tiltfile deleted file mode 100644 index e4ea4e25d9..0000000000 --- a/web/tilt_modules/uibutton/test/Tiltfile +++ /dev/null @@ -1,17 +0,0 @@ -load('../Tiltfile', 'cmd_button', 'location', 'text_input') - -local_resource('vigoda', cmd='echo "Hello from resource"') - -cmd_button("resource-button", resource="vigoda", - argv=["bash", "-c", "echo Hello from bash ${BASH_VERSION}"], - text='Resource Button', icon_name='star') - -goose_icon = read_file('./goose.svg') - -cmd_button('nav-button-svg', argv=["echo", "✨ Hello from SVG ✨"], - location=location.NAV, icon_svg=goose_icon, text='SVG Nav Button') -cmd_button('nav-button-std', argv=["echo", "Hello from nav"], - location=location.NAV, icon_name='calendar_today') -cmd_button('nav-button-hello', argv=["sh", "-c", "echo Hello, $NAME"], - location=location.NAV, icon_name='front_hand', text='Hello', - inputs=[text_input('NAME')]) diff --git a/web/tilt_modules/uibutton/test/goose.svg b/web/tilt_modules/uibutton/test/goose.svg deleted file mode 100644 index 18e41e01ce..0000000000 --- a/web/tilt_modules/uibutton/test/goose.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/web/tilt_modules/uibutton/test/test.sh b/web/tilt_modules/uibutton/test/test.sh deleted file mode 100755 index 9aaf15c4b9..0000000000 --- a/web/tilt_modules/uibutton/test/test.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -cd "$(dirname "$0")" - -set -ex -tilt ci -tilt down --delete-namespaces