heroku_hatchet 7.1.3 → 7.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 457db77bc6c69de2ef9179c0b3027ab3c8036b2e639dc924bbb3d4c6903b32b5
4
- data.tar.gz: c42cf1ceb1a679f1f0dfdff99c60e6ba1b7cc442d5accd0182ac8b44b2d8add1
3
+ metadata.gz: 2dd4a304828fca43576fed5a71b7be305142c3220c7dd020bff44b17a4a070a0
4
+ data.tar.gz: c0dee0a8ffb794f10a3dbb96beeafa26dd5f95b489eb9638a40bf408dce44c11
5
5
  SHA512:
6
- metadata.gz: 4f7d83b39c17fb2700d4b3ddca889df1fd55a884017d09d72b178870d556b1948753d491fbeb02c9a35aee92c3de8d49d4c4aca6c2fd164ddfabe4f3bfe54168
7
- data.tar.gz: 78e2a70ce5b00b69e1c98da9a36f691978e043db9e58976947011a3178e8bf6c63520dca01a471c58fdc24e5da7726408704bb0d46f2f0e8ed31ce61fe4ec5c0
6
+ metadata.gz: f6fa0cef6167e9b4985b08f01e42f33410632d4dfb083551d2641e7009745a079044f606cc4456ed8b8f0dfb0ec087b9b3197d1dabb9a0c41cd7f96ee978e22c
7
+ data.tar.gz: 50689c87372d4df2fc7d354ccc0f6f870285283869a190f3272f76d0d238e2d3cd13ca485ca6436916a6976c847fbd2be057fbe4e99ed1aa8ac97abdfef9eeb9
@@ -1,7 +1,8 @@
1
1
  name: Check Changelog
2
2
 
3
- on: [pull_request]
4
-
3
+ on:
4
+ pull_request:
5
+ types: [opened, reopened, edited, synchronize]
5
6
  jobs:
6
7
  build:
7
8
  runs-on: ubuntu-latest
@@ -1,5 +1,10 @@
1
1
  ## HEAD
2
2
 
3
+ ## 7.1.4
4
+
5
+ - App#setup! no longer modifies files on disk. (https://github.com/heroku/hatchet/pull/125)
6
+ - Add `$ hatchet init` command for bootstrapping new projects (https://github.com/heroku/hatchet/pull/123)
7
+
3
8
  ## 7.1.3
4
9
 
5
10
  - Important!! Fix branch name detection on CircleCI (https://github.com/heroku/hatchet/pull/124)
data/README.md CHANGED
@@ -57,6 +57,8 @@ In addition to speed, Hatchet provides isolation. Suppose you're executing `bin/
57
57
 
58
58
  ## Quicklinks
59
59
 
60
+ - Getting started
61
+ - [Add hatchet tests to a existing buildpack](#hatchet-init)
60
62
  - Concepts
61
63
  - [Tell Hatchet how to find your buildpack](#specify-buildpack)
62
64
  - [Give Hatchet some example apps to deploy](#example-apps)
@@ -78,6 +80,45 @@ In addition to speed, Hatchet provides isolation. Suppose you're executing `bin/
78
80
  - [Introduction to the Rspec testing framework for non-rubyists](#basic-rspec)
79
81
  - [Introduction to Ruby for non-rubyists](#basic-ruby)
80
82
 
83
+ ## Getting Started
84
+
85
+ ### Hatchet Init
86
+
87
+ If you're working in a project that does not already have hatchet tests you can run this command to get started quickly:
88
+
89
+ Make sure you're in directory that contains your buildpack and run:
90
+
91
+ ```
92
+ $ gem install heroku_hatchet
93
+ $ hatchet init
94
+ ```
95
+
96
+ This will bootstrap your project with the necessarry files to test your buildpack. Including but not limited to:
97
+
98
+ - Gemfile
99
+ - hatchet.json
100
+ - spec/spec_helper.rb
101
+ - spec/hatchet/buildpack_spec.rb
102
+ - .circleci/config.yml
103
+ - .github/dependabot.yml
104
+ - .gitignore
105
+
106
+ Once this executes successfully then you can run your tests with:
107
+
108
+ ```
109
+ $ bundle exec rspec
110
+ ```
111
+
112
+ > Note: You'll need to update the `buildpack_spec.rb` file to remove the exception
113
+
114
+ You can also focus a specific file or test by providing a path and line number:
115
+
116
+ ```
117
+ $ bundle exec rspec spec/hatchet/buildpack_spec:5
118
+ ```
119
+
120
+ Keep reading to find out more about how hatchet works.
121
+
81
122
  ## Concepts
82
123
 
83
124
  ### Specify buildpack
@@ -89,7 +130,7 @@ ENV["HATCHET_BUILDPACK_BASE"] = "https://github.com/path-to-your/buildpack"
89
130
  require 'hatchet'`
90
131
  ```
91
132
 
92
- If you do not specify `HATCHET_BUILDPACK_URL` the default Ruby buildpack will be used. If you do not specify a `HATCHET_BUILDPACK_BRANCH` the current branch you are on will be used. This is how the Ruby buildpack runs tests on branches on CI (by leaving `HATCHET_BUILDPACK_BRANCH` blank).
133
+ If you do not specify `HATCHET_BUILDPACK_BASE` the default Ruby buildpack will be used. If you do not specify a `HATCHET_BUILDPACK_BRANCH` the current branch you are on will be used. This is how the Ruby buildpack runs tests on branches on CI (by leaving `HATCHET_BUILDPACK_BRANCH` blank).
93
134
 
94
135
  The workflow generally looks like this:
95
136
 
@@ -156,7 +197,7 @@ You can reference one of these applications in your test by using it's git name:
156
197
  Hatchet::Runner.new('no_lockfile')
157
198
  ```
158
199
 
159
- If you have conflicting names, use full paths like `Hatchet::RUnner.new("sharpstone/no_lockfile")`.
200
+ If you have conflicting names, use full paths like `Hatchet::Runner.new("sharpstone/no_lockfile")`.
160
201
 
161
202
  When you run `hatchet install` it will lock all the Repos to a specific commit. This is done so that if a repo changes upstream that introduces an error the test suite won't automatically pick it up. For example in https://github.com/sharpstone/lock_fail/commit/e61ba47043fbae131abb74fd74added7e6e504df an error is added, but this will only cause a failure if your project intentionally locks to commit `e61ba47043fbae131abb74fd74added7e6e504df` or later.
162
203
 
@@ -15,8 +15,14 @@ require 'thor'
15
15
  require 'threaded'
16
16
  require 'date'
17
17
  require 'yaml'
18
+ require 'pathname'
18
19
 
19
20
  class HatchetCLI < Thor
21
+ desc "init", "bootstraps a project with minimal files required to add hatchet tests"
22
+ define_method("init") do
23
+ Hatchet::InitProject.new.call
24
+ end
25
+
20
26
  desc "ci:install_heroku", "installs the `heroku` cli"
21
27
  define_method("ci:install_heroku") do
22
28
  if `which heroku` && $?.success?
@@ -18,6 +18,7 @@ require 'hatchet/anvil_app'
18
18
  require 'hatchet/git_app'
19
19
  require 'hatchet/config'
20
20
  require 'hatchet/api_rate_limit'
21
+ require 'hatchet/init_project'
21
22
 
22
23
  module Hatchet
23
24
  RETRIES = Integer(ENV['HATCHET_RETRIES'] || 1)
@@ -297,19 +297,27 @@ module Hatchet
297
297
  def setup!
298
298
  return self if @app_is_setup
299
299
  puts "Hatchet setup: #{name.inspect} for #{repo_name.inspect}"
300
- create_git_repo! unless is_git_repo?
301
300
  create_app
302
301
  set_labs!
303
302
  buildpack_list = @buildpacks.map { |pack| { buildpack: pack } }
304
303
  api_rate_limit.call.buildpack_installation.update(name, updates: buildpack_list)
305
304
  set_config @app_config
306
305
 
307
- call_before_deploy
308
306
  @app_is_setup = true
309
307
  self
310
308
  end
311
309
  alias :setup :setup!
312
310
 
311
+ private def in_dir_setup!
312
+ setup!
313
+ raise "Error you're in #{Dir.pwd} and might accidentally modify your disk contents" unless @already_in_dir
314
+ @in_dir_setup ||= begin
315
+ create_git_repo! unless is_git_repo?
316
+ call_before_deploy
317
+ true
318
+ end
319
+ end
320
+
313
321
  def before_deploy(&block)
314
322
  raise "block required" unless block
315
323
  @before_deploy = block
@@ -379,7 +387,7 @@ module Hatchet
379
387
 
380
388
  def deploy(&block)
381
389
  in_directory do
382
- self.setup!
390
+ in_dir_setup!
383
391
  self.push_with_retry!
384
392
  block.call(self, api_rate_limit.call, output) if block_given?
385
393
  end
@@ -434,7 +442,7 @@ module Hatchet
434
442
  # that would, with bad timing, mean our test run info poll in wait! would 403, and/or the delete_pipeline at the end
435
443
  # that's why we create an app explictly (or maybe it already exists), and then associate it with with the pipeline
436
444
  # the app will be auto cleaned up later
437
- self.setup!
445
+ in_dir_setup!
438
446
  max_retries_count.times.retry do
439
447
  couple_pipeline(@name, @pipeline_id)
440
448
  end
@@ -0,0 +1,86 @@
1
+ require 'thor'
2
+ require 'yaml'
3
+
4
+ module Hatchet
5
+ # Bootstraps a project with files for running hatchet tests
6
+ #
7
+ # Hatchet::InitProject.new.call
8
+ #
9
+ # puts File.exist?("spec/spec_helper.rb") # => true
10
+ # puts File.exist?("") # => true
11
+ class InitProject
12
+ def initialize(dir: ".", io: STDOUT)
13
+
14
+ @target_dir = Pathname.new(dir)
15
+ raise "Must run in a directory with a buildpack, #{@target_dir} has no bin/ directory" unless @target_dir.join("bin").directory?
16
+
17
+ @template_dir = Pathname.new(__dir__).join("templates")
18
+ @thor_shell = ::Thor::Shell::Basic.new
19
+ @io = io
20
+ @git_ignore = @target_dir.join(".gitignore")
21
+
22
+ FileUtils.touch(@git_ignore)
23
+ FileUtils.touch(@target_dir.join("hatchet.lock"))
24
+ end
25
+
26
+ def call
27
+ write_target(target: ".circleci/config.yml", template: "circleci_template.erb")
28
+ write_target(target: "Gemfile", template: "Gemfile.erb")
29
+ write_target(target: "hatchet.json", template: "hatchet_json.erb")
30
+ write_target(target: "spec/spec_helper.rb", template: "spec_helper.erb")
31
+ write_target(target: "spec/hatchet/buildpack_spec.rb", template: "buildpack_spec.erb")
32
+ write_target(target: ".github/dependabot.yml", template: "dependabot.erb")
33
+ write_target(target: ".github/workflows/check_changelog.yml", template: "check_changelog.erb")
34
+
35
+ add_gitignore(".rspec_status")
36
+ add_gitignore("repos/*")
37
+
38
+ stream("cd #{@target_dir} && bundle install")
39
+ stream("cd #{@target_dir} && hatchet install")
40
+
41
+ @io.puts
42
+ @io.puts "Done, run `bundle exec rspec` to execute your tests"
43
+ @io.puts
44
+ end
45
+
46
+ private def add_gitignore(statement)
47
+ @git_ignore.open("a") {|f| f.puts statement } unless @git_ignore.read.include?(statement)
48
+ end
49
+
50
+ private def stream(command)
51
+ output = ""
52
+ IO.popen(command) do |io|
53
+ until io.eof?
54
+ buffer = io.gets
55
+ output << buffer
56
+ @io.puts(buffer)
57
+ end
58
+ end
59
+ raise "Error running #{command}. Output:\n#{output}" unless $?.success?
60
+ output
61
+ end
62
+
63
+ private def write_target(template: nil, target:, contents: nil)
64
+ if template
65
+ template = @template_dir.join(template)
66
+ contents = ERB.new(template.read).result(binding)
67
+ end
68
+
69
+ target = @target_dir.join(target)
70
+ target.dirname.mkpath # Create directory if it doesn't exist already
71
+
72
+ if target.exist?
73
+ return if contents === target.read # identical
74
+ target.write(contents) if @thor_shell.file_collision(target) { contents }
75
+ else
76
+ target.write(contents)
77
+ end
78
+ end
79
+
80
+ private def cmd(command)
81
+ result = `#{command}`.chomp
82
+ raise "Command #{command} failed:\n#{result}" unless $?.success?
83
+ result
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "parallel_split_test"
4
+ gem "heroku_hatchet"
5
+ gem "rspec-retry"
@@ -0,0 +1,23 @@
1
+ require_relative "../spec_helper.rb"
2
+
3
+ RSpec.describe "This buildpack" do
4
+ it "has its own tests" do
5
+ raise "delete this and replace it with your own logic"
6
+
7
+ # Specify where you want your buildpack to go using :default
8
+ buildpacks = [:default, "heroku/ruby"]
9
+
10
+ # To deploy a different app modify the hatchet.json or
11
+ # commit an app to your source control and use a path
12
+ # instead of "default_ruby" here
13
+ Hatchet::Runner.new("default_ruby", buildpacks: buildpacks).tap do |app|
14
+ app.before_deploy do
15
+ # Modfiy the app here if you need
16
+ end
17
+ app.deploy do
18
+ # Assert the behavior you desire here
19
+ expect(app.output).to match("deployed to Heroku")
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,13 @@
1
+ name: Check Changelog
2
+
3
+ on:
4
+ pull_request:
5
+ types: [opened, reopened, edited, synchronize]
6
+ jobs:
7
+ build:
8
+ runs-on: ubuntu-latest
9
+ steps:
10
+ - uses: actions/checkout@v1
11
+ - name: Check that CHANGELOG is touched
12
+ run: |
13
+ cat $GITHUB_EVENT_PATH | jq .pull_request.title | grep -i '\[\(\(changelog skip\)\|\(ci skip\)\)\]' || git diff remotes/origin/${{ github.base_ref }} --name-only | grep CHANGELOG.md
@@ -0,0 +1,45 @@
1
+ version: 2
2
+ references:
3
+ unit: &unit
4
+ run:
5
+ name: Run test suite
6
+ command: PARALLEL_SPLIT_TEST_PROCESSES=25 IS_RUNNING_ON_CI=1 bundle exec parallel_split_test spec/
7
+ restore: &restore
8
+ restore_cache:
9
+ keys:
10
+ - v1_bundler_deps-{{ .Environment.CIRCLE_JOB }}
11
+ save: &save
12
+ save_cache:
13
+ paths:
14
+ - ./vendor/bundle
15
+ key: v1_bundler_deps-{{ .Environment.CIRCLE_JOB }} # CIRCLE_JOB e.g. "ruby-2.5"
16
+ hatchet_setup: &hatchet_setup
17
+ run:
18
+ name: Hatchet setup
19
+ command: |
20
+ bundle exec hatchet ci:setup
21
+ bundle: &bundle
22
+ run:
23
+ name: install dependencies
24
+ command: |
25
+ bundle install --jobs=4 --retry=3 --path vendor/bundle
26
+ bundle update
27
+ bundle clean
28
+ jobs:
29
+ "ruby-2.7":
30
+ docker:
31
+ - image: circleci/ruby:2.7
32
+ steps:
33
+ - checkout
34
+ - <<: *restore
35
+ - <<: *bundle
36
+ - <<: *hatchet_setup
37
+ - <<: *unit
38
+ - <<: *save
39
+
40
+ workflows:
41
+ version: 2
42
+ build:
43
+ jobs:
44
+ - "ruby-2.7"
45
+
@@ -0,0 +1,9 @@
1
+ version: 1
2
+ updates:
3
+ - package-ecosystem: "bundler"
4
+ directory: "/"
5
+ open-pull-requests-limit: 1 # Limit concurrent CI runs from executing
6
+ schedule:
7
+ interval: "weekly"
8
+ labels:
9
+ - "dependencies"
@@ -0,0 +1,11 @@
1
+ {
2
+ "ruby_apps": [
3
+ "sharpstone/default_ruby"
4
+ ],
5
+ "node_apps": [
6
+ "heroku/node-js-getting-started"
7
+ ],
8
+ "python_apps": [
9
+ "heroku/python-getting-started"
10
+ ]
11
+ }
@@ -0,0 +1,30 @@
1
+ require "bundler/setup"
2
+
3
+ require 'rspec/retry'
4
+
5
+ ENV["HATCHET_BUILDPACK_BASE"] = "<%= cmd("git config --get remote.origin.url") %>"
6
+
7
+ require 'hatchet'
8
+ require 'pathname'
9
+
10
+ RSpec.configure do |config|
11
+ # Enable flags like --only-failures and --next-failure
12
+ config.example_status_persistence_file_path = ".rspec_status"
13
+ config.verbose_retry = true # show retry status in spec process
14
+ config.default_retry_count = 2 if ENV['IS_RUNNING_ON_CI'] # retry all tests that fail again
15
+
16
+ config.expect_with :rspec do |c|
17
+ c.syntax = :expect
18
+ end
19
+ end
20
+
21
+ def run!(cmd)
22
+ out = `#{cmd}`
23
+ raise "Error running #{cmd}, output: #{out}" unless $?.success?
24
+ out
25
+ end
26
+
27
+ def spec_dir
28
+ Pathname.new(__dir__)
29
+ end
30
+
@@ -1,3 +1,3 @@
1
1
  module Hatchet
2
- VERSION = "7.1.3"
2
+ VERSION = "7.2.0"
3
3
  end
@@ -1,6 +1,28 @@
1
1
  require("spec_helper")
2
2
 
3
3
  describe "AppTest" do
4
+ it "does not modify local files by mistake" do
5
+ Dir.mktmpdir do |dir_1|
6
+ app = Hatchet::Runner.new(dir_1)
7
+ Dir.mktmpdir do |dir_2|
8
+ Dir.chdir(dir_2) do
9
+ FileUtils.touch("foo.txt")
10
+
11
+ app.setup!
12
+ end
13
+
14
+ entries_array = Dir.entries(dir_2)
15
+ entries_array -= ["..", ".", "foo.txt"]
16
+ expect(entries_array).to be_empty
17
+
18
+
19
+ entries_array = Dir.entries(dir_1)
20
+ entries_array -= ["..", ".", "foo.txt"]
21
+ expect(entries_array).to be_empty
22
+ end
23
+ end
24
+ end
25
+
4
26
  it "rate throttles `git push` " do
5
27
  app = Hatchet::GitApp.new("default_ruby")
6
28
  def app.git_push_heroku_yall
@@ -13,14 +13,15 @@ describe "LocalRepoTest" do
13
13
 
14
14
  it "repos checked into git" do
15
15
  begin
16
- app = Hatchet::App.new("repo_fixtures/different-folder-for-checked-in-repos/default_ruby")
17
- app.in_directory do
18
- expect(Dir.exist?(".git")).to eq(false)
19
- app.setup!
20
- expect(Dir.exist?(".git")).to eq(true)
16
+ fixture_dir = "repo_fixtures/different-folder-for-checked-in-repos/default_ruby"
17
+ app = Hatchet::App.new(fixture_dir)
18
+ def app.push_with_retry!; end
19
+
20
+ expect(Dir.exist?("#{fixture_dir}/.git")).to be_falsey
21
+
22
+ app.deploy do
23
+ expect(Dir.exist?(".git")).to be_truthy
21
24
  end
22
- ensure
23
- app.teardown! if app
24
25
  end
25
26
  end
26
27
  end
@@ -0,0 +1,52 @@
1
+ require "spec_helper"
2
+
3
+ describe "Hatchet::Init" do
4
+ def fake_buildpack_dir
5
+ Dir.mktmpdir do |dir|
6
+ FileUtils.mkdir_p("#{dir}/bin")
7
+ yield dir
8
+ end
9
+ end
10
+ it "raises an error when not pointing at the right directory" do
11
+ Dir.mktmpdir do |dir|
12
+ expect {
13
+ Hatchet::InitProject.new(dir: dir)
14
+ }.to raise_error(/Must run in a directory with a buildpack/)
15
+ end
16
+ end
17
+
18
+ # write_target(target: ".circleci/config.yml", template: "circleci_template.erb")
19
+ # write_target(target: "Gemfile", template: "Gemfile.erb")
20
+ # write_target(target: "hatchet.json", contents: "{}")
21
+ # write_target(target: "hatchet.lock", contents: YAML.dump({}))
22
+ # write_target(target: "spec/spec_helper.rb", template: "spec_helper.erb")
23
+ # write_target(target: "spec/hatchet/buildpack_spec.rb", template: "buildpack_spec.erb")
24
+ # write_target(target: ".github/dependabot.yml", template: "dependabot.erb")
25
+
26
+ it "generates files" do
27
+ fake_buildpack_dir do |dir|
28
+ fake_stdout = StringIO.new
29
+ init = Hatchet::InitProject.new(dir: dir, io: fake_stdout)
30
+ init.call
31
+
32
+ circle_ci_file = Pathname.new(dir).join(".circleci/config.yml")
33
+ expect(circle_ci_file.read).to match("parallel_split_test")
34
+
35
+ %W{
36
+ .circleci/config.yml
37
+ Gemfile
38
+ hatchet.json
39
+ hatchet.lock
40
+ spec/spec_helper.rb
41
+ spec/hatchet/buildpack_spec.rb
42
+ .github/dependabot.yml
43
+ .github/workflows/check_changelog.yml
44
+ .gitignore
45
+ }.each do |path|
46
+ expect(Pathname.new(dir).join(path)).to exist
47
+ end
48
+
49
+ expect(fake_stdout.string).to match("Bundle complete")
50
+ end
51
+ end
52
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: heroku_hatchet
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.1.3
4
+ version: 7.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Schneeman
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-09 00:00:00.000000000 Z
11
+ date: 2020-09-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: platform-api
@@ -192,11 +192,19 @@ files:
192
192
  - lib/hatchet/app.rb
193
193
  - lib/hatchet/config.rb
194
194
  - lib/hatchet/git_app.rb
195
+ - lib/hatchet/init_project.rb
195
196
  - lib/hatchet/reaper.rb
196
197
  - lib/hatchet/reaper/app_age.rb
197
198
  - lib/hatchet/reaper/reaper_throttle.rb
198
199
  - lib/hatchet/shell_throttle.rb
199
200
  - lib/hatchet/tasks.rb
201
+ - lib/hatchet/templates/Gemfile.erb
202
+ - lib/hatchet/templates/buildpack_spec.erb
203
+ - lib/hatchet/templates/check_changelog.erb
204
+ - lib/hatchet/templates/circleci_template.erb
205
+ - lib/hatchet/templates/dependabot.erb
206
+ - lib/hatchet/templates/hatchet_json.erb
207
+ - lib/hatchet/templates/spec_helper.erb
200
208
  - lib/hatchet/test_run.rb
201
209
  - lib/hatchet/version.rb
202
210
  - repo_fixtures/different-folder-for-checked-in-repos/default_ruby/Gemfile
@@ -211,6 +219,7 @@ files:
211
219
  - spec/hatchet/local_repo_spec.rb
212
220
  - spec/hatchet/lock_spec.rb
213
221
  - spec/spec_helper.rb
222
+ - spec/unit/init_spec.rb
214
223
  - spec/unit/reaper_spec.rb
215
224
  - spec/unit/shell_throttle.rb
216
225
  - tmp/parallel_runtime_test.log
@@ -248,5 +257,6 @@ test_files:
248
257
  - spec/hatchet/local_repo_spec.rb
249
258
  - spec/hatchet/lock_spec.rb
250
259
  - spec/spec_helper.rb
260
+ - spec/unit/init_spec.rb
251
261
  - spec/unit/reaper_spec.rb
252
262
  - spec/unit/shell_throttle.rb