From 326f8401233d6e7f9e7647d61007ddef6a745025 Mon Sep 17 00:00:00 2001
From: Fred Hebert <mononcqc@ferd.ca>
Date: Sat, 18 Apr 2015 01:45:06 +0000
Subject: Add support for an unlock command

Allows to remove entries from the lock file, or the lock file as a
whole.
---
 src/rebar.app.src        |  1 +
 src/rebar_prv_lock.erl   |  6 +---
 src/rebar_prv_unlock.erl | 84 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 86 insertions(+), 5 deletions(-)
 create mode 100644 src/rebar_prv_unlock.erl

(limited to 'src')

diff --git a/src/rebar.app.src b/src/rebar.app.src
index 63cca9c..b404c42 100644
--- a/src/rebar.app.src
+++ b/src/rebar.app.src
@@ -49,6 +49,7 @@
                      rebar_prv_report,
                      rebar_prv_shell,
                      rebar_prv_tar,
+                     rebar_prv_unlock,
                      rebar_prv_update,
                      rebar_prv_upgrade,
                      rebar_prv_version,
diff --git a/src/rebar_prv_lock.erl b/src/rebar_prv_lock.erl
index f88b1ff..6fdd743 100644
--- a/src/rebar_prv_lock.erl
+++ b/src/rebar_prv_lock.erl
@@ -23,7 +23,7 @@ init(State) ->
                                                                {deps, ?DEPS},
                                                                {example, ""},
                                                                {short_desc, "Locks dependencies."},
-                                                               {desc, info("Locks dependencies")},
+                                                               {desc, "Locks dependencies"},
                                                                {opts, []}])),
     {ok, State1}.
 
@@ -39,9 +39,6 @@ do(State) ->
 format_error(Reason) ->
     io_lib:format("~p", [Reason]).
 
-info(_) ->
-    "".
-
 build_locks(State) ->
     AllDeps = rebar_state:lock(State),
     [begin
@@ -54,4 +51,3 @@ build_locks(State) ->
         ,rebar_fetch:lock_source(Dir, Source, State)
         ,rebar_app_info:dep_level(Dep)}
      end || Dep <- AllDeps, not(rebar_app_info:is_checkout(Dep))].
-
diff --git a/src/rebar_prv_unlock.erl b/src/rebar_prv_unlock.erl
new file mode 100644
index 0000000..dc9fe11
--- /dev/null
+++ b/src/rebar_prv_unlock.erl
@@ -0,0 +1,84 @@
+-module(rebar_prv_unlock).
+
+-behaviour(provider).
+
+-export([init/1,
+         do/1,
+         format_error/1]).
+
+-include("rebar.hrl").
+-include_lib("providers/include/providers.hrl").
+
+-define(PROVIDER, unlock).
+-define(DEPS, []).
+
+%% ===================================================================
+%% Public API
+%% ===================================================================
+
+-spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
+init(State) ->
+    State1 = rebar_state:add_provider(
+        State,
+        providers:create([{name, ?PROVIDER},
+                          {module, ?MODULE},
+                          {bare, false},
+                          {deps, ?DEPS},
+                          {example, ""},
+                          {short_desc, "Unlock dependencies."},
+                          {desc, "Unlock project dependencies. Mentioning no application "
+                                 "will unlock all dependencies. To unlock specific dependencies, "
+                                 "their name can be listed in the command."},
+                          {opts, [
+                            {package, undefined, undefined, string,
+                             "List of packages to upgrade. If not specified, all dependencies are upgraded."}
+                          ]}
+                         ])
+    ),
+    {ok, State1}.
+
+do(State) ->
+    Dir = rebar_state:dir(State),
+    LockFile = filename:join(Dir, ?LOCK_FILE),
+    case file:consult(LockFile) of
+        {error, enoent} ->
+            %% Our work is done.
+            {ok, State};
+        {error, Reason} ->
+            ?PRV_ERROR({file,Reason});
+        {ok, [Locks]} ->
+            case handle_unlocks(State, Locks, LockFile) of
+                ok ->
+                    {ok, State};
+                {error, Reason} ->
+                    ?PRV_ERROR({file,Reason})
+            end;
+        {ok, _Other} ->
+            ?PRV_ERROR(unknown_lock_format)
+    end.
+
+-spec format_error(any()) -> iolist().
+format_error({file, Reason}) ->
+    io_lib:format("Lock file editing failed for reason ~p", [Reason]);
+format_error(unknown_lock_format) ->
+    "Lock file format unknown";
+format_error(Reason) ->
+    io_lib:format("~p", [Reason]).
+
+handle_unlocks(State, Locks, LockFile) ->
+    {Args, _} = rebar_state:command_parsed_args(State),
+    Names = parse_names(ec_cnv:to_binary(proplists:get_value(package, Args, <<"">>))),
+    case [Lock || Lock = {Name, _, _} <- Locks, not lists:member(Name, Names)] of
+        [] ->
+            file:delete(LockFile);
+        _ when Names =:= [] -> % implicitly all locks
+            file:delete(LockFile);
+        NewLocks ->
+            file:write_file(LockFile, io_lib:format("~p.~n", [NewLocks]))
+    end.
+
+parse_names(Bin) ->
+    case lists:usort(re:split(Bin, <<" *, *">>, [trim])) of
+        [<<"">>] -> []; % nothing submitted
+        Other -> Other
+    end.
-- 
cgit v1.1