renuo-cli 4.29.1 → 5.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bd6c38dab2678be2dd22abe7ce47388faa20efaa270f7e2f2f4e09ef57d62ef4
4
- data.tar.gz: 3fc28c5af477aaa418a6c6211531dd29ea4153ce80d7e74f6b70386e0a4ddd71
3
+ metadata.gz: 8462a65e3075cb5e8a3f7c01a254ac225e81ce774ab53d16a1915883e6d4969a
4
+ data.tar.gz: 4985892734d7db16156a8139c325779c4d60f8763de9b7d690cac7b91503ce77
5
5
  SHA512:
6
- metadata.gz: 4d80152b88e3f9f8f202eb369d396f0c04785be52a4c9799a872a3dbb7d53085ee7ede4caf7264c15c75dbbf84be1510ca570bca105d97160e40617665bd974e
7
- data.tar.gz: 3cd1cd4b8699861f309690a82a04d09b9fd53c9587dbb53914dc73d040e4d21cedae12e159db6af167bfafaac2d2939c13efb853bd46c41b4093023d6e40e001
6
+ metadata.gz: 1dfd2825a7348c1ca48eb9a337aa5bb2d2c217c36e2653914fec2a3db8cc8c0a034aebe486142809895f75e7c0442fd41e78224173b4bd82460e66550bdc798b
7
+ data.tar.gz: eb01997fbb165adaaf5a904306b379ae1875098d16e65d5b3b62bb1396527c71680aa69ddc604cfcf43270e12ce83f46786c7e638e8429fb3026ea6d80f21cd1
@@ -99,7 +99,7 @@ module Renuo::Cli::Commands::Ci
99
99
  end
100
100
 
101
101
  def succeeded?(type, name, status) # rubocop:disable Metrics/MethodLength
102
- time = Time.current.iso8601.to_s
102
+ time = Time.now.iso8601.to_s
103
103
 
104
104
  case status
105
105
  when "available", "success"
@@ -33,6 +33,7 @@ class Renuo::Cli::Commands::ConfigureSemaphore
33
33
 
34
34
  create_semaphore_notification
35
35
  create_semaphore_secrets
36
+ create_nctl_service_account_secret
36
37
  create_semaphore_deployment_targets
37
38
  end
38
39
 
@@ -52,6 +53,42 @@ class Renuo::Cli::Commands::ConfigureSemaphore
52
53
  system("sem create secret -p #{project_name} #{project_name}")
53
54
  end
54
55
 
56
+ DEPLOIO_ORGANIZATION = "renuo"
57
+
58
+ def deploio_project
59
+ "#{DEPLOIO_ORGANIZATION}-#{project_name}"
60
+ end
61
+
62
+ def service_account_name
63
+ "#{project_name}-semaphore"
64
+ end
65
+
66
+ def nctl_secret_name
67
+ "nctl-#{project_name}"
68
+ end
69
+
70
+ def create_nctl_service_account_secret
71
+ if system(nctl_create_service_account_command)
72
+ system(sem_create_nctl_secret_command)
73
+ else
74
+ warn("Could not create the API service account. Does the Deploio project #{deploio_project} exist? " \
75
+ "Create it with `renuo create-deploio-app`, then run:\n\n " \
76
+ "#{nctl_create_service_account_command}\n #{sem_create_nctl_secret_command}")
77
+ end
78
+ end
79
+
80
+ def nctl_create_service_account_command
81
+ "nctl create asa #{service_account_name} --project #{deploio_project} --wait"
82
+ end
83
+
84
+ def sem_create_nctl_secret_command
85
+ get_asa = "nctl get asa #{service_account_name} --project #{deploio_project}"
86
+ "sem create secret #{nctl_secret_name} -p #{project_name} " \
87
+ "-e NCTL_API_CLIENT_ID=\"$(#{get_asa} --print-client-id)\" " \
88
+ "-e NCTL_API_CLIENT_SECRET=\"$(#{get_asa} --print-client-secret)\" " \
89
+ "-e NCTL_ORGANIZATION=#{DEPLOIO_ORGANIZATION}"
90
+ end
91
+
55
92
  def create_semaphore_deployment_targets
56
93
  %w[main develop].each do |branch|
57
94
  system("sem create dt #{branch} -p #{project_name} --object-rule branch,exact,#{branch}")
@@ -175,7 +175,7 @@ class Renuo::Cli::Commands::CreateDeploioApp # rubocop:disable Metrics/ClassLeng
175
175
  --basic-auth=#{environment == "develop"} \\
176
176
  --build-env=SECRET_KEY_BASE="$(rails secret)" \\
177
177
  --language=ruby \\
178
- --buildpack-stack=heroku \\
178
+ --buildpack-type=heroku \\
179
179
  --size=mini \\
180
180
  --replicas=1
181
181
  OUTPUT
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../../helpers/review_app_notification"
4
+
5
+ module Renuo::Cli::Commands::Notify
6
+ module Slack
7
+ class ReviewAppDeploy
8
+ include ReviewAppNotification
9
+ include Renuo::Cli::Services
10
+
11
+ command "notify:slack:review-app-deploy" do |c|
12
+ c.syntax = "renuo notify:slack:review-app-deploy [options]"
13
+ c.summary = "Posts the branch's review app URL (with its basic auth credentials) and pull request to Slack."
14
+ c.description = "#{c.summary} The Slack incoming webhook is read from SLACK_WEBHOOK_URL, the GitHub " \
15
+ "repository from SEMAPHORE_GIT_REPO_SLUG and bundler's GitHub credentials (a token or " \
16
+ "user:token) from BUNDLE_GITHUB__COM to link the branch's open pull request. On Semaphore, " \
17
+ "it links the workflow (SEMAPHORE_ORGANIZATION_URL, SEMAPHORE_WORKFLOW_ID) offering the " \
18
+ "review app promotions."
19
+ ReviewAppNotification.define_options(c)
20
+ c.example "renuo notify:slack:review-app-deploy -p renuo-acme -b feature/42-migrate-to-deploio " \
21
+ "--channel '#project-acme-notifications'",
22
+ "Announces the branch's review app in the project's Slack channel"
23
+ c.action { |_args, options| new(options).run }
24
+ end
25
+
26
+ COLOR = "#141d50"
27
+
28
+ private
29
+
30
+ def message_lines
31
+ credentials = basic_auth_credentials
32
+ pull_request = fetch_pull_request
33
+
34
+ [
35
+ "Review app is ready: #{app_link(credentials)}",
36
+ pull_request && pull_request_line(pull_request),
37
+ branch_line
38
+ ].compact
39
+ end
40
+
41
+ def pull_request_line(pull_request)
42
+ "Pull request: <#{pull_request["html_url"]}|##{pull_request["number"]} #{pull_request["title"]}>"
43
+ end
44
+
45
+ def app_link(credentials)
46
+ url = app_url
47
+ url.userinfo = userinfo(credentials) if credentials
48
+ "<#{url}|#{url.host}>"
49
+ end
50
+
51
+ def userinfo(credentials)
52
+ credentials.values_at("username", "password").map { |value| URI.encode_www_form_component(value) }.join(":")
53
+ end
54
+
55
+ def app_url
56
+ url = Deploio.fetch_resource("app", @app_name, @project).to_h.dig("status", "atProvider", "defaultURLs", 0)
57
+ URI(url || abort("#{@app_name} has no URL assigned yet"))
58
+ end
59
+
60
+ # Deploio assigns basic auth credentials per app, so the review app's differ from the origin's.
61
+ def basic_auth_credentials
62
+ stdout, = system!("nctl get app #{@app_name} -p #{@project} -o json --basic-auth-credentials", quiet: true)
63
+ Array(JSON.parse(stdout)).first&.dig("basicauth")
64
+ end
65
+
66
+ def fetch_pull_request
67
+ credentials = ENV.fetch("BUNDLE_GITHUB__COM", "")
68
+ return if credentials.empty?
69
+
70
+ response = Net::HTTP.start("api.github.com", 443, use_ssl: true) do |http|
71
+ http.request(pull_request_request(credentials))
72
+ end
73
+ return JSON.parse(response.body).first if response.is_a?(Net::HTTPSuccess)
74
+
75
+ warn "GitHub pull request lookup failed: #{response.code}"
76
+ nil
77
+ end
78
+
79
+ def pull_request_request(credentials)
80
+ query = URI.encode_www_form(state: "open", head: "#{@repo.split("/").first}:#{@branch}")
81
+ request = Net::HTTP::Get.new("/repos/#{@repo}/pulls?#{query}", "Accept" => "application/vnd.github+json")
82
+ user, token = credentials.split(":", 2)
83
+ request.basic_auth(user, token || "x-oauth-basic")
84
+ request
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../../helpers/review_app_notification"
4
+
5
+ module Renuo::Cli::Commands::Notify
6
+ module Slack
7
+ class ReviewAppPurge
8
+ include ReviewAppNotification
9
+
10
+ command "notify:slack:review-app-purge" do |c|
11
+ c.syntax = "renuo notify:slack:review-app-purge [options]"
12
+ c.summary = "Announces on Slack that the branch's review app was purged."
13
+ c.description = "#{c.summary} The Slack incoming webhook is read from SLACK_WEBHOOK_URL and the GitHub " \
14
+ "repository from SEMAPHORE_GIT_REPO_SLUG. On Semaphore, it links the workflow " \
15
+ "(SEMAPHORE_ORGANIZATION_URL, SEMAPHORE_WORKFLOW_ID) offering the review app promotions."
16
+ ReviewAppNotification.define_options(c)
17
+ c.example "renuo notify:slack:review-app-purge -p renuo-acme -b feature/42-migrate-to-deploio " \
18
+ "--channel '#project-acme-notifications'",
19
+ "Announces that the branch's review app was purged"
20
+ c.action { |_args, options| new(options).run }
21
+ end
22
+
23
+ COLOR = "#9e9e9e"
24
+
25
+ private
26
+
27
+ def message_lines
28
+ ["Review app #{@app_name} was purged", branch_line]
29
+ end
30
+ end
31
+ end
32
+ end
@@ -201,7 +201,8 @@ class Renuo::Cli::Commands::Release # rubocop:disable Metrics/ClassLength
201
201
  abort(">> Cancelling Release.")
202
202
  end
203
203
 
204
- return unless Time.current.friday? && Time.current.hour >= 16
204
+ now = Time.now.getlocal
205
+ return unless now.friday? && now.hour >= 16
205
206
 
206
207
  unless agree(">> Are you sure you want to deploy on late Friday afternoon? " \
207
208
  "Did you think about your family...waiting for you at home?")
@@ -13,7 +13,9 @@ These are the steps of command execution in the happy path:
13
13
  1. Create or update `renuo review-app:deploy`
14
14
  1. _Intentional gap for configuration. Consider this the wiggle-room for LLMs_
15
15
  1. Start the paused app `renuo review-app:start`
16
+ 1. Announce it on Slack `renuo notify:slack:review-app-deploy`
16
17
  1. Cleanup `renuo review-app:purge`
18
+ 1. Announce the cleanup on Slack `renuo notify:slack:review-app-purge`
17
19
 
18
20
  ## Design Choices
19
21
 
@@ -27,3 +29,9 @@ These are the steps of command execution in the happy path:
27
29
  * All `nctl` commands are routed through `system!` and are logged in a command trail for auditing.
28
30
  * Configuration of system tools like `psql` is intentionally out of scope.
29
31
  But CI templating can give you some pointers.
32
+ * `notify:slack:review-app-deploy` posts the review app URL (carrying its Deploio-assigned basic auth credentials) and
33
+ the branch's open pull request to a Slack incoming webhook (`SLACK_WEBHOOK_URL`). The pull request lookup uses
34
+ bundler's `BUNDLE_GITHUB__COM` (a token or `user:token`) and never fails the pipeline when GitHub refuses it.
35
+ Incoming webhooks can't edit messages, so `notify:slack:review-app-purge` posts a separate one once purged.
36
+ On Semaphore, both messages link the branch's workflow offering the promotions. It's a link, not a button:
37
+ buttons posted by an incoming webhook show a warning unless they come from an interactive Slack app.
@@ -8,7 +8,7 @@ module Renuo::Cli::Commands::ReviewApp
8
8
  DEPLOY_PIPELINE_TARGET_FILE = ".semaphore/review-app-deploy.yml"
9
9
  PURGE_PIPELINE_TARGET_FILE = ".semaphore/review-app-purge.yml"
10
10
 
11
- attr_reader :project, :origin_pattern
11
+ attr_reader :project, :origin_pattern, :slack_channel
12
12
 
13
13
  command "review-app:setup-semaphoreci" do |c|
14
14
  c.syntax = "renuo review-app:setup-semaphoreci [options]"
@@ -16,14 +16,17 @@ module Renuo::Cli::Commands::ReviewApp
16
16
  c.description = c.summary
17
17
  c.option "-p", "--project <name>", String, "The name of the Deploio project"
18
18
  c.option "--origin-pattern <pattern>", String, "Name pattern identifying the origin app to copy from"
19
+ c.option "--slack-channel <name>", String, "The Slack channel notified when a review app is ready or purged"
19
20
  c.action { |_args, options| new(options).run }
20
21
  end
21
22
 
22
23
  def run
23
24
  FileUtils.mkdir_p File.dirname(DEPLOY_PIPELINE_TARGET_FILE)
24
25
  write_or_warn DEPLOY_PIPELINE_TARGET_FILE,
25
- render(DEPLOY_PIPELINE_TEMPLATE_FILE, { project:, origin_pattern:, secret_name: })
26
- write_or_warn PURGE_PIPELINE_TARGET_FILE, render(PURGE_PIPELINE_TEMPLATE_FILE, { project:, secret_name: })
26
+ render(DEPLOY_PIPELINE_TEMPLATE_FILE,
27
+ { project:, origin_pattern:, secret_name:, slack_channel: })
28
+ write_or_warn PURGE_PIPELINE_TARGET_FILE,
29
+ render(PURGE_PIPELINE_TEMPLATE_FILE, { project:, secret_name:, slack_channel: })
27
30
  print_instructions
28
31
  end
29
32
 
@@ -32,10 +35,11 @@ module Renuo::Cli::Commands::ReviewApp
32
35
  def initialize(options)
33
36
  @project = options.project || abort("missing project name")
34
37
  @origin_pattern = options.origin_pattern || abort("missing origin pattern")
38
+ @slack_channel = options.slack_channel || abort("missing Slack channel")
35
39
  end
36
40
 
37
41
  def secret_name
38
- "nctl-#{project}-review-apps"
42
+ "review-apps"
39
43
  end
40
44
 
41
45
  # Deploio project names are prefixed with the name of their organization.
@@ -51,12 +55,17 @@ module Renuo::Cli::Commands::ReviewApp
51
55
 
52
56
  nctl create asa #{service_account_name} --project #{project}
53
57
 
54
- Register its credentials as the Semaphore secret #{secret_name}, used by both pipelines:
58
+ Register its credentials and the incoming webhook of the Slack channel #{slack_channel}
59
+ (https://api.slack.com/messaging/webhooks) as the project secret #{secret_name} of the Semaphore project
60
+ (assumed to be named #{project} as well), used by both pipelines:
55
61
 
56
- sem create secret #{secret_name} \\
62
+ sem create secret -p #{project} #{secret_name} \\
57
63
  -e NCTL_API_CLIENT_ID="$(nctl get asa #{service_account_name} --project #{project} --print-client-id)" \\
58
64
  -e NCTL_API_CLIENT_SECRET="$(nctl get asa #{service_account_name} --project #{project} --print-client-secret)" \\
59
- -e NCTL_ORGANIZATION=#{organization}
65
+ -e NCTL_ORGANIZATION=#{organization} \\
66
+ -e SLACK_WEBHOOK_URL=https://hooks.slack.com/services/...
67
+
68
+ The deploy pipeline also reads the bundle-github secret: its BUNDLE_GITHUB__COM links the branch's pull request.
60
69
 
61
70
  Add these promotions to .semaphore/semaphore.yml (no auto_promote, so they only run on click):
62
71
 
@@ -67,7 +76,8 @@ module Renuo::Cli::Commands::ReviewApp
67
76
  pipeline_file: review-app-purge.yml
68
77
 
69
78
  Both buttons appear on every branch workflow in Semaphore. Deploy and purge the review app
70
- of a branch by clicking the promotion on that branch's workflow.
79
+ of a branch by clicking the promotion on that branch's workflow. The Slack message announcing
80
+ a review app links that workflow, so it can be purged from there.
71
81
  INSTRUCTIONS
72
82
  end
73
83
 
@@ -1,14 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/encrypted_configuration"
4
-
5
3
  module CredentialsHelper
6
- CONFIG = ActiveSupport::EncryptedConfiguration.new(
7
- config_path: File.expand_path("../credentials.yml.enc", __dir__),
8
- key_path: "", env_key: "RENUO_CLI_MASTER_KEY", raise_if_missing_key: true
9
- )
4
+ # required lazily, it takes a large share of the CLI's startup time
5
+ def self.config
6
+ @config ||= begin
7
+ require "active_support/encrypted_configuration"
8
+ ActiveSupport::EncryptedConfiguration.new(
9
+ config_path: File.expand_path("../credentials.yml.enc", __dir__),
10
+ key_path: "", env_key: "RENUO_CLI_MASTER_KEY", raise_if_missing_key: true
11
+ )
12
+ end
13
+ end
10
14
 
11
15
  def credentials
12
- CONFIG.config
16
+ CredentialsHelper.config.config
13
17
  end
14
18
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "review_app_helper"
4
+ require_relative "safe_review_app_name_contract"
5
+ require_relative "../services/review_app_slack_webhook"
6
+
7
+ module ReviewAppNotification
8
+ include ReviewAppHelper
9
+ include Contracts::Core
10
+
11
+ def self.define_options(command)
12
+ command.option "-p", "--project <name>", String, "The name of the Deploio project"
13
+ command.option "-b", "--branch <name>", String, "The Git branch"
14
+ command.option "--channel <name>", String, "The Slack channel to post to"
15
+ command.option "--repo <owner/name>", String, "The GitHub repository, defaults to SEMAPHORE_GIT_REPO_SLUG"
16
+ end
17
+
18
+ def run
19
+ @slack.post([*message_lines, workflow_line].compact.join("\n"), color: self.class::COLOR)
20
+ CmdTrail::Print.call
21
+ puts "\nnotified #{@channel}"
22
+ end
23
+
24
+ private
25
+
26
+ def initialize(options)
27
+ @project = options.project || abort("missing project name")
28
+ @branch = options.branch || abort("missing branch name")
29
+ @channel = options.channel || abort("missing Slack channel")
30
+ @repo = options.repo || ENV.fetch("SEMAPHORE_GIT_REPO_SLUG", nil).presence ||
31
+ abort("missing GitHub repository, set SEMAPHORE_GIT_REPO_SLUG or pass --repo")
32
+ @slack = Renuo::Cli::Services::ReviewAppSlackWebhook.new(@channel)
33
+ derive_app_name(@branch)
34
+ end
35
+
36
+ Contract String => SafeReviewAppNameContract
37
+ def derive_app_name(branch)
38
+ @app_name = review_app_name(branch)
39
+ end
40
+
41
+ def branch_line
42
+ "Branch: <https://github.com/#{@repo}/tree/#{@branch}|#{@branch}>"
43
+ end
44
+
45
+ # The deploy and purge promotions both run within the branch's workflow, which offers the other one.
46
+ # A link, because a button posted by an incoming webhook shows a warning without an interactive Slack app.
47
+ def workflow_line
48
+ organization_url, workflow_id = ENV.values_at("SEMAPHORE_ORGANIZATION_URL", "SEMAPHORE_WORKFLOW_ID")
49
+ return if organization_url.blank? || workflow_id.blank?
50
+
51
+ "Workflow: <#{organization_url}/workflows/#{workflow_id}|Open on Semaphore>"
52
+ end
53
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Renuo::Cli::Services::ReviewAppSlackWebhook
4
+ SENDER = { username: "Deploio Review Apps", icon_url: "https://deploio.ch/meta/apple-touch-icon.png" }.freeze
5
+
6
+ def initialize(channel)
7
+ @channel = channel
8
+ @url = ENV.fetch("SLACK_WEBHOOK_URL", nil).presence || abort("SLACK_WEBHOOK_URL is not set")
9
+ end
10
+
11
+ # The color bar on the side comes from the (legacy) attachment, which an incoming webhook still renders.
12
+ def post(text, color:)
13
+ attachment = { color: color, text: text, mrkdwn_in: ["text"] }
14
+ payload = SENDER.merge(channel: @channel, attachments: [attachment]).to_json
15
+ response = Net::HTTP.post(URI(@url), payload, "Content-Type" => "application/json")
16
+ return if response.is_a?(Net::HTTPSuccess)
17
+
18
+ abort "Slack refused the notification: #{response.code} #{response.body}"
19
+ end
20
+ end
@@ -20,6 +20,10 @@ do
20
20
  cache restore "sprocketscache-$SEMAPHORE_GIT_BRANCH,sprocketscache-develop"
21
21
  ;;
22
22
 
23
+ run_times)
24
+ cache restore "rspec-run-times-$SEMAPHORE_GIT_BRANCH,rspec-run-times-develop,rspec-run-times-main"
25
+ ;;
26
+
23
27
  *)
24
28
  echo "Invalid argument: Cache group $cache_group not found."
25
29
  exit 1
@@ -20,6 +20,15 @@ do
20
20
  cache store sprocketscache-$SEMAPHORE_GIT_BRANCH tmp/cache/assets/sprockets
21
21
  ;;
22
22
 
23
+ # rspec-core-parallel run times change with every build, so the key is replaced.
24
+ # Only main and develop store them, the other branches start from theirs.
25
+ run_times)
26
+ if [ -f tmp/rspec_parallel_run_times.txt ] && { [ "$SEMAPHORE_GIT_BRANCH" = "main" ] || [ "$SEMAPHORE_GIT_BRANCH" = "develop" ]; }; then
27
+ cache delete rspec-run-times-$SEMAPHORE_GIT_BRANCH
28
+ cache store rspec-run-times-$SEMAPHORE_GIT_BRANCH tmp/rspec_parallel_run_times.txt
29
+ fi
30
+ ;;
31
+
23
32
  *)
24
33
  echo "Invalid argument: Cache group $cache_group not found."
25
34
  exit 1
@@ -13,6 +13,7 @@ blocks:
13
13
  task:
14
14
  secrets:
15
15
  - name: <%= secret_name %>
16
+ - name: bundle-github
16
17
  jobs:
17
18
  - name: deploying
18
19
  commands:
@@ -42,3 +43,4 @@ blocks:
42
43
  # - renuo review-app:deploy --project <%= project %> --branch $SEMAPHORE_GIT_WORKING_BRANCH --origin-pattern <%= origin_pattern %> --keep-envs=build,sensitive-build,runtime,sensitive-runtime --start
43
44
  - renuo review-app:start --project <%= project %> --branch $SEMAPHORE_GIT_WORKING_BRANCH
44
45
  - renuo ci check-deploio-status $(renuo review-app:name --branch $SEMAPHORE_GIT_WORKING_BRANCH) --project <%= project %>
46
+ - renuo notify:slack:review-app-deploy --project <%= project %> --branch $SEMAPHORE_GIT_WORKING_BRANCH --channel '<%= slack_channel %>'
@@ -19,3 +19,4 @@ blocks:
19
19
  - gem i -q renuo-cli
20
20
  - renuo ci setup
21
21
  - renuo review-app:purge --project <%= project %> --branch $SEMAPHORE_GIT_WORKING_BRANCH
22
+ - renuo notify:slack:review-app-purge --project <%= project %> --branch $SEMAPHORE_GIT_WORKING_BRANCH --channel '<%= slack_channel %>'
@@ -13,7 +13,7 @@ blocks:
13
13
  task:
14
14
  secrets:
15
15
  - name: <%= project_name %>
16
- - name: nctl
16
+ - name: <%= nctl_secret_name %>
17
17
  jobs:
18
18
  - name: deploying
19
19
  commands:
@@ -53,10 +53,12 @@ blocks:
53
53
  - sem-service start postgres 18
54
54
  - cp .env.example .env
55
55
  - bundle exec rails db:create db:schema:load
56
+ - source .semaphore/bin/cache_restore run_times
56
57
  jobs:
57
58
  - name: tests
58
59
  commands:
59
60
  - bin/check
61
+ - source .semaphore/bin/cache_store run_times
60
62
  epilogue:
61
63
  always:
62
64
  commands:
@@ -3,7 +3,7 @@
3
3
  # simplecov:disable
4
4
  module Renuo
5
5
  class Cli
6
- VERSION = "4.29.1"
6
+ VERSION = "5.0.0"
7
7
  NAME = "renuo-cli"
8
8
  end
9
9
  end
data/lib/renuo/cli.rb CHANGED
@@ -1,18 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support"
4
- require "active_support/core_ext"
4
+ require "active_support/core_ext/enumerable"
5
+ require "active_support/core_ext/hash/keys"
6
+ require "active_support/core_ext/object/blank"
7
+ require "active_support/core_ext/string/exclude"
8
+ require "active_support/core_ext/string/filters"
9
+ require "active_support/core_ext/string/inflections"
5
10
  require "colorize"
6
11
  require "fileutils"
7
12
  require "highline"
8
13
  require "json"
9
14
  require "net/http"
10
15
  require "open3"
11
- require "psych"
12
16
  require "securerandom"
13
17
  require "service_actor"
18
+ require "time"
14
19
  require "tmpdir"
15
20
  require "uri"
21
+ require "yaml"
16
22
  require "renuo/cli/version"
17
23
 
18
24
  module Renuo
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: renuo-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.29.1
4
+ version: 5.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Renuo AG
@@ -116,6 +116,8 @@ files:
116
116
  - lib/renuo/cli/commands/github_replace.rb
117
117
  - lib/renuo/cli/commands/heroku_users.rb
118
118
  - lib/renuo/cli/commands/list_large_git_files.rb
119
+ - lib/renuo/cli/commands/notify/slack/review_app_deploy.rb
120
+ - lib/renuo/cli/commands/notify/slack/review_app_purge.rb
119
121
  - lib/renuo/cli/commands/release.rb
120
122
  - lib/renuo/cli/commands/review_app/README.md
121
123
  - lib/renuo/cli/commands/review_app/deploy.rb
@@ -135,6 +137,7 @@ files:
135
137
  - lib/renuo/cli/helpers/environments.rb
136
138
  - lib/renuo/cli/helpers/renuo_version.rb
137
139
  - lib/renuo/cli/helpers/review_app_helper.rb
140
+ - lib/renuo/cli/helpers/review_app_notification.rb
138
141
  - lib/renuo/cli/helpers/safe_review_app_name_contract.rb
139
142
  - lib/renuo/cli/helpers/zsh_completion_script.rb
140
143
  - lib/renuo/cli/services/cache.rb
@@ -152,6 +155,7 @@ files:
152
155
  - lib/renuo/cli/services/heroku.rb
153
156
  - lib/renuo/cli/services/hetzner.rb
154
157
  - lib/renuo/cli/services/renuo_cli_config.rb
158
+ - lib/renuo/cli/services/review_app_slack_webhook.rb
155
159
  - lib/renuo/cli/templates/semaphore/bin/cache_restore.erb
156
160
  - lib/renuo/cli/templates/semaphore/bin/cache_store.erb
157
161
  - lib/renuo/cli/templates/semaphore/notification.yml.erb