diff --git a/.github/actionlint.yaml b/.github/actionlint.yaml index dd1dc08..9d35771 100644 --- a/.github/actionlint.yaml +++ b/.github/actionlint.yaml @@ -1,6 +1,5 @@ # actionlint configuration # https://github.com/rhysd/actionlint/blob/main/docs/config.md - self-hosted-runner: labels: # GitHub-hosted larger runners (not in actionlint's default list) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0254b51..c389657 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,44 +1,34 @@ name: ci - on: push: branches: ["main"] pull_request: - jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable with: components: rustfmt, clippy - - name: Install cargo-deny run: cargo install cargo-deny@0.18.3 --locked - - name: Install cbindgen run: cargo install cbindgen --locked - - name: Quality gates run: make check - - name: Verify generated C header is up to date run: | make cbindgen git diff --exit-code ffi/docprims-ffi/docprims.h diff -u ffi/docprims-ffi/docprims.h bindings/go/docprims/include/docprims.h - test-go-vendored: runs-on: macos-14 steps: - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 with: go-version: "1.22.x" - - name: Run Go tests (vendored libs) run: | cd bindings/go/docprims diff --git a/.github/workflows/go-bindings-prep.yml b/.github/workflows/go-bindings-prep.yml index 080517e..f771277 100644 --- a/.github/workflows/go-bindings-prep.yml +++ b/.github/workflows/go-bindings-prep.yml @@ -1,8 +1,6 @@ name: Go Bindings (Prep) - # NOTE: Windows arm64 is NOT supported for Go bindings. # Go's CGo on Windows requires MinGW, and MinGW does not support the arm64 target. - on: workflow_dispatch: inputs: @@ -10,10 +8,8 @@ on: description: "Release version (e.g. 0.1.0)" required: true type: string - env: CARGO_TERM_COLOR: always - jobs: validate: name: Validate Inputs @@ -22,7 +18,6 @@ jobs: version: ${{ steps.version.outputs.version }} steps: - uses: actions/checkout@v4 - - name: Validate version matches VERSION file id: version run: | @@ -35,7 +30,6 @@ jobs: exit 1 fi echo "version=$FILE_VERSION" >> "$GITHUB_OUTPUT" - build-linux: name: Build FFI (Linux) runs-on: ubuntu-latest @@ -60,7 +54,6 @@ jobs: artifact_suffix: linux-arm64-musl steps: - uses: actions/checkout@v4 - - name: Verify cargo-zigbuild is available shell: bash run: | @@ -71,7 +64,6 @@ jobs: exit 1 fi cargo-zigbuild --version - - name: Build FFI shell: bash run: | @@ -81,7 +73,6 @@ jobs: else cargo zigbuild --release --target ${{ matrix.target }} -p docprims-ffi fi - - name: Package FFI shell: bash run: | @@ -113,13 +104,11 @@ jobs: TARGET_DIR="$(resolve_target libdocprims_ffi.so)" cp "target/${TARGET_DIR}/release/libdocprims_ffi.so" "dist/ffi/${{ matrix.artifact_suffix }}/" fi - - name: Upload FFI artifact uses: actions/upload-artifact@v4 with: name: ffi-${{ matrix.artifact_suffix }} path: dist/ffi/${{ matrix.artifact_suffix }} - build-macos: name: Build FFI (macOS) runs-on: macos-14 @@ -128,16 +117,13 @@ jobs: MACOSX_DEPLOYMENT_TARGET: "11.0" steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable with: targets: aarch64-apple-darwin - - name: Build FFI (arm64) run: | set -euo pipefail cargo build --release --target aarch64-apple-darwin -p docprims-ffi - - name: Package FFI run: | set -euo pipefail @@ -148,24 +134,20 @@ jobs: # Fix dylib install_name to use @rpath instead of hardcoded CI build path. # This allows consumers to set rpath or place dylib alongside executable. install_name_tool -id "@rpath/libdocprims_ffi.dylib" dist/ffi/darwin-arm64/libdocprims_ffi.dylib - - name: Upload FFI artifact (darwin-arm64) uses: actions/upload-artifact@v4 with: name: ffi-darwin-arm64 path: dist/ffi/darwin-arm64 - build-windows: name: Build FFI (Windows GNU) runs-on: windows-latest needs: validate steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable with: targets: x86_64-pc-windows-gnu - - name: Install MinGW uses: msys2/setup-msys2@v2 with: @@ -173,13 +155,11 @@ jobs: update: true install: mingw-w64-x86_64-gcc path-type: inherit - - name: Build FFI (GNU target) shell: msys2 {0} run: | set -euo pipefail cargo build --release --target x86_64-pc-windows-gnu -p docprims-ffi - - name: Package FFI shell: bash run: | @@ -188,13 +168,11 @@ jobs: cp target/x86_64-pc-windows-gnu/release/libdocprims_ffi.a dist/ffi/windows-amd64/ cp target/x86_64-pc-windows-gnu/release/docprims_ffi.dll dist/ffi/windows-amd64/ cp target/x86_64-pc-windows-gnu/release/libdocprims_ffi.dll.a dist/ffi/windows-amd64/ - - name: Upload FFI artifact uses: actions/upload-artifact@v4 with: name: ffi-windows-amd64 path: dist/ffi/windows-amd64 - cbindgen: name: Generate C Header runs-on: ubuntu-latest @@ -204,19 +182,16 @@ jobs: needs: validate steps: - uses: actions/checkout@v4 - - name: Generate C header shell: bash run: | set -euo pipefail cbindgen --config cbindgen.toml --crate docprims-ffi --output docprims.h - - name: Upload header uses: actions/upload-artifact@v4 with: name: c-header path: docprims.h - update-go-bindings: name: Create Go Bindings PR runs-on: ubuntu-latest @@ -228,19 +203,16 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Download all FFI artifacts uses: actions/download-artifact@v4.1.3 with: pattern: ffi-* path: ffi-libs - - name: Download C header uses: actions/download-artifact@v4.1.3 with: name: c-header path: ffi-libs - - name: Update Go bindings directory run: | set -euo pipefail @@ -285,7 +257,6 @@ jobs: echo "Go bindings updated for $VERSION" ls -la bindings/go/docprims/lib/*/ || true - - name: Create Pull Request uses: peter-evans/create-pull-request@v6 with: diff --git a/.github/workflows/go-bindings.yml b/.github/workflows/go-bindings.yml index 07c177f..f4639f8 100644 --- a/.github/workflows/go-bindings.yml +++ b/.github/workflows/go-bindings.yml @@ -1,85 +1,66 @@ name: go-bindings - on: pull_request: workflow_dispatch: - jobs: go-test-linux-amd64: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable with: toolchain: 1.88.0 - - uses: actions/setup-go@v5 with: go-version: "1.22.x" - - name: Install cbindgen run: cargo install cbindgen --locked - - name: Build FFI and run Go tests (glibc) run: | make go-test make go-test-shared - go-test-darwin-arm64-vendored: runs-on: macos-14 steps: - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 with: go-version: "1.22.x" - - name: Run Go tests (vendored darwin-arm64) run: | cd bindings/go/docprims go test -v ./... - go-test-darwin-arm64-shared: runs-on: macos-14 steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable with: toolchain: 1.88.0 - - uses: actions/setup-go@v5 with: go-version: "1.22.x" - - name: Install cbindgen run: cargo install cbindgen --locked - - name: Build FFI and run Go tests (shared) run: make go-test-shared - go-test-linux-amd64-musl: if: github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable with: toolchain: 1.88.0 - - uses: actions/setup-go@v5 with: go-version: "1.22.x" - - name: Install toolchain run: | sudo apt-get update sudo apt-get install -y musl-tools rustup target add x86_64-unknown-linux-musl cargo install cbindgen --locked - - name: Build musl static lib and run Go tests run: | make cbindgen diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b10ca95..26a86bf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,13 +1,10 @@ name: Release - on: push: tags: ['v*'] - env: CARGO_TERM_COLOR: always MACOSX_DEPLOYMENT_TARGET: "11.0" - jobs: validate-version: name: Validate Version @@ -16,7 +13,6 @@ jobs: version: ${{ steps.version.outputs.version }} steps: - uses: actions/checkout@v4 - - name: Validate VERSION matches tag id: version run: | @@ -28,14 +24,12 @@ jobs: fi echo "version=$FILE_VERSION" >> "$GITHUB_OUTPUT" echo "Version validated: $FILE_VERSION" - verify-go-bindings-assets: name: Verify Go Binding Assets runs-on: ubuntu-latest needs: validate-version steps: - uses: actions/checkout@v4 - - name: Verify Go module contains prebuilt libs run: | set -euo pipefail @@ -93,7 +87,6 @@ jobs: done echo "Go binding assets verified." - build-linux: name: Build Linux runs-on: ubuntu-latest @@ -118,7 +111,6 @@ jobs: artifact_suffix: linux-arm64-musl steps: - uses: actions/checkout@v4 - - name: Build CLI run: | if [ -n "${{ matrix.glibc }}" ]; then @@ -126,7 +118,6 @@ jobs: else cargo zigbuild --release --target ${{ matrix.target }} -p docprims-cli fi - - name: Package CLI shell: bash run: | @@ -156,13 +147,11 @@ jobs: cp LICENSE-MIT LICENSE-APACHE dist/cli/ VERSION=${{ needs.validate-version.outputs.version }} tar -czf docprims-${VERSION}-${{ matrix.artifact_suffix }}.tar.gz -C dist/cli . - - name: Upload CLI artifact uses: actions/upload-artifact@v4 with: name: cli-${{ matrix.artifact_suffix }} path: docprims-${{ needs.validate-version.outputs.version }}-${{ matrix.artifact_suffix }}.tar.gz - build-macos: name: Build macOS runs-on: macos-14 @@ -171,38 +160,31 @@ jobs: MACOSX_DEPLOYMENT_TARGET: "11.0" steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable with: targets: aarch64-apple-darwin - - name: Build CLI run: cargo build --release --target aarch64-apple-darwin -p docprims-cli - - name: Package CLI run: | mkdir -p dist/cli cp target/aarch64-apple-darwin/release/docprims dist/cli/docprims cp LICENSE-MIT LICENSE-APACHE dist/cli/ tar -czf docprims-${{ needs.validate-version.outputs.version }}-darwin-arm64.tar.gz -C dist/cli . - - name: Upload CLI artifact uses: actions/upload-artifact@v4 with: name: cli-darwin-arm64 path: docprims-${{ needs.validate-version.outputs.version }}-darwin-arm64.tar.gz - build-windows: name: Build Windows runs-on: windows-latest needs: validate-version steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable with: targets: x86_64-pc-windows-gnu - - name: Install MinGW uses: msys2/setup-msys2@v2 with: @@ -210,11 +192,9 @@ jobs: update: true install: mingw-w64-x86_64-gcc path-type: inherit - - name: Build CLI (GNU target) shell: msys2 {0} run: cargo build --release --target x86_64-pc-windows-gnu -p docprims-cli - - name: Package CLI shell: bash run: | @@ -222,13 +202,11 @@ jobs: cp target/x86_64-pc-windows-gnu/release/docprims.exe dist/cli/docprims.exe cp LICENSE-MIT LICENSE-APACHE dist/cli/ cd dist/cli && 7z a -tzip ../../docprims-${{ needs.validate-version.outputs.version }}-windows-amd64.zip . - - name: Upload CLI artifact uses: actions/upload-artifact@v4 with: name: cli-windows-amd64 path: docprims-${{ needs.validate-version.outputs.version }}-windows-amd64.zip - cbindgen: name: Generate C Header runs-on: ubuntu-latest @@ -238,16 +216,13 @@ jobs: needs: validate-version steps: - uses: actions/checkout@v4 - - name: Generate C header run: cbindgen --config cbindgen.toml --crate docprims-ffi --output docprims.h - - name: Upload header uses: actions/upload-artifact@v4 with: name: c-header path: docprims.h - publish: name: Publish Release runs-on: ubuntu-latest @@ -262,19 +237,16 @@ jobs: contents: write steps: - uses: actions/checkout@v4 - - name: Download all CLI artifacts uses: actions/download-artifact@v4.1.3 with: pattern: cli-* path: release-cli - - name: Download C header uses: actions/download-artifact@v4.1.3 with: name: c-header path: release-header - - name: Organize release files run: | mkdir -p release @@ -289,7 +261,6 @@ jobs: cp LICENSE-MIT LICENSE-APACHE release/ ls -la release/ - - name: Create Release uses: softprops/action-gh-release@v2 with: diff --git a/.github/workflows/typescript-bindings.yml b/.github/workflows/typescript-bindings.yml index c98c07a..e870d28 100644 --- a/.github/workflows/typescript-bindings.yml +++ b/.github/workflows/typescript-bindings.yml @@ -1,10 +1,8 @@ name: TypeScript Bindings - on: push: branches: ["main"] pull_request: - jobs: test-typescript: name: Test TypeScript Bindings @@ -15,25 +13,20 @@ jobs: runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 with: node-version: "20" - - uses: dtolnay/rust-toolchain@stable - - name: Install deps and run tests run: | cd bindings/typescript/docprims npm install npm run test:ci - test-typescript-alpine: name: Test TypeScript Bindings (Alpine/musl) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Run tests in node:20-alpine run: | docker run --rm \ diff --git a/.github/workflows/typescript-napi-prebuilds.yml b/.github/workflows/typescript-napi-prebuilds.yml index c3c1b05..f720ef0 100644 --- a/.github/workflows/typescript-napi-prebuilds.yml +++ b/.github/workflows/typescript-napi-prebuilds.yml @@ -1,31 +1,24 @@ name: TypeScript N-API Prebuilds - on: workflow_dispatch: - env: CARGO_TERM_COLOR: always - jobs: build-linux: name: Build Linux prebuilds runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Setup Node uses: actions/setup-node@v4 with: node-version: '20' - - name: Install Rust uses: dtolnay/rust-toolchain@stable - - name: Install Zig uses: goto-bus-stop/setup-zig@v2 with: version: 0.12.0 - - name: Install Rust targets shell: bash run: | @@ -34,11 +27,9 @@ jobs: rustup target add \ x86_64-unknown-linux-musl \ aarch64-unknown-linux-musl - - name: Install Node dependencies working-directory: bindings/typescript/docprims run: npm install - - name: Build .node files (zig) working-directory: bindings/typescript/docprims shell: bash @@ -61,31 +52,25 @@ jobs: npx napi build --cargo-cwd native --release --platform --js false --strip \ --target aarch64-unknown-linux-musl --zig mv docprims.linux-arm64-musl.node prebuilds/ - - name: Upload prebuilds uses: actions/upload-artifact@v4 with: name: ts-prebuilds-linux path: bindings/typescript/docprims/prebuilds/*.node - build-linux-arm64: name: Build Linux arm64 prebuild (native) runs-on: ubuntu-latest-arm64-s steps: - uses: actions/checkout@v4 - - name: Setup Node uses: actions/setup-node@v4 with: node-version: '20' - - name: Install Rust uses: dtolnay/rust-toolchain@stable - - name: Install Node dependencies working-directory: bindings/typescript/docprims run: npm install - - name: Build .node file working-directory: bindings/typescript/docprims shell: bash @@ -95,31 +80,25 @@ jobs: mkdir -p prebuilds npx napi build --cargo-cwd native --release --platform --js false --strip mv docprims.linux-arm64-gnu.node prebuilds/ - - name: Upload prebuild uses: actions/upload-artifact@v4 with: name: ts-prebuilds-linux-arm64-gnu-native path: bindings/typescript/docprims/prebuilds/*.node - build-macos-arm64: name: Build macOS arm64 prebuild runs-on: macos-14 steps: - uses: actions/checkout@v4 - - name: Setup Node uses: actions/setup-node@v4 with: node-version: '20' - - name: Install Rust uses: dtolnay/rust-toolchain@stable - - name: Install Node dependencies working-directory: bindings/typescript/docprims run: npm install - - name: Build .node file working-directory: bindings/typescript/docprims shell: bash @@ -129,31 +108,25 @@ jobs: mkdir -p prebuilds npx napi build --cargo-cwd native --release --platform --js false --strip mv docprims.darwin-arm64.node prebuilds/ - - name: Upload prebuild uses: actions/upload-artifact@v4 with: name: ts-prebuilds-darwin-arm64 path: bindings/typescript/docprims/prebuilds/*.node - build-windows: name: Build Windows x64 prebuild runs-on: windows-latest steps: - uses: actions/checkout@v4 - - name: Setup Node uses: actions/setup-node@v4 with: node-version: '20' - - name: Install Rust uses: dtolnay/rust-toolchain@stable - - name: Install Node dependencies working-directory: bindings/typescript/docprims run: npm install - - name: Build .node file working-directory: bindings/typescript/docprims shell: pwsh @@ -163,49 +136,40 @@ jobs: New-Item -ItemType Directory -Force -Path prebuilds | Out-Null npx napi build --cargo-cwd native --release --platform --js false --strip Move-Item docprims.win32-x64-msvc.node prebuilds/ - - name: Upload prebuild uses: actions/upload-artifact@v4 with: name: ts-prebuilds-win32-x64-msvc path: bindings/typescript/docprims/prebuilds/*.node - package: name: Package (npm dir) runs-on: ubuntu-latest needs: [build-linux, build-linux-arm64, build-macos-arm64, build-windows] steps: - uses: actions/checkout@v4 - - name: Setup Node uses: actions/setup-node@v4 with: node-version: '20' - - name: Install Rust uses: dtolnay/rust-toolchain@stable - - name: Install Node dependencies working-directory: bindings/typescript/docprims run: npm install - - name: Build JS dist (root package) working-directory: bindings/typescript/docprims run: npm run clean && npm run build && npm run build:native - - name: Remove local .node from root dist working-directory: bindings/typescript/docprims shell: bash run: | set -euo pipefail rm -f dist/native/docprims.*.node || true - - name: Download prebuilds uses: actions/download-artifact@v4 with: pattern: ts-prebuilds-* path: downloaded-prebuilds - - name: Create npm package templates working-directory: bindings/typescript/docprims shell: bash @@ -213,7 +177,6 @@ jobs: set -euo pipefail rm -rf npm npx napi create-npm-dir -t . - - name: Remove unsupported platform packages working-directory: bindings/typescript/docprims shell: bash @@ -223,7 +186,6 @@ jobs: # win32-arm64 is not supported (no Go bindings either) rm -rf npm/darwin-x64 npm/win32-arm64-msvc echo "Removed unsupported platforms: darwin-x64, win32-arm64-msvc" - - name: Stage .node files into npm packages working-directory: bindings/typescript/docprims shell: bash @@ -238,7 +200,6 @@ jobs: mkdir -p "$(dirname "$dest")" cp "$f" "$dest" done - - name: Upload npm dir uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/typescript-npm-publish.yml b/.github/workflows/typescript-npm-publish.yml index 85c6bdd..8344ca1 100644 --- a/.github/workflows/typescript-npm-publish.yml +++ b/.github/workflows/typescript-npm-publish.yml @@ -1,5 +1,4 @@ name: TypeScript npm Publish - # Publishes TypeScript bindings to npm using OIDC trusted publishing. # See ADR-0006 for setup and troubleshooting. # @@ -7,7 +6,6 @@ name: TypeScript npm Publish # - Package must already exist on npm (first publish is manual) # - Trusted publisher configured on npmjs.com for this workflow # - Prebuilds workflow must have completed successfully - on: workflow_dispatch: inputs: @@ -24,12 +22,10 @@ on: required: false type: boolean default: false - permissions: - id-token: write # Required for OIDC trusted publishing + id-token: write # Required for OIDC trusted publishing contents: read - actions: read # Required to download artifacts from other runs - + actions: read # Required to download artifacts from other runs jobs: publish: name: Publish to npm @@ -39,13 +35,11 @@ jobs: # Note: Do NOT use fetch-tags here. When running from a tag ref, # it causes a git fetch conflict. The tag is already checked out. - uses: actions/checkout@v4 - - name: Setup Node uses: actions/setup-node@v4 with: node-version: '20' registry-url: 'https://registry.npmjs.org' - - name: Ensure npm CLI supports OIDC run: | set -euo pipefail @@ -53,7 +47,6 @@ jobs: # Trusted publishing requires npm >= 11.5.1 npm install -g npm@11.5.1 echo "npm (after): $(npm --version)" - - name: Validate running from a tag ref run: | set -euo pipefail @@ -62,7 +55,6 @@ jobs: echo "Re-run it selecting the release tag in the GitHub UI, or trigger it by pushing a v* tag." exit 1 fi - - name: Validate release tag and versions run: | set -euo pipefail @@ -97,7 +89,6 @@ jobs: echo "head_sha=$(git rev-parse HEAD)" } >> "$GITHUB_ENV" echo "Validated tag/version: ${TAG}" - - name: Find prebuilds run id: find-run env: @@ -138,7 +129,6 @@ jobs: fi echo "run_id=${RUN_ID}" >> "$GITHUB_OUTPUT" - - name: Download npm directory artifact env: GH_TOKEN: ${{ github.token }} @@ -165,7 +155,6 @@ jobs: echo "Downloaded packages:" ls -la npm-packages/ - - name: Verify packages run: | set -euo pipefail @@ -206,7 +195,6 @@ jobs: done echo "All platform packages verified" - - name: Build root package working-directory: bindings/typescript/docprims run: | @@ -217,7 +205,6 @@ jobs: # 3. We only need devDependencies for TypeScript compilation npm install --omit=optional npm run build - - name: Publish (dry run) if: ${{ inputs.dry_run }} working-directory: bindings/typescript/docprims @@ -237,7 +224,6 @@ jobs: echo "Root package:" npm publish --access public --dry-run - - name: Publish platform packages if: ${{ !inputs.dry_run }} run: | @@ -254,7 +240,6 @@ jobs: echo "Publishing @3leaps/docprims-${pkg}..." (cd "$dir" && npm publish --access public) done - - name: Publish root package if: ${{ !inputs.dry_run }} working-directory: bindings/typescript/docprims @@ -265,7 +250,6 @@ jobs: printf '%s\n' 'registry=https://registry.npmjs.org/' 'always-auth=false' > "$NPM_CONFIG_USERCONFIG" echo "Publishing @3leaps/docprims..." npm publish --access public - - name: Verify publication if: ${{ !inputs.dry_run }} run: | diff --git a/.github/workflows/validate-release.yml b/.github/workflows/validate-release.yml index b6f6900..15fcb0a 100644 --- a/.github/workflows/validate-release.yml +++ b/.github/workflows/validate-release.yml @@ -1,5 +1,4 @@ name: Validate Release - on: workflow_dispatch: inputs: @@ -7,7 +6,6 @@ on: description: "Release tag to validate (e.g. v0.1.0)" required: true type: string - jobs: validate-go: name: Validate Go Bindings @@ -17,7 +15,6 @@ jobs: uses: actions/setup-go@v5 with: go-version: '1.22' - - name: Test Go module is fetchable run: | set -euo pipefail @@ -33,7 +30,6 @@ jobs: go get "${GO_MODULE}@${GO_VERSION}" echo "Go binding fetch: OK" - validate-typescript: name: Validate TypeScript Bindings (${{ matrix.os }}) runs-on: ${{ matrix.os }} @@ -47,19 +43,15 @@ jobs: - uses: actions/checkout@v4 with: ref: ${{ inputs.tag }} - - uses: actions/setup-node@v4 with: node-version: "20" - - uses: dtolnay/rust-toolchain@stable - - name: Build and test TypeScript bindings run: | cd bindings/typescript/docprims npm install npm run test:ci - validate-typescript-musl: name: Validate TypeScript Bindings (Alpine/musl) runs-on: ubuntu-latest @@ -69,13 +61,11 @@ jobs: - uses: actions/checkout@v4 with: ref: ${{ inputs.tag }} - - name: Install build dependencies shell: sh run: | set -eu apk add --no-cache bash curl build-base python3 git - - name: Install Rust (rustup) shell: bash run: | @@ -84,7 +74,6 @@ jobs: . "$HOME/.cargo/env" rustc -V cargo -V - - name: Build and test TypeScript bindings working-directory: bindings/typescript/docprims shell: bash diff --git a/.gitignore b/.gitignore index 91fd08e..8ccbfe5 100644 --- a/.gitignore +++ b/.gitignore @@ -12,11 +12,11 @@ Cargo.lock # macOS .DS_Store -# Planning docs (local only) -.plans/ - -# Local agent guidance -AGENTS.local.md +# Planning and local guidance +# Defense-in-depth only: these files live outside the repo tree during normal work. +# Root-anchored so they only match at the repository root. +/.plans/ +/AGENTS.local.md # Build artifacts /bin/ diff --git a/.goneat/assess.yaml b/.goneat/assess.yaml index cf82028..3e511ef 100644 --- a/.goneat/assess.yaml +++ b/.goneat/assess.yaml @@ -7,9 +7,7 @@ # Run with: goneat assess --categories format # goneat assess --categories lint # goneat assess --all - version: 1 - # Rust toolchain checks # goneat v0.5.x should support these - if not, fallback to Makefile rust: @@ -30,7 +28,6 @@ rust: - "ffi/**/*.rs" ignore: - "**/target/**" - format: # YAML formatting yamlfmt: @@ -41,7 +38,6 @@ format: - ".github/workflows/**/*.yaml" - ".goneat/**/*.yaml" ignore: [] - lint: # YAML linting (CI workflows, configs) yamllint: @@ -52,7 +48,6 @@ lint: - ".github/workflows/**/*.yaml" - ".goneat/**/*.yaml" ignore: [] - # Shell script linting shell: shfmt: @@ -68,7 +63,6 @@ lint: - "**/target/**" - "**/.git/**" - ".plans/**" - # GitHub Actions linting github_actions: actionlint: @@ -77,7 +71,6 @@ lint: - ".github/workflows/**/*.yml" - ".github/workflows/**/*.yaml" ignore: [] - # Makefile linting make: checkmake: diff --git a/.goneat/tools.yaml b/.goneat/tools.yaml index 45da2f4..25b8b60 100644 --- a/.goneat/tools.yaml +++ b/.goneat/tools.yaml @@ -12,7 +12,6 @@ # # Validate changes with: goneat doctor tools validate # See schema: schemas/tools/tools.v1.0.0.json - scopes: all: description: All tools from all scopes @@ -31,7 +30,6 @@ scopes: - cargo-audit - cargo-edit - cbindgen - foundation: description: Core foundation tools required for goneat and basic AI agent operation tools: @@ -43,7 +41,6 @@ scopes: - yamllint - prettier - actionlint - rust: description: Rust development and quality tools tools: @@ -51,18 +48,15 @@ scopes: - cargo-audit - cargo-edit - cbindgen - security: description: Security scanning tools tools: - cargo-audit - cargo-deny - tools: # ========================================================================== # Foundation Tools # ========================================================================== - checkmake: name: checkmake description: Makefile linter @@ -76,7 +70,6 @@ tools: installer_priority: all: - go-install - ripgrep: name: ripgrep description: Fast text search - used by goneat for license auditing @@ -94,7 +87,6 @@ tools: windows: - scoop - winget - jq: name: jq description: JSON processor - used in CI/CD scripts and cargo metadata parsing @@ -112,7 +104,6 @@ tools: windows: - scoop - winget - yq: name: yq description: YAML processor @@ -130,7 +121,6 @@ tools: windows: - scoop - winget - yamlfmt: name: yamlfmt description: YAML formatter @@ -144,7 +134,6 @@ tools: installer_priority: all: - go-install - yamllint: name: yamllint description: YAML linter @@ -159,7 +148,6 @@ tools: - manual install_commands: manual: uv tool install yamllint - prettier: name: prettier description: Opinionated code formatter for markdown and JSON @@ -173,7 +161,6 @@ tools: windows: - bun - npm - actionlint: name: actionlint description: GitHub Actions workflow linter @@ -187,11 +174,9 @@ tools: installer_priority: all: - go-install - # ========================================================================== # Rust Tools # ========================================================================== - cargo-deny: name: cargo-deny description: Cargo plugin for license and advisory checks (ADR-0001) @@ -206,7 +191,6 @@ tools: - cargo-install install_commands: cargo-install: cargo install cargo-deny --locked - cargo-audit: name: cargo-audit description: Cargo plugin for security vulnerability auditing @@ -221,7 +205,6 @@ tools: - cargo-install install_commands: cargo-install: cargo install cargo-audit --locked - cargo-edit: name: cargo-edit description: Cargo plugin for version management (cargo set-version) @@ -236,7 +219,6 @@ tools: - cargo-install install_commands: cargo-install: cargo install cargo-edit --locked - cbindgen: name: cbindgen description: Generate C headers from Rust FFI code diff --git a/.yamllint b/.yamllint index f495493..c18e918 100644 --- a/.yamllint +++ b/.yamllint @@ -2,6 +2,8 @@ extends: default rules: + comments: + min-spaces-from-content: 1 document-start: disable line-length: max: 120 diff --git a/AGENTS.md b/AGENTS.md index 871c44a..d2fcd5f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -124,11 +124,20 @@ Follow [3leaps commit style](https://crucible.3leaps.dev/repository/commit-style Generated by via under supervision of @ -Co-Authored-By: +Co-Authored-By: Role: Committer-of-Record: @ ``` +> **Attribution email policy**: All AI model Co-Authored-By trailers MUST use `noreply@3leaps.net` as the +> email address, regardless of model vendor. This prevents third-party email squatting on GitHub's +> contributor attribution. The model name in the name field provides the transparency; the email is +> plumbing under our control. Do NOT use vendor noreply addresses (`noreply@anthropic.com`, +> `noreply@openai.com`, etc.). + +> **Attribution role policy**: `Role:` trailers should use bare role slugs (for example, +> `Role: devlead`). + **Example:** ``` @@ -144,7 +153,7 @@ Changes: Generated by Claude Sonnet via Claude Code under supervision of @3leapsdave -Co-Authored-By: Claude Sonnet +Co-Authored-By: Claude Sonnet Role: devlead Committer-of-Record: @3leapsdave ``` diff --git a/README.md b/README.md index 360cc81..dccdcc5 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Content extraction from documents in a permissive licensing toolkit. [![License](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg)](LICENSE-MIT) -[![Rust](https://img.shields.io/badge/rust-1.81%2B-orange.svg)](https://www.rust-lang.org/) +[![Rust](https://img.shields.io/badge/rust-1.85%2B-orange.svg)](https://www.rust-lang.org/) ## Overview @@ -13,13 +13,13 @@ Content extraction from documents in a permissive licensing toolkit. | Format | Crate | Status | | -------- | ---------------- | ------- | -| DOCX | `docprims-ooxml` | Planned | -| XLSX | `docprims-ooxml` | Planned | -| PPTX | `docprims-ooxml` | Planned | +| DOCX | `docprims-ooxml` | Supported | +| XLSX | `docprims-ooxml` | Supported | +| PPTX | `docprims-ooxml` | Supported | | PDF | `docprims-pdf` | Future | -| Markdown | `docprims-text` | Planned | -| HTML | `docprims-text` | Planned | -| XML | `docprims-text` | Planned | +| Markdown | `docprims-text` | Supported | +| HTML | `docprims-text` | Supported | +| XML | `docprims-text` | Supported | ### The Problem @@ -131,8 +131,8 @@ docprims extract document.docx # Output as JSON with metadata docprims extract document.docx --format json --include-metadata -# Extract from multiple formats -docprims extract report.pdf slides.pptx data.xlsx +# Extract from multiple supported formats +docprims extract report.docx slides.pptx data.xlsx notes.md ``` ## Architecture @@ -159,7 +159,7 @@ Project documentation conventions (what is canonical vs planning notes): ### Prerequisites -- Rust 1.81+ +- Rust 1.85+ - curl (for bootstrap) ### Setup diff --git a/config/agentic/roles/devlead.yaml b/config/agentic/roles/devlead.yaml index 44901e0..73069fa 100644 --- a/config/agentic/roles/devlead.yaml +++ b/config/agentic/roles/devlead.yaml @@ -11,7 +11,6 @@ tags: - implementation - architecture - parser - context: | Use this role for implementation work on docprims. The devlead role handles parser implementation, API design, and cross-format coordination. @@ -20,7 +19,6 @@ context: | - All code parses untrusted input - defensive programming required - GPL-free dependencies only - check with cargo deny - Read-only extraction focus - no document modification - scope: - Parser implementation (OOXML, PDF, text formats) - API design and consistency across formats @@ -28,7 +26,6 @@ scope: - Cross-platform compatibility - Code review and PR oversight - Release preparation - mindset: focus: - Is this parser defensive against malformed input? @@ -40,7 +37,6 @@ mindset: - Prefer standard library over dependencies - Write tests alongside implementation - Keep parsers focused and composable - responsibilities: - Implement format parsers according to specifications - Maintain code quality and consistency @@ -48,7 +44,6 @@ responsibilities: - Document architectural decisions - Coordinate with other roles on cross-cutting concerns - Ensure GPL-free dependency compliance - escalates_to: - target: human maintainers when: Releases, version tags, breaking changes @@ -56,14 +51,12 @@ escalates_to: when: Parser security concerns, input validation changes - target: entarch when: API changes affecting consumers (Gentry, etc.) - does_not: - Push without maintainer approval (supervised mode) - Skip quality gates or license checks - Add GPL/LGPL/AGPL dependencies - Commit secrets or credentials - Modify files outside task scope without justification - examples: - type: commit title: Parser implementation diff --git a/config/agentic/roles/devrev.yaml b/config/agentic/roles/devrev.yaml index e83d1fa..f102fec 100644 --- a/config/agentic/roles/devrev.yaml +++ b/config/agentic/roles/devrev.yaml @@ -11,7 +11,6 @@ tags: - review - audit - four-eyes - context: | Use this role for reviewing code written by others. The devrev role enables the four-eyes model where one agent (or human) writes code and another reviews it. @@ -24,7 +23,6 @@ context: | Distinct from: - devlead: Writes the implementation (devrev reviews it) - secrev: Focuses on security vulnerabilities (devrev focuses on correctness) - scope: - Code review for correctness and maintainability - Bug finding and edge case identification @@ -32,7 +30,6 @@ scope: - Error handling verification - Performance concern identification - Consistency with codebase patterns - mindset: focus: - What happens with malformed/truncated documents? @@ -46,7 +43,6 @@ mindset: - Verify error paths are handled - Ensure tests cover malformed input - Be constructively critical, not adversarial - responsibilities: - Review code changes for correctness - Identify bugs, edge cases, and logic errors @@ -55,7 +51,6 @@ responsibilities: - Assess code maintainability and readability - Confirm consistency with existing patterns - Provide actionable feedback with specific suggestions - escalates_to: - target: human maintainers when: Fundamental design disagreements @@ -63,7 +58,6 @@ escalates_to: when: Security concerns discovered during review - target: devlead when: Questions about implementation intent - does_not: - Approve changes without thorough review - Ignore test coverage gaps @@ -71,7 +65,6 @@ does_not: - Rubber-stamp changes from senior contributors - Rewrite the implementation (suggest changes instead) - Block on style preferences (focus on correctness) - checklists: parser_review: - "Malformed input: What happens with truncated/corrupted documents?" diff --git a/config/agentic/roles/entarch.yaml b/config/agentic/roles/entarch.yaml index 1745f4c..df4c3ca 100644 --- a/config/agentic/roles/entarch.yaml +++ b/config/agentic/roles/entarch.yaml @@ -11,7 +11,6 @@ tags: - architecture - integration - ecosystem - context: | Use this role for cross-system integration and Fulmen ecosystem alignment. The entarch role ensures docprims works well with its consumers (Gentry, etc.) @@ -25,14 +24,12 @@ context: | - rsfulmen: Logging, exit codes, config patterns - sysprims: FFI patterns, binding structure - crucible: Standards, schemas, role definitions - scope: - API design for consumer needs - Cross-repository coordination - FFI and binding architecture - Schema contracts for output formats - Enterprise deployment patterns - mindset: focus: - Does this API work well for Gentry's use case? @@ -44,14 +41,12 @@ mindset: - Consistent patterns across 3leaps ecosystem - Stable contracts for bindings - Document integration points - responsibilities: - Ensure API contracts align with consumer needs - Coordinate with sysprims for FFI patterns - Design schema contracts for extracted text output - Document integration patterns - Review changes affecting consumers - escalates_to: - target: human maintainers when: Cross-repository decisions @@ -59,7 +54,6 @@ escalates_to: when: Implementation details - target: human maintainers when: Breaking changes to consumer APIs - does_not: - Implement features (architecture only) - Make unilateral cross-repo decisions diff --git a/config/agentic/roles/prodmktg.yaml b/config/agentic/roles/prodmktg.yaml index e829134..8468acb 100644 --- a/config/agentic/roles/prodmktg.yaml +++ b/config/agentic/roles/prodmktg.yaml @@ -11,7 +11,6 @@ tags: - marketing - positioning - messaging - context: | Use this role for product marketing and positioning of docprims. Focus on communicating the GPL-free value proposition and use cases. @@ -26,14 +25,12 @@ context: | - Enterprise developers needing document text extraction - Teams building content analysis tools (like Gentry) - Projects that can't use GPL-licensed alternatives - scope: - Value proposition messaging - README and documentation clarity - Use case articulation - Comparison with alternatives - Developer experience communication - mindset: focus: - Why would someone choose docprims over alternatives? @@ -45,14 +42,12 @@ mindset: - Clear, jargon-free communication - Honest about limitations - Developer-focused messaging - responsibilities: - Craft clear value proposition messaging - Review README and docs for clarity - Articulate use cases and examples - Position against GPL alternatives - Ensure consistent messaging - escalates_to: - target: human maintainers when: Brand or positioning strategy changes @@ -60,7 +55,6 @@ escalates_to: when: Technical accuracy validation needed - target: entarch when: Ecosystem messaging coordination - does_not: - Make unsubstantiated claims about capabilities - Disparage GPL-licensed alternatives diff --git a/config/agentic/roles/releng.yaml b/config/agentic/roles/releng.yaml index 901baf2..4f2dc9f 100644 --- a/config/agentic/roles/releng.yaml +++ b/config/agentic/roles/releng.yaml @@ -12,7 +12,6 @@ tags: - versioning - changelog - cicd - context: | Use this role for release-related work. The releng role handles versioning, changelog maintenance, CI/CD configuration, and release coordination. @@ -22,7 +21,6 @@ context: | - FFI library builds for multiple platforms - Language binding releases (Go, TypeScript, Python) - Coordinated releases with consumers (Gentry) - scope: - Version management (semantic versioning) - Changelog maintenance @@ -30,7 +28,6 @@ scope: - CI/CD pipeline configuration - Cross-platform build coordination - Binding package releases - mindset: focus: - Is the version bump correct (major/minor/patch)? @@ -43,7 +40,6 @@ mindset: - Document all user-facing changes - Test releases before publishing - Coordinate binding releases - responsibilities: - Determine appropriate version bumps - Maintain changelog with all changes @@ -51,7 +47,6 @@ responsibilities: - Configure and maintain CI/CD pipelines - Coordinate cross-platform builds - Manage binding package releases - escalates_to: - target: human maintainers when: Major version releases @@ -59,7 +54,6 @@ escalates_to: when: Breaking changes requiring communication - target: entarch when: Coordinated releases with consumers - does_not: - Release without maintainer approval - Skip changelog entries diff --git a/config/agentic/roles/secrev.yaml b/config/agentic/roles/secrev.yaml index 9bd2aed..14c4ab5 100644 --- a/config/agentic/roles/secrev.yaml +++ b/config/agentic/roles/secrev.yaml @@ -11,7 +11,6 @@ tags: - security - review - parser-safety - context: | Use this role for security-focused review and analysis. The secrev role applies a security lens to parser code handling untrusted documents. @@ -26,14 +25,12 @@ context: | Distinct from: - devrev: Reviews for correctness (secrev focuses on security) - scope: - Parser security review - Input validation assessment - Resource limit verification - Dependency security audit - Memory safety review - mindset: focus: - How could a malicious document exploit this parser? @@ -46,7 +43,6 @@ mindset: - Defense in depth - Fail-secure defaults - Resource limits on everything - responsibilities: - Review parser code for security vulnerabilities - Verify input validation completeness @@ -54,7 +50,6 @@ responsibilities: - Audit dependencies for vulnerabilities - Review FFI boundary for memory safety - Provide security-focused remediation guidance - escalates_to: - target: human maintainers when: Critical vulnerabilities requiring immediate action @@ -62,14 +57,12 @@ escalates_to: when: Security incidents or suspected exploitation - target: devlead when: Implementation guidance needed - does_not: - Approve security-sensitive code without thorough review - Ignore minor security issues (they compound) - Skip review of third-party dependencies - Assume document content is trusted - Provide security guarantees (identify risks, don't certify) - checklists: parser_security: - "Zip bombs: Are there limits on decompressed size?" diff --git a/crates/docprims-ooxml/src/docx.rs b/crates/docprims-ooxml/src/docx.rs index b28be49..12f3593 100644 --- a/crates/docprims-ooxml/src/docx.rs +++ b/crates/docprims-ooxml/src/docx.rs @@ -200,15 +200,11 @@ fn extract_paragraphs(xml: &str) -> Result> { in_p = true; cur.clear(); } - b"br" => { - if in_p { - cur.push('\n'); - } + b"br" if in_p => { + cur.push('\n'); } - b"tab" => { - if in_p { - cur.push('\t'); - } + b"tab" if in_p => { + cur.push('\t'); } _ => {} }, diff --git a/crates/docprims-text/src/markdown.rs b/crates/docprims-text/src/markdown.rs index decb5ae..2daa242 100644 --- a/crates/docprims-text/src/markdown.rs +++ b/crates/docprims-text/src/markdown.rs @@ -52,10 +52,8 @@ pub fn extract(content: &str) -> Result { text.push('\n'); } } - Event::End(TagEnd::Item) => { - if !text.ends_with('\n') { - text.push('\n'); - } + Event::End(TagEnd::Item) if !text.ends_with('\n') => { + text.push('\n'); } _ => {} } diff --git a/docs/decisions/README.md b/docs/decisions/README.md index 5b1da1a..d1056a4 100644 --- a/docs/decisions/README.md +++ b/docs/decisions/README.md @@ -16,8 +16,11 @@ This directory contains architectural, design, and security decision records for | ID | Title | Status | Date | | -------- | --------------------------------------------------- | -------- | ---------- | -| [ADR-0001](ADR-0001-license-policy.md) | License Policy | Accepted | 2025-01-25 | -| [ADR-0002](ADR-0002-crate-structure.md) | Crate Structure | Accepted | 2025-01-25 | +| [ADR-0001](ADR-0001-license-policy.md) | License Policy | Accepted | 2025-01-25 | +| [ADR-0002](ADR-0002-crate-structure.md) | Crate Structure | Accepted | 2025-01-25 | +| [ADR-0003](ADR-0003-input-validation-policy.md) | Input Validation Policy | Accepted | 2025-01-25 | +| [ADR-0004](ADR-0004-stdout-purity.md) | Stdout Purity for CLI Composability | Accepted | 2025-01-25 | +| [ADR-0006](ADR-0006-typescript-npm-publishing.md) | TypeScript npm Publishing Standard | Accepted | 2026-01-31 | ### Design Decision Records (DDR) @@ -29,7 +32,7 @@ This directory contains architectural, design, and security decision records for | ID | Title | Status | Date | | -------- | -------------------- | -------- | ---------- | -| _None yet_ | | | | +| [SDR-0002](SDR-0002-ooxml-v0-block-granularity.md) | OOXML v0 Block Granularity | Accepted | 2026-01-27 | ## Creating a New Record