Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
23ddc74
commit da6126a
Showing
9 changed files
with
262 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
defmodule Mix.Tasks.Publishex.S3 do | ||
use Mix.Task | ||
|
||
@shortdoc "Publish a directory to S3" | ||
@moduledoc """ | ||
Publish directory to Netlify | ||
## Usage | ||
mix publishex.s3 --bucket bucket_name --region us-west-1 --access_key_id access_key_id --secret_access_key secret_access_key --acl public_read | ||
# Set a custom directory | ||
mix publishex.s3 --bucket bucket_name --region us-west-1 --access_key_id access_key_id --secret_access_key secret_access_key --acl public_read --directory "some_dir" | ||
You have to explicitly pass the acl so files are not accidentally made public | ||
The values can be any of the following `private`, `public_read`, `public_read_write`, `authenticated_read`, `bucket_owner_read` or `bucket_owner_full_control`. | ||
""" | ||
|
||
@impl Mix.Task | ||
def run(argv) do | ||
Application.ensure_all_started(:httpoison) | ||
|
||
parse_options = [ | ||
strict: [ | ||
bucket: :string, | ||
region: :string, | ||
directory: :string, | ||
secret_access_key: :string, | ||
access_key_id: :string, | ||
acl: :string | ||
] | ||
] | ||
|
||
{opts, _args, _} = OptionParser.parse(argv, parse_options) | ||
|
||
directory = Keyword.get(opts, :directory, "doc") | ||
bucket = Keyword.fetch!(opts, :bucket) | ||
region = Keyword.fetch!(opts, :region) | ||
access_key_id = Keyword.fetch!(opts, :access_key_id) | ||
secret_access_key = Keyword.fetch!(opts, :secret_access_key) | ||
acl = Keyword.fetch!(opts, :acl) | ||
|
||
Publishex.publish(directory, | ||
adapter: Publishex.Adapter.S3, | ||
adapter_opts: [ | ||
bucket: bucket, | ||
region: region, | ||
access_key_id: access_key_id, | ||
secret_access_key: secret_access_key, | ||
acl: String.to_atom(acl) | ||
] | ||
) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
defmodule Publishex.Adapter.S3 do | ||
@moduledoc "Adapter for uploading a directory to netlify" | ||
|
||
alias ExAws.S3 | ||
alias Publishex.Util | ||
|
||
@doc """ | ||
Publish to S3 | ||
config = Publishex.Config.build([ | ||
directory: "doc", | ||
adapter_opts: [bucket: "your.s3.bucket", region: "us-west-1", access_key_id: "access_key_id", secret_access_key: "secret_access_key"], | ||
]) | ||
Publishex.Adapter.S3.publish(config) | ||
""" | ||
def publish(config) do | ||
bucket = Util.config_key(config.adapter_opts, :bucket) | ||
access_key_id = Util.config_key(config.adapter_opts, :access_key_id) | ||
secret_access_key = Util.config_key(config.adapter_opts, :secret_access_key) | ||
region = Util.config_key(config.adapter_opts, :region) | ||
acl = Util.config_key(config.adapter_opts, :acl) | ||
client = Keyword.get(config.adapter_opts, :client, ExAws) | ||
|
||
directory = config.directory | ||
|
||
if !File.dir?(directory) do | ||
raise ArgumentError, "Not a valid directory" | ||
end | ||
|
||
directory | ||
|> config.file_lister.run() | ||
|> build_src_dests(directory) | ||
|> config.upload_strategy.run(fn {src, dest} -> | ||
IO.puts("Uploading #{src} to #{dest}...") | ||
|
||
"." <> ext = Path.extname(src) | ||
|
||
S3.put_object(bucket, dest, config.file_reader.run(src), | ||
content_type: "text/#{ext}", | ||
acl: acl | ||
) | ||
|> client.request!( | ||
region: region, | ||
access_key_id: access_key_id, | ||
secret_access_key: secret_access_key | ||
) | ||
end) | ||
|
||
IO.puts("Site uploaded to bucket: #{bucket}") | ||
end | ||
|
||
defp build_src_dests(paths, prefix) do | ||
paths | ||
|> Enum.map(fn path -> | ||
{path, Util.remove_prefix(path, prefix)} | ||
end) | ||
|> Map.new() | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
defmodule Publishex.Util do | ||
def remove_prefix(path, prefix) do | ||
base = byte_size(prefix) | ||
<<_::binary-size(base), rest::binary>> = path | ||
rest | ||
end | ||
|
||
def config_key(opts, key) do | ||
case Keyword.fetch(opts, key) do | ||
{:ok, value} -> | ||
value | ||
|
||
:error -> | ||
raise ArgumentError, | ||
"Could not find required #{inspect(key)} in adapter_opts: #{inspect(opts)}" | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
defmodule Publishex.Adapter.S3Test do | ||
use ExUnit.Case | ||
import ExUnit.CaptureIO | ||
|
||
defmodule FakeClient do | ||
def request!(operation, opts) do | ||
send(self(), {:s3, operation, opts}) | ||
|
||
:ok | ||
end | ||
end | ||
|
||
defmodule FakeLister do | ||
def run(_dir) do | ||
["doc/123.html"] | ||
end | ||
end | ||
|
||
defmodule FakeReader do | ||
def run(file) do | ||
send(self(), {:read_file, file}) | ||
"some content" | ||
end | ||
end | ||
|
||
test "returns error without bucket" do | ||
assert_raise ArgumentError, "Could not find required :bucket in adapter_opts: []", fn -> | ||
Publishex.publish("doc", adapter: Publishex.Adapter.S3) | ||
end | ||
end | ||
|
||
test "returns error without access key id" do | ||
assert_raise ArgumentError, | ||
"Could not find required :access_key_id in adapter_opts: [bucket: \"abc\"]", | ||
fn -> | ||
Publishex.publish("doc", | ||
adapter: Publishex.Adapter.S3, | ||
adapter_opts: [bucket: "abc"] | ||
) | ||
end | ||
end | ||
|
||
test "returns error without secret_access_key" do | ||
assert_raise ArgumentError, | ||
"Could not find required :secret_access_key in adapter_opts: [bucket: \"abc\", access_key_id: \"access key\"]", | ||
fn -> | ||
Publishex.publish("doc", | ||
adapter: Publishex.Adapter.S3, | ||
adapter_opts: [bucket: "abc", access_key_id: "access key"] | ||
) | ||
end | ||
end | ||
|
||
test "returns error without region" do | ||
assert_raise ArgumentError, | ||
"Could not find required :region in adapter_opts: [bucket: \"abc\", access_key_id: \"access key\", secret_access_key: \"secret\"]", | ||
fn -> | ||
Publishex.publish("doc", | ||
adapter: Publishex.Adapter.S3, | ||
adapter_opts: [ | ||
bucket: "abc", | ||
access_key_id: "access key", | ||
secret_access_key: "secret" | ||
] | ||
) | ||
end | ||
end | ||
|
||
test "returns error without acl" do | ||
assert_raise ArgumentError, | ||
"Could not find required :acl in adapter_opts: [bucket: \"abc\", access_key_id: \"access key\", secret_access_key: \"secret\", region: \"us-west-1\"]", | ||
fn -> | ||
Publishex.publish("doc", | ||
adapter: Publishex.Adapter.S3, | ||
adapter_opts: [ | ||
bucket: "abc", | ||
access_key_id: "access key", | ||
secret_access_key: "secret", | ||
region: "us-west-1" | ||
] | ||
) | ||
end | ||
end | ||
|
||
test "uploads files" do | ||
capture_io(fn -> | ||
Publishex.publish("doc", | ||
adapter: Publishex.Adapter.S3, | ||
file_lister: FakeLister, | ||
file_reader: FakeReader, | ||
upload_strategy: Publishex.UploadStrategy.Sync, | ||
adapter_opts: [ | ||
bucket: "abc", | ||
access_key_id: "access key", | ||
secret_access_key: "secret", | ||
region: "region", | ||
acl: :public_read, | ||
client: FakeClient | ||
] | ||
) | ||
end) | ||
|
||
assert_received {:s3, operation, opts} | ||
|
||
assert %{body: "some content", bucket: "abc", path: "/123.html"} = operation | ||
|
||
assert [ | ||
region: "region", | ||
access_key_id: "access key", | ||
secret_access_key: "secret" | ||
] = opts | ||
end | ||
end |