chore(deps): relock test_apps/rust to html-to-markdown-rs 3.12.0 #355
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI E2E | |
| on: | |
| push: | |
| branches: [main] | |
| paths: | |
| - "crates/**" | |
| - "packages/**" | |
| - "e2e/**" | |
| - "fixtures/**" | |
| - "alef.toml" | |
| - "tools/**" | |
| - "scripts/**" | |
| - "Cargo.toml" | |
| - "Cargo.lock" | |
| - "Taskfile.yaml" | |
| - ".task/**" | |
| - "pyproject.toml" | |
| - "uv.lock" | |
| - "uv.toml" | |
| - "pnpm-lock.yaml" | |
| - "pnpm-workspace.yaml" | |
| - "package.json" | |
| - "Gemfile" | |
| - "Gemfile.lock" | |
| - "composer.json" | |
| - "composer.lock" | |
| - "go.mod" | |
| - "go.sum" | |
| - "rust-toolchain.toml" | |
| - ".cargo/config.toml" | |
| - ".github/workflows/ci-e2e.yaml" | |
| pull_request: | |
| branches: [main] | |
| paths: | |
| - "crates/**" | |
| - "packages/**" | |
| - "e2e/**" | |
| - "fixtures/**" | |
| - "alef.toml" | |
| - "tools/**" | |
| - "scripts/**" | |
| - "Cargo.toml" | |
| - "Cargo.lock" | |
| - "Taskfile.yaml" | |
| - ".task/**" | |
| - "pyproject.toml" | |
| - "uv.lock" | |
| - "uv.toml" | |
| - "pnpm-lock.yaml" | |
| - "pnpm-workspace.yaml" | |
| - "package.json" | |
| - "Gemfile" | |
| - "Gemfile.lock" | |
| - "composer.json" | |
| - "composer.lock" | |
| - "go.mod" | |
| - "go.sum" | |
| - "rust-toolchain.toml" | |
| - ".cargo/config.toml" | |
| - ".github/workflows/ci-e2e.yaml" | |
| workflow_dispatch: {} | |
| concurrency: | |
| group: ci-e2e-${{ github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| CARGO_TERM_COLOR: always | |
| CARGO_INCREMENTAL: 0 | |
| RUST_BACKTRACE: short | |
| BUILD_PROFILE: "ci" | |
| GO_VERSION: "1.26.0" | |
| GO_TOOLCHAIN: "go1.26.0" | |
| GOLANGCI_LINT_VERSION: "latest" | |
| permissions: | |
| contents: read | |
| jobs: | |
| changes: | |
| name: "Detect Changes" | |
| runs-on: ubuntu-24.04-arm | |
| outputs: | |
| core: ${{ steps.filter.outputs.core }} | |
| rust: ${{ steps.filter.outputs.rust }} | |
| ffi: ${{ steps.filter.outputs.ffi }} | |
| python: ${{ steps.filter.outputs.python }} | |
| node: ${{ steps.filter.outputs.node }} | |
| ruby: ${{ steps.filter.outputs.ruby }} | |
| php: ${{ steps.filter.outputs.php }} | |
| go: ${{ steps.filter.outputs.go }} | |
| java: ${{ steps.filter.outputs.java }} | |
| elixir: ${{ steps.filter.outputs.elixir }} | |
| r: ${{ steps.filter.outputs.r }} | |
| wasm: ${{ steps.filter.outputs.wasm }} | |
| csharp: ${{ steps.filter.outputs.csharp }} | |
| kotlin: ${{ steps.filter.outputs.kotlin }} | |
| swift: ${{ steps.filter.outputs.swift }} | |
| dart: ${{ steps.filter.outputs.dart }} | |
| zig: ${{ steps.filter.outputs.zig }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| - name: Detect changes | |
| uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4 | |
| id: filter | |
| with: | |
| filters: | | |
| core: | |
| - 'crates/html-to-markdown/**' | |
| - 'Cargo.toml' | |
| - 'Cargo.lock' | |
| - 'rust-toolchain.toml' | |
| - '.cargo/config.toml' | |
| - 'fixtures/**' | |
| - 'tools/e2e-generator/**' | |
| rust: | |
| - 'crates/html-to-markdown/**' | |
| - 'crates/html-to-markdown-cli/**' | |
| - 'e2e/rust/**' | |
| - 'Cargo.toml' | |
| - 'Cargo.lock' | |
| - 'rustfmt.toml' | |
| ffi: | |
| - 'crates/html-to-markdown-ffi/**' | |
| - 'crates/html-to-markdown/**' | |
| - 'e2e/c/**' | |
| - 'Cargo.toml' | |
| - 'Cargo.lock' | |
| python: | |
| - 'crates/html-to-markdown-py/**' | |
| - 'packages/python/**' | |
| - 'e2e/python/**' | |
| - 'pyproject.toml' | |
| - 'uv.lock' | |
| - 'uv.toml' | |
| - 'fixtures/**' | |
| node: | |
| - 'crates/html-to-markdown-node/**' | |
| - 'e2e/node/**' | |
| - 'package.json' | |
| - 'pnpm-lock.yaml' | |
| - 'pnpm-workspace.yaml' | |
| - 'fixtures/**' | |
| ruby: | |
| - 'packages/ruby/**' | |
| - 'e2e/ruby/**' | |
| - 'Gemfile' | |
| - 'Gemfile.lock' | |
| - 'fixtures/**' | |
| php: | |
| - 'crates/html-to-markdown-php/**' | |
| - 'packages/php-ext/**' | |
| - 'e2e/php/**' | |
| - 'composer.json' | |
| - 'composer.lock' | |
| - 'fixtures/**' | |
| go: | |
| - 'packages/go/**' | |
| - 'e2e/go/**' | |
| - 'crates/html-to-markdown-ffi/**' | |
| - 'go.mod' | |
| - 'go.sum' | |
| - 'fixtures/**' | |
| java: | |
| - 'packages/java/**' | |
| - 'e2e/java/**' | |
| - 'crates/html-to-markdown-ffi/**' | |
| - 'fixtures/**' | |
| elixir: | |
| - 'packages/elixir/**' | |
| - 'e2e/elixir/**' | |
| - 'crates/html-to-markdown-ffi/**' | |
| - 'fixtures/**' | |
| r: | |
| - 'packages/r/**' | |
| - 'e2e/r/**' | |
| - 'fixtures/**' | |
| wasm: | |
| - 'crates/html-to-markdown-wasm/**' | |
| - 'crates/html-to-markdown-wasm-wasi/**' | |
| - 'e2e/wasm/**' | |
| - 'fixtures/**' | |
| csharp: | |
| - 'packages/csharp/**' | |
| - 'e2e/csharp/**' | |
| - 'crates/html-to-markdown-ffi/**' | |
| - 'fixtures/**' | |
| kotlin: | |
| - 'packages/kotlin-android/**' | |
| - 'crates/html-to-markdown-ffi/**' | |
| - 'fixtures/**' | |
| swift: | |
| - 'packages/swift/**' | |
| - 'e2e/swift_e2e/**' | |
| - 'fixtures/**' | |
| dart: | |
| - 'packages/dart/**' | |
| - 'e2e/dart/**' | |
| - 'fixtures/**' | |
| zig: | |
| - 'packages/zig/**' | |
| - 'e2e/zig/**' | |
| - 'crates/html-to-markdown-ffi/**' | |
| - 'fixtures/**' | |
| # ~keep e2e/ is entirely generated from fixtures/ (html-to-markdown audit #40). | |
| # Nothing previously regenerated it in CI and diffed the result, so a fixture or | |
| # generator change could drift from the checked-in e2e suite indefinitely. | |
| e2e-freshness: | |
| name: "Verify: e2e freshness" | |
| runs-on: ubuntu-24.04-arm | |
| timeout-minutes: 30 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| - name: Setup Rust | |
| uses: xberg-io/actions/setup-rust@v1 | |
| with: | |
| cache-key-prefix: e2e-freshness | |
| # ~keep alef formats the Elixir suite with `mix format` during generation. Without mix on | |
| # PATH it degrades to a WARN and emits unformatted output, which the drift check below then | |
| # reports as staleness that has nothing to do with fixtures/ — the whole job failed on | |
| # e2e/elixir/**.exs for that reason alone. Only the formatter is needed here, so no hex, | |
| # rebar or dependency install. Every other language's formatter is already on the runner | |
| # image or installed by the steps below. | |
| - name: Setup Elixir | |
| uses: erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124 # v1 | |
| with: | |
| elixir-version: "1.19" | |
| otp-version: "28.1" | |
| - name: Install Task | |
| uses: xberg-io/actions/install-task@v1 | |
| - name: Install alef | |
| uses: xberg-io/actions/install-alef@v1 | |
| # ~keep `alef e2e generate` formats every suite it emits by shelling out to poly. With | |
| # poly absent this job failed all 16 languages with "poly not found on PATH" before the | |
| # drift check below ever ran, so the gate could not report on the drift it exists to catch. | |
| - name: Install poly | |
| uses: Goldziher/poly@v0 | |
| - name: Regenerate e2e suites | |
| run: task e2e:generate | |
| shell: bash | |
| - name: Fail if generated e2e/ drifted from committed output | |
| # ~keep `git diff` reports only tracked paths, so a regeneration that ADDS a new suite | |
| # left the gate at exit 0 — it could not fail on the defect it exists to catch. Use | |
| # `git status --porcelain -uall`, which reports additions, modifications and deletions. | |
| run: | | |
| drift="$(git status --porcelain --untracked-files=all -- e2e/)" | |
| if [ -n "$drift" ]; then | |
| echo "::error::e2e/ is stale — regenerating from fixtures/ changed the tree. Run 'task e2e:generate' and commit the result." | |
| printf '%s\n' "$drift" | |
| git diff -- e2e/ | |
| exit 1 | |
| fi | |
| shell: bash | |
| # ~keep The FFI crate is the shared cross-compile base for the C, Go, Java, and | |
| # C# publish legs, all of which build it for macos-15-intel (darwin x64) and | |
| # windows-11-arm (windows arm64) too. Those two targets were previously only | |
| # ever compiled at publish time — cover them here so a cross-compile break | |
| # surfaces on the commit that causes it, not at tag time. | |
| build-ffi: | |
| needs: [changes] | |
| name: "Build: FFI (${{ matrix.runner }})" | |
| if: | | |
| github.event_name == 'workflow_dispatch' || | |
| needs.changes.outputs.core == 'true' || | |
| needs.changes.outputs.ffi == 'true' | |
| runs-on: ${{ matrix.runner }} | |
| timeout-minutes: 60 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| runner: | |
| - ubuntu-latest | |
| - ubuntu-24.04-arm | |
| - macos-latest | |
| - macos-15-intel | |
| - windows-latest | |
| - windows-11-arm | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| id: checkout | |
| - name: Free disk space | |
| if: startsWith(matrix.runner, 'ubuntu') | |
| uses: xberg-io/actions/free-disk-space-linux@v1 | |
| with: | |
| show-initial: "false" | |
| show-final: "true" | |
| - name: Setup Rust | |
| uses: xberg-io/actions/setup-rust@v1 | |
| with: | |
| cache-key-prefix: ffi-${{ matrix.runner }} | |
| use-sccache: false | |
| - name: Build html-to-markdown-ffi (release, Unix) | |
| if: "!startsWith(matrix.runner, 'windows')" | |
| run: cargo build --release --locked -p html-to-markdown-ffi | |
| shell: bash | |
| env: | |
| CARGO_TERM_COLOR: always | |
| CARGO_INCREMENTAL: "0" | |
| RUST_BACKTRACE: short | |
| - name: Build html-to-markdown-ffi (debug, Windows) | |
| if: startsWith(matrix.runner, 'windows') | |
| run: cargo build --locked -p html-to-markdown-ffi | |
| shell: bash | |
| env: | |
| CARGO_TERM_COLOR: always | |
| # ~keep `test -f` alone only proves build.rs wrote *a* header — it says | |
| # nothing about whether the freshly-generated header matches the one | |
| # committed to git, so a stale checked-in header could never fail this | |
| # step. Diff against git to actually assert freshness. | |
| - name: Verify header is fresh | |
| shell: bash | |
| run: | | |
| HEADER="crates/html-to-markdown-ffi/include/html_to_markdown.h" | |
| test -f "$HEADER" | |
| git diff --exit-code -- "$HEADER" || { | |
| echo "::error::$HEADER is stale — cbindgen regenerated a different header than the one committed. Run 'cargo build -p html-to-markdown-ffi' and commit the result." | |
| exit 1 | |
| } | |
| echo "Header verified fresh: $HEADER" | |
| - name: Upload FFI artifacts | |
| if: "!startsWith(matrix.runner, 'windows')" | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: ffi-${{ matrix.runner }} | |
| path: | | |
| target/release/libhtml_to_markdown_ffi.* | |
| target/release/html_to_markdown_ffi.* | |
| crates/html-to-markdown-ffi/include/html_to_markdown.h | |
| retention-days: 7 | |
| if-no-files-found: warn | |
| - name: Cleanup Rust cache | |
| if: always() && steps.checkout.outcome == 'success' | |
| uses: xberg-io/actions/cleanup-rust-cache@v1 | |
| build-python: | |
| needs: [changes] | |
| name: "Build: Python (${{ matrix.os }})" | |
| if: | | |
| github.event_name == 'workflow_dispatch' || | |
| needs.changes.outputs.core == 'true' || | |
| needs.changes.outputs.python == 'true' | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 45 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, windows-latest, macos-latest] | |
| python: ["3.10", "3.12", "3.14"] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| id: checkout | |
| - name: Install Task | |
| uses: xberg-io/actions/install-task@v1 | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7 | |
| with: | |
| enable-cache: true | |
| - name: Set up Python | |
| uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 | |
| with: | |
| python-version: ${{ matrix.python }} | |
| - name: Setup Rust | |
| uses: xberg-io/actions/setup-rust@v1 | |
| with: | |
| components: rustfmt, clippy, llvm-tools-preview | |
| cache-key-prefix: python-${{ matrix.os }}-${{ matrix.python }} | |
| - name: Install Python Dependencies | |
| uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4 | |
| with: | |
| timeout_minutes: 5 | |
| max_attempts: 3 | |
| retry_wait_seconds: 30 | |
| command: | | |
| if [[ "${{ runner.os }}" == "Windows" ]] && [[ -d ".venv" ]]; then | |
| echo "Removing existing .venv directory on Windows" | |
| rm -rf .venv | |
| fi | |
| uv sync --all-extras --no-install-project --no-install-workspace | |
| shell: bash | |
| - name: Build Python Bindings | |
| run: | | |
| uv pip install maturin | |
| cd packages/python && uv run maturin develop --release | |
| shell: bash | |
| - name: Build CLI binary | |
| run: cargo build --release --locked -p html-to-markdown-cli | |
| shell: bash | |
| - name: Cleanup Rust cache | |
| if: always() && steps.checkout.outcome == 'success' | |
| uses: xberg-io/actions/cleanup-rust-cache@v1 | |
| build-node: | |
| needs: [changes] | |
| name: "Build: Node (${{ matrix.os }}, ${{ matrix.runtime }})" | |
| if: | | |
| github.event_name == 'workflow_dispatch' || | |
| needs.changes.outputs.core == 'true' || | |
| needs.changes.outputs.node == 'true' | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 60 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, windows-latest, macos-latest] | |
| runtime: [node, bun] | |
| exclude: | |
| - os: windows-latest | |
| runtime: bun | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| id: checkout | |
| - name: Install Task | |
| uses: xberg-io/actions/install-task@v1 | |
| - name: Setup Rust | |
| uses: xberg-io/actions/setup-rust@v1 | |
| with: | |
| cache-key-prefix: node-${{ matrix.os }}-${{ matrix.runtime }} | |
| - name: Setup Node.js workspace | |
| if: matrix.runtime == 'node' | |
| uses: xberg-io/actions/setup-node-workspace@v1 | |
| - name: Setup Bun | |
| if: matrix.runtime == 'bun' | |
| uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 | |
| with: | |
| bun-version: latest | |
| - name: Build NAPI-RS Bindings (Node.js) | |
| if: matrix.runtime == 'node' | |
| uses: xberg-io/actions/build-node-napi@v1 | |
| with: | |
| crate-dir: crates/html-to-markdown-node | |
| - name: Install workspace dependencies (Bun) | |
| if: matrix.runtime == 'bun' | |
| run: bun install | |
| shell: bash | |
| - name: Build NAPI-RS Bindings (Bun) | |
| if: matrix.runtime == 'bun' | |
| working-directory: crates/html-to-markdown-node | |
| run: bun run build | |
| shell: bash | |
| - name: Cleanup Rust cache | |
| if: always() && steps.checkout.outcome == 'success' | |
| uses: xberg-io/actions/cleanup-rust-cache@v1 | |
| # ~keep The musl platform packages are cross-compiled through zig and were only ever built at | |
| # publish time, where both legs failed and skipped the whole npm publish. This mirrors the | |
| # publish job's steps so a broken cross-compile surfaces on the commit that causes it. | |
| build-node-musl: | |
| needs: [changes] | |
| name: "Build: Node (${{ matrix.target }}, zig cross-compile)" | |
| if: | | |
| github.event_name == 'workflow_dispatch' || | |
| needs.changes.outputs.core == 'true' || | |
| needs.changes.outputs.node == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| target: [x86_64-unknown-linux-musl, aarch64-unknown-linux-musl] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| id: checkout | |
| - name: Setup Rust | |
| uses: xberg-io/actions/setup-rust@v1 | |
| with: | |
| target: ${{ matrix.target }} | |
| cache-key-prefix: node-musl-${{ matrix.target }} | |
| configure-musl-cc: "false" | |
| - name: Setup Node.js workspace | |
| uses: xberg-io/actions/setup-node-workspace@v1 | |
| with: | |
| node-version: "24" | |
| - name: Setup Zig | |
| uses: xberg-io/actions/setup-zig@v1 | |
| with: | |
| version: "0.16.0" | |
| - name: Install cargo-zigbuild | |
| shell: bash | |
| run: command -v cargo-zigbuild || cargo install --locked cargo-zigbuild | |
| - name: Build native module | |
| shell: bash | |
| working-directory: crates/html-to-markdown-node | |
| run: | | |
| set -euo pipefail | |
| rm -rf npm | |
| pnpm exec napi create-npm-dirs | |
| pnpm exec napi build --platform --release --target "${{ matrix.target }}" -x \ | |
| --js index.js --dts index.d.ts | |
| pnpm exec napi artifacts --output-dir . --npm-dir npm | |
| - name: Verify the platform package holds a native module | |
| shell: bash | |
| working-directory: crates/html-to-markdown-node | |
| run: | | |
| set -euo pipefail | |
| node_file=$(find npm -maxdepth 2 -name "*.node" -type f | head -1) | |
| if [ -z "${node_file}" ]; then | |
| echo "::error::No .node file found under npm/ for ${{ matrix.target }}" | |
| find npm -maxdepth 2 -type f | |
| exit 1 | |
| fi | |
| file "${node_file}" | |
| echo "Verified: $(basename "$(dirname "${node_file}")") contains $(basename "${node_file}")" | |
| - name: Cleanup Rust cache | |
| if: always() && steps.checkout.outcome == 'success' | |
| uses: xberg-io/actions/cleanup-rust-cache@v1 | |
| # ~keep aarch64-unknown-linux-gnu is cross-compiled through the docker-based | |
| # @napi-rs/cross-toolchain (napi build --use-napi-cross), a different code path | |
| # than the host-native builds above and the zig cross-compile below. It was only | |
| # ever exercised at publish time (same publish-only-leg class as the musl job | |
| # above) — mirror it here so a broken cross-toolchain surfaces on the commit | |
| # that causes it instead of at tag time. | |
| build-node-linux-arm64-gnu: | |
| needs: [changes] | |
| name: "Build: Node (aarch64-unknown-linux-gnu, napi-cross)" | |
| if: | | |
| github.event_name == 'workflow_dispatch' || | |
| needs.changes.outputs.core == 'true' || | |
| needs.changes.outputs.node == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| id: checkout | |
| - name: Setup Rust | |
| uses: xberg-io/actions/setup-rust@v1 | |
| with: | |
| target: aarch64-unknown-linux-gnu | |
| cache-key-prefix: node-linux-arm64-gnu | |
| - name: Setup Node.js workspace | |
| uses: xberg-io/actions/setup-node-workspace@v1 | |
| with: | |
| node-version: "24" | |
| - name: Build native module (napi-cross, docker toolchain) | |
| uses: xberg-io/actions/build-node-napi@v1 | |
| with: | |
| crate-dir: crates/html-to-markdown-node | |
| target: aarch64-unknown-linux-gnu | |
| use-napi-cross: true | |
| install-deps: "false" | |
| - name: Verify the platform package holds a native module | |
| shell: bash | |
| working-directory: crates/html-to-markdown-node | |
| run: | | |
| set -euo pipefail | |
| node_file=$(find npm -maxdepth 2 -name "*.node" -type f | head -1) | |
| if [ -z "${node_file}" ]; then | |
| echo "::error::No .node file found under npm/ for aarch64-unknown-linux-gnu" | |
| find npm -maxdepth 2 -type f | |
| exit 1 | |
| fi | |
| file "${node_file}" | |
| echo "Verified: $(basename "$(dirname "${node_file}")") contains $(basename "${node_file}")" | |
| - name: Cleanup Rust cache | |
| if: always() && steps.checkout.outcome == 'success' | |
| uses: xberg-io/actions/cleanup-rust-cache@v1 | |
| build-ruby: | |
| needs: [changes] | |
| name: "Build: Ruby (${{ matrix.os }}, ruby-${{ matrix.ruby }})" | |
| if: | | |
| github.event_name == 'workflow_dispatch' || | |
| needs.changes.outputs.core == 'true' || | |
| needs.changes.outputs.ruby == 'true' | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 60 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| ruby: ["3.2", "3.3"] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| id: checkout | |
| - name: Setup Rust | |
| uses: xberg-io/actions/setup-rust@v1 | |
| with: | |
| cache-key-prefix: ruby-${{ matrix.os }}-${{ matrix.ruby }} | |
| - name: Setup Ruby (Unix) | |
| if: runner.os != 'Windows' | |
| uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1 | |
| with: | |
| ruby-version: ${{ matrix.ruby }} | |
| bundler: "4.0.3" | |
| bundler-cache: false | |
| working-directory: packages/ruby | |
| - name: Setup Ruby (Windows) | |
| if: runner.os == 'Windows' | |
| uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1 | |
| with: | |
| ruby-version: ${{ matrix.ruby }} | |
| bundler: "4.0.3" | |
| bundler-cache: false | |
| working-directory: packages/ruby | |
| windows-toolchain: UCRT64 | |
| - name: Build CLI binary | |
| uses: xberg-io/actions/build-rust-cli@v1 | |
| with: | |
| package-name: html-to-markdown-cli | |
| binary-name: html-to-markdown | |
| - name: Build Ruby extension | |
| uses: xberg-io/actions/build-ruby-gem@v1 | |
| with: | |
| # Non-release e2e CI builds the gem against the local workspace path-dep; | |
| # skip the registry rewrite that hard-fails until core is on crates.io. ~keep | |
| rewrite-native-deps: "false" | |
| - name: Cleanup Rust cache | |
| if: always() && steps.checkout.outcome == 'success' | |
| uses: xberg-io/actions/cleanup-rust-cache@v1 | |
| # ~keep Publish builds the PHP extension across linux-x86_64, linux-arm64, | |
| # macos-arm64, and windows-x86_64 for every supported PHP version (16 legs | |
| # total), but until now only ubuntu/php8.4 was ever exercised before tag | |
| # time. Mirror the per-platform build (one php version, not the full 4x4 | |
| # matrix) so a total per-platform failure surfaces in CI instead of at | |
| # publish. Cross-PHP-version coverage stays publish-only — see the audit. | |
| build-php: | |
| needs: [changes] | |
| name: "Build: PHP (${{ matrix.platform.label }})" | |
| if: | | |
| github.event_name == 'workflow_dispatch' || | |
| needs.changes.outputs.core == 'true' || | |
| needs.changes.outputs.php == 'true' | |
| runs-on: ${{ matrix.platform.os }} | |
| timeout-minutes: 60 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| platform: | |
| - { os: ubuntu-latest, label: linux-x86_64 } | |
| - { os: ubuntu-24.04-arm, label: linux-arm64 } | |
| - { os: macos-latest, label: macos-arm64 } | |
| - { os: windows-latest, label: windows-x86_64 } | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| id: checkout | |
| - name: Setup PHP | |
| uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2 | |
| with: | |
| php-version: "8.4" | |
| extensions: ctype, dom, json, libxml, mbstring, tokenizer, xml, xmlwriter | |
| tools: composer:2.9.8 | |
| coverage: none | |
| - name: Setup Rust | |
| uses: xberg-io/actions/setup-rust@v1 | |
| with: | |
| cache-key-prefix: php-${{ matrix.platform.label }} | |
| - name: Capture php-config path | |
| run: scripts/ci/php/set-php-config.sh | |
| shell: bash | |
| - name: Install root Composer dependencies | |
| uses: ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda # 4.0.0 | |
| with: | |
| dependency-versions: locked | |
| env: | |
| COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GITHUB_TOKEN }}"}}' | |
| - name: Install PHP package Composer dependencies | |
| uses: ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda # 4.0.0 | |
| with: | |
| dependency-versions: locked | |
| working-directory: crates/html-to-markdown-php/src | |
| env: | |
| COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GITHUB_TOKEN }}"}}' | |
| - name: Build PHP extension | |
| id: build-php-extension | |
| uses: xberg-io/actions/build-php-extension@v1 | |
| with: | |
| # Non-release e2e CI builds the extension against the local workspace path-dep; | |
| # skip the registry rewrite that hard-fails until core is on crates.io. ~keep | |
| rewrite-native-deps: "false" | |
| crate-name: html-to-markdown-php | |
| lib-name: html_to_markdown_php | |
| - name: Upload PHP extension artifact | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: php-extension-${{ matrix.platform.label }} | |
| path: ${{ steps.build-php-extension.outputs.extension-path }} | |
| retention-days: 7 | |
| - name: Cleanup Rust cache | |
| if: always() && steps.checkout.outcome == 'success' | |
| uses: xberg-io/actions/cleanup-rust-cache@v1 | |
| build-csharp: | |
| needs: [changes] | |
| name: "Build: C# (${{ matrix.os }})" | |
| if: | | |
| github.event_name == 'workflow_dispatch' || | |
| needs.changes.outputs.core == 'true' || | |
| needs.changes.outputs.csharp == 'true' | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 60 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| id: checkout | |
| - name: Install Task | |
| uses: xberg-io/actions/install-task@v1 | |
| - name: Setup Rust | |
| uses: xberg-io/actions/setup-rust@v1 | |
| with: | |
| cache-key-prefix: csharp-${{ matrix.os }} | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5 | |
| with: | |
| dotnet-version: "10.0.x" | |
| - name: Build FFI library (Unix) | |
| if: runner.os != 'Windows' | |
| run: cargo build --release --locked -p html-to-markdown-ffi | |
| shell: bash | |
| - name: Build FFI library (Windows) | |
| if: runner.os == 'Windows' | |
| run: cargo build --locked -p html-to-markdown-ffi | |
| shell: bash | |
| - name: Restore C# dependencies | |
| run: dotnet restore packages/csharp/ | |
| shell: bash | |
| - name: Build C# package | |
| run: dotnet build packages/csharp/ -c Release | |
| shell: bash | |
| - name: Cleanup Rust cache | |
| if: always() && steps.checkout.outcome == 'success' | |
| uses: xberg-io/actions/cleanup-rust-cache@v1 | |
| build-java: | |
| needs: [changes] | |
| name: "Build: Java (${{ matrix.os }})" | |
| if: | | |
| github.event_name == 'workflow_dispatch' || | |
| needs.changes.outputs.core == 'true' || | |
| needs.changes.outputs.java == 'true' | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 60 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, windows-latest, macos-latest] | |
| java: ["25"] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| id: checkout | |
| - name: Setup Rust | |
| uses: xberg-io/actions/setup-rust@v1 | |
| with: | |
| cache-key-prefix: java-${{ matrix.os }} | |
| - name: Test Java Panama FFI bindings | |
| uses: xberg-io/actions/test-java-ffi@v1 | |
| with: | |
| ffi-crate-name: html-to-markdown-ffi | |
| ffi-lib-name: html_to_markdown_ffi | |
| java-version: ${{ matrix.java }} | |
| - name: Cleanup Rust cache | |
| if: always() && steps.checkout.outcome == 'success' | |
| uses: xberg-io/actions/cleanup-rust-cache@v1 | |
| build-wasm: | |
| needs: [changes] | |
| name: "Build: WASM" | |
| if: | | |
| github.event_name == 'workflow_dispatch' || | |
| needs.changes.outputs.core == 'true' || | |
| needs.changes.outputs.wasm == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 120 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| id: checkout | |
| - name: Setup Rust | |
| uses: xberg-io/actions/setup-rust@v1 | |
| with: | |
| target: wasm32-unknown-unknown | |
| use-sccache: false | |
| cache-key-prefix: wasm | |
| - name: Ensure wasm target installed | |
| run: scripts/common/ensure-wasm-target.sh | |
| shell: bash | |
| - name: Install wasm-pack | |
| run: scripts/common/install-wasm-pack.sh | |
| shell: bash | |
| - name: Setup Node workspace | |
| uses: xberg-io/actions/setup-node-workspace@v1 | |
| - name: Build WASM (all targets) | |
| uses: xberg-io/actions/build-wasm-package@v1 | |
| with: | |
| crate-dir: crates/html-to-markdown-wasm | |
| - name: Cleanup Rust cache | |
| if: always() && steps.checkout.outcome == 'success' | |
| uses: xberg-io/actions/cleanup-rust-cache@v1 | |
| test-python: | |
| needs: [build-python] | |
| name: "Test: Python (${{ matrix.os }}, py-${{ matrix.python }})" | |
| if: always() && !cancelled() && needs.build-python.result != 'skipped' | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 45 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, windows-latest, macos-latest] | |
| python: ["3.10", "3.12", "3.14"] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| - name: Install Task | |
| uses: xberg-io/actions/install-task@v1 | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7 | |
| with: | |
| enable-cache: true | |
| - name: Set up Python | |
| uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 | |
| with: | |
| python-version: ${{ matrix.python }} | |
| - name: Setup Rust | |
| uses: xberg-io/actions/setup-rust@v1 | |
| with: | |
| components: rustfmt, clippy, llvm-tools-preview | |
| - name: Install Python Dependencies | |
| uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4 | |
| with: | |
| timeout_minutes: 5 | |
| max_attempts: 3 | |
| retry_wait_seconds: 30 | |
| command: | | |
| if [[ "${{ runner.os }}" == "Windows" ]] && [[ -d ".venv" ]]; then | |
| echo "Removing existing .venv directory on Windows" | |
| rm -rf .venv | |
| fi | |
| uv sync --all-extras --no-install-project --no-install-workspace | |
| shell: bash | |
| - name: Build Python Bindings | |
| run: | | |
| uv pip install maturin | |
| cd packages/python && uv run maturin develop --release | |
| shell: bash | |
| - name: Build CLI binary | |
| run: cargo build --release --locked -p html-to-markdown-cli | |
| shell: bash | |
| - name: Install alef | |
| uses: xberg-io/actions/install-alef@v1 | |
| - name: Run E2E tests | |
| run: alef test --e2e --lang python | |
| shell: bash | |
| test-node: | |
| needs: [build-node] | |
| name: "Test: Node (${{ matrix.os }}, ${{ matrix.runtime }})" | |
| if: always() && !cancelled() && needs.build-node.result != 'skipped' | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 60 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, windows-latest, macos-latest] | |
| runtime: [node, bun] | |
| exclude: | |
| - os: windows-latest | |
| runtime: bun | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| - name: Install Task | |
| uses: xberg-io/actions/install-task@v1 | |
| - name: Setup Rust | |
| uses: xberg-io/actions/setup-rust@v1 | |
| - name: Setup Node.js workspace | |
| if: matrix.runtime == 'node' | |
| uses: xberg-io/actions/setup-node-workspace@v1 | |
| - name: Setup Bun | |
| if: matrix.runtime == 'bun' | |
| uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 | |
| with: | |
| bun-version: latest | |
| - name: Build NAPI-RS Bindings (Node.js) | |
| if: matrix.runtime == 'node' | |
| uses: xberg-io/actions/build-node-napi@v1 | |
| with: | |
| crate-dir: crates/html-to-markdown-node | |
| - name: Install workspace dependencies (Bun) | |
| if: matrix.runtime == 'bun' | |
| run: bun install | |
| shell: bash | |
| - name: Build NAPI-RS Bindings (Bun) | |
| if: matrix.runtime == 'bun' | |
| working-directory: crates/html-to-markdown-node | |
| run: bun run build | |
| shell: bash | |
| - name: Install alef | |
| if: matrix.runtime == 'node' | |
| uses: xberg-io/actions/install-alef@v1 | |
| - name: Run E2E tests (Node.js only) | |
| if: matrix.runtime == 'node' | |
| run: alef test --e2e --lang node | |
| shell: bash | |
| test-ruby: | |
| needs: [build-ruby] | |
| name: "Test: Ruby (${{ matrix.os }}, ruby-${{ matrix.ruby }})" | |
| if: always() && !cancelled() && needs.build-ruby.result != 'skipped' | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 60 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| ruby: ["3.2", "3.3"] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| - name: Setup Rust | |
| uses: xberg-io/actions/setup-rust@v1 | |
| - name: Setup Ruby (Unix) | |
| if: runner.os != 'Windows' | |
| uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1 | |
| with: | |
| ruby-version: ${{ matrix.ruby }} | |
| bundler: "4.0.3" | |
| bundler-cache: false | |
| working-directory: packages/ruby | |
| - name: Setup Ruby (Windows) | |
| if: runner.os == 'Windows' | |
| uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1 | |
| with: | |
| ruby-version: ${{ matrix.ruby }} | |
| bundler: "4.0.3" | |
| bundler-cache: false | |
| working-directory: packages/ruby | |
| windows-toolchain: UCRT64 | |
| - name: Build CLI binary | |
| uses: xberg-io/actions/build-rust-cli@v1 | |
| with: | |
| package-name: html-to-markdown-cli | |
| binary-name: html-to-markdown | |
| - name: Build Ruby extension | |
| uses: xberg-io/actions/build-ruby-gem@v1 | |
| with: | |
| # Non-release e2e CI builds the gem against the local workspace path-dep; | |
| # skip the registry rewrite that hard-fails until core is on crates.io. ~keep | |
| rewrite-native-deps: "false" | |
| - name: Run Rubocop (Ubuntu/ruby-3.3 only) | |
| if: runner.os != 'Windows' && matrix.os == 'ubuntu-latest' && matrix.ruby == '3.3' | |
| run: ./scripts/ci/ruby/run-rubocop.sh | |
| shell: bash | |
| - name: Validate RBS signatures (Ubuntu/ruby-3.3 only) | |
| if: runner.os != 'Windows' && matrix.os == 'ubuntu-latest' && matrix.ruby == '3.3' | |
| run: ./scripts/ci/ruby/run-rbs-validate.sh | |
| shell: bash | |
| - name: Run Steep type checking (Ubuntu/ruby-3.3 only) | |
| if: runner.os != 'Windows' && matrix.os == 'ubuntu-latest' && matrix.ruby == '3.3' | |
| working-directory: packages/ruby | |
| run: ../../scripts/ci/ruby/run-steep.sh | |
| shell: bash | |
| - name: Run Ruby specs (Unix) | |
| if: runner.os != 'Windows' | |
| working-directory: packages/ruby | |
| run: ../../scripts/ci/ruby/run-rspec-unix.sh | |
| shell: bash | |
| - name: Run Ruby specs (Windows) | |
| if: runner.os == 'Windows' | |
| working-directory: packages/ruby | |
| shell: pwsh | |
| run: ../../scripts/ci/ruby/run-rspec-windows.ps1 | |
| - name: Install Task | |
| if: runner.os != 'Windows' | |
| uses: xberg-io/actions/install-task@v1 | |
| - name: Install alef | |
| if: runner.os != 'Windows' | |
| uses: xberg-io/actions/install-alef@v1 | |
| - name: Run E2E tests (Unix only) | |
| if: runner.os != 'Windows' | |
| run: alef test --e2e --lang ruby | |
| shell: bash | |
| test-php: | |
| needs: [build-php] | |
| name: "Test: PHP" | |
| if: always() && !cancelled() && needs.build-php.result != 'skipped' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| - name: Setup PHP | |
| uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2 | |
| with: | |
| php-version: "8.4" | |
| extensions: ctype, dom, json, libxml, mbstring, tokenizer, xml, xmlwriter | |
| tools: composer:2.9.8 | |
| coverage: none | |
| - name: Setup Rust | |
| uses: xberg-io/actions/setup-rust@v1 | |
| - name: Capture php-config path | |
| run: scripts/ci/php/set-php-config.sh | |
| shell: bash | |
| - name: Install root Composer dependencies | |
| uses: ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda # 4.0.0 | |
| with: | |
| dependency-versions: locked | |
| env: | |
| COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GITHUB_TOKEN }}"}}' | |
| - name: Install PHP package Composer dependencies | |
| uses: ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda # 4.0.0 | |
| with: | |
| dependency-versions: locked | |
| working-directory: crates/html-to-markdown-php/src | |
| env: | |
| COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GITHUB_TOKEN }}"}}' | |
| - name: Build PHP extension | |
| id: build-php-extension | |
| uses: xberg-io/actions/build-php-extension@v1 | |
| with: | |
| # Non-release e2e CI builds the extension against the local workspace path-dep; | |
| # skip the registry rewrite that hard-fails until core is on crates.io. ~keep | |
| rewrite-native-deps: "false" | |
| crate-name: html-to-markdown-php | |
| lib-name: html_to_markdown_php | |
| - name: Run PHP tests | |
| run: scripts/ci/php/run-php-tests.sh | |
| shell: bash | |
| env: | |
| EXTENSION_PATH: ${{ steps.build-php-extension.outputs.extension-path }} | |
| - name: Install Task | |
| uses: xberg-io/actions/install-task@v1 | |
| - name: Install alef | |
| uses: xberg-io/actions/install-alef@v1 | |
| - name: Run E2E tests | |
| run: alef test --e2e --lang php | |
| shell: bash | |
| env: | |
| EXTENSION_PATH: ${{ steps.build-php-extension.outputs.extension-path }} | |
| test-csharp: | |
| needs: [build-csharp, build-ffi, changes] | |
| name: "Test: C# (${{ matrix.os }})" | |
| if: | | |
| always() && !cancelled() && needs.build-csharp.result != 'skipped' && | |
| (github.event_name == 'workflow_dispatch' || | |
| needs.changes.outputs.csharp == 'true' || | |
| needs.changes.outputs.ffi == 'true' || | |
| needs.changes.outputs.core == 'true') | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 60 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| - name: Install Task | |
| uses: xberg-io/actions/install-task@v1 | |
| - name: Setup Rust | |
| uses: xberg-io/actions/setup-rust@v1 | |
| with: | |
| cache-key-prefix: csharp-test-${{ matrix.os }} | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5 | |
| with: | |
| dotnet-version: "10.0.x" | |
| - name: Build FFI library (Unix) | |
| if: runner.os != 'Windows' | |
| run: cargo build --release --locked -p html-to-markdown-ffi | |
| shell: bash | |
| - name: Build FFI library (Windows) | |
| if: runner.os == 'Windows' | |
| run: cargo build --locked -p html-to-markdown-ffi | |
| shell: bash | |
| - name: Restore C# dependencies | |
| run: dotnet restore packages/csharp/ | |
| shell: bash | |
| - name: Build C# package | |
| run: dotnet build packages/csharp/ -c Release | |
| shell: bash | |
| - name: Install alef | |
| uses: xberg-io/actions/install-alef@v1 | |
| - name: Run E2E tests (Unix) | |
| if: runner.os != 'Windows' | |
| run: alef test --e2e --lang csharp | |
| shell: bash | |
| env: | |
| LD_LIBRARY_PATH: ${{ github.workspace }}/target/release:${{ github.workspace }}/target/debug | |
| DYLD_LIBRARY_PATH: ${{ github.workspace }}/target/release:${{ github.workspace }}/target/debug | |
| - name: Run E2E tests (Windows) | |
| if: runner.os == 'Windows' | |
| run: | | |
| export PATH="${{ github.workspace }}/target/release:${{ github.workspace }}/target/debug:$PATH" | |
| alef test --e2e --lang csharp | |
| shell: bash | |
| - name: Cleanup Rust cache | |
| if: always() | |
| uses: xberg-io/actions/cleanup-rust-cache@v1 | |
| test-go: | |
| needs: [build-ffi, changes] | |
| name: "Test: Go" | |
| if: | | |
| always() && !cancelled() && needs.build-ffi.result != 'skipped' && | |
| (github.event_name == 'workflow_dispatch' || | |
| needs.changes.outputs.go == 'true' || | |
| needs.changes.outputs.ffi == 'true' || | |
| needs.changes.outputs.core == 'true') | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| - name: Setup Rust | |
| uses: xberg-io/actions/setup-rust@v1 | |
| - name: Setup Go | |
| uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6 | |
| with: | |
| go-version: ${{ env.GO_VERSION }} | |
| check-latest: true | |
| - name: Build FFI library | |
| run: cargo build --release --locked -p html-to-markdown-ffi | |
| shell: bash | |
| - name: Detect Go modules | |
| id: set-modules | |
| shell: bash | |
| run: scripts/ci/go/detect-go-modules.sh | |
| - name: Install golangci-lint | |
| if: steps.set-modules.outputs.modules != '[]' | |
| env: | |
| GOTOOLCHAIN: ${{ env.GO_TOOLCHAIN }} | |
| run: scripts/ci/go/install-golangci-lint.sh | |
| shell: bash | |
| - name: Run golangci-lint (all modules) | |
| if: steps.set-modules.outputs.modules != '[]' | |
| shell: bash | |
| run: | | |
| for module in $(echo '${{ steps.set-modules.outputs.modules }}' | jq -r '.[]'); do | |
| echo "=== Linting $module ===" | |
| (cd "$module" && "${{ github.workspace }}/scripts/ci/go/run-golangci-lint.sh") | |
| done | |
| - name: Install Task | |
| uses: xberg-io/actions/install-task@v1 | |
| - name: Install alef | |
| uses: xberg-io/actions/install-alef@v1 | |
| - name: Run E2E tests | |
| run: alef test --e2e --lang go | |
| shell: bash | |
| test-java: | |
| needs: [build-java] | |
| name: "Test: Java (${{ matrix.os }})" | |
| if: always() && !cancelled() && needs.build-java.result != 'skipped' | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 60 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, windows-latest, macos-latest] | |
| java: ["25"] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| - name: Setup Rust | |
| uses: xberg-io/actions/setup-rust@v1 | |
| - name: Test Java Panama FFI bindings | |
| uses: xberg-io/actions/test-java-ffi@v1 | |
| with: | |
| ffi-crate-name: html-to-markdown-ffi | |
| ffi-lib-name: html_to_markdown_ffi | |
| java-version: ${{ matrix.java }} | |
| - name: Install Task | |
| uses: xberg-io/actions/install-task@v1 | |
| - name: Install alef | |
| if: matrix.os != 'windows-latest' | |
| uses: xberg-io/actions/install-alef@v1 | |
| - name: Run E2E tests (Linux + macOS) | |
| if: matrix.os != 'windows-latest' | |
| run: alef test --e2e --lang java | |
| shell: bash | |
| test-elixir: | |
| needs: [build-ffi, changes] | |
| name: "Test: Elixir" | |
| if: | | |
| always() && !cancelled() && needs.build-ffi.result != 'skipped' && | |
| (github.event_name == 'workflow_dispatch' || | |
| needs.changes.outputs.elixir == 'true' || | |
| needs.changes.outputs.ffi == 'true' || | |
| needs.changes.outputs.core == 'true') | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| - name: Setup Elixir | |
| uses: erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124 # v1 | |
| with: | |
| elixir-version: "1.19" | |
| otp-version: "28.1" | |
| - name: Setup Rust | |
| uses: xberg-io/actions/setup-rust@v1 | |
| - name: Install Hex/Rebar | |
| run: scripts/ci/elixir/install-hex-rebar.sh | |
| shell: bash | |
| - name: Install dependencies | |
| working-directory: packages/elixir | |
| run: ../../scripts/ci/elixir/install-deps.sh | |
| shell: bash | |
| - name: Run tests | |
| working-directory: packages/elixir | |
| run: ../../scripts/ci/elixir/run-tests.sh | |
| shell: bash | |
| - name: Credo lint | |
| working-directory: packages/elixir | |
| run: ../../scripts/ci/elixir/run-credo.sh | |
| shell: bash | |
| - name: Install Task | |
| uses: xberg-io/actions/install-task@v1 | |
| - name: Install alef | |
| uses: xberg-io/actions/install-alef@v1 | |
| - name: Run E2E tests | |
| run: alef test --e2e --lang elixir | |
| shell: bash | |
| test-r: | |
| needs: [changes] | |
| name: "Test: R" | |
| if: | | |
| always() && !cancelled() && | |
| (github.event_name == 'workflow_dispatch' || | |
| needs.changes.outputs.r == 'true' || | |
| needs.changes.outputs.core == 'true') | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| - name: Setup R | |
| uses: xberg-io/actions/setup-r@v1 | |
| with: | |
| install-deps-script: scripts/ci/r/install-deps.sh | |
| - name: Setup Rust | |
| uses: xberg-io/actions/setup-rust@v1 | |
| - name: Run tests | |
| working-directory: packages/r | |
| run: ../../scripts/ci/r/run-tests.sh | |
| shell: bash | |
| - name: Run lintr | |
| working-directory: packages/r | |
| run: ../../scripts/ci/r/run-lintr.sh | |
| shell: bash | |
| - name: Install Task | |
| uses: xberg-io/actions/install-task@v1 | |
| - name: Install alef | |
| uses: xberg-io/actions/install-alef@v1 | |
| - name: Run E2E tests | |
| run: alef test --e2e --lang r | |
| shell: bash | |
| test-c-ffi: | |
| needs: [build-ffi, changes] | |
| name: "Test: C FFI (${{ matrix.runner }})" | |
| if: | | |
| always() && !cancelled() && needs.build-ffi.result != 'skipped' && | |
| (github.event_name == 'workflow_dispatch' || | |
| needs.changes.outputs.ffi == 'true' || | |
| needs.changes.outputs.core == 'true') | |
| runs-on: ${{ matrix.runner }} | |
| timeout-minutes: 60 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| runner: | |
| - ubuntu-latest | |
| - ubuntu-24.04-arm | |
| - macos-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| - name: Setup Rust | |
| uses: xberg-io/actions/setup-rust@v1 | |
| with: | |
| cache-key-prefix: c-ffi-${{ matrix.runner }} | |
| use-sccache: false | |
| - name: Build html-to-markdown-ffi | |
| shell: bash | |
| run: cargo build --release --locked -p html-to-markdown-ffi | |
| - name: Run C e2e tests | |
| shell: bash | |
| env: | |
| LD_LIBRARY_PATH: ${{ github.workspace }}/target/release | |
| DYLD_LIBRARY_PATH: ${{ github.workspace }}/target/release | |
| run: cd e2e/c && make test | |
| - name: Verify header is fresh | |
| shell: bash | |
| run: | | |
| HEADER="crates/html-to-markdown-ffi/include/html_to_markdown.h" | |
| test -f "$HEADER" | |
| git diff --exit-code -- "$HEADER" || { | |
| echo "::error::$HEADER is stale — cbindgen regenerated a different header than the one committed. Run 'cargo build -p html-to-markdown-ffi' and commit the result." | |
| exit 1 | |
| } | |
| echo "Header verified fresh: $HEADER" | |
| - name: Verify pkg-config output | |
| shell: bash | |
| run: | | |
| PC_DIR="$(pwd)/target/release/build" | |
| PC_FILE=$(find "$PC_DIR" -name 'html-to-markdown.pc' -path '*/html-to-markdown-ffi-*/out/*' 2>/dev/null | head -1) | |
| if [ -z "$PC_FILE" ]; then | |
| echo "Warning: html-to-markdown.pc not found in build output" | |
| find "$PC_DIR" -name '*.pc' 2>/dev/null || echo "No .pc files found" | |
| else | |
| echo "Found pkg-config file: $PC_FILE" | |
| cat "$PC_FILE" | |
| fi | |
| test-c-ffi-windows: | |
| needs: [build-ffi, changes] | |
| name: "Test: C FFI (windows-latest)" | |
| if: | | |
| always() && !cancelled() && needs.build-ffi.result != 'skipped' && | |
| (github.event_name == 'workflow_dispatch' || | |
| needs.changes.outputs.ffi == 'true' || | |
| needs.changes.outputs.core == 'true') | |
| runs-on: windows-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| - name: Setup Rust | |
| uses: xberg-io/actions/setup-rust@v1 | |
| with: | |
| cache-key-prefix: c-ffi-windows | |
| use-sccache: false | |
| - name: Build html-to-markdown-ffi | |
| shell: bash | |
| run: cargo build --locked -p html-to-markdown-ffi | |
| - name: Verify header is fresh | |
| shell: bash | |
| run: | | |
| HEADER="crates/html-to-markdown-ffi/include/html_to_markdown.h" | |
| test -f "$HEADER" | |
| git diff --exit-code -- "$HEADER" || { | |
| echo "::error::$HEADER is stale — cbindgen regenerated a different header than the one committed. Run 'cargo build -p html-to-markdown-ffi' and commit the result." | |
| exit 1 | |
| } | |
| echo "Header verified fresh on Windows: $HEADER" | |
| test-wasm: | |
| needs: [build-wasm] | |
| name: "Test: WASM" | |
| if: always() && !cancelled() && needs.build-wasm.result != 'skipped' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 120 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| - name: Setup Rust | |
| uses: xberg-io/actions/setup-rust@v1 | |
| with: | |
| target: wasm32-unknown-unknown | |
| use-sccache: false | |
| - name: Ensure wasm target installed | |
| run: scripts/common/ensure-wasm-target.sh | |
| shell: bash | |
| - name: Install wasm-pack | |
| run: scripts/common/install-wasm-pack.sh | |
| shell: bash | |
| - name: Setup Node workspace | |
| uses: xberg-io/actions/setup-node-workspace@v1 | |
| - name: Build WASM (all targets) | |
| uses: xberg-io/actions/build-wasm-package@v1 | |
| with: | |
| crate-dir: crates/html-to-markdown-wasm | |
| - name: Test WASM bundle | |
| working-directory: crates/html-to-markdown-wasm | |
| run: ../../scripts/ci/wasm/test-wasm-bundle.sh | |
| shell: bash | |
| - name: Run Rust WASM tests | |
| working-directory: crates/html-to-markdown-wasm | |
| run: ../../scripts/ci/wasm/test-wasm-rust.sh | |
| shell: bash | |
| build-kotlin-android: | |
| needs: [changes] | |
| name: "Build: Kotlin Android" | |
| if: | | |
| github.event_name == 'workflow_dispatch' || | |
| needs.changes.outputs.core == 'true' || | |
| needs.changes.outputs.ffi == 'true' || | |
| needs.changes.outputs.kotlin == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| id: checkout | |
| - name: Setup Rust | |
| uses: xberg-io/actions/setup-rust@v1 | |
| with: | |
| targets: aarch64-linux-android,x86_64-linux-android | |
| cache-key-prefix: kotlin-android | |
| - name: Setup Android SDK/NDK | |
| uses: android-actions/setup-android@40fd30fb8d7440372e1316f5d1809ec01dcd3699 # v4 | |
| # ~keep Bundle the JNI crate (libhtm_jni.so) that the Kotlin bridge loads via | |
| # System.loadLibrary("htm_jni") — NOT the plain C-FFI crate, whose lib*_ffi.so | |
| # lacks the JNI entry points (html-to-markdown#446). This mirrors the exact | |
| # crate/lib pair the publish workflow's kotlin-android-natives job builds, so a | |
| # regression in the JNI crate surfaces here instead of only at publish time. | |
| - name: Build Android native libraries | |
| uses: xberg-io/actions/build-android-natives@v1 | |
| with: | |
| crate-name: html-to-markdown-rs-jni | |
| lib-name: htm_jni | |
| abis: arm64-v8a,x86_64 | |
| - name: Cleanup Rust cache | |
| if: always() && steps.checkout.outcome == 'success' | |
| uses: xberg-io/actions/cleanup-rust-cache@v1 | |
| # ~keep Kotlin/Android ships to Maven Central but was never compiled or tested in | |
| # CI (html-to-markdown audit #38) — build-kotlin-android above only cross-compiles | |
| # the native .so, it never invokes Gradle/Kotlin compilation. `gradle test` here | |
| # compiles the Kotlin facade against the host JVM and runs the e2e fixture suite | |
| # via a host-platform JNI build (see packages/kotlin-android/build.gradle.kts' | |
| # buildHostJni/copyHostJni tasks) — no Android emulator required. | |
| test-kotlin-android: | |
| needs: [changes] | |
| name: "Test: Kotlin Android (host JVM)" | |
| if: | | |
| github.event_name == 'workflow_dispatch' || | |
| needs.changes.outputs.core == 'true' || | |
| needs.changes.outputs.ffi == 'true' || | |
| needs.changes.outputs.kotlin == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| id: checkout | |
| - name: Setup Rust | |
| uses: xberg-io/actions/setup-rust@v1 | |
| with: | |
| cache-key-prefix: kotlin-android-test | |
| - name: Setup Android SDK/NDK | |
| uses: android-actions/setup-android@40fd30fb8d7440372e1316f5d1809ec01dcd3699 # v4 | |
| - name: Setup JDK | |
| uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 | |
| with: | |
| distribution: temurin | |
| java-version: "17" | |
| - name: Install Task | |
| uses: xberg-io/actions/install-task@v1 | |
| - name: Install alef | |
| uses: xberg-io/actions/install-alef@v1 | |
| - name: Compile and test Kotlin Android binding (host JVM) | |
| run: alef test --e2e --lang kotlin_android | |
| shell: bash | |
| - name: Cleanup Rust cache | |
| if: always() && steps.checkout.outcome == 'success' | |
| uses: xberg-io/actions/cleanup-rust-cache@v1 | |
| test-swift: | |
| needs: [changes] | |
| name: "Test: Swift" | |
| if: | | |
| github.event_name == 'workflow_dispatch' || | |
| needs.changes.outputs.core == 'true' || | |
| needs.changes.outputs.swift == 'true' | |
| runs-on: macos-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| - name: Install Task | |
| uses: xberg-io/actions/install-task@v1 | |
| - name: Setup Rust | |
| uses: xberg-io/actions/setup-rust@v1 | |
| with: | |
| cache-key-prefix: swift | |
| - name: Setup Swift | |
| uses: xberg-io/actions/setup-swift@v1 | |
| with: | |
| version: "6.0" | |
| - name: Setup PHP | |
| uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2 | |
| with: | |
| php-version: "8.3" | |
| - name: Install alef | |
| uses: xberg-io/actions/install-alef@v1 | |
| - name: Run E2E tests | |
| run: alef test --e2e --lang swift | |
| shell: bash | |
| - name: Cleanup Rust cache | |
| if: always() | |
| uses: xberg-io/actions/cleanup-rust-cache@v1 | |
| test-dart: | |
| needs: [changes] | |
| name: "Test: Dart" | |
| if: | | |
| github.event_name == 'workflow_dispatch' || | |
| needs.changes.outputs.core == 'true' || | |
| needs.changes.outputs.dart == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| - name: Install Task | |
| uses: xberg-io/actions/install-task@v1 | |
| - name: Setup Rust | |
| uses: xberg-io/actions/setup-rust@v1 | |
| with: | |
| cache-key-prefix: dart | |
| - name: Setup Dart | |
| uses: dart-lang/setup-dart@7654d458321ee25acccccfdb86cd48bd95768ff1 # v1 | |
| - name: Install alef | |
| uses: xberg-io/actions/install-alef@v1 | |
| - name: Run E2E tests | |
| run: alef test --e2e --lang dart | |
| shell: bash | |
| - name: Cleanup Rust cache | |
| if: always() | |
| uses: xberg-io/actions/cleanup-rust-cache@v1 | |
| test-zig: | |
| needs: [changes] | |
| name: "Test: Zig" | |
| if: | | |
| github.event_name == 'workflow_dispatch' || | |
| needs.changes.outputs.core == 'true' || | |
| needs.changes.outputs.ffi == 'true' || | |
| needs.changes.outputs.zig == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 | |
| - name: Install Task | |
| uses: xberg-io/actions/install-task@v1 | |
| - name: Setup Rust | |
| uses: xberg-io/actions/setup-rust@v1 | |
| with: | |
| cache-key-prefix: zig | |
| - name: Setup Zig | |
| uses: xberg-io/actions/setup-zig@v1 | |
| with: | |
| version: "0.16.0" | |
| - name: Install alef | |
| uses: xberg-io/actions/install-alef@v1 | |
| - name: Run E2E tests | |
| run: alef test --e2e --lang zig | |
| shell: bash | |
| - name: Cleanup Rust cache | |
| if: always() | |
| uses: xberg-io/actions/cleanup-rust-cache@v1 |