cc-os/plugins/os-backlog/bin/os-backlog

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