tilt icon indicating copy to clipboard operation
tilt copied to clipboard

`local` run-once option for `tilt up`

Open Cyanopus opened this issue 2 years ago • 5 comments

Describe the Feature You Want

local run once or some prerequisite phase that runs only once for a tiltfile.

Current Behavior

Tilt tries to generate files that trigger the k8s_yaml or any deps field change and reload of tilt. This leads to an endless loop of local commands triggering k8s_yaml tilt reload. I don't want to ignore the files as these are regenerated with each save by a tilt state.

Why Do You Want This?

This phase can be used to pre-generate some files that will be loaded as resources by k8s_yaml without triggering an endless loop. I use kapitan go generate some files and would like to generate the files for each save. But to load these files in the first place I have to have them in the folder which requires me to do the first generation outside of Tilt.

Additional context Add any other context or screenshots about the feature request here.

Cyanopus avatar Jan 16 '23 14:01 Cyanopus

I'm not sure if you have tried something like this (maybe you have), but you could do something like below to get this behavior:

if config.tilt_subcommand == "up":
	did_once = os.getenv("DID_ONCE", False)
	if not did_once:
		print("Do once!")
		os.putenv("DID_ONCE", "true")

gamerson avatar Jan 17 '23 20:01 gamerson

Note that if you just use DID_ONCE env var - it will run once during tilt up lifetime. If you want to run once per Tiltfile load, you can use smth like below:

ONCE_VARS_VAR='_TILT_ONCE_VARS'

def on_load():
    os.putenv(ONCE_VARS_VAR, '')

def call_once(fn, *args, **kwargs):
  desc = "fn={} args={}, kwargs={}".format(str(fn), args, kwargs)
  desc_hash = str(hash(desc))
  once_vars = os.getenv(ONCE_VARS_VAR, '')
  did_once = desc_hash in once_vars.split()
  if not did_once:
    os.putenv(ONCE_VARS_VAR, once_vars + ' ' + desc_hash)
    print("calling {}".format(desc))
    fn(*args, **kwargs)
  else:
    print("already called {}".format(desc))

on_load()

Usage:

load('lib/functions.star',
      'call_once',
)

call_once(print, "hello")
call_once(print, "hello")

call_once(print, "hello1")
call_once(print, "hello1")

Output will be:

Loading Tiltfile at: /home/me/tmp/tilt-test/Tiltfile
calling fn=<built-in function print> args=("hello",), kwargs={}
hello
already called fn=<built-in function print> args=("hello",), kwargs={}
calling fn=<built-in function print> args=("hello1",), kwargs={}
hello1
already called fn=<built-in function print> args=("hello1",), kwargs={}

amkartashov avatar Aug 08 '23 03:08 amkartashov

These are good workarounds, but env vars are potentially not side effect safe. I am looking for a native lifecycle mechanism.

Cyanopus avatar Aug 08 '23 05:08 Cyanopus

@Cyanopus I believe this is starlark limitation by design, see https://github.com/bazelbuild/starlark/blob/master/spec.md#freezing-a-value - so there are no synchronization primitives provided by the language.

Still, Tilt authors may decide to add this functionality with some built-in function.

amkartashov avatar Aug 09 '23 02:08 amkartashov

I'm not sure if this helps, but I managed to break the infinite loop I had with my project - because I generate Kustomize YAML files with python-kustomize - by using watch_settings .

diogobaeder avatar Oct 17 '23 00:10 diogobaeder