github_snap_builder 0.1.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
+ 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