Skip to content

Releases

v0.1.0

Choose a tag to compare

@github-actions github-actions released this 14 Nov 02:21

First release of ichava/icon-sets-sync as a single published commit. The 0.1.x tags cut between 2026-10-23 and 2026-10-26 (v0.1.0 to v0.1.2, with their src/ module aliases) are folded into this release, and their notes are kept below under Archived history.

The Go module proxy and checksum database already hold github.com/ichava/icon-sets-sync/src at v0.1.0, v0.1.1 and v0.1.2, fetched from commits before the single-commit publication, and neither can be cleared. So go install github.com/ichava/icon-sets-sync/src/...@v0.1.0 serves that older code, and @latest the older v0.1.2. The supported installs are the signed release binaries and the Composer package; go install is not offered.

Added

  • Integrations: a REST API, an event bus with an event stream, webhooks and an MCP server, every one off by default (owner, 2026-11-12, one pull request). A fresh install behaves exactly as before; each surface is a setting in a new Integrations card on the Settings page, and doctor says which are on and warns about each risky combination. See docs/tools/api.md, events.md, webhooks.md and mcp.md.

    • Events. internal/events is an in-process bus of CloudEvents 1.0 envelopes (JSON format, ULID ids) with eleven v1 types: job.started, job.progress (stream only), job.finished, pack.synced, pack.behind_upstream, upstream.checked, config.changed (names the key, never the value), server.started, server.stopping, webhook.disabled and webhook.test. Each type's data has a JSON Schema embedded in the binary. TestEveryTypeHasASchema fails on a type without a schema or a schema without a type, TestEveryTypeHasAPublisher on a type no production code publishes, and TestRunEventsMatchTheirSchemas validates every event a run emits. The job registry gains publishing hooks; the run page's per-job stream is unchanged.
    • REST API under /api/v1 on the serve listener (api.enabled; beyond loopback also api.allow_remote), 22 endpoints with an OpenAPI 3.1 document that TestOpenAPIMatchesTheRoutes holds to the routes declared in apiRoutes, both ways and scope by scope. Bearer tokens only, with scopes read, sync, config:write and admin: icon-sets-sync api token create|list|revoke shows a token once and keeps only its SHA-256 in api-tokens.json in the state directory (mode 0600), compared in constant time, with expiry, revocation and a recorded last use. The session cookie does not open /api/ and a bearer token opens no page. RFC 9457 problem documents, a 1 MiB body cap, a per-token rate limit (api.rate_limit, 60 a minute), Idempotency-Key on the sync endpoints, an audit line per write in api-audit.jsonl, and dry_run defaulting to true. --read-only registers no write endpoint. GET /api/v1/events streams the bus as Server-Sent Events when events.stream is on. The UI and the API now start runs through one web.StartSync, and save settings through one web.SaveGlobalSetting with the Settings page's checks (web.ParseSettingValue).
    • Webhooks (webhooks.enabled, endpoints in webhooks.endpoints, each with a required event filter). icon-sets-sync webhooks add|list|remove|rotate|test; secrets are generated (whsec_ and 32 random bytes), shown once and kept in webhook-secrets.json (mode 0600), with an optional secret_env override, and a rotation keeps the old secret signing for 24 hours, so both signatures are sent. Standard Webhooks headers and HMAC-SHA256 signing (checked against the specification's test vector). Every address is checked when it is dialled (RFC 1918, loopback, link-local and metadata, CGNAT, ULA, unspecified, and the IPv6 prefixes that embed IPv4) unless webhooks.allow_private; https only (plain http only to localhost with webhooks.allow_insecure_localhost), no proxy, no redirects, a 10 s timeout and a 256 KiB cap. A bounded queue (1,000), retries after 1 m, 5 m, 30 m, 2 h and 6 h, a rotated webhook-deliveries.jsonl, and auto-disable after 50 consecutive failures with a webhook.disabled event.
    • MCP. icon-sets-sync mcp serves the Model Context Protocol on stdio with the official Go SDK, github.com/modelcontextprotocol/go-sdk v1.8.0 (mcp.enabled or --enable). Eight read tools by default; sync_pack, cancel_job and set_setting only with mcp.allow_writes and never under --read-only, and a real sync needs confirm equal to the slug and is refused while a serve runs on the machine.
    • Security guards, each proven red before it was kept: a token without the scope is refused; an expired or revoked token is refused; a webhook to a private address is refused at connect time; a bad signature does not verify; the write tools are absent under --read-only and without mcp.allow_writes; every integration is off on a fresh config; a non-loopback bind defaults to TLS.
  • A Windows launcher that opens the web UI with no console window. make cross builds icon-sets-sync-launcher_windows_<arch>.exe (amd64, arm64, 386) beside each Windows binary. It is a separate program (src/cmd/icon-sets-sync-launcher) linked for the GUI subsystem (-H=windowsgui), so Windows opens no console for it: it finds the CLI beside itself, starts its serve detached with ICHAVA_DESKTOP_LAUNCH=1, and exits, and if the CLI is missing it shows an error box naming the paths it tried. A double-clicked CLI still flashes a console for under a second; the CLI itself is unchanged, so it behaves normally in cmd and PowerShell (owner, option A, 2026-11-11). Like the macOS app and the Linux .desktop entry, it is not a GoReleaser build or a release asset. TestWindowsLauncherIsAGUIProgramAndTheCLIIsNot reads both PE headers (proven red both ways); the build-layout guard names the three launcher files; TestOnlyOneEntrypoint allows exactly this second src/cmd directory.

  • Settings are edited in place on the Settings page. Each effective setting has the control its type calls for (a menu, a switch, a bounded number, a list, a path or a multi-line field), a Save that writes it to the global config and a Reset that removes it from there, and says beside it when an ICHAVA_* variable or a flag overrides the saved value. Values are checked as they are typed by the same code as the save (POST /settings/value/<name>/check), and the save adds checks the loader does not make: directories and the npm path must exist, allowed_hosts must be bare host names, branch names must be ones git accepts, and lists may not repeat an entry. A refused save keeps what was typed and says why beside the field.

  • Pagination on every list, with a page_size setting. Packs, runs, audit results and a run's log show page_size rows (default 25, 5 to 500, ICHAVA_PAGE_SIZE) with a Per page menu and page links that keep any filter in the address. Settings gain a Max bound, used by page_size and carried into global.schema.json.

  • The global config starts initialised, and can be reinitialised. serve and the new config init write it from what the machine has: $schema, packages_dirs (the directory of pack checkouts this tool sits in) and private_config_dir (when exactly one pack carries one). A missing, $schema-only or still-generated file is (re)written; one a person edited is left alone, told apart by a record of what was last written (global-init.json in the state directory). The global config page says which it is and has Reinitialise (a confirmed action; config init --force in the CLI), which keeps the old copy in the history. Read-only servers and CI runs write nothing.

  • Recipes for the 34 v0.1.0 packs and sets. config/ gains Lucide, Heroicons, Bootstrap Icons, Simple Icons, Material Design Icons, Phosphor, Font Awesome Free, Remix Icon (pinned to v4.8.0, the last Apache-2.0 release), Material Symbols, Fluent, Ionicons, Carbon, Octicons and Iconoir, and one config per set of icon-sets-extras (20, each recording its version at upstream.sets.<set>.current_version in the pack). All 38 registered configs validate, and a dry run of each new one at its pinned version against the local pack checkout reproduced the committed SVGs byte for byte (0 added, removed or changed, 2026-11-04). TestEveryShippedRecipeValidates loads and builds every registered config and fails below 38, or on a config file packs.json does not register.

  • split-variants transform. Sorts upstream files into one directory per variant by the directory they come from (from), the suffix they carry (suffix, stripped), or both; unclaimed files are dropped and counted, which is how a pack takes a subset, and flatten_prefix keeps sub-folders as a name prefix. It covers Heroicons, Phosphor, Material Symbols, Fluent, Ionicons, Carbon and Octicons. A variant that claims nothing, or two names reducing to one, fail the run.

  • exclude transform. Drops icons by stem (names, Octicons' GitHub marks), by directory (directories, Game Icons' unlicensed badges/) or from an upstream data file (metadata, Simple Icons' non-CC0 logos from data/simple-icons.json, so the list follows each release). An excluded item that cannot be named fails the run.

  • More transform options. slugify takes dirs (slug directory names too: Remix's Game & Sports) and hyphenate_dots (lucene.net -> lucene-net), and now fails when two names slug to one instead of renaming one over the other. flatten-variants takes slug, which slugs each stem before the affix (Health Icons' !.svg is committed as -filled.svg). sanitise takes drop_unparsable, which removes a file the policy cannot parse instead of leaving it unchecked (Noto Emoji's 170 SVGs with an Illustrator DTD).

  • Licence guards for every licence the packs carry. licence now admits Apache-2.0, CC-BY-3.0, CC0-1.0, ISC and MIT beside the two CC-BY ids, each checked against its opening lines (a Markdown # is ignored). The new `licence_sha256...

Read more