Skip to content
Open

RoF #325

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
87f307d
feat: rof
3v0k4 Jul 29, 2025
a985a58
feat: lost batch
3v0k4 Aug 12, 2025
7eab5d5
chore: simplify test_queue_id
3v0k4 Aug 13, 2025
8596378
feat(rof): retry command
3v0k4 Aug 26, 2025
03daeb9
feat: add missing frozen_string_literal
3v0k4 Aug 27, 2025
7bd6a6e
bk
3v0k4 Nov 24, 2025
c08b640
feat(rof): rake task to initialize queue
3v0k4 Jan 8, 2026
265ba09
feat(rof): fall back to triplet when no id
3v0k4 Jan 9, 2026
7133331
test(rails-app): update gems with c extensions to fix build issues
3v0k4 Jan 12, 2026
601900e
chore: remove confusing logs
3v0k4 Jan 14, 2026
6ee16f8
refactor: better naming
3v0k4 Jan 12, 2026
9927007
perf: avoid double init
3v0k4 Jan 20, 2026
a0229a3
build: update bundler (to remove warns)
3v0k4 Jan 14, 2026
86e42dc
chore: update bin scripts
3v0k4 Jan 14, 2026
34d018c
chore: paths -> test_paths
3v0k4 Jan 14, 2026
0ad3799
feat: tweak retry command
3v0k4 Jan 20, 2026
84af173
chore: remove unused code
3v0k4 Jan 20, 2026
04d0f1a
chore: remove code comment
3v0k4 Jan 20, 2026
b01076f
test: remove noise from logs
3v0k4 Jan 20, 2026
62bdbb4
fix: do not oblige users to pass a string as args
3v0k4 Jan 20, 2026
1a0d93c
feat: support encrypted branch in retry command
3v0k4 Jan 20, 2026
6bb69e2
fix: avoid name collisions
3v0k4 Jan 20, 2026
668da13
fix: rspec queue command
3v0k4 Jan 20, 2026
d59da10
feat: send fixed queue split in v2
3v0k4 Jan 21, 2026
de1ab88
refactor: simplify params
3v0k4 Jan 21, 2026
7e8734b
chore: retry only rspec for now
3v0k4 Jan 21, 2026
b033fce
fix: commands expect string (keep compat)
3v0k4 Jan 21, 2026
6e72505
test: remove unneeded ENV
3v0k4 Jan 23, 2026
6f2844f
feat: log a message when no tests were executed in queue mode
3v0k4 Jan 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 20 additions & 21 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ jobs:
RACK_ENV: test
KNAPSACK_PRO_ENDPOINT: https://api-staging.knapsackpro.com
KNAPSACK_PRO_TEST_SUITE_TOKEN_RSPEC: $KNAPSACK_PRO_TEST_SUITE_TOKEN_RSPEC
KNAPSACK_PRO_LOG_LEVEL: debug
EXTRA_TEST_FILES_DELAY: 10
- image: cimg/postgres:14.7
environment:
Expand All @@ -239,44 +240,40 @@ jobs:
- run:
working_directory: ~/knapsack_pro-ruby/rails-app-with-knapsack_pro
command: |
export KNAPSACK_PRO_BRANCH="$CIRCLE_BRANCH--$CIRCLE_BUILD_NUM--queue"
export KNAPSACK_PRO_TEST_QUEUE_ID="<< pipeline.number >>:$CIRCLE_JOB--queue"
export KNAPSACK_PRO_BRANCH="$CIRCLE_BRANCH--<< pipeline.number >>:$CIRCLE_JOB--queue"
bundle exec rake knapsack_pro:queue:rspec
- run:
working_directory: ~/knapsack_pro-ruby/rails-app-with-knapsack_pro
command: |
# run 0 tests as queue is consumed ||
export KNAPSACK_PRO_BRANCH="$CIRCLE_BRANCH--$CIRCLE_BUILD_NUM--queue"
export KNAPSACK_PRO_FIXED_QUEUE_SPLIT=false
bundle exec rake knapsack_pro:queue:rspec
- run:
working_directory: ~/knapsack_pro-ruby/rails-app-with-knapsack_pro
command: |
# retry the same split ||
export KNAPSACK_PRO_BRANCH="$CIRCLE_BRANCH--$CIRCLE_BUILD_NUM--queue"
export KNAPSACK_PRO_FIXED_QUEUE_SPLIT=true
# run 0 tests because nothing failed in the previous step ||
export KNAPSACK_PRO_TEST_QUEUE_ID="<< pipeline.number >>:$CIRCLE_JOB--queue"
export KNAPSACK_PRO_BRANCH="$CIRCLE_BRANCH--<< pipeline.number >>:$CIRCLE_JOB--queue"
bundle exec rake knapsack_pro:queue:rspec
- run:
working_directory: ~/knapsack_pro-ruby/rails-app-with-knapsack_pro
command: |
# fallback ||
export KNAPSACK_PRO_TEST_QUEUE_ID="<< pipeline.number >>:$CIRCLE_JOB--queue--fallback"
export KNAPSACK_PRO_BRANCH="$CIRCLE_BRANCH--<< pipeline.number >>:$CIRCLE_JOB--queue--fallback"
export KNAPSACK_PRO_ENDPOINT=https://api-fake.knapsackpro.com
export KNAPSACK_PRO_MAX_REQUEST_RETRIES=1
bundle exec rake knapsack_pro:queue:rspec
- run:
working_directory: ~/knapsack_pro-ruby/rails-app-with-knapsack_pro
command: |
# split by test examples above the slow test file threshold ||
export KNAPSACK_PRO_BRANCH="$CIRCLE_BRANCH--$CIRCLE_BUILD_NUM--queue--split-above-slow-test-file-threshold"
export KNAPSACK_PRO_FIXED_QUEUE_SPLIT=true
export KNAPSACK_PRO_TEST_QUEUE_ID="<< pipeline.number >>:$CIRCLE_JOB--queue--split-above-slow-test-file-threshold"
export KNAPSACK_PRO_BRANCH="$CIRCLE_BRANCH--<< pipeline.number >>:$CIRCLE_JOB--queue--split-above-slow-test-file-threshold"
export KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES=true
export KNAPSACK_PRO_SLOW_TEST_FILE_THRESHOLD=1
bundle exec rake knapsack_pro:queue:rspec
- run:
working_directory: ~/knapsack_pro-ruby/rails-app-with-knapsack_pro
command: |
# split by test examples AND a single CI node ||
export KNAPSACK_PRO_BRANCH="$CIRCLE_BRANCH--$CIRCLE_BUILD_NUM--queue--split--single-node--$CIRCLE_NODE_INDEX"
export KNAPSACK_PRO_FIXED_QUEUE_SPLIT=true
export KNAPSACK_PRO_TEST_QUEUE_ID="<< pipeline.number >>:$CIRCLE_JOB--queue--split--single-node"
export KNAPSACK_PRO_BRANCH="$CIRCLE_BRANCH--<< pipeline.number >>:$CIRCLE_JOB--queue--split--single-node"
export KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES=true
export KNAPSACK_PRO_CI_NODE_TOTAL=1
export KNAPSACK_PRO_CI_NODE_INDEX=0
Expand All @@ -285,7 +282,8 @@ jobs:
working_directory: ~/knapsack_pro-ruby/rails-app-with-knapsack_pro
command: |
# split custom files by test examples AND the --tag option passed ||
export KNAPSACK_PRO_BRANCH="$CIRCLE_BRANCH--$CIRCLE_BUILD_NUM--queue--split-custom-files--tag-option"
export KNAPSACK_PRO_TEST_QUEUE_ID="<< pipeline.number >>:$CIRCLE_JOB--queue--split-custom-files--tag-option"
export KNAPSACK_PRO_BRANCH="$CIRCLE_BRANCH--<< pipeline.number >>:$CIRCLE_JOB--queue--split-custom-files--tag-option"
export KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES=true
export KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN="spec/bar_spec.rb"
export SKIP_ME_OR_I_WILL_FAIL=true
Expand All @@ -296,8 +294,8 @@ jobs:
# turnip ||
mv .rspec .rspec.off
cp .rspec.turnip .rspec
export KNAPSACK_PRO_BRANCH="$CIRCLE_BRANCH--$CIRCLE_BUILD_NUM--queue--turnip"
export KNAPSACK_PRO_FIXED_QUEUE_SPLIT=true
export KNAPSACK_PRO_TEST_QUEUE_ID="<< pipeline.number >>:$CIRCLE_JOB--queue--turnip"
export KNAPSACK_PRO_BRANCH="$CIRCLE_BRANCH--<< pipeline.number >>:$CIRCLE_JOB--queue--turnip"
export KNAPSACK_PRO_TEST_DIR=turnip
export KNAPSACK_PRO_TEST_FILE_PATTERN="turnip/**/*.feature"
export KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN="turnip/acceptance/foo.feature"
Expand All @@ -312,8 +310,8 @@ jobs:
# turnip retry ||
mv .rspec .rspec.off
cp .rspec.turnip .rspec
export KNAPSACK_PRO_BRANCH="$CIRCLE_BRANCH--$CIRCLE_BUILD_NUM--queue--turnip"
export KNAPSACK_PRO_FIXED_QUEUE_SPLIT=true
export KNAPSACK_PRO_TEST_QUEUE_ID="<< pipeline.number >>:$CIRCLE_JOB--queue--turnip"
export KNAPSACK_PRO_BRANCH="$CIRCLE_BRANCH--<< pipeline.number >>:$CIRCLE_JOB--queue--turnip"
export KNAPSACK_PRO_TEST_DIR=turnip
export KNAPSACK_PRO_TEST_FILE_PATTERN="turnip/**/*.feature"
export KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN="turnip/acceptance/foo.feature"
Expand All @@ -326,7 +324,8 @@ jobs:
working_directory: ~/knapsack_pro-ruby/rails-app-with-knapsack_pro
command: |
# separate queue initialization
export KNAPSACK_PRO_BRANCH="$CIRCLE_BRANCH--$CIRCLE_BUILD_NUM--queue-init"
export KNAPSACK_PRO_TEST_QUEUE_ID="<< pipeline.number >>:$CIRCLE_JOB--queue--init"
export KNAPSACK_PRO_BRANCH="$CIRCLE_BRANCH--<< pipeline.number >>:$CIRCLE_JOB--queue--init"
bundle exec rake knapsack_pro:queue:rspec:initialize
bundle exec rake knapsack_pro:queue:rspec

Expand Down
2 changes: 1 addition & 1 deletion .rspec
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
--color
#--warnings
--require spec_helper
--format documentation
--format progress
#--profile
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ bundle update knapsack_pro
RSpec:

```bash
bundle exec rspec
bin/rspec
```

### Publishing
Expand Down
25 changes: 3 additions & 22 deletions bin/knapsack_pro
Original file line number Diff line number Diff line change
@@ -1,25 +1,6 @@
#!/usr/bin/env ruby

require_relative '../lib/knapsack_pro'
require "rubygems"
require "bundler/setup"

runner = ARGV[0]
arguments = ARGV[1]

MAP = {
'rspec' => KnapsackPro::Runners::RSpecRunner,
'queue:rspec' => KnapsackPro::Runners::Queue::RSpecRunner,
'cucumber' => KnapsackPro::Runners::CucumberRunner,
'queue:cucumber' => KnapsackPro::Runners::Queue::CucumberRunner,
'minitest' => KnapsackPro::Runners::MinitestRunner,
'queue:minitest' => KnapsackPro::Runners::Queue::MinitestRunner,
'test_unit' => KnapsackPro::Runners::TestUnitRunner,
'spinach' => KnapsackPro::Runners::SpinachRunner,
}

runner_class = MAP[runner]

if runner_class
runner_class.run(arguments)
else
raise 'Undefined runner. Please provide runner name and optional arguments, for instance: knapsack_pro rspec "--color --profile"'
end
load Gem.bin_path("knapsack_pro", "knapsack_pro")
27 changes: 27 additions & 0 deletions bin/rspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

#
# This file was generated by Bundler.
#
# The application 'rspec' is installed as part of a gem, and
# this file is here to facilitate running it.
#

ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)

bundle_binstub = File.expand_path("bundle", __dir__)

if File.file?(bundle_binstub)
if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
end
end

require "rubygems"
require "bundler/setup"

load Gem.bin_path("rspec-core", "rspec")
5 changes: 5 additions & 0 deletions exe/knapsack_pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env ruby

require "knapsack_pro"
require "knapsack_pro/commands"
KnapsackPro::Commands.start(ARGV)
43 changes: 21 additions & 22 deletions knapsack_pro.gemspec
Original file line number Diff line number Diff line change
@@ -1,32 +1,31 @@
# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'knapsack_pro/version'
require_relative "lib/knapsack_pro/version"

Gem::Specification.new do |spec|
spec.name = 'knapsack_pro'
spec.name = "knapsack_pro"
spec.version = KnapsackPro::VERSION
spec.required_ruby_version = '>= 3.0.0'
spec.authors = ['ArturT']
spec.email = ['support@knapsackpro.com']
spec.summary = %q{Knapsack Pro splits tests across parallel CI nodes and ensures each parallel job finish work at a similar time.}
spec.description = %q{Knapsack Pro wraps your current test runner(s) and works with your existing CI infrastructure to parallelize tests optimally. It dynamically splits your tests based on up-to-date test execution data. It's designed from the ground up for CI and supports all of them.}
spec.homepage = 'https://knapsackpro.com'
spec.license = 'MIT'
spec.metadata = {
'bug_tracker_uri' => 'https://github.com/KnapsackPro/knapsack_pro-ruby/issues',
'changelog_uri' => 'https://github.com/KnapsackPro/knapsack_pro-ruby/blob/main/CHANGELOG.md',
'documentation_uri' => 'https://docs.knapsackpro.com/knapsack_pro-ruby/guide/',
'homepage_uri' => 'https://knapsackpro.com',
'source_code_uri' => 'https://github.com/KnapsackPro/knapsack_pro-ruby'
}
spec.authors = ["ArturT"]
spec.email = ["support@knapsackpro.com"]
spec.summary = "Knapsack Pro splits tests across parallel CI nodes and ensures each parallel job finish work at a similar time."
spec.description = "Knapsack Pro wraps your current test runner(s) and works with your existing CI infrastructure to parallelize tests optimally. It dynamically splits your tests based on up-to-date test execution data. It's designed from the ground up for CI and supports all of them."
spec.homepage = "https://knapsackpro.com"
spec.license = "MIT"

spec.files = Dir.glob("lib/**/*") + Dir.glob("exe/*")
spec.executables = ['knapsack_pro']
spec.metadata["bug_tracker_uri"] = "https://github.com/KnapsackPro/knapsack_pro-ruby/issues"
spec.metadata["changelog_uri"] = "https://github.com/KnapsackPro/knapsack_pro-ruby/blob/main/CHANGELOG.md"
spec.metadata["documentation_uri"] = "https://docs.knapsackpro.com/knapsack_pro-ruby/guide/"
spec.metadata["homepage_uri"] = "https://knapsackpro.com"
spec.metadata["source_code_uri"] = "https://github.com/KnapsackPro/knapsack_pro-ruby"
spec.metadata["rubygems_mfa_required"] = "true"

spec.files = Dir.glob("lib/**/*") + Dir.glob("exe/*")
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ['lib']
spec.require_paths = ["lib"]

spec.add_dependency 'rake', '>= 0'
spec.add_dependency "rake", ">= 0"
spec.add_dependency "thor", "~> 1.4"

spec.add_development_dependency 'bundler', '>= 1.6'
spec.add_development_dependency 'cucumber', '>= 0'
Expand Down
15 changes: 14 additions & 1 deletion lib/knapsack_pro.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
require_relative 'knapsack_pro/client/api/v1/base'
require_relative 'knapsack_pro/client/api/v1/build_distributions'
require_relative 'knapsack_pro/client/api/v1/build_subsets'
require_relative 'knapsack_pro/client/api/v1/queues'
require_relative 'knapsack_pro/client/connection'
require_relative 'knapsack_pro/repository_adapters/base_adapter'
require_relative 'knapsack_pro/repository_adapters/env_adapter'
Expand Down Expand Up @@ -84,6 +83,20 @@
require_relative 'knapsack_pro/crypto/digestor'
require_relative 'knapsack_pro/pure/queue/rspec_pure'

module KnapsackPro
module Client
module API
module V1
autoload(:Queues, 'knapsack_pro/client/api/v1/queues')
end

module V2
autoload(:Queues, 'knapsack_pro/client/api/v2/queues')
end
end
end
end

require 'knapsack_pro/railtie' if defined?(Rails::Railtie)

module KnapsackPro
Expand Down
2 changes: 1 addition & 1 deletion lib/knapsack_pro/adapters/rspec_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ def bind_regular_mode_time_tracker
::RSpec.configure do |config|
config.append_before(:suite) do
time_tracker = KnapsackPro::Formatters::TimeTrackerFetcher.call
time_tracker.scheduled_paths = KnapsackPro::Adapters::RSpecAdapter.scheduled_paths
time_tracker.schedule(KnapsackPro::Adapters::RSpecAdapter.scheduled_paths)
end
end
end
Expand Down
10 changes: 1 addition & 9 deletions lib/knapsack_pro/client/api/action.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,7 @@
module KnapsackPro
module Client
module API
class Action
attr_reader :endpoint_path, :http_method, :request_hash

def initialize(args)
@endpoint_path = args.fetch(:endpoint_path)
@http_method = args.fetch(:http_method)
@request_hash = args.fetch(:request_hash)
end
end
Action = Struct.new(:endpoint_path, :http_method, :request_hash, keyword_init: true)
end
end
end
27 changes: 0 additions & 27 deletions lib/knapsack_pro/client/api/v1/queues.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,33 +36,6 @@ def queue(args)
request_hash: request_hash
)
end

def initialize(paths)
git_adapter = KnapsackPro::RepositoryAdapters::GitAdapter.new
repository_adapter = KnapsackPro::RepositoryAdapterInitiator.call

request_hash = {
attempt_connect_to_queue: false,
branch: KnapsackPro::Crypto::BranchEncryptor.call(repository_adapter.branch),
build_author: git_adapter.build_author,
can_initialize_queue: true,
commit_authors: git_adapter.commit_authors,
commit_hash: repository_adapter.commit_hash,
fixed_queue_split: KnapsackPro::Config::Env.fixed_queue_split,
node_build_id: KnapsackPro::Config::Env.ci_node_build_id,
node_index: KnapsackPro::Config::Env.ci_node_index,
node_total: KnapsackPro::Config::Env.ci_node_total,
skip_pull: true,
test_files: KnapsackPro::Crypto::Encryptor.call(paths),
user_seat: KnapsackPro::Config::Env.masked_user_seat,
}

action_class.new(
endpoint_path: '/v1/queues/queue',
http_method: :post,
request_hash: request_hash
)
end
end
end
end
Expand Down
Loading