kcppayments_rails 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/CHANGELOG.md +5 -0
- data/README.md +49 -0
- data/Rakefile +4 -0
- data/lib/generators/kcppayments_rails/install/install_generator.rb +21 -0
- data/lib/generators/kcppayments_rails/install/templates/kcp_controller.js +54 -0
- data/lib/generators/kcppayments_rails/install/templates/kcppayments_rails.rb +14 -0
- data/lib/kcppayments_rails/client.rb +49 -0
- data/lib/kcppayments_rails/configuration.rb +24 -0
- data/lib/kcppayments_rails/engine.rb +21 -0
- data/lib/kcppayments_rails/helpers/kcp_helper.rb +33 -0
- data/lib/kcppayments_rails/version.rb +5 -0
- data/lib/kcppayments_rails.rb +26 -0
- data/sig/kcppayments_rails.rbs +4 -0
- data/test_app/.dockerignore +51 -0
- data/test_app/.github/dependabot.yml +12 -0
- data/test_app/.github/workflows/ci.yml +55 -0
- data/test_app/.gitignore +87 -0
- data/test_app/.kamal/hooks/docker-setup.sample +3 -0
- data/test_app/.kamal/hooks/post-app-boot.sample +3 -0
- data/test_app/.kamal/hooks/post-deploy.sample +14 -0
- data/test_app/.kamal/hooks/post-proxy-reboot.sample +3 -0
- data/test_app/.kamal/hooks/pre-app-boot.sample +3 -0
- data/test_app/.kamal/hooks/pre-build.sample +51 -0
- data/test_app/.kamal/hooks/pre-connect.sample +47 -0
- data/test_app/.kamal/hooks/pre-deploy.sample +122 -0
- data/test_app/.kamal/hooks/pre-proxy-reboot.sample +3 -0
- data/test_app/.kamal/secrets +17 -0
- data/test_app/.rubocop.yml +8 -0
- data/test_app/.ruby-version +1 -0
- data/test_app/Dockerfile +72 -0
- data/test_app/Gemfile +60 -0
- data/test_app/Gemfile.lock +370 -0
- data/test_app/README.md +181 -0
- data/test_app/Rakefile +6 -0
- data/test_app/app/assets/images/.keep +0 -0
- data/test_app/app/assets/stylesheets/application.css +10 -0
- data/test_app/app/controllers/application_controller.rb +4 -0
- data/test_app/app/controllers/concerns/.keep +0 -0
- data/test_app/app/controllers/orders_controller.rb +31 -0
- data/test_app/app/controllers/payments_controller.rb +120 -0
- data/test_app/app/helpers/application_helper.rb +2 -0
- data/test_app/app/helpers/orders_helper.rb +2 -0
- data/test_app/app/helpers/payments_helper.rb +2 -0
- data/test_app/app/javascript/application.js +3 -0
- data/test_app/app/javascript/controllers/application.js +9 -0
- data/test_app/app/javascript/controllers/hello_controller.js +7 -0
- data/test_app/app/javascript/controllers/index.js +4 -0
- data/test_app/app/javascript/controllers/kcp_controller.js +310 -0
- data/test_app/app/jobs/application_job.rb +7 -0
- data/test_app/app/mailers/application_mailer.rb +4 -0
- data/test_app/app/models/application_record.rb +3 -0
- data/test_app/app/models/concerns/.keep +0 -0
- data/test_app/app/models/order.rb +67 -0
- data/test_app/app/views/layouts/application.html.erb +28 -0
- data/test_app/app/views/layouts/mailer.html.erb +13 -0
- data/test_app/app/views/layouts/mailer.text.erb +1 -0
- data/test_app/app/views/orders/create.html.erb +2 -0
- data/test_app/app/views/orders/index.html.erb +41 -0
- data/test_app/app/views/orders/new.html.erb +44 -0
- data/test_app/app/views/orders/show.html.erb +36 -0
- data/test_app/app/views/payments/_receipt_links.html.erb +31 -0
- data/test_app/app/views/payments/callback.html.erb +2 -0
- data/test_app/app/views/payments/create.html.erb +2 -0
- data/test_app/app/views/payments/failure.html.erb +20 -0
- data/test_app/app/views/payments/new.html.erb +145 -0
- data/test_app/app/views/payments/receipt.html.erb +156 -0
- data/test_app/app/views/payments/success.html.erb +34 -0
- data/test_app/app/views/pwa/manifest.json.erb +22 -0
- data/test_app/app/views/pwa/service-worker.js +26 -0
- data/test_app/bin/brakeman +7 -0
- data/test_app/bin/dev +2 -0
- data/test_app/bin/docker-entrypoint +14 -0
- data/test_app/bin/importmap +4 -0
- data/test_app/bin/jobs +6 -0
- data/test_app/bin/kamal +16 -0
- data/test_app/bin/rails +4 -0
- data/test_app/bin/rake +4 -0
- data/test_app/bin/rubocop +8 -0
- data/test_app/bin/setup +65 -0
- data/test_app/bin/thrust +5 -0
- data/test_app/config/application.rb +42 -0
- data/test_app/config/boot.rb +4 -0
- data/test_app/config/cable.yml +17 -0
- data/test_app/config/cache.yml +16 -0
- data/test_app/config/credentials.yml.enc +1 -0
- data/test_app/config/database.yml +41 -0
- data/test_app/config/deploy.yml +116 -0
- data/test_app/config/environment.rb +5 -0
- data/test_app/config/environments/development.rb +74 -0
- data/test_app/config/environments/production.rb +90 -0
- data/test_app/config/environments/test.rb +53 -0
- data/test_app/config/importmap.rb +7 -0
- data/test_app/config/initializers/assets.rb +7 -0
- data/test_app/config/initializers/content_security_policy.rb +25 -0
- data/test_app/config/initializers/filter_parameter_logging.rb +8 -0
- data/test_app/config/initializers/inflections.rb +16 -0
- data/test_app/config/initializers/kcppayments_rails.rb +29 -0
- data/test_app/config/locales/en.yml +31 -0
- data/test_app/config/master.key +1 -0
- data/test_app/config/puma.rb +41 -0
- data/test_app/config/queue.yml +18 -0
- data/test_app/config/recurring.yml +15 -0
- data/test_app/config/routes.rb +29 -0
- data/test_app/config/storage.yml +34 -0
- data/test_app/config.ru +6 -0
- data/test_app/db/cable_schema.rb +11 -0
- data/test_app/db/cache_schema.rb +14 -0
- data/test_app/db/migrate/20250827075913_create_orders.rb +16 -0
- data/test_app/db/migrate/20250827121258_add_kcp_fields_to_orders.rb +6 -0
- data/test_app/db/queue_schema.rb +129 -0
- data/test_app/db/schema.rb +28 -0
- data/test_app/db/seeds.rb +80 -0
- data/test_app/lib/tasks/.keep +0 -0
- data/test_app/log/.keep +0 -0
- data/test_app/public/.well-known/appspecific/com.chrome.devtools.json +1 -0
- data/test_app/public/400.html +114 -0
- data/test_app/public/404.html +114 -0
- data/test_app/public/406-unsupported-browser.html +114 -0
- data/test_app/public/422.html +114 -0
- data/test_app/public/500.html +114 -0
- data/test_app/public/icon.png +0 -0
- data/test_app/public/icon.svg +3 -0
- data/test_app/public/robots.txt +1 -0
- data/test_app/script/.keep +0 -0
- data/test_app/tmp/.keep +0 -0
- data/test_app/tmp/restart.txt +0 -0
- data/test_app/vendor/.keep +0 -0
- data/test_app/vendor/javascript/.keep +0 -0
- metadata +184 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 7026a79427dd30b522920128a591957439895f19d06776aab7e96766ee05d596
|
|
4
|
+
data.tar.gz: b9d7886e466d486c3921c29dac75abebd0922ef9391712d49e5ad52abfc5c2cc
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 426bb8883a6ea59ba25631ee2afe05dd5d3464ceb617086fef2e1c1d48576dd441ccb533db677b715eaf89426f0e43e54410670abc68288026d7eb82df2ca46c
|
|
7
|
+
data.tar.gz: 50098b2c35b0d1cf665a37bd7ed1314a54f2b21435a53d720906f8cf2163caf6cbf40172eaba17a0a2fd1020bc186a89abfa00ec8ce7a64f006aa45b01eaac0f
|
data/CHANGELOG.md
ADDED
data/README.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# KCP Payments Rails
|
|
2
|
+
|
|
3
|
+
Rails 7/8에서 KCP 표준결제 연동을 간편하게 도와주는 Rails Engine + Stimulus 래퍼 젬입니다.
|
|
4
|
+
|
|
5
|
+
## 설치
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
gem "kcppayments_rails", github: "luciuschoi/kcppayments_rails"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
bundle install
|
|
13
|
+
bin/rails g kcppayments_rails:install
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## 초기화 설정
|
|
17
|
+
|
|
18
|
+
`config/initializers/kcppayments_rails.rb` 파일을 열어 KCP 상점 정보를 설정하세요.
|
|
19
|
+
|
|
20
|
+
## 뷰 사용 예시 (ERB)
|
|
21
|
+
|
|
22
|
+
```erb
|
|
23
|
+
<%= kcp_script_tag %>
|
|
24
|
+
|
|
25
|
+
<%= form_with url: checkout_payments_path, method: :post, **kcp_form_attrs(
|
|
26
|
+
order_id: @order.number,
|
|
27
|
+
amount: @order.total_price,
|
|
28
|
+
buyer_name: current_user.name,
|
|
29
|
+
buyer_email: current_user.email,
|
|
30
|
+
product_name: @order.summary,
|
|
31
|
+
return_url: payment_complete_url
|
|
32
|
+
) do |f| %>
|
|
33
|
+
<button data-controller="kcp" data-action="click->kcp#requestPayment">결제하기</button>
|
|
34
|
+
<% end %>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## 서버 처리
|
|
38
|
+
|
|
39
|
+
승인/취소 등의 서버-서버 통신은 `KcppaymentsRails::Client`를 사용하세요.
|
|
40
|
+
|
|
41
|
+
```ruby
|
|
42
|
+
client = KcppaymentsRails::Client.new
|
|
43
|
+
result = client.approve({ # KCP 요구 파라미터 })
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## 주의사항
|
|
47
|
+
|
|
48
|
+
- KCP 표준결제는 레거시 스크립트를 로드합니다. `kcp_script_tag`를 `<head>` 혹은 `<body>` 상단에서 불러오세요.
|
|
49
|
+
- 상점코드(`site_cd`)와 키(`site_key`)는 환경변수 또는 이니셜라이저에서 설정하세요.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
|
|
5
|
+
module KcppaymentsRails
|
|
6
|
+
module Generators
|
|
7
|
+
class InstallGenerator < Rails::Generators::Base
|
|
8
|
+
source_root File.expand_path("templates", __dir__)
|
|
9
|
+
|
|
10
|
+
def copy_stimulus_controller
|
|
11
|
+
template "kcp_controller.js", "app/javascript/controllers/kcp_controller.js"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def create_initializer
|
|
15
|
+
template "kcppayments_rails.rb", "config/initializers/kcppayments_rails.rb"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
|
2
|
+
|
|
3
|
+
// data-controller="kcp"
|
|
4
|
+
// Values:
|
|
5
|
+
// data-kcp-order-id-value
|
|
6
|
+
// data-kcp-amount-value
|
|
7
|
+
// data-kcp-buyer-name-value
|
|
8
|
+
// data-kcp-buyer-email-value
|
|
9
|
+
// data-kcp-product-name-value
|
|
10
|
+
// data-kcp-return-url-value
|
|
11
|
+
// data-kcp-escrow-value
|
|
12
|
+
// data-kcp-tax-free-amount-value
|
|
13
|
+
|
|
14
|
+
export default class extends Controller {
|
|
15
|
+
static values = {
|
|
16
|
+
orderId: String,
|
|
17
|
+
amount: Number,
|
|
18
|
+
buyerName: String,
|
|
19
|
+
buyerEmail: String,
|
|
20
|
+
productName: String,
|
|
21
|
+
returnUrl: String,
|
|
22
|
+
escrow: Boolean,
|
|
23
|
+
taxFreeAmount: Number,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
connect() {
|
|
27
|
+
// KCP 스크립트는 서버에서 include되어야 함 (kcp_script_tag)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
requestPayment(event) {
|
|
31
|
+
event?.preventDefault();
|
|
32
|
+
|
|
33
|
+
if (typeof window.KCP_Pay_Execute !== "function") {
|
|
34
|
+
console.error(
|
|
35
|
+
"KCP_Pay_Execute is not loaded. Make sure kcp_script_tag is included."
|
|
36
|
+
);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 결제 실행 (KCP 페이지의 form name/id 관례를 따를 수 있음)
|
|
41
|
+
const form = this.element.closest("form");
|
|
42
|
+
if (!form) {
|
|
43
|
+
console.error("No form element found for KCP payment.");
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// KCP_Pay_Execute(form) 같은 레거시 호출을 래핑
|
|
48
|
+
try {
|
|
49
|
+
window.KCP_Pay_Execute(form);
|
|
50
|
+
} catch (e) {
|
|
51
|
+
console.error("KCP_Pay_Execute failed:", e);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# KcpPaymentsRails 초기화 설정
|
|
4
|
+
KcppaymentsRails.configure do |config|
|
|
5
|
+
# 테스트/운영 계정에 맞게 설정하세요
|
|
6
|
+
# config.site_cd = ENV["KCP_SITE_CD"]
|
|
7
|
+
# config.site_key = ENV["KCP_SITE_KEY"]
|
|
8
|
+
# config.gateway_url = "https://testpaygw.kcp.co.kr"
|
|
9
|
+
# config.js_url = "https://testpay.kcp.co.kr/plugin/payplus_web.jsp"
|
|
10
|
+
# config.escrow = false
|
|
11
|
+
# config.tax_free_amount_field = :tax_free_amount
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "net/http"
|
|
4
|
+
require "uri"
|
|
5
|
+
require "json"
|
|
6
|
+
|
|
7
|
+
module KcppaymentsRails
|
|
8
|
+
class Client
|
|
9
|
+
# 서버-서버 통신용 기본 클라이언트 (승인/취소 등)
|
|
10
|
+
def initialize(site_cd: KcppaymentsRails.configuration.site_cd, site_key: KcppaymentsRails.configuration.site_key, gateway_url: KcppaymentsRails.configuration.gateway_url)
|
|
11
|
+
@site_cd = site_cd
|
|
12
|
+
@site_key = site_key
|
|
13
|
+
@gateway_url = gateway_url
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def approve(params)
|
|
17
|
+
post("/center/pp_ax_hub.jsp", params)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def cancel(params)
|
|
21
|
+
post("/center/pp_ax_hub.jsp", params.merge(type: "cancel"))
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def post(path, params)
|
|
27
|
+
uri = URI.join(@gateway_url, path)
|
|
28
|
+
req = Net::HTTP::Post.new(uri)
|
|
29
|
+
req.set_form_data(params.merge(site_cd: @site_cd, site_key: @site_key))
|
|
30
|
+
|
|
31
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
32
|
+
http.use_ssl = uri.scheme == "https"
|
|
33
|
+
res = http.request(req)
|
|
34
|
+
parse_response(res)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def parse_response(res)
|
|
38
|
+
content_type = res["Content-Type"]
|
|
39
|
+
if content_type&.include?("json")
|
|
40
|
+
JSON.parse(res.body)
|
|
41
|
+
else
|
|
42
|
+
# KCP는 key=value\n 형태도 반환함
|
|
43
|
+
res.body.to_s.split(/\r?\n/).map { |l| l.split("=", 2) }.to_h
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module KcppaymentsRails
|
|
4
|
+
class Configuration
|
|
5
|
+
# KCP 표준결제 기본 설정 값들
|
|
6
|
+
attr_accessor :site_cd, :site_key, :gateway_url, :js_url, :escrow, :tax_free_amount_field
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
@site_cd = ENV["KCP_SITE_CD"]
|
|
10
|
+
@site_key = ENV["KCP_SITE_KEY"]
|
|
11
|
+
@gateway_url = ENV["KCP_GATEWAY_URL"] || "https://testpaygw.kcp.co.kr"
|
|
12
|
+
@js_url = ENV["KCP_JS_URL"] || "https://testpay.kcp.co.kr/plugin/payplus_web.jsp"
|
|
13
|
+
# @target_url = if Rails.env.production? # 운영서버
|
|
14
|
+
# "https://spl.kcp.co.kr"
|
|
15
|
+
# else # Rails.env.development? || Rails.env.staging? # 개발서버 또는 스테이징서버
|
|
16
|
+
# "https://stg-spl.kcp.co.kr"
|
|
17
|
+
# end
|
|
18
|
+
@escrow = false
|
|
19
|
+
@tax_free_amount_field = :tax_free_amount
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/engine"
|
|
4
|
+
|
|
5
|
+
module KcppaymentsRails
|
|
6
|
+
class Engine < ::Rails::Engine
|
|
7
|
+
isolate_namespace KcppaymentsRails
|
|
8
|
+
|
|
9
|
+
initializer "kcppayments_rails.view_helpers" do
|
|
10
|
+
ActiveSupport.on_load(:action_view) do
|
|
11
|
+
include KcppaymentsRails::KcpHelper
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
ActiveSupport.on_load(:action_controller) do
|
|
15
|
+
helper KcppaymentsRails::KcpHelper
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module KcppaymentsRails
|
|
4
|
+
module KcpHelper
|
|
5
|
+
# KCP 표준결제 스크립트 로더
|
|
6
|
+
def kcp_script_tag
|
|
7
|
+
src = KcppaymentsRails.configuration.js_url
|
|
8
|
+
# KCP는 전통적으로 jsp 경로를 로드합니다 (ex: payplus_web.jsp)
|
|
9
|
+
# 레거시 리소스이므로 async/defer 없이 삽입하는 것을 권장합니다.
|
|
10
|
+
javascript_include_tag(src)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# 결제 폼에 필요한 hidden 필드 및 data-controller 속성을 부여
|
|
14
|
+
# form_with 등의 블록 내부에서 사용
|
|
15
|
+
def kcp_form_attrs(order_id:, amount:, buyer_name:, buyer_email:, product_name:, return_url: nil, escrow: nil, tax_free_amount: nil)
|
|
16
|
+
{
|
|
17
|
+
data: {
|
|
18
|
+
controller: "kcp",
|
|
19
|
+
kcp_order_id_value: order_id,
|
|
20
|
+
kcp_amount_value: amount,
|
|
21
|
+
kcp_buyer_name_value: buyer_name,
|
|
22
|
+
kcp_buyer_email_value: buyer_email,
|
|
23
|
+
kcp_product_name_value: product_name,
|
|
24
|
+
kcp_return_url_value: return_url,
|
|
25
|
+
kcp_escrow_value: (escrow.nil? ? KcppaymentsRails.configuration.escrow : escrow),
|
|
26
|
+
kcp_tax_free_amount_value: tax_free_amount
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "kcppayments_rails/version"
|
|
4
|
+
require_relative "kcppayments_rails/configuration"
|
|
5
|
+
require_relative "kcppayments_rails/client"
|
|
6
|
+
require_relative "kcppayments_rails/helpers/kcp_helper"
|
|
7
|
+
|
|
8
|
+
begin
|
|
9
|
+
require_relative "kcppayments_rails/engine"
|
|
10
|
+
rescue LoadError
|
|
11
|
+
# Rails가 아닌 환경에서는 엔진을 로드하지 않습니다.
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
module KcppaymentsRails
|
|
15
|
+
class Error < StandardError; end
|
|
16
|
+
|
|
17
|
+
class << self
|
|
18
|
+
def configuration
|
|
19
|
+
@configuration ||= Configuration.new
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def configure
|
|
23
|
+
yield(configuration)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# See https://docs.docker.com/engine/reference/builder/#dockerignore-file for more about ignoring files.
|
|
2
|
+
|
|
3
|
+
# Ignore git directory.
|
|
4
|
+
/.git/
|
|
5
|
+
/.gitignore
|
|
6
|
+
|
|
7
|
+
# Ignore bundler config.
|
|
8
|
+
/.bundle
|
|
9
|
+
|
|
10
|
+
# Ignore all environment files.
|
|
11
|
+
/.env*
|
|
12
|
+
|
|
13
|
+
# Ignore all default key files.
|
|
14
|
+
/config/master.key
|
|
15
|
+
/config/credentials/*.key
|
|
16
|
+
|
|
17
|
+
# Ignore all logfiles and tempfiles.
|
|
18
|
+
/log/*
|
|
19
|
+
/tmp/*
|
|
20
|
+
!/log/.keep
|
|
21
|
+
!/tmp/.keep
|
|
22
|
+
|
|
23
|
+
# Ignore pidfiles, but keep the directory.
|
|
24
|
+
/tmp/pids/*
|
|
25
|
+
!/tmp/pids/.keep
|
|
26
|
+
|
|
27
|
+
# Ignore storage (uploaded files in development and any SQLite databases).
|
|
28
|
+
/storage/*
|
|
29
|
+
!/storage/.keep
|
|
30
|
+
/tmp/storage/*
|
|
31
|
+
!/tmp/storage/.keep
|
|
32
|
+
|
|
33
|
+
# Ignore assets.
|
|
34
|
+
/node_modules/
|
|
35
|
+
/app/assets/builds/*
|
|
36
|
+
!/app/assets/builds/.keep
|
|
37
|
+
/public/assets
|
|
38
|
+
|
|
39
|
+
# Ignore CI service files.
|
|
40
|
+
/.github
|
|
41
|
+
|
|
42
|
+
# Ignore Kamal files.
|
|
43
|
+
/config/deploy*.yml
|
|
44
|
+
/.kamal
|
|
45
|
+
|
|
46
|
+
# Ignore development files
|
|
47
|
+
/.devcontainer
|
|
48
|
+
|
|
49
|
+
# Ignore Docker-related files
|
|
50
|
+
/.dockerignore
|
|
51
|
+
/Dockerfile*
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches: [ main ]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
scan_ruby:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
|
|
12
|
+
steps:
|
|
13
|
+
- name: Checkout code
|
|
14
|
+
uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Set up Ruby
|
|
17
|
+
uses: ruby/setup-ruby@v1
|
|
18
|
+
with:
|
|
19
|
+
ruby-version: .ruby-version
|
|
20
|
+
bundler-cache: true
|
|
21
|
+
|
|
22
|
+
- name: Scan for common Rails security vulnerabilities using static analysis
|
|
23
|
+
run: bin/brakeman --no-pager
|
|
24
|
+
|
|
25
|
+
scan_js:
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
|
|
28
|
+
steps:
|
|
29
|
+
- name: Checkout code
|
|
30
|
+
uses: actions/checkout@v4
|
|
31
|
+
|
|
32
|
+
- name: Set up Ruby
|
|
33
|
+
uses: ruby/setup-ruby@v1
|
|
34
|
+
with:
|
|
35
|
+
ruby-version: .ruby-version
|
|
36
|
+
bundler-cache: true
|
|
37
|
+
|
|
38
|
+
- name: Scan for security vulnerabilities in JavaScript dependencies
|
|
39
|
+
run: bin/importmap audit
|
|
40
|
+
|
|
41
|
+
lint:
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
steps:
|
|
44
|
+
- name: Checkout code
|
|
45
|
+
uses: actions/checkout@v4
|
|
46
|
+
|
|
47
|
+
- name: Set up Ruby
|
|
48
|
+
uses: ruby/setup-ruby@v1
|
|
49
|
+
with:
|
|
50
|
+
ruby-version: .ruby-version
|
|
51
|
+
bundler-cache: true
|
|
52
|
+
|
|
53
|
+
- name: Lint code for consistent style
|
|
54
|
+
run: bin/rubocop -f github
|
|
55
|
+
|
data/test_app/.gitignore
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
|
|
2
|
+
#
|
|
3
|
+
# If you find yourself ignoring temporary files generated by your text editor
|
|
4
|
+
# or operating system, you probably want to add a global ignore instead:
|
|
5
|
+
# git config --global core.excludesfile '~/.gitignore_global'
|
|
6
|
+
|
|
7
|
+
# Ignore bundler config.
|
|
8
|
+
/.bundle
|
|
9
|
+
|
|
10
|
+
# Ignore all environment files (dotenv).
|
|
11
|
+
/.env*
|
|
12
|
+
!/.env*.enc
|
|
13
|
+
|
|
14
|
+
# Ignore all logfiles and tempfiles.
|
|
15
|
+
/log/*
|
|
16
|
+
/tmp/*
|
|
17
|
+
!/log/.keep
|
|
18
|
+
!/tmp/.keep
|
|
19
|
+
|
|
20
|
+
# Ignore pidfiles, but keep the directory.
|
|
21
|
+
/tmp/pids/*
|
|
22
|
+
!/tmp/pids/
|
|
23
|
+
!/tmp/pids/.keep
|
|
24
|
+
|
|
25
|
+
/tmp/cache/*
|
|
26
|
+
!/tmp/cache/
|
|
27
|
+
!/tmp/cache/.keep
|
|
28
|
+
|
|
29
|
+
# Ignore storage (uploaded files in development and any SQLite databases).
|
|
30
|
+
/storage/*
|
|
31
|
+
!/storage/.keep
|
|
32
|
+
/tmp/storage/*
|
|
33
|
+
!/tmp/storage/
|
|
34
|
+
!/tmp/storage/.keep
|
|
35
|
+
|
|
36
|
+
/public/assets
|
|
37
|
+
|
|
38
|
+
# Ignore master key for decrypting credentials and more.
|
|
39
|
+
/config/master.key
|
|
40
|
+
|
|
41
|
+
# Ignore database files
|
|
42
|
+
/db/*.sqlite3
|
|
43
|
+
/db/*.sqlite3-*
|
|
44
|
+
|
|
45
|
+
# Ignore test files
|
|
46
|
+
/coverage/
|
|
47
|
+
|
|
48
|
+
# Ignore node_modules
|
|
49
|
+
/node_modules
|
|
50
|
+
|
|
51
|
+
# Ignore yarn files
|
|
52
|
+
/yarn-error.log
|
|
53
|
+
yarn-debug.log*
|
|
54
|
+
.yarn-integrity
|
|
55
|
+
|
|
56
|
+
# Ignore editor and system files
|
|
57
|
+
.DS_Store
|
|
58
|
+
*~
|
|
59
|
+
.*.sw?
|
|
60
|
+
.#*
|
|
61
|
+
\#*#
|
|
62
|
+
/.emacs.desktop
|
|
63
|
+
/.emacs.desktop.lock
|
|
64
|
+
*.elc
|
|
65
|
+
auto-save-list
|
|
66
|
+
tramp
|
|
67
|
+
.\#*
|
|
68
|
+
|
|
69
|
+
# Ignore RubyMine files
|
|
70
|
+
/.idea
|
|
71
|
+
|
|
72
|
+
# Ignore Sublime Text files
|
|
73
|
+
/*.sublime*
|
|
74
|
+
|
|
75
|
+
# Ignore VS Code files
|
|
76
|
+
.vscode/
|
|
77
|
+
|
|
78
|
+
# Ignore vim swap files
|
|
79
|
+
*.swp
|
|
80
|
+
*.swo
|
|
81
|
+
|
|
82
|
+
# Ignore generated files
|
|
83
|
+
/public/packs
|
|
84
|
+
/public/packs-test
|
|
85
|
+
/public/vite*
|
|
86
|
+
/app/assets/builds/*
|
|
87
|
+
!/app/assets/builds/.keep
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
|
|
3
|
+
# A sample post-deploy hook
|
|
4
|
+
#
|
|
5
|
+
# These environment variables are available:
|
|
6
|
+
# KAMAL_RECORDED_AT
|
|
7
|
+
# KAMAL_PERFORMER
|
|
8
|
+
# KAMAL_VERSION
|
|
9
|
+
# KAMAL_HOSTS
|
|
10
|
+
# KAMAL_ROLES (if set)
|
|
11
|
+
# KAMAL_DESTINATION (if set)
|
|
12
|
+
# KAMAL_RUNTIME
|
|
13
|
+
|
|
14
|
+
echo "$KAMAL_PERFORMER deployed $KAMAL_VERSION to $KAMAL_DESTINATION in $KAMAL_RUNTIME seconds"
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
|
|
3
|
+
# A sample pre-build hook
|
|
4
|
+
#
|
|
5
|
+
# Checks:
|
|
6
|
+
# 1. We have a clean checkout
|
|
7
|
+
# 2. A remote is configured
|
|
8
|
+
# 3. The branch has been pushed to the remote
|
|
9
|
+
# 4. The version we are deploying matches the remote
|
|
10
|
+
#
|
|
11
|
+
# These environment variables are available:
|
|
12
|
+
# KAMAL_RECORDED_AT
|
|
13
|
+
# KAMAL_PERFORMER
|
|
14
|
+
# KAMAL_VERSION
|
|
15
|
+
# KAMAL_HOSTS
|
|
16
|
+
# KAMAL_ROLES (if set)
|
|
17
|
+
# KAMAL_DESTINATION (if set)
|
|
18
|
+
|
|
19
|
+
if [ -n "$(git status --porcelain)" ]; then
|
|
20
|
+
echo "Git checkout is not clean, aborting..." >&2
|
|
21
|
+
git status --porcelain >&2
|
|
22
|
+
exit 1
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
first_remote=$(git remote)
|
|
26
|
+
|
|
27
|
+
if [ -z "$first_remote" ]; then
|
|
28
|
+
echo "No git remote set, aborting..." >&2
|
|
29
|
+
exit 1
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
current_branch=$(git branch --show-current)
|
|
33
|
+
|
|
34
|
+
if [ -z "$current_branch" ]; then
|
|
35
|
+
echo "Not on a git branch, aborting..." >&2
|
|
36
|
+
exit 1
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
remote_head=$(git ls-remote $first_remote --tags $current_branch | cut -f1)
|
|
40
|
+
|
|
41
|
+
if [ -z "$remote_head" ]; then
|
|
42
|
+
echo "Branch not pushed to remote, aborting..." >&2
|
|
43
|
+
exit 1
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
if [ "$KAMAL_VERSION" != "$remote_head" ]; then
|
|
47
|
+
echo "Version ($KAMAL_VERSION) does not match remote HEAD ($remote_head), aborting..." >&2
|
|
48
|
+
exit 1
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
exit 0
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# A sample pre-connect check
|
|
4
|
+
#
|
|
5
|
+
# Warms DNS before connecting to hosts in parallel
|
|
6
|
+
#
|
|
7
|
+
# These environment variables are available:
|
|
8
|
+
# KAMAL_RECORDED_AT
|
|
9
|
+
# KAMAL_PERFORMER
|
|
10
|
+
# KAMAL_VERSION
|
|
11
|
+
# KAMAL_HOSTS
|
|
12
|
+
# KAMAL_ROLES (if set)
|
|
13
|
+
# KAMAL_DESTINATION (if set)
|
|
14
|
+
# KAMAL_RUNTIME
|
|
15
|
+
|
|
16
|
+
hosts = ENV["KAMAL_HOSTS"].split(",")
|
|
17
|
+
results = nil
|
|
18
|
+
max = 3
|
|
19
|
+
|
|
20
|
+
elapsed = Benchmark.realtime do
|
|
21
|
+
results = hosts.map do |host|
|
|
22
|
+
Thread.new do
|
|
23
|
+
tries = 1
|
|
24
|
+
|
|
25
|
+
begin
|
|
26
|
+
Socket.getaddrinfo(host, 0, Socket::AF_UNSPEC, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME)
|
|
27
|
+
rescue SocketError
|
|
28
|
+
if tries < max
|
|
29
|
+
puts "Retrying DNS warmup: #{host}"
|
|
30
|
+
tries += 1
|
|
31
|
+
sleep rand
|
|
32
|
+
retry
|
|
33
|
+
else
|
|
34
|
+
puts "DNS warmup failed: #{host}"
|
|
35
|
+
host
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
tries
|
|
40
|
+
end
|
|
41
|
+
end.map(&:value)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
retries = results.sum - hosts.size
|
|
45
|
+
nopes = results.count { |r| r == max }
|
|
46
|
+
|
|
47
|
+
puts "Prewarmed %d DNS lookups in %.2f sec: %d retries, %d failures" % [ hosts.size, elapsed, retries, nopes ]
|