diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 8db837c..6dbbb2e 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,30 +1,17 @@ version: 2 updates: - # Library dependencies - package-ecosystem: npm - directory: / + directory: "/" schedule: interval: weekly + target-branch: develop open-pull-requests-limit: 5 - groups: - minor-and-patch: - update-types: [minor, patch] - - # Example app dependencies - - package-ecosystem: npm - directory: /example - schedule: - interval: weekly - open-pull-requests-limit: 5 - groups: - minor-and-patch: - update-types: [minor, patch] - - # GitHub Actions versions (also bumps the deprecated Node-20 actions) + commit-message: + prefix: "build(deps)" - package-ecosystem: github-actions - directory: / + directory: "/" schedule: interval: weekly - groups: - actions: - patterns: ['*'] + target-branch: develop + commit-message: + prefix: "ci(deps)" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..23b3f60 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,36 @@ +name: ci + +on: + pull_request: + branches: [develop, main] + +permissions: + contents: read + +jobs: + ci: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v2 + - run: bun install --frozen-lockfile + - run: bun run lint + - run: bun run typecheck + - run: bun run build + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v2 + - run: bun install --frozen-lockfile + - run: bun run test + gitleaks: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: gitleaks/gitleaks-action@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }} diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml new file mode 100644 index 0000000..01b238e --- /dev/null +++ b/.github/workflows/commitlint.yml @@ -0,0 +1,17 @@ +name: commitlint + +on: + pull_request: + branches: [develop, main] + +permissions: + contents: read + +jobs: + commitlint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: wagoid/commitlint-github-action@v6 diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index a846020..42ef97e 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -1,8 +1,5 @@ name: release-please -# Maintains a release PR (version bump in package.json + CHANGELOG.md from -# Conventional Commits). Merging that PR creates a GitHub Release + tag, which -# triggers release.yml to publish to npm. on: push: branches: [main] @@ -11,14 +8,12 @@ permissions: contents: write pull-requests: write -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - jobs: release-please: runs-on: ubuntu-latest steps: - uses: googleapis/release-please-action@v4 with: + target-branch: main config-file: release-please-config.json manifest-file: .release-please-manifest.json diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ba46458..5a7c171 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,6 +16,10 @@ jobs: publish: needs: test runs-on: ubuntu-latest + environment: production + concurrency: + group: deploy-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false permissions: contents: read steps: diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 37fcefa..5fdd883 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.0.0" + ".": "1.1.0" } diff --git a/README.md b/README.md index c11e64c..881ca86 100644 --- a/README.md +++ b/README.md @@ -26,11 +26,15 @@ and produces payloads **~3Γ— smaller than JSON**. See [PERFORMANCE.md](./PERFORM automatically; override per field or globally only when you want to. - πŸ”’ **Generated TypeScript types** - a typed `Message.encode/decode` object per message plus a generic `encode`/`decode`, fully inferred. +- 🧩 **Broad schema coverage** - proto3 + proto2, every scalar type, `enum`, + nested messages, `repeated`, **`oneof`**, **`map`**. - πŸ•’ **Well-known types** - `Timestamp`, `Duration`, `Empty`, `FieldMask` and the scalar wrappers, with natural JS mapping (`Timestamp` ⇄ `Date`, `Duration` ⇄ ms). -- πŸ‘€ **Watch mode** - `generate --watch` regenerates on `.proto` changes. -- πŸ“± **iOS & Android**, New Architecture. -- 🧩 **Expo config plugin** - regenerates on `expo prebuild`. +- 🧰 **Runtime helpers** - `byteLength()`, field reflection, canonical proto3 + JSON (`toJson`/`fromJson`), and typed `ProtobufError`s. +- πŸ”’ **Type options** - `bigint` for 64-bit fields, string-literal enums. +- πŸ‘€ **Watch mode** + **Metro plugin** - regenerate on `.proto` changes / Metro start. +- πŸ“± **iOS & Android**, New Architecture Β· 🧩 **Expo config plugin**. ## Requirements @@ -174,6 +178,7 @@ Options: --protoc Use a specific protoc (default: bundled) --nanopb Use a specific protoc-gen-nanopb (default: auto-installed) --strict Require explicit .options for every static field + --skipProtoc Generate the registry + TS types only (no nanopb sources) --bigint Type 64-bit fields as bigint (default: decimal string) --enums Enum representation: "string" or "number" (default) --watch, -w Regenerate on .proto changes (debounced) @@ -357,8 +362,7 @@ Deprecations are announced in the changelog and kept for at least one minor release before removal in the next major. The protobuf **wire format is stable** (it is standard protobuf); upgrades never change bytes on the wire for a given `.proto`. Releases use [Conventional Commits](https://www.conventionalcommits.org) - -- release-please (see [Releasing](#releasing)). +(see [Releasing](#releasing)). ## Errors & validation @@ -432,18 +436,24 @@ CI (`.github/workflows/test.yml`) runs typecheck, build, and the test suite ## Releasing -Releases are automated with [Conventional Commits](https://www.conventionalcommits.org) -and [release-please](https://github.com/googleapis/release-please): +Publishing is automated by `release.yml`, which runs the full test suite and then +`npm publish` on every **published GitHub Release**: + +1. Bump `version` in `package.json` (Conventional Commits: `feat:`/`fix:` β†’ + minor/patch) and land it on `main`. +2. Create a GitHub Release with the matching tag (`vX.Y.Z`): + `gh release create vX.Y.Z --target main --notes "…"`. +3. `release.yml` runs CI (lint + tests + ASan/UBSan fuzz), then publishes + `@klaappinc/react-native-nitro-protobuf` to npm. -1. Land changes on `main` using Conventional Commit messages (`feat:`, `fix:`, …). -2. release-please keeps an open "release" PR that bumps the version and updates - `CHANGELOG.md`. Review and merge it when you want to ship. -3. Merging creates a GitHub Release + tag, which triggers `release.yml` to run the - test suite and publish `@klaappinc/react-native-nitro-protobuf` to npm. +> A [release-please](https://github.com/googleapis/release-please) workflow is +> also configured to draft the release PR automatically, but it needs the org +> setting **β€œAllow GitHub Actions to create pull requests”** enabled; until then, +> cut releases manually as above. -One-time setup: add an npm automation token as the `NPM_TOKEN` repository secret -(Settings β†’ Secrets and variables β†’ Actions) with publish rights to the -`@klaappinc` scope. +One-time setup: add an npm **automation** token (2FA-exempt) as the `NPM_TOKEN` +repository secret (Settings β†’ Secrets and variables β†’ Actions) with publish +rights to the `@klaappinc` scope. ## Contributing diff --git a/commitlint.config.mjs b/commitlint.config.mjs new file mode 100644 index 0000000..0616fb9 --- /dev/null +++ b/commitlint.config.mjs @@ -0,0 +1,3 @@ +export default { + extends: ['@commitlint/config-conventional'], +}; diff --git a/generated/nitro_protobuf_registry.cpp b/generated/nitro_protobuf_registry.cpp index d4e5fd6..a264561 100644 --- a/generated/nitro_protobuf_registry.cpp +++ b/generated/nitro_protobuf_registry.cpp @@ -1,4 +1,9 @@ -// This file is auto-generated. Do not edit. +/// +/// nitro_protobuf_registry.cpp +/// This file was generated by react-native-nitro-protobuf. DO NOT MODIFY THIS FILE. +/// https://github.com/KlaappInc/react-native-nitro-protobuf +/// Copyright Β© Thomas Roux @ Klaapp (https://klaapp.io) +/// #include "../cpp/ProtobufRegistry.hpp" #include "example.pb.h" diff --git a/release-please-config.json b/release-please-config.json index fc28b4e..0827346 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -5,7 +5,8 @@ "release-type": "node", "package-name": "@klaappinc/react-native-nitro-protobuf", "changelog-path": "CHANGELOG.md", - "include-component-in-tag": false + "include-component-in-tag": false, + "include-v-in-tag": true } } } diff --git a/scripts/generate-protos.mjs b/scripts/generate-protos.mjs index 988b8ad..e7ae722 100755 --- a/scripts/generate-protos.mjs +++ b/scripts/generate-protos.mjs @@ -13,6 +13,18 @@ const moduleRoot = path.resolve(__dirname, '..') // nanopb generator must match the vendored runtime in cpp/nanopb. const NANOPB_VERSION = '0.4.9.1' +// Signature stamped onto every file this generator emits (nitrogen/margelo style). +function signedHeader(filename, p = '//') { + return [ + p, + `${p} ${filename}`, + `${p} This file was generated by react-native-nitro-protobuf. DO NOT MODIFY THIS FILE.`, + `${p} https://github.com/KlaappInc/react-native-nitro-protobuf`, + `${p} Copyright Β© Thomas Roux @ Klaapp (https://klaapp.io)`, + p, + ].join('\n') +} + function findExecutable(name) { const exts = process.platform === 'win32' ? ['.exe', '.cmd', '.bat', ''] : [''] @@ -557,7 +569,7 @@ function tsFieldType(field, opts = {}) { // facade. Timestamp/Duration fields are converted to/from Date|ISO / ms via a // spec-driven recursive transform. function generateTypes(messages, opts = {}) { - const out = ['// Auto-generated by react-native-nitro-protobuf. Do not edit.'] + const out = [signedHeader('nitro-protobuf.ts'), ''] out.push( 'import {', ' NitroProtobuf,', @@ -1122,7 +1134,7 @@ async function runGenerate(args) { ]) const lines = [] - lines.push('// This file is auto-generated. Do not edit.') + lines.push(signedHeader('nitro_protobuf_registry.cpp', '///')) lines.push('#include "../cpp/ProtobufRegistry.hpp"') headerIncludes.forEach((header) => { lines.push(`#include "${header}"`)