Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

243 lines (222 sloc) 10.708 kB
%% -*- Mode: Erlang; tab-width: 4 -*-
-module(agner_github).
-behaviour(agner_index).
-include_lib("agner.hrl").
-include_lib("agner_index.hrl").
-include_lib("kernel/include/file.hrl").
-export([repositories/1,
repository/2,
tags/2,
branches/2,
spec/3,
spec_url/3
]).
repositories(Account) ->
repositories(Account, 1).
repositories(Account, Page) ->
case request("https://github.com/api/v2/json/repos/show/" ++ Account ++ "?page=" ++ integer_to_list(Page)) of
{error, _Reason} = Error ->
Error;
Object ->
case proplists:get_value(<<"repositories">>, Object) of
[] ->
[];
Repositories ->
Repos = lists:filter(fun
({_,undefined}) ->
false;
({invalid,_}) ->
false;
(_) ->
true
end,
lists:map(fun (RepObject) ->
{repo_name(proplists:get_value(<<"name">>, RepObject)),
proplists:get_value(<<"pushed_at">>, RepObject)}
end, Repositories)),
Repos ++ repositories(Account, Page + 1)
end
end.
repository(Account, Name) ->
case request("https://github.com/api/v2/json/repos/show/" ++ proper_repo_name(Account, Name)) of
{error, _Reason} = Error ->
Error;
Object ->
proplists:get_value(<<"repository">>, Object)
end.
tags(Account, Name) ->
{ok, RepoServer} = agner_repo_server:create(Name, {flavour, "master"}),
ok = agner_repo_server:clone(RepoServer, fun (N) -> "git://github.com/" ++ proper_repo_name(Account, N) ++ ".git" end),
Path = agner_repo_server:file(RepoServer, []),
Port = agner_download:git(["tag", "-l"], [{cd, Path}, use_stdio, stderr_to_stdout, {line, 255}]),
PortHandler = fun (F,Acc) ->
receive
{'EXIT', Port, _} ->
error;
{Port,{exit_status,0}} ->
{ok, Acc};
{Port,{exit_status,_}} ->
error;
{Port, {data, {_, D}}} when is_list(D) ->
Tag = string:strip(D, right, $\n),
F(F,[Tag|Acc]);
_ ->
F(F,Acc)
end
end,
Result = PortHandler(PortHandler,[]),
case Result of
error ->
[];
{ok, Tags} ->
lists:map(fun (Tag) ->
RevParsePort = agner_download:git(["rev-parse",Tag],
[{cd, Path}, use_stdio, stderr_to_stdout, {line, 255}]),
RevParsePortHandler = fun (F1,Acc1) ->
receive
{'EXIT', RevParsePort, _} ->
"";
{RevParsePort,{exit_status,0}} ->
Acc1;
{RevParsePort, {exit_status, _}} ->
"";
{RevParsePort, {data, {_, D1}}} when is_list (D1) ->
F1(F1,D1);
_ ->
F1(F1,Acc1)
end
end,
SHA1 = RevParsePortHandler(RevParsePortHandler,""),
{Tag, SHA1}
end, Tags)
end.
branches(Account, Name) ->
{ok, RepoServer} = agner_repo_server:create(Name, {flavour, "master"}),
ok = agner_repo_server:clone(RepoServer, fun (N) -> "git://github.com/" ++ proper_repo_name(Account, N) ++ ".git" end),
Path = agner_repo_server:file(RepoServer, []),
Port = agner_download:git(["branch", "-r"], [{cd, Path}, use_stdio, stderr_to_stdout, {line, 255}]),
PortHandler = fun (F,Acc) ->
receive
{'EXIT', Port, _} ->
error;
{Port,{exit_status,0}} ->
{ok, Acc};
{Port,{exit_status,_}} ->
error;
{Port, {data, {_, " origin/HEAD" ++ _}}} -> %% ignore
F(F, Acc);
{Port, {data, {_, " origin/" ++ Branch}}} when is_list(Branch) ->
F(F,[Branch|Acc]);
{Port, {data, {_, _}}} -> %% ignore as well
F(F,Acc);
_ ->
F(F,Acc)
end
end,
Result = PortHandler(PortHandler,[]),
case Result of
error ->
[];
{ok, Branches} ->
lists:map(fun (Branch) ->
RevParsePort = agner_download:git(["rev-parse",Branch],
[{cd, Path}, use_stdio, stderr_to_stdout, {line, 255}]),
RevParsePortHandler = fun (F1,Acc1) ->
receive
{'EXIT', RevParsePort, _} ->
"";
{RevParsePort,{exit_status,0}} ->
Acc1;
{RevParsePort, {exit_status, _}} ->
"";
{RevParsePort, {data, {_, D1}}} when is_list (D1) ->
F1(F1,D1);
_ ->
F1(F1,Acc1)
end
end,
SHA1 = RevParsePortHandler(RevParsePortHandler,""),
{Branch, SHA1}
end, Branches)
end.
spec(Account, Name, Version) ->
{ok, RepoServer} = agner_repo_server:create(Name, Version),
case agner_repo_server:pushed_at(RepoServer) of
At when is_list(At) ->
DotDir = filename:join("/tmp","agner"),
filelib:ensure_dir(DotDir ++ "/"),
{ok, #file_info{mode = Mode}} = file:read_file_info(DotDir),
file:change_mode(DotDir, Mode bor 8#0222 bor 8#0444),
AtFilename = filename:join([DotDir, "cache." ++ Name ++ integer_to_list(erlang:phash2(Version)) ++
lists:map(fun ($/) ->
$_;
($\s) ->
$_;
(C) ->
C
end, At)]),
spec_1(Account, RepoServer, AtFilename);
undefined ->
{A,B,C} = now(),
N = node(),
TmpFile = lists:flatten(io_lib:format("/tmp/agner-~p-~p.~p.~p",[N,A,B,C])),
Result = spec_1(Account, RepoServer, TmpFile),
file:delete(TmpFile),
Result
end.
spec_1(Account, RepoServer, AtFilename) ->
Spec =
case file:read_file_info(AtFilename) of
{error, enoent} ->
case agner_repo_server:clone(RepoServer, fun (Name) -> "git://github.com/" ++ proper_repo_name(Account, Name) ++ ".git" end) of
ok ->
Config = agner_repo_server:file(RepoServer, "agner.config"),
{ok, S} = file:consult(Config),
{ok, _} = file:copy(Config, AtFilename),
{ok, #file_info{mode = Mode}} = file:read_file_info(AtFilename),
file:change_mode(AtFilename, Mode bor 8#00444),
S;
_ ->
{error, not_found}
end;
{ok, _} ->
{ok, S} = file:consult(AtFilename),
S
end,
agner_spec:normalize(Spec).
spec_url(Account, Name, SHA1) ->
"https://github.com/" ++ proper_repo_name(Account, Name) ++ "/blob/" ++ SHA1 ++ "/agner.config".
%%%
proper_repo_name(Account, Name) ->
case string:tokens(Name,"/") of
[_, _]=L ->
string:join(L,"/") ++ ".agner";
[Name] ->
string:join([Account, Name],"/") ++ ".agner"
end.
%%%
request(URL) ->
parse_response(httpc_request(URL)).
httpc_request(URL) ->
httpc_request_1(URL,[]).
httpc_request_1(URL, Opts) ->
httpc:request(get,{URL,
[]},
[{timeout, 60000}],
[{body_format, binary}|Opts],
agner).
parse_response({ok, {{"HTTP/1.1",200,_},_Headers,Body}}) ->
jsx:json_to_term(Body);
parse_response({ok, {{"HTTP/1.1",404,_},_Headers,_Body}}) ->
{error, not_found};
parse_response({ok, {{"HTTP/1.1",403,_}, _Headers, _Body}}) ->
{error, github_rate_limit_exceeded}.
%%%
repo_name(B) when is_binary(B) ->
S = binary_to_list(B),
case string:tokens(S,".") of
[Name,"agner"] ->
Name;
_ ->
invalid
end.
Jump to Line
Something went wrong with that request. Please try again.