aptly-watcher 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0075d97cdbdfb1ee8109e056d10b3394d24e1543
4
+ data.tar.gz: 10514a3381747764025f9e24aa82d257490315f3
5
+ SHA512:
6
+ metadata.gz: 65e14eac20a518b06f3f503c605554ed89dfa89310626c17cc8590aa3c9ebe3d86b047916996359fae1cf906d99116c1a35a8be4fdff5959e2f249b7e0a64f57
7
+ data.tar.gz: 67405c474d1fcb2d3a9fd1ebacde3cf6bf86f7782273d721e0c7bbada222492c6c16e2e0331e4715a71678952a6b2be761e6028f75fce13845d8012c8f168074
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in aptly-watcher.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Robert McLeod
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 all
13
+ 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 THE
21
+ SOFTWARE.
22
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Robert McLeod
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,74 @@
1
+ # Aptly::Watcher
2
+
3
+ Configures Aptly in a application centric manner, and watches a set of folders for incoming Debian packages.
4
+
5
+ The idea is that you setup a user on a server and push packages over SSH to the folder relating to your repo using password-less certificate based login.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'aptly-watcher'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install aptly-watcher
22
+
23
+ ## Usage
24
+
25
+ First you need to create a config file as such (we parse tildes to `ENV['HOME']`):
26
+
27
+ ```yaml
28
+ ---
29
+ :distrib: myapp
30
+ :repos:
31
+ - stable
32
+ - testing
33
+ :log: ~/watcher.log # use '-' for STDOUT
34
+ :conf: ~/aptly.conf
35
+ :pidfile: ~/aptly-watcher.pid
36
+ :incoming_dir: ~/incoming
37
+ :user: aptly
38
+ ```
39
+
40
+ Then you can run the watcher with the config file:
41
+
42
+ $ aptly-watcher -c config.yml
43
+
44
+ This will:
45
+
46
+ * create the repositories (if they don't exist)
47
+ * publish the repository for the first time
48
+ * create the folders to watch (if they don't exist)
49
+ * `~/incoming/stable`
50
+ * `~/incoming/testing`
51
+ * start watching for files added to the folders
52
+
53
+ Every time a file is added to one of the watched folders:
54
+
55
+ * the file is added to the relative repository (i.e. `~/incoming/stable` goes to stable)
56
+ * the repository is published again
57
+
58
+ You will then be able to access your repos via apt like so:
59
+
60
+ ```
61
+ deb http://debian.example.com/ myapp stable testing # OR
62
+ #deb http://debian.example.com/ myapp stable # OR
63
+ #deb http://debian.example.com/ myapp testing
64
+ ```
65
+
66
+ Files added to the folders will immediately be available in the repository.
67
+
68
+ ## Contributing
69
+
70
+ 1. Fork it ( https://github.com/AutogrowSystems/aptly-watcher/fork )
71
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
72
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
73
+ 4. Push to the branch (`git push origin my-new-feature`)
74
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'aptly/watcher/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "aptly-watcher"
8
+ spec.version = Aptly::Watcher::VERSION
9
+ spec.authors = ["Robert McLeod"]
10
+ spec.email = ["robert@autogrow.com"]
11
+ spec.summary = %q{Watches folders and adds them to their relative aptly repository}
12
+ spec.description = %q{Configures Aptly in a application centric manner, and watches a set of folders for incoming Debian packages.}
13
+ spec.homepage = "https://github.com/AutogrowSystems/aptly-watcher"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "slop", "~> 3.0"
22
+ spec.add_dependency "rb-inotify"
23
+ spec.add_dependency "redis"
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.7"
26
+ spec.add_development_dependency "rake", "~> 10.0"
27
+ end
data/bin/aptly-watcher ADDED
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'aptly/watcher'
4
+ require 'logger'
5
+ require 'fileutils'
6
+ require 'slop'
7
+
8
+ opts = Slop.parse strict: true, help: true do
9
+ on :c, :config=, "Location of the config file"
10
+ end
11
+
12
+ abort "ERROR: Must specify config file" unless opts[:config]
13
+
14
+ # Try to parse the config file
15
+ begin
16
+ config = Aptly::Watcher::Config.parse(opts[:config])
17
+ Aptly::Watcher.setup(config)
18
+ rescue ArgumentError => e
19
+ abort "ERROR: #{e.message}"
20
+ end
21
+
22
+ # remove pidfile on exit
23
+ Signal.trap("EXIT") do
24
+ FileUtils.rm config[:pidfile]
25
+ end
26
+
27
+ begin
28
+ Aptly::Watcher.log :info, "Starting the watcher on pid #{Process.pid}"
29
+ File.write(config[:pidfile], Process.pid)
30
+ Aptly::Watcher.run(config)
31
+ rescue ArgumentError, StandardError => e
32
+ Aptly::Watcher.log :fatal, e.message
33
+ # abort "ERROR: #{e.message}"
34
+ raise e
35
+ end
@@ -0,0 +1,56 @@
1
+ module Aptly
2
+ module Watcher
3
+ class AptlyShim
4
+ def initialize(name, components, config)
5
+ @name = name # distribution
6
+ @components = components # repos
7
+ @config = config
8
+
9
+ create
10
+ end
11
+
12
+ def create
13
+ repo_list = `aptly repo list --raw`
14
+
15
+ @components.each do |repo|
16
+ next if repo_list.include? repo
17
+ output = `aptly repo create #{distrib} #{config} #{component(repo)} #{repo}`
18
+ raise StandardError, "Failed to create repo #{repo}\n#{output}" unless $?.success?
19
+ end
20
+
21
+ published_list = `aptly publish list --raw`
22
+
23
+ unless published_list.include? @name
24
+ output = `aptly publish repo #{config} #{distrib} #{component(:all)} #{component(:all, ' ')}`
25
+ raise StandardError, "Failed to publish #{@name} for the first time\n#{output}" unless $?.success?
26
+ end
27
+ end
28
+
29
+ def add(repo, path)
30
+ # TODO: check if the file has already been added
31
+ # TODO: check that the file is a Debian package
32
+ output = `aptly repo add -remove-files=true #{config} #{repo} #{path} 2>&1`
33
+ raise StandardError, "Failed to add #{path} to #{repo(component)}\n#{output}" unless $?.success?
34
+ end
35
+
36
+ def publish
37
+ system "aptly publish update #{config} #{@name}"
38
+ end
39
+
40
+ private
41
+
42
+ def component(repo, joiner=',')
43
+ repo = @components.join(joiner) if repo == :all
44
+ "-component=#{repo}"
45
+ end
46
+
47
+ def distrib
48
+ "-distribution=#{@name}"
49
+ end
50
+
51
+ def config
52
+ "-config='#{@config}'"
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,46 @@
1
+ require 'yaml'
2
+ require 'json'
3
+
4
+ module Aptly
5
+ module Watcher
6
+ module Config
7
+ class << self
8
+
9
+ def parse(config_file)
10
+ raise ArgumentError, "Config file not found: #{config_file}" unless File.exist?(config_file)
11
+ config = YAML.load_file(config_file)
12
+ valid_config!(config)
13
+ config = parse_tildes(config)
14
+ config[:aptly] = parse_aptly_conf(config[:conf])
15
+ config
16
+ end
17
+
18
+ def valid_config!(config)
19
+ raise ArgumentError, "Config file was empty" unless config
20
+ raise ArgumentError, "Config file missing :pidfile:" unless config[:pidfile]
21
+ raise ArgumentError, "Config file missing :conf:" unless config[:conf]
22
+ raise ArgumentError, "Config file missing :log:" unless config[:log]
23
+ raise ArgumentError, "Config file missing :repos:" unless config[:repos]
24
+ raise ArgumentError, "Config file missing :distrib:" unless config[:distrib]
25
+ raise ArgumentError, "Config file missing :incoming_dir:" unless config[:incoming_dir]
26
+ end
27
+
28
+ # Parse any tildes into the full home path
29
+ def parse_tildes(config)
30
+ [:pidfile, :conf, :incoming_dir, :log].each do |key|
31
+ config[key].sub! /~/, ENV['HOME']
32
+ end
33
+
34
+ config
35
+ end
36
+
37
+ # Load the aptly config
38
+ def parse_aptly_conf(conf)
39
+ raise ArgumentError, "Aptly config file does not exist: #{conf}" unless File.exist? conf
40
+ JSON.parse(File.read(conf))
41
+ end
42
+
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,32 @@
1
+ module Aptly
2
+ module Watcher
3
+ class EventDispatcher
4
+ def initialize(aptly)
5
+ @added = []
6
+ @aptly = aptly
7
+ end
8
+
9
+ def process(dir, event, component)
10
+ return false unless valid_event?(event)
11
+
12
+ filepath = "#{dir}/#{event.name}"
13
+
14
+ raise StandardError, "File not found: #{filepath}" unless File.exists? filepath
15
+
16
+ @aptly.add(component, filepath)
17
+ @aptly.publish
18
+
19
+ @added << event.name
20
+ true
21
+ end
22
+
23
+ def valid_event?(event)
24
+ return false if
25
+ ( @added.include? event.name ) or
26
+ ( event.name.nil? or event.name == '' )
27
+ true
28
+ end
29
+
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,5 @@
1
+ module Aptly
2
+ module Watcher
3
+ VERSION = "0.3.0"
4
+ end
5
+ end
@@ -0,0 +1,71 @@
1
+ require 'rb-inotify'
2
+ require 'redis'
3
+
4
+ require "aptly/watcher/version"
5
+ require 'aptly/watcher/event_dispatcher'
6
+ require 'aptly/watcher/aptly_shim'
7
+ require 'aptly/watcher/config'
8
+
9
+ module Aptly
10
+ module Watcher
11
+ class << self
12
+
13
+ def redis
14
+ @redis ||= Redis.new
15
+ end
16
+
17
+ def logger
18
+ return @logger if @logger
19
+ target = (@config[:log] == '-') ? STDOUT : @config[:log]
20
+ @logger ||= Logger.new target, 0, 1024000
21
+ end
22
+
23
+ # sets up the incoming directories
24
+ def setup(config)
25
+ @config = config
26
+ @config[:repos].each do |repo|
27
+ repo_dir = "#{@config[:incoming_dir]}/#{@config[:distrib]}/#{repo}"
28
+
29
+ # Create the directory if it's not already there
30
+ FileUtils.mkdir_p(repo_dir) unless File.directory?(repo_dir)
31
+ end
32
+ end
33
+
34
+ # log into redis so we can pick it up in the DMS
35
+ def log(level, message)
36
+ timestamp = Time.now.strftime('%Y-%m-%d %H:%M:%S')
37
+ msg = "#{level[0].upcase}, [#{timestamp}] #{level.upcase} -- #{message}"
38
+ redis.lpush("aptly:watcher:log", msg)
39
+ redis.ltrim("aptly:watcher:log", 0, 99)
40
+ logger.send(level.to_sym, message)
41
+ end
42
+
43
+ # run the watcher with the given config
44
+ def run(config)
45
+ notifier = INotify::Notifier.new
46
+ aptly = Aptly::Watcher::AptlyShim.new(config[:distrib], config[:repos], config[:conf])
47
+ dispatcher = Aptly::Watcher::EventDispatcher.new(aptly)
48
+
49
+ # setup a notifier for each repo
50
+ config[:repos].each do |repo|
51
+ repo_dir = "#{config[:incoming_dir]}/#{@config[:distrib]}/#{repo}"
52
+
53
+ # watch the directory for new files
54
+ notifier.watch(repo_dir, :close_write) do |event|
55
+ begin
56
+ dispatcher.process(repo_dir, event, repo)
57
+ log :info, "Successfully added #{event.name} to #{repo}"
58
+ system "chown #{config[:user]}:#{config[:user]} #{config[:aptly]['rootDir']} -R"
59
+ rescue StandardError => e
60
+ e.message.lines.each {|line| log :error, line.chomp }
61
+ end
62
+ end
63
+ end
64
+
65
+ # start the notifier
66
+ notifier.run
67
+ end
68
+
69
+ end
70
+ end
71
+ end
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: aptly-watcher
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Robert McLeod
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-07-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: slop
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rb-inotify
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: redis
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.7'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.7'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ description: Configures Aptly in a application centric manner, and watches a set of
84
+ folders for incoming Debian packages.
85
+ email:
86
+ - robert@autogrow.com
87
+ executables:
88
+ - aptly-watcher
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - ".gitignore"
93
+ - Gemfile
94
+ - LICENSE
95
+ - LICENSE.txt
96
+ - README.md
97
+ - Rakefile
98
+ - aptly-watcher.gemspec
99
+ - bin/aptly-watcher
100
+ - lib/aptly/watcher.rb
101
+ - lib/aptly/watcher/aptly_shim.rb
102
+ - lib/aptly/watcher/config.rb
103
+ - lib/aptly/watcher/event_dispatcher.rb
104
+ - lib/aptly/watcher/version.rb
105
+ homepage: https://github.com/AutogrowSystems/aptly-watcher
106
+ licenses:
107
+ - MIT
108
+ metadata: {}
109
+ post_install_message:
110
+ rdoc_options: []
111
+ require_paths:
112
+ - lib
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ requirements: []
124
+ rubyforge_project:
125
+ rubygems_version: 2.4.2
126
+ signing_key:
127
+ specification_version: 4
128
+ summary: Watches folders and adds them to their relative aptly repository
129
+ test_files: []