├── rebar.config ├── .gitignore ├── rebar.lock ├── src ├── rebar3_docker.app.src ├── rebar3_docker.erl ├── rebar3_docker_util.erl └── rebar3_docker_build.erl ├── CHANGELOG.md ├── priv └── templates │ └── Dockerfile ├── README.md └── LICENSE /rebar.config: -------------------------------------------------------------------------------- 1 | {erl_opts, [debug_info]}. 2 | {deps, [bbmustache]}. -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .rebar3 2 | _* 3 | .eunit 4 | *.o 5 | *.beam 6 | *.plt 7 | *.swp 8 | *.swo 9 | .erlang.cookie 10 | ebin 11 | log 12 | erl_crash.dump 13 | .rebar 14 | logs 15 | _build 16 | .idea 17 | *.iml 18 | rebar3.crashdump 19 | *~ 20 | -------------------------------------------------------------------------------- /rebar.lock: -------------------------------------------------------------------------------- 1 | {"1.2.0", 2 | [{<<"bbmustache">>,{pkg,<<"bbmustache">>,<<"1.12.2">>},0}]}. 3 | [ 4 | {pkg_hash,[ 5 | {<<"bbmustache">>, <<"0CABDCE0DB9FE6D3318131174B9F2B351328A4C0AFBEB3E6E99BB0E02E9B621D">>}]}, 6 | {pkg_hash_ext,[ 7 | {<<"bbmustache">>, <<"688B33A4D5CC2D51F575ADF0B3683FC40A38314A2F150906EDCFC77F5B577B3B">>}]} 8 | ]. 9 | -------------------------------------------------------------------------------- /src/rebar3_docker.app.src: -------------------------------------------------------------------------------- 1 | {application, rebar3_docker, 2 | [{description, "A rebar plugin to generate docker containers"}, 3 | {vsn, "0.2.0"}, 4 | {registered, []}, 5 | {applications, [ 6 | kernel, 7 | stdlib 8 | ]}, 9 | {env,[]}, 10 | {modules, []}, 11 | {licenses, ["Apache 2.0"]}, 12 | {links, []} 13 | ]}. 14 | -------------------------------------------------------------------------------- /src/rebar3_docker.erl: -------------------------------------------------------------------------------- 1 | -module(rebar3_docker). 2 | 3 | 4 | %%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 5 | 6 | -export([init/1]). 7 | 8 | 9 | %%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 10 | 11 | -spec init(rebar_state:t()) -> {ok, rebar_state:t()}. 12 | init(State) -> 13 | {ok, AllDeps} = application:get_key(?MODULE, applications), 14 | Deps = AllDeps -- [kernel, stdlib], 15 | [{ok, _} = application:ensure_all_started(A) || A <- Deps], 16 | lists:foldl(fun(Mod, {ok, S}) -> Mod:init(S) end, {ok, State}, [ 17 | rebar3_docker_build 18 | ]). 19 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ## [0.2.0] - 2024-05-09 11 | 12 | ### Added 13 | 14 | * New option to specify the target platform for the build output: [#4](https://github.com/stritzinger/rebar3_docker/pull/4) 15 | * New option to change the base image: [#2](https://github.com/stritzinger/rebar3_docker/pull/2) 16 | 17 | ### Fix 18 | 19 | * Pick the current rebar3 profile: [#1](https://github.com/stritzinger/rebar3_docker/pull/1) 20 | * Handling of Erlang version: [6d0549a](https://github.com/stritzinger/rebar3_docker/commit/6d0549af96f70943d02b7661155db0bf77991e55) 21 | 22 | ## [0.1.0] - 2023-01-26 23 | 24 | ### Added 25 | 26 | * Initial release. 27 | 28 | [Unreleased]: https://github.com/stritzinger/rebar3_docker/compare/0.2.0...HEAD 29 | [0.2.0]: https://github.com/stritzinger/rebar3_docker/compare/0.1.0...0.2.0 30 | [0.1.0]: https://github.com/grisp/rebar3_grisp/compare/4b20c38f0d324da3b53fab86e7b8bc02db1ce178...0.1.0 31 | -------------------------------------------------------------------------------- /priv/templates/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax = docker/dockerfile:experimental 2 | 3 | #--- Builder ------------------------------------------------------------------- 4 | 5 | ARG profile={{profile}} 6 | FROM {{builder_image}} as builder 7 | 8 | WORKDIR /app/src 9 | ENV REBAR_BASE_DIR /app/_build 10 | 11 | # Install git for fetching non-hex depenencies. 12 | # Add any other Alpine libraries needed to compile the project here. 13 | # See https://wiki.alpinelinux.org/wiki/Local_APK_cache for details 14 | # on the local cache and need for the symlink 15 | RUN --mount=type=cache,id=apk,sharing=locked,target=/var/cache/apk \ 16 | ln -s /var/cache/apk /etc/apk/cache && \ 17 | apk add --update \ 18 | {{#build_packages}} 19 | {{name}} \ 20 | {{/build_packages}} 21 | git 22 | 23 | # build and cache dependencies as their own layer 24 | COPY rebar.config rebar.lock ./ 25 | 26 | RUN apk add --no-cache openssh-client git && \ 27 | {{#git_url_rewrites}} 28 | git config --global url."{{to}}".insteadOf "{{from}}" && \ 29 | {{/git_url_rewrites}} 30 | mkdir -p -m 0600 ~/.ssh && \ 31 | ssh-keyscan github.com >> ~/.ssh/known_hosts 32 | 33 | RUN --mount=id=hex-cache,type=cache,sharing=locked,target=/root/.cache/rebar3 --mount=type=ssh,required=true \ 34 | rebar3 compile 35 | 36 | #--- Profile Builder ----------------------------------------------------------- 37 | 38 | FROM builder as builder-profile 39 | 40 | ARG profile 41 | RUN --mount=target=. \ 42 | --mount=id=hex-cache,type=cache,sharing=locked,target=/root/.cache/rebar3 \ 43 | rebar3 as $profile compile 44 | 45 | #--- Releaser ------------------------------------------------------------------ 46 | 47 | FROM builder-profile as releaser 48 | 49 | # create the directory to unpack the release to 50 | RUN mkdir -p /opt/rel 51 | 52 | # tar for unpacking the target system 53 | RUN --mount=type=cache,id=apk,sharing=locked,target=/var/cache/apk \ 54 | apk add --update tar 55 | 56 | ARG profile 57 | RUN --mount=target=. \ 58 | --mount=id=hex-cache,type=cache,sharing=locked,target=/root/.cache/rebar3 \ 59 | rebar3 as $profile release && \ 60 | rebar3 as $profile tar && \ 61 | tar -zxvf $REBAR_BASE_DIR/$profile/rel/*/*.tar.gz -C /opt/rel 62 | 63 | #--- Runner -------------------------------------------------------------------- 64 | 65 | FROM alpine as runner 66 | 67 | WORKDIR /opt/{{appname}}/ 68 | 69 | ENV \ 70 | # application specific env variables to act as defaults 71 | {{#env}} 72 | {{name}}={{value}} \ 73 | {{/env}} 74 | # write files generated during startup to /tmp 75 | RELX_OUT_FILE_PATH=/tmp 76 | 77 | # openssl needed by the crypto app 78 | RUN --mount=type=cache,id=apk,sharing=locked,target=/var/cache/apk \ 79 | ln -s /var/cache/apk /etc/apk/cache && \ 80 | apk add --update \ 81 | {{#runtime_packages}} 82 | {{name}} \ 83 | {{/runtime_packages}} 84 | libstdc++ \ 85 | openssl \ 86 | ncurses 87 | 88 | COPY --from=releaser /opt/rel . 89 | 90 | {{#ports}} 91 | EXPOSE {{port}}/{{protocol}} 92 | {{/ports}} 93 | 94 | ENTRYPOINT ["/opt/{{appname}}/bin/{{appname}}"] 95 | CMD ["foreground"] 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | rebar3_docker 2 | ===== 3 | 4 | A simple rebar plugin to build docker containers out of an Erlang release. 5 | 6 | 7 | Build 8 | ----- 9 | 10 | $ rebar3 compile 11 | 12 | 13 | Use 14 | --- 15 | 16 | Add the plugin to your rebar config: 17 | 18 | {plugins, [ 19 | rebar3_docker 20 | ]}. 21 | 22 | Add docker the configuration as needed to your rebar config: 23 | 24 | {docker, [ 25 | % The tag to be used to publish the docker image. 26 | % If not specified, uses "locale/release_name". 27 | {tag, "local/somename"}, 28 | % The version of the erlang docker image. 29 | % If not specified, uses "25.3.2.2" 30 | {erlang_version, "25.3.2.2"}, 31 | % If you need to further customize the building environment, 32 | % this option overrides the official erlang image with anything you want. 33 | % in this case the erlang_version option is ignored 34 | % Must still be an alpine based image and contain OTP + Rebar3! 35 | % {builder_image, "your-repo/your-image:tag"}, 36 | % The name of the application release. 37 | % If not specified use the first release name found in the relx config. 38 | {appname, "appname"}, 39 | % The extra packages to install in the building docker layer. 40 | {build_packages, [ % 41 | make, 42 | gcc, 43 | "libc-dev", 44 | "libbsd-dev" 45 | ]}, 46 | % The git url to be rewritten. Used to access private repository. 47 | {git_url_rewrites, [ 48 | {"https://github.com/", "git@github.com:"} 49 | ]}, 50 | % The extra runtime package to install in the final docker image. 51 | {runtime_packages, []}, 52 | % The ports to be exposed by the docker image. 53 | {ports, [ 54 | {8888, tcp} 55 | ]}, 56 | % The docker image environment. 57 | {env, [ 58 | {'COOKIE', "dummy"}, 59 | {'LOGGER_LEVEL', debug} 60 | ]}, 61 | % The target platform for the build output 62 | % You can specify multiple platform if your docker configuration allows it 63 | % If not specified, then the flag isn't used when building the docker image 64 | {platform, ["linux/arm64"]} 65 | ]} 66 | 67 | Be sure to configure a relx release and then just call the plugin build command: 68 | 69 | $ rebar3 docker build 70 | 71 | Example of a minimal working configuration for a release using port 1234, 72 | and defining logging and cookie environment variables: 73 | 74 | {plugins, [ 75 | {rebar3_docker, {git, "https://github.com/stritzinger/rebar3_docker.git", 76 | {tag, "0.1.0"}}} 77 | ]}. 78 | {deps, []}. 79 | {docker, [ 80 | {ports, [{1234, tcp}]}, 81 | {env, [ 82 | {'COOKIE', "dummy"}, 83 | {'LOGGER_LEVEL', debug} 84 | ]} 85 | ]}. 86 | {relx, [ 87 | {release, {myapp, "1.0.0"}, [myapp, sasl]}, 88 | {include_src, false}, 89 | {include_erts, true} 90 | ]}. 91 | 92 | 93 | STARTING A CONTAINER 94 | -------------------- 95 | 96 | When the image as been build you can create a container and start it with: 97 | 98 | docker create --name myapp local/myapp 99 | docker start myapp 100 | 101 | Or run it interactively in console mode: 102 | 103 | docker run --rm -it local/myapp console 104 | 105 | If your application export some ports, you will need to explicitly publish it 106 | with the docker argument `-p 1234:1234`. 107 | 108 | 109 | DEBUGGING 110 | --------- 111 | 112 | When running the build command, the docker configuration is saved in: 113 | 114 | _build/[PROFILE]/container/Dockerfile 115 | 116 | 117 | TODO 118 | ---- 119 | 120 | Add support for multiple profiles. 121 | -------------------------------------------------------------------------------- /src/rebar3_docker_util.erl: -------------------------------------------------------------------------------- 1 | -module(rebar3_docker_util). 2 | 3 | 4 | %%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 5 | 6 | % API functions 7 | -export([config/1]). 8 | -export([container_dir/1]). 9 | 10 | 11 | %%% MACROS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 12 | 13 | -define(DEFAULT_ERLANG_VERSION, <<"25.3.2.2">>). 14 | -define(CONFIG_KEYS, [ 15 | {tag, binary}, 16 | {erlang_version, binary}, 17 | {builder_image, binary}, 18 | {appname, binary}, 19 | {build_packages, [list, binary]}, 20 | {git_url_rewrites, [list, [tuple, [binary, binary]]]}, 21 | {runtime_packages, [list, binary]}, 22 | {ports, [list, [tuple, [integer, {tcp, udp}]]]}, 23 | {env, [list, [tuple, [binary, any]]]}, 24 | {platform, [list, binary]} 25 | ]). 26 | 27 | 28 | %%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 29 | 30 | config(RState) -> 31 | UserCfg = maps:merge(default_config(RState), docker_config(RState)), 32 | define_builder_image(UserCfg). 33 | 34 | container_dir(RState) -> 35 | filename:join([rebar_state:dir(RState), 36 | "_build", 37 | atom_to_list(hd(rebar_state:current_profiles(RState))), 38 | "container"]). 39 | 40 | %%% INTERNAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 41 | 42 | define_builder_image(#{builder_image := _} = Cfg) -> Cfg; 43 | define_builder_image(#{erlang_version := ErlV} = Cfg) -> 44 | Cfg#{builder_image => iolist_to_binary(["erlang:", ErlV ,"-alpine"])}. 45 | 46 | release_config(RState) -> 47 | Config = rebar_state:get(RState, relx, []), 48 | case lists:keyfind(release, 1, Config) of 49 | {release, {Name, _Ver}, _Apps} -> 50 | convert_docker_config(release_name, binary, Name); 51 | false -> undefined 52 | end. 53 | 54 | docker_config(RState) -> 55 | Config = rebar_state:get(RState, docker, []), 56 | parse_docker_config(?CONFIG_KEYS, Config, #{}). 57 | 58 | parse_docker_config([], _Config, Acc) -> Acc; 59 | parse_docker_config([{Name, Type} | Rest], Config, Acc) -> 60 | case lists:keyfind(Name, 1, Config) of 61 | {Name, RawValue} -> 62 | Acc2 = Acc#{Name => convert_docker_config(Name, Type, RawValue)}, 63 | parse_docker_config(Rest, Config, Acc2); 64 | false -> 65 | parse_docker_config(Rest, Config, Acc) 66 | end. 67 | 68 | convert_docker_config(_Name, any, V) -> V; 69 | convert_docker_config(_Name, binary, V) when is_binary(V) -> V; 70 | convert_docker_config(_Name, integer, V) when is_integer(V) -> V; 71 | convert_docker_config(_Name, binary, V) when is_list(V) -> list_to_binary(V); 72 | convert_docker_config(_Name, binary, V) when is_integer(V) -> integer_to_binary(V); 73 | convert_docker_config(_Name, binary, V) when is_atom(V) -> atom_to_binary(V); 74 | convert_docker_config(Name, integer, V) when is_binary(V) -> 75 | try binary_to_integer(V) 76 | catch _:badarg -> erlang:error({bad_config, Name}) 77 | end; 78 | convert_docker_config(Name, integer, V) when is_list(V) -> 79 | try list_to_integer(V) 80 | catch _:badarg -> erlang:error({bad_config, Name}) 81 | end; 82 | convert_docker_config(Name, [list, Type], V) when is_list(V) -> 83 | [convert_docker_config(Name, Type, I) || I <- V]; 84 | convert_docker_config(Name, [tuple, Types], V) when is_tuple(V) -> 85 | list_to_tuple([convert_docker_config(Name, T, I) || 86 | {T, I} <- lists:zip(Types, tuple_to_list(V))]); 87 | convert_docker_config(Name, Enum, V) when is_tuple(Enum) -> 88 | case lists:member(V, tuple_to_list(Enum)) of 89 | true -> V; 90 | false -> erlang:error({bad_config, Name}) 91 | end; 92 | convert_docker_config(Name, _T, _V) -> 93 | erlang:error({bad_config, Name}). 94 | 95 | default_config(RState) -> 96 | AppName = release_config(RState), 97 | #{ 98 | tag => iolist_to_binary(io_lib:format("local/~s", [AppName])), 99 | appname => AppName, 100 | env => [], 101 | erlang_version => ?DEFAULT_ERLANG_VERSION, 102 | git_url_rewrites => [], 103 | ports => [], 104 | build_packages => [], 105 | runtime_packages => [], 106 | platform => [] 107 | }. 108 | -------------------------------------------------------------------------------- /src/rebar3_docker_build.erl: -------------------------------------------------------------------------------- 1 | -module(rebar3_docker_build). 2 | 3 | 4 | %%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 5 | 6 | -export([init/1]). 7 | -export([do/1]). 8 | -export([format_error/1]). 9 | 10 | 11 | %%% MACROS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 12 | 13 | -define(PROVIDER, docker). 14 | -define(DEPS, [app_discovery]). 15 | 16 | 17 | %%% API FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 18 | 19 | -spec init(rebar_state:t()) -> {ok, rebar_state:t()}. 20 | init(RState) -> 21 | Provider = providers:create([ 22 | {name, ?PROVIDER}, % The 'user friendly' name of the task 23 | {module, ?MODULE}, % The module implementation of the task 24 | {bare, true}, % The task can be run by the user, always true 25 | {deps, ?DEPS}, % The list of dependencies 26 | {example, "rebar3 docker build"}, % How to use the plugin 27 | {opts, [ % list of options understood by the plugin 28 | ]}, 29 | {short_desc, "A rebar plugin to generate docker containers"}, 30 | {desc, "A rebar plugin to generate docker containers"} 31 | ]), 32 | {ok, rebar_state:add_provider(RState, Provider)}. 33 | 34 | 35 | -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. 36 | do(RState) -> 37 | case rebar_prv_lock:do(RState) of 38 | {ok, RState2} -> 39 | Config = rebar3_docker_util:config(RState2), 40 | Params = template_parameters(RState2, Config), 41 | TemplateFile = filename:join([code:priv_dir(rebar3_docker), 42 | "templates", "Dockerfile"]), 43 | Template = bbmustache:parse_file(TemplateFile), 44 | Data = bbmustache:compile(Template, Params, 45 | [{key_type, atom}, {escape_fun, fun(X) -> X end}]), 46 | ContainerDir = rebar3_docker_util:container_dir(RState2), 47 | DataFilename = filename:join(ContainerDir, "Dockerfile"), 48 | filelib:ensure_dir(DataFilename), 49 | file:write_file(DataFilename, Data), 50 | Args = ["build", ".", "-f", DataFilename, "--progress=plain", 51 | "--tag", binary_to_list(maps:get(tag, Config))], 52 | Args2 = case maps:get(platform, Config) of 53 | [] -> Args; 54 | Platforms -> Args ++ format_platform_option(Platforms) 55 | end, 56 | Args3 = case os:getenv("SSH_AUTH_SOCK") of 57 | false -> Args2; 58 | Value -> Args2 ++ ["--ssh=default=" ++ Value] 59 | end, 60 | CmdOpts = [use_stdout, abort_on_error, {cd, rebar_state:dir(RState2)}], 61 | Command = lists:flatten(lists:join(" ", ["docker" | Args3])), 62 | rebar_utils:sh(Command, CmdOpts), 63 | {ok, RState2}; 64 | Error -> 65 | Error 66 | end. 67 | 68 | -spec format_error(any()) -> iolist(). 69 | format_error(Reason) -> 70 | io_lib:format("~p", [Reason]). 71 | 72 | 73 | %%% PRIVATE FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 74 | 75 | template_parameters(RState, Config) -> 76 | Config#{ 77 | profile => hd(lists:reverse(rebar_state:current_profiles(RState))), 78 | env => [#{name => N, value => escape_string(V)} 79 | || {N, V} <- maps:get(env, Config)], 80 | git_url_rewrites => [#{from => F, to => T} 81 | || {F, T} <- maps:get(git_url_rewrites, Config)], 82 | ports => [#{port => Port, protocol => Proto} 83 | || {Port, Proto} <- maps:get(ports, Config)], 84 | build_packages => [#{name => N} 85 | || N <- maps:get(build_packages, Config)], 86 | runtime_packages => [#{name => N} 87 | || N <- maps:get(runtime_packages, Config)] 88 | }. 89 | 90 | escape_string(Str) when is_binary(Str) -> 91 | iolist_to_binary(io_lib:format("\"~s\"", 92 | [rebar_utils:escape_double_quotes(Str)])); 93 | escape_string(Str) when is_list(Str) -> 94 | iolist_to_binary(io_lib:format("\"~s\"", 95 | [rebar_utils:escape_double_quotes(list_to_binary(Str))])); 96 | escape_string(Other) -> 97 | Other. 98 | 99 | format_platform_option(Platforms) -> 100 | ["--platform", 101 | lists:join(",", lists:map(fun(Platform) -> 102 | binary_to_list(Platform) 103 | end, Platforms))]. 104 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------