Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate RustlerPrecompiled for Precompiled NIFs & CI/CD Enhancements #4

Merged
merged 12 commits into from
Feb 7, 2025
87 changes: 87 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
name: CI
on:
push:
branches:
- main
pull_request:
workflow_dispatch:

env:
EXSTATIC_BUILD: true
MIX_ENV: test

jobs:
test:
strategy:
matrix:
include:
- otp_version: "27.2"
elixir_version: "1.18"
os: ubuntu-latest

runs-on: ${{ matrix.os }}
name: Test on Elixir ${{ matrix.elixir_version }} / OTP ${{ matrix.otp_version }}
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Elixir and Erlang
uses: erlef/setup-beam@v1
with:
otp-version: ${{ matrix.otp_version }}
elixir-version: ${{ matrix.elixir_version }}

- name: Cache Mix dependencies
uses: actions/cache@v4
id: cache-deps
with:
path: |
deps
_build
_build/test
key: mix-${{ runner.os }}-${{ matrix.elixir_version }}-${{ matrix.otp_version }}-${{ hashFiles('mix.lock') }}
restore-keys: |
mix-${{ runner.os }}-${{ matrix.elixir_version }}-${{ matrix.otp_version }}-

- name: Cache Rust dependencies
uses: Swatinem/rust-cache@v2
with:
workspaces: native/exstatic

- name: Install dependencies
env:
HEX_API_KEY: ${{ secrets.HEX_API_KEY }}
run: mix deps.get

- name: Compile
run: mix compile --warnings-as-errors

- name: Check for unused packages
run: mix deps.unlock --check-unused

- name: Check code formatting
run: mix format --check-formatted

- name: Run Credo (Code Linter)
run: mix credo --strict

- name: Run Dialyzer (Static Analysis)
run: mix dialyzer

- name: Check for abandoned dependencies
run: mix hex.audit

- name: Check for outdated dependencies
run: mix hex.outdated --within-requirements || true

- name: Run tests
run: mix test

- name: Run tests with coverage
run: mix test --cover --export-coverage default

- name: Check test coverage
run: mix test.coverage

- name: Scan for security vulnerabilities
run: mix sobelow --exit --threshold medium
99 changes: 83 additions & 16 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,87 @@
name: Release
name: Compile & Upload NIFs

on:
release:
types: [published]
push:
branches:
- main
paths:
- "native/**"
- ".github/workflows/release.yml"
tags:
- "*"
pull_request:
paths:
- ".github/workflows/release.yml"
workflow_dispatch:

jobs:
publish:
name: Publish
runs-on: ubuntu-22.04
compile_nifs:
name: ${{ matrix.job.platform }} (NIF ${{ matrix.nif }})
runs-on: ${{ matrix.job.os }}

permissions:
contents: write
id-token: write

strategy:
fail-fast: false
matrix:
nif: ["2.16"]
job:
- { target: aarch64-apple-darwin, os: macos-13, platform: "macOS (ARM64)" }
- { target: aarch64-unknown-linux-gnu, os: ubuntu-22.04, use-cross: true, platform: "Linux (ARM64)" }
- { target: x86_64-apple-darwin, os: macos-13, platform: "macOS (x86_64)" }
- { target: x86_64-unknown-linux-gnu, os: ubuntu-22.04, platform: "Linux (x86_64)" }

env:
EXSTATIC_BUILD: true # Ensure RustlerPrecompiled forces a build

steps:
- uses: actions/checkout@v4
- uses: erlef/setup-beam@v1
- name: Checkout source code
uses: actions/checkout@v4

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
elixir-version: 1.18
otp-version: 27.2
- name: Fetch dependencies
run: mix deps.get
- name: Publish package
env:
HEX_API_KEY: ${{ secrets.HEX_API_KEY }}
run: mix hex.publish --organization ${{ vars.HEX_ORG }} --replace --yes
toolchain: stable
targets: ${{ matrix.job.target }}

- name: Install Cross for cross-compilation
if: ${{ matrix.job.use-cross }}
run: cargo install cross --git https://github.com/cross-rs/cross

- name: Extract project version
shell: bash
run: |
echo "PROJECT_VERSION=$(sed -n 's/^ @version "\(.*\)"/\1/p' mix.exs | head -n1)" >> $GITHUB_ENV

- uses: Swatinem/rust-cache@v2
with:
prefix-key: v0-precomp
shared-key: ${{ matrix.job.target }}-${{ matrix.nif }}
workspaces: |
native/exstatic

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

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

- name: Publish NIFs to GitHub Releases
uses: softprops/action-gh-release@v2
with:
files: |
${{ steps.build-crate.outputs.file-path }}
if: startsWith(github.ref, 'refs/tags/')
93 changes: 0 additions & 93 deletions .github/workflows/test.yml

This file was deleted.

10 changes: 9 additions & 1 deletion lib/exstatic/native.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
defmodule Exstatic.Native do
@moduledoc false
use Rustler, otp_app: :exstatic, crate: "exstatic"
version = Mix.Project.config()[:version]

use RustlerPrecompiled,
otp_app: :exstatic,
crate: "exstatic",
base_url: "https://github.com/intellection/exstatic/releases/download/v#{version}",
force_build: System.get_env("EXSTATIC_BUILD") == "true",
nif_versions: ["2.16"],
version: version

@spec normal_pdf(float(), float(), float()) :: float()
def normal_pdf(_mean, _std_dev, _x), do: :erlang.nif_error(:nif_not_loaded)
Expand Down
5 changes: 3 additions & 2 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ defmodule Exstatic.MixProject do
[
name: "exstatic",
organization: "zappi",
files: ~w(lib .formatter.exs mix.exs README*),
files: ~w(lib mix.exs README.md checksum-*.exs),
licenses: ["MIT"],
links: %{"GitHub" => @source_url}
]
Expand All @@ -70,7 +70,8 @@ defmodule Exstatic.MixProject do
# Run "mix help deps" to learn about dependencies.
defp deps do
[
{:rustler, "~> 0.35.1", runtime: false},
{:rustler_precompiled, "~> 0.8"},
{:rustler, ">= 0.0.0", optional: true},
{:credo, "~> 1.7", only: [:dev, :test], runtime: false},
{:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false},
{:ex_doc, "~> 0.34", only: :dev, runtime: false},
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.11", "4bbd584741601eb658007339ea730b082cc61f3554cf2e8f39bf693a11b49073", [:mix], [], "hexpm", "e03990b4db988df56262852f20de0f659871c35154691427a5047f4967a16a62"},
"credo": {:hex, :credo, "1.7.11", "d3e805f7ddf6c9c854fd36f089649d7cf6ba74c42bc3795d587814e3c9847102", [: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", "56826b4306843253a66e47ae45e98e7d284ee1f95d53d1612bb483f88a8cf219"},
"dialyxir": {:hex, :dialyxir, "1.4.5", "ca1571ac18e0f88d4ab245f0b60fa31ff1b12cbae2b11bd25d207f865e8ae78a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b0fb08bb8107c750db5c0b324fa2df5ceaa0f9307690ee3c1f6ba5b9eb5d35c3"},
"earmark_parser": {:hex, :earmark_parser, "1.4.43", "34b2f401fe473080e39ff2b90feb8ddfeef7639f8ee0bbf71bb41911831d77c5", [:mix], [], "hexpm", "970a3cd19503f5e8e527a190662be2cee5d98eed1ff72ed9b3d1a3d466692de8"},
Expand All @@ -19,6 +20,7 @@
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
"req": {:hex, :req, "0.5.8", "50d8d65279d6e343a5e46980ac2a70e97136182950833a1968b371e753f6a662", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "d7fc5898a566477e174f26887821a3c5082b243885520ee4b45555f5d53f40ef"},
"rustler": {:hex, :rustler, "0.35.1", "ec81961ef9ee833d721dafb4449cab29b16b969a3063a842bb9e3ea912f6b938", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}, {:toml, "~> 0.6", [hex: :toml, repo: "hexpm", optional: false]}], "hexpm", "3713b2e70e68ec2bfa8291dfd9cb811fe64a770f254cd9c331f8b34fa7989115"},
"rustler_precompiled": {:hex, :rustler_precompiled, "0.8.2", "5f25cbe220a8fac3e7ad62e6f950fcdca5a5a5f8501835d2823e8c74bf4268d5", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "63d1bd5f8e23096d1ff851839923162096364bac8656a4a3c00d1fff8e83ee0a"},
"sobelow": {:hex, :sobelow, "0.13.0", "218afe9075904793f5c64b8837cc356e493d88fddde126a463839351870b8d1e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cd6e9026b85fc35d7529da14f95e85a078d9dd1907a9097b3ba6ac7ebbe34a0d"},
"telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"},
"toml": {:hex, :toml, "0.7.0", "fbcd773caa937d0c7a02c301a1feea25612720ac3fa1ccb8bfd9d30d822911de", [:mix], [], "hexpm", "0690246a2478c1defd100b0c9b89b4ea280a22be9a7b313a8a058a2408a2fa70"},
Expand Down
2 changes: 1 addition & 1 deletion native/exstatic/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ path = "src/lib.rs"
crate-type = ["cdylib"]

[dependencies]
rustler = "0.35.1"
rustler = { version = "0.35.1", default-features = false, features = ["nif_version_2_16"] }
statrs = "0.18.0"