92 lines
3.1 KiB
Ruby
92 lines
3.1 KiB
Ruby
require_relative "board_spec"
|
|
require_relative "config"
|
|
|
|
module Backlog
|
|
# Pure repo -> board routing decision. No filesystem, no network: every
|
|
# input (repo path, config contents, board inventory) is passed in.
|
|
#
|
|
# Decision, exactly one of:
|
|
# "use <board>" - an active board to work against
|
|
# "activate <archived board>" - an archived board matches; rename it back
|
|
# "stop-and-discuss" - no confident match; ask the human
|
|
class Resolver
|
|
Decision = Struct.new(:action, :board) do
|
|
def to_s
|
|
action == :use || action == :activate ? "#{action} #{board}" : "stop-and-discuss"
|
|
end
|
|
end
|
|
|
|
DEV_ROOT = File.expand_path("~/dev")
|
|
CLIENTS_ROOT = File.expand_path("~/clients")
|
|
SERVERS_ROOT = File.expand_path("~/servers")
|
|
|
|
# Path-based Planka project heuristic, shared by the resolver's own
|
|
# ambiguity gate and the `board-ensure` CLI's --project default: "Dev"
|
|
# under ~/dev/*, "Clients" under ~/clients/*, "Servers" under
|
|
# ~/servers/* (the servers umbrella repo — ADR: infrastructure is a
|
|
# distinct domain from Dev), nil (ambiguous) otherwise. Board-ensure
|
|
# always accepts an arbitrary --project name too — this heuristic only
|
|
# supplies the default when none is given.
|
|
#
|
|
# @param repo_path [String]
|
|
# @return [String, nil]
|
|
def self.project_for(repo_path)
|
|
path = File.expand_path(repo_path)
|
|
return "Dev" if under_root?(path, DEV_ROOT)
|
|
return "Clients" if under_root?(path, CLIENTS_ROOT)
|
|
return "Servers" if under_root?(path, SERVERS_ROOT)
|
|
|
|
nil
|
|
end
|
|
|
|
def self.under_root?(path, root)
|
|
path == root || path.start_with?("#{root}/")
|
|
end
|
|
private_class_method :under_root?
|
|
|
|
# @param repo_path [String] absolute path to the repo
|
|
# @param config_contents [String, nil] raw contents of .cc-os/config, or nil/absent
|
|
# @param boards [Array<String>] board names known to exist on the Planka instance
|
|
# (both active and archived--prefixed names)
|
|
def initialize(repo_path:, config_contents:, boards:)
|
|
@repo_path = File.expand_path(repo_path)
|
|
@config = Config.new(config_contents)
|
|
@boards = boards
|
|
end
|
|
|
|
# @return [Decision]
|
|
def resolve
|
|
configured_board = @config.planka_board
|
|
return decision_for_name(configured_board) || stop_and_discuss if configured_board
|
|
|
|
derived_decision || stop_and_discuss
|
|
end
|
|
|
|
# @return [String] one of "use <board>" / "activate <board>" / "stop-and-discuss"
|
|
def resolve_string = resolve.to_s
|
|
|
|
private
|
|
|
|
def derived_decision
|
|
return nil if project.nil?
|
|
|
|
decision_for_name(board_name)
|
|
end
|
|
|
|
def decision_for_name(name)
|
|
return use(name) if @boards.include?(name)
|
|
return activate(name) if @boards.include?(BoardSpec.archived_name(name))
|
|
|
|
nil
|
|
end
|
|
|
|
def stop_and_discuss = Decision.new(:stop_and_discuss, nil)
|
|
def use(name) = Decision.new(:use, name)
|
|
def activate(name) = Decision.new(:activate, BoardSpec.archived_name(name))
|
|
|
|
def board_name = File.basename(@repo_path)
|
|
|
|
def project = self.class.project_for(@repo_path)
|
|
end
|
|
end
|