github_snap_builder 0.1.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
+ SHA256:
3
+ metadata.gz: 3ab5c0008844156c54ae4bdca678c75f5c5834b985e5d22dfbfeb3798d6d592d
4
+ data.tar.gz: 1920c78ec7f5d9ae27d276dc4e9ca6a784a81f079a72dbc7d70c75aafab65cbd
5
+ SHA512:
6
+ metadata.gz: da93b02af100acc6fd4c5cae802e2edd0112cf03ac66207895d560a63668e4da96eb77e0e423adfc92ecfa5b01e1540fa98f59d0616d4bda18c85e83bdab34f9
7
+ data.tar.gz: 13cdd5e0a397785a243a75cf4dc46e0141552ce57c3575707585fa37d1714f2c4feabdf96034fd910cf62f575053a744e912f621e257c9cb95f931a094f22db4
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ *.gem
10
+ snap/.snapcraft/
11
+ parts
12
+ stage/
13
+ prime/
14
+ *.snap
15
+ .vscode/
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ snap-builder
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.6.1
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.6.1
7
+ before_install: gem install bundler -v 2.0.1
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in github_snap_builder.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,64 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ github_snap_builder (0.1.0)
5
+ docker-api (~> 1.34)
6
+ jwt (~> 2.1)
7
+ octokit (~> 4.0)
8
+ rugged (~> 0.0)
9
+ sinatra (~> 2.0)
10
+
11
+ GEM
12
+ remote: https://rubygems.org/
13
+ specs:
14
+ addressable (2.5.2)
15
+ public_suffix (>= 2.0.2, < 4.0)
16
+ docker-api (1.34.2)
17
+ excon (>= 0.47.0)
18
+ multi_json
19
+ excon (0.62.0)
20
+ faraday (0.15.4)
21
+ multipart-post (>= 1.2, < 3)
22
+ jwt (2.1.0)
23
+ metaclass (0.0.4)
24
+ mocha (1.8.0)
25
+ metaclass (~> 0.0.1)
26
+ multi_json (1.13.1)
27
+ multipart-post (2.0.0)
28
+ mustermann (1.0.3)
29
+ octokit (4.13.0)
30
+ sawyer (~> 0.8.0, >= 0.5.3)
31
+ power_assert (1.1.3)
32
+ public_suffix (3.0.3)
33
+ rack (2.0.6)
34
+ rack-protection (2.0.5)
35
+ rack
36
+ rack-test (1.1.0)
37
+ rack (>= 1.0, < 3)
38
+ rake (12.3.2)
39
+ rugged (0.27.7)
40
+ sawyer (0.8.1)
41
+ addressable (>= 2.3.5, < 2.6)
42
+ faraday (~> 0.8, < 1.0)
43
+ sinatra (2.0.5)
44
+ mustermann (~> 1.0)
45
+ rack (~> 2.0)
46
+ rack-protection (= 2.0.5)
47
+ tilt (~> 2.0)
48
+ test-unit (3.3.0)
49
+ power_assert
50
+ tilt (2.0.9)
51
+
52
+ PLATFORMS
53
+ ruby
54
+
55
+ DEPENDENCIES
56
+ bundler (~> 2.0)
57
+ github_snap_builder!
58
+ mocha (~> 1.0)
59
+ rack-test (~> 1.0)
60
+ rake (~> 12.3)
61
+ test-unit (~> 3.2)
62
+
63
+ BUNDLED WITH
64
+ 2.0.1
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # GithubSnapBuilder
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/github_snap_builder`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'github_snap_builder'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install github_snap_builder
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ 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).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/github_snap_builder.
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new(:test) do |t|
4
+ if ENV['TEST'].nil? or ENV['TEST'].empty?
5
+ t.pattern = "tests/**/*_test.rb"
6
+ else
7
+ t.pattern = ENV['TEST']
8
+ end
9
+ end
10
+
11
+ task default: :test
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "github_snap_builder"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'github_snap_builder/server'
4
+
5
+ GithubSnapBuilder::Application.run!
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'github_snap_builder/config'
4
+
5
+ if ARGV.length != 1
6
+ puts "Usage:"
7
+ puts " github_snap_builder_config_validator <config file>"
8
+ exit 0
9
+ end
10
+
11
+ config = GithubSnapBuilder::Config.new(File.read(ARGV[0]))
12
+
13
+ begin
14
+ config.validate
15
+ rescue GithubSnapBuilder::ConfigurationError => e
16
+ abort e.message
17
+ end
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/configure ADDED
@@ -0,0 +1,20 @@
1
+ #!/bin/sh -e
2
+
3
+ if [ "$(id -u)" -ne 0 ]; then
4
+ echo "This utility needs to run as root"
5
+ exit 1
6
+ fi
7
+
8
+ tmp_config="$(mktemp)"
9
+ cp "$SNAP_BUILDER_CONFIG" "$tmp_config"
10
+
11
+ editor "$tmp_config"
12
+ while ! "$SNAP/bin/ruby" "$SNAP/bin/github_snap_builder_config_validator" "$tmp_config"; do
13
+ echo "Press [ENTER] to try again"
14
+ read _
15
+ editor "$tmp_config"
16
+ done
17
+
18
+ mv "$tmp_config" "$SNAP_BUILDER_CONFIG"
19
+
20
+ snapctl restart github-snap-builder.github-snap-builder
@@ -0,0 +1,45 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "github_snap_builder/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "github_snap_builder"
8
+ spec.version = GithubSnapBuilder::VERSION
9
+ spec.authors = ["Kyle Fazzari"]
10
+ spec.email = ["kyrofa@ubuntu.com"]
11
+
12
+ spec.summary = "Github app that builds and releases snaps for each pull request."
13
+ spec.homepage = "https://github.com/kyrofa/github-snap-builder"
14
+ spec.license = "GPL-3.0"
15
+
16
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
18
+ if spec.respond_to?(:metadata)
19
+ spec.metadata["homepage_uri"] = spec.homepage
20
+ spec.metadata["source_code_uri"] = spec.homepage
21
+ else
22
+ raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
23
+ end
24
+
25
+ # Specify which files should be added to the gem when it is released.
26
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
27
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
28
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|snap)/}) }
29
+ end
30
+ spec.executables << "github_snap_builder"
31
+ spec.executables << "github_snap_builder_config_validator"
32
+ spec.require_paths = ["lib"]
33
+
34
+ spec.add_dependency 'sinatra', '~> 2.0'
35
+ spec.add_dependency 'jwt', '~> 2.1'
36
+ spec.add_dependency 'octokit', '~> 4.0'
37
+ spec.add_dependency 'rugged', '~> 0.0'
38
+ spec.add_dependency 'docker-api', '~> 1.34'
39
+
40
+ spec.add_development_dependency "bundler", "~> 2.0"
41
+ spec.add_development_dependency 'test-unit', '~> 3.2'
42
+ spec.add_development_dependency 'rack-test', '~> 1.0'
43
+ spec.add_development_dependency 'mocha', '~> 1.0'
44
+ spec.add_development_dependency 'rake', '~> 12.3'
45
+ end
@@ -0,0 +1,87 @@
1
+ require 'date'
2
+ require 'shellwords'
3
+ require 'docker'
4
+ require 'github_snap_builder'
5
+
6
+ module GithubSnapBuilder
7
+ class DockerBuilder
8
+ def initialize(logger, base)
9
+ @logger = logger
10
+ @base = base
11
+
12
+ begin
13
+ Docker.validate_version!
14
+ rescue Excon::Error::Socket
15
+ raise DockerVersionError
16
+ end
17
+ end
18
+
19
+ def build(project_directory)
20
+ # Snapcraft will detect if it's in a docker container and default to
21
+ # destructive mode.
22
+ run(['sh', '-c', "apt update -qq && snapcraft"], {
23
+ 'Env' => ['SNAPCRAFT_MANAGED_HOST=yes'],
24
+ 'WorkingDir' => '/snapcraft',
25
+ 'HostConfig' => {
26
+ 'Binds' => ["#{project_directory}:/snapcraft"],
27
+ 'AutoRemove' => true,
28
+ }
29
+ })
30
+ end
31
+
32
+ def release(snap_path, token, channel)
33
+ run(['sh', '-c', "snapcraft login --with /token && snapcraft push #{File.basename(snap_path).shellescape} --release=#{channel.shellescape}"], {
34
+ 'Env' => ['SNAPCRAFT_MANAGED_HOST=yes'],
35
+ 'WorkingDir' => '/snapcraft',
36
+ 'HostConfig' => {
37
+ 'Binds' => ["#{File.dirname(snap_path)}:/snapcraft"],
38
+ 'AutoRemove' => true,
39
+ }
40
+ }) do |container|
41
+ container.store_file("/token", token)
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def run(command, options)
48
+ # Grab the image for this base
49
+ @logger.info 'Fetching image...'
50
+ image = get_image(@base)
51
+ options = {'Image' => image.id, 'Cmd' => command}.merge(options)
52
+
53
+ begin
54
+ # Create a new container
55
+ @logger.info 'Provisioning...'
56
+ container = Docker::Container.create options
57
+
58
+ # Give called the change to mess with container before running
59
+ # anything
60
+ yield container if block_given?
61
+
62
+ # Fire up the container and stream its logs
63
+ @logger.info 'Firing up...'
64
+ container.tap(&:start).attach({}, {read_timeout: 600}) do |stream, chunk|
65
+ if stream == :stdout
66
+ @logger.info chunk
67
+ else
68
+ @logger.error chunk
69
+ end
70
+ end
71
+
72
+ if container.wait()['StatusCode'] != 0
73
+ @logger.error "Command exited non-zero: aborting"
74
+ raise DockerRunError, command
75
+ end
76
+ ensure
77
+ # This shouldn't be necessary given the AutoRemove option, but it's
78
+ # easy to ensure.
79
+ container.delete(force: true) unless container.nil?
80
+ end
81
+ end
82
+
83
+ def get_image(base)
84
+ Docker::Image.create('fromImage' => "kyrofa/github-snap-builder:#{base}")
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,143 @@
1
+ require 'yaml'
2
+ require 'github_snap_builder'
3
+ require 'github_snap_builder/snap_builder'
4
+
5
+ module GithubSnapBuilder
6
+ class Config
7
+ def initialize(config_file_contents)
8
+ @config = YAML.safe_load(config_file_contents) || {}
9
+ end
10
+
11
+ def github_webhook_secret
12
+ @config['github_webhook_secret']
13
+ end
14
+
15
+ def github_app_id
16
+ @config['github_app_id']
17
+ end
18
+
19
+ def github_app_private_key
20
+ @config['github_app_private_key']
21
+ end
22
+
23
+ def build_type
24
+ @config['build_type']
25
+ end
26
+
27
+ def port
28
+ @config.fetch('port', 3000)
29
+ end
30
+
31
+ def bind
32
+ @config.fetch('bind', '0.0.0.0')
33
+ end
34
+
35
+ def repos
36
+ @repos ||= initialize_repos
37
+ end
38
+
39
+ def repos_include?(repo_name)
40
+ @config.fetch('repos', {}).include? repo_name
41
+ end
42
+
43
+ def repo(repo_name)
44
+ repos.each do | repo |
45
+ if repo.name == repo_name
46
+ return repo
47
+ end
48
+ end
49
+ end
50
+
51
+ def validate
52
+ if @config.nil? or @config.empty?
53
+ raise ConfigurationError, "Config seems completely empty"
54
+ end
55
+
56
+ if github_webhook_secret.nil? || !github_webhook_secret.is_a?(String) || github_webhook_secret.empty?
57
+ raise ConfigurationFieldError, "github_webhook_secret"
58
+ end
59
+
60
+ if github_app_id.nil? || !github_app_id.is_a?(Integer) || github_app_id == 0
61
+ raise ConfigurationFieldError, "github_app_id"
62
+ end
63
+
64
+ if github_app_private_key.nil? || !github_app_private_key.is_a?(String) || github_app_private_key.empty?
65
+ raise ConfigurationFieldError, "github_app_private_key"
66
+ end
67
+
68
+ if build_type.nil? || !build_type.is_a?(String) || build_type.empty? || !SnapBuilder.supported_build_types.include?(build_type)
69
+ raise ConfigurationFieldError, "build_type"
70
+ end
71
+
72
+ if port.nil? || !port.is_a?(Integer) || port == 0
73
+ raise ConfigurationFieldError, "port"
74
+ end
75
+
76
+ if bind.nil? || !bind.is_a?(String) || bind.empty?
77
+ raise ConfigurationFieldError, "bind"
78
+ end
79
+ end
80
+
81
+ def valid?
82
+ begin
83
+ validate
84
+ rescue ConfigurationError
85
+ return false
86
+ end
87
+
88
+ begin
89
+ repos.each do | repo |
90
+ repo.validate
91
+ end
92
+ rescue ConfigurationError
93
+ return false
94
+ end
95
+
96
+ true
97
+ end
98
+
99
+ private
100
+
101
+ def initialize_repos
102
+ repos = []
103
+ @config.fetch('repos', {}).each do | name, definition |
104
+ repos << RepoConfig.new(name, definition)
105
+ end
106
+ repos
107
+ end
108
+ end
109
+
110
+ class RepoConfig
111
+ attr_reader :name, :channel, :token
112
+
113
+ def initialize(name, config)
114
+ @name = name
115
+ @channel = config.fetch('channel', 'edge')
116
+ @token = config['token']
117
+ end
118
+
119
+ def validate
120
+ if name.nil? || !name.is_a?(String) || name.empty?
121
+ raise ConfigurationFieldError, "repo name"
122
+ end
123
+
124
+ if channel.nil? || !channel.is_a?(String) || channel.empty?
125
+ raise ConfigurationFieldError, "#{name}'s channel"
126
+ end
127
+
128
+ if token.nil? || !token.is_a?(String) || token.empty?
129
+ raise ConfigurationFieldError, "#{name}'s token"
130
+ end
131
+ end
132
+
133
+ def valid?
134
+ begin
135
+ validate
136
+ rescue ConfigurationError
137
+ return false
138
+ end
139
+
140
+ true
141
+ end
142
+ end
143
+ end