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
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/[email protected]
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"