diff --git a/.github/workflows/publish-jsr.yml b/.github/workflows/publish-jsr.yml index 63df8cc..71bc996 100644 --- a/.github/workflows/publish-jsr.yml +++ b/.github/workflows/publish-jsr.yml @@ -3,7 +3,11 @@ name: Publish to JSR (@serial/cpp-bindings-linux) on: workflow_dispatch: inputs: {} + push: + branches: + - '*' + tags: - "v*" @@ -36,7 +40,7 @@ jobs: - name: Setup Deno uses: denoland/setup-deno@v2 with: - deno-version: "2.6.0" + deno-version: "2.x" - name: Configure CMake (Release) env: @@ -48,19 +52,22 @@ jobs: run: | cmake --build build --config Release - - name: Embed .so as JSON/base64 for JSR + - name: Embed binary as JSON/base64 for JSR run: | - deno run --allow-read --allow-write jsr/scripts/embed_so.ts \ - build/libcpp_bindings_linux.so \ - jsr/bin/x84_64.json \ + ./jsr/scripts/embed_binary.sh \ + ./build/libcpp_bindings_linux.so \ + ./jsr/package/bin \ linux-x86_64 + cp LICENSE ./jsr/package/LICENSE - - name: Publish package to JSR - working-directory: jsr + - name: Publish package to JSR (real) + if: github.ref_type == 'tag' + working-directory: jsr/package run: | - # CMake + embed_so.ts generate files (jsr/jsr.json + binaries/*.json), - # which makes the worktree dirty. Publishing is still deterministic because - # the workflow builds + generates in a single run. deno publish --allow-dirty - + - name: Publish package to JSR (dry-run) + if: github.ref_type != 'tag' + working-directory: jsr/package + run: | + deno publish --allow-dirty --dry-run diff --git a/.gitignore b/.gitignore index 63c68c9..05ff4e0 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ compile_commands.json # Generated files generated/ src/version_config.cpp +*.so # OS .DS_Store diff --git a/CMakeLists.txt b/CMakeLists.txt index 292fb97..5046ac8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,8 +26,8 @@ project( # Generate JSR package metadata from the same git-derived version as the library. # We generate into the build directory to avoid touching tracked files during normal local builds. configure_file( - "${CMAKE_SOURCE_DIR}/jsr/jsr.json.in" - "${CMAKE_SOURCE_DIR}/jsr/jsr.json" + "${CMAKE_SOURCE_DIR}/jsr/package/jsr.json.in" + "${CMAKE_SOURCE_DIR}/jsr/package/jsr.json" @ONLY ) diff --git a/jsr/bin/x84_64.json b/jsr/bin/x84_64.json deleted file mode 100644 index 4184085..0000000 --- a/jsr/bin/x84_64.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "target": "linux-x86_64", - "filename": "libcpp_bindings_linux.so", - "encoding": "base64", - "sha256": "", - "data": "" -} diff --git a/jsr/.gitignore b/jsr/package/.gitignore similarity index 100% rename from jsr/.gitignore rename to jsr/package/.gitignore diff --git a/jsr/README.md b/jsr/package/README.md similarity index 71% rename from jsr/README.md rename to jsr/package/README.md index 34ec660..4782e52 100644 --- a/jsr/README.md +++ b/jsr/package/README.md @@ -8,7 +8,7 @@ blob**. Import the JSON and write the `.so` to disk (consumer project example): ```ts -import blob from "@serial/cpp-bindings-linux/x84_64" with { type: "json" }; +import blob from "@serial/cpp-bindings-linux/bin/x84_64" with { type: "json" }; const bytes = new TextEncoder().encode(atob(blob.data)); @@ -17,8 +17,3 @@ Deno.writeFileSync(tempFilePath, bytes, { mode: 0o755 }); // Now you can open the binary using for example `Deno.dlopen` ``` - -## License - -This package is licensed under **LGPL-3.0-only** (see the repository root -`LICENSE`). diff --git a/jsr/jsr.json.in b/jsr/package/jsr.json.in similarity index 80% rename from jsr/jsr.json.in rename to jsr/package/jsr.json.in index 3e8c0b5..0cbcaf8 100644 --- a/jsr/jsr.json.in +++ b/jsr/package/jsr.json.in @@ -1,14 +1,15 @@ { "name": "@serial/cpp-bindings-linux", "version": "@PROJECT_VERSION@", - "license": "LGPL-3.0-only", + "license": "LICENSE", "description": "Linux shared-library bindings for Serial-IO/cpp-core (distributed via JSON/base64 because JSR has limited binary support).", "exports": { - "./x84_64": "./bin/x84_64.json" + "./bin/x86_64": "./bin/x86_64.json" }, "publish": { "include": [ "README.md", + "LICENSE", "jsr.json", "bin/**" ] diff --git a/jsr/scripts/embed_binary.sh b/jsr/scripts/embed_binary.sh new file mode 100755 index 0000000..2f500dd --- /dev/null +++ b/jsr/scripts/embed_binary.sh @@ -0,0 +1,35 @@ +#!/bin/sh +set -eu + +if [ "$#" -ne 3 ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +BINARY_PATH=$1 +JSR_BIN_PATH=$2 +TARGET=$3 + +if [ ! -f "$BINARY_PATH" ]; then + echo "Error: Binary path is not a file: $BINARY_PATH" >&2 + exit 1 +fi + +FILENAME=$(basename "$BINARY_PATH") + +mkdir -p "$JSR_BIN_PATH" + +cp "$BINARY_PATH" "$JSR_BIN_PATH/x86_64.so" + +BASE64_DATA=$(base64 "$BINARY_PATH" | tr -d '\n') + +jq -n \ + --arg target "$TARGET" \ + --arg filename "$FILENAME" \ + --arg data "$BASE64_DATA" \ + '{ + target: $target, + filename: $filename, + encoding: "base64", + data: $data + }' > "$JSR_BIN_PATH/x86_64.json" diff --git a/jsr/scripts/embed_so.ts b/jsr/scripts/embed_so.ts deleted file mode 100644 index 81cdafe..0000000 --- a/jsr/scripts/embed_so.ts +++ /dev/null @@ -1,51 +0,0 @@ -// Usage: -// deno run --allow-read --allow-write jsr/scripts/embed_so.ts \ -// ./build/libcpp_bindings_linux.so ./jsr/bin/x84_64.json linux-x86_64 -// -// This converts the shared library into a JSON file containing base64 data for publishing to JSR. - -function bytesToHex(bytes: Uint8Array): string { - return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join(""); -} - -function base64FromBytes(bytes: Uint8Array): string { - // Chunk to avoid call stack limits in String.fromCharCode(...bigArray) - const chunkSize = 0x8000; - let binary = ""; - for (let i = 0; i < bytes.length; i += chunkSize) { - const chunk = bytes.subarray(i, i + chunkSize); - binary += String.fromCharCode(...chunk); - } - return btoa(binary); -} - -if (import.meta.main) { - const [inPath, outPath, target = "linux-x86_64"] = Deno.args; - if (!inPath || !outPath) { - console.error( - "Expected: [target]\nExample: build/libcpp_bindings_linux.so jsr/bin/x84_64.json linux-x86_64", - ); - Deno.exit(2); - } - - const bytes = await Deno.readFile(inPath); - const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", bytes)); - const sha256 = bytesToHex(digest); - - const filename = outPath.endsWith(".json") - ? (target === "linux-x86_64" - ? "libcpp_bindings_linux.so" - : "libcpp_bindings_linux.so") - : "libcpp_bindings_linux.so"; - - const payload = { - target, - filename, - encoding: "base64" as const, - sha256, - data: base64FromBytes(bytes), - }; - - await Deno.writeTextFile(outPath, JSON.stringify(payload)); - console.log(`Wrote ${outPath} (${bytes.length} bytes, sha256=${sha256})`); -}