deploio-cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.ruby-version +1 -0
- data/README.md +173 -0
- data/Rakefile +12 -0
- data/bin/deploio +18 -0
- data/deploio-cli.gemspec +33 -0
- data/lib/deploio/app_ref.rb +67 -0
- data/lib/deploio/app_resolver.rb +112 -0
- data/lib/deploio/cli.rb +95 -0
- data/lib/deploio/commands/apps.rb +149 -0
- data/lib/deploio/commands/auth.rb +30 -0
- data/lib/deploio/commands/orgs.rb +54 -0
- data/lib/deploio/completion_generator.rb +152 -0
- data/lib/deploio/nctl_client.rb +214 -0
- data/lib/deploio/output.rb +59 -0
- data/lib/deploio/shared_options.rb +41 -0
- data/lib/deploio/templates/completion.zsh.erb +126 -0
- data/lib/deploio/utils.rb +12 -0
- data/lib/deploio/version.rb +5 -0
- data/lib/deploio.rb +18 -0
- data/setup +115 -0
- metadata +92 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 4464fa0c5bfb5ca034fc5b7936d2b3d856224068fb984bea2bf03018b1e33841
|
|
4
|
+
data.tar.gz: 8b9a0dc13c1262ebc81b8bee5e4aac9d9d47e70fb2850a6259ded636a40ea3af
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 61678961554d647509a198b88510862d178774814e2fe094aa42e891ed655f079a8216b9663addd75da1861a2d24d7d9c0419ec6abb50bed139cdbab1176dec3
|
|
7
|
+
data.tar.gz: 9fcedee868c9a4621b4e0441618267074dc6963cb7ff0f8e7d00565b9f2e4f01c8f926e38e1d8ec5029c4775f975cd687e40b13f7b6e35141c2c20d814841d18
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.4.8
|
data/README.md
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# deploio-cli (deploio / depl)
|
|
2
|
+
|
|
3
|
+
A CLI for [Deploio](https://www.deplo.io/) that wraps [`nctl`](https://github.com/ninech/nctl) commands with a simpler interface.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- Ruby 3.3+
|
|
8
|
+
- nctl version 1.10.0 or higher
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
### From source (development)
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
git clone https://github.com/renuo/deploio-cli.git
|
|
16
|
+
cd deploio-cli
|
|
17
|
+
bundle install
|
|
18
|
+
bundle exec bin/deploio --help
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### As a gem (coming soon)
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
gem install deploio-cli
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Shell Completion
|
|
28
|
+
|
|
29
|
+
Enable autocompletion by adding this to your `~/.zshrc` or `~/.bashrc`, or whatever you use:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
eval "$(deploio completion)"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
After setup, completions work for:
|
|
36
|
+
- Commands: `deploio <TAB>` → shows all commands and subcommands
|
|
37
|
+
- Options: `deploio logs --<TAB>` → shows --tail, --lines, --app, etc.
|
|
38
|
+
- Apps (dynamic): `deploio logs --app <TAB>` → fetches available apps from server
|
|
39
|
+
|
|
40
|
+
## Configuration
|
|
41
|
+
|
|
42
|
+
### App naming convention
|
|
43
|
+
|
|
44
|
+
Apps are referenced using `<project>-<app>` format, where:
|
|
45
|
+
- `project` is your Deploio project name (e.g., `deploio-landing-page`)
|
|
46
|
+
- `app` is the environment/app name (e.g., `develop`, `production`)
|
|
47
|
+
|
|
48
|
+
Example: `deploio-landing-page-develop`
|
|
49
|
+
|
|
50
|
+
### Automatic app detection
|
|
51
|
+
|
|
52
|
+
When you run a command without `--app`, the CLI will automatically detect the app by matching your git remote URL against nctl apps:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
cd ~/projects/deploio-landing-page
|
|
56
|
+
deploio logs --tail # Automatically detects app from git remote
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
If multiple apps match (e.g., develop and production), you'll be prompted to select one.
|
|
60
|
+
|
|
61
|
+
## Usage
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
deploio - CLI for Deploio (wraps nctl)
|
|
65
|
+
|
|
66
|
+
AUTHENTICATION
|
|
67
|
+
deploio auth:login Authenticate with nctl
|
|
68
|
+
deploio auth:logout Log out
|
|
69
|
+
deploio auth:whoami Show current user and organization
|
|
70
|
+
deploio login Shortcut for auth:login
|
|
71
|
+
|
|
72
|
+
APPS
|
|
73
|
+
deploio apps List all apps
|
|
74
|
+
deploio apps:info -a APP Show app details
|
|
75
|
+
|
|
76
|
+
LOGS
|
|
77
|
+
deploio logs -a APP Show recent logs
|
|
78
|
+
deploio logs -a APP --tail Stream logs continuously
|
|
79
|
+
deploio logs -a APP -n 200 Show last N lines
|
|
80
|
+
|
|
81
|
+
EXECUTION
|
|
82
|
+
deploio exec -a APP -- CMD Run command in app container
|
|
83
|
+
deploio run -a APP -- CMD Alias for exec
|
|
84
|
+
|
|
85
|
+
OTHER
|
|
86
|
+
deploio completion Generate shell completion script
|
|
87
|
+
deploio version Show version
|
|
88
|
+
|
|
89
|
+
FLAGS
|
|
90
|
+
-a, --app APP App in <project>-<app> format
|
|
91
|
+
-o, --org ORG Organization
|
|
92
|
+
--dry-run Print commands without executing
|
|
93
|
+
--no-color Disable colored output
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Examples
|
|
97
|
+
|
|
98
|
+
### Authentication
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# Login to nctl
|
|
102
|
+
deploio login
|
|
103
|
+
|
|
104
|
+
# Check current user
|
|
105
|
+
deploio auth:whoami
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Working with apps
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
# List all apps
|
|
112
|
+
deploio apps
|
|
113
|
+
|
|
114
|
+
# Show app info
|
|
115
|
+
deploio apps:info -a myproject-staging
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Logs and execution
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
# View logs
|
|
123
|
+
deploio logs -a deploio-landing-page-develop
|
|
124
|
+
|
|
125
|
+
# Stream logs
|
|
126
|
+
deploio logs -a deploio-landing-page-develop --tail
|
|
127
|
+
|
|
128
|
+
# Run a command
|
|
129
|
+
deploio exec -a deploio-landing-page-develop -- rails console
|
|
130
|
+
|
|
131
|
+
# With git remote matching (auto-detected)
|
|
132
|
+
cd ~/projects/deploio-landing-page
|
|
133
|
+
deploio logs --tail
|
|
134
|
+
deploio exec -- rails console
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Development
|
|
138
|
+
|
|
139
|
+
### Running tests
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
bundle exec rake test
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Building the gem
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
gem build deploio-cli.gemspec
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Testing commands
|
|
152
|
+
|
|
153
|
+
The best way to test the commands in the shell is to temporarly set:
|
|
154
|
+
|
|
155
|
+
```shell
|
|
156
|
+
export PATH="$PWD/bin:$PATH"
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
to have the `deploio` command binded to the current one, and
|
|
160
|
+
|
|
161
|
+
```shell
|
|
162
|
+
eval "$(deploio completion)"
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
to refresh the autocompletion options.
|
|
166
|
+
|
|
167
|
+
## License
|
|
168
|
+
|
|
169
|
+
MIT
|
|
170
|
+
|
|
171
|
+
## Copyright
|
|
172
|
+
|
|
173
|
+
Renuo AG
|
data/Rakefile
ADDED
data/bin/deploio
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
|
|
5
|
+
|
|
6
|
+
require 'deploio'
|
|
7
|
+
|
|
8
|
+
$stdout.sync = true
|
|
9
|
+
|
|
10
|
+
begin
|
|
11
|
+
Deploio::CLI.start(ARGV)
|
|
12
|
+
rescue Deploio::Error => e
|
|
13
|
+
Deploio::Output.error(e.message)
|
|
14
|
+
exit 1
|
|
15
|
+
rescue Interrupt
|
|
16
|
+
puts "\nInterrupted"
|
|
17
|
+
exit 130
|
|
18
|
+
end
|
data/deploio-cli.gemspec
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/deploio/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "deploio-cli"
|
|
7
|
+
spec.version = Deploio::VERSION
|
|
8
|
+
spec.authors = ["Renuo AG"]
|
|
9
|
+
spec.email = ["info@renuo.ch"]
|
|
10
|
+
|
|
11
|
+
spec.summary = "CLI for Deploio"
|
|
12
|
+
spec.description = "A Ruby CLI that provides an interface for managing Deploio applications."
|
|
13
|
+
spec.homepage = "https://github.com/renuo/deploio-cli"
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
spec.required_ruby_version = ">= 3.0.0"
|
|
16
|
+
|
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
18
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
|
19
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
|
20
|
+
|
|
21
|
+
spec.files = Dir.chdir(__dir__) do
|
|
22
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
23
|
+
(File.expand_path(f) == __FILE__) ||
|
|
24
|
+
f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile])
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
spec.bindir = "bin"
|
|
28
|
+
spec.executables = ["deploio"]
|
|
29
|
+
spec.require_paths = ["lib"]
|
|
30
|
+
|
|
31
|
+
spec.add_dependency "thor", "~> 1.3"
|
|
32
|
+
spec.add_dependency "tty-table", "~> 0.12"
|
|
33
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "did_you_mean"
|
|
4
|
+
|
|
5
|
+
# AppRef represents a reference to a deploio application.
|
|
6
|
+
module Deploio
|
|
7
|
+
class AppRef
|
|
8
|
+
attr_reader :project_name, :app_name
|
|
9
|
+
|
|
10
|
+
# The input is given in the format "<project>-<app>"
|
|
11
|
+
def initialize(input, available_apps: {})
|
|
12
|
+
@input = input.to_s
|
|
13
|
+
parse_from_available_apps(available_apps)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def full_name
|
|
17
|
+
"#{project_name}-#{app_name}"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def to_s
|
|
21
|
+
full_name
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def ==(other)
|
|
25
|
+
return false unless other.is_a?(AppRef)
|
|
26
|
+
|
|
27
|
+
project_name == other.project_name && app_name == other.app_name
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def parse_from_available_apps(available_apps)
|
|
33
|
+
if available_apps.key?(@input)
|
|
34
|
+
match = available_apps[@input]
|
|
35
|
+
@project_name = match[:project_name]
|
|
36
|
+
@app_name = match[:app_name]
|
|
37
|
+
return
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# If available_apps provided but no match, raise error with suggestions
|
|
41
|
+
raise_not_found_error(@input, available_apps.keys) unless available_apps.empty?
|
|
42
|
+
|
|
43
|
+
raise_not_found_error(@input, [])
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def raise_not_found_error(input, available_app_names)
|
|
47
|
+
message = "App not found: '#{input}'"
|
|
48
|
+
|
|
49
|
+
suggestions = suggest_similar(input, available_app_names)
|
|
50
|
+
unless suggestions.empty?
|
|
51
|
+
message += "\n\nDid you mean?"
|
|
52
|
+
suggestions.each { |s| message += "\n #{s}" }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
message += "\n\nRun 'deploio apps' to see available apps."
|
|
56
|
+
|
|
57
|
+
raise Deploio::AppNotFoundError, message
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def suggest_similar(input, dictionary)
|
|
61
|
+
return [] if dictionary.empty?
|
|
62
|
+
|
|
63
|
+
spell_checker = DidYouMean::SpellChecker.new(dictionary: dictionary)
|
|
64
|
+
spell_checker.correct(input)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deploio
|
|
4
|
+
class AppResolver
|
|
5
|
+
attr_reader :nctl, :git_remote_url, :current_org
|
|
6
|
+
|
|
7
|
+
def initialize(nctl_client:)
|
|
8
|
+
@nctl = nctl_client
|
|
9
|
+
@git_remote_url = Utils.detect_git_remote
|
|
10
|
+
@current_org = @nctl.current_org
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def resolve(app_name: nil)
|
|
14
|
+
# 1. Explicit --app flag
|
|
15
|
+
if app_name
|
|
16
|
+
return AppRef.new(app_name, available_apps: available_apps_hash)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# 2. Match git remote against nctl apps
|
|
20
|
+
if git_remote_url
|
|
21
|
+
matches = find_apps_by_git_remote(git_remote_url)
|
|
22
|
+
|
|
23
|
+
case matches.size
|
|
24
|
+
when 0
|
|
25
|
+
raise Deploio::AppNotFoundError,
|
|
26
|
+
"No deploio apps found matching git remote: #{git_remote_url}\n" \
|
|
27
|
+
"Use --app to specify the app explicitly.\n" \
|
|
28
|
+
"Run 'deploio apps' to see available apps."
|
|
29
|
+
when 1
|
|
30
|
+
return build_app_ref_from_match(matches.first)
|
|
31
|
+
else
|
|
32
|
+
app_names = matches.map { |m| format_match(m) }
|
|
33
|
+
raise Deploio::Error,
|
|
34
|
+
"Multiple apps found for this repo. Use --app to specify which one:\n" \
|
|
35
|
+
"#{app_names.map { |name| " #{name}" }.join("\n")}"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
raise Deploio::Error,
|
|
40
|
+
"No app specified. Use --app <project-app> or run from a git repo with a matching remote."
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Returns hash mapping app names -> {project_name:, app_name:}
|
|
44
|
+
# Supports both full names (org-project-app) and short names (project-app)
|
|
45
|
+
def available_apps_hash
|
|
46
|
+
@available_apps_hash ||= begin
|
|
47
|
+
hash = {}
|
|
48
|
+
current_org = @nctl.current_org
|
|
49
|
+
@nctl.get_all_apps.each do |app|
|
|
50
|
+
metadata = app["metadata"] || {}
|
|
51
|
+
project_name = metadata["namespace"] || ""
|
|
52
|
+
app_name = metadata["name"]
|
|
53
|
+
full_name = "#{project_name}-#{app_name}"
|
|
54
|
+
hash[full_name] = {project_name: project_name, app_name: app_name}
|
|
55
|
+
|
|
56
|
+
# Also index by short name (without org prefix) for convenience
|
|
57
|
+
if current_org && project_name.start_with?("#{current_org}-")
|
|
58
|
+
project = project_name.delete_prefix("#{current_org}-")
|
|
59
|
+
short_name = "#{project}-#{app_name}"
|
|
60
|
+
hash[short_name] ||= {project_name: project_name, app_name: app_name}
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
hash
|
|
64
|
+
end
|
|
65
|
+
rescue
|
|
66
|
+
{}
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def short_name_for(namespace, app_name)
|
|
70
|
+
org = current_org
|
|
71
|
+
if org && namespace.start_with?("#{org}-")
|
|
72
|
+
project = namespace.delete_prefix("#{org}-")
|
|
73
|
+
"#{project}-#{app_name}"
|
|
74
|
+
else
|
|
75
|
+
"#{namespace}-#{app_name}"
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
def find_apps_by_git_remote(git_url)
|
|
82
|
+
normalized_url = normalize_git_url(git_url)
|
|
83
|
+
@nctl.get_all_apps.select do |app|
|
|
84
|
+
app_git_url = app.dig("spec", "forProvider", "git", "url")
|
|
85
|
+
normalize_git_url(app_git_url) == normalized_url
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def normalize_git_url(url)
|
|
90
|
+
return nil if url.nil?
|
|
91
|
+
|
|
92
|
+
url.strip
|
|
93
|
+
.sub(/\.git$/, "")
|
|
94
|
+
.sub(%r{^https://github\.com/}, "git@github.com:")
|
|
95
|
+
.downcase
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def build_app_ref_from_match(match)
|
|
99
|
+
metadata = match["metadata"] || {}
|
|
100
|
+
namespace = metadata["namespace"] || ""
|
|
101
|
+
name = metadata["name"]
|
|
102
|
+
AppRef.new("#{namespace}-#{name}", available_apps: available_apps_hash)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def format_match(match)
|
|
106
|
+
metadata = match["metadata"] || {}
|
|
107
|
+
namespace = metadata["namespace"] || ""
|
|
108
|
+
name = metadata["name"]
|
|
109
|
+
short_name_for(namespace, name)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
data/lib/deploio/cli.rb
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "thor"
|
|
4
|
+
require_relative "commands/auth"
|
|
5
|
+
require_relative "commands/apps"
|
|
6
|
+
require_relative "commands/orgs"
|
|
7
|
+
require_relative "completion_generator"
|
|
8
|
+
|
|
9
|
+
module Deploio
|
|
10
|
+
class CLI < Thor
|
|
11
|
+
include SharedOptions
|
|
12
|
+
|
|
13
|
+
desc "version", "Show version"
|
|
14
|
+
def version
|
|
15
|
+
puts "deploio-cli #{Deploio::VERSION}"
|
|
16
|
+
end
|
|
17
|
+
map %w[-v --version] => :version
|
|
18
|
+
|
|
19
|
+
desc "completion", "Generate shell completion script"
|
|
20
|
+
method_option :shell, aliases: "-s", type: :string, default: "zsh",
|
|
21
|
+
desc: "Shell type (zsh, bash, fish)"
|
|
22
|
+
def completion
|
|
23
|
+
case options[:shell].downcase
|
|
24
|
+
when "zsh"
|
|
25
|
+
puts CompletionGenerator.new.generate
|
|
26
|
+
when "bash", "fish"
|
|
27
|
+
Output.error("#{options[:shell]} completion is not yet supported. Only zsh is available.")
|
|
28
|
+
exit 1
|
|
29
|
+
else
|
|
30
|
+
Output.error("Unknown shell: #{options[:shell]}. Supported: zsh")
|
|
31
|
+
exit 1
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
desc "auth COMMAND", "Authentication commands"
|
|
36
|
+
subcommand "auth", Commands::Auth
|
|
37
|
+
|
|
38
|
+
desc "apps COMMAND", "Apps management commands"
|
|
39
|
+
subcommand "apps", Commands::Apps
|
|
40
|
+
desc "orgs COMMAND", "Organization management commands"
|
|
41
|
+
subcommand "orgs", Commands::Orgs
|
|
42
|
+
|
|
43
|
+
# Shortcut for auth:login
|
|
44
|
+
desc "login", "Authenticate with nctl (alias for auth:login)"
|
|
45
|
+
def login
|
|
46
|
+
Commands::Auth.start(["login"] + build_option_args)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Shortcut for auth:whoami
|
|
50
|
+
desc "whoami", "Show current user (alias for auth:whoami)"
|
|
51
|
+
def whoami
|
|
52
|
+
Commands::Auth.start(["whoami"] + build_option_args)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Shortcut for auth:logout
|
|
56
|
+
desc "logout", "Log out from nctl (alias for auth:logout)"
|
|
57
|
+
def logout
|
|
58
|
+
Commands::Auth.start(["logout"] + build_option_args)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Logs command
|
|
62
|
+
desc "logs", "Show logs for an app"
|
|
63
|
+
method_option :tail, aliases: "-t", type: :boolean, default: false, desc: "Stream logs continuously"
|
|
64
|
+
method_option :lines, aliases: "-n", type: :numeric, default: 100, desc: "Number of lines to show"
|
|
65
|
+
def logs
|
|
66
|
+
setup_options
|
|
67
|
+
app_ref = resolve_app
|
|
68
|
+
@nctl.logs(app_ref, tail: options[:tail], lines: options[:lines])
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Exec command
|
|
72
|
+
desc "exec [-- COMMAND]", "Run command in app container"
|
|
73
|
+
def exec(*args)
|
|
74
|
+
setup_options
|
|
75
|
+
app_ref = resolve_app
|
|
76
|
+
if args.empty?
|
|
77
|
+
Output.error("No command specified. Usage: deploio exec -a APP -- COMMAND")
|
|
78
|
+
exit 1
|
|
79
|
+
end
|
|
80
|
+
@nctl.exec_command(app_ref, args)
|
|
81
|
+
end
|
|
82
|
+
map "run" => :exec
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
def build_option_args
|
|
87
|
+
args = []
|
|
88
|
+
args << "--dry-run" if options[:dry_run]
|
|
89
|
+
args << "--no-color" if options[:no_color]
|
|
90
|
+
args << "--app" << options[:app] if options[:app]
|
|
91
|
+
args << "--org" << options[:org] if options[:org]
|
|
92
|
+
args
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deploio
|
|
4
|
+
module Commands
|
|
5
|
+
class Apps < Thor
|
|
6
|
+
include SharedOptions
|
|
7
|
+
|
|
8
|
+
namespace "apps"
|
|
9
|
+
|
|
10
|
+
class_option :json, type: :boolean, default: false, desc: "Output as JSON"
|
|
11
|
+
|
|
12
|
+
default_task :list
|
|
13
|
+
|
|
14
|
+
desc "list", "List all apps"
|
|
15
|
+
def list
|
|
16
|
+
setup_options
|
|
17
|
+
raw_apps = @nctl.get_all_apps
|
|
18
|
+
|
|
19
|
+
if options[:json]
|
|
20
|
+
puts JSON.pretty_generate(raw_apps)
|
|
21
|
+
return
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
if raw_apps.empty?
|
|
25
|
+
Output.warning("No apps found") unless merged_options[:dry_run]
|
|
26
|
+
return
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
resolver = AppResolver.new(nctl_client: @nctl)
|
|
30
|
+
|
|
31
|
+
rows = raw_apps.map do |app|
|
|
32
|
+
metadata = app["metadata"] || {}
|
|
33
|
+
spec = app["spec"] || {}
|
|
34
|
+
for_provider = spec["forProvider"] || {}
|
|
35
|
+
git = for_provider["git"] || {}
|
|
36
|
+
config = for_provider["config"] || {}
|
|
37
|
+
namespace = metadata["namespace"] || ""
|
|
38
|
+
name = metadata["name"] || ""
|
|
39
|
+
|
|
40
|
+
[
|
|
41
|
+
resolver.short_name_for(namespace, name),
|
|
42
|
+
project_from_namespace(namespace, resolver.current_org),
|
|
43
|
+
presence(config["size"], default: "micro"),
|
|
44
|
+
presence(git["revision"])
|
|
45
|
+
]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
Output.table(rows, headers: %w[APP PROJECT SIZE REVISION])
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
desc "info", "Show app details"
|
|
52
|
+
def info
|
|
53
|
+
setup_options
|
|
54
|
+
app_ref = resolve_app
|
|
55
|
+
data = @nctl.get_app(app_ref)
|
|
56
|
+
|
|
57
|
+
if options[:json]
|
|
58
|
+
puts JSON.pretty_generate(data)
|
|
59
|
+
return
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
display_app_info(data, app_ref)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def display_app_info(data, app_ref)
|
|
68
|
+
metadata = data["metadata"] || {}
|
|
69
|
+
spec = data["spec"] || {}
|
|
70
|
+
status = data["status"] || {}
|
|
71
|
+
for_provider = spec["forProvider"] || {}
|
|
72
|
+
git = for_provider["git"] || {}
|
|
73
|
+
config = for_provider["config"] || {}
|
|
74
|
+
build_env = for_provider["buildEnv"] || []
|
|
75
|
+
at_provider = status["atProvider"] || {}
|
|
76
|
+
|
|
77
|
+
Output.header("App: #{app_ref.full_name}")
|
|
78
|
+
puts
|
|
79
|
+
|
|
80
|
+
Output.header("General")
|
|
81
|
+
Output.table([
|
|
82
|
+
["Name", presence(metadata["name"])],
|
|
83
|
+
["Project", presence(metadata["namespace"])],
|
|
84
|
+
["Size", presence(config["size"], default: "micro")],
|
|
85
|
+
["Replicas", presence(for_provider["replicas"], default: "1")],
|
|
86
|
+
["Port", presence(config["port"], default: "8080")]
|
|
87
|
+
])
|
|
88
|
+
|
|
89
|
+
puts
|
|
90
|
+
|
|
91
|
+
Output.header("Status")
|
|
92
|
+
conditions = status["conditions"] || []
|
|
93
|
+
ready_condition = conditions.find { |c| c["type"] == "Ready" }
|
|
94
|
+
synced_condition = conditions.find { |c| c["type"] == "Synced" }
|
|
95
|
+
|
|
96
|
+
Output.table([
|
|
97
|
+
["Ready", presence(ready_condition&.dig("status"))],
|
|
98
|
+
["Synced", presence(synced_condition&.dig("status"))],
|
|
99
|
+
["Default URL", presence(at_provider["defaultURL"])],
|
|
100
|
+
["Latest Build", presence(at_provider["latestBuild"])],
|
|
101
|
+
["Latest Release", presence(at_provider["latestRelease"])]
|
|
102
|
+
])
|
|
103
|
+
|
|
104
|
+
puts
|
|
105
|
+
|
|
106
|
+
hosts = for_provider["hosts"] || []
|
|
107
|
+
if hosts.any?
|
|
108
|
+
Output.header("Hosts")
|
|
109
|
+
Output.table(hosts.map { |h| [presence(h)] })
|
|
110
|
+
puts
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
Output.header("Git")
|
|
114
|
+
Output.table([
|
|
115
|
+
["Repository", presence(git["url"])],
|
|
116
|
+
["Revision", presence(git["revision"])],
|
|
117
|
+
["Sub Path", presence(git["subPath"])]
|
|
118
|
+
])
|
|
119
|
+
puts
|
|
120
|
+
|
|
121
|
+
if build_env.any? || for_provider["dockerfilePath"]
|
|
122
|
+
Output.header("Build")
|
|
123
|
+
rows = []
|
|
124
|
+
rows << ["Dockerfile", presence(for_provider["dockerfilePath"])] if for_provider["dockerfilePath"]
|
|
125
|
+
build_env.each do |env|
|
|
126
|
+
rows << [env["name"], presence(env["value"], max_length: 60)]
|
|
127
|
+
end
|
|
128
|
+
Output.table(rows, headers: %w[SETTING VALUE]) if rows.any?
|
|
129
|
+
puts
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def presence(value, default: "-", max_length: nil)
|
|
134
|
+
return default if value.nil? || value.to_s.empty?
|
|
135
|
+
|
|
136
|
+
str = value.to_s
|
|
137
|
+
(max_length && str.length > max_length) ? "#{str[0, max_length - 3]}..." : str
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def project_from_namespace(namespace, current_org)
|
|
141
|
+
if current_org && namespace.start_with?("#{current_org}-")
|
|
142
|
+
namespace.delete_prefix("#{current_org}-")
|
|
143
|
+
else
|
|
144
|
+
namespace
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Deploio
|
|
4
|
+
module Commands
|
|
5
|
+
class Auth < Thor
|
|
6
|
+
include SharedOptions
|
|
7
|
+
|
|
8
|
+
namespace "auth"
|
|
9
|
+
|
|
10
|
+
desc "login", "Authenticate with nctl"
|
|
11
|
+
def login
|
|
12
|
+
setup_options
|
|
13
|
+
@nctl.auth_login
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
desc "logout", "Log out from nctl"
|
|
17
|
+
def logout
|
|
18
|
+
setup_options
|
|
19
|
+
@nctl.auth_logout
|
|
20
|
+
Output.success("Logged out successfully")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
desc "whoami", "Show current user and organization"
|
|
24
|
+
def whoami
|
|
25
|
+
setup_options
|
|
26
|
+
@nctl.auth_whoami
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|