From c321fd8c1ce2d6dce2988aee95b4c63b8c6bb34a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Almeida?= Date: Sun, 16 Aug 2026 16:50:13 +0100 Subject: [PATCH 1/2] feat: add marketplace icon --- .github/workflows/build.yml | 2 +- .github/workflows/release.yml | 2 +- Makefile | 37 ++++++++++++++++++--- README.md | 8 ++--- assets/NOTICE.md | 10 ++++++ assets/icon.svg | 5 +++ manifest.yaml | 1 + server/manifest_test.go | 60 +++++++++++++++++++++++++++++++++++ 8 files changed, 115 insertions(+), 10 deletions(-) create mode 100644 assets/NOTICE.md create mode 100644 assets/icon.svg create mode 100644 server/manifest_test.go diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dc5f758..ae04bb4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -40,4 +40,4 @@ jobs: - name: Package all supported platforms working-directory: plugin - run: make package + run: make package verify-package diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0cd0fd1..7747410 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -182,7 +182,7 @@ jobs: - name: Package all platforms working-directory: plugin - run: make package + run: make package verify-package - name: Extract checksums working-directory: plugin diff --git a/Makefile b/Makefile index adc7d24..71ec05f 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,10 @@ -.PHONY: build test fmt vet package package-host clean +.PHONY: build test fmt vet package package-host verify-package verify-package-host clean BIN := bin/kandev-plugin-github-status VERSION := 0.1.1 STAGE := .build/stage PKG_OUT := kandev-plugin-github-status-$(VERSION).tar.gz +KANDEV_BACKEND := ../kandev/apps/backend build: mkdir -p bin @@ -18,18 +19,20 @@ fmt: vet: go vet ./server/... -## Cross-compile every platform in manifest.yaml, stage manifest + ui, pack. +## Cross-compile every platform in manifest.yaml, stage manifest + assets + ui, +## then pack the archive. package: rm -rf $(STAGE) mkdir -p $(STAGE)/server cp manifest.yaml $(STAGE)/manifest.yaml + cp -r assets $(STAGE)/assets cp -r ui $(STAGE)/ui GOOS=linux GOARCH=amd64 go build -o $(STAGE)/server/plugin-linux-amd64 ./server GOOS=linux GOARCH=arm64 go build -o $(STAGE)/server/plugin-linux-arm64 ./server GOOS=darwin GOARCH=amd64 go build -o $(STAGE)/server/plugin-darwin-amd64 ./server GOOS=darwin GOARCH=arm64 go build -o $(STAGE)/server/plugin-darwin-arm64 ./server GOOS=windows GOARCH=amd64 go build -o $(STAGE)/server/plugin-windows-amd64.exe ./server - go run github.com/kandev/kandev/cmd/plugin-pack -dir $(STAGE) -out $(PKG_OUT) + go -C $(KANDEV_BACKEND) run ./cmd/plugin-pack -dir $(abspath $(STAGE)) -out $(abspath $(PKG_OUT)) rm -rf $(STAGE) @echo "Wrote $(PKG_OUT)" @@ -38,11 +41,37 @@ package-host: rm -rf $(STAGE) mkdir -p $(STAGE)/server cp manifest.yaml $(STAGE)/manifest.yaml + cp -r assets $(STAGE)/assets cp -r ui $(STAGE)/ui go build -o $(STAGE)/server/plugin-$$(go env GOOS)-$$(go env GOARCH)$$(go env GOEXE) ./server - go run github.com/kandev/kandev/cmd/plugin-pack -dir $(STAGE) -out $(PKG_OUT) -platform-only + go -C $(KANDEV_BACKEND) run ./cmd/plugin-pack -dir $(abspath $(STAGE)) -out $(abspath $(PKG_OUT)) -platform-only rm -rf $(STAGE) @echo "Wrote $(PKG_OUT)" +## Verify the generated archive, including the manifest-declared marketplace +## icon and plugin-pack's checksums, before it reaches the host installer. +define verify_package_archive +VERIFY_DIR="$$(mktemp -d)"; \ +trap 'rm -rf "$$VERIFY_DIR"' EXIT; \ +test -f "$(PKG_OUT)" || { echo "package not found: $(PKG_OUT)"; exit 1; }; \ +tar -xzf "$(PKG_OUT)" -C "$$VERIFY_DIR"; \ +test -f "$$VERIFY_DIR/manifest.yaml"; \ +grep -Fx 'icon: "assets/icon.svg"' "$$VERIFY_DIR/manifest.yaml" >/dev/null; \ +test -f "$$VERIFY_DIR/assets/icon.svg"; \ +test -f "$$VERIFY_DIR/assets/NOTICE.md"; \ +test -f "$$VERIFY_DIR/ui/bundle.js"; \ +test -f "$$VERIFY_DIR/ui/plugin.css"; \ +test -f "$$VERIFY_DIR/checksums.txt"; \ +grep -Eq '^[0-9a-f]{64} assets/icon\.svg$$' "$$VERIFY_DIR/checksums.txt"; \ +(cd "$$VERIFY_DIR" && sha256sum -c checksums.txt); \ +$(1) +endef + +verify-package: + @$(call verify_package_archive,for executable in server/plugin-linux-amd64 server/plugin-linux-arm64 server/plugin-darwin-amd64 server/plugin-darwin-arm64 server/plugin-windows-amd64.exe; do test -f "$$VERIFY_DIR/$$executable" || { echo "package missing $$executable"; exit 1; }; done) + +verify-package-host: + @$(call verify_package_archive,test -f "$$VERIFY_DIR/server/plugin-$$(go env GOOS)-$$(go env GOARCH)$$(go env GOEXE)" || { echo "package missing host executable"; exit 1; }) + clean: rm -rf bin $(STAGE) kandev-plugin-github-status-*.tar.gz diff --git a/README.md b/README.md index 9dd4edb..3241a81 100644 --- a/README.md +++ b/README.md @@ -203,15 +203,15 @@ Built against a sibling checkout of the kandev monorepo — `go.mod` has expects `../kandev` next to it. ```bash -go test ./server/... # 59 test functions, 79 runs incl. subtests +go test ./server/... # 60 test functions, 80 runs incl. subtests go vet ./server/... gofmt -l . -make package-host # host platform only, fast local loop -make package # all five platforms in manifest.yaml +make package-host verify-package-host # host platform only, fast local loop +make package verify-package # all five manifest platforms ``` CI (`.github/workflows/ci.yml`) runs tidy + gofmt + vet + test on every PR, -and `build.yml` packages all five platforms. Both check out `kdlbs/kandev` +and `build.yml` packages and verifies all five platforms. Both check out `kdlbs/kandev` as a sibling so the `replace` in `go.mod` resolves. `release.yml` is manual (`workflow_dispatch` with a patch/minor/major bump, or a `v*` tag push): it syncs `manifest.yaml`/`Makefile`/README, writes `CHANGELOG.md`, tags, then diff --git a/assets/NOTICE.md b/assets/NOTICE.md new file mode 100644 index 0000000..068a381 --- /dev/null +++ b/assets/NOTICE.md @@ -0,0 +1,10 @@ +# Marketplace icon notice + +`icon.svg` is an original Kandev integration asset. Its generic service-health +pulse motif does not reproduce or incorporate GitHub, Invertocat, or Octocat +logo artwork. + +GITHUB, the GITHUB logo design, INVERTOCAT, OCTOCAT, and the OCTOCAT logo design +are trademarks of GitHub, Inc., registered in the United States and other +countries. This plugin is not affiliated with, sponsored by, or endorsed by +GitHub. diff --git a/assets/icon.svg b/assets/icon.svg new file mode 100644 index 0000000..e3be330 --- /dev/null +++ b/assets/icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/manifest.yaml b/manifest.yaml index 642a1ed..97d1380 100644 --- a/manifest.yaml +++ b/manifest.yaml @@ -6,6 +6,7 @@ description: "Is it me or is it GitHub? A quiet status-bar chip that turns loud author: "kandev" categories: ["tools"] repo_url: "https://github.com/kdlbs/kandev-plugin-github-status" +icon: "assets/icon.svg" runtime: type: binary diff --git a/server/manifest_test.go b/server/manifest_test.go new file mode 100644 index 0000000..943ea5c --- /dev/null +++ b/server/manifest_test.go @@ -0,0 +1,60 @@ +package main + +import ( + "encoding/xml" + "os" + "path/filepath" + "strings" + "testing" +) + +func TestManifestIncludesPackagedMarketplaceIcon(t *testing.T) { + contents, err := os.ReadFile("../manifest.yaml") + if err != nil { + t.Fatal(err) + } + + iconPath := manifestIconPath(string(contents)) + if iconPath != "assets/icon.svg" { + t.Fatalf("manifest icon = %q, want %q", iconPath, "assets/icon.svg") + } + + icon, err := os.ReadFile(filepath.Join("..", filepath.FromSlash(iconPath))) + if err != nil { + t.Fatal(err) + } + assertMarketplaceSVG(t, icon) +} + +func manifestIconPath(manifest string) string { + for _, line := range strings.Split(manifest, "\n") { + if strings.HasPrefix(line, "icon:") { + return strings.Trim(strings.TrimSpace(strings.TrimPrefix(line, "icon:")), `"`) + } + } + return "" +} + +func assertMarketplaceSVG(t *testing.T, icon []byte) { + t.Helper() + + var root struct { + XMLName xml.Name + Width string `xml:"width,attr"` + Height string `xml:"height,attr"` + ViewBox string `xml:"viewBox,attr"` + } + if err := xml.Unmarshal(icon, &root); err != nil { + t.Fatal(err) + } + if root.XMLName.Local != "svg" || root.Width != "128" || root.Height != "128" || root.ViewBox != "0 0 128 128" { + t.Fatalf("unexpected SVG root: name=%q width=%q height=%q viewBox=%q", root.XMLName.Local, root.Width, root.Height, root.ViewBox) + } + + lower := strings.ToLower(string(icon)) + for _, forbidden := range []string{" Date: Sun, 16 Aug 2026 17:10:09 +0100 Subject: [PATCH 2/2] fix: make package verification fail closed --- Makefile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 71ec05f..240205c 100644 --- a/Makefile +++ b/Makefile @@ -51,6 +51,7 @@ package-host: ## Verify the generated archive, including the manifest-declared marketplace ## icon and plugin-pack's checksums, before it reaches the host installer. define verify_package_archive +set -eu; \ VERIFY_DIR="$$(mktemp -d)"; \ trap 'rm -rf "$$VERIFY_DIR"' EXIT; \ test -f "$(PKG_OUT)" || { echo "package not found: $(PKG_OUT)"; exit 1; }; \ @@ -63,7 +64,11 @@ test -f "$$VERIFY_DIR/ui/bundle.js"; \ test -f "$$VERIFY_DIR/ui/plugin.css"; \ test -f "$$VERIFY_DIR/checksums.txt"; \ grep -Eq '^[0-9a-f]{64} assets/icon\.svg$$' "$$VERIFY_DIR/checksums.txt"; \ -(cd "$$VERIFY_DIR" && sha256sum -c checksums.txt); \ +if command -v sha256sum >/dev/null 2>&1; then \ + (cd "$$VERIFY_DIR" && sha256sum -c checksums.txt); \ +else \ + (cd "$$VERIFY_DIR" && shasum -a 256 -c checksums.txt); \ +fi; \ $(1) endef