Initial implementation of portainer-cli

Ruby CLI tool for interacting with the Portainer API. Supports listing
endpoints/stacks/containers/volumes/networks, creating stacks and containers,
and opening an interactive exec session in a container via WebSocket.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-22 12:58:33 +01:00
commit d3cb4fdebd
14 changed files with 815 additions and 0 deletions
+65
View File
@@ -0,0 +1,65 @@
# frozen_string_literal: true
require_relative 'base'
module PortainerCli
module Commands
class Configure
include Base
def initialize(config)
@config = config
end
def run(_args)
puts bold("Portainer CLI configuration")
puts "Config will be saved to: #{cyan(Config::CONFIG_FILE)}"
puts
@config.url = prompt("Portainer URL", @config.url || "https://portainer.example.com")
puts
puts "Authentication — choose one:"
puts " #{cyan('1')} API Key (recommended, no expiry)"
puts " #{cyan('2')} JWT token (from login)"
choice = prompt("Choice [1/2]", "1")
if choice == "2"
@config.api_key = nil
@config.token = prompt_secret("JWT token", @config.token)
else
@config.token = nil
@config.api_key = prompt_secret("API key", @config.api_key)
end
@config.save
puts
puts green("Configuration saved to #{Config::CONFIG_FILE}")
end
private
def prompt(label, default = nil)
default_hint = default ? " [#{dim(default)}]" : ""
print "#{label}#{default_hint}: "
input = $stdin.gets&.chomp
(input.nil? || input.empty?) ? default.to_s : input
end
def prompt_secret(label, current = nil)
hint = current ? " [#{dim('keep current')}]" : ""
print "#{label}#{hint}: "
# Hide input if on a TTY
value = if $stdin.tty?
require 'io/console'
v = $stdin.noecho(&:gets)&.chomp
puts
v
else
$stdin.gets&.chomp
end
(value.nil? || value.empty?) ? current.to_s : value
end
end
end
end