cpflow 4.2.0 → 5.0.0.rc.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 +4 -4
- data/.claude/commands/update-changelog.md +367 -0
- data/.github/workflows/claude.yml +5 -0
- data/.overcommit.yml +43 -3
- data/.rubocop.yml +3 -3
- data/CHANGELOG.md +28 -4
- data/CONTRIBUTING.md +6 -0
- data/Gemfile +8 -7
- data/Gemfile.lock +92 -72
- data/README.md +43 -15
- data/cpflow.gemspec +5 -5
- data/docs/ai-github-flow-prompt.md +61 -0
- data/docs/ci-automation.md +335 -28
- data/docs/commands.md +65 -4
- data/docs/releasing.md +153 -0
- data/lib/command/ai_github_flow_prompt.rb +47 -0
- data/lib/command/base.rb +14 -0
- data/lib/command/cleanup_images.rb +1 -1
- data/lib/command/cleanup_stale_apps.rb +1 -1
- data/lib/command/copy_image_from_upstream.rb +14 -3
- data/lib/command/exists.rb +13 -2
- data/lib/command/generate.rb +153 -4
- data/lib/command/generate_github_actions.rb +170 -0
- data/lib/command/generator_helpers.rb +31 -0
- data/lib/command/github_flow_readiness.rb +37 -0
- data/lib/command/run.rb +1 -1
- data/lib/command/terraform/generate.rb +1 -0
- data/lib/command/version.rb +1 -0
- data/lib/constants/exit_code.rb +1 -0
- data/lib/core/controlplane.rb +9 -7
- data/lib/core/controlplane_api_direct.rb +3 -3
- data/lib/core/github_flow_readiness/checks.rb +143 -0
- data/lib/core/github_flow_readiness_service.rb +453 -0
- data/lib/core/repo_introspection.rb +118 -0
- data/lib/core/terraform_config/dsl.rb +1 -1
- data/lib/core/terraform_config/local_variable.rb +1 -1
- data/lib/cpflow/version.rb +1 -1
- data/lib/cpflow.rb +65 -3
- data/lib/generator_templates/Dockerfile +59 -3
- data/lib/generator_templates/controlplane.yml +27 -39
- data/lib/generator_templates/entrypoint.sh +1 -1
- data/lib/generator_templates/release_script.sh +23 -0
- data/lib/generator_templates/templates/app.yml +5 -8
- data/lib/generator_templates/templates/rails.yml +2 -11
- data/lib/generator_templates_sqlite/controlplane.yml +46 -0
- data/lib/generator_templates_sqlite/release_script.sh +25 -0
- data/lib/generator_templates_sqlite/templates/app.yml +15 -0
- data/lib/generator_templates_sqlite/templates/db.yml +6 -0
- data/lib/generator_templates_sqlite/templates/rails.yml +32 -0
- data/lib/generator_templates_sqlite/templates/storage.yml +6 -0
- data/lib/github_flow_templates/.github/actions/cpflow-build-docker-image/action.yml +131 -0
- data/lib/github_flow_templates/.github/actions/cpflow-delete-control-plane-app/action.yml +24 -0
- data/lib/github_flow_templates/.github/actions/cpflow-delete-control-plane-app/delete-app.sh +50 -0
- data/lib/github_flow_templates/.github/actions/cpflow-detect-release-phase/action.yml +62 -0
- data/lib/github_flow_templates/.github/actions/cpflow-setup-environment/action.yml +98 -0
- data/lib/github_flow_templates/.github/actions/cpflow-validate-config/action.yml +85 -0
- data/lib/github_flow_templates/.github/actions/cpflow-wait-for-health/action.yml +92 -0
- data/lib/github_flow_templates/.github/cpflow-help.md +47 -0
- data/lib/github_flow_templates/.github/workflows/cpflow-cleanup-stale-review-apps.yml +56 -0
- data/lib/github_flow_templates/.github/workflows/cpflow-delete-review-app.yml +142 -0
- data/lib/github_flow_templates/.github/workflows/cpflow-deploy-review-app.yml +445 -0
- data/lib/github_flow_templates/.github/workflows/cpflow-deploy-staging.yml +140 -0
- data/lib/github_flow_templates/.github/workflows/cpflow-help-command.yml +53 -0
- data/lib/github_flow_templates/.github/workflows/cpflow-promote-staging-to-production.yml +490 -0
- data/lib/github_flow_templates/.github/workflows/cpflow-review-app-help.yml +46 -0
- data/rakelib/create_release.rake +662 -37
- data/script/check_command_docs +4 -2
- data/script/check_cpln_links +25 -11
- data/script/precommit/check_command_docs +22 -0
- data/script/precommit/check_cpln_links +21 -0
- data/script/precommit/check_trailing_newlines +68 -0
- data/script/precommit/get_changed_files +49 -0
- data/script/precommit/ruby_autofix +52 -0
- data/script/precommit/ruby_lint +33 -0
- metadata +52 -14
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "yaml"
|
|
4
|
+
|
|
5
|
+
module RepoIntrospection
|
|
6
|
+
DEFAULT_APP_PREFIX = "my-app"
|
|
7
|
+
RUBY_VERSION_DIRECTIVE_PATTERN = /^\s*ruby\s+['"\d]/
|
|
8
|
+
RUBY_VERSION_DIRECTIVE_PREFIX = /^\s*ruby\s+/
|
|
9
|
+
|
|
10
|
+
# Pure string → version-string extractor. Strips a leading `ruby-` prefix and returns
|
|
11
|
+
# the first `MAJOR.MINOR[.PATCH]` found in the source, or nil.
|
|
12
|
+
def self.parse_ruby_version_string(source)
|
|
13
|
+
normalized = source.strip.sub(/\Aruby-/, "")
|
|
14
|
+
normalized[/\d+\.\d+(?:\.\d+)?/]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Returns the first Ruby version string the repo declares, checked in the order Bundler
|
|
18
|
+
# itself uses: `.ruby-version`, then `.tool-versions`, then `Gemfile`. Returns nil when
|
|
19
|
+
# no source declares a version. Both `Command::Generator` and `GithubFlowReadinessService`
|
|
20
|
+
# call into this so a future format change (e.g. `.tool-versions`) only updates here.
|
|
21
|
+
def self.inferred_ruby_version_string(root)
|
|
22
|
+
ruby_version_from_ruby_version_file(root) ||
|
|
23
|
+
ruby_version_from_tool_versions(root) ||
|
|
24
|
+
ruby_version_from_gemfile(root)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.ruby_version_from_ruby_version_file(root)
|
|
28
|
+
path = File.join(root, ".ruby-version")
|
|
29
|
+
return unless File.file?(path)
|
|
30
|
+
|
|
31
|
+
parse_ruby_version_string(File.read(path))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.ruby_version_from_tool_versions(root)
|
|
35
|
+
path = File.join(root, ".tool-versions")
|
|
36
|
+
return unless File.file?(path)
|
|
37
|
+
|
|
38
|
+
ruby_line = File.readlines(path, chomp: true).find { |line| line.match?(RUBY_VERSION_DIRECTIVE_PREFIX) }
|
|
39
|
+
return unless ruby_line
|
|
40
|
+
|
|
41
|
+
parse_ruby_version_string(ruby_line.sub(RUBY_VERSION_DIRECTIVE_PREFIX, ""))
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.ruby_version_from_gemfile(root)
|
|
45
|
+
path = File.join(root, "Gemfile")
|
|
46
|
+
return unless File.file?(path)
|
|
47
|
+
|
|
48
|
+
ruby_lines = File.readlines(path, chomp: true).select { |line| line.match?(RUBY_VERSION_DIRECTIVE_PREFIX) }
|
|
49
|
+
ruby_line = ruby_lines.find { |line| line.match?(RUBY_VERSION_DIRECTIVE_PATTERN) }
|
|
50
|
+
warn_dynamic_ruby_directive if ruby_lines.any? && ruby_line.nil?
|
|
51
|
+
return unless ruby_line
|
|
52
|
+
|
|
53
|
+
parse_ruby_version_string(ruby_line.sub(RUBY_VERSION_DIRECTIVE_PREFIX, ""))
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def self.warn_dynamic_ruby_directive
|
|
57
|
+
return unless ENV["CPFLOW_DEBUG"]
|
|
58
|
+
|
|
59
|
+
warn "cpflow: Gemfile has a dynamic `ruby` directive; falling back to the default Ruby version"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Returns a Control Plane-safe app prefix derived from the basename of `root`:
|
|
63
|
+
# lower-cased, with non-alphanumeric runs collapsed to dashes and stripped from
|
|
64
|
+
# the ends. Falls back to DEFAULT_APP_PREFIX when the result is empty.
|
|
65
|
+
def self.inferred_app_prefix(root)
|
|
66
|
+
sanitized = File.basename(root)
|
|
67
|
+
.downcase
|
|
68
|
+
.gsub(/[^a-z0-9]+/, "-")
|
|
69
|
+
.gsub(/\A-+|-+\z/, "")
|
|
70
|
+
|
|
71
|
+
sanitized.empty? ? DEFAULT_APP_PREFIX : sanitized
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Returns true if `config/database.yml` under `root` configures SQLite for production.
|
|
75
|
+
# YAML merge keys such as `<<: *default` are resolved by safe_load, so only the
|
|
76
|
+
# final production hash should be inspected.
|
|
77
|
+
def self.sqlite_database_in_production?(root)
|
|
78
|
+
path = File.join(root, "config/database.yml")
|
|
79
|
+
return false unless File.file?(path)
|
|
80
|
+
|
|
81
|
+
parsed = safe_load_database_yml(File.read(path))
|
|
82
|
+
return false unless parsed.is_a?(Hash)
|
|
83
|
+
|
|
84
|
+
production = parsed["production"]
|
|
85
|
+
return false unless production.is_a?(Hash)
|
|
86
|
+
|
|
87
|
+
url = production["url"]
|
|
88
|
+
return sqlite_database_url?(url) if url.is_a?(String) && !url.strip.empty?
|
|
89
|
+
|
|
90
|
+
sqlite_adapter_in_hash?(production)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def self.safe_load_database_yml(raw_contents)
|
|
94
|
+
# ERB conditionals can change YAML structure, so avoid guessing. Output-only
|
|
95
|
+
# ERB is stubbed as a scalar so common Rails defaults like `pool: <%= ... %>`
|
|
96
|
+
# still parse, but control-flow ERB returns unknown. `<%- ... %>` is a
|
|
97
|
+
# whitespace-trimming code tag, not an output tag, so treat it as unknown too.
|
|
98
|
+
# Callers treat unknown as non-SQLite and
|
|
99
|
+
# emit the default Postgres scaffold rather than guessing wrong.
|
|
100
|
+
return nil if raw_contents.match?(/<%(?![=#])/m)
|
|
101
|
+
|
|
102
|
+
stubbed = raw_contents.gsub(/<%=.*?%>/m, "__erb__").gsub(/<%#.*?%>/m, "")
|
|
103
|
+
YAML.safe_load(stubbed, aliases: true, permitted_classes: [Symbol])
|
|
104
|
+
rescue Psych::SyntaxError
|
|
105
|
+
nil
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def self.sqlite_adapter_in_hash?(config)
|
|
109
|
+
return false unless config.is_a?(Hash)
|
|
110
|
+
|
|
111
|
+
adapter = config["adapter"]
|
|
112
|
+
adapter.is_a?(String) && adapter.strip.start_with?("sqlite3")
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def self.sqlite_database_url?(url)
|
|
116
|
+
url.strip.downcase.start_with?("sqlite:", "sqlite3:")
|
|
117
|
+
end
|
|
118
|
+
end
|
data/lib/cpflow/version.rb
CHANGED
data/lib/cpflow.rb
CHANGED
|
@@ -53,9 +53,13 @@ module Cpflow
|
|
|
53
53
|
ENV["CPLN_SKIP_UPDATE_CHECK"] = "true"
|
|
54
54
|
ENV["NODE_NO_WARNINGS"] = "1"
|
|
55
55
|
|
|
56
|
-
check_cpln_version
|
|
57
|
-
check_cpflow_version
|
|
58
56
|
fix_help_option
|
|
57
|
+
# Thor's `start(args = ARGV.dup, ...)` accepts an explicit argv as the first
|
|
58
|
+
# positional argument. Use that when present so the startup-check decision matches
|
|
59
|
+
# the command Thor is about to dispatch (and so test invocations don't pick up
|
|
60
|
+
# rspec's ARGV by accident).
|
|
61
|
+
argv = args.first.is_a?(Array) ? args.first : ARGV
|
|
62
|
+
run_startup_checks if requires_startup_checks?(argv)
|
|
59
63
|
|
|
60
64
|
super
|
|
61
65
|
end
|
|
@@ -120,13 +124,71 @@ module Cpflow
|
|
|
120
124
|
end
|
|
121
125
|
private_class_method :subcommand?
|
|
122
126
|
|
|
127
|
+
def self.run_startup_checks
|
|
128
|
+
check_cpln_version
|
|
129
|
+
check_cpflow_version
|
|
130
|
+
end
|
|
131
|
+
private_class_method :run_startup_checks
|
|
132
|
+
|
|
133
|
+
def self.requires_startup_checks?(argv = ARGV)
|
|
134
|
+
return false if argv.empty?
|
|
135
|
+
return false if help_request?(argv)
|
|
136
|
+
return false if version_flag?(argv)
|
|
137
|
+
|
|
138
|
+
command_class = command_class_for_argv(argv)
|
|
139
|
+
# Default to true when the command name is unrecognized so a typo still gets the
|
|
140
|
+
# version check (Thor's "unknown command" error then surfaces). Pre-PR behavior
|
|
141
|
+
# was always-on; only known commands explicitly opt out.
|
|
142
|
+
command_class ? command_class::REQUIRES_STARTUP_CHECKS : true
|
|
143
|
+
end
|
|
144
|
+
private_class_method :requires_startup_checks?
|
|
145
|
+
|
|
146
|
+
def self.help_request?(argv)
|
|
147
|
+
help_mappings = Thor::HELP_MAPPINGS + ["help"]
|
|
148
|
+
help_mappings.include?(argv.first)
|
|
149
|
+
end
|
|
150
|
+
private_class_method :help_request?
|
|
151
|
+
|
|
152
|
+
def self.version_flag?(argv)
|
|
153
|
+
%w[--version -v].include?(argv.first)
|
|
154
|
+
end
|
|
155
|
+
private_class_method :version_flag?
|
|
156
|
+
|
|
157
|
+
def self.command_class_for_argv(argv)
|
|
158
|
+
first_arg = argv[0]
|
|
159
|
+
return if first_arg.nil?
|
|
160
|
+
|
|
161
|
+
return subcommand_class_for_argv(first_arg, argv[1]) if subcommand_names.include?(first_arg)
|
|
162
|
+
|
|
163
|
+
top_level_command_class_for(first_arg)
|
|
164
|
+
end
|
|
165
|
+
private_class_method :command_class_for_argv
|
|
166
|
+
|
|
167
|
+
def self.subcommand_class_for_argv(subcommand_name, command_name)
|
|
168
|
+
return if command_name.nil?
|
|
169
|
+
|
|
170
|
+
all_base_commands[:"#{subcommand_name}_#{command_name.tr('-', '_')}"] ||
|
|
171
|
+
all_base_commands.values.find do |command_class|
|
|
172
|
+
subcommand_name == command_class::SUBCOMMAND_NAME && command_name == command_class::NAME
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
private_class_method :subcommand_class_for_argv
|
|
176
|
+
|
|
177
|
+
def self.top_level_command_class_for(command_name)
|
|
178
|
+
all_base_commands[command_name.tr("-", "_").to_sym] ||
|
|
179
|
+
all_base_commands.values.find do |command_class|
|
|
180
|
+
command_class::SUBCOMMAND_NAME.nil? && command_name == command_class::NAME
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
private_class_method :top_level_command_class_for
|
|
184
|
+
|
|
123
185
|
# Needed to silence deprecation warning
|
|
124
186
|
def self.exit_on_failure?
|
|
125
187
|
true
|
|
126
188
|
end
|
|
127
189
|
|
|
128
190
|
# Needed to be able to use "run" as a command
|
|
129
|
-
def self.is_thor_reserved_word?(word, type) # rubocop:disable Naming/
|
|
191
|
+
def self.is_thor_reserved_word?(word, type) # rubocop:disable Naming/PredicatePrefix
|
|
130
192
|
return false if word == "run"
|
|
131
193
|
|
|
132
194
|
super
|
|
@@ -1,23 +1,79 @@
|
|
|
1
|
-
|
|
1
|
+
ARG RUBY_VERSION=__RUBY_VERSION__
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# Node and Ruby base images share the same Debian release so that glibc, libssl, and
|
|
4
|
+
# other system libraries line up between stages.
|
|
5
|
+
FROM docker.io/library/node:22-bookworm-slim AS node
|
|
6
|
+
FROM ruby:$RUBY_VERSION-slim-bookworm
|
|
7
|
+
|
|
8
|
+
# Yarn Classic / pnpm fallbacks for projects that haven't adopted corepack via
|
|
9
|
+
# `packageManager` in package.json. Override at build time (`--build-arg=YARN_CLASSIC_VERSION=...`)
|
|
10
|
+
# rather than editing this file so regenerating the Dockerfile keeps your pin.
|
|
11
|
+
ARG YARN_CLASSIC_VERSION=1.22.22
|
|
12
|
+
ARG PNPM_FALLBACK_VERSION=9.12.3
|
|
4
13
|
|
|
5
14
|
WORKDIR /app
|
|
6
15
|
|
|
16
|
+
# Keep Node.js available both for asset compilation and for SSR runtimes that
|
|
17
|
+
# rely on ExecJS in production. Narrowed to just what the node stage actually
|
|
18
|
+
# ships under /usr/local so we don't drag in unused Debian libs from that image.
|
|
19
|
+
COPY --from=node /usr/local/bin/node /usr/local/bin/node
|
|
20
|
+
COPY --from=node /usr/local/bin/npm /usr/local/bin/npm
|
|
21
|
+
COPY --from=node /usr/local/bin/npx /usr/local/bin/npx
|
|
22
|
+
COPY --from=node /usr/local/bin/corepack /usr/local/bin/corepack
|
|
23
|
+
COPY --from=node /usr/local/lib/node_modules /usr/local/lib/node_modules
|
|
24
|
+
COPY --from=node /usr/local/include/node /usr/local/include/node
|
|
25
|
+
|
|
26
|
+
# Expose Corepack-managed shims so later build steps can call yarn/pnpm
|
|
27
|
+
# directly during asset precompilation hooks.
|
|
28
|
+
RUN printf '%s\n' '#!/bin/sh' 'exec corepack yarn "$@"' > /usr/bin/yarn && \
|
|
29
|
+
chmod +x /usr/bin/yarn && \
|
|
30
|
+
printf '%s\n' '#!/bin/sh' 'exec corepack pnpm "$@"' > /usr/bin/pnpm && \
|
|
31
|
+
chmod +x /usr/bin/pnpm
|
|
32
|
+
|
|
7
33
|
# install ruby gems
|
|
8
34
|
COPY Gemfile* ./
|
|
9
35
|
|
|
10
36
|
RUN bundle config set without 'development test' && \
|
|
11
|
-
bundle config set with '
|
|
37
|
+
bundle config set with 'production' && \
|
|
12
38
|
bundle install --jobs=3 --retry=3
|
|
13
39
|
|
|
14
40
|
COPY . ./
|
|
15
41
|
|
|
42
|
+
# Install JavaScript dependencies only when the project actually has them.
|
|
43
|
+
# Pin `packageManager` in package.json to take the corepack path and avoid the
|
|
44
|
+
# YARN_CLASSIC_VERSION / PNPM_FALLBACK_VERSION fallbacks below; the fallbacks exist for
|
|
45
|
+
# projects that haven't adopted corepack and should be reviewed periodically as they go stale.
|
|
46
|
+
RUN if [ -f package.json ]; then \
|
|
47
|
+
package_manager="$(node -p "require('./package.json').packageManager || ''")"; \
|
|
48
|
+
if [ -f yarn.lock ]; then \
|
|
49
|
+
if printf '%s' "$package_manager" | grep -q '^yarn@'; then \
|
|
50
|
+
corepack prepare "$package_manager" --activate && \
|
|
51
|
+
(corepack yarn install --immutable || corepack yarn install --frozen-lockfile); \
|
|
52
|
+
else \
|
|
53
|
+
npm install -g "yarn@${YARN_CLASSIC_VERSION}" && \
|
|
54
|
+
(yarn install --immutable || yarn install --frozen-lockfile); \
|
|
55
|
+
fi; \
|
|
56
|
+
elif [ -f pnpm-lock.yaml ]; then \
|
|
57
|
+
if printf '%s' "$package_manager" | grep -q '^pnpm@'; then \
|
|
58
|
+
corepack prepare "$package_manager" --activate && \
|
|
59
|
+
corepack pnpm install --frozen-lockfile; \
|
|
60
|
+
else \
|
|
61
|
+
corepack prepare "pnpm@${PNPM_FALLBACK_VERSION}" --activate && \
|
|
62
|
+
corepack pnpm install --frozen-lockfile; \
|
|
63
|
+
fi; \
|
|
64
|
+
elif [ -f package-lock.json ]; then \
|
|
65
|
+
npm ci; \
|
|
66
|
+
else \
|
|
67
|
+
npm install; \
|
|
68
|
+
fi; \
|
|
69
|
+
fi
|
|
70
|
+
|
|
16
71
|
ENV RAILS_ENV=production
|
|
17
72
|
|
|
18
73
|
# compiling assets requires any value for ENV of SECRET_KEY_BASE
|
|
19
74
|
ENV SECRET_KEY_BASE=NOT_USED_NON_BLANK
|
|
20
75
|
|
|
76
|
+
__ASSET_PRECOMPILE_HOOK_RUN__
|
|
21
77
|
RUN rails assets:precompile
|
|
22
78
|
|
|
23
79
|
# add entrypoint
|
|
@@ -1,62 +1,50 @@
|
|
|
1
1
|
# Keys beginning with "cpln_" correspond to your settings in Control Plane.
|
|
2
|
+
#
|
|
3
|
+
# Generated baseline for a Rails app that uses PostgreSQL in production.
|
|
4
|
+
# Rename the app keys below if you want something other than the repo name.
|
|
2
5
|
|
|
3
|
-
# You can opt out of allowing the use of CPLN_ORG and CPLN_APP env vars
|
|
4
|
-
# to avoid any accidents with the wrong org / app.
|
|
5
6
|
allow_org_override_by_env: true
|
|
6
7
|
allow_app_override_by_env: true
|
|
7
8
|
|
|
8
9
|
aliases:
|
|
9
10
|
common: &common
|
|
10
|
-
# Organization name for staging (customize to your needs).
|
|
11
|
-
# Production apps will use a different organization, specified below, for security.
|
|
12
11
|
cpln_org: my-org-staging
|
|
13
|
-
|
|
14
|
-
# Example apps use only one location. Control Plane offers the ability to use multiple locations.
|
|
15
|
-
# TODO: Allow specification of multiple locations.
|
|
16
12
|
default_location: aws-us-east-2
|
|
13
|
+
setup_app_templates:
|
|
14
|
+
- app
|
|
15
|
+
- postgres
|
|
16
|
+
- rails
|
|
17
17
|
|
|
18
|
-
# Configure the workload name used as a template for one-off scripts, like a Heroku one-off dyno.
|
|
19
18
|
one_off_workload: rails
|
|
20
|
-
|
|
21
|
-
# Workloads that are for the application itself and are using application Docker images.
|
|
22
|
-
# These are updated with the new image when running the `deploy-image` command,
|
|
23
|
-
# and are also used by the `info` and `ps:` commands in order to get all of the defined workloads.
|
|
24
|
-
# On the other hand, if you have a workload for Redis, that would NOT use the application Docker image
|
|
25
|
-
# and not be listed here.
|
|
26
19
|
app_workloads:
|
|
27
20
|
- rails
|
|
28
|
-
|
|
29
|
-
# Additional "service type" workloads, using non-application Docker images.
|
|
30
|
-
# These are only used by the `info` and `ps:` commands in order to get all of the defined workloads.
|
|
31
21
|
additional_workloads:
|
|
32
22
|
- postgres
|
|
33
23
|
|
|
34
|
-
# Configure the workload name used when maintenance mode is on (defaults to "maintenance")
|
|
35
24
|
maintenance_workload: maintenance
|
|
25
|
+
release_script: release_script.sh
|
|
36
26
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
# Use the values from the common section above.
|
|
40
|
-
<<: *common
|
|
41
|
-
my-app-review:
|
|
42
|
-
<<: *common
|
|
43
|
-
# If `match_if_app_name_starts_with` is `true`, then use this config for app names starting with this name,
|
|
44
|
-
# e.g., "my-app-review-pr123", "my-app-review-anything-goes", etc.
|
|
45
|
-
match_if_app_name_starts_with: true
|
|
46
|
-
my-app-production:
|
|
47
|
-
<<: *common
|
|
27
|
+
stale_app_image_deployed_days: 5
|
|
28
|
+
image_retention_days: 7
|
|
48
29
|
|
|
49
|
-
|
|
50
|
-
|
|
30
|
+
production: &production
|
|
31
|
+
<<: *common
|
|
51
32
|
allow_org_override_by_env: false
|
|
52
33
|
allow_app_override_by_env: false
|
|
53
|
-
|
|
54
|
-
# Use a different organization for production.
|
|
55
34
|
cpln_org: my-org-production
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
35
|
+
upstream: __APP_PREFIX__-staging
|
|
36
|
+
|
|
37
|
+
apps:
|
|
38
|
+
__APP_PREFIX__-staging:
|
|
60
39
|
<<: *common
|
|
61
|
-
|
|
62
|
-
|
|
40
|
+
|
|
41
|
+
__APP_PREFIX__-review:
|
|
42
|
+
<<: *common
|
|
43
|
+
match_if_app_name_starts_with: true
|
|
44
|
+
# Uncomment to automatically initialize and tear down review-app databases:
|
|
45
|
+
# hooks:
|
|
46
|
+
# post_creation: bundle exec rails db:prepare
|
|
47
|
+
# pre_deletion: bundle exec rails db:drop
|
|
48
|
+
|
|
49
|
+
__APP_PREFIX__-production:
|
|
50
|
+
<<: *production
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
log() {
|
|
5
|
+
echo "[$(date +%Y-%m-%d:%H:%M:%S)]: $1"
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
error_exit() {
|
|
9
|
+
log "$1" 1>&2
|
|
10
|
+
exit 1
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
log "Running release_script.sh per controlplane.yml"
|
|
14
|
+
|
|
15
|
+
if [ -x ./bin/rails ]; then
|
|
16
|
+
log "Run DB migrations"
|
|
17
|
+
SECRET_KEY_BASE="${SECRET_KEY_BASE:-precompile_placeholder}" ./bin/rails db:prepare || \
|
|
18
|
+
error_exit "Failed to run DB migrations"
|
|
19
|
+
else
|
|
20
|
+
error_exit "./bin/rails does not exist or is not executable"
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
log "Completed release_script.sh per controlplane.yml"
|
|
@@ -2,20 +2,17 @@
|
|
|
2
2
|
kind: gvc
|
|
3
3
|
name: {{APP_NAME}}
|
|
4
4
|
spec:
|
|
5
|
-
# For using templates for test apps, put ENV values here, stored in git repo.
|
|
6
|
-
# Production apps will have values configured manually after app creation.
|
|
7
5
|
env:
|
|
8
6
|
- name: DATABASE_URL
|
|
9
|
-
# Password does not matter because host postgres.{{APP_NAME}}.cpln.local can only be accessed
|
|
10
|
-
# locally within CPLN GVC, and postgres running on a CPLN workload is something only for a
|
|
11
|
-
# test app that lacks persistence.
|
|
12
7
|
value: 'postgres://the_user:the_password@postgres.{{APP_NAME}}.cpln.local:5432/{{APP_NAME}}'
|
|
13
8
|
- name: RAILS_ENV
|
|
14
9
|
value: production
|
|
10
|
+
- name: RAILS_LOG_TO_STDOUT
|
|
11
|
+
value: "true"
|
|
15
12
|
- name: RAILS_SERVE_STATIC_FILES
|
|
16
|
-
value:
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
value: "true"
|
|
14
|
+
- name: SECRET_KEY_BASE
|
|
15
|
+
value: cpln://secret/{{APP_SECRETS}}.SECRET_KEY_BASE
|
|
19
16
|
staticPlacement:
|
|
20
17
|
locationLinks:
|
|
21
18
|
- {{APP_LOCATION_LINK}}
|
|
@@ -6,31 +6,22 @@ spec:
|
|
|
6
6
|
type: standard
|
|
7
7
|
containers:
|
|
8
8
|
- name: rails
|
|
9
|
-
# 300m is a good starting place for a test app. You can experiment with CPU configuration
|
|
10
|
-
# once your app is running.
|
|
11
9
|
cpu: 300m
|
|
12
|
-
env:
|
|
13
|
-
- name: LOG_LEVEL
|
|
14
|
-
value: debug
|
|
15
|
-
# Inherit other ENV values from GVC
|
|
16
10
|
inheritEnv: true
|
|
17
11
|
image: {{APP_IMAGE_LINK}}
|
|
18
|
-
# 512 corresponds to a standard 1x dyno type
|
|
19
12
|
memory: 512Mi
|
|
20
13
|
ports:
|
|
21
14
|
- number: 3000
|
|
22
15
|
protocol: http
|
|
23
16
|
defaultOptions:
|
|
24
|
-
# Start out like this for "test apps"
|
|
25
17
|
autoscaling:
|
|
26
|
-
|
|
18
|
+
minScale: 1
|
|
27
19
|
maxScale: 1
|
|
28
20
|
capacityAI: false
|
|
21
|
+
timeoutSeconds: 60
|
|
29
22
|
firewallConfig:
|
|
30
23
|
external:
|
|
31
|
-
# Default to allow public access to Rails server
|
|
32
24
|
inboundAllowCIDR:
|
|
33
25
|
- 0.0.0.0/0
|
|
34
|
-
# Could configure outbound for more security
|
|
35
26
|
outboundAllowCIDR:
|
|
36
27
|
- 0.0.0.0/0
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Keys beginning with "cpln_" correspond to your settings in Control Plane.
|
|
2
|
+
#
|
|
3
|
+
# Generated baseline for a Rails app that uses SQLite in production. This setup
|
|
4
|
+
# keeps `/app/db` and `/app/storage` on persistent volumes.
|
|
5
|
+
|
|
6
|
+
allow_org_override_by_env: true
|
|
7
|
+
allow_app_override_by_env: true
|
|
8
|
+
|
|
9
|
+
aliases:
|
|
10
|
+
common: &common
|
|
11
|
+
cpln_org: my-org-staging
|
|
12
|
+
default_location: aws-us-east-2
|
|
13
|
+
setup_app_templates:
|
|
14
|
+
- app
|
|
15
|
+
- db
|
|
16
|
+
- storage
|
|
17
|
+
- rails
|
|
18
|
+
|
|
19
|
+
one_off_workload: rails
|
|
20
|
+
app_workloads:
|
|
21
|
+
- rails
|
|
22
|
+
additional_workloads: []
|
|
23
|
+
|
|
24
|
+
maintenance_workload: maintenance
|
|
25
|
+
release_script: release_script.sh
|
|
26
|
+
|
|
27
|
+
stale_app_image_deployed_days: 5
|
|
28
|
+
image_retention_days: 7
|
|
29
|
+
|
|
30
|
+
production: &production
|
|
31
|
+
<<: *common
|
|
32
|
+
allow_org_override_by_env: false
|
|
33
|
+
allow_app_override_by_env: false
|
|
34
|
+
cpln_org: my-org-production
|
|
35
|
+
upstream: __APP_PREFIX__-staging
|
|
36
|
+
|
|
37
|
+
apps:
|
|
38
|
+
__APP_PREFIX__-staging:
|
|
39
|
+
<<: *common
|
|
40
|
+
|
|
41
|
+
__APP_PREFIX__-review:
|
|
42
|
+
<<: *common
|
|
43
|
+
match_if_app_name_starts_with: true
|
|
44
|
+
|
|
45
|
+
__APP_PREFIX__-production:
|
|
46
|
+
<<: *production
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
log() {
|
|
5
|
+
echo "[$(date +%Y-%m-%d:%H:%M:%S)]: $1"
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
error_exit() {
|
|
9
|
+
log "$1" 1>&2
|
|
10
|
+
exit 1
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
log "Running release_script.sh per controlplane.yml"
|
|
14
|
+
|
|
15
|
+
mkdir -p db storage
|
|
16
|
+
|
|
17
|
+
if [ -x ./bin/rails ]; then
|
|
18
|
+
log "Run DB migrations"
|
|
19
|
+
SECRET_KEY_BASE="${SECRET_KEY_BASE:-precompile_placeholder}" ./bin/rails db:prepare || \
|
|
20
|
+
error_exit "Failed to run DB migrations"
|
|
21
|
+
else
|
|
22
|
+
error_exit "./bin/rails does not exist or is not executable"
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
log "Completed release_script.sh per controlplane.yml"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
kind: gvc
|
|
2
|
+
name: {{APP_NAME}}
|
|
3
|
+
spec:
|
|
4
|
+
env:
|
|
5
|
+
- name: RAILS_ENV
|
|
6
|
+
value: production
|
|
7
|
+
- name: RAILS_LOG_TO_STDOUT
|
|
8
|
+
value: "true"
|
|
9
|
+
- name: RAILS_SERVE_STATIC_FILES
|
|
10
|
+
value: "true"
|
|
11
|
+
- name: SECRET_KEY_BASE
|
|
12
|
+
value: cpln://secret/{{APP_SECRETS}}.SECRET_KEY_BASE
|
|
13
|
+
staticPlacement:
|
|
14
|
+
locationLinks:
|
|
15
|
+
- {{APP_LOCATION_LINK}}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
kind: workload
|
|
2
|
+
name: rails
|
|
3
|
+
spec:
|
|
4
|
+
type: standard
|
|
5
|
+
containers:
|
|
6
|
+
- name: rails
|
|
7
|
+
cpu: 300m
|
|
8
|
+
inheritEnv: true
|
|
9
|
+
image: {{APP_IMAGE_LINK}}
|
|
10
|
+
memory: 512Mi
|
|
11
|
+
ports:
|
|
12
|
+
- number: 3000
|
|
13
|
+
protocol: http
|
|
14
|
+
volumes:
|
|
15
|
+
- path: /app/db
|
|
16
|
+
recoveryPolicy: retain
|
|
17
|
+
uri: cpln://volumeset/app-db
|
|
18
|
+
- path: /app/storage
|
|
19
|
+
recoveryPolicy: retain
|
|
20
|
+
uri: cpln://volumeset/app-storage
|
|
21
|
+
defaultOptions:
|
|
22
|
+
autoscaling:
|
|
23
|
+
minScale: 1
|
|
24
|
+
maxScale: 1
|
|
25
|
+
capacityAI: false
|
|
26
|
+
timeoutSeconds: 60
|
|
27
|
+
firewallConfig:
|
|
28
|
+
external:
|
|
29
|
+
inboundAllowCIDR:
|
|
30
|
+
- 0.0.0.0/0
|
|
31
|
+
outboundAllowCIDR:
|
|
32
|
+
- 0.0.0.0/0
|