deadpull 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9746519a34dc5962bd60f2453d8b9a9080a231b151f82bb18a07a8160992b3d3
4
+ data.tar.gz: ea7c4d54bf24a03417e019496f979239fca1d7a02fac8fc983806333b67cb805
5
+ SHA512:
6
+ metadata.gz: 46527b410c7c2449876aa84a302122bcf358fee52e2fc4a367446a5471b38f45c51e5d702afb157b94514570f480eab628c11ba3a254960f98f65610d402f208
7
+ data.tar.gz: a1dff71358a2485f0011fc2fc95e9f8460a9026c2d87704474b179bbe0580ed6e6d3df6d567e77976442eb6b01e6680142dc0bbc5ffc20c30358a7538e9c54bc
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+ .deadpull.local.yml
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,15 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.5.1
3
+
4
+ LineLength:
5
+ Max: 125
6
+
7
+ StringLiterals:
8
+ EnforcedStyle: single_quotes
9
+
10
+ Documentation:
11
+ Enabled: false
12
+
13
+ Metrics/BlockLength:
14
+ Exclude:
15
+ - spec/**/*.rb
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.5.1
data/.travis.yml ADDED
@@ -0,0 +1,20 @@
1
+ sudo: false
2
+ language: ruby
3
+ env:
4
+ global:
5
+ - CC_TEST_REPORTER_ID=b6f2f94380074e6db38810ba993e806a752de86d8ea00af35bf657f3eceae0e6
6
+ rvm:
7
+ - 2.5.0
8
+
9
+ before_install: gem install bundler -v 1.16.1
10
+
11
+ before_script:
12
+ - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
13
+ - chmod +x ./cc-test-reporter
14
+ - ./cc-test-reporter before-build
15
+
16
+ script:
17
+ - bundle exec rspec
18
+
19
+ after_script:
20
+ - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at pawel.wal@codesthq.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,106 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ deadpull (0.1.0)
5
+ activesupport (~> 5.2)
6
+ aws-sdk-s3 (~> 1)
7
+ dry-initializer (~> 2.4)
8
+ dry-transaction (~> 0.13)
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ activesupport (5.2.0)
14
+ concurrent-ruby (~> 1.0, >= 1.0.2)
15
+ i18n (>= 0.7, < 2)
16
+ minitest (~> 5.1)
17
+ tzinfo (~> 1.1)
18
+ aws-eventstream (1.0.1)
19
+ aws-partitions (1.94.0)
20
+ aws-sdk-core (3.22.1)
21
+ aws-eventstream (~> 1.0)
22
+ aws-partitions (~> 1.0)
23
+ aws-sigv4 (~> 1.0)
24
+ jmespath (~> 1.0)
25
+ aws-sdk-kms (1.6.0)
26
+ aws-sdk-core (~> 3)
27
+ aws-sigv4 (~> 1.0)
28
+ aws-sdk-s3 (1.16.0)
29
+ aws-sdk-core (~> 3, >= 3.21.2)
30
+ aws-sdk-kms (~> 1)
31
+ aws-sigv4 (~> 1.0)
32
+ aws-sigv4 (1.0.3)
33
+ coderay (1.1.2)
34
+ concurrent-ruby (1.0.5)
35
+ diff-lcs (1.3)
36
+ docile (1.3.1)
37
+ dry-configurable (0.7.0)
38
+ concurrent-ruby (~> 1.0)
39
+ dry-container (0.6.0)
40
+ concurrent-ruby (~> 1.0)
41
+ dry-configurable (~> 0.1, >= 0.1.3)
42
+ dry-core (0.4.7)
43
+ concurrent-ruby (~> 1.0)
44
+ dry-equalizer (0.2.1)
45
+ dry-events (0.1.0)
46
+ concurrent-ruby (~> 1.0)
47
+ dry-core (~> 0.4)
48
+ dry-equalizer (~> 0.2)
49
+ dry-initializer (2.4.0)
50
+ dry-matcher (0.7.0)
51
+ dry-monads (1.0.0)
52
+ concurrent-ruby (~> 1.0)
53
+ dry-core (~> 0.4, >= 0.4.4)
54
+ dry-equalizer
55
+ dry-transaction (0.13.0)
56
+ dry-container (>= 0.2.8)
57
+ dry-events (>= 0.1.0)
58
+ dry-matcher (>= 0.7.0)
59
+ dry-monads (>= 0.4.0)
60
+ fakefs (0.14.2)
61
+ i18n (1.0.1)
62
+ concurrent-ruby (~> 1.0)
63
+ jmespath (1.4.0)
64
+ json (2.1.0)
65
+ method_source (0.9.0)
66
+ minitest (5.11.3)
67
+ pry (0.11.3)
68
+ coderay (~> 1.1.0)
69
+ method_source (~> 0.9.0)
70
+ rake (10.5.0)
71
+ rspec (3.7.0)
72
+ rspec-core (~> 3.7.0)
73
+ rspec-expectations (~> 3.7.0)
74
+ rspec-mocks (~> 3.7.0)
75
+ rspec-core (3.7.1)
76
+ rspec-support (~> 3.7.0)
77
+ rspec-expectations (3.7.0)
78
+ diff-lcs (>= 1.2.0, < 2.0)
79
+ rspec-support (~> 3.7.0)
80
+ rspec-mocks (3.7.0)
81
+ diff-lcs (>= 1.2.0, < 2.0)
82
+ rspec-support (~> 3.7.0)
83
+ rspec-support (3.7.1)
84
+ simplecov (0.16.1)
85
+ docile (~> 1.1)
86
+ json (>= 1.8, < 3)
87
+ simplecov-html (~> 0.10.0)
88
+ simplecov-html (0.10.2)
89
+ thread_safe (0.3.6)
90
+ tzinfo (1.2.5)
91
+ thread_safe (~> 0.1)
92
+
93
+ PLATFORMS
94
+ ruby
95
+
96
+ DEPENDENCIES
97
+ bundler (~> 1.16)
98
+ deadpull!
99
+ fakefs (~> 0.14)
100
+ pry (~> 0.11)
101
+ rake (~> 10.0)
102
+ rspec (~> 3.0)
103
+ simplecov (~> 0.16)
104
+
105
+ BUNDLED WITH
106
+ 1.16.1
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Paweł J. Wal
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,168 @@
1
+ # Deadpull
2
+
3
+ [![Build Status](https://travis-ci.org/paweljw/deadpull.svg?branch=master)](https://travis-ci.org/paweljw/deadpull)
4
+ [![Maintainability](https://api.codeclimate.com/v1/badges/d53d13278ba762fc66dc/maintainability)](https://codeclimate.com/github/paweljw/deadpull/maintainability)
5
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/d53d13278ba762fc66dc/test_coverage)](https://codeclimate.com/github/paweljw/deadpull/test_coverage)
6
+
7
+ A simple gem to organize storing and retrieving configuration files on AWS S3 in a programmatic, automated fashion.
8
+
9
+ Use cases:
10
+
11
+ * Automatically synchronizing newest configs during a deploy
12
+ * Bootstrapping developers' environments as part of an onboarding process
13
+ * Personal dotfiles synchronization between machines
14
+
15
+ ## Installation
16
+
17
+ Add this line to your application's Gemfile:
18
+
19
+ ```ruby
20
+ gem 'deadpull', '~> 0.1', group: :development
21
+ ```
22
+
23
+ And then execute:
24
+
25
+ $ bundle
26
+
27
+ If you wish to take advantage of the `deadpull` command in your project's directory, install binstubs with
28
+
29
+ $ bundle binstubs deadpull
30
+
31
+ If you wish to use the provided `deadpull` command globally from the command line, install it with `gem`:
32
+
33
+ $ gem install deadpull
34
+
35
+ ## Configuration
36
+
37
+ Configuration uses two keys currently:
38
+
39
+ * `path` - required. This is composed of a bucket part and then a prefix, e.g. `my-fancy-bucket/a-directory/prefix`. It relates to locations on AWS S3.
40
+ * `aws` - optional. This is a hash containing anything [`Aws::S3::Client`](https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Client.html) accepts in its initializer. You should set at least `profile` and `region` keys here.
41
+
42
+ ### Configuration file order
43
+
44
+ Deadpull will first look for a file called `.deadpull.yml`. Then, data from file `.deadpull.local.yml` will be merged into this. The idea is that you use `.deadpull.yml` for your public configuration and commit this file, while the local file contains e.g. secrets and does not get commited.
45
+
46
+ When using programmatic operation, any hash passed to `Deadpull::Configuration#new` will be merged into the hash resulting from the above operations, effectively giving it highest priority.
47
+
48
+ ### Environment
49
+
50
+ Environment is decided by either passing it explicitly to commands, or by using `DEADPULL_ENV` or `RAILS_ENV`. If none of the above is provided, it defaults to `development`, following Rails convention.
51
+
52
+ ## CLI usage
53
+
54
+ Pulling config for 'production' environment to 'tmp' example:
55
+
56
+ ```
57
+ $ deadpull -e production tmp
58
+ ```
59
+
60
+ Pushing config from 'tmp' to a specific path on S3 for 'staging' environment example:
61
+
62
+ ```
63
+ $ deadpull -u -e staging -p my-fancy-bucket/this-project tmp
64
+ ```
65
+
66
+ Note that when not given `-u` (or `--upload`), Deadpull defaults to pulling in order to prevent inadvertent damage to your configuration source-of-truth on S3.
67
+
68
+ Options description (use `-h` locally to get this):
69
+
70
+ ```
71
+ Usage: deadpull [options] <path>
72
+
73
+ Options:
74
+ -u, --upload Push to S3 from given path
75
+ -e, --environment [ENVIRONMENT] Provides environment, superseding DEADPULL_ENV and RAILS_ENV
76
+ -p, --path [PATH] S3 path to be used for upload or download in form of bucket-name/prefix. Supersedes config.
77
+ -a, --aws [AWS] AWS configuration hash in the form of a JSON string. Supersedes config.
78
+ -v, --[no-]verbose Explicitly print used configuration.
79
+
80
+ Common options:
81
+ -h, --help Show this message
82
+ ```
83
+
84
+ ## Capistrano
85
+
86
+ The Capistrano plugin works by downloading files from S3 to a temporary directory on your machine, then uploading them
87
+ over SSH to the target server. That way your AWS keys never leave your machine and the configuration files never hit
88
+ a network connection unencrypted. They will be present unencrypted on your machine for the duration of the Cap task though.
89
+
90
+ Add the following to your Capfile:
91
+
92
+ ```ruby
93
+ require 'capistrano/deadpull'
94
+ ```
95
+
96
+ Options that are configurable in deploy files:
97
+
98
+ ```ruby
99
+ set :deadpull_path, 'my-fancy-bucket/some-prefix' # shouldn't be necessary if you have .deadpull.yml in your project
100
+ set :deadpull_environment, 'production' # defaults to fetch(:stage) or the fallback flow as described above
101
+ set :deadpull_roles, :app # defaults to :app, this you might actually need to set if you do multi-machine deploys
102
+ ```
103
+
104
+ The task hooks into after `deploy:updating`, as soon as shared directories are linked, to ensure your config is in place
105
+ for e.g. asset rebuilding, services restart etc.
106
+
107
+ The structure of your prefix is rebuilt on the target machine, so if your path is `my-bucket/prefix`, environment is `staging` and
108
+ you uploaded `config/staging.yml`- resulting in a `my-bucket/prefix/staging/config/staging.yml` structure on S3 - then your
109
+ file will be uploaded to `config/staging.yml` with respect to release path on the server.
110
+
111
+ Note that Deadpull is not concerned whether your files are linked or not; if your config includes paths that are linked,
112
+ Deadpull will happily write to those, in effect changing the contents of your `shared` directory during every deploy. This
113
+ is because Deadpull aims to essentialy replace linked config files - if you can refresh them on every deploy from a
114
+ single source of truth, why not?
115
+
116
+ ## Programmatic usage
117
+
118
+ ### `Deadpull::Commands::Push`
119
+
120
+ The most complete example of local usage would be:
121
+
122
+ ```ruby
123
+ configuration = Deadpull::Values::Configuration.concretize({ path: ..., aws: ...})
124
+ environment = Deadpull::Values::Environment.concretize('test')
125
+
126
+ Deadpull::Commands::Push.call('/some/local/path', configuration, environment) #=> true
127
+ # or
128
+ Deadpull::Commands::Push.call('/some/local/path/file.yml', configuration, environment) #=> true
129
+ ```
130
+
131
+ Note that configuration and environment can be ommited if you want to use the defaults found by methods described in the Configuration section above.
132
+
133
+ ### `Deadpull::Commands::Pull`
134
+
135
+ The most complete example of local usage would be:
136
+
137
+ ```ruby
138
+ configuration = Deadpull::Values::Configuration.concretize({ path: ..., aws: ...})
139
+ environment = Deadpull::Values::Environment.concretize('test')
140
+
141
+ Deadpull::Commands::Pull.call('/some/local/path', configuration, environment) #=> true
142
+ ```
143
+
144
+ Note that configuration and environment can be ommited if you want to use the defaults found by methods described in the Configuration section above.
145
+
146
+ Note that `Pull` will raise an `ArgumentError` if it detects that the given `path` is a regular file.
147
+
148
+ ## Development
149
+
150
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
151
+
152
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
153
+
154
+ ## Contributing
155
+
156
+ Bug reports and pull requests are welcome on GitHub at https://github.com/paweljw/deadpull. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
157
+
158
+ ## Contributors
159
+
160
+ * [@macbury](https://github.com/macbury) - collaboration on the initial idea
161
+
162
+ ## License
163
+
164
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
165
+
166
+ ## Code of Conduct
167
+
168
+ Everyone interacting in the Deadpull project’s codebases and issue trackers is expected to follow the [code of conduct](https://github.com/paweljw/deadpull/blob/master/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
data/bin/console ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'deadpull'
6
+ require 'pry'
7
+
8
+ Pry.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/config.reek ADDED
@@ -0,0 +1,2 @@
1
+ IrresponsibleModule:
2
+ enabled: false
data/deadpull.gemspec ADDED
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('./lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'deadpull/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'deadpull'
9
+ spec.version = Deadpull::VERSION
10
+ spec.authors = ['Paweł J. Wal']
11
+ spec.email = ['p@steamshard.net']
12
+
13
+ spec.summary = 'Share config securely with your servers and organization using AWS S3.'
14
+ spec.homepage = 'https://github.com/paweljw/deadpull'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = 'exe'
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ['lib']
23
+
24
+ spec.add_dependency 'activesupport', '~> 5.2'
25
+ spec.add_dependency 'aws-sdk-s3', '~> 1'
26
+ spec.add_dependency 'dry-initializer', '~> 2.4'
27
+ spec.add_dependency 'dry-transaction', '~> 0.13'
28
+
29
+ spec.add_development_dependency 'bundler', '~> 1.16'
30
+ spec.add_development_dependency 'fakefs', '~> 0.14'
31
+ spec.add_development_dependency 'pry', '~> 0.11'
32
+ spec.add_development_dependency 'rake', '~> 10.0'
33
+ spec.add_development_dependency 'rspec', '~> 3.0'
34
+ spec.add_development_dependency 'simplecov', '~> 0.16'
35
+ end
data/exe/deadpull ADDED
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'deadpull'
6
+ require 'optparse'
7
+
8
+ options = OpenStruct.new
9
+ options.upload = false
10
+ options.verbose = false
11
+
12
+ opt_parser = OptionParser.new do |opts|
13
+ opts.banner = 'Usage: deadpull [options] <path>'
14
+
15
+ opts.separator ''
16
+ opts.separator 'Options:'
17
+
18
+ opts.on('-u', '--upload', 'Push to S3 from given path') do |v|
19
+ options.upload = v
20
+ end
21
+
22
+ opts.on('-e', '--environment [ENVIRONMENT]', 'Provides environment, superseding DEADPULL_ENV and RAILS_ENV') do |v|
23
+ options.environment = v
24
+ end
25
+
26
+ opts.on('-p', '--path [PATH]',
27
+ 'S3 path to be used for upload or download in form of bucket-name/prefix. Supersedes config.') do |v|
28
+ options.path = v
29
+ end
30
+
31
+ opts.on('-a', '--aws [AWS]', 'AWS configuration hash in the form of a JSON string. Supersedes config.') do |v|
32
+ options.aws = JSON.parse(v).symbolize_keys
33
+ end
34
+
35
+ opts.on('-v', '--[no-]verbose', 'Explicitly print used configuration.') do |v|
36
+ options.verbose = v
37
+ end
38
+
39
+ opts.separator ''
40
+ opts.separator 'Common options:'
41
+
42
+ opts.on_tail('-h', '--help', 'Show this message') do
43
+ puts opts
44
+ exit
45
+ end
46
+ end
47
+
48
+ opt_parser.parse!(ARGV)
49
+
50
+ local_path = ARGV.last
51
+
52
+ environment = Deadpull::Values::Environment.concretize(options.environment)
53
+ configuration = Deadpull::Values::Configuration.concretize
54
+ configuration[:path] = options.path if options.path
55
+ configuration[:aws] = options.aws if options.aws
56
+
57
+ pp(configuration) if options.verbose
58
+
59
+ if options.upload
60
+ Deadpull::Commands::Push.call(local_path, configuration, environment)
61
+ else
62
+ Deadpull::Commands::Pull.call(local_path, configuration, environment)
63
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+ require 'active_support/core_ext/hash/keys'
5
+ require 'active_support/core_ext/hash/deep_merge'
6
+
7
+ module Deadpull
8
+ module Builders
9
+ class Configuration
10
+ include Dry::Transaction
11
+
12
+ HOME_PATH = File.expand_path('~/.config/deadpull.yml').freeze
13
+
14
+ step :working_directory_config
15
+ step :local_config
16
+ step :inline_config
17
+
18
+ def working_directory_config(input)
19
+ transactionally_merge_input_with_file(input, current_working_path.join('.deadpull.yml'))
20
+ end
21
+
22
+ def local_config(input)
23
+ transactionally_merge_input_with_file(input, current_working_path.join('.deadpull.local.yml'))
24
+ end
25
+
26
+ def inline_config(input, inline_config)
27
+ Success(input.deep_merge(inline_config))
28
+ end
29
+
30
+ private
31
+
32
+ def current_working_path
33
+ @current_working_path ||= Pathname.new(Dir.pwd).freeze
34
+ end
35
+
36
+ def transactionally_merge_input_with_file(input, file)
37
+ output = if File.exist?(file)
38
+ YAML.load_file(file).deep_symbolize_keys
39
+ else
40
+ {}
41
+ end
42
+ Success(input.deep_merge(output))
43
+ rescue Psych::SyntaxError => error
44
+ Failure(error)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'deadpull/builders/configuration'
4
+
5
+ module Deadpull
6
+ module Builders; end
7
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ load File.expand_path('tasks/deadpull.rake', __dir__)
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable BlockLength
4
+ namespace :deadpull do
5
+ def deadpull_environment
6
+ environment = fetch(:deadpull_environment) || fetch(:stage)
7
+ Deadpull::Values::Environment.concretize(environment)
8
+ end
9
+
10
+ def deadpull_path
11
+ fetch(:deadpull_path)
12
+ end
13
+
14
+ def deadpull_config
15
+ config = deadpull_path ? { path: deadpull_path } : {}
16
+ Deadpull::Values::Configuration.concretize(config)
17
+ end
18
+
19
+ def deadpull_roles
20
+ fetch(:deadpull_roles)
21
+ end
22
+
23
+ desc 'Fetch files locally and upload them to server'
24
+ task :upload do
25
+ Dir.mktmpdir do |dir|
26
+ Deadpull::Commands::Pull.call(dir, deadpull_config, deadpull_environment)
27
+ on roles(deadpull_roles) do
28
+ within release_path do
29
+ Dir[Pathname.new(dir).join('**', '*')].each do |local_path|
30
+ upload! local_path, Deadpull::Values::RootRelativePath.concretize(dir, local_path)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ after('deploy:updating', 'deadpull:upload') if Rake::Task.task_defined?('deploy:updating')
38
+ end
39
+ # rubocop:enable BlockLength
40
+
41
+ namespace :load do
42
+ task :defaults do
43
+ set :deadpull_roles, :app
44
+ end
45
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deadpull
4
+ module Commands
5
+ class Base
6
+ def self.call(*args)
7
+ new(*args).call
8
+ end
9
+
10
+ def call
11
+ raise NotImplementedError, "implement `call` for #{self.class.name}"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deadpull
4
+ module Commands
5
+ class Pull < S3Command
6
+ extend Dry::Initializer
7
+
8
+ param(:path, proc { |path| path.is_a?(Pathname) ? path : Pathname.new(File.expand_path(path)) })
9
+ param :configuration, default: proc { Values::Configuration.concretize }
10
+ param :environment, default: proc { Values::Environment.concretize }
11
+
12
+ def call
13
+ ensure_target_is_not_a_file
14
+ objects.each do |object|
15
+ location = Values::LocalLocation.concretize(path, s3_locations.prefix, object.key)
16
+ object.download_file(location)
17
+ end
18
+ true
19
+ end
20
+
21
+ private
22
+
23
+ def objects
24
+ bucket.objects(prefix: s3_locations.prefix)
25
+ end
26
+
27
+ def ensure_target_is_not_a_file
28
+ raise(ArgumentError, "#{path} is a regular file") if File.file?(path)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deadpull
4
+ module Commands
5
+ class Push < S3Command
6
+ extend Dry::Initializer
7
+
8
+ param(:path, proc { |path| path.is_a?(Pathname) ? path : Pathname.new(File.expand_path(path)) })
9
+ param :configuration, default: proc { Values::Configuration.concretize }
10
+ param :environment, default: proc { Values::Environment.concretize }
11
+
12
+ def call
13
+ paths.each do |current_path|
14
+ bucket.put_object(
15
+ key: Values::S3Path.concretize(local_root, current_path, s3_locations.prefix),
16
+ body: File.read(current_path)
17
+ )
18
+ end
19
+ true
20
+ end
21
+
22
+ private
23
+
24
+ def paths
25
+ @paths ||= if File.file?(path)
26
+ [path]
27
+ else
28
+ Dir[path.join('**', '*')]
29
+ end
30
+ end
31
+
32
+ def local_root
33
+ @local_root ||= File.file?(path) ? File.dirname(path) : path
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deadpull
4
+ module Commands
5
+ class S3Command < Base
6
+ private
7
+
8
+ def s3_client
9
+ @s3_client ||= Values::S3Client.concretize(configuration)
10
+ end
11
+
12
+ def s3_locations
13
+ @s3_locations ||= Values::S3Locations.concretize(configuration, environment)
14
+ end
15
+
16
+ def bucket
17
+ @bucket ||= Aws::S3::Bucket.new(s3_locations.bucket, client: s3_client)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'deadpull/commands/base'
4
+ require 'deadpull/commands/s3_command'
5
+ require 'deadpull/commands/push'
6
+ require 'deadpull/commands/pull'
7
+
8
+ module Deadpull
9
+ module Commands; end
10
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deadpull
4
+ module Values
5
+ class Base
6
+ def self.concretize(*args)
7
+ new(*args).concretize
8
+ end
9
+
10
+ def concretize
11
+ raise NotImplementedError, "implement `concretize` for #{self.class.name}"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deadpull
4
+ module Values
5
+ class Configuration < Base
6
+ extend Dry::Initializer
7
+
8
+ param :inline_config, default: proc { {} }
9
+
10
+ def concretize
11
+ Builders::Configuration.new.with_step_args(
12
+ inline_config: [inline_config]
13
+ ).call({}).value!
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deadpull
4
+ module Values
5
+ class Environment < Base
6
+ extend Dry::Initializer
7
+
8
+ option :environment, optional: true
9
+
10
+ def concretize
11
+ environment || ENV['DEADPULL_ENV'] || ENV['RAILS_ENV'] || 'development'
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deadpull
4
+ module Values
5
+ class LocalLocation < Base
6
+ extend Dry::Initializer
7
+
8
+ param(:path, proc { |path| path.is_a?(Pathname) ? path : Pathname.new(File.expand_path(path)) })
9
+ param :prefix
10
+ param :key
11
+
12
+ def concretize
13
+ create_local_directory unless local_directory_exists?
14
+ local_path
15
+ end
16
+
17
+ private
18
+
19
+ def local_path
20
+ @local_path ||= path.join(key.sub(%r{\A#{prefix}?\/}, ''))
21
+ end
22
+
23
+ def local_dirname
24
+ @local_dirname = File.dirname(local_path)
25
+ end
26
+
27
+ def local_directory_exists?
28
+ Dir.exist?(local_dirname)
29
+ end
30
+
31
+ def create_local_directory
32
+ FileUtils.mkdir_p(local_dirname)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deadpull
4
+ module Values
5
+ class RootRelativePath < Base
6
+ extend Dry::Initializer
7
+
8
+ param :root
9
+ param :path
10
+
11
+ def concretize
12
+ path.sub(%r{\A#{root}?\/}, '')
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deadpull
4
+ module Values
5
+ class S3Client < Base
6
+ extend Dry::Initializer
7
+
8
+ param :config
9
+
10
+ def concretize
11
+ aws = config[:aws] || {}
12
+ Aws::S3::Client.new(aws)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deadpull
4
+ module Values
5
+ class S3Locations < Base
6
+ extend Dry::Initializer
7
+
8
+ param :config
9
+ param :environment
10
+
11
+ def concretize
12
+ OpenStruct.new(bucket: bucket, prefix: prefix)
13
+ end
14
+
15
+ private
16
+
17
+ def bucket
18
+ @bucket ||= config[:path].split('/')[0]
19
+ end
20
+
21
+ def prefix
22
+ @prefix ||= (config[:path].split('/')[1..-1] + [environment]).reject(&:blank?).join('/')
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deadpull
4
+ module Values
5
+ class S3Path < Base
6
+ extend Dry::Initializer
7
+
8
+ param :local_root
9
+ param :local_path
10
+ param :s3_prefix
11
+
12
+ def concretize
13
+ [s3_prefix, RootRelativePath.concretize(local_root, local_path)].join('/')
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'deadpull/values/base'
4
+ require 'deadpull/values/configuration'
5
+ require 'deadpull/values/environment'
6
+ require 'deadpull/values/s3_client'
7
+ require 'deadpull/values/s3_locations'
8
+ require 'deadpull/values/root_relative_path'
9
+ require 'deadpull/values/s3_path'
10
+ require 'deadpull/values/local_location'
11
+
12
+ module Deadpull
13
+ module Values; end
14
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deadpull
4
+ VERSION = '0.1.0'
5
+ end
data/lib/deadpull.rb ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'aws-sdk-s3'
4
+ require 'dry/transaction'
5
+ require 'dry/initializer'
6
+ require 'active_support/core_ext/object/blank'
7
+
8
+ require 'deadpull/version'
9
+
10
+ require 'deadpull/values'
11
+ require 'deadpull/builders'
12
+ require 'deadpull/commands'
13
+
14
+ module Deadpull; end
metadata ADDED
@@ -0,0 +1,221 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: deadpull
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Paweł J. Wal
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-07-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: aws-sdk-s3
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: dry-initializer
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.4'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.4'
55
+ - !ruby/object:Gem::Dependency
56
+ name: dry-transaction
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.13'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.13'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.16'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.16'
83
+ - !ruby/object:Gem::Dependency
84
+ name: fakefs
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.14'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.14'
97
+ - !ruby/object:Gem::Dependency
98
+ name: pry
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.11'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.11'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rake
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '10.0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '10.0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '3.0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '3.0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: simplecov
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '0.16'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '0.16'
153
+ description:
154
+ email:
155
+ - p@steamshard.net
156
+ executables:
157
+ - deadpull
158
+ extensions: []
159
+ extra_rdoc_files: []
160
+ files:
161
+ - ".gitignore"
162
+ - ".rspec"
163
+ - ".rubocop.yml"
164
+ - ".ruby-version"
165
+ - ".travis.yml"
166
+ - CODE_OF_CONDUCT.md
167
+ - Gemfile
168
+ - Gemfile.lock
169
+ - LICENSE.txt
170
+ - README.md
171
+ - Rakefile
172
+ - bin/console
173
+ - bin/setup
174
+ - config.reek
175
+ - deadpull.gemspec
176
+ - exe/deadpull
177
+ - lib/deadpull.rb
178
+ - lib/deadpull/builders.rb
179
+ - lib/deadpull/builders/configuration.rb
180
+ - lib/deadpull/capistrano/deadpull.rb
181
+ - lib/deadpull/capistrano/tasks/deadpull.rake
182
+ - lib/deadpull/commands.rb
183
+ - lib/deadpull/commands/base.rb
184
+ - lib/deadpull/commands/pull.rb
185
+ - lib/deadpull/commands/push.rb
186
+ - lib/deadpull/commands/s3_command.rb
187
+ - lib/deadpull/values.rb
188
+ - lib/deadpull/values/base.rb
189
+ - lib/deadpull/values/configuration.rb
190
+ - lib/deadpull/values/environment.rb
191
+ - lib/deadpull/values/local_location.rb
192
+ - lib/deadpull/values/root_relative_path.rb
193
+ - lib/deadpull/values/s3_client.rb
194
+ - lib/deadpull/values/s3_locations.rb
195
+ - lib/deadpull/values/s3_path.rb
196
+ - lib/deadpull/version.rb
197
+ homepage: https://github.com/paweljw/deadpull
198
+ licenses:
199
+ - MIT
200
+ metadata: {}
201
+ post_install_message:
202
+ rdoc_options: []
203
+ require_paths:
204
+ - lib
205
+ required_ruby_version: !ruby/object:Gem::Requirement
206
+ requirements:
207
+ - - ">="
208
+ - !ruby/object:Gem::Version
209
+ version: '0'
210
+ required_rubygems_version: !ruby/object:Gem::Requirement
211
+ requirements:
212
+ - - ">="
213
+ - !ruby/object:Gem::Version
214
+ version: '0'
215
+ requirements: []
216
+ rubyforge_project:
217
+ rubygems_version: 2.7.6
218
+ signing_key:
219
+ specification_version: 4
220
+ summary: Share config securely with your servers and organization using AWS S3.
221
+ test_files: []