Skip to content

Test storage automations locally

Watch a local directory with the Tilebox CLI, register a Python storage-event automation, and trigger a job by writing a file.

Use a local storage location to test storage-event automations without configuring a cloud bucket or cloud notifications. The CLI watches a directory and sends events to Tilebox. A Python runner on the same machine reads the files and executes the triggered tasks.

Local storage locations also work for processing files locally. Files stay on your machine, while Tilebox coordinates the jobs. The watcher and runner need a connection to Tilebox.

Prerequisites

  • Install the Tilebox CLI and uv.
  • Set up authentication. The CLI and Python SDK both use TILEBOX_API_KEY; saving it in your shell configuration makes it available in new terminals.

Create a project

The shell examples use Bash. Create a project and install the Python workflow SDK:

bash
mkdir local-storage-test
cd local-storage-test
uv init --no-package
uv add tilebox-workflows
mkdir input

Register and watch the directory

Register input as a local storage location:

bash
tilebox storage-location create local --path ./input --name "Local automation test"

Copy the returned location ID. Replace <storage-location-id> with it throughout this guide. The CLI resolves --path to an absolute path, so the registered location does not depend on the watcher’s working directory.

Start the watcher in this terminal:

bash
tilebox storage-location watch <storage-location-id>

Wait for the Watching ... message and leave the command running.

The watcher reports new and modified files in the directory and its subdirectories after one second without changes. Existing, unchanged files are not sent when it starts. It respects .gitignore rules by default; use --no-gitignore to include ignored files.

Define the Python task

Open a second terminal in local-storage-test. Save this as local_automation.py:

local_automation.py python
from tilebox.workflows import Client, ExecutionContext, Runner
from tilebox.workflows.automations import StorageEventTask


class ReadFile(StorageEventTask):
    def execute(self, context: ExecutionContext) -> None:
        path = self.trigger.location
        data = self.trigger.storage.read(path)
        context.logger.info(
            "Read file",
            path=path,
            size_bytes=len(data),
        )


if __name__ == "__main__":
    client = Client()
    runner = Runner(tasks=[ReadFile])
    runner.connect_to(client).run_forever()

self.trigger.location is relative to the registered directory, for example hello.txt or nested/hello.txt, without a leading /. self.trigger.storage.read() returns the file’s bytes. The task logs its path and size, so it works with text, images, and other binary files.

Register the automation

Save this second file beside local_automation.py:

register_automation.py python
import sys
from uuid import UUID

from tilebox.workflows import Client

from local_automation import ReadFile


client = Client()
automations = client.automations()
location_id = UUID(sys.argv[1])
location = next(
    location
    for location in automations.storage_locations()
    if location.id == location_id
)

automation = automations.create_storage_event_automation(
    "read-local-files",
    ReadFile(),
    triggers=[(location, "**")],
)
print(f"Automation ID: {automation.id}")

Select the location by the ID returned by the CLI, then register the automation once:

bash
uv run python register_automation.py <storage-location-id>

Save the printed automation ID for cleanup. Register once: each run creates a new automation.

The pattern ** matches all files in the directory and its subdirectories. To process only text files, use **.txt. See glob patterns for the syntax, examples, and an online way to test them.

Start the runner

In the second terminal, start the task runner:

bash
uv run python local_automation.py

Leave it running alongside the watcher on the same machine. Both the automation and this runner use the default cluster. Storage reads use the registered absolute path, so the runner does not need to start in the watched directory. The command above runs from the project directory so uv can find the Python environment and script.

Trigger an event and inspect the job

Open a third terminal in local-storage-test. With both the watcher and runner running, create a matching file:

bash
echo "Hello from local storage" > input/hello.txt

After the file has been unchanged for a second and the notification is delivered, the watcher prints:

text
Event: hello.txt

The runner should log Read file with path=hello.txt and size_bytes=25. You can also copy an image or another file into input to trigger a job. Check the received event and its triggered jobs:

bash
tilebox storage-location events <storage-location-id> --limit 10 --json

Find object_key: "hello.txt" and copy a job_id from triggered_jobs. Inspect the job in the Console or read its logs:

bash
tilebox job logs <job-id>

To trigger another event, modify the file after the first event has arrived:

bash
echo "A second update" >> input/hello.txt

If no event appears, check that the watcher is still running, the file is inside the registered directory, and no .gitignore rule excludes it. If the event has no triggered jobs, check the automation’s location ID and glob. If a job stays queued or fails, check the runner’s cluster and access to the directory.

Stop the test

Press Ctrl+C in the watcher and runner terminals.

In the Console’s Automations page, delete read-local-files. Use the ID printed during registration to identify it if you have several automations with that name. You can keep the location for another test if you want.

Use the same task with cloud storage

The same StorageEventTask works with local storage, AWS S3, Google Cloud Storage, and Azure Blob Storage. To use ReadFile with a cloud location, connect the bucket or container, then register an automation with that location’s ID. Configure the runner’s cloud credentials so self.trigger.storage.read() can read the objects. Cloud notifications replace the local CLI watcher; the task code stays the same.

Type to search…