capistrano-nuxt2 0.2.17

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8fc077f38a45719b8343f127005d7eba3d591b5aa9b55c4d4e85b186f7688034
4
+ data.tar.gz: 33242fcbb894de7223cac35b376dca936f2980fcc0a7cd707c33498789156faf
5
+ SHA512:
6
+ metadata.gz: e6ed8fc6a8971a54ae645c7dcca0323961d825dc97a0ebdf8ec039a7ea4adacbb783c90906cb89dc57d6cd6baea651713508f3424edf41b87a2a567944998b2c
7
+ data.tar.gz: 54db3f4d71e1d6b066c65ec7a311be3218cbc948036cc50829659921aae0bfb5f5c6d64d88dbe7bc263a22bd5e2113e40f31779140c66aff22095d80f02a29ff
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright Austin Strange
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,222 @@
1
+ # Capistrano::Nuxt2
2
+
3
+ Capistrano recipes to deploy nuxt 2 apps.
4
+
5
+
6
+ ## Usage
7
+
8
+ prepare the server:
9
+ ```sh
10
+ bundle exec cap production setup
11
+ ```
12
+
13
+ deploy the app:
14
+ ```sh
15
+ bundle exec cap production deploy
16
+ ```
17
+
18
+ create SSL certificates:
19
+ ```sh
20
+ bundle exec cap production certbot:generate
21
+ ```
22
+
23
+ ## Installation
24
+
25
+ Inside your **Nuxt.js project root**, run:
26
+
27
+ ```sh
28
+ bundle init
29
+ ```
30
+
31
+ Then, edit the `Gemfile` and add:
32
+
33
+ ```ruby
34
+ source "https://rubygems.org"
35
+
36
+ gem "capistrano-nuxt2", require: false, github: "2strange/capistrano-nuxt2"
37
+ ```
38
+
39
+ Then, install the dependencies:
40
+
41
+ ```sh
42
+ bundle install
43
+ ```
44
+
45
+ #### Initialize Capistrano
46
+
47
+ ```sh
48
+ bundle exec cap install
49
+ ```
50
+
51
+ This will generate the following files:
52
+ ```
53
+ .
54
+ ├── Capfile
55
+ ├── config
56
+ │ ├── deploy.rb
57
+ │ ├── deploy
58
+ │ │ ├── production.rb
59
+ │ │ ├── staging.rb
60
+ │ │ ├── development.rb
61
+ │ │ └── shared.rb
62
+ ├── lib
63
+ │ └── capistrano
64
+ │ └── tasks
65
+ └── ...
66
+ ```
67
+
68
+ #### Add the following to your `Capfile`:
69
+
70
+ ```ruby
71
+ require "capistrano/nuxt2"
72
+ ## or as you want
73
+ require "capistrano/nuxt2/nuxt"
74
+ ## for certbot
75
+ require "capistrano/nuxt2/certbot"
76
+ ## for nginx
77
+ require "capistrano/nuxt2/nginx"
78
+ ## for nginx with a proxy server
79
+ require "capistrano/nuxt2/proxy_nginx"
80
+ ```
81
+
82
+ #### Add the following to your `config/deploy.rb`:
83
+
84
+ ```ruby
85
+ set :application , "my-nuxt-app"
86
+ set :repo_url, "git_path_to_your_repo"
87
+ ```
88
+
89
+ #### Add the following to your `config/deploy/-STAGE-.rb`:
90
+
91
+ ```ruby
92
+ server "SERVER_DOMAIN_OR_IP", user: "DEPLOY_USER", roles: %w{web}
93
+
94
+ set :user, "DEPLOY_USER"
95
+ set :deploy_to, "/home/#{fetch(:user)}/#{fetch(:application)}-#{fetch(:stage)}"
96
+
97
+ set :branch, 'STAGE_BRANCH'
98
+
99
+ ## NginX
100
+ set :nginx_domains, ["YOUR_DOMAIN"]
101
+ set :nginx_remove_www, true
102
+
103
+ ## ssl-handling
104
+ set :nginx_use_ssl, true
105
+ set :certbot_email, "YOUR_EMAIL"
106
+
107
+ ```
108
+
109
+ #### For nginx with a proxy server
110
+
111
+ ```ruby
112
+
113
+ server "100.200.300.23", user: "deploy", roles: %w{app web}
114
+ server "100.200.300.42", user: "deploy", roles: %w{proxy}, no_release: true
115
+
116
+ set :user, "DEPLOY_USER"
117
+ set :deploy_to, "/home/#{fetch(:user)}/#{fetch(:application)}-#{fetch(:stage)}"
118
+
119
+ set :branch, 'STAGE_BRANCH'
120
+
121
+ set :nginx_upstream_host, "100.200.300.23"
122
+ set :nginx_upstream_port, "3550"
123
+
124
+ ## NginX
125
+ set :nginx_domains, ["YOUR_DOMAIN"]
126
+
127
+ ## ssl-handling
128
+ set :nginx_use_ssl, true
129
+ set :certbot_email, "YOUR_EMAIL"
130
+
131
+ ```
132
+
133
+ ---
134
+
135
+ ## Nuxt 3
136
+
137
+ The `nuxt3:` tasks deploy Nuxt 3 apps. Nuxt 3 builds into `.output/`
138
+ (`.output/server/index.mjs` for SSR, `.output/public/` for static assets),
139
+ not `dist/`. Two modes are supported:
140
+
141
+ - **SSR (default):** `nuxt build` → the Nitro server runs as a systemd service
142
+ (`node .output/server/index.mjs`), with the existing proxy in front of it.
143
+ - **Static:** `nuxt generate` → `.output/public/` is synced to `shared/www`
144
+ and served directly by nginx (no node service). Good for content sites.
145
+
146
+ Opt-in, leaves the Nuxt-2 `nuxt:` tasks untouched. Add to your `Capfile`:
147
+
148
+ ```ruby
149
+ require "capistrano/nuxt2/nuxt3"
150
+ ## plus proxy + certbot as needed
151
+ require "capistrano/nuxt2/proxy_nginx"
152
+ require "capistrano/nuxt2/certbot"
153
+ ```
154
+
155
+ #### SSR app — `config/deploy/-STAGE-.rb`
156
+
157
+ ```ruby
158
+ server "100.200.300.23", user: "deploy", roles: %w{app web}
159
+ server "100.200.300.42", user: "deploy", roles: %w{proxy}, no_release: true
160
+
161
+ set :user, "DEPLOY_USER"
162
+ set :deploy_to, "/home/#{fetch(:user)}/#{fetch(:application)}-#{fetch(:stage)}"
163
+ set :branch, 'STAGE_BRANCH'
164
+
165
+ ## Nuxt 3 SSR (Nitro node service)
166
+ set :nuxt3_deploy_mode, :ssr # default
167
+ set :nuxt3_use_nvm, true
168
+ set :nuxt3_nvm_version, "20.19.0"
169
+ set :nuxt3_ssr_port, 3500 # Nitro listens here (127.0.0.1)
170
+ set :nuxt3_ssr_env, { "NUXT_PUBLIC_API_BASE" => "https://api.example.com" }
171
+
172
+ ## Proxy points at the Nitro service
173
+ set :nginx_upstream_host, "100.200.300.23"
174
+ set :nginx_upstream_port, fetch(:nuxt3_ssr_port)
175
+
176
+ ## NginX / SSL
177
+ set :nginx_domains, ["YOUR_DOMAIN"]
178
+ set :nginx_use_ssl, true
179
+ set :certbot_email, "YOUR_EMAIL"
180
+ ```
181
+
182
+ #### Static site — `config/deploy/-STAGE-.rb`
183
+
184
+ ```ruby
185
+ server "SERVER_DOMAIN_OR_IP", user: "DEPLOY_USER", roles: %w{web}
186
+
187
+ set :user, "DEPLOY_USER"
188
+ set :deploy_to, "/home/#{fetch(:user)}/#{fetch(:application)}-#{fetch(:stage)}"
189
+ set :branch, 'STAGE_BRANCH'
190
+
191
+ set :nuxt3_deploy_mode, :static
192
+ set :nuxt3_use_nvm, true
193
+ set :nuxt3_nvm_version, "20.19.0"
194
+
195
+ ## NginX / SSL (serves shared/www directly)
196
+ set :nginx_domains, ["YOUR_DOMAIN"]
197
+ set :nginx_use_ssl, true
198
+ set :certbot_email, "YOUR_EMAIL"
199
+ ```
200
+
201
+ The `deploy:published` hook rebuilds automatically per `:nuxt3_deploy_mode`.
202
+ Manage the SSR service with `cap <stage> nuxt3:ssr:{setup,activate,restart,check_status,logs}`.
203
+
204
+ **First SSR deploy** (the systemd unit doesn't exist yet, so an auto-restart
205
+ would fail — same as puma/sidekiq):
206
+
207
+ ```ruby
208
+ set :nuxt3_ssr_hooks, false # in the stage file, for the first deploy only
209
+ ```
210
+
211
+ ```sh
212
+ cap <stage> deploy # builds + syncs .output (no restart)
213
+ cap <stage> nuxt3:ssr:configure # uploads + enables + starts the Nitro unit
214
+ ```
215
+
216
+ Then set `nuxt3_ssr_hooks` back to `true` (the default) so subsequent deploys
217
+ restart the service cleanly.
218
+
219
+ ---
220
+
221
+ ## License
222
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ require "bundler/setup"
2
+
3
+ require "bundler/gem_tasks"
@@ -0,0 +1,120 @@
1
+ require 'erb'
2
+ require 'stringio'
3
+
4
+ module Capistrano
5
+ module Nuxt2
6
+ module BaseHelpers
7
+
8
+ def build_deploy_env_var
9
+ app_name = fetch(:application).gsub(/ /,'_').gsub(/-/,'_').upcase
10
+ stage_name = fetch(:stage) == 'production' ? 'PROD' : 'STG'
11
+ "#{ app_name }_#{ stage_name }_DEPLOY_MODE"
12
+ end
13
+
14
+ ## PAth helpers
15
+ def ensure_shared_path(path)
16
+ unless test("[ -d #{path} ]")
17
+ puts "📂 Directory #{path} does not exist. Creating it..."
18
+ execute :mkdir, "-p", path
19
+ else
20
+ puts "✅ Directory #{path} already exists."
21
+ end
22
+ ensure_shared_path_ownership
23
+ end
24
+
25
+ def ensure_shared_www_path
26
+ ensure_shared_path("#{shared_path}/www")
27
+ end
28
+
29
+ def ensure_shared_log_path
30
+ ensure_shared_path("#{shared_path}/log")
31
+ end
32
+
33
+ # Nuxt 3 SSR (Nitro) output dir – holds `server/index.mjs` + `public/`
34
+ def ensure_shared_output_path
35
+ ensure_shared_path("#{shared_path}/#{fetch(:nuxt3_output_folder, 'output')}")
36
+ end
37
+
38
+ # PID dir for the Nitro systemd service (mirrors recipes2go puma/sidekiq)
39
+ def ensure_shared_pids_path
40
+ ensure_shared_path("#{shared_path}/pids")
41
+ end
42
+
43
+ # bash -lc prefix that activates the requested node version via nvm.
44
+ # Mirrors the nvm pattern used by the nuxt:/vue: build tasks.
45
+ def nuxt3_nvm_prefix
46
+ "source #{fetch(:nuxt3_nvm_script)} && nvm use #{fetch(:nuxt3_nvm_version)}"
47
+ end
48
+
49
+ # Disable + stop + remove an old systemd service file (no-op if absent).
50
+ def remove_app_service(name = "SERVICE", service_path = "/lib/systemd/system", service_file = nil)
51
+ if test("[ -f #{service_path}/#{service_file}.service ]")
52
+ unless test("systemctl is-enabled #{service_file} || echo disabled") == "disabled"
53
+ info "🔧 Disabling #{service_file} service..."
54
+ execute :sudo, "systemctl disable #{service_file}"
55
+ else
56
+ info "✅ #{service_file} is already disabled, skipping."
57
+ end
58
+ puts "🔄 Stopping old #{name} service: #{service_file}.service"
59
+ execute :sudo, "systemctl stop #{service_file}"
60
+ puts "🗑 Removing old #{name} service file: #{service_file}.service"
61
+ execute :sudo, :rm, "-f", "#{service_path}/#{service_file}.service"
62
+ else
63
+ puts "⚠️ Old #{name} service file #{service_file}.service does not exist, skipping removal."
64
+ end
65
+ end
66
+
67
+ def ensure_shared_path_ownership
68
+ # Fix ownership only if needed (avoids unnecessary chown operations)
69
+ unless test("stat -c '%U:%G' #{shared_path} | grep #{fetch(:user)}:#{fetch(:user)}")
70
+ puts "🔧 Fixing ownership of #{shared_path} and its parent directories..."
71
+ execute :sudo, :chown, "-R #{fetch(:user)}:#{fetch(:user)} #{shared_path}"
72
+ execute :sudo, :chown, "#{fetch(:user)}:#{fetch(:user)} #{fetch(:deploy_to)}"
73
+ else
74
+ puts "✅ Ownership is already correct."
75
+ end
76
+ end
77
+
78
+
79
+
80
+ def template2go(from, to)
81
+ erb = get_template_file(from)
82
+ upload! StringIO.new( ERB.new(erb).result(binding) ), to
83
+ end
84
+
85
+
86
+ def render2go(tmpl)
87
+ erb = get_template_file(tmpl)
88
+ ERB.new(erb).result(binding)
89
+ end
90
+
91
+
92
+ def template_with_role(from, to, role = nil)
93
+ erb = get_template_file(from)
94
+ upload! StringIO.new(ERB.new(erb).result(binding)), to
95
+ end
96
+
97
+
98
+ def get_template_file( from )
99
+ [
100
+ File.join('config', 'deploy', 'templates', "#{from}.erb"),
101
+ File.join('config', 'deploy', 'templates', "#{from}"),
102
+ File.join('lib', 'capistrano', 'templates', "#{from}.erb"),
103
+ File.join('lib', 'capistrano', 'templates', "#{from}"),
104
+ File.expand_path("../../../generators/capistrano/nuxt2/templates/#{from}.erb", __FILE__),
105
+ File.expand_path("../../../generators/capistrano/nuxt2/templates/#{from}", __FILE__)
106
+ ].each do |path|
107
+ return File.read(path) if File.file?(path)
108
+ end
109
+ # false
110
+ raise "File '#{from}' was not found!!!"
111
+ end
112
+
113
+
114
+ end
115
+ end
116
+ end
117
+
118
+
119
+
120
+
@@ -0,0 +1 @@
1
+ load File.expand_path("../../tasks/certbot.rake", __FILE__)
@@ -0,0 +1 @@
1
+ load File.expand_path("../../tasks/nginx.rake", __FILE__)
@@ -0,0 +1,56 @@
1
+ module Capistrano
2
+ module Nuxt2
3
+ module NginxHelpers
4
+ def joiner
5
+ "\n "
6
+ end
7
+
8
+ def clear_domain(domain)
9
+ "#{domain}".gsub(/^www\./, "").gsub(/^\*?\./, "")
10
+ end
11
+
12
+ def subdomain_regex(domain)
13
+ "~^(www\.)?(?<sub>[\w-]+)#{Regexp.escape(".#{domain}")}"
14
+ end
15
+
16
+ def nginx_domains
17
+ Array(fetch(:nginx_domains)).map { |d| clear_domain(d) }.uniq
18
+ end
19
+
20
+ def nginx_domains_with_www
21
+ domains = []
22
+ nginx_domains.each do |domain|
23
+ domains << domain
24
+ domains << "www.#{domain}" unless domain.match(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/)
25
+ domains << ".#{domain}" if fetch(:nginx_domain_wildcard, false)
26
+ end
27
+ domains
28
+ end
29
+
30
+ def nginx_major_domain
31
+ fetch(:nginx_major_domain, false) ? clear_domain(fetch(:nginx_major_domain)) : false
32
+ end
33
+
34
+ def cert_domain
35
+ fetch(:nginx_major_domain, false) ? fetch(:nginx_major_domain) : Array(fetch(:nginx_domains)).first
36
+ end
37
+
38
+ def nginx_all_domains_with_www
39
+ domains = []
40
+ nginx_domains.each do |domain|
41
+ domains << domain
42
+ domains << "www.#{domain}" unless domain.match(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/)
43
+ domains << ".#{domain}" if fetch(:nginx_domain_wildcard, false)
44
+ end
45
+ if nginx_major_domain
46
+ domains << nginx_major_domain
47
+ domains << "www.#{nginx_major_domain}" unless nginx_major_domain.match(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/)
48
+ domains << ".#{nginx_major_domain}" if fetch(:nginx_domain_wildcard, false)
49
+ end
50
+ domains
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+
@@ -0,0 +1 @@
1
+ load File.expand_path("../../tasks/nuxt.rake", __FILE__)
@@ -0,0 +1 @@
1
+ load File.expand_path("../../tasks/nuxt3.rake", __FILE__)
@@ -0,0 +1 @@
1
+ load File.expand_path("../../tasks/proxy_nginx.rake", __FILE__)
@@ -0,0 +1,5 @@
1
+ module Capistrano
2
+ module Nuxt2
3
+ VERSION = "0.2.17"
4
+ end
5
+ end
@@ -0,0 +1 @@
1
+ load File.expand_path("../../tasks/vue.rake", __FILE__)
@@ -0,0 +1,9 @@
1
+ load File.expand_path("../tasks/nuxt.rake", __FILE__)
2
+ load File.expand_path("../tasks/nginx.rake", __FILE__)
3
+ load File.expand_path("../tasks/certbot.rake", __FILE__)
4
+
5
+ # module Capistrano
6
+ # module Nuxt2
7
+ # # Your code goes here...
8
+ # end
9
+ # end