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
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ on:
env:
CARGO_TERM_COLOR: always
MIX_ENV: test
ECTO_LIBSQL_BUILD: "true"

jobs:
rust-checks:
Expand Down
94 changes: 94 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
name: Build Precompiled NIFs

on:
push:
tags:
- "v*"
workflow_dispatch:

permissions:
contents: write

jobs:
build_release:
name: NIF ${{ matrix.nif }} - ${{ matrix.job.target }} (${{ matrix.job.os }})
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: false
matrix:
nif: ["2.15"]
job:
- { target: aarch64-apple-darwin, os: macos-15 }
- { target: x86_64-apple-darwin, os: macos-15-intel }
- { target: aarch64-unknown-linux-gnu, os: ubuntu-24.04, use-cross: true }
- { target: aarch64-unknown-linux-musl, os: ubuntu-24.04, use-cross: true }
- { target: x86_64-unknown-linux-gnu, os: ubuntu-24.04 }
- { target: x86_64-unknown-linux-musl, os: ubuntu-24.04, use-cross: true }

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Extract and validate project version
shell: bash
run: |
# Extract version from mix.exs @version attribute
VERSION=$(sed -n 's/^[[:space:]]*@version "\(.*\)"/\1/p' mix.exs | head -n1)
if [ -z "$VERSION" ]; then
echo "::error::Failed to extract version from mix.exs"
exit 1
fi

# Check if this is a tag push or workflow_dispatch
if [[ "$GITHUB_REF" == refs/tags/* ]]; then
# Extract git tag from GITHUB_REF_NAME (e.g., "v0.8.10")
GIT_TAG="${GITHUB_REF_NAME}"

# Validate that git tag matches mix.exs version
# Git tag should be v<version>, so strip the 'v' prefix for comparison
TAG_VERSION="${GIT_TAG#v}"

if [ "$VERSION" != "$TAG_VERSION" ]; then
echo "::error::Version mismatch: mix.exs version is '$VERSION' but git tag is '$GIT_TAG' (without 'v': '$TAG_VERSION')"
exit 1
fi

echo "✓ Version validation passed: mix.exs version '$VERSION' matches git tag '$GIT_TAG'"
else
# workflow_dispatch or other non-tag trigger
echo "ℹ Using version from mix.exs: '$VERSION' (not a tag push)"
fi

echo "PROJECT_VERSION=$VERSION" >> $GITHUB_ENV

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

- name: Build the project
id: build-crate
uses: philss/rustler-precompiled-action@v1.1.4
env:
CARGO_BUILD_TARGET: ${{ matrix.job.target }}
with:
project-name: ecto_libsql
project-version: ${{ env.PROJECT_VERSION }}
target: ${{ matrix.job.target }}
nif-version: ${{ matrix.nif }}
use-cross: ${{ matrix.job.use-cross }}
cross-version: "from-source"
project-dir: "native/ecto_libsql"

- name: Artifact upload
uses: actions/upload-artifact@v4
with:
name: ${{ steps.build-crate.outputs.file-name }}
path: ${{ steps.build-crate.outputs.file-path }}

- name: Publish archives and packages
uses: softprops/action-gh-release@v2
with:
files: |
${{ steps.build-crate.outputs.file-path }}
if: startsWith(github.ref, 'refs/tags/')
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ resolver = "2"

members = ["native/ecto_libsql"]
exclude = ["native/ecto_libsql/fuzz"]

[profile.release]
lto = "thin"
22 changes: 20 additions & 2 deletions lib/ecto_libsql/native.ex
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,27 @@ defmodule EctoLibSql.Native do

"""

use Rustler,
mix_config = Mix.Project.config()
version = mix_config[:version]
github_url = mix_config[:package][:links]["GitHub"]
mode = if Mix.env() in [:dev, :test], do: :debug, else: :release

use RustlerPrecompiled,
otp_app: :ecto_libsql,
crate: :ecto_libsql
crate: "ecto_libsql",
version: version,
base_url: "#{github_url}/releases/download/v#{version}",
targets: ~w(
aarch64-apple-darwin
aarch64-unknown-linux-gnu
aarch64-unknown-linux-musl
x86_64-apple-darwin
x86_64-unknown-linux-gnu
x86_64-unknown-linux-musl
),
nif_versions: ["2.15"],
mode: mode,
force_build: System.get_env("ECTO_LIBSQL_BUILD") in ["1", "true"]

# Raw NIF functions - implemented in Rust (native/ecto_libsql/src/lib.rs)
# These all raise :nif_not_loaded errors until the NIF is loaded
Expand Down
8 changes: 6 additions & 2 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ defmodule EctoLibSql.MixProject do

@version "0.8.9"
@source_url "https://github.com/ocean/ecto_libsql"
@dev? String.ends_with?(@version, "-dev")
@force_build? System.get_env("ECTO_LIBSQL_BUILD") in ["1", "true"]

def project do
[
Expand Down Expand Up @@ -55,7 +57,8 @@ defmodule EctoLibSql.MixProject do
{:ecto_sql, "~> 3.11"},
{:ex_doc, "~> 0.31", only: :dev, runtime: false},
{:jason, "~> 1.4"},
{:rustler, "~> 0.37.1"},
{:rustler, "~> 0.37.1", optional: not (@dev? or @force_build?)},
{:rustler_precompiled, "~> 0.8"},
{:sobelow, "~> 0.13", only: [:dev, :test], runtime: false},
{:stream_data, "~> 1.0", only: [:dev, :test]}
]
Expand All @@ -64,7 +67,8 @@ defmodule EctoLibSql.MixProject do
defp package() do
[
name: "ecto_libsql",
files: ~w(lib priv .formatter.exs mix.exs README.md LICENSE CHANGELOG.md USAGE.md native),
files:
~w(lib priv .formatter.exs mix.exs README.md LICENSE CHANGELOG.md USAGE.md ECTO_MIGRATION_GUIDE.md SECURITY.md native checksum-*.exs),
licenses: ["Apache-2.0"],
links: %{
"GitHub" => @source_url,
Expand Down
2 changes: 2 additions & 0 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
%{
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
"castore": {:hex, :castore, "1.0.17", "4f9770d2d45fbd91dcf6bd404cf64e7e58fed04fadda0923dc32acca0badffa2", [:mix], [], "hexpm", "12d24b9d80b910dd3953e165636d68f147a31db945d2dcb9365e441f8b5351e5"},
"credo": {:hex, :credo, "1.7.16", "a9f1389d13d19c631cb123c77a813dbf16449a2aebf602f590defa08953309d4", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "d0562af33756b21f248f066a9119e3890722031b6d199f22e3cf95550e4f1579"},
"db_connection": {:hex, :db_connection, "2.9.0", "a6a97c5c958a2d7091a58a9be40caf41ab496b0701d21e1d1abff3fa27a7f371", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "17d502eacaf61829db98facf6f20808ed33da6ccf495354a41e64fe42f9c509c"},
"decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"},
Expand All @@ -16,6 +17,7 @@
"makeup_erlang": {:hex, :makeup_erlang, "1.0.3", "4252d5d4098da7415c390e847c814bad3764c94a814a0b4245176215615e1035", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "953297c02582a33411ac6208f2c6e55f0e870df7f80da724ed613f10e6706afd"},
"nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"},
"rustler": {:hex, :rustler, "0.37.1", "721434020c7f6f8e1cdc57f44f75c490435b01de96384f8ccb96043f12e8a7e0", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "24547e9b8640cf00e6a2071acb710f3e12ce0346692e45098d84d45cdb54fd79"},
"rustler_precompiled": {:hex, :rustler_precompiled, "0.8.4", "700a878312acfac79fb6c572bb8b57f5aae05fe1cf70d34b5974850bbf2c05bf", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "3b33d99b540b15f142ba47944f7a163a25069f6d608783c321029bc1ffb09514"},
"sobelow": {:hex, :sobelow, "0.14.1", "2f81e8632f15574cba2402bcddff5497b413c01e6f094bc0ab94e83c2f74db81", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8fac9a2bd90fdc4b15d6fca6e1608efb7f7c600fa75800813b794ee9364c87f2"},
"stream_data": {:hex, :stream_data, "1.2.0", "58dd3f9e88afe27dc38bef26fce0c84a9e7a96772b2925c7b32cd2435697a52b", [:mix], [], "hexpm", "eb5c546ee3466920314643edf68943a5b14b32d1da9fe01698dc92b73f89a9ed"},
"telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"},
Expand Down
20 changes: 20 additions & 0 deletions native/ecto_libsql/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Cross-compilation configuration for rustler_precompiled NIF builds.
#
# macOS: Use dynamic lookup for Erlang NIF symbols (resolved at load time).
# musl: Disable static CRT linking for compatibility with BEAM's dynamic loader.

[target.'cfg(target_os = "macos")']
rustflags = [
"-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup",
]

[target.x86_64-unknown-linux-musl]
rustflags = [
"-C", "target-feature=-crt-static",
]

[target.aarch64-unknown-linux-musl]
rustflags = [
"-C", "target-feature=-crt-static",
]
2 changes: 1 addition & 1 deletion native/ecto_libsql/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ crate-type = ["cdylib", "rlib"]

[dependencies]
libsql = { version = "0.9.29", features = ["encryption", "replication"] }
rustler = "0.37.0"
rustler = { version = "0.37.1", default-features = false, features = ["derive", "nif_version_2_15"] }
tokio = "1.45.1"
uuid = "1.17.0"
bytes = "1.5"
Expand Down