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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions skills/buildr-artifacts/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
name: buildr-artifacts
description: Publish browser-viewable artifacts. Use static mode for S3-hosted HTML artifacts and Vite app mode for stateful apps that should run on Codexbox with a bld.run URL.
compatibility: Static mode requires aws CLI and Codexbox instance role permissions for the artifact S3 bucket. Vite app mode requires npm, tailscale, and an existing per-user bld.run DNS record.
compatibility: Static mode requires aws CLI and artifact S3 bucket credentials from ~/.env (ARTIFACT_AWS_*) or the Codexbox instance role. Vite app mode requires npm, tailscale, and an existing per-user bld.run DNS record.
metadata:
homepage: https://artifacts.buildrtools.com
allowed-tools: Bash({baseDir}/scripts/share_artifact.py:*) Bash({baseDir}/scripts/serve_vite_app.py:*) Read
Expand Down Expand Up @@ -92,7 +92,7 @@ Both scripts print only the shareable URL on success. Report that URL to the use

## Environment

Codexbox normally sources these from `/home/codex/.config/codexbox/env`, populated from Secrets Manager:
Static uploads read these from `~/.env` (dotenv format, optional `export` prefix). Variables already exported in the process environment take precedence over `~/.env`:

- `ARTIFACT_AWS_REGION` — AWS region, falls back to `AWS_REGION`, then `us-east-1`
- `ARTIFACT_AWS_ACCESS_KEY_ID` — optional artifact-scoped AWS access key for static uploads
Expand All @@ -102,7 +102,7 @@ Codexbox normally sources these from `/home/codex/.config/codexbox/env`, populat
- `ARTIFACT_BASE_URL` — URL base, defaults to `https://artifacts.buildrtools.com`
- `BLD_RUN_HOST` — optional Vite app URL host override, for example `mike.bld.run`

Static uploads use the `ARTIFACT_AWS_*` values when provided; otherwise they rely on the Codexbox instance role or normal AWS CLI credential resolution.
Static uploads use the `ARTIFACT_AWS_*` values when provided; otherwise they rely on the Codexbox instance role or normal AWS CLI credential resolution. Vite app mode does not read `~/.env`; export `BLD_RUN_HOST` or pass `--url-host`.

## Safety Rules

Expand All @@ -119,5 +119,5 @@ The script preserves the pi extension's constraints:

- Prefer writing artifacts under `tmp/` or another scratch directory, then upload the final directory.
- Do not upload secrets or raw customer data unless the user explicitly requests it and confirms the artifact is safe to share internally.
- If static upload fails with missing credentials or access denied, check that the Codexbox instance role has artifact bucket permissions.
- If static upload fails with missing credentials or access denied, check that `~/.env` defines `ARTIFACT_AWS_ACCESS_KEY_ID` and `ARTIFACT_AWS_SECRET_ACCESS_KEY` with artifact bucket permissions, or that the Codexbox instance role has them.
- If Vite starts but the URL does not load, check the app logs in `~/.cache/buildr-artifacts/vite-apps/<slug>/` and confirm the printed port is reachable over Tailscale.
24 changes: 24 additions & 0 deletions skills/buildr-artifacts/scripts/share_artifact.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@

DEFAULT_BUCKET = "buildr-bizops-artifacts"
DEFAULT_BASE_URL = "https://artifacts.buildrtools.com"
DOTENV_PATH = Path.home() / ".env"
DEFAULT_MAX_FILES = 2000
DEFAULT_MAX_DEPTH = 20
MAX_FILE_BYTES = 1_073_741_824
Expand All @@ -123,6 +124,7 @@ class ArtifactFile:
def main() -> int:
args = parse_args()
try:
load_dotenv(DOTENV_PATH)
artifact_files = collect_input_files(args)
slug = generate_slug()
upload_files(artifact_files, slug)
Expand All @@ -144,6 +146,28 @@ def parse_args() -> argparse.Namespace:
return parser.parse_args()


def load_dotenv(path: Path) -> None:
"""Fill missing environment variables from a dotenv file. Exported values win."""
if not path.is_file():
return

for line in path.read_text(encoding="utf-8").splitlines():
line = line.strip()
if not line or line.startswith("#") or "=" not in line:
continue

name, value = line.split("=", 1)
name = name.removeprefix("export ").strip()
value = value.strip()
if len(value) >= 2 and value[0] == value[-1] and value[0] in "'\"":
value = value[1:-1]
else:
value = value.split(" #", 1)[0].rstrip()

if name and name not in os.environ:
os.environ[name] = value


def collect_input_files(args: argparse.Namespace) -> list[ArtifactFile]:
if args.html is not None:
return [inline_html_file(args.html)]
Expand Down
Loading