{"id":50382568,"url":"https://github.com/bramp/assets","last_synced_at":"2026-05-30T13:01:18.515Z","repository":{"id":360278767,"uuid":"1249450446","full_name":"bramp/assets","owner":"bramp","description":"Asset Management Pipeline","archived":false,"fork":false,"pushed_at":"2026-05-25T18:42:48.000Z","size":57,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-25T20:10:06.967Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bramp.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null},"funding":{"github":"bramp","buy_me_a_coffee":"bramp"}},"created_at":"2026-05-25T18:01:13.000Z","updated_at":"2026-05-25T18:42:52.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/bramp/assets","commit_stats":null,"previous_names":["bramp/assets"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/bramp/assets","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bramp%2Fassets","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bramp%2Fassets/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bramp%2Fassets/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bramp%2Fassets/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bramp","download_url":"https://codeload.github.com/bramp/assets/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bramp%2Fassets/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33692997,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-30T02:00:06.278Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-30T13:01:17.637Z","updated_at":"2026-05-30T13:01:18.509Z","avatar_url":"https://github.com/bramp.png","language":"Go","funding_links":["https://github.com/sponsors/bramp","https://buymeacoffee.com/bramp"],"categories":[],"sub_categories":[],"readme":"# assets\n\nDeclarative asset pipeline and registry for image processing, metadata validation, and deterministic lockfile verification.\n\n## Why This Project Exists\n\nModern apps accumulate many generated assets (logos, icons, screenshots, variants) that are easy to let drift. This project exists to make that process predictable and auditable.\n\nIt provides:\n\n1. Asset management: a single manifest to declare source files and generated outputs.\n2. Licensing/compliance tracking: owner/copyright/license metadata can be validated consistently.\n3. A consistent processing pipeline: standardized build steps for transforming and compressing assets.\n4. A cheap freshness check in CI: verify committed outputs and lockfile state without re-running full image processing on every build.\n\n## When To Use\n\nUse `assets` when you want to treat generated image assets like build artifacts that are versioned in git.\n\nIt is especially useful as a cheap CI gate to confirm generated assets are current and checked in:\n\n1. Developers run `make` locally to regenerate changed outputs and update `assets.lock`.\n2. CI runs `assets verify` to fail fast if committed assets/lockfile are stale.\n3. CI can perform this check without re-running expensive image pipelines for every build.\n\n## Quickstart\n\n### 0) Install or run the CLI\n\nOption A (preferred): install `assets` into your Go bin path.\n\n```bash\ngo install github.com/bramp/assets/cmd/assets@latest\nassets --help\n```\n\nOption B: run without installing.\n\n```bash\nalias assets='go run github.com/bramp/assets/cmd/assets@latest --'\nassets --help\n```\n\n### 1) Create assets.yaml\n\nStart with the smallest manifest. This is a single base logo.svg that gets exported in two different sizes, and file formats.\n\n```yaml\nmeta:\n  project: \"My App\"\n\nassets:\n  - id: \"logo\"\n    source: \"raw/logo.svg\"\n    owner: \"Example Org\"\n    copyright: \"Copyright 2026\"\n    license: \"Proprietary\"\n    outputs:\n      - path: \"assets/images/logo_128.png\"\n        width: 128\n        height: 128\n        options:\n          scale_mode: \"fit\"\n          background: \"transparent\"\n    outputs:\n      - path: \"assets/images/logo_256.jpg\"\n        width: 256\n        height: 256\n```\n\n### 2) Add root Makefile wiring\n\n```makefile\n.PHONY: all check-assets clean\n\n# Load generated asset dependency rules if present.\n-include .assets.mk\n\n# Build all declared generated assets.\nall: $(GENERATED_ASSET_FILES)\n  @echo \"assets up to date\"\n\n# Regenerate dependency rules whenever manifest changes.\n.assets.mk: assets.yaml\n  @assets gen \u003e .assets.mk\n\n# Build each output file via the assets CLI.\n$(GENERATED_ASSET_FILES):\n  @assets build --target $@\n\n# Validate manifest and metadata policy in strict mode.\ncheck-assets:\n  @assets check --strict\n\n# Remove generated assets and generated Makefile fragment.\nclean:\n  rm -f $(GENERATED_ASSET_FILES) .assets.mk\n```\n\n### 3) Run workflow locally\n\n```bash\n# Validate manifest schema and required legal metadata.\nassets check --strict\n\n# Build all declared outputs (Make regenerates .assets.mk as needed).\nmake\n\n# Confirm lockfile, outputs, and provenance are in sync.\nassets verify\n```\n\n## Customize Pipeline\n\nWhen defaults are not enough, define stage catalogs and pick tools independently.\n\nGraph tool selection is composable:\n\n- Global defaults: set ordered tool preference list in `meta.render.defaults.tools`.\n- Per output: override with `outputs[].options.tools` as either a string or list.\n- Tool compatibility metadata: set `accepts` and `produces` per tool entry so the resolver can find the shortest valid conversion path.\n- Size capability metadata: set `sets_size` to a command fragment template (for example `\"--width {width} --height {height}\"`) so tools can optionally honor target width/height.\n- Mode capability metadata: set `scale_modes` per tool entry so the resolver can require aspect-ratio behavior compatibility.\n- Selection behavior: shortest valid path wins; preference order breaks ties.\n\nResize intent (`options.scale_mode`) semantics:\n\n- `fit`: preserve aspect ratio; fully visible within target box.\n- `fill`: preserve aspect ratio; cover target box (cropping may occur).\n- `stretch`: ignore aspect ratio; force exact target width/height.\n- `crop`: preserve aspect ratio and crop to target box.\n\n```yaml\nmeta:\n  project: \"My App\"\n  render:\n    defaults:\n      tools: [\"resvg\", \"rsvg-convert\", \"inkscape\", \"vips-transform\", \"magick-transform\", \"vips-encode\", \"magick-encode\", \"oxipng\", \"gifsicle\", \"jpegoptim\", \"cwebp\"]\n    tools:\n      resvg:\n        tool: \"resvg\"\n        accepts: [\".svg\"]\n        produces: [\".png\", \".webp\", \".jpg\", \".jpeg\"]\n        scale_modes: [\"fit\"]\n        sets_size: \"--width {width} --height {height}\"\n        command: \"resvg {sets_size} {input} {output}\"\n      rsvg-convert:\n        tool: \"rsvg-convert\"\n        accepts: [\".svg\"]\n        produces: [\".png\"]\n        scale_modes: [\"fit\"]\n        command: \"rsvg-convert -w {width} -h {height} -o {output} {input}\"\n      inkscape:\n        tool: \"inkscape\"\n        accepts: [\".svg\", \".eps\"]\n        produces: [\".png\"]\n        scale_modes: [\"fit\"]\n        command: \"inkscape {input} --export-filename={output} --export-width={width} --export-height={height}\"\n      vips-transform:\n        tool: \"vips\"\n        accepts: [\".png\", \".jpg\", \".jpeg\", \".webp\", \".gif\"]\n        produces: [\".png\", \".jpg\", \".jpeg\", \".webp\", \".gif\"]\n        scale_modes: [\"fit\", \"fill\", \"stretch\", \"crop\"]\n        command: \"vips resize {input} {output} {scale}\"\n      magick-transform:\n        tool: \"magick\"\n        accepts: [\"*\"]\n        produces: [\"*\"]\n        scale_modes: [\"fit\", \"fill\", \"stretch\", \"crop\"]\n        command: \"magick {input} {resize_args} {output}\"\n      vips-encode:\n        tool: \"vips\"\n        accepts: [\".png\", \".jpg\", \".jpeg\", \".webp\"]\n        produces: [\".png\", \".jpg\", \".jpeg\", \".webp\"]\n        command: \"vips copy {input} {output}\"\n      magick-encode:\n        tool: \"magick\"\n        accepts: [\"*\"]\n        produces: [\"*\"]\n        command: \"magick {input} {output}\"\n      oxipng:\n        tool: \"oxipng\"\n        accepts: [\".png\"]\n        produces: [\".png\"]\n        command: \"oxipng -o 3 --strip safe {output}\"\n      gifsicle:\n        tool: \"gifsicle\"\n        accepts: [\".gif\"]\n        produces: [\".gif\"]\n        command: \"gifsicle -O3 -b {output}\"\n      jpegoptim:\n        tool: \"jpegoptim\"\n        accepts: [\".jpg\", \".jpeg\"]\n        produces: [\".jpg\", \".jpeg\"]\n        command: \"jpegoptim --strip-all {output}\"\n      cwebp:\n        tool: \"cwebp\"\n        accepts: [\".webp\"]\n        produces: [\".webp\"]\n        command: \"cwebp -quiet -q 82 {output} -o {output}\"\n\nassets:\n  - id: \"logo\"\n    source: \"raw/logo.svg\"\n    outputs:\n      - path: \"assets/images/logo_128.png\"\n        width: 128\n        height: 128\n        options:\n          tools: [\"inkscape\", \"resvg\", \"rsvg-convert\", \"magick-transform\", \"magick-encode\"]\n          scale_mode: \"fit\"\n          background: \"transparent\"\n```\n\nExample behavior:\n\n- `assets/images/logo.png` resolves the shortest valid graph path from source format to `.png`, then uses `defaults.tools` ordering to break ties.\n- `assets/images/anim.gif` resolves similarly for `.gif` and can choose a different chain based on tool capabilities.\n- Per-output override still wins (for example `options.tools: [\"resvg\", \"magick-encode\"]`).\n\n## Complex Example\n\nFor a full graph-first setup with mixed source formats, fallback tools, scale mode constraints, and optimizer-only outputs, see:\n\n- `examples/complex/assets.yaml`\n- `examples/complex/README.md`\n\n## Commands\n\n- assets check\n- assets gen\n- assets defaults\n- assets build --target \u003cpath\u003e\n- assets verify\n\n### assets check\n\nValidates manifest structure and semantics.\n\n- Use strict mode to enforce legal metadata:\n\n```bash\n# Strictly validate manifest and legal metadata.\nassets check --strict\n```\n\n### assets gen\n\nEmits deterministic Makefile fragment to stdout.\n\nThis is primarily useful for debugging or for non-Make workflows.\n\n```bash\n# Regenerate deterministic Makefile rules from assets.yaml.\n# Output includes commented planned command chains per target.\nassets gen \u003e .assets.mk\n```\n\n### assets defaults\n\nPrints a recommended `meta.render` snippet you can paste into `assets.yaml`.\n\n```bash\nassets defaults\n```\n\n### assets build\n\nBuilds one output and updates assets.lock provenance/size/source hash entry.\n\n```bash\n# Build a single declared output target.\nassets build --target assets/images/logo_128.png\n```\n\n### assets verify\n\nVerifies manifest, sources, lockfile, and output size/provenance alignment.\n\n```bash\n# Verify outputs and lockfile provenance are current.\nassets verify\n```\n\n## Failure And Recovery\n\nCommon failures:\n\n- source hash mismatch: source file changed after lockfile generation\n- provenance mismatch: pipeline command chain/tool versions changed\n- missing output: generated file missing from disk\n- size mismatch: output differs from lockfile entry\n\nRecovery flow:\n\n1. Run build locally for affected targets (or run make).\n2. Re-run assets verify.\n3. Commit updated generated outputs plus assets.lock.\n\n## Release Checklist\n\nWhen changing rendering semantics or pipeline placeholders:\n\n1. Update DESIGN.md with schema/behavior changes.\n2. Add or update manifest validation tests.\n3. Add or update build and verify tests.\n4. Verify lockfile provenance behavior remains deterministic.\n5. Run:\n  - go fmt ./...\n  - goimports -w .\n  - go vet ./...\n  - staticcheck ./...\n  - go test ./...\n\n## Coverage Trend Tracking\n\nCoverage trend reporting is configured via Codecov and GitHub Actions:\n\n1. CI generates `coverage.out` using `go test ./... -coverprofile=coverage.out -covermode=atomic`.\n2. CI uploads coverage with `codecov/codecov-action` using OIDC (no long-lived token required).\n3. Threshold policy is stored in `codecov.yml`.\n\n## Development Plan\n\nSee TODO checklist in TODOs.md.\n\n## Design\n\nSystem design and architecture are documented in DESIGN.md.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbramp%2Fassets","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbramp%2Fassets","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbramp%2Fassets/lists"}