158 lines
5.3 KiB
Ruby
Executable File
158 lines
5.3 KiB
Ruby
Executable File
#!/usr/bin/env ruby
|
|
# frozen_string_literal: true
|
|
#
|
|
# os-backlog CLI: board-ensure, activate, archive, resolve, card-add,
|
|
# cards, snapshot.
|
|
#
|
|
# Usage:
|
|
# os-backlog board-ensure NAME --project Dev|Clients
|
|
# os-backlog activate NAME
|
|
# os-backlog archive NAME --yes
|
|
# os-backlog resolve < input.json
|
|
# input.json: {"repo_path": "...", "config": "..."|null, "boards": ["...", ...]}
|
|
# os-backlog card-add --board NAME --title "..." [--description "..."]
|
|
# os-backlog cards --board NAME (human-readable per-list card listing)
|
|
# os-backlog snapshot --board NAME (full board state as JSON; read-only)
|
|
#
|
|
# board-ensure/activate/archive talk to Planka over the network (the
|
|
# installed planka-api gem, credentials from PLANKA_BASE_URL/USERNAME/
|
|
# PASSWORD). resolve is pure — no network access at all.
|
|
#
|
|
# Fails soft: if the gem or Planka credentials are unavailable, prints one
|
|
# clear error and exits nonzero. Never destructive.
|
|
|
|
require "json"
|
|
require_relative "../lib/backlog"
|
|
|
|
def fail_soft(message)
|
|
warn "os-backlog: #{message}"
|
|
exit 1
|
|
end
|
|
|
|
def build_client
|
|
require "planka_api"
|
|
client = Planka::Client.new
|
|
client.login
|
|
client
|
|
rescue LoadError
|
|
fail_soft("the planka-api gem is not installed/loadable; cannot reach Planka")
|
|
rescue StandardError => e
|
|
fail_soft("could not authenticate to Planka: #{e.message}")
|
|
end
|
|
|
|
def parse_project_flag(args)
|
|
idx = args.index("--project")
|
|
return "Dev" unless idx
|
|
|
|
args.delete_at(idx)
|
|
project = args.delete_at(idx)
|
|
project || fail_soft("--project requires a value (Dev or Clients)")
|
|
end
|
|
|
|
def parse_flag(args, flag)
|
|
idx = args.index(flag)
|
|
return nil unless idx
|
|
|
|
args.delete_at(idx)
|
|
args.delete_at(idx)
|
|
end
|
|
|
|
def require_flag(args, flag, command)
|
|
parse_flag(args, flag) || fail_soft("#{command} requires #{flag} <value>")
|
|
end
|
|
|
|
command, *rest = ARGV
|
|
|
|
begin
|
|
case command
|
|
when "board-ensure"
|
|
name = rest.shift
|
|
fail_soft("board-ensure requires a board name") unless name
|
|
project = parse_project_flag(rest)
|
|
client = build_client
|
|
result = Backlog::BoardEnsurer.new(client: client).ensure(name, project_name: project)
|
|
puts "board #{result.board.name} (#{result.created ? 'created' : 'existing'}) " \
|
|
"in project #{project}; lists created: #{result.lists_created.join(', ')}; " \
|
|
"labels created: #{result.labels_created.join(', ')}"
|
|
when "activate"
|
|
name = rest.shift
|
|
fail_soft("activate requires a board name") unless name
|
|
client = build_client
|
|
board = Backlog::BoardEnsurer.new(client: client).activate(name)
|
|
puts "activated: #{board.name}"
|
|
when "archive"
|
|
name = rest.shift
|
|
fail_soft("archive requires a board name") unless name
|
|
fail_soft("archive requires explicit --yes (human go-ahead only)") unless rest.include?("--yes")
|
|
|
|
client = build_client
|
|
board = Backlog::BoardEnsurer.new(client: client).archive(name)
|
|
puts "archived: #{board.name}"
|
|
when "resolve"
|
|
input = JSON.parse($stdin.read)
|
|
resolver = Backlog::Resolver.new(
|
|
repo_path: input.fetch("repo_path"),
|
|
config_contents: input["config"],
|
|
boards: input.fetch("boards", [])
|
|
)
|
|
puts resolver.resolve_string
|
|
when "card-add"
|
|
board = require_flag(rest, "--board", "card-add")
|
|
title = require_flag(rest, "--title", "card-add")
|
|
description = parse_flag(rest, "--description")
|
|
client = build_client
|
|
card = Backlog::Cards.new(client: client).add(board_name: board, title: title, description: description)
|
|
puts "created card ##{card.id} at Backlog on #{board}: #{card.name}"
|
|
when "cards"
|
|
board = require_flag(rest, "--board", "cards")
|
|
client = build_client
|
|
snapshot = Backlog::Cards.new(client: client).snapshot(board_name: board)
|
|
snapshot["lists"].each do |list|
|
|
puts "#{list['name']} (#{list['cards'].size})"
|
|
list["cards"].each do |card|
|
|
labels = card["labels"].empty? ? "" : " [#{card['labels'].join(', ')}]"
|
|
puts " - #{card['name']}#{labels}"
|
|
end
|
|
end
|
|
when "snapshot"
|
|
board = require_flag(rest, "--board", "snapshot")
|
|
client = build_client
|
|
puts JSON.pretty_generate(Backlog::Cards.new(client: client).snapshot(board_name: board))
|
|
when "card-label"
|
|
board = require_flag(rest, "--board", "card-label")
|
|
card_id = require_flag(rest, "--card", "card-label")
|
|
label = require_flag(rest, "--label", "card-label")
|
|
client = build_client
|
|
Backlog::Cards.new(client: client).attach_label(board_name: board, card_id: card_id, label: label)
|
|
puts "labeled card ##{card_id} with #{label}"
|
|
when "card-comment"
|
|
card_id = require_flag(rest, "--card", "card-comment")
|
|
text = require_flag(rest, "--text", "card-comment")
|
|
client = build_client
|
|
comment = Backlog::Cards.new(client: client).comment(card_id: card_id, text: text)
|
|
puts "commented on card ##{card_id} (comment ##{comment.id})"
|
|
when nil, "-h", "--help"
|
|
puts <<~USAGE
|
|
usage: os-backlog <command> [options]
|
|
|
|
commands:
|
|
board-ensure NAME --project Dev|Clients
|
|
activate NAME
|
|
archive NAME --yes
|
|
resolve (reads {"repo_path","config","boards"} JSON from stdin)
|
|
card-add --board NAME --title "..." [--description "..."]
|
|
cards --board NAME
|
|
snapshot --board NAME
|
|
card-label --board NAME --card ID --label NAME
|
|
card-comment --card ID --text "..."
|
|
USAGE
|
|
exit(command.nil? ? 1 : 0)
|
|
else
|
|
fail_soft("unknown command #{command.inspect}")
|
|
end
|
|
rescue SystemExit
|
|
raise
|
|
rescue StandardError => e
|
|
fail_soft(e.message)
|
|
end
|