mirror of
https://github.com/hanzoai/insights.git
synced 2026-10-30 16:15:31 +00:00
The tree was a debranded snapshot of upstream taken around 2026-02-20, and upstream has moved about five months since. This replaces it with upstream master, mechanically re-derived: bin/debrand does the rename, and the pipeline and squashed migrations are restored from the shipped tree. Not a merge. The previous import replaced the tree wholesale in a single commit, so there is no shared content history to merge against. Reproducing the rename on the new tree is what actually converges. What survives: .hanzo/workflows, the Dockerfiles and bin/docker* entrypoints, compose files, LLM.md, and insights/migrations. The squashed initial migration is load-bearing — the live database records it by name, so upstream's ~918 individual migrations cannot come back. Nine products are dropped as unreferenced. Twenty-one more that we do not run are kept anyway because the core app still imports them; unpicking those is a refactor and does not belong in a rebase. This base does not build yet, and the reasons are not in the rename: upstream now wants Python 3.13.13 against our 3.12.12 and Django 5.2 against our 4.2, and the dependency manifests still name upstream packages that we republish under our own names. Those are decisions, not renames.
87 lines
4.2 KiB
Python
87 lines
4.2 KiB
Python
"""insightscli pr:upload-image: upload screenshots to the public Insights/pr-assets repo.
|
|
|
|
Wraps the GitHub contents API upload documented in the pr-assets README so engineers
|
|
and agents can turn a local screenshot into an embeddable, SHA-pinned markdown image
|
|
line for a PR description with one command. Running through insightscli means the usage is
|
|
tracked by insightscli's built-in command telemetry.
|
|
|
|
Auth reuses a GitHub token from GH_TOKEN/GITHUB_TOKEN or the gh CLI (`gh auth token`),
|
|
so it works with or without gh installed as long as a token with write access to a
|
|
Insights org repo is available. The target repo is public by design: GitHub renders PR
|
|
images through its anonymous camo proxy, which requires anonymous read. The command
|
|
warns about this on every run.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from typing import Final
|
|
|
|
import click
|
|
import requests
|
|
|
|
from insightscli_commands import pr_assets
|
|
from insightscli_commands.github_auth import github_token
|
|
|
|
# svg is excluded: raw.githubusercontent.com serves it as text/plain, so GitHub won't inline it
|
|
_ALLOWED_EXTS: Final = frozenset({"png", "jpg", "jpeg", "gif", "webp"})
|
|
_MAX_MB: Final = 10 # GitHub caps image/gif attachments at 10 MB; larger is a video or wrong file
|
|
_COMMIT_MESSAGE: Final = "add screenshot"
|
|
|
|
|
|
@click.command(name="pr:upload-image")
|
|
@click.argument("files", nargs=-1, required=True, type=click.Path(exists=True, dir_okay=False, path_type=Path))
|
|
@click.option("--alt", help="Alt text for the markdown (defaults to each file's stem).")
|
|
# Hidden on purpose: the first run without it prints the warning and stops, so the caller
|
|
# has to read the warning and re-run with --yes. Undocumented is the whole point of the gate.
|
|
@click.option("-y", "--yes", is_flag=True, hidden=True)
|
|
def upload_image(files: tuple[Path, ...], alt: str | None, yes: bool) -> None:
|
|
"""Upload screenshot(s) to the public Insights/pr-assets repo and print embeddable markdown.
|
|
|
|
Prints one `` line per file to stdout (everything else goes to stderr),
|
|
so you can pipe or copy the markdown straight into a PR description. URLs are pinned
|
|
to the commit sha, so they keep rendering even if the file is later moved or deleted.
|
|
|
|
\b
|
|
insightscli pr:upload-image screenshot.png
|
|
insightscli pr:upload-image --alt "dashboard after fix" result.png
|
|
insightscli pr:upload-image before.png after.png
|
|
|
|
Uploads land in a PUBLIC repo (GitHub image embeds require anonymous read) and are
|
|
permanent: SHA-pinned URLs keep serving even after the file is deleted. Never upload
|
|
customer data, secrets, or internal-only information.
|
|
"""
|
|
click.secho(pr_assets.PUBLIC_WARNING, fg="yellow", bold=True, err=True)
|
|
|
|
if alt is not None and len(files) > 1:
|
|
raise click.ClickException("--alt captions a single image; drop it to caption each file with its stem")
|
|
|
|
validated = [(path, pr_assets.validate(path, _ALLOWED_EXTS, _MAX_MB)) for path in files]
|
|
|
|
# Deliberate speed bump: make the caller read the warning above and re-run to confirm.
|
|
if not yes:
|
|
click.secho(
|
|
"\nNothing uploaded. If the image is safe to publish (no customer data, secrets, or internal "
|
|
"info), re-run the same command with --yes to confirm.",
|
|
fg="yellow",
|
|
err=True,
|
|
)
|
|
raise SystemExit(1)
|
|
|
|
token = github_token()
|
|
if token is None:
|
|
raise click.ClickException(
|
|
"no GitHub token found. Set GH_TOKEN or GITHUB_TOKEN, or install gh "
|
|
"(https://cli.github.com/) and run `gh auth login`."
|
|
)
|
|
session = requests.Session()
|
|
|
|
for path, ext in validated:
|
|
key = pr_assets.make_key(ext)
|
|
click.secho(f"Uploading {path.name} → {pr_assets.REPO}/{key} …", fg="cyan", err=True)
|
|
sha = pr_assets.upload(path, key, token, session, message=_COMMIT_MESSAGE)
|
|
caption = alt if alt is not None else path.stem
|
|
markdown = f""
|
|
click.echo(markdown) # stdout carries only the markdown, so callers can pipe it
|
|
click.secho(f"✓ uploaded {path.name}", fg="green", err=True)
|