vx-aptly 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/Dockerfile +11 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +29 -0
  7. data/Rakefile +36 -0
  8. data/bin/vx-aptly +4 -0
  9. data/doc/aptly.example +16 -0
  10. data/doc/aptly.upstart +27 -0
  11. data/doc/cloud-files.example +9 -0
  12. data/docker/install.sh +47 -0
  13. data/docker/my_init +57 -0
  14. data/docker/supervisord.conf +17 -0
  15. data/lib/vx/aptly.rb +33 -0
  16. data/lib/vx/aptly/app.rb +194 -0
  17. data/lib/vx/aptly/cli.sh +116 -0
  18. data/lib/vx/aptly/config.ru +3 -0
  19. data/lib/vx/aptly/mixin/capture.rb +37 -0
  20. data/lib/vx/aptly/package.rb +110 -0
  21. data/lib/vx/aptly/public/404.html +153 -0
  22. data/lib/vx/aptly/public/css/animate.css +3158 -0
  23. data/lib/vx/aptly/public/css/bootstrap.min.css +5 -0
  24. data/lib/vx/aptly/public/css/flat-ui.css +6007 -0
  25. data/lib/vx/aptly/public/css/theme.css +131 -0
  26. data/lib/vx/aptly/public/fonts/glyphicons/flat-ui-icons-regular.eot +0 -0
  27. data/lib/vx/aptly/public/fonts/glyphicons/flat-ui-icons-regular.svg +126 -0
  28. data/lib/vx/aptly/public/fonts/glyphicons/flat-ui-icons-regular.ttf +0 -0
  29. data/lib/vx/aptly/public/fonts/glyphicons/flat-ui-icons-regular.woff +0 -0
  30. data/lib/vx/aptly/public/fonts/glyphicons/selection.json +2106 -0
  31. data/lib/vx/aptly/public/fonts/lato/lato-black.eot +0 -0
  32. data/lib/vx/aptly/public/fonts/lato/lato-black.svg +4691 -0
  33. data/lib/vx/aptly/public/fonts/lato/lato-black.ttf +0 -0
  34. data/lib/vx/aptly/public/fonts/lato/lato-black.woff +0 -0
  35. data/lib/vx/aptly/public/fonts/lato/lato-bold.eot +0 -0
  36. data/lib/vx/aptly/public/fonts/lato/lato-bold.svg +5085 -0
  37. data/lib/vx/aptly/public/fonts/lato/lato-bold.ttf +0 -0
  38. data/lib/vx/aptly/public/fonts/lato/lato-bold.woff +0 -0
  39. data/lib/vx/aptly/public/fonts/lato/lato-bolditalic.eot +0 -0
  40. data/lib/vx/aptly/public/fonts/lato/lato-bolditalic.svg +4514 -0
  41. data/lib/vx/aptly/public/fonts/lato/lato-bolditalic.ttf +0 -0
  42. data/lib/vx/aptly/public/fonts/lato/lato-bolditalic.woff +0 -0
  43. data/lib/vx/aptly/public/fonts/lato/lato-italic.eot +0 -0
  44. data/lib/vx/aptly/public/fonts/lato/lato-italic.svg +4514 -0
  45. data/lib/vx/aptly/public/fonts/lato/lato-italic.ttf +0 -0
  46. data/lib/vx/aptly/public/fonts/lato/lato-italic.woff +0 -0
  47. data/lib/vx/aptly/public/fonts/lato/lato-light.eot +0 -0
  48. data/lib/vx/aptly/public/fonts/lato/lato-light.svg +4691 -0
  49. data/lib/vx/aptly/public/fonts/lato/lato-light.ttf +0 -0
  50. data/lib/vx/aptly/public/fonts/lato/lato-light.woff +0 -0
  51. data/lib/vx/aptly/public/fonts/lato/lato-regular.eot +0 -0
  52. data/lib/vx/aptly/public/fonts/lato/lato-regular.svg +4691 -0
  53. data/lib/vx/aptly/public/fonts/lato/lato-regular.ttf +0 -0
  54. data/lib/vx/aptly/public/fonts/lato/lato-regular.woff +0 -0
  55. data/lib/vx/aptly/public/img/favicon.ico +0 -0
  56. data/lib/vx/aptly/public/js/app.js +12 -0
  57. data/lib/vx/aptly/public/js/jquery_ujs.js +446 -0
  58. data/lib/vx/aptly/repo.rb +161 -0
  59. data/lib/vx/aptly/repo_publish.rb +165 -0
  60. data/lib/vx/aptly/version.rb +5 -0
  61. data/lib/vx/aptly/views/index.erb +94 -0
  62. data/lib/vx/aptly/views/layout.erb +54 -0
  63. data/lib/vx/aptly/views/repo_publishes/new.erb +49 -0
  64. data/lib/vx/aptly/views/repos/edit.erb +17 -0
  65. data/lib/vx/aptly/views/repos/errors.erb +5 -0
  66. data/lib/vx/aptly/views/repos/form.erb +26 -0
  67. data/lib/vx/aptly/views/repos/new.erb +17 -0
  68. data/lib/vx/aptly/views/repos/packages.erb +51 -0
  69. data/lib/vx/aptly/views/repos/show.erb +66 -0
  70. data/spec/lib/repo_publish_spec.rb +60 -0
  71. data/spec/spec_helper.rb +13 -0
  72. data/spec/support/aptly.conf +0 -0
  73. data/spec/support/home.rb +39 -0
  74. data/vx-aptly.gemspec +32 -0
  75. metadata +263 -0
@@ -0,0 +1,161 @@
1
+ module Vx
2
+ module Aptly
3
+ class Repo
4
+
5
+ include Mixin::Capture
6
+ extend Mixin::Capture
7
+
8
+ attr_accessor :error, :attributes, :notice
9
+
10
+ def initialize(params = {})
11
+ @attributes = {}
12
+ params.each do |k,v|
13
+ if self.respond_to?(:"#{k}=")
14
+ self.public_send(:"#{k}=", v)
15
+ end
16
+ end
17
+ end
18
+
19
+ def persisted!
20
+ @persisted = true
21
+ end
22
+
23
+ def new_record?
24
+ !@persisted
25
+ end
26
+
27
+ def valid?
28
+ !@error
29
+ end
30
+
31
+ def packages(reload = false)
32
+ @packages = nil if reload
33
+ @packages ||= Package.all(self)
34
+ end
35
+
36
+ %w{ name comment default_component default_distribution number_of_packages}.each do |m|
37
+ define_method :"#{m}=" do |value|
38
+ if value.is_a?(String) && value == ""
39
+ @attributes[m.to_sym] = nil
40
+ else
41
+ @attributes[m.to_sym] = value
42
+ end
43
+ end
44
+
45
+ define_method :"#{m}" do
46
+ value = @attributes[m.to_sym]
47
+ if value == nil
48
+ case m
49
+ when 'default_distribution'
50
+ value = 'ubuntu'
51
+ when 'default_component'
52
+ value = 'main'
53
+ end
54
+ end
55
+ value
56
+ end
57
+ end
58
+
59
+ def create
60
+ line = "aptly repo create"
61
+ line << " -comment=\"#{comment}\"" if comment
62
+ line << " -component=\"#{default_component}\"" if default_component
63
+ line << " -distribution=\"#{default_distribution}\"" if default_distribution
64
+ line << " #{name}" if name
65
+
66
+ begin
67
+ capture line do |re|
68
+ @notice = "The command '#{line}' was successfuly finished with output:\n"
69
+ @notice << re
70
+ end
71
+ persisted!
72
+ rescue AptlyCommandError => e
73
+ @error = e
74
+ end
75
+
76
+ valid?
77
+ end
78
+
79
+ def update
80
+ line = "aptly repo edit"
81
+ line << " -comment=\"#{comment}\"" if comment
82
+ line << " -component=\"#{default_component}\"" if default_component
83
+ line << " -distribution=\"#{default_distribution}\"" if default_distribution
84
+ line << " #{name}" if name
85
+
86
+ begin
87
+ capture line do |re|
88
+ @notice = "The command '#{line}' was successfuly finished with output:\n"
89
+ @notice << re
90
+ end
91
+ rescue AptlyCommandError => e
92
+ @error = e
93
+ end
94
+
95
+ valid?
96
+ end
97
+
98
+ def destroy
99
+ cmd = "aptly repo drop"
100
+ cmd << " #{name}" if name
101
+ capture cmd do |re|
102
+ @notice = "The command '#{cmd}' was successfuly finished with output:\n"
103
+ @notice << re
104
+ end
105
+ end
106
+
107
+ def publishes(reload = false)
108
+ @publishes = nil if reload
109
+ @publishes ||= RepoPublish.for_repo(self)
110
+ end
111
+
112
+ def build_publish(options = {})
113
+ options[:repo_name] = self.name
114
+ options = {
115
+ component: default_component,
116
+ distribution: default_distribution
117
+ }.merge(options)
118
+
119
+ RepoPublish.new(options)
120
+ end
121
+
122
+ class << self
123
+
124
+ def find(name)
125
+ capture "aptly repo show #{name}" do |re|
126
+ repo = new(name: name)
127
+ repo.persisted!
128
+
129
+ re.lines.each do |line|
130
+ key, value = line.split(":").map(&:strip)
131
+ case key
132
+ when 'Comment'
133
+ repo.comment = value
134
+ when 'Default Distribution'
135
+ repo.default_distribution = value
136
+ when 'Default Component'
137
+ repo.default_component = value
138
+ when 'Number of packages'
139
+ repo.number_of_packages = value.to_i
140
+ end
141
+ end
142
+
143
+ repo
144
+ end
145
+ end
146
+
147
+ def all
148
+ capture "aptly repo list" do |re|
149
+ re.lines.inject([]) do |a, line|
150
+ if line =~ / \* \[(.*)\]/
151
+ a << find($1)
152
+ end
153
+ a
154
+ end
155
+ end
156
+ end
157
+ end
158
+
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,165 @@
1
+ module Vx
2
+ module Aptly
3
+ class RepoPublish
4
+
5
+ PublishedRepo = Struct.new(:name, :component)
6
+
7
+ include Mixin::Capture
8
+ extend Mixin::Capture
9
+
10
+ attr_reader :attributes, :error, :notice
11
+
12
+ def initialize(params = {})
13
+ @attributes = {}
14
+ params.each do |k,v|
15
+ if self.respond_to?(:"#{k}=")
16
+ self.public_send(:"#{k}=", v)
17
+ end
18
+ end
19
+ end
20
+
21
+ def id
22
+ [prefix, distribution].join("/")
23
+ end
24
+
25
+ def valid?
26
+ !@error
27
+ end
28
+
29
+ def create
30
+ line = "aptly publish repo"
31
+ line << " -architectures=\"#{architectures}\"" if architectures
32
+
33
+ components = repos.map{|i| "" }.join(",") # all components
34
+ line << " -component=\"#{components}\""
35
+
36
+ line << " -distribution=\"#{distribution}\"" if distribution
37
+
38
+ repos.each do |repo|
39
+ line << " #{repo.name}"
40
+ end
41
+
42
+ if repos.any? && prefix
43
+ line << " #{prefix}"
44
+ end
45
+
46
+ begin
47
+ capture line do |re|
48
+ @notice = "The command '#{line}' was successfuly finished with output:\n"
49
+ @notice << re
50
+ end
51
+ rescue AptlyCommandError => e
52
+ @error = e
53
+ end
54
+
55
+ valid?
56
+ end
57
+
58
+ def update
59
+ begin
60
+ line = update_command_line
61
+ capture line do |re|
62
+ @notice = "The command '#{line}' was successfuly finished with output:\n"
63
+ @notice << re
64
+ end
65
+ rescue AptlyCommandError => e
66
+ @error = e
67
+ end
68
+
69
+ valid?
70
+ end
71
+
72
+ def update_command_line
73
+ line = "aptly publish update"
74
+ line << " -architectures=\"#{architectures}\"" if architectures
75
+ line << " #{distribution}" if distribution
76
+ line << " #{prefix}" if distribution && prefix
77
+ line
78
+ end
79
+
80
+ def destroy
81
+ line = "aptly publish drop "
82
+ line << " -architectures=\"#{architectures}\"" if architectures
83
+ line << " #{distribution}" if distribution
84
+ line << " #{prefix}" if distribution && prefix
85
+ begin
86
+ capture line do |re|
87
+ @notice = "The command '#{line}' was successfuly finished with output:\n"
88
+ @notice << re
89
+ end
90
+ rescue AptlyCommandError => e
91
+ @error = e
92
+ end
93
+ valid?
94
+ end
95
+
96
+ %w{ prefix component architectures distribution }.each do |m|
97
+ define_method :"#{m}=" do |value|
98
+ if value.to_s.strip == ''
99
+ value = nil
100
+ end
101
+ @attributes[m.to_sym] = value
102
+ end
103
+
104
+ define_method :"#{m}" do
105
+ @attributes[m.to_sym]
106
+ end
107
+ end
108
+
109
+ def repos=(val)
110
+ @attributes[:repos] = []
111
+ if val.is_a?(Array)
112
+ val.each do |repo|
113
+ if repo.is_a?(Hash)
114
+ @attributes[:repos].push PublishedRepo.new(repo[:name], repo[:component])
115
+ else
116
+ @attributes[:repos].push PublishedRepo.new(repo.to_s)
117
+ end
118
+ end
119
+ end
120
+ @attributes[:repos]
121
+ end
122
+
123
+ def repos
124
+ @attributes[:repos] ||= []
125
+ @attributes[:repos]
126
+ end
127
+
128
+ class << self
129
+ def all(options = {})
130
+ capture 'aptly publish list' do |re|
131
+ re.lines.inject([]) do |a,line|
132
+ if line =~ /\* (.+)\/(.+) \[(.+)\] publishes (.*)$/
133
+ it = new(prefix: $1, distribution: $2, architectures: $3)
134
+ repos = $4
135
+ published_repos = []
136
+
137
+ repos.split(",").map(&:strip).map do |repo|
138
+ if repo =~ /\{(.*): \[(.*)\]/
139
+ published_repos << { name: $2, component: $1 }
140
+ end
141
+ end
142
+
143
+ it.repos = published_repos
144
+
145
+ a << it
146
+ end
147
+ a
148
+ end
149
+ end
150
+ end
151
+
152
+ def for_repo(repo)
153
+ all.select{|pub| pub.repos.map(&:name).include?(repo.name) }
154
+ end
155
+
156
+ def find(id)
157
+ if id.split("/").size == 1
158
+ id = "./#{id}"
159
+ end
160
+ all.find{|pub| pub.id == id } or raise(PublishNotFoundError, "The publish with id #{id} was not found")
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,5 @@
1
+ module Vx
2
+ module Aptly
3
+ VERSION = "0.9.0"
4
+ end
5
+ end
@@ -0,0 +1,94 @@
1
+ <% @menu = [['Dashboard', '/', true]] %>
2
+
3
+ <% if @repos.any? %>
4
+ <div class="panel panel-default">
5
+ <div class="panel-heading">
6
+ <div class="pull-right">
7
+ <a href="/repos/new" class="btn btn-default btn-sm">
8
+ <%= ico 'plus' %>
9
+ Add a repo
10
+ </a>
11
+ </div>
12
+ <h6>Repos</h6>
13
+ </div>
14
+ <table class="table table-hover">
15
+ <% for repo in @repos %>
16
+ <tr>
17
+ <td>
18
+ <a href="/repos/<%= repo.name %>">
19
+ <small>
20
+ <%= repo.name %> (<%= repo.number_of_packages %>)
21
+ </small>
22
+ </a>
23
+ </td>
24
+ <td><small class="text-muted"><%= repo.comment %></small></td>
25
+ <td><small class=""><%= repo.default_distribution %></small></td>
26
+ <td><small class=""><%= repo.default_component %></small></td>
27
+ </tr>
28
+ <% end %>
29
+ </table>
30
+ </div>
31
+ <% else %>
32
+ <div class="jumbotron">
33
+ <p class="lead">You have no repositories</p>
34
+ <p><a class="btn btn-lg btn-success" href="/repos/new" role="button">
35
+ <%= ico 'plus' %>
36
+ Create one
37
+ </a></p>
38
+ </div>
39
+ <% end %>
40
+
41
+ <% if @pubs.any? %>
42
+ <div class="panel panel-default">
43
+ <div class="panel-heading">
44
+ <div class="pull-right">
45
+ <a href="/repo/publishes/new" class="btn btn-default btn-sm">
46
+ <%= ico 'plus' %>
47
+ Add a publish
48
+ </a>
49
+ </div>
50
+ <h6>Published Repos</h6>
51
+ </div>
52
+ <table class="table table-hover table-btn-group">
53
+ <% for pub in @pubs %>
54
+ <tr>
55
+ <td>
56
+ <small><%= pub.id %></small>
57
+ </td>
58
+ <td>
59
+ <small><%= pub.architectures %></small>
60
+ </td>
61
+ <td>
62
+ <% for repo in pub.repos %>
63
+ <a href="/repos/<%= repo.name %>" style="display:block">
64
+ <small class=""><%= repo.name %> (<%= repo.component %>)</small>
65
+ </a>
66
+ <% end %>
67
+ </td>
68
+ <td>
69
+ <div class="btn-group">
70
+ <a class="btn btn-xs btn-primary" href="/repo/publishes/<%= pub.id %>" data-method="patch" data-confirm="Are you sure?">
71
+ <%= ico 'upload' %>
72
+ Update
73
+ </a>
74
+ <a class="btn btn-xs btn-danger" href="/repo/publishes/<%= pub.id %>" data-method="delete" data-confirm="Are you sure?">
75
+ <%= ico 'cross' %>
76
+ Remove
77
+ </a>
78
+ </div>
79
+ </td>
80
+ </tr>
81
+ <% end %>
82
+ </table>
83
+ </div>
84
+ <% else %>
85
+ <% if @repos.any? %>
86
+ <div class="jumbotron">
87
+ <p class="lead">You have no published repos</p>
88
+ <p><a class="btn btn-lg btn-success" href="/repo/publishes/new" role="button">
89
+ <%= ico 'plus' %>
90
+ Create one
91
+ </a></p>
92
+ </div>
93
+ <% end %>
94
+ <% end %>
@@ -0,0 +1,54 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1">
7
+
8
+ <link rel="stylesheet" href="/css/bootstrap.min.css">
9
+ <link rel="stylesheet" href="/css/flat-ui.css">
10
+ <link rel="stylesheet" href="/css/animate.css">
11
+ <link rel="stylesheet" href="/css/theme.css">
12
+
13
+ <script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
14
+ <script src="/js/jquery_ujs.js"></script>
15
+ <script src="/js/app.js"></script>
16
+
17
+ <link rel="shortcut icon" href="img/favicon.ico">
18
+
19
+ <title>Aptly Web</title>
20
+ </head>
21
+
22
+ <body>
23
+
24
+ <a href="https://github.com/vexor/vx-aptly" target="_blank"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/a6677b08c955af8400f44c6298f40e7d19cc5b2d/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f677261795f3664366436642e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_gray_6d6d6d.png"></a>
25
+
26
+ <div class="container">
27
+ <div class="header">
28
+ <% if @menu %>
29
+ <ul class="nav nav-pills " role="tablist">
30
+ <% for name, url, active in @menu %>
31
+ <li role="presentation" class="<%= 'active' if active %>"><a href="<%= url %>"><%= name %></a></li>
32
+ <% end %>
33
+ </ul>
34
+ <% end %>
35
+ </div>
36
+
37
+ <% if flash[:notice] %>
38
+ <pre class="notice"><%= flash[:notice] %></pre>
39
+ <% end %>
40
+ <% if flash[:error] %>
41
+ <pre class="error"><%= flash[:error] %></pre>
42
+ <% end %>
43
+
44
+ <%= yield %>
45
+ <!--
46
+ <div class="footer">
47
+ <p>&copy; Vexor 2014</p>
48
+ </div>
49
+ -->
50
+
51
+ </div> <!-- /container -->
52
+ </body>
53
+ </html>
54
+