Skip to main content

mix.exs

defmodule VFS.MixProject do
  use Mix.Project

  @version "0.1.0"
  @source_url "https://github.com/ivarvong/vfs"

  # `coveralls` only exists in :test; without this, a bare `mix check`
  # from the default dev env dies at the last gate step.
  def cli do
    [preferred_envs: [check: :test, coveralls: :test, "coveralls.html": :test]]
  end

  def project do
    [
      app: :vfs,
      version: @version,
      elixir: "~> 1.16",
      elixirc_paths: elixirc_paths(Mix.env()),
      consolidate_protocols: consolidate_protocols?(),
      start_permanent: Mix.env() == :prod,
      deps: deps(),
      aliases: aliases(),
      test_coverage: [tool: ExCoveralls],
      dialyzer: [
        plt_add_apps: [:ex_unit, :stream_data, :mix],
        plt_core_path: "priv/plts",
        plt_local_path: "priv/plts",
        flags: [
          :error_handling,
          :unknown,
          :unmatched_returns,
          :extra_return,
          :missing_return
        ]
      ],
      package: package(),
      description: description(),
      docs: docs(),
      name: "VFS",
      source_url: @source_url
    ]
  end

  # Consolidate protocols everywhere except :test (the suite defines its own
  # VFS.Mountable impls), unless VFS_CONSOLIDATE_PROTOCOLS overrides it — CI sets
  # it to prove release-style consolidation doesn't break protocol dispatch.
  defp consolidate_protocols? do
    case System.get_env("VFS_CONSOLIDATE_PROTOCOLS") do
      "true" -> true
      "false" -> false
      _ -> Mix.env() != :test
    end
  end

  # `dev/` holds maintainer-only Mix tasks (vfs.audit, vfs.mutate); it is
  # compiled in dev/test but never shipped (see package `files:`), so consumers
  # don't get internal tooling injected into their projects.
  defp elixirc_paths(:dev), do: ["lib", "dev"]
  defp elixirc_paths(:test), do: ["lib", "test/support", "dev"]
  defp elixirc_paths(_), do: ["lib"]

  defp deps do
    [
      {:telemetry, "~> 1.3"},
      {:stream_data, "~> 1.1", only: [:dev, :test]},
      {:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false},
      {:credo, "~> 1.7", only: [:dev, :test], runtime: false},
      {:excoveralls, "~> 0.18", only: :test},
      {:ex_doc, "~> 0.34", only: :dev, runtime: false},
      {:benchee, "~> 1.3", only: [:dev, :test], runtime: false},

      # Test-only: real exgit for the integration test set. Validates that
      # VFS.Mountable actually fits a non-trivial real-world backend (lazy
      # partial-clone, content-addressed objects, network transports). The
      # defimpl ships in test/support/ for now; production version moves
      # into :exgit once the protocol shape is stable.
      {:exgit, github: "ivarvong/exgit", only: :test}
    ]
  end

  defp aliases do
    [
      setup: ["deps.get", "dialyzer --plt", &install_pre_commit_hook/1],
      check: [
        "format --check-formatted",
        "compile --warnings-as-errors --force",
        "credo",
        "dialyzer",
        "coveralls --raise"
      ]
    ]
  end

  # CLAUDE.md promises `mix setup` wires the local pre-commit gate.
  defp install_pre_commit_hook(_args) do
    hook = ".git/hooks/pre-commit"

    if File.dir?(Path.dirname(hook)) do
      File.write!(hook, "#!/bin/sh\nexec mix check\n")
      File.chmod!(hook, 0o755)
      Mix.shell().info("Installed #{hook} (runs mix check)")
    end
  end

  defp description do
    "Protocol-based virtual filesystem for the Elixir AI tools stack — pluggable backends, " <>
      "state threading, lazy reads, and a tiny core surface."
  end

  defp package do
    [
      licenses: ["MIT"],
      links: %{"GitHub" => @source_url},
      files: ~w(lib mix.exs README.md SPEC.md CHANGELOG.md LICENSE .formatter.exs)
    ]
  end

  defp docs do
    [
      main: "readme",
      source_ref: "v#{@version}",
      extras: ["README.md", "SPEC.md", "CHANGELOG.md"],
      # Docs build in :dev, where dev/ (maintainer-only Mix tasks) is
      # compiled; without the filter those tasks leak into hexdocs.
      filter_modules: ~r/^Elixir\.VFS(\.|$)/,
      groups_for_modules: [
        Core: [VFS, VFS.Mountable, VFS.Stat, VFS.Path, VFS.Error],
        "Backends & helpers": [VFS.Memory, VFS.Skeleton, VFS.Default, VFS.StreamOptions]
      ]
    ]
  end
end