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
4 changes: 4 additions & 0 deletions java/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
/build/

# Gradle
.gradle/
build/
60 changes: 55 additions & 5 deletions java/README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,70 @@
# Rustwright Java alpha binding

This dependency-free Java 23 package uses the finalized `java.lang.foreign` API to load an explicit Rustwright C ABI dynamic library. It owns and serializes native browser/page handles, copies native errors immediately, and releases all transferred Rust strings, screenshot buffers, and opaque handles with their matching ABI free function.
This dependency-free Java 23 package uses the finalized `java.lang.foreign` API. Published JARs bundle the Rustwright C ABI native libraries, while from-source and exact-path loading remain available. The binding owns and serializes native browser/page handles, copies native errors immediately, and releases all transferred Rust strings, screenshot buffers, and opaque handles with their matching ABI free function.

## Install (not yet published)

The planned Maven Central coordinates are `io.github.skyvern-ai:rustwright:0.1.1`. The artifact is not yet published. Once it is available, add it with Gradle:

```kotlin
dependencies {
implementation("io.github.skyvern-ai:rustwright:0.1.1")
}
```

Or with Maven:

```xml
<dependency>
<groupId>io.github.skyvern-ai</groupId>
<artifactId>rustwright</artifactId>
<version>0.1.1</version>
</dependency>
```

The JAR selects and extracts one of these resources when `new Chromium()` is used:

| Runtime | JAR resource |
| --- | --- |
| macOS arm64 | `native/osx-aarch64/librustwright_capi.dylib` |
| macOS x86-64 | `native/osx-x86_64/librustwright_capi.dylib` |
| Linux x86-64 | `native/linux-x86_64/librustwright_capi.so` |
| Linux arm64 | `native/linux-aarch64/librustwright_capi.so` |
| Windows x86-64 | `native/windows-x86_64/librustwright_capi.dll` |

An explicit `new Chromium(path)` remains an exact pin: it never substitutes a bundled or fallback library. Without an explicit path, the resolver tries the bundled resource first and then `target/release` for from-source use.

## Requirements

- A 64-bit JDK 23 (`java` and `javac` on `PATH`)
- The shared library at `target/release/librustwright_capi.dylib` on macOS or `target/release/librustwright_capi.so` on Linux
- A 64-bit JDK 23 or newer (`java` and `javac` on `PATH`); compilation targets Java 23
- Gradle 9 (the checked-in wrapper downloads it automatically)
- For from-source use, the shared library at `target/release/librustwright_capi.dylib` on macOS or `target/release/librustwright_capi.so` on Linux
- `--enable-native-access=ALL-UNNAMED` (already supplied by `run.sh`)

Run all commands from the repository root. On macOS, the exact smoke command is:
## From source

Run all commands from the repository root. The existing dependency-free `run.sh` entrypoint remains supported. On macOS, the exact smoke command is:

```sh
./java/run.sh smoke --lib target/release/librustwright_capi.dylib
```

The smoke command also accepts no arguments, defaulting to `target/release/librustwright_capi.dylib` on macOS and `target/release/librustwright_capi.so` on Linux, relative to the repository-root working directory.
The smoke command also accepts no arguments. It first tries a native bundled in the classpath and then defaults to `target/release/librustwright_capi.dylib` on macOS or `target/release/librustwright_capi.so` on Linux, relative to the repository-root working directory.

Build the Gradle artifacts from `java/`:

```sh
./gradlew build
./gradlew publishToMavenLocal
```

Release automation downloads natives beneath per-platform directories and stages them with:

```sh
./java/scripts/stage-natives.sh <native-artifact-directory> --require-all
```

For a local one-platform build, omit `--require-all`. Staged binaries live below `java/src/main/resources/native/` and are ignored by git.

The exact five-case runner command is:

Expand Down
91 changes: 91 additions & 0 deletions java/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import com.vanniktech.maven.publish.JavaLibrary
import com.vanniktech.maven.publish.JavadocJar
import com.vanniktech.maven.publish.SourcesJar
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.api.tasks.javadoc.Javadoc
import org.gradle.jvm.tasks.Jar

plugins {
`java-library`
id("com.vanniktech.maven.publish.base") version "0.37.0"
}

group = "io.github.skyvern-ai"
version = "0.1.1"

tasks.withType<JavaCompile>().configureEach {
options.encoding = "UTF-8"
options.release = 23
}

tasks.withType<Javadoc>().configureEach {
options.encoding = "UTF-8"
}

tasks.withType<Jar>().configureEach {
isPreserveFileTimestamps = false
isReproducibleFileOrder = true
if (name == "sourcesJar" || name == "plainSourcesJar") {
exclude("native/**")
}
}

val contractSelfTest = tasks.register<JavaExec>("contractSelfTest") {
group = LifecycleBasePlugin.VERIFICATION_GROUP
description = "Runs the dependency-free Java binding contract self-test."
classpath = sourceSets.test.get().runtimeClasspath
mainClass = "com.skyvern.rustwright.ContractSelfTest"
}

tasks.check {
dependsOn(contractSelfTest)
}

tasks.test {
// ContractSelfTest is a dependency-free main class, not a JUnit test.
failOnNoDiscoveredTests = false
}

mavenPublishing {
configure(JavaLibrary(
javadocJar = JavadocJar.Javadoc(),
sourcesJar = SourcesJar.Sources(),
))
coordinates("io.github.skyvern-ai", "rustwright", "0.1.1")
publishToMavenCentral(automaticRelease = true)

// Maven-local builds intentionally stay unsigned. The guarded release job
// supplies this property and therefore signs every Central artifact.
if (providers.gradleProperty("signingInMemoryKey").isPresent) {
signAllPublications()
}

pom {
name = "Rustwright Java"
description = "Java bindings for Rustwright, a Rust CDP browser automation engine."
url = "https://github.com/Skyvern-AI/rustwright"
licenses {
license {
name = "MIT License"
url = "https://opensource.org/licenses/MIT"
distribution = "repo"
}
}
developers {
developer {
id = "skyvern-ai"
name = "Skyvern AI"
url = "https://github.com/Skyvern-AI"
}
}
scm {
url = "https://github.com/Skyvern-AI/rustwright"
connection = "scm:git:https://github.com/Skyvern-AI/rustwright.git"
developerConnection = "scm:git:ssh://git@github.com/Skyvern-AI/rustwright.git"
}
}
}

tasks.named("assemble") {
dependsOn("plainJavadocJar", "sourcesJar")
}
Binary file added java/gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
10 changes: 10 additions & 0 deletions java/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.1-bin.zip
distributionSha256Sum=9c0f7faeeb306cb14e4279a3e084ca6b596894089a0638e68a07c945a32c9e14
networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading
Loading