Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
250 changes: 250 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
name: Release

on:
push:
tags: ["v*"]

permissions:
contents: write

env:
CARGO_TERM_COLOR: always

jobs:
build:
name: Build (${{ matrix.target }})
strategy:
fail-fast: false
matrix:
include:
- target: aarch64-apple-darwin
os: macos-14
npm_pkg: marknest-darwin-arm64
binary_name: marknest
- target: x86_64-apple-darwin
os: macos-13
npm_pkg: marknest-darwin-x64
binary_name: marknest
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
npm_pkg: marknest-linux-x64
binary_name: marknest
- target: aarch64-unknown-linux-gnu
os: ubuntu-latest
npm_pkg: marknest-linux-arm64
binary_name: marknest
use_cross: true
- target: x86_64-pc-windows-msvc
os: windows-latest
npm_pkg: marknest-win32-x64
binary_name: marknest.exe
runs-on: ${{ matrix.os }}

steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}

- name: Install cross (Linux ARM64)
if: matrix.use_cross
run: cargo install cross --git https://github.com/cross-rs/cross

- name: Build release binary
run: |
if [ "${{ matrix.use_cross }}" = "true" ]; then
cross build --release --target ${{ matrix.target }} -p marknest
else
cargo build --release --target ${{ matrix.target }} -p marknest
fi
shell: bash

- name: Prepare artifact
run: |
mkdir -p staging
cp target/${{ matrix.target }}/release/${{ matrix.binary_name }} staging/
shell: bash

- name: Upload binary artifact
uses: actions/upload-artifact@v4
with:
name: binary-${{ matrix.target }}
path: staging/${{ matrix.binary_name }}
retention-days: 1

publish-crates:
name: Publish to crates.io
needs: build
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable

- name: Publish marknest-core
run: cargo publish -p marknest-core --token ${{ secrets.CARGO_REGISTRY_TOKEN }}

- name: Wait for crates.io index
run: sleep 30

- name: Publish marknest
run: cargo publish -p marknest --token ${{ secrets.CARGO_REGISTRY_TOKEN }}

- name: Wait for crates.io index
run: sleep 30

- name: Publish marknest-server
run: cargo publish -p marknest-server --token ${{ secrets.CARGO_REGISTRY_TOKEN }}

publish-npm:
name: Publish to npm
needs: build
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
registry-url: https://registry.npmjs.org

- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts

- name: Place binaries into npm packages
run: |
declare -A TARGET_TO_PKG=(
["aarch64-apple-darwin"]="marknest-darwin-arm64"
["x86_64-apple-darwin"]="marknest-darwin-x64"
["x86_64-unknown-linux-gnu"]="marknest-linux-x64"
["aarch64-unknown-linux-gnu"]="marknest-linux-arm64"
["x86_64-pc-windows-msvc"]="marknest-win32-x64"
)

for target in "${!TARGET_TO_PKG[@]}"; do
pkg="${TARGET_TO_PKG[$target]}"
pkg_dir="npm/${pkg}"
mkdir -p "${pkg_dir}/bin"

if [ "$target" = "x86_64-pc-windows-msvc" ]; then
cp "artifacts/binary-${target}/marknest.exe" "${pkg_dir}/bin/"
else
cp "artifacts/binary-${target}/marknest" "${pkg_dir}/bin/"
chmod +x "${pkg_dir}/bin/marknest"
fi
done

- name: Publish platform packages
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
for pkg_dir in npm/marknest-*/; do
echo "Publishing $(basename $pkg_dir)..."
cd "$pkg_dir"
npm publish --access public || true
cd ../..
done

- name: Publish umbrella package
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
cd npm/marknest
npm publish --access public

github-release:
name: GitHub Release
needs: build
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts

- name: Package release assets
run: |
mkdir -p release

declare -A TARGETS=(
["aarch64-apple-darwin"]="marknest-aarch64-apple-darwin"
["x86_64-apple-darwin"]="marknest-x86_64-apple-darwin"
["x86_64-unknown-linux-gnu"]="marknest-x86_64-unknown-linux-gnu"
["aarch64-unknown-linux-gnu"]="marknest-aarch64-unknown-linux-gnu"
["x86_64-pc-windows-msvc"]="marknest-x86_64-pc-windows-msvc"
)

for target in "${!TARGETS[@]}"; do
asset_name="${TARGETS[$target]}"
if [ "$target" = "x86_64-pc-windows-msvc" ]; then
cp "artifacts/binary-${target}/marknest.exe" "release/${asset_name}.exe"
else
cp "artifacts/binary-${target}/marknest" "release/${asset_name}"
chmod +x "release/${asset_name}"
tar -czf "release/${asset_name}.tar.gz" -C release "${asset_name}"
rm "release/${asset_name}"
fi
done

- name: Get previous tag
id: prev_tag
run: |
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
echo "tag=${PREV_TAG}" >> "$GITHUB_OUTPUT"

- name: Build contributors list
id: contributors
run: |
if [ -n "${{ steps.prev_tag.outputs.tag }}" ]; then
CONTRIBUTORS=$(git log ${{ steps.prev_tag.outputs.tag }}..HEAD --format='%an' | sort -u | grep -v 'dependabot' || true)
else
CONTRIBUTORS=$(git log --format='%an' | sort -u | grep -v 'dependabot' || true)
fi
# Write to file to avoid quoting issues
echo "$CONTRIBUTORS" > /tmp/contributors.txt

- name: Create GitHub Release
env:
GH_TOKEN: ${{ github.token }}
run: |
TAG="${GITHUB_REF_NAME}"
CONTRIBUTORS=$(cat /tmp/contributors.txt)

BODY="## Installation

### Cargo
\`\`\`sh
cargo install marknest
\`\`\`

### npm / npx
\`\`\`sh
npx marknest --help
# or install globally
npm install -g marknest
\`\`\`

### Download binary
Download the appropriate binary for your platform from the assets below.

## Contributors
${CONTRIBUTORS}
"

gh release create "$TAG" release/* \
--title "$TAG" \
--notes "$BODY"
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ dist/
.claude/
validation/.cache/
validation/.runs/

# npm platform package binaries (populated during CI release)
npm/marknest-*/bin/
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@ resolver = "2"
edition = "2024"
license = "MIT"
version = "0.1.0"
repository = "https://github.com/developer0hye/marknest"
homepage = "https://github.com/developer0hye/marknest"

[workspace.dependencies]
ammonia = "4.1.2"
emojis = "0.6.4"
marknest-core = { version = "0.1.0", path = "crates/marknest-core" }
marknest = { version = "0.1.0", path = "crates/marknest" }
pulldown-cmark = "0.13"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
Expand Down
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2025 Yonghye Kwon

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,41 @@ Key capabilities:
- `validation/`: pinned 60-entry README corpus manifest, baseline artifacts, and hybrid PDF fidelity validator
- `Dockerfile`: shared CLI and fallback-server runtime image

## Installation

### Cargo

```bash
cargo install marknest
```

### npm / npx

```bash
# Run directly
npx marknest validate README.md

# Or install globally
npm install -g marknest
marknest convert README.md -o output.pdf
```

### From source

```bash
git clone https://github.com/developer0hye/marknest.git
cd marknest
cargo install --path crates/marknest
```

For PDF rendering, install the Playwright headless shell:

```bash
npx playwright install chromium
# or for headless-only environments:
npx playwright install --with-deps chromium
```

## Development

Run the formatter:
Expand Down
6 changes: 6 additions & 0 deletions crates/marknest-core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
[package]
name = "marknest-core"
description = "Core library for Markdown workspace analysis, ZIP inspection, and self-contained HTML rendering"
edition.workspace = true
license.workspace = true
version.workspace = true
repository.workspace = true
homepage.workspace = true
readme = "../../README.md"
keywords = ["markdown", "pdf", "html", "converter", "documentation"]
categories = ["command-line-utilities", "text-processing", "web-programming"]

[dependencies]
ammonia.workspace = true
Expand Down
10 changes: 8 additions & 2 deletions crates/marknest-server/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
[package]
name = "marknest-server"
description = "Local HTTP fallback service for high-quality Markdown-to-PDF conversion via Playwright"
edition.workspace = true
license.workspace = true
version.workspace = true
repository.workspace = true
homepage.workspace = true
readme = "../../README.md"
keywords = ["markdown", "pdf", "server", "converter"]
categories = ["command-line-utilities", "web-programming"]

[dependencies]
axum = { version = "0.8", features = ["multipart"] }
marknest = { path = "../marknest" }
marknest-core = { path = "../marknest-core" }
marknest.workspace = true
marknest-core.workspace = true
serde.workspace = true
serde_json.workspace = true
tokio = { version = "1", features = ["rt-multi-thread", "macros", "net"] }
Expand Down
6 changes: 5 additions & 1 deletion crates/marknest-wasm/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
[package]
name = "marknest-wasm"
description = "WASM bindings for MarkNest Markdown workspace analysis and HTML rendering"
edition.workspace = true
license.workspace = true
version.workspace = true
repository.workspace = true
homepage.workspace = true
publish = false

[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
console_error_panic_hook = "0.1"
marknest-core = { path = "../marknest-core" }
marknest-core.workspace = true
serde.workspace = true
serde_json.workspace = true
serde-wasm-bindgen = "0.6"
Expand Down
Loading