melai 0.0.1

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.
@@ -0,0 +1,21 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+ Gemfile.lock
15
+ .rvmrc
16
+
17
+ # YARD artifacts
18
+ .yardoc
19
+ _yardoc
20
+ doc/
21
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in melai.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Mike Fiedler
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.
@@ -0,0 +1,67 @@
1
+ # Melai
2
+
3
+ Melai is a command-line tool to create software package repositories for
4
+ APT and YUM package management tools.
5
+
6
+ ## Installation
7
+ This tool is a standalone tool, and should be installed wither via RubyGems:
8
+
9
+ $ gem install melai
10
+
11
+ If you'd rather install dependencies and install from source:
12
+
13
+ $ bundle install && rake install
14
+
15
+ ## Usage
16
+
17
+ ### Requirements
18
+ Some prerequisites are needed, and exist currently on Debian/Ubuntu based distros.
19
+
20
+ * `apt-ftparchive` (Debian-style packages) - `sudo apt-get install apt-utils`
21
+ * `createrepo` (RedHat-style packages) - `sudo apt-get install createrepo`
22
+
23
+ Both of these external tools are needed to build the required repo metadata.
24
+
25
+ NOTE: This process cannot be run on RedHat/CentOS yet - as they do not have the
26
+ tools to work with Debian-style packages.
27
+
28
+ ## Execution
29
+
30
+ An example:
31
+
32
+ melai -r newrepo create -p sourcepackages -u "http://mypackageserver.com/myrepo"
33
+
34
+ Another way is to initialize a config, and use that:
35
+
36
+ melai -r newrepo initconfig
37
+
38
+ This will create a file named `~/.melai.rc`. Edit it to contain your values:
39
+
40
+ ---
41
+ :help: false
42
+ :r: newrepo
43
+ :repos-path: newrepo
44
+ commands:
45
+ :create:
46
+ :pkgs-path: sourcepackages
47
+ :url-root: "http://mypackageserver.com/myrepo"
48
+ :list: {}
49
+ :destroy: {}
50
+ :p: tmpfoo
51
+ :pkgs-path: tmpfoo
52
+ :u: "http://mypackageserver.com/myrepo"
53
+ :url-root: "http://mypackageserver.com/myrepo"
54
+ :list: {}
55
+ :destroy: {}
56
+
57
+ Then you can run `melai create` with no arguments.
58
+ See [here](https://github.com/davetron5000/gli/wiki/Config) for more details.
59
+
60
+ ## Contributing
61
+
62
+ 1. Fork it
63
+ 1. Create your feature branch (`git checkout -b my-new-feature`)
64
+ 1. Test your changes (`rake test`)
65
+ 1. Commit your changes (`git commit -am 'Added some feature'`)
66
+ 1. Push to the branch (`git push origin my-new-feature`)
67
+ 1. Create new Pull Request
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env rake
2
+ require 'bundler/gem_tasks'
3
+ require 'cucumber'
4
+ require 'cucumber/rake/task'
5
+
6
+ task :default => [:install, :test]
7
+
8
+ desc "Run tests"
9
+ task :test => [:features, :tailor]
10
+
11
+ Cucumber::Rake::Task.new(:features) do |t|
12
+ # t.cucumber_opts = ['--format pretty -x']
13
+
14
+ t.cucumber_opts = ['--format', 'progress', '-x']
15
+ t.cucumber_opts += ['features']
16
+
17
+ # This turns on the GLI debugging backtraces
18
+ t.cucumber_opts += ['GLI_DEBUG=true']
19
+ end
20
+
21
+ # https://github.com/turboladen/tailor
22
+ require 'tailor/rake_task'
23
+ Tailor::RakeTask.new do |task|
24
+ task.file_set('lib/**/*.rb', 'code') do |style|
25
+ style.max_line_length 100, level: :warn
26
+ style.max_code_lines_in_method 50, level: :warn
27
+ end
28
+ task.file_set('bin/*', 'binaries') do |style|
29
+ style.max_line_length 100, level: :warn
30
+ end
31
+ end
32
+
33
+ require 'rdoc/task'
34
+ Rake::RDocTask.new do |rd|
35
+ rd.main = "README.md"
36
+ rd.rdoc_files.include("README.md","lib/**/*.rb","bin/**/*")
37
+ rd.title = 'melai'
38
+ end
39
+
40
+ # File lib/tasks/notes.rake
41
+ desc "Find notes in code"
42
+ task :notes do
43
+ puts `grep --exclude=Rakefile -r 'OPTIMIZE:\\|FIXME:\\|TODO:' .`
44
+ end
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env ruby
2
+ # $: << File.expand_path(File.dirname(File.realpath(__FILE__)) + '/../lib')
3
+ # require 'rubygems'
4
+
5
+ require 'gli'
6
+ require_relative '../lib/melai'
7
+
8
+ include GLI
9
+
10
+ program_desc "Describe your application here"
11
+
12
+ version Melai::VERSION
13
+
14
+ # desc "Describe some switch here"
15
+ # switch [:s, :switch]
16
+
17
+ desc "Root directory for repositories, relative to current directory"
18
+ default_value "repo"
19
+ arg_name "REPOS_PATH"
20
+ flag [:r, "repos-path"]
21
+
22
+ # https://github.com/davetron5000/gli/wiki/Config
23
+ config_file '.melai.rc'
24
+
25
+ desc "Create a repository structure"
26
+ command :create do |c|
27
+ # TODO: add a force option to rebuild from scratch
28
+
29
+ c.desc "Source directory containing all packages, relative to current directory"
30
+ c.default_value "srcpkgs"
31
+ c.arg_name "PKGS_PATH"
32
+ c.flag [:p, "pkgs-path"]
33
+
34
+ c.desc "FQDN of the web server, must include http:// and desired docroot"
35
+ c.default_value "http://somewhere.com/repo"
36
+ c.arg_name "URL_ROOT"
37
+ c.flag [:u, "url-root"]
38
+
39
+ c.action do |global_options, options, args|
40
+ repositories_path = File.absolute_path(global_options[:r])
41
+ packages_path = File.absolute_path(options[:p])
42
+ url_root = options[:u]
43
+ $melai.create(repositories_path, packages_path, url_root)
44
+ end
45
+ end
46
+
47
+ desc "List all packages in a directory"
48
+ command :list do |c|
49
+
50
+ c.desc "Source directory containing all packages"
51
+ c.default_value "srcpkgs"
52
+ c.arg_name "PKGS_PATH"
53
+ c.flag [:p, "pkgs-path"]
54
+
55
+ c.action do |global_options, options, args|
56
+ puts $melai.list(options[:p])
57
+ end
58
+ end
59
+
60
+ desc "Destroy the repository directory completely"
61
+ command :destroy do |c|
62
+ c.action do |global_options, options, args|
63
+ # TODO: Add a --force option
64
+ # TODO: Add a "Are you sure? (y/n)" prompt
65
+ $melai.destroy(global_options[:r])
66
+ end
67
+ end
68
+
69
+
70
+ pre do |global, command, options, args|
71
+ # Pre logic here
72
+ # Return true to proceed; false to abort and not call the
73
+ # chosen command
74
+ # Use skips_pre before a command to skip this block
75
+ # on that command only
76
+ # true
77
+ $melai = Melai::CommandHandler.new
78
+ # TODO: Why do I have to extend the CommandHandler class? Inclusion?
79
+ $melai.extend(Melai::DirHelpers)
80
+ $melai.extend(Melai::PackageHelpers)
81
+ $melai.extend(Melai::StringHelpers)
82
+ end
83
+
84
+ post do |global, command, options, args|
85
+ # Post logic here
86
+ # Use skips_post before a command to skip this
87
+ # block on that command only
88
+ end
89
+
90
+ on_error do |exception|
91
+ # Error logic here
92
+ # return false to skip default error handling
93
+ true
94
+ end
95
+
96
+ exit GLI.run(ARGV)
97
+
98
+
99
+ # module Melai
100
+ # cmd_line = CommandLine.new(ARGV)
101
+ # # review, status = Linter.check(cmd_line)
102
+ # # printer = cmd_line.show_context? ? ContextOutput.new : SummaryOutput.new
103
+ # # printer.output(review)
104
+ # exit status.to_i
105
+ # end
106
+ #
107
+ #
108
+ #
@@ -0,0 +1,26 @@
1
+ Feature: Set up the repository directory
2
+ In order to increase the amount of confidence in repeatable package repositories,
3
+ As an operator
4
+ I want to set up all the directories I need
5
+
6
+ Scenario: Listing an empty source directory returns no files
7
+ Given a directory named "emptysrcpkgs"
8
+ When I successfully run `melai list -p emptysrcpkgs`
9
+ Then the output should contain exactly ""
10
+
11
+ Scenario: List all package files in a source directory
12
+ Given a directory named "srcpkgs"
13
+ And an empty file named "srcpkgs/example-1.0.deb"
14
+ And an empty file named "srcpkgs/example-1.0.rpm"
15
+ When I successfully run `melai list -p srcpkgs`
16
+ Then the output should contain:
17
+ """
18
+ srcpkgs/example-1.0.deb
19
+ srcpkgs/example-1.0.rpm
20
+ """
21
+
22
+ # Final step, cleans up
23
+ Scenario: A repository directory exists and I want to destroy it
24
+ Given a directory named "repo"
25
+ When I successfully run `melai -r repo destroy`
26
+ Then a directory named "repo" should not exist
@@ -0,0 +1,45 @@
1
+ Feature: Create repositories from files
2
+ In order provide packages to end users,
3
+ As an operator
4
+ I want to create multiple repositories from a set of files
5
+
6
+ Background:
7
+ Given I double `apt-ftparchive` with exit status 0
8
+ And I double `createrepo` with exit status 0
9
+
10
+ Scenario: Create a repository for source packages in a semi-flat directory
11
+ Given an empty file named "srcpkgs/foo-1.0.0.i686.rpm"
12
+ And an empty file named "srcpkgs/foo-1.0.0.i386.deb"
13
+ And an empty file named "srcpkgs/ubuntu/foo-1.0.0-bar.i386.deb"
14
+ When I successfully run `melai -r repo create -p srcpkgs`
15
+ Then a directory named "repo/redhat/1.0/i686/RPMS" should exist
16
+ And a directory named "repo/redhat/os/i686/RPMS" should exist
17
+ And a directory named "repo/debian-sysvinit/dists/dist/10gen/binary-i386" should exist
18
+ And a directory named "repo/ubuntu-upstart/dists/dist/10gen/binary-i386" should exist
19
+
20
+ Scenario Outline: Create a repository directory structure for a given file
21
+ Given a directory named "repo" does not exist
22
+ And an empty file named <SrcFile>
23
+ When I successfully run `melai -r repo create -p srcpkgs`
24
+ Then a directory named <RepoDir> should exist
25
+ Examples:
26
+ | SrcFile | RepoDir |
27
+ | "srcpkgs/redhat/foo-2.0.0.i686.rpm" | "repo/redhat/os/i686/RPMS" |
28
+ | "srcpkgs/redhat/foo-2.0.1.i686.rpm" | "repo/redhat/2.0/i686/RPMS" |
29
+ | "srcpkgs/redhat/foo-2.0.2.x86_64.rpm" | "repo/redhat/2.0/x86_64/RPMS" |
30
+ | "srcpkgs/debian/foo-2.0.0.i386.deb" | "repo/debian-sysvinit/dists/dist/10gen/binary-i386/" |
31
+ | "srcpkgs/redhat/os/i686/RPMS/bar-server-2.0.6-version_1.i686.rpm" | "repo/redhat/os/i686/RPMS/" |
32
+ | "srcpkgs/redhat/os/i686/RPMS/bar-unstable-2.1.0-version_1.i686.rpm" | "repo/redhat/2.1/i686/RPMS/" |
33
+ | "srcpkgs/redhat/os/i686/RPMS/bar18-server-1.8.5-version_1.i686.rpm" | "repo/redhat/1.8/i686/RPMS/" |
34
+ | "srcpkgs/redhat/os/i686/RPMS/bar-2.0.6-version_1.i686.rpm" | "repo/redhat/2.0/i686/RPMS/" |
35
+
36
+ Scenario Outline: Create a repository file
37
+ Given a directory named "repo" does not exist
38
+ And an empty file named <SrcFile>
39
+ When I successfully run `melai -r repo create -p srcpkgs`
40
+ Then a file named <RepoTemplate> should exist
41
+ Examples:
42
+ | SrcFile | RepoTemplate |
43
+ | "srcpkgs/redhat/foo-2.0.0.i686.rpm" | "repo/redhat/os/i686/RPMS/10gen.repo" |
44
+ | "srcpkgs/debian/foo-2.0.0.i386.deb" | "repo/debian-sysvinit/dists/dist/10gen/binary-i386/10gen.list" |
45
+ | "srcpkgs/ubuntu/foo-2.0.0.i386.deb" | "repo/ubuntu-upstart/dists/dist/10gen/binary-i386/10gen.list" |
@@ -0,0 +1,5 @@
1
+ # It doesn't look lik there is currently a defined method in aruba for
2
+ # directories that should not exist.
3
+ Given /^a directory named "([^"]*)" does not exist$/ do |directory|
4
+ check_directory_presence([directory], false)
5
+ end
@@ -0,0 +1,15 @@
1
+ # Set up the environment for testing
2
+ require 'aruba/cucumber'
3
+ require 'aruba-doubles/cucumber'
4
+
5
+ Before do
6
+ @aruba_timeout_seconds = 5
7
+ # @dirs = ["tmp/aruba"]
8
+ end
9
+
10
+ After do |s|
11
+ # Tell Cucumber to quit after this scenario is done - if it failed.
12
+ # This is useful to inspect the 'tmp/aruba' directory before any other
13
+ # steps are executed and clear it out.
14
+ Cucumber.wants_to_quit = true if s.failed?
15
+ end
@@ -0,0 +1,80 @@
1
+ # require 'rubygems'
2
+ require_relative 'melai/version'
3
+ require_relative 'melai/dir_helpers'
4
+ require_relative 'melai/package_helpers'
5
+ require_relative 'melai/string_helpers'
6
+
7
+ module Melai
8
+ #
9
+ # This module is the main module. Creates a class of CommandHandler.
10
+ # Not entirely sure if the code is structured correctly. Might have to
11
+ # rethink module vs class locations, based on inheritance.
12
+ #
13
+ class CommandHandler
14
+
15
+ # Creates/updates a symlink-based repository structure
16
+ #
17
+ # @param [String] a directory to build into
18
+ # @param [String] a directory containing the source packages
19
+ def create(repositories_path, packages_path, url_root)
20
+ puts "Creating a repository at #{repositories_path}."
21
+ ensure_directory(repositories_path)
22
+
23
+ # Accumulate a Hash keyed by repository filesystem path
24
+ # whose values are hashes of {:needs_update => bool,
25
+ # :arch => String, :variant => String }
26
+ repositories = Hash.new
27
+
28
+ find_packages(packages_path).each do |package_path|
29
+ package_metadata(package_path, repositories_path).each do |metadata|
30
+ repository_path = metadata[:repository_path]
31
+ needs_update = ensure_symlink(metadata[:symlink_path], metadata[:package_path])
32
+
33
+ if repositories.include?(repository_path)
34
+ repositories[repository_path][:needs_update] ||= needs_update
35
+ else
36
+ repositories[repository_path] = {
37
+ :needs_update => needs_update,
38
+ :variant => metadata[:variant],
39
+ :arch => metadata[:arch],
40
+ :repository_path => metadata[:repository_path],
41
+ :repository_prefix => metadata[:repository_prefix]
42
+ }
43
+ end
44
+ end
45
+ end
46
+
47
+ # Now we have a Hash of all the repo directories that need updating by a value of 'true'
48
+ repositories.each do |repository_path, metadata|
49
+ next unless metadata[:needs_update]
50
+
51
+ repo_template(metadata, repositories_path, url_root)
52
+ update_repo_metadata(metadata, repositories_path, packages_path)
53
+ end
54
+ end
55
+
56
+ # List all package files found
57
+ #
58
+ # @param [String] a directory containing the source packages
59
+ # @return [Array] an array of filenames
60
+ def list(packages_path)
61
+ find_packages(packages_path)
62
+ end
63
+
64
+ # Destroy the repository directory completely.
65
+ # This is a glorified `rm -fr repo/` command, with a simple guard.
66
+ #
67
+ # @param [String] the root of repository directory
68
+ def destroy(repositories_path)
69
+ if repositories_path == "/"
70
+ exit_now!("WTF are you trying to do?", 1)
71
+ elsif Dir.exists?(repositories_path)
72
+ puts "Removing the entire #{repositories_path}"
73
+ FileUtils.remove_dir repositories_path
74
+ puts "It's gone!"
75
+ else
76
+ puts "Nothing there... idiot."
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,43 @@
1
+ module Melai
2
+
3
+ # This module provides some helper methods, specificlally pertaining
4
+ # to directory creation, removal, searching, et al.
5
+ module DirHelpers
6
+
7
+ # Get any files with a known package extension
8
+ #
9
+ # @param [String] a directory to evaluate
10
+ # @return [Array] an array of filenmes
11
+ def find_packages(packages_path)
12
+ packages = File.join(packages_path, "**", "*.{rpm,deb}")
13
+ return Dir.glob(packages).sort()
14
+ end
15
+
16
+ # Ensure a directory exists
17
+ #
18
+ # @param [String] a directory to evaluate
19
+ # @return [Bool] true if the directory was created, false if already exists
20
+ def ensure_directory(directory)
21
+ unless File.directory?(directory)
22
+ FileUtils.mkdir_p(directory)
23
+ return true
24
+ end
25
+ return false
26
+ end
27
+
28
+ # Ensure a symlink exists
29
+ #
30
+ # @param [String] symlink_name: /foo/bar/i-am-a-symlink.rpm
31
+ # @param [String] original_file: /baz/i-am-a-real-file.rpm
32
+ # @return [Bool] true if the link was created, false if already exists
33
+ def ensure_symlink(symlink_name, original_file)
34
+ unless File.symlink?(symlink_name)
35
+ ensure_directory(File.dirname(symlink_name))
36
+ target = File.absolute_path(original_file)
37
+ File.symlink(target, symlink_name)
38
+ return true
39
+ end
40
+ return false
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,168 @@
1
+ require 'erb'
2
+ require 'tempfile'
3
+ require 'mixlib/shellout'
4
+ require_relative 'dir_helpers'
5
+ require_relative 'string_helpers'
6
+
7
+ module Melai
8
+
9
+ # This module provides some package helper methods
10
+ module PackageHelpers
11
+
12
+ # Return an array of package metadata hashes. Each
13
+ # array element describes a particular repository
14
+ # that this package should be a member of.
15
+ def package_metadata(package_path, repositories_path)
16
+ case File.extname(package_path)
17
+ when '.rpm'
18
+ return rpm_package_metadata(package_path, repositories_path)
19
+ when '.deb'
20
+ if File.fnmatch('*ubuntu*', package_path)
21
+ return ubuntu_package_metadata(package_path, repositories_path)
22
+ else File.fnmatch('*debian*', package_path)
23
+ return debian_package_metadata(package_path, repositories_path)
24
+ end
25
+ end
26
+ end
27
+
28
+ def repo_template(metadata, repositories_path, url_root)
29
+ repository_path = metadata[:repository_path]
30
+ case repository_path
31
+ when /redhat/
32
+ source = "redhat.repo.erb"
33
+ target = "10gen.repo"
34
+ url_path = File.join(metadata[:repository_prefix], metadata[:variant], metadata[:arch])
35
+ else
36
+ source = "debian.list.erb"
37
+ target = "10gen.list"
38
+ url_path = File.join(metadata[:repository_prefix])
39
+ end
40
+
41
+ here = File.dirname(__FILE__)
42
+ template = ERB.new(File.read(File.join(here, "..", "..", "templates", source)))
43
+ output = File.new(File.join(repository_path, target), "w")
44
+ output.write(template.result(binding))
45
+ end
46
+
47
+ def update_repo_metadata(metadata, repositories_path, packages_path)
48
+ repository_path = metadata[:repository_path]
49
+
50
+ # e.g /data/packages/cache/debian-sysvinit/dists/
51
+ cache_path = File.join(packages_path, "cache", metadata[:repository_prefix])
52
+ ensure_directory(cache_path)
53
+
54
+ case repository_path
55
+ when /redhat/
56
+ begin
57
+ # e.g. repo/redhat/2.0/x86_64/
58
+ variant_dir = File.join(repositories_path,
59
+ metadata[:repository_prefix],
60
+ metadata[:variant],
61
+ metadata[:arch])
62
+
63
+ shell_out("createrepo --pretty -c #{cache_path} #{variant_dir}")
64
+ rescue Exception=>e
65
+ exit_now!("Could not complete createrepo:\n#{e}", 1)
66
+ end
67
+
68
+ else
69
+ # Do Debian-style repo builds
70
+ here = File.dirname(__FILE__)
71
+ fileloc = File.join(here, "../..", "templates", "apt-ftparchive.conf.erb")
72
+ template = ERB.new(File.read(fileloc))
73
+ output = Tempfile.new("apt-ftparchive.conf")
74
+ output.write(template.result(binding))
75
+ output.close()
76
+
77
+ begin
78
+ # Generates the Packages, Contents files
79
+ shell_out("apt-ftparchive generate #{output.path}")
80
+
81
+ # Generate the Release files on stdout
82
+ variant_dir = File.join(repositories_path,
83
+ metadata[:repository_prefix],
84
+ "dists",
85
+ metadata[:variant])
86
+
87
+ result = shell_out("apt-ftparchive -c #{output.path} release #{variant_dir}")
88
+
89
+ # Dump the Release stdout to a Release file
90
+ release_file = File.join(variant_dir, "Release.new")
91
+ File.open(release_file, "w").write(result.stdout)
92
+ # This ensures that the Release file is not included within itself
93
+ File.rename(release_file, File.join(variant_dir, "Release"))
94
+
95
+ # TODO: GPG Sign the Release file
96
+
97
+ rescue Exception=>e
98
+ exit_now!("Could not complete apt-ftparchive:\n#{e}", 1)
99
+ end
100
+ end
101
+ end
102
+
103
+ private
104
+
105
+ def shell_out(command)
106
+ proc = Mixlib::ShellOut.new(command)
107
+ proc.run_command
108
+ proc.error!
109
+ return proc
110
+ end
111
+
112
+ def rpm_package_metadata(package_path, repositories_path)
113
+ generate_metadata(package_path, repositories_path, ["os"]) do |variant, arch|
114
+ # Return path segments, relative to repositories_path,
115
+ # necessary to construct a path to the correct directory
116
+ # for RPM packages in the given variant and arch.
117
+ ["redhat", variant, arch, "RPMS"]
118
+ end
119
+ end
120
+
121
+ def debian_package_metadata(package_path, repositories_path)
122
+ generate_metadata(package_path, repositories_path, ["dist"]) do |variant, arch|
123
+ # Return path segments, relative to repositories_path,
124
+ # necessary to construct a path to the correct directory
125
+ # for Debian packages in the given variant and arch.
126
+ ["debian-sysvinit", "dists", variant, "10gen", "binary-#{arch}"]
127
+ end
128
+ end
129
+
130
+ def ubuntu_package_metadata(package_path, repositories_path)
131
+ generate_metadata(package_path, repositories_path, ["dist"]) do |variant, arch|
132
+ # Return path segments, relative to repositories_path,
133
+ # necessary to construct a path to the correct directory
134
+ # for Ubuntu packages in the given variant and arch.
135
+ ["ubuntu-upstart", "dists", variant, "10gen", "binary-#{arch}"]
136
+ end
137
+ end
138
+
139
+ def generate_metadata(package_path, repositories_path, variants)
140
+ # Since we want to provide both a 'base' variant and a version-specific
141
+ # one, we build an array of variants based on the the version maj.min
142
+ version, dist = get_version_from_filename(package_path)
143
+ arch = get_arch_from_filename(package_path)
144
+
145
+ variants << dist
146
+
147
+ package_metadata = []
148
+ variants.each do |variant|
149
+ # This is where the link will end up
150
+ path_segments = yield variant, arch
151
+ repository_path = File.join(repositories_path, *path_segments)
152
+ repository_prefix = path_segments[0]
153
+
154
+ # Create a symlink from the source file to the destination directories
155
+ package_metadata << {
156
+ :symlink_path => File.join(repository_path, File.basename(package_path)),
157
+ :package_path => package_path,
158
+ :variant => variant,
159
+ :arch => arch,
160
+ :repository_path => repository_path,
161
+ :repository_prefix => repository_prefix
162
+ }
163
+ end
164
+
165
+ return package_metadata
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,40 @@
1
+ require 'versionomy'
2
+
3
+ module Melai
4
+
5
+ # This module provides some string helper methods
6
+ module StringHelpers
7
+
8
+ # Find the arch name within a filename
9
+ #
10
+ # @params [String] A filename
11
+ # @return [String] the arch name
12
+ def get_arch_from_filename(filename)
13
+ case
14
+ when matched = filename.match(/(i686|x86_64|i386|amd64)/)
15
+ arch = matched[0]
16
+ else
17
+ return 'unknown'
18
+ end
19
+ end
20
+
21
+ # Find the version within a filename
22
+ #
23
+ # @params [String] A filename
24
+ # @return [Object] the version object, Versionomy-style
25
+ # @return [String] The Major + Minor versions
26
+ def get_version_from_filename(filename)
27
+ version = Versionomy.parse(/(\d+)\.(\d+)\.(\d+)/.match(filename).to_s)
28
+ # TODO: If we start packaging prerelease/rc packages, try:
29
+ # (\d+)\.(\d+)\.(\d+)(?:-[^\.]+)?
30
+ dist = [version.major, version.minor].join(".")
31
+ return version, dist
32
+ end
33
+
34
+ # TODO: needs work, currently unused
35
+ def get_unstable_from_filename(filename)
36
+ unstable = /unstable/.match(filename)
37
+ return unstable
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,3 @@
1
+ module Melai
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,38 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/melai/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+
6
+ gem.name = 'melai'
7
+ gem.summary = %q{melai builds multi-platform repositories for a given list of package files}
8
+ gem.description = %q{Build your repositories with melai}
9
+ gem.version = Melai::VERSION
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.require_paths = ['lib']
15
+
16
+
17
+ # GLI (Github-Like Interface) http://davetron5000.github.com/gli/
18
+ gem.add_dependency 'gli'
19
+
20
+ # versiononmy http://dazuma.github.com/versionomy/
21
+ gem.add_dependency 'versionomy'
22
+
23
+ # MixLib::ShellOut to perform system-level commands
24
+ gem.add_dependency 'mixlib-shellout'
25
+
26
+ gem.add_development_dependency 'rake'
27
+ gem.add_development_dependency 'aruba'
28
+ gem.add_development_dependency 'aruba-doubles'
29
+ gem.add_development_dependency 'pry'
30
+
31
+ # tailor, for style. https://github.com/turboladen/tailor
32
+ gem.add_development_dependency 'tailor'
33
+
34
+ gem.authors = ["Mike Fiedler"]
35
+ gem.email = ["miketheman@gmail.com"]
36
+ gem.homepage = "http://www.miketheman.net"
37
+
38
+ end
@@ -0,0 +1,4 @@
1
+ = melai
2
+
3
+ consider changing to ronn
4
+ http://rtomayko.github.com/ronn/ronn.1.html
@@ -0,0 +1,26 @@
1
+ Dir {
2
+ ArchiveDir "<%= repositories_path %>/<%= metadata[:repository_prefix] %>";
3
+ CacheDir "<%= cache_path %>";
4
+ };
5
+
6
+ Tree "dists/<%= metadata[:variant] %>" {
7
+ Sections "10gen";
8
+ Architectures "i386 amd64";
9
+ };
10
+
11
+ APT::FTPArchive {
12
+ MD5 true;
13
+ SHA1 true;
14
+ SHA256 true;
15
+ SHA512 true;
16
+ Release {
17
+ Origin "10gen";
18
+ Label "10gen";
19
+ Suite "10gen";
20
+ Version "<%= metadata[:variant] %>";
21
+ Codename "<%= metadata[:variant] %>";
22
+ Architectures "i386 amd64";
23
+ Components "10gen";
24
+ Description "10gen packages";
25
+ };
26
+ };
@@ -0,0 +1,2 @@
1
+ # This file provides the 10gen software repository
2
+ deb <%= url_root %>/<%= url_path %> <%= metadata[:variant] %> 10gen
@@ -0,0 +1,6 @@
1
+ # This file provides the 10gen software repository
2
+ [10gen]
3
+ name=10gen Repository
4
+ baseurl=<%= url_root %>/<%= url_path %>
5
+ gpgcheck=0
6
+ enabled=1
metadata ADDED
@@ -0,0 +1,198 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: melai
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Mike Fiedler
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-16 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: gli
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: versionomy
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: mixlib-shellout
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rake
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: aruba
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: aruba-doubles
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: pry
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: tailor
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ description: Build your repositories with melai
143
+ email:
144
+ - miketheman@gmail.com
145
+ executables:
146
+ - melai
147
+ extensions: []
148
+ extra_rdoc_files: []
149
+ files:
150
+ - .gitignore
151
+ - Gemfile
152
+ - LICENSE
153
+ - README.md
154
+ - Rakefile
155
+ - bin/melai
156
+ - features/directories.feature
157
+ - features/repository.feature
158
+ - features/step_definitions/directories_steps.rb
159
+ - features/support/env.rb
160
+ - lib/melai.rb
161
+ - lib/melai/dir_helpers.rb
162
+ - lib/melai/package_helpers.rb
163
+ - lib/melai/string_helpers.rb
164
+ - lib/melai/version.rb
165
+ - melai.gemspec
166
+ - melai.rdoc
167
+ - templates/apt-ftparchive.conf.erb
168
+ - templates/debian.list.erb
169
+ - templates/redhat.repo.erb
170
+ homepage: http://www.miketheman.net
171
+ licenses: []
172
+ post_install_message:
173
+ rdoc_options: []
174
+ require_paths:
175
+ - lib
176
+ required_ruby_version: !ruby/object:Gem::Requirement
177
+ none: false
178
+ requirements:
179
+ - - ! '>='
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ required_rubygems_version: !ruby/object:Gem::Requirement
183
+ none: false
184
+ requirements:
185
+ - - ! '>='
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ requirements: []
189
+ rubyforge_project:
190
+ rubygems_version: 1.8.24
191
+ signing_key:
192
+ specification_version: 3
193
+ summary: melai builds multi-platform repositories for a given list of package files
194
+ test_files:
195
+ - features/directories.feature
196
+ - features/repository.feature
197
+ - features/step_definitions/directories_steps.rb
198
+ - features/support/env.rb