cpl 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2392653f1a75e0dc398590cc5c0e9d89d4769a50af3432b2861e01ff794e8cda
4
- data.tar.gz: 13c33cc6465394b90b1fd8e94f711c98135bfdcb25756db7d3b7b0c8bfc819fa
3
+ metadata.gz: 940fc95a21ea2771a453b12e646d182ed42ef448fd83081ac822af26d1820ad3
4
+ data.tar.gz: 4405dd3555275a1bbe135d52abaf072ed683cb183d0ee36e0060c38262ca8017
5
5
  SHA512:
6
- metadata.gz: bc2b5f2555b8f329ad8d5b2be9f5762e6f265f499baaa035e65c24257b47c26459842f2611ee8d352af65b3dcabf4f0e6570007c59f5ca543f3edb382ed04747
7
- data.tar.gz: 904028e00550311c8b9327fb3a17a8a00c75b4806e81ca8359d5b3f6ae55b19f8a89efbc3ccc087e4f62e2243930587df1f693ebbbac9470377b0e6905e0dd19
6
+ metadata.gz: 579a48aff74172dfe8ac8652e3538006d897ad2ce82459869227889d1e294595c0a698f846f2fe9db3970950e1f0372a6149f75ce06f6a2ab526fa451fea6649
7
+ data.tar.gz: 26581dbe132fea73f6066ba4a030738a1b08978e17d02fe6ba95ebed74d879f25a103ce5ddbeff8ba33c4508d7b302fc9f28c0d822f77c423c74c07cdf95006b
@@ -1,4 +1,4 @@
1
- name: CI
1
+ name: RSpec
2
2
 
3
3
  on:
4
4
  push:
@@ -7,29 +7,7 @@ on:
7
7
  pull_request:
8
8
 
9
9
  jobs:
10
- rubocop:
11
- runs-on: ubuntu-latest
12
- name: Rubocop
13
- strategy:
14
- matrix:
15
- ruby:
16
- - "2.7"
17
- - "3.0"
18
- steps:
19
- - name: Checkout code
20
- uses: actions/checkout@v3
21
- - name: Set up Ruby
22
- uses: ruby/setup-ruby@v1
23
- with:
24
- ruby-version: ${{ matrix.ruby }}
25
- rubygems: latest
26
- - name: Install dependencies
27
- run: bundle install
28
- - name: Analyze code
29
- run: bundle exec rubocop
30
-
31
10
  rspec:
32
- needs: rubocop
33
11
  runs-on: ubuntu-latest
34
12
  name: RSpec
35
13
  env:
@@ -46,8 +24,7 @@ jobs:
46
24
  uses: ruby/setup-ruby@v1
47
25
  with:
48
26
  ruby-version: ${{ matrix.ruby }}
49
- rubygems: latest
50
- bundler-cache: false
27
+ bundler-cache: true
51
28
  - name: Install dependencies
52
29
  run: bundle install
53
30
  - name: Run tests
@@ -0,0 +1,29 @@
1
+ name: Rubocop
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request:
8
+
9
+ jobs:
10
+ rubocop:
11
+ runs-on: ubuntu-latest
12
+ name: Rubocop
13
+ strategy:
14
+ matrix:
15
+ ruby:
16
+ - "2.7"
17
+ - "3.0"
18
+ steps:
19
+ - name: Checkout code
20
+ uses: actions/checkout@v3
21
+ - name: Set up Ruby
22
+ uses: ruby/setup-ruby@v1
23
+ with:
24
+ ruby-version: ${{ matrix.ruby }}
25
+ bundler-cache: true
26
+ - name: Install dependencies
27
+ run: bundle install
28
+ - name: Analyze code
29
+ run: bundle exec rubocop
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
- ## [Unreleased]
1
+ # Changelog
2
2
 
3
- ## [0.1.0] - 2023-02-08
3
+ ## 0.1.1 - 2023-03-09
4
+
5
+ - Fixed issue with default gems for older Ruby versions (#14)
6
+
7
+ ## 0.1.0 - 2023-02-19
4
8
 
5
9
  - Initial release
data/CONTRIBUTING.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Contributing
2
2
 
3
+ ## Installation
4
+ Rather than installing `cpl` as a Ruby gem, install this repo locally and alias `cpl` command globally for easier access, e.g.:
5
+
6
+ ```sh
7
+ git clone https://github.com/shakacode/heroku-to-control-plane
8
+
9
+ # Create an alias in some local shell startup script, e.g., `.profile`, `.bashrc`, etc.
10
+ alias cpl="~/projects/heroku-to-control-plane/cpl"
11
+ ```
12
+
13
+ Or set the path of the Ruby gem in your Gemfile.
14
+
15
+ ```ruby
16
+ gem 'cpl', path: '~/projects/heroku-to-control-plane'
17
+ ```
18
+
3
19
  ## Linting
4
20
  Be sure to run `rubocop -a` before committing code.
5
21
 
data/Gemfile.lock CHANGED
@@ -1,23 +1,16 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cpl (0.1.0)
5
- cgi (~> 0.3.6)
4
+ cpl (0.1.1)
6
5
  debug (~> 1.7.1)
7
6
  dotenv (~> 2.8.1)
8
- json (~> 2.6.3)
9
- net-http (~> 0.3.2)
10
- pathname (~> 0.2.1)
11
7
  psych (~> 5.1.0)
12
- tempfile (~> 0.1.3)
13
8
  thor (~> 1.2.1)
14
- yaml (~> 0.2.1)
15
9
 
16
10
  GEM
17
11
  remote: https://rubygems.org/
18
12
  specs:
19
13
  ast (2.4.2)
20
- cgi (0.3.6)
21
14
  debug (1.7.1)
22
15
  irb (>= 1.5.0)
23
16
  reline (>= 0.3.1)
@@ -28,12 +21,9 @@ GEM
28
21
  irb (1.6.2)
29
22
  reline (>= 0.3.0)
30
23
  json (2.6.3)
31
- net-http (0.3.2)
32
- uri
33
24
  parallel (1.22.1)
34
25
  parser (3.2.0.0)
35
26
  ast (~> 2.4.1)
36
- pathname (0.2.1)
37
27
  psych (5.1.0)
38
28
  stringio
39
29
  rainbow (3.1.1)
@@ -82,11 +72,8 @@ GEM
82
72
  simplecov-html (0.12.3)
83
73
  simplecov_json_formatter (0.1.4)
84
74
  stringio (3.0.5)
85
- tempfile (0.1.3)
86
75
  thor (1.2.1)
87
76
  unicode-display_width (2.4.2)
88
- uri (0.12.0)
89
- yaml (0.2.1)
90
77
 
91
78
  PLATFORMS
92
79
  x86_64-linux
data/README.md CHANGED
@@ -1,7 +1,12 @@
1
- # Heroku to Control Plane
1
+ # Heroku to Control Plane `cpl` CLI
2
2
 
3
3
  _A playbook for migrating from [Heroku](https://heroku.com) to [Control Plane](https://controlplane.com)_
4
4
 
5
+ [![RSpec](https://github.com/shakacode/heroku-to-control-plane/actions/workflows/rspec.yml/badge.svg)](https://github.com/shakacode/heroku-to-control-plane/actions/workflows/rspec.yml)
6
+ [![Rubocop](https://github.com/shakacode/heroku-to-control-plane/actions/workflows/rubocop.yml/badge.svg)](https://github.com/shakacode/heroku-to-control-plane/actions/workflows/rubocop.yml)
7
+
8
+ [![Gem](https://badge.fury.io/rb/cpl.svg)](https://badge.fury.io/rb/cpl)
9
+
5
10
  This playbook shows how to move "Heroku apps" to "Control Plane workloads" via an open-source `cpl` CLI on top of Control Plane's `cpln` CLI.
6
11
 
7
12
  Heroku provides a UX and CLI that enables easy publishing of Ruby on Rails and other apps. This ease of use comes via many "Heroku" abstractions and naming conventions.
@@ -20,8 +25,8 @@ To simplify migration to and usage of Control Plane for Heroku users, this repos
20
25
  9. [CLI commands reference](#cli-commands-reference)
21
26
  10. [Mapping of Heroku Commands to `cpl` and `cpln`](#mapping-of-heroku-commands-to-cpl-and-cpln)
22
27
  11. [Examples](#examples)
23
- 12. [Migrating Postgres database from Heroku infrastructure](/postgres.md)
24
- 13. [Migrating Redis database from Heroku infrastructure](/redis.md)
28
+ 12. [Migrating Postgres database from Heroku infrastructure](/docs/postgres.md)
29
+ 13. [Migrating Redis database from Heroku infrastructure](/docs/redis.md)
25
30
 
26
31
  ## Key features
27
32
 
@@ -69,25 +74,20 @@ For the typical Rails app, this means:
69
74
 
70
75
  ## Installation
71
76
 
72
- **Note:** `cpl` CLI is configured via a local clone clone of this repo. We may publish it later as a Ruby gem or Node package.
77
+ **Note:** `cpl` CLI is configured either a Ruby gem, [`cpl`](https://rubygems.org/gems/cpl) install or a local clone clone. For information on the latter, see [CONTRIBUTING.md](CONTRIBUTING.md).
73
78
 
74
79
  1. Install `node` (required for Control Plane CLI).
75
80
  2. Install `ruby` (required for these helpers).
76
- 3. Install Control Plane CLI (adds `cpln` command) and configure credentials.
81
+ 3. Install Control Plane CLI (adds `cpln` command) and configure credentials by running command `cpln login`.
77
82
 
78
83
  ```sh
79
84
  npm install -g @controlplane/cli
80
85
  cpln login
81
86
  ```
82
87
 
83
- 4. Install this repo locally and alias `cpl` command globally for easier access, e.g.:
88
+ ## Tips
89
+ Do not confuse the `cpl` CLI with the `cpln` CLI. The `cpl` CLI is the Heroku to Control Plane playbook CLI. The `cpln` CLI is the Control Plane CLI.
84
90
 
85
- ```sh
86
- git clone https://github.com/shakacode/heroku-to-control-plane
87
-
88
- # Create an alias in some local shell startup script, e.g., `.profile`, `.bashrc`, etc.
89
- alias cpl="~/projects/heroku-to-control-plane/cpl"
90
- ```
91
91
 
92
92
  - For each Git project that you want to deploy to Control Plane, copy project-specific configs to a `.controlplane` directory at the top of your project. `cpl` will pick those up depending on which project
93
93
  folder tree it runs. Thus, this automates running several projects with different configs without explicitly switching configs.
@@ -101,6 +101,8 @@ alias cpl="~/projects/heroku-to-control-plane/cpl"
101
101
  1. `myapp` is an app name defined in the `.controlplane/controlplane.yml` file, such as `ror-tutorial` in [this `controlplane.yml` file](https://github.com/shakacode/react-webpack-rails-tutorial/blob/master/.controlplane/controlplane.yml).
102
102
  2. Other files in the `.controlplane/templates` directory are used by the `cpl setup` command.
103
103
 
104
+ ### Initial Setup and Deployment
105
+
104
106
  ```sh
105
107
  # Provision infrastructure (one-time-only for new apps) using templates.
106
108
  # Note how the arguments correspond to files in the `.controlplane/templates` directory.
@@ -119,6 +121,26 @@ cpl promote-image -a myapp
119
121
  cpl open -a myapp
120
122
  ```
121
123
 
124
+ ### Promoting code upgrades
125
+
126
+ ```sh
127
+ # Build and push new image with sequential image tagging, e.g. 'ror-tutorial_123'
128
+ cpl build-image -a ror-tutorial
129
+
130
+ # OR
131
+ # Build and push with sequential image tagging and commit SHA, e.g. 'ror-tutorial_123_ABCD'
132
+ cpl build-image -a ror-tutorial --commit ABCD
133
+
134
+ # Run database migrations (or other release tasks) with latest image,
135
+ # while app is still running on previous image.
136
+ # This is analogous to the release phase.
137
+ cpl runner rails db:migrate -a ror-tutorial --image latest
138
+
139
+ # Pomote latest image to app
140
+ cpl promote-image -a ror-tutorial
141
+ ```
142
+
143
+
122
144
  ## Example project modifications for Control Plane
123
145
 
124
146
  _See this for a complete example._
@@ -300,17 +322,17 @@ cpl --help
300
322
 
301
323
  **`[WIP]`**
302
324
 
303
- | Heroku Command | `cpl` or `cpln` |
304
- | ---------------------------------------------------------------------------------------------------------------- | --------------- |
305
- | `[heroku ps](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-ps-type-type)` | `cpl ps` |
306
- | `[heroku config](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-config)` | ? |
307
- | `[heroku maintenance](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-maintenance)` | ? |
308
- | `[heroku logs](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-logs)` | `cpl logs` |
309
- | `[heroku pg](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-pg-database)` | ? |
310
- | `[heroku pipelines:promote](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-pipelines-promote)` | `cpl promote` |
311
- | `[heroku psql](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-psql-database)` | ? |
312
- | `[heroku redis](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-redis-database)` | ? |
313
- | `[heroku releases](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-releases)` | ? |
325
+ | Heroku Command | `cpl` or `cpln` |
326
+ | -------------------------------------------------------------------------------------------------------------- | --------------- |
327
+ | [heroku ps](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-ps-type-type) | `cpl ps` |
328
+ | [heroku config](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-config) | ? |
329
+ | [heroku maintenance](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-maintenance) | ? |
330
+ | [heroku logs](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-logs) | `cpl logs` |
331
+ | [heroku pg](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-pg-database) | ? |
332
+ | [heroku pipelines:promote](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-pipelines-promote) | `cpl promote` |
333
+ | [heroku psql](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-psql-database) | ? |
334
+ | [heroku redis](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-redis-database) | ? |
335
+ | [heroku releases](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-releases) | ? |
314
336
 
315
337
  ## Examples
316
338
 
data/Rakefile CHANGED
@@ -9,3 +9,8 @@ RSpec::Core::RakeTask.new(:spec)
9
9
  RuboCop::RakeTask.new
10
10
 
11
11
  task default: %i[spec rubocop]
12
+
13
+ desc "Updates commands.md file"
14
+ task :command_docs do
15
+ sh "./script/generate_commands_docs"
16
+ end
data/cpl.gemspec CHANGED
@@ -15,16 +15,10 @@ Gem::Specification.new do |spec|
15
15
 
16
16
  spec.required_ruby_version = ">= 2.7.0"
17
17
 
18
- spec.add_dependency "cgi", "~> 0.3.6"
19
18
  spec.add_dependency "debug", "~> 1.7.1"
20
19
  spec.add_dependency "dotenv", "~> 2.8.1"
21
- spec.add_dependency "json", "~> 2.6.3"
22
- spec.add_dependency "net-http", "~> 0.3.2"
23
- spec.add_dependency "pathname", "~> 0.2.1"
24
20
  spec.add_dependency "psych", "~> 5.1.0"
25
- spec.add_dependency "tempfile", "~> 0.1.3"
26
21
  spec.add_dependency "thor", "~> 1.2.1"
27
- spec.add_dependency "yaml", "~> 0.2.1"
28
22
 
29
23
  spec.add_development_dependency "rspec", "~> 3.12.0"
30
24
  spec.add_development_dependency "rubocop", "~> 1.45.0"
data/docs/commands.md CHANGED
@@ -21,6 +21,29 @@ This `-a` option is used in most of the commands and will pick all other app con
21
21
  cpl build-image -a $APP_NAME
22
22
  ```
23
23
 
24
+ ### `cleanup-old-images`
25
+
26
+ - Deletes all images for an app that are older than the specified amount of days
27
+ - Specify the amount of days through `old_image_retention_days` in the `.controlplane/controlplane.yml` file
28
+ - Will ask for explicit user confirmation
29
+ - Does not affect the latest image, regardless of how old it is
30
+
31
+ ```sh
32
+ cpl cleanup-old-images -a $APP_NAME
33
+ ```
34
+
35
+ ### `cleanup-stale-apps`
36
+
37
+ - Deletes the whole app (GVC with all workloads and all images) for all stale apps
38
+ - Stale apps are identified based on the creation date of the latest image
39
+ - Specify the amount of days after an app should be considered stale through `stale_app_image_deployed_days` in the `.controlplane/controlplane.yml` file
40
+ - If `match_if_app_name_starts_with` is `true` in the `.controlplane/controlplane.yml` file, it will delete all stale apps that start with the name
41
+ - Will ask for explicit user confirmation
42
+
43
+ ```sh
44
+ cpl cleanup-stale-apps -a $APP_NAME
45
+ ```
46
+
24
47
  ### `config`
25
48
 
26
49
  - Displays current configs (global and app-specific)
data/lib/command/base.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Command
4
4
  class Base # rubocop:disable Metrics/ClassLength
5
- attr_reader :config
5
+ attr_reader :thor_shell, :config
6
6
 
7
7
  # Used to call the command (`cpl NAME`)
8
8
  # NAME = ""
@@ -27,6 +27,7 @@ module Command
27
27
  NO_IMAGE_AVAILABLE = "NO_IMAGE_AVAILABLE"
28
28
 
29
29
  def initialize(config)
30
+ @thor_shell = Thor::Shell::Color.new
30
31
  @config = config
31
32
  end
32
33
 
@@ -90,14 +91,27 @@ module Command
90
91
  }
91
92
  end
92
93
 
94
+ def self.skip_confirm_option(required: false)
95
+ {
96
+ name: :yes,
97
+ params: {
98
+ aliases: ["-y"],
99
+ banner: "SKIP_CONFIRM",
100
+ desc: "Skip confirmation",
101
+ type: :boolean,
102
+ required: required
103
+ }
104
+ }
105
+ end
106
+
93
107
  def self.all_options
94
108
  methods.grep(/_option$/).map { |method| send(method.to_s) }
95
109
  end
96
110
 
97
- def self.all_options_key_name
111
+ def self.all_options_by_key_name
98
112
  all_options.each_with_object({}) do |option, result|
99
- option[:params][:aliases].each { |current_alias| result[current_alias.to_s] = option[:name] }
100
- result["--#{option[:name]}"] = option[:name]
113
+ option[:params][:aliases].each { |current_alias| result[current_alias.to_s] = option }
114
+ result["--#{option[:name]}"] = option
101
115
  end
102
116
  end
103
117
 
@@ -125,20 +139,23 @@ module Command
125
139
  cp.workload_delete(workload, no_raise: true)
126
140
  end
127
141
 
128
- def latest_image # rubocop:disable Metrics/MethodLength
142
+ def latest_image_from(items, app_name: config.app, name_only: true)
143
+ matching_items = items.filter { |item| item["name"].start_with?("#{app_name}:") }
144
+
145
+ # Or special string to indicate no image available
146
+ if matching_items.empty?
147
+ "#{app_name}:#{NO_IMAGE_AVAILABLE}"
148
+ else
149
+ latest_item = matching_items.max_by { |item| extract_image_number(item["name"]) }
150
+ name_only ? latest_item["name"] : latest_item
151
+ end
152
+ end
153
+
154
+ def latest_image
129
155
  @latest_image ||=
130
156
  begin
131
157
  items = cp.image_query["items"]
132
- matching_items = items.filter_map do |item|
133
- item["name"] if item["name"].start_with?("#{config.app}:")
134
- end
135
-
136
- # Or special string to indicate no image available
137
- if matching_items.empty?
138
- "#{config.app}:#{NO_IMAGE_AVAILABLE}"
139
- else
140
- matching_items.max_by { |item| extract_image_number(item) }
141
- end
158
+ latest_image_from(items)
142
159
  end
143
160
  end
144
161
 
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Command
4
+ class CleanupOldImages < Base
5
+ NAME = "cleanup-old-images"
6
+ OPTIONS = [
7
+ app_option(required: true),
8
+ skip_confirm_option
9
+ ].freeze
10
+ DESCRIPTION = "Deletes all images for an app that are older than the specified amount of days"
11
+ LONG_DESCRIPTION = <<~HEREDOC
12
+ - Deletes all images for an app that are older than the specified amount of days
13
+ - Specify the amount of days through `old_image_retention_days` in the `.controlplane/controlplane.yml` file
14
+ - Will ask for explicit user confirmation
15
+ - Does not affect the latest image, regardless of how old it is
16
+ HEREDOC
17
+
18
+ def call
19
+ return progress.puts "No old images found" if old_images.empty?
20
+
21
+ progress.puts "Old images:"
22
+ old_images.each do |image|
23
+ progress.puts " #{image[:name]} (#{thor_shell.set_color((image[:date]).to_s, :red)})"
24
+ end
25
+
26
+ return unless confirm_delete
27
+
28
+ progress.puts
29
+ delete_images
30
+ end
31
+
32
+ private
33
+
34
+ def old_images # rubocop:disable Metrics/MethodLength
35
+ @old_images ||=
36
+ begin
37
+ result_images = []
38
+
39
+ now = DateTime.now
40
+ old_image_retention_days = config[:old_image_retention_days]
41
+
42
+ images = cp.image_query["items"].filter { |item| item["name"].start_with?("#{config.app}:") }
43
+
44
+ # Remove latest image, because we don't want to delete the image that is currently deployed
45
+ latest_image_name = latest_image_from(images)
46
+ images = images.filter { |item| item["name"] != latest_image_name }
47
+
48
+ images.each do |image|
49
+ created_date = DateTime.parse(image["created"])
50
+ diff_in_days = (now - created_date).to_i
51
+ next unless diff_in_days >= old_image_retention_days
52
+
53
+ result_images.push({
54
+ name: image["name"],
55
+ date: created_date
56
+ })
57
+ end
58
+
59
+ result_images
60
+ end
61
+ end
62
+
63
+ def confirm_delete
64
+ return true if config.options[:yes]
65
+
66
+ thor_shell.yes?("\nAre you sure you want to delete these #{old_images.length} images (y/n)?")
67
+ end
68
+
69
+ def delete_images
70
+ old_images.each do |image|
71
+ cp.image_delete(image[:name])
72
+ progress.puts "#{image[:name]} deleted"
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+
5
+ module Command
6
+ class CleanupStaleApps < Base
7
+ NAME = "cleanup-stale-apps"
8
+ OPTIONS = [
9
+ app_option(required: true),
10
+ skip_confirm_option
11
+ ].freeze
12
+ DESCRIPTION = "Deletes the whole app (GVC with all workloads and all images) for all stale apps"
13
+ LONG_DESCRIPTION = <<~HEREDOC
14
+ - Deletes the whole app (GVC with all workloads and all images) for all stale apps
15
+ - Stale apps are identified based on the creation date of the latest image
16
+ - Specify the amount of days after an app should be considered stale through `stale_app_image_deployed_days` in the `.controlplane/controlplane.yml` file
17
+ - If `match_if_app_name_starts_with` is `true` in the `.controlplane/controlplane.yml` file, it will delete all stale apps that start with the name
18
+ - Will ask for explicit user confirmation
19
+ HEREDOC
20
+
21
+ def call # rubocop:disable Metrics/MethodLength
22
+ return progress.puts "No stale apps found" if stale_apps.empty?
23
+
24
+ progress.puts "Stale apps:"
25
+ stale_apps.each do |app|
26
+ progress.puts " #{app[:name]} (#{thor_shell.set_color((app[:date]).to_s, :red)})"
27
+ end
28
+
29
+ return unless confirm_delete
30
+
31
+ progress.puts
32
+ stale_apps.each do |app|
33
+ delete_gvc(app)
34
+ delete_images(app)
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def stale_apps # rubocop:disable Metrics/MethodLength
41
+ @stale_apps ||=
42
+ begin
43
+ apps = []
44
+
45
+ now = DateTime.now
46
+ stale_app_image_deployed_days = config[:stale_app_image_deployed_days]
47
+
48
+ gvcs = cp.gvc_query(config.app)["items"]
49
+ gvcs.each do |gvc|
50
+ app_name = gvc["name"]
51
+
52
+ images = cp.image_query(app_name)["items"].filter { |item| item["name"].start_with?("#{app_name}:") }
53
+ image = latest_image_from(images, app_name: app_name, name_only: false)
54
+
55
+ created_date = DateTime.parse(image["created"])
56
+ diff_in_days = (now - created_date).to_i
57
+ next unless diff_in_days >= stale_app_image_deployed_days
58
+
59
+ apps.push({
60
+ name: app_name,
61
+ date: created_date,
62
+ images: images.map { |current_image| current_image["name"] }
63
+ })
64
+ end
65
+
66
+ apps
67
+ end
68
+ end
69
+
70
+ def confirm_delete
71
+ return true if config.options[:yes]
72
+
73
+ thor_shell.yes?("\nAre you sure you want to delete these #{stale_apps.length} apps (y/n)?")
74
+ end
75
+
76
+ def delete_gvc(app)
77
+ cp.gvc_delete(app[:name])
78
+ progress.puts "#{app[:name]} deleted"
79
+ end
80
+
81
+ def delete_images(app)
82
+ app[:images].each do |image|
83
+ cp.image_delete(image)
84
+ progress.puts "#{image} deleted"
85
+ end
86
+ end
87
+ end
88
+ end
@@ -4,7 +4,8 @@ module Command
4
4
  class Delete < Base
5
5
  NAME = "delete"
6
6
  OPTIONS = [
7
- app_option(required: true)
7
+ app_option(required: true),
8
+ skip_confirm_option
8
9
  ].freeze
9
10
  DESCRIPTION = "Deletes the whole app (GVC with all workloads and all images)"
10
11
  LONG_DESCRIPTION = <<~HEREDOC
@@ -13,10 +14,7 @@ module Command
13
14
  HEREDOC
14
15
 
15
16
  def call
16
- progress.puts "Type 'delete' to delete #{config.app} and images"
17
- progress.print "> "
18
-
19
- return progress.puts "Not confirmed" unless $stdin.gets.chomp == "delete"
17
+ return unless confirm_delete
20
18
 
21
19
  delete_gvc
22
20
  delete_images
@@ -24,6 +22,16 @@ module Command
24
22
 
25
23
  private
26
24
 
25
+ def confirm_delete
26
+ return true if config.options[:yes]
27
+
28
+ confirmed = thor_shell.yes?("Are you sure you want to delete '#{config.app}' (y/n)?")
29
+ return false unless confirmed
30
+
31
+ progress.puts
32
+ true
33
+ end
34
+
27
35
  def delete_gvc
28
36
  progress.puts "- Deleting gvc:"
29
37
 
@@ -18,8 +18,8 @@ class Controlplane
18
18
  perform(cmd)
19
19
  end
20
20
 
21
- def image_query
22
- cmd = "cpln image query --org #{org} -o yaml --max -1 --prop repository=#{config.app}"
21
+ def image_query(app_name = config.app)
22
+ cmd = "cpln image query --org #{org} -o yaml --max -1 --prop repository=#{app_name}"
23
23
  perform_yaml(cmd)
24
24
  end
25
25
 
@@ -29,6 +29,15 @@ class Controlplane
29
29
 
30
30
  # gvc
31
31
 
32
+ def gvc_query(app_name = config.app)
33
+ # When `match_if_app_name_starts_with` is `true`, we query for any gvc containing the name,
34
+ # otherwise we query for a gvc with the exact name.
35
+ op = config.current[:match_if_app_name_starts_with] ? "~" : "="
36
+
37
+ cmd = "cpln gvc query --org #{org} -o yaml --prop name#{op}#{app_name}"
38
+ perform_yaml(cmd)
39
+ end
40
+
32
41
  def gvc_get(a_gvc = gvc)
33
42
  api.gvc_get(gvc: a_gvc, org: org)
34
43
  end
data/lib/cpl/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cpl
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cpl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Gordon
@@ -9,22 +9,8 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2023-02-19 00:00:00.000000000 Z
12
+ date: 2023-03-10 00:00:00.000000000 Z
13
13
  dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: cgi
16
- requirement: !ruby/object:Gem::Requirement
17
- requirements:
18
- - - "~>"
19
- - !ruby/object:Gem::Version
20
- version: 0.3.6
21
- type: :runtime
22
- prerelease: false
23
- version_requirements: !ruby/object:Gem::Requirement
24
- requirements:
25
- - - "~>"
26
- - !ruby/object:Gem::Version
27
- version: 0.3.6
28
14
  - !ruby/object:Gem::Dependency
29
15
  name: debug
30
16
  requirement: !ruby/object:Gem::Requirement
@@ -53,48 +39,6 @@ dependencies:
53
39
  - - "~>"
54
40
  - !ruby/object:Gem::Version
55
41
  version: 2.8.1
56
- - !ruby/object:Gem::Dependency
57
- name: json
58
- requirement: !ruby/object:Gem::Requirement
59
- requirements:
60
- - - "~>"
61
- - !ruby/object:Gem::Version
62
- version: 2.6.3
63
- type: :runtime
64
- prerelease: false
65
- version_requirements: !ruby/object:Gem::Requirement
66
- requirements:
67
- - - "~>"
68
- - !ruby/object:Gem::Version
69
- version: 2.6.3
70
- - !ruby/object:Gem::Dependency
71
- name: net-http
72
- requirement: !ruby/object:Gem::Requirement
73
- requirements:
74
- - - "~>"
75
- - !ruby/object:Gem::Version
76
- version: 0.3.2
77
- type: :runtime
78
- prerelease: false
79
- version_requirements: !ruby/object:Gem::Requirement
80
- requirements:
81
- - - "~>"
82
- - !ruby/object:Gem::Version
83
- version: 0.3.2
84
- - !ruby/object:Gem::Dependency
85
- name: pathname
86
- requirement: !ruby/object:Gem::Requirement
87
- requirements:
88
- - - "~>"
89
- - !ruby/object:Gem::Version
90
- version: 0.2.1
91
- type: :runtime
92
- prerelease: false
93
- version_requirements: !ruby/object:Gem::Requirement
94
- requirements:
95
- - - "~>"
96
- - !ruby/object:Gem::Version
97
- version: 0.2.1
98
42
  - !ruby/object:Gem::Dependency
99
43
  name: psych
100
44
  requirement: !ruby/object:Gem::Requirement
@@ -109,20 +53,6 @@ dependencies:
109
53
  - - "~>"
110
54
  - !ruby/object:Gem::Version
111
55
  version: 5.1.0
112
- - !ruby/object:Gem::Dependency
113
- name: tempfile
114
- requirement: !ruby/object:Gem::Requirement
115
- requirements:
116
- - - "~>"
117
- - !ruby/object:Gem::Version
118
- version: 0.1.3
119
- type: :runtime
120
- prerelease: false
121
- version_requirements: !ruby/object:Gem::Requirement
122
- requirements:
123
- - - "~>"
124
- - !ruby/object:Gem::Version
125
- version: 0.1.3
126
56
  - !ruby/object:Gem::Dependency
127
57
  name: thor
128
58
  requirement: !ruby/object:Gem::Requirement
@@ -137,20 +67,6 @@ dependencies:
137
67
  - - "~>"
138
68
  - !ruby/object:Gem::Version
139
69
  version: 1.2.1
140
- - !ruby/object:Gem::Dependency
141
- name: yaml
142
- requirement: !ruby/object:Gem::Requirement
143
- requirements:
144
- - - "~>"
145
- - !ruby/object:Gem::Version
146
- version: 0.2.1
147
- type: :runtime
148
- prerelease: false
149
- version_requirements: !ruby/object:Gem::Requirement
150
- requirements:
151
- - - "~>"
152
- - !ruby/object:Gem::Version
153
- version: 0.2.1
154
70
  - !ruby/object:Gem::Dependency
155
71
  name: rspec
156
72
  requirement: !ruby/object:Gem::Requirement
@@ -230,7 +146,8 @@ executables:
230
146
  extensions: []
231
147
  extra_rdoc_files: []
232
148
  files:
233
- - ".github/workflows/ci.yml"
149
+ - ".github/workflows/rspec.yml"
150
+ - ".github/workflows/rubocop.yml"
234
151
  - ".gitignore"
235
152
  - ".rspec"
236
153
  - ".rubocop.yml"
@@ -245,11 +162,15 @@ files:
245
162
  - cpl
246
163
  - cpl.gemspec
247
164
  - docs/commands.md
165
+ - docs/postgres.md
166
+ - docs/redis.md
248
167
  - docs/troubleshooting.md
249
168
  - examples/circleci.yml
250
169
  - examples/controlplane.yml
251
170
  - lib/command/base.rb
252
171
  - lib/command/build_image.rb
172
+ - lib/command/cleanup_old_images.rb
173
+ - lib/command/cleanup_stale_apps.rb
253
174
  - lib/command/config.rb
254
175
  - lib/command/delete.rb
255
176
  - lib/command/env.rb
@@ -275,8 +196,6 @@ files:
275
196
  - lib/cpl.rb
276
197
  - lib/cpl/version.rb
277
198
  - lib/main.rb
278
- - postgres.md
279
- - redis.md
280
199
  - script/generate_commands_docs
281
200
  - templates/gvc.yml
282
201
  - templates/identity.yml
File without changes
File without changes