aptly-watcher 0.3.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
+ 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: []