Rewrite tasks to use new macro style, rip library to separate repository
This commit is contained in:
parent
5af3350fc0
commit
1c15eda4fa
18 changed files with 116 additions and 555 deletions
|
@ -1,72 +0,0 @@
|
||||||
defmodule AppRunner do
|
|
||||||
def wait_for_eof() do
|
|
||||||
case IO.getn("", 1024) do
|
|
||||||
:eof -> nil
|
|
||||||
_ -> wait_for_eof()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def exec(program, args) do
|
|
||||||
Port.open(
|
|
||||||
{:spawn_executable, program},
|
|
||||||
[
|
|
||||||
:exit_status,
|
|
||||||
:stderr_to_stdout, # Redirect stderr to stdout to log properly
|
|
||||||
args: args,
|
|
||||||
line: 1024
|
|
||||||
]
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_pid(port) do
|
|
||||||
case Port.info(port) do
|
|
||||||
nil ->
|
|
||||||
nil
|
|
||||||
|
|
||||||
info when is_list(info) ->
|
|
||||||
case Keyword.get(info, :os_pid) do
|
|
||||||
nil -> nil
|
|
||||||
pid -> Integer.to_string(pid)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def kill(pid) do
|
|
||||||
System.find_executable("kill") |> System.cmd([pid])
|
|
||||||
end
|
|
||||||
|
|
||||||
def wait_loop(port) do
|
|
||||||
receive do
|
|
||||||
{_, {:data, {:eol, msg}}} ->
|
|
||||||
msg |> :unicode.characters_to_binary(:unicode) |> IO.puts()
|
|
||||||
|
|
||||||
{_, {:data, {:noeol, msg}}} ->
|
|
||||||
msg |> :unicode.characters_to_binary(:unicode) |> IO.write()
|
|
||||||
|
|
||||||
{_, :eof_received} ->
|
|
||||||
get_pid(port) |> kill()
|
|
||||||
:erlang.halt(0)
|
|
||||||
|
|
||||||
{_, :closed} ->
|
|
||||||
:erlang.halt(0)
|
|
||||||
|
|
||||||
{_, {:exit_status, status}} ->
|
|
||||||
:erlang.halt(status)
|
|
||||||
|
|
||||||
{:EXIT, _, _} ->
|
|
||||||
:erlang.halt(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
wait_loop(port)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
[program | args] = System.argv()
|
|
||||||
port = AppRunner.exec(program, args)
|
|
||||||
|
|
||||||
Task.async(fn ->
|
|
||||||
AppRunner.wait_for_eof()
|
|
||||||
:eof_received
|
|
||||||
end)
|
|
||||||
|
|
||||||
AppRunner.wait_loop(port)
|
|
|
@ -1,14 +1,16 @@
|
||||||
defmodule Mix.Tasks.Frontend.Build.Assets do
|
defmodule Mix.Tasks.Frontend.Build.Assets do
|
||||||
use Mix.Task
|
use FBU.BuildTask
|
||||||
import MebeWeb.{FrontendConfs}
|
import MebeWeb.FrontendConfs
|
||||||
|
|
||||||
@shortdoc "Copy other assets to target dir"
|
@shortdoc "Copy other assets to target dir"
|
||||||
|
|
||||||
def run(_) do
|
task _ do
|
||||||
|
in_path = Path.join([node_path(), "bootstrap-sass", "assets", "fonts", "bootstrap"])
|
||||||
|
|
||||||
# Ensure target path exists
|
# Ensure target path exists
|
||||||
out_path = "#{dist_path()}/fonts"
|
out_path = Path.join([dist_path(), "fonts"])
|
||||||
|
|
||||||
File.mkdir_p!(out_path)
|
File.mkdir_p!(out_path)
|
||||||
File.cp_r!("#{node_path()}/bootstrap-sass/assets/fonts/bootstrap", out_path)
|
File.cp_r!(in_path, out_path)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
defmodule Mix.Tasks.Frontend.Build.Css.Compile do
|
defmodule Mix.Tasks.Frontend.Build.Css.Compile do
|
||||||
use Mix.Task
|
use FBU.BuildTask
|
||||||
import MebeWeb.{TaskUtils, FrontendConfs}
|
import MebeWeb.FrontendConfs
|
||||||
|
import FBU.TaskUtils
|
||||||
|
|
||||||
@shortdoc "Build the SCSS sources"
|
@shortdoc "Build the SCSS sources"
|
||||||
|
|
||||||
def bin(), do: node_bin("node-sass")
|
def bin(), do: node_bin("node-sass")
|
||||||
|
|
||||||
def out_path(), do: "#{tmp_path()}/compiled/css"
|
def out_path(), do: Path.join([tmp_path(), "compiled", "css"])
|
||||||
|
|
||||||
def args(), do: [
|
def args(), do: [
|
||||||
"-o",
|
"-o",
|
||||||
|
@ -14,14 +15,14 @@ defmodule Mix.Tasks.Frontend.Build.Css.Compile do
|
||||||
"--source-map",
|
"--source-map",
|
||||||
"true",
|
"true",
|
||||||
"--include-path",
|
"--include-path",
|
||||||
"#{node_path()}/bootstrap-sass/assets/stylesheets",
|
Path.join([node_path(), "boostrap-sass", "assets", "stylesheets"]),
|
||||||
"--precision",
|
"--precision",
|
||||||
"8"
|
"8"
|
||||||
]
|
]
|
||||||
|
|
||||||
def scss_file(), do: "#{src_path()}/css/app.scss"
|
def scss_file(), do: Path.join([src_path(), "css", "app.scss"])
|
||||||
|
|
||||||
def run(_) do
|
task _ do
|
||||||
bin() |> exec(args() ++ [scss_file()]) |> listen()
|
bin() |> exec(args() ++ [scss_file()]) |> listen()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,20 +1,18 @@
|
||||||
defmodule Mix.Tasks.Frontend.Build.Css.Copy do
|
defmodule Mix.Tasks.Frontend.Build.Css.Copy do
|
||||||
use Mix.Task
|
use FBU.BuildTask
|
||||||
import MebeWeb.{TaskUtils, FrontendConfs}
|
import MebeWeb.FrontendConfs
|
||||||
|
|
||||||
@shortdoc "Copy compiled CSS to target dir"
|
@shortdoc "Copy compiled CSS to target dir"
|
||||||
|
|
||||||
def do_copy() do
|
@deps [
|
||||||
|
"frontend.build.css.compile"
|
||||||
|
]
|
||||||
|
|
||||||
|
task _ do
|
||||||
# Ensure target path exists
|
# Ensure target path exists
|
||||||
out_path = "#{dist_path()}/css"
|
out_path = Path.join([dist_path(), "css"])
|
||||||
File.mkdir_p!(out_path)
|
File.mkdir_p!(out_path)
|
||||||
|
|
||||||
File.cp_r!(Mix.Tasks.Frontend.Build.Css.Compile.out_path(), out_path)
|
File.cp_r!(Mix.Tasks.Frontend.Build.Css.Compile.out_path(), out_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
def run(_) do
|
|
||||||
run_task("frontend.build.css.compile")
|
|
||||||
|
|
||||||
do_copy()
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
defmodule Mix.Tasks.Frontend.Build.Css do
|
defmodule Mix.Tasks.Frontend.Build.Css do
|
||||||
use Mix.Task
|
use FBU.BuildTask
|
||||||
import MebeWeb.TaskUtils
|
import FBU.TaskUtils
|
||||||
|
|
||||||
@shortdoc "Build the frontend CSS"
|
@shortdoc "Build the frontend CSS"
|
||||||
|
|
||||||
def run(_) do
|
task _ do
|
||||||
task = case Mix.env() do
|
todo = case Mix.env() do
|
||||||
:prod -> "frontend.build.css.minify"
|
:prod -> "frontend.build.css.minify"
|
||||||
_ -> "frontend.build.css.copy"
|
_ -> "frontend.build.css.copy"
|
||||||
end
|
end
|
||||||
|
|
||||||
run_task(task)
|
run_task(todo)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
defmodule Mix.Tasks.Frontend.Build.Css.Minify do
|
defmodule Mix.Tasks.Frontend.Build.Css.Minify do
|
||||||
use Mix.Task
|
use FBU.BuildTask
|
||||||
import MebeWeb.{TaskUtils, FrontendConfs}
|
import MebeWeb.FrontendConfs
|
||||||
|
import FBU.TaskUtils
|
||||||
|
|
||||||
@shortdoc "Minify built CSS files"
|
@shortdoc "Minify built CSS files"
|
||||||
@preferred_cli_env :prod
|
@preferred_cli_env :prod
|
||||||
|
|
||||||
def run(_) do
|
@deps [
|
||||||
run_task("frontend.build.css.compile")
|
"frontend.build.css.compile"
|
||||||
|
]
|
||||||
|
|
||||||
|
task _ do
|
||||||
in_path = Mix.Tasks.Frontend.Build.Css.Compile.out_path()
|
in_path = Mix.Tasks.Frontend.Build.Css.Compile.out_path()
|
||||||
in_file = "#{in_path}/app.css"
|
in_file = Path.join([in_path, "app.css"])
|
||||||
out_path = "#{dist_path()}/css"
|
out_path = Path.join([dist_path(), "css"])
|
||||||
out_file = "#{out_path}/app.css"
|
out_file = Path.join(out_path, "app.css")
|
||||||
|
|
||||||
File.mkdir_p!(out_path)
|
File.mkdir_p!(out_path)
|
||||||
|
|
||||||
|
@ -21,7 +24,7 @@ defmodule Mix.Tasks.Frontend.Build.Css.Minify do
|
||||||
in_file,
|
in_file,
|
||||||
out_file,
|
out_file,
|
||||||
"--sourcemap",
|
"--sourcemap",
|
||||||
"#{out_path}/app.css.map"
|
Path.join([out_path, "app.css.map"])
|
||||||
]
|
]
|
||||||
) |> listen()
|
) |> listen()
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
defmodule Mix.Tasks.Frontend.Build do
|
defmodule Mix.Tasks.Frontend.Build do
|
||||||
use Mix.Task
|
use FBU.BuildTask
|
||||||
import MebeWeb.TaskUtils
|
import FBU.TaskUtils
|
||||||
|
|
||||||
@shortdoc "Build the frontend"
|
@shortdoc "Build the frontend"
|
||||||
|
|
||||||
def run(_) do
|
@deps [
|
||||||
run_task("frontend.clean")
|
"frontend.clean"
|
||||||
|
]
|
||||||
|
|
||||||
|
task _ do
|
||||||
run_tasks([
|
run_tasks([
|
||||||
"frontend.build.js",
|
"frontend.build.js",
|
||||||
"frontend.build.css",
|
"frontend.build.css",
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
defmodule Mix.Tasks.Frontend.Build.Js.Bundle do
|
defmodule Mix.Tasks.Frontend.Build.Js.Bundle do
|
||||||
use Mix.Task
|
use FBU.BuildTask
|
||||||
import MebeWeb.{TaskUtils, FrontendConfs}
|
import MebeWeb.FrontendConfs
|
||||||
|
import FBU.TaskUtils
|
||||||
|
|
||||||
@shortdoc "Bundle the JavaScript sources into app.js"
|
@shortdoc "Bundle the JavaScript sources into app.js"
|
||||||
|
|
||||||
|
@deps [
|
||||||
|
"frontend.build.js.transpile"
|
||||||
|
]
|
||||||
|
|
||||||
def bin(), do: node_bin("rollup")
|
def bin(), do: node_bin("rollup")
|
||||||
|
|
||||||
def out_path(), do: "#{tmp_path()}/bundled/js"
|
def out_path(), do: Path.join([tmp_path(), "bundled", "js"])
|
||||||
|
|
||||||
def args() do
|
def args() do
|
||||||
op = out_path()
|
op = out_path()
|
||||||
|
@ -15,23 +20,17 @@ defmodule Mix.Tasks.Frontend.Build.Js.Bundle do
|
||||||
"--config",
|
"--config",
|
||||||
"rollup.config.js",
|
"rollup.config.js",
|
||||||
"--input",
|
"--input",
|
||||||
"#{Mix.Tasks.Frontend.Build.Js.Transpile.out_path()}/app.js",
|
Path.join([Mix.Tasks.Frontend.Build.Js.Transpile.out_path(), "app.js"]),
|
||||||
"--output",
|
"--output",
|
||||||
"#{op}/app.js",
|
Path.join([op, "app.js"]),
|
||||||
"--format",
|
"--format",
|
||||||
"cjs",
|
"cjs",
|
||||||
"--sourcemap",
|
"--sourcemap",
|
||||||
"#{op}/app.js.map"
|
Path.join([op, "app.js.map"])
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
def do_bundle() do
|
task _ do
|
||||||
bin() |> exec(args()) |> listen()
|
bin() |> exec(args()) |> listen()
|
||||||
end
|
end
|
||||||
|
|
||||||
def run(_) do
|
|
||||||
run_task("frontend.build.js.transpile")
|
|
||||||
|
|
||||||
do_bundle()
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,20 +1,18 @@
|
||||||
defmodule Mix.Tasks.Frontend.Build.Js.Copy do
|
defmodule Mix.Tasks.Frontend.Build.Js.Copy do
|
||||||
use Mix.Task
|
use FBU.BuildTask
|
||||||
import MebeWeb.{TaskUtils, FrontendConfs}
|
import MebeWeb.FrontendConfs
|
||||||
|
|
||||||
@shortdoc "Copy bundled JS to target dir"
|
@shortdoc "Copy bundled JS to target dir"
|
||||||
|
|
||||||
def do_copy() do
|
@deps [
|
||||||
|
"frontend.build.js.bundle"
|
||||||
|
]
|
||||||
|
|
||||||
|
task _ do
|
||||||
# Ensure target path exists
|
# Ensure target path exists
|
||||||
out_path = "#{dist_path()}/js"
|
out_path = Path.join([dist_path(), "js"])
|
||||||
File.mkdir_p!(out_path)
|
File.mkdir_p!(out_path)
|
||||||
|
|
||||||
File.cp_r!(Mix.Tasks.Frontend.Build.Js.Bundle.out_path(), out_path)
|
File.cp_r!(Mix.Tasks.Frontend.Build.Js.Bundle.out_path(), out_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
def run(_) do
|
|
||||||
run_task("frontend.build.js.bundle")
|
|
||||||
|
|
||||||
do_copy()
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
defmodule Mix.Tasks.Frontend.Build.Js do
|
defmodule Mix.Tasks.Frontend.Build.Js do
|
||||||
use Mix.Task
|
use FBU.BuildTask
|
||||||
import MebeWeb.TaskUtils
|
import FBU.TaskUtils
|
||||||
|
|
||||||
@shortdoc "Build the frontend JavaScript"
|
@shortdoc "Build the frontend JavaScript"
|
||||||
|
|
||||||
def run(_) do
|
task _ do
|
||||||
task = case Mix.env() do
|
todo = case Mix.env() do
|
||||||
:prod -> "frontend.build.js.minify"
|
:prod -> "frontend.build.js.minify"
|
||||||
_ -> "frontend.build.js.copy"
|
_ -> "frontend.build.js.copy"
|
||||||
end
|
end
|
||||||
|
|
||||||
run_task(task)
|
run_task(todo)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
defmodule Mix.Tasks.Frontend.Build.Js.Minify do
|
defmodule Mix.Tasks.Frontend.Build.Js.Minify do
|
||||||
use Mix.Task
|
use FBU.BuildTask
|
||||||
import MebeWeb.{TaskUtils, FrontendConfs}
|
import MebeWeb.FrontendConfs
|
||||||
|
import FBU.TaskUtils
|
||||||
|
|
||||||
@shortdoc "Minify built JS files"
|
@shortdoc "Minify built JS files"
|
||||||
@preferred_cli_env :prod
|
@preferred_cli_env :prod
|
||||||
|
|
||||||
def run(_) do
|
@deps [
|
||||||
run_task("frontend.build.js.bundle")
|
"frontend.build.js.bundle"
|
||||||
|
]
|
||||||
|
|
||||||
|
task _ do
|
||||||
in_path = Mix.Tasks.Frontend.Build.Js.Bundle.out_path()
|
in_path = Mix.Tasks.Frontend.Build.Js.Bundle.out_path()
|
||||||
in_file = "#{in_path}/app.js"
|
in_file = Path.join([in_path, "app.js"])
|
||||||
out_path = "#{dist_path()}/js"
|
out_path = Path.join([dist_path(), "js"])
|
||||||
out_file = "#{out_path}/app.js"
|
out_file = Path.join([out_path, "app.js"])
|
||||||
|
|
||||||
File.mkdir_p!(out_path)
|
File.mkdir_p!(out_path)
|
||||||
|
|
||||||
|
@ -19,9 +22,9 @@ defmodule Mix.Tasks.Frontend.Build.Js.Minify do
|
||||||
node_bin("uglifyjs"),
|
node_bin("uglifyjs"),
|
||||||
[
|
[
|
||||||
"--in-source-map",
|
"--in-source-map",
|
||||||
"#{in_path}/app.js.map",
|
Path.join([in_path, "app.js.map"]),
|
||||||
"--source-map",
|
"--source-map",
|
||||||
"#{out_path}/app.js.map",
|
Path.join([out_path, "app.js.map"]),
|
||||||
"--source-map-url",
|
"--source-map-url",
|
||||||
"app.js.map",
|
"app.js.map",
|
||||||
"--screw-ie8",
|
"--screw-ie8",
|
||||||
|
|
|
@ -1,22 +1,23 @@
|
||||||
defmodule Mix.Tasks.Frontend.Build.Js.Transpile do
|
defmodule Mix.Tasks.Frontend.Build.Js.Transpile do
|
||||||
use Mix.Task
|
use FBU.BuildTask
|
||||||
import MebeWeb.{TaskUtils, FrontendConfs}
|
import MebeWeb.FrontendConfs
|
||||||
|
import FBU.TaskUtils
|
||||||
|
|
||||||
@shortdoc "Transpile JS sources to ES5"
|
@shortdoc "Transpile JS sources to ES5"
|
||||||
|
|
||||||
def bin(), do: node_bin("babel")
|
def bin(), do: node_bin("babel")
|
||||||
|
|
||||||
def out_path(), do: "#{tmp_path()}/transpiled/js"
|
def out_path(), do: Path.join([tmp_path(), "transpiled", "js"])
|
||||||
|
|
||||||
def args(), do: [
|
def args(), do: [
|
||||||
"#{src_path()}/js",
|
Path.join([src_path(), "js"]),
|
||||||
"--out-dir",
|
"--out-dir",
|
||||||
out_path(),
|
out_path(),
|
||||||
"--source-maps",
|
"--source-maps",
|
||||||
"inline"
|
"inline"
|
||||||
]
|
]
|
||||||
|
|
||||||
def run(_) do
|
task _ do
|
||||||
bin() |> exec(args()) |> listen()
|
bin() |> exec(args()) |> listen()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
defmodule Mix.Tasks.Frontend.Clean do
|
defmodule Mix.Tasks.Frontend.Clean do
|
||||||
use Mix.Task
|
use FBU.BuildTask
|
||||||
import MebeWeb.{FrontendConfs}
|
import MebeWeb.FrontendConfs
|
||||||
|
|
||||||
@shortdoc "Clean build artifacts"
|
@shortdoc "Clean build artifacts"
|
||||||
|
|
||||||
def run(_) do
|
task _ do
|
||||||
File.rm_rf!(tmp_path())
|
File.rm_rf!(tmp_path())
|
||||||
File.rm_rf!(dist_path())
|
File.rm_rf!(dist_path())
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
defmodule Mix.Tasks.Frontend.Watch do
|
defmodule Mix.Tasks.Frontend.Watch do
|
||||||
use Mix.Task
|
use FBU.BuildTask
|
||||||
import MebeWeb.{TaskUtils}
|
import FBU.TaskUtils
|
||||||
alias Mix.Tasks.Frontend.Build.Js.Transpile, as: TranspileJS
|
alias Mix.Tasks.Frontend.Build.Js.Transpile, as: TranspileJS
|
||||||
alias Mix.Tasks.Frontend.Build.Js.Bundle, as: BundleJS
|
alias Mix.Tasks.Frontend.Build.Js.Bundle, as: BundleJS
|
||||||
alias Mix.Tasks.Frontend.Build.Js.Copy, as: CopyJS
|
alias Mix.Tasks.Frontend.Build.Js.Copy, as: CopyJS
|
||||||
|
@ -9,9 +9,11 @@ defmodule Mix.Tasks.Frontend.Watch do
|
||||||
|
|
||||||
@shortdoc "Watch frontend and rebuild when necessary"
|
@shortdoc "Watch frontend and rebuild when necessary"
|
||||||
|
|
||||||
def run(_) do
|
@deps [
|
||||||
run_task("frontend.build")
|
"frontend.build"
|
||||||
|
]
|
||||||
|
|
||||||
|
task _ do
|
||||||
[
|
[
|
||||||
exec(
|
exec(
|
||||||
TranspileJS.bin(),
|
TranspileJS.bin(),
|
||||||
|
@ -21,13 +23,13 @@ defmodule Mix.Tasks.Frontend.Watch do
|
||||||
watch(
|
watch(
|
||||||
"JSBundle",
|
"JSBundle",
|
||||||
TranspileJS.out_path(),
|
TranspileJS.out_path(),
|
||||||
fn _, _ -> Task.start_link(BundleJS, :do_bundle, []) end
|
fn _, _ -> run_task(BundleJS, deps: false) end
|
||||||
),
|
),
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
"JSCopy",
|
"JSCopy",
|
||||||
BundleJS.out_path(),
|
BundleJS.out_path(),
|
||||||
fn _, _ -> CopyJS.do_copy() end
|
fn _, _ -> run_task(CopyJS, deps: false) end
|
||||||
),
|
),
|
||||||
|
|
||||||
exec(
|
exec(
|
||||||
|
@ -41,8 +43,9 @@ defmodule Mix.Tasks.Frontend.Watch do
|
||||||
watch(
|
watch(
|
||||||
"CSSCopy",
|
"CSSCopy",
|
||||||
CompileCSS.out_path(),
|
CompileCSS.out_path(),
|
||||||
fn _, _ -> CopyCSS.do_copy() end
|
fn _, _ -> run_task(CopyCSS, deps: false) end
|
||||||
)
|
)
|
||||||
] |> listen(watch: true)
|
]
|
||||||
|
|> listen(watch: true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,34 +1,39 @@
|
||||||
defmodule MebeWeb.FrontendConfs do
|
defmodule MebeWeb.FrontendConfs do
|
||||||
import MebeWeb.TaskUtils
|
|
||||||
|
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
Project specific paths and other stuff for Mebe frontend.
|
Project specific paths and other stuff for Mebe frontend.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Get absolute path to root directory of project.
|
||||||
|
"""
|
||||||
|
def proj_path() do
|
||||||
|
Path.expand("../../../", __DIR__)
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Get absolute path to node_modules.
|
Get absolute path to node_modules.
|
||||||
"""
|
"""
|
||||||
def node_path() do
|
def node_path() do
|
||||||
"#{proj_path()}/node_modules"
|
Path.join(proj_path(), "node_modules")
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Get absolute path to binary installed with npm.
|
Get absolute path to binary installed with npm.
|
||||||
"""
|
"""
|
||||||
def node_bin(executable), do: "#{node_path()}/.bin/#{executable}"
|
def node_bin(executable), do: Path.join([node_path(), ".bin", executable])
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Get absolute path to source directory for frontend build.
|
Get absolute path to source directory for frontend build.
|
||||||
"""
|
"""
|
||||||
def src_path(), do: "#{proj_path()}/web/static"
|
def src_path(), do: Path.join([proj_path(), "web", "static"])
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Get absolute path to temp directory for build artifacts.
|
Get absolute path to temp directory for build artifacts.
|
||||||
"""
|
"""
|
||||||
def tmp_path(), do: "#{proj_path()}/.tmp"
|
def tmp_path(), do: Path.join([proj_path(), ".tmp"])
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Get absolute path to target directory for frontend build.
|
Get absolute path to target directory for frontend build.
|
||||||
"""
|
"""
|
||||||
def dist_path(), do: "#{proj_path()}/priv/static"
|
def dist_path(), do: Path.join([proj_path(), "priv", "static"])
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,382 +0,0 @@
|
||||||
defmodule MebeWeb.TaskUtils do
|
|
||||||
@moduledoc """
|
|
||||||
Utilities for project build tasks.
|
|
||||||
"""
|
|
||||||
|
|
||||||
require Logger
|
|
||||||
|
|
||||||
@elixir System.find_executable("elixir")
|
|
||||||
|
|
||||||
@default_task_timeout 60000
|
|
||||||
|
|
||||||
defmodule ProgramSpec do
|
|
||||||
@moduledoc """
|
|
||||||
Program that is executed with arguments. Name is used for prefixing logs.
|
|
||||||
"""
|
|
||||||
defstruct [
|
|
||||||
name: "",
|
|
||||||
port: nil,
|
|
||||||
pending_output: ""
|
|
||||||
]
|
|
||||||
end
|
|
||||||
|
|
||||||
defmodule WatchSpec do
|
|
||||||
@moduledoc """
|
|
||||||
Watch specification, target to watch and callback to execute on events.
|
|
||||||
Name is used for prefixing logs.
|
|
||||||
|
|
||||||
Callback must have arity 2, gets filename/path and list of events as arguments.
|
|
||||||
"""
|
|
||||||
defstruct [
|
|
||||||
name: "",
|
|
||||||
path: "",
|
|
||||||
callback: nil,
|
|
||||||
pid: nil,
|
|
||||||
name_atom: nil
|
|
||||||
]
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Get configuration value.
|
|
||||||
"""
|
|
||||||
def conf(key) when is_atom(key) do
|
|
||||||
Application.get_env(:code_stats, key)
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Get path to project root directory.
|
|
||||||
"""
|
|
||||||
def proj_path() do
|
|
||||||
Path.expand("../../..", __DIR__)
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Get absolute path to a program in $PATH.
|
|
||||||
"""
|
|
||||||
def exec_path(program) do
|
|
||||||
System.find_executable(program)
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Run the given Mix task and wait for it to stop before returning.
|
|
||||||
|
|
||||||
See run_tasks/2 for the argument description.
|
|
||||||
"""
|
|
||||||
def run_task(task, timeout \\ @default_task_timeout) do
|
|
||||||
run_tasks([task], timeout)
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Run the given Mix tasks in parallel and wait for them all to stop
|
|
||||||
before returning.
|
|
||||||
|
|
||||||
Tasks should be tuples {task_name, args} or binaries (no args).
|
|
||||||
"""
|
|
||||||
def run_tasks(tasks, timeout \\ @default_task_timeout) do
|
|
||||||
tasks
|
|
||||||
|> Enum.map(fn
|
|
||||||
task when is_binary(task) ->
|
|
||||||
fn -> Mix.Task.run(task, []) end
|
|
||||||
{task, args} ->
|
|
||||||
fn -> Mix.Task.run(task, args) end
|
|
||||||
end)
|
|
||||||
|> run_funs(timeout)
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Run the given functions in parallel and wait for them all to stop
|
|
||||||
before returning.
|
|
||||||
|
|
||||||
Functions can either be anonymous functions or tuples of
|
|
||||||
{module, fun, args}.
|
|
||||||
"""
|
|
||||||
def run_funs(funs, timeout \\ @default_task_timeout) when is_list(funs) do
|
|
||||||
funs
|
|
||||||
|> Enum.map(fn
|
|
||||||
fun when is_function(fun) ->
|
|
||||||
Task.async(fun)
|
|
||||||
{module, fun, args} ->
|
|
||||||
Task.async(module, fun, args)
|
|
||||||
end)
|
|
||||||
|> Enum.map(fn task ->
|
|
||||||
Task.await(task, timeout)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Start an external program with apprunner.
|
|
||||||
|
|
||||||
Apprunner handles killing the program if BEAM is abruptly shut down.
|
|
||||||
Name is used as a prefix for logging output.
|
|
||||||
|
|
||||||
Options that can be given:
|
|
||||||
|
|
||||||
- name: Use as name for logging, otherwise name of binary is used.
|
|
||||||
- cd: Directory to change to before executing.
|
|
||||||
|
|
||||||
Returns ProgramSpec for the started program.
|
|
||||||
"""
|
|
||||||
def exec(executable, args, opts \\ []) do
|
|
||||||
name = Keyword.get(
|
|
||||||
opts,
|
|
||||||
:name,
|
|
||||||
(executable |> Path.rootname() |> Path.basename())
|
|
||||||
)
|
|
||||||
|
|
||||||
options = [
|
|
||||||
:exit_status, # Send msg with status when command stops
|
|
||||||
args: ["#{proj_path()}/lib/mix/tasks/apprunner.exs" | [executable | args]],
|
|
||||||
line: 1024 # Send command output as lines of 1k length
|
|
||||||
]
|
|
||||||
|
|
||||||
options = case Keyword.get(opts, :cd) do
|
|
||||||
nil -> options
|
|
||||||
cd -> Keyword.put(options, :cd, cd)
|
|
||||||
end
|
|
||||||
|
|
||||||
Logger.debug("[Spawned] Program #{name}")
|
|
||||||
|
|
||||||
%ProgramSpec{
|
|
||||||
name: name,
|
|
||||||
pending_output: "",
|
|
||||||
port: Port.open(
|
|
||||||
{:spawn_executable, @elixir},
|
|
||||||
options
|
|
||||||
)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Start watching a path. Name is used for prefixing logs.
|
|
||||||
|
|
||||||
Path can point to a file or a directory in which case all subdirs will be watched.
|
|
||||||
|
|
||||||
Returns a WatchSpec.
|
|
||||||
"""
|
|
||||||
def watch(name, path, fun) do
|
|
||||||
name_atom = String.to_atom(name)
|
|
||||||
{:ok, pid} = :fs.start_link(name_atom, String.to_charlist(path))
|
|
||||||
:fs.subscribe(name_atom)
|
|
||||||
|
|
||||||
Logger.debug("[Spawned] Watch #{name}")
|
|
||||||
|
|
||||||
%WatchSpec{
|
|
||||||
name: name,
|
|
||||||
path: path,
|
|
||||||
callback: fun,
|
|
||||||
pid: pid,
|
|
||||||
name_atom: name_atom
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Listen to messages from specs and print them to the screen.
|
|
||||||
|
|
||||||
If watch: true is given in the options, will listen for user's enter key and
|
|
||||||
kill programs if enter is pressed.
|
|
||||||
"""
|
|
||||||
def listen(specs, opts \\ [])
|
|
||||||
|
|
||||||
# If there are no specs, stop running
|
|
||||||
def listen([], _), do: :ok
|
|
||||||
|
|
||||||
def listen(spec, opts) when not is_list(spec) do
|
|
||||||
listen([spec], opts)
|
|
||||||
end
|
|
||||||
|
|
||||||
def listen(specs, opts) when is_list(specs) do
|
|
||||||
# Start another task to ask for user input if we are in watch mode
|
|
||||||
task = with \
|
|
||||||
true <- Keyword.get(opts, :watch, false),
|
|
||||||
nil <- Keyword.get(opts, :task),
|
|
||||||
{:ok, task} <- Task.start_link(__MODULE__, :wait_for_input, [self()])
|
|
||||||
do
|
|
||||||
Logger.info("Programs/watches started, press ENTER to exit.")
|
|
||||||
task
|
|
||||||
end
|
|
||||||
|
|
||||||
specs = receive do
|
|
||||||
# User pressed enter
|
|
||||||
:user_input_received ->
|
|
||||||
Logger.info("ENTER received, killing tasks.")
|
|
||||||
Enum.each(specs, &kill/1)
|
|
||||||
[]
|
|
||||||
|
|
||||||
# Program sent output with end of line
|
|
||||||
{port, {:data, {:eol, msg}}} ->
|
|
||||||
program = Enum.find(specs, program_checker(port))
|
|
||||||
msg = :unicode.characters_to_binary(msg, :unicode)
|
|
||||||
|
|
||||||
prefix = "[#{program.name}] #{program.pending_output}"
|
|
||||||
|
|
||||||
Logger.debug(prefix <> msg)
|
|
||||||
|
|
||||||
specs
|
|
||||||
|> Enum.reject(program_checker(port))
|
|
||||||
|> Enum.concat([
|
|
||||||
%{program | pending_output: ""}
|
|
||||||
])
|
|
||||||
|
|
||||||
# Program sent output without end of line
|
|
||||||
{port, {:data, {:noeol, msg}}} ->
|
|
||||||
program = Enum.find(specs, program_checker(port))
|
|
||||||
msg = :unicode.characters_to_binary(msg, :unicode)
|
|
||||||
|
|
||||||
specs
|
|
||||||
|> Enum.reject(program_checker(port))
|
|
||||||
|> Enum.concat([
|
|
||||||
%{program | pending_output: "#{program.pending_output}#{msg}"}
|
|
||||||
])
|
|
||||||
|
|
||||||
# Port was closed normally after being told to close
|
|
||||||
{port, :closed} ->
|
|
||||||
handle_closed(specs, port)
|
|
||||||
|
|
||||||
# Port closed because the program closed by itself
|
|
||||||
{port, {:exit_status, 0}} ->
|
|
||||||
handle_closed(specs, port)
|
|
||||||
|
|
||||||
# Program closed with error status
|
|
||||||
{port, {:exit_status, status}} ->
|
|
||||||
program = Enum.find(specs, program_checker(port))
|
|
||||||
Logger.error("Program #{program.name} returned status #{status}.")
|
|
||||||
raise "Failed status #{status} from #{program.name}!"
|
|
||||||
|
|
||||||
# Port crashed
|
|
||||||
{:EXIT, port, _} ->
|
|
||||||
handle_closed(specs, port)
|
|
||||||
|
|
||||||
# FS watch sent file event
|
|
||||||
{_, {:fs, :file_event}, {file, events}} ->
|
|
||||||
handle_events(specs, file, events)
|
|
||||||
end
|
|
||||||
|
|
||||||
listen(specs, Keyword.put(opts, :task, task))
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Kill a running program returned by exec().
|
|
||||||
"""
|
|
||||||
def kill(%ProgramSpec{name: name, port: port}) do
|
|
||||||
if name != nil do
|
|
||||||
Logger.debug("[Killing] #{name}")
|
|
||||||
end
|
|
||||||
|
|
||||||
send(port, {self(), :close})
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Kill a running watch.
|
|
||||||
"""
|
|
||||||
def kill(%WatchSpec{name: name, pid: pid}) do
|
|
||||||
Logger.debug("[Killing] #{name}")
|
|
||||||
Process.exit(pid, :kill)
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Print file size of given file in human readable form.
|
|
||||||
|
|
||||||
If old file is given as second argument, print the old file's size
|
|
||||||
and the diff also.
|
|
||||||
"""
|
|
||||||
def print_size(new_file, old_file \\ nil) do
|
|
||||||
new_size = get_size(new_file)
|
|
||||||
|
|
||||||
{prefix, postfix} = if old_file != nil do
|
|
||||||
old_size = get_size(old_file)
|
|
||||||
|
|
||||||
{
|
|
||||||
"#{human_size(old_size)} -> ",
|
|
||||||
" Diff: #{human_size(old_size - new_size)}"
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{"", ""}
|
|
||||||
end
|
|
||||||
|
|
||||||
Logger.debug("#{Path.basename(new_file)}: #{prefix}#{human_size(new_size)}.#{postfix}")
|
|
||||||
end
|
|
||||||
|
|
||||||
def wait_for_input(target) do
|
|
||||||
IO.gets("")
|
|
||||||
send(target, :user_input_received)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp get_size(file) do
|
|
||||||
%File.Stat{size: size} = File.stat!(file)
|
|
||||||
size
|
|
||||||
end
|
|
||||||
|
|
||||||
defp human_size(size) do
|
|
||||||
size_units = ["B", "kiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"] # You never know
|
|
||||||
|
|
||||||
human_size(size, size_units)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp human_size(size, [unit | []]), do: size_with_unit(size, unit)
|
|
||||||
|
|
||||||
defp human_size(size, [unit | rest]) do
|
|
||||||
if size > 1024 do
|
|
||||||
human_size(size / 1024, rest)
|
|
||||||
else
|
|
||||||
size_with_unit(size, unit)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp size_with_unit(size, unit) when is_float(size), do: "#{Float.round(size, 2)} #{unit}"
|
|
||||||
|
|
||||||
defp size_with_unit(size, unit), do: "#{size} #{unit}"
|
|
||||||
|
|
||||||
# Utility to find program in spec list based on port
|
|
||||||
defp program_checker(port) do
|
|
||||||
fn
|
|
||||||
%ProgramSpec{port: program_port} ->
|
|
||||||
program_port == port
|
|
||||||
|
|
||||||
%WatchSpec{} ->
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Utility to find watch in spec list based on path
|
|
||||||
defp watch_checker(path) do
|
|
||||||
fn
|
|
||||||
%ProgramSpec{} ->
|
|
||||||
false
|
|
||||||
|
|
||||||
%WatchSpec{path: watch_path} ->
|
|
||||||
# If given path is relative to (under) the watch path or is the same
|
|
||||||
# path completely, it's a match.
|
|
||||||
path != watch_path and Path.relative_to(path, watch_path) != path
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp handle_closed(specs, port) do
|
|
||||||
case Enum.find(specs, program_checker(port)) do
|
|
||||||
%ProgramSpec{} = program ->
|
|
||||||
Logger.debug("[Stopped] #{program.name}")
|
|
||||||
|
|
||||||
specs
|
|
||||||
|> Enum.reject(program_checker(port))
|
|
||||||
|
|
||||||
nil ->
|
|
||||||
# Program was already removed
|
|
||||||
specs
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp handle_events(specs, file, events) do
|
|
||||||
file = to_string(file)
|
|
||||||
case Enum.find(specs, watch_checker(file)) do
|
|
||||||
%WatchSpec{name: name, callback: callback} ->
|
|
||||||
Logger.debug("[#{name}] Changed #{inspect(events)}: #{file}")
|
|
||||||
|
|
||||||
callback.(file, events)
|
|
||||||
|
|
||||||
nil ->
|
|
||||||
# Watch was maybe removed for some reason
|
|
||||||
Logger.debug("[Error] Watch sent event but path was not in specs list: #{inspect(events)} #{file}")
|
|
||||||
end
|
|
||||||
|
|
||||||
specs
|
|
||||||
end
|
|
||||||
end
|
|
2
mix.exs
2
mix.exs
|
@ -40,7 +40,7 @@ defmodule MebeWeb.Mixfile do
|
||||||
{:earmark, "~> 1.0.3"},
|
{:earmark, "~> 1.0.3"},
|
||||||
{:slugger, github: "h4cc/slugger", ref: "ef864669cdaae18d475600589c19c74e92ef67b4"},
|
{:slugger, github: "h4cc/slugger", ref: "ef864669cdaae18d475600589c19c74e92ef67b4"},
|
||||||
{:calendar, "~> 0.17.1"},
|
{:calendar, "~> 0.17.1"},
|
||||||
{:fs, "~> 2.12.0", override: true, only: :dev}
|
{:fbu, github: "Nicd/fbu", only: :dev}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
4
mix.lock
4
mix.lock
|
@ -3,8 +3,8 @@
|
||||||
"cowboy": {:hex, :cowboy, "1.0.4", "a324a8df9f2316c833a470d918aaf73ae894278b8aa6226ce7a9bf699388f878", [:make, :rebar], [{:cowlib, "~> 1.0.0", [hex: :cowlib, optional: false]}, {:ranch, "~> 1.0", [hex: :ranch, optional: false]}]},
|
"cowboy": {:hex, :cowboy, "1.0.4", "a324a8df9f2316c833a470d918aaf73ae894278b8aa6226ce7a9bf699388f878", [:make, :rebar], [{:cowlib, "~> 1.0.0", [hex: :cowlib, optional: false]}, {:ranch, "~> 1.0", [hex: :ranch, optional: false]}]},
|
||||||
"cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], []},
|
"cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], []},
|
||||||
"earmark": {:hex, :earmark, "1.0.3", "89bdbaf2aca8bbb5c97d8b3b55c5dd0cff517ecc78d417e87f1d0982e514557b", [:mix], []},
|
"earmark": {:hex, :earmark, "1.0.3", "89bdbaf2aca8bbb5c97d8b3b55c5dd0cff517ecc78d417e87f1d0982e514557b", [:mix], []},
|
||||||
|
"fbu": {:git, "https://github.com/Nicd/fbu.git", "6b1729f4a7c8607fcd115be5a48fbba9865d0eed", []},
|
||||||
"fs": {:hex, :fs, "2.12.0", "ad631efacc9a5683c8eaa1b274e24fa64a1b8eb30747e9595b93bec7e492e25e", [:rebar3], []},
|
"fs": {:hex, :fs, "2.12.0", "ad631efacc9a5683c8eaa1b274e24fa64a1b8eb30747e9595b93bec7e492e25e", [:rebar3], []},
|
||||||
"fwatch": {:hex, :fwatch, "0.5.0", "c61d9c1653ef5958770165315552e560c01202edd21ec05be9ba737ed44166e0", [:mix], [{:earmark, ">= 0.0.0", [hex: :earmark, optional: false]}, {:fs, "~> 0.9.1", [hex: :fs, optional: false]}]},
|
|
||||||
"hackney": {:hex, :hackney, "1.7.1", "e238c52c5df3c3b16ce613d3a51c7220a784d734879b1e231c9babd433ac1cb4", [:rebar3], [{:certifi, "1.0.0", [hex: :certifi, optional: false]}, {:idna, "4.0.0", [hex: :idna, optional: false]}, {:metrics, "1.0.1", [hex: :metrics, optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, optional: false]}]},
|
"hackney": {:hex, :hackney, "1.7.1", "e238c52c5df3c3b16ce613d3a51c7220a784d734879b1e231c9babd433ac1cb4", [:rebar3], [{:certifi, "1.0.0", [hex: :certifi, optional: false]}, {:idna, "4.0.0", [hex: :idna, optional: false]}, {:metrics, "1.0.1", [hex: :metrics, optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, optional: false]}]},
|
||||||
"idna": {:hex, :idna, "4.0.0", "10aaa9f79d0b12cf0def53038547855b91144f1bfcc0ec73494f38bb7b9c4961", [:rebar3], []},
|
"idna": {:hex, :idna, "4.0.0", "10aaa9f79d0b12cf0def53038547855b91144f1bfcc0ec73494f38bb7b9c4961", [:rebar3], []},
|
||||||
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], []},
|
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], []},
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
"mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], []},
|
"mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], []},
|
||||||
"phoenix": {:hex, :phoenix, "1.2.3", "b68dd6a7e6ff3eef38ad59771007d2f3f344988ea6e658e9b2c6ffb2ef494810", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, optional: true]}, {:phoenix_pubsub, "~> 1.0", [hex: :phoenix_pubsub, optional: false]}, {:plug, "~> 1.4 or ~> 1.3.3 or ~> 1.2.4 or ~> 1.1.8 or ~> 1.0.5", [hex: :plug, optional: false]}, {:poison, "~> 1.5 or ~> 2.0", [hex: :poison, optional: false]}]},
|
"phoenix": {:hex, :phoenix, "1.2.3", "b68dd6a7e6ff3eef38ad59771007d2f3f344988ea6e658e9b2c6ffb2ef494810", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, optional: true]}, {:phoenix_pubsub, "~> 1.0", [hex: :phoenix_pubsub, optional: false]}, {:plug, "~> 1.4 or ~> 1.3.3 or ~> 1.2.4 or ~> 1.1.8 or ~> 1.0.5", [hex: :plug, optional: false]}, {:poison, "~> 1.5 or ~> 2.0", [hex: :poison, optional: false]}]},
|
||||||
"phoenix_html": {:hex, :phoenix_html, "2.9.3", "1b5a2122cbf743aa242f54dced8a4f1cc778b8bd304f4b4c0043a6250c58e258", [:mix], [{:plug, "~> 1.0", [hex: :plug, optional: false]}]},
|
"phoenix_html": {:hex, :phoenix_html, "2.9.3", "1b5a2122cbf743aa242f54dced8a4f1cc778b8bd304f4b4c0043a6250c58e258", [:mix], [{:plug, "~> 1.0", [hex: :plug, optional: false]}]},
|
||||||
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.0.8", "4333f9c74190f485a74866beff2f9304f069d53f047f5fbb0fb8d1ee4c495f73", [:mix], [{:fs, "~> 0.9.1", [hex: :fs, optional: false]}, {:phoenix, "~> 1.0 or ~> 1.2-rc", [hex: :phoenix, optional: false]}]},
|
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.0.7", "167ab0942e88d1d4a597996cf7dd8d2b014cc14d3f9472b58858cde8dd9ac2e4", [:mix], [{:fs, "~> 2.12.0", [hex: :fs, optional: false]}, {:phoenix, "~> 1.0 or ~> 1.2-rc", [hex: :phoenix, optional: false]}]},
|
||||||
"phoenix_pubsub": {:hex, :phoenix_pubsub, "1.0.1", "c10ddf6237007c804bf2b8f3c4d5b99009b42eca3a0dfac04ea2d8001186056a", [:mix], []},
|
"phoenix_pubsub": {:hex, :phoenix_pubsub, "1.0.1", "c10ddf6237007c804bf2b8f3c4d5b99009b42eca3a0dfac04ea2d8001186056a", [:mix], []},
|
||||||
"plug": {:hex, :plug, "1.3.4", "b4ef3a383f991bfa594552ded44934f2a9853407899d47ecc0481777fb1906f6", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, optional: true]}, {:mime, "~> 1.0", [hex: :mime, optional: false]}]},
|
"plug": {:hex, :plug, "1.3.4", "b4ef3a383f991bfa594552ded44934f2a9853407899d47ecc0481777fb1906f6", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, optional: true]}, {:mime, "~> 1.0", [hex: :mime, optional: false]}]},
|
||||||
"poison": {:hex, :poison, "2.2.0", "4763b69a8a77bd77d26f477d196428b741261a761257ff1cf92753a0d4d24a63", [:mix], []},
|
"poison": {:hex, :poison, "2.2.0", "4763b69a8a77bd77d26f477d196428b741261a761257ff1cf92753a0d4d24a63", [:mix], []},
|
||||||
|
|
Reference in a new issue