capistrano-nextjs 1.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 +7 -0
- data/CHANGELOG.md +41 -0
- data/LICENSE.txt +68 -0
- data/README.md +65 -0
- data/lib/capistrano/nextjs/systemd.rb +50 -0
- data/lib/capistrano/nextjs/version.rb +7 -0
- data/lib/capistrano/nextjs.rb +108 -0
- data/lib/capistrano/tasks/nextjs.rake +35 -0
- data/lib/capistrano/tasks/systemd.rake +147 -0
- data/lib/capistrano/templates/nextjs.service.capistrano.erb +23 -0
- metadata +102 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 06e138f48096ae45c17c5bb40ec3b3331128dc4c1d2b7b69bed5a52d8fdb7332
|
|
4
|
+
data.tar.gz: 3e03654dac9515fc45d142ddcd94557e4a162d508801c9cd5a78422c37b678b5
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: f65195942ae440a5a079564e11218d646318ab7eeb8eb2d1388fdca972be96b12f0fe615e004b3301b062cde31c821048036d76e4a985ad3d414be51ca57da6d
|
|
7
|
+
data.tar.gz: 011cd3efcec06a345b1e9f570b0c625550a896549c0026528381944f7e5e1cee38a576cb2bb2d47782951720a68705241c2ac6d7131f09a545ced547a1c394f3
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.0.0] - 2025-07-07
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Initial release of capistrano-nextjs
|
|
13
|
+
- Next.js application deployment support with Capistrano
|
|
14
|
+
- Systemd service integration for Next.js applications
|
|
15
|
+
- Support for pnpm package manager
|
|
16
|
+
- Automatic build and deployment hooks with proper sequencing
|
|
17
|
+
- Graceful shutdown support
|
|
18
|
+
- Environment variable management
|
|
19
|
+
- Service management tasks (start, stop, restart, status)
|
|
20
|
+
- Installation and uninstallation of systemd services
|
|
21
|
+
- Database migration support before build process
|
|
22
|
+
- RuboCop integration for code quality and style enforcement
|
|
23
|
+
|
|
24
|
+
### Features
|
|
25
|
+
|
|
26
|
+
- Automatic detection of pnpm executable
|
|
27
|
+
- NODE_ENV environment variable management
|
|
28
|
+
- Customizable service templates
|
|
29
|
+
- User and system service support
|
|
30
|
+
- Logging configuration
|
|
31
|
+
- Background process management
|
|
32
|
+
- Optimized deployment flow: migrate → build → deploy
|
|
33
|
+
- Code linting and formatting with RuboCop
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- Reordered deployment hooks to run migrations before build process
|
|
38
|
+
- Improved deployment sequence for better reliability
|
|
39
|
+
- Added RuboCop configuration and fixed all linting issues
|
|
40
|
+
- Moved development dependencies from gemspec to Gemfile
|
|
41
|
+
- Added MFA requirement for gem publishing
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
GNU LESSER GENERAL PUBLIC LICENSE
|
|
2
|
+
Version 3, 29 June 2007
|
|
3
|
+
|
|
4
|
+
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
|
5
|
+
Everyone is permitted to copy and distribute verbatim copies
|
|
6
|
+
of this license document, but changing it is not allowed.
|
|
7
|
+
|
|
8
|
+
This version of the GNU Lesser General Public License incorporates
|
|
9
|
+
the terms and conditions of version 3 of the GNU General Public
|
|
10
|
+
License, supplemented by the additional permissions listed below.
|
|
11
|
+
|
|
12
|
+
0. Additional Definitions.
|
|
13
|
+
|
|
14
|
+
As used herein, "this License" refers to version 3 of the GNU Lesser
|
|
15
|
+
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
|
16
|
+
General Public License.
|
|
17
|
+
|
|
18
|
+
"The Library" refers to a covered work governed by this License,
|
|
19
|
+
other than an Application or a Combined Work as defined below.
|
|
20
|
+
|
|
21
|
+
An "Application" is any work that makes use of an interface provided
|
|
22
|
+
by the Library, but which is not otherwise based on the Library.
|
|
23
|
+
Defining a subclass of a class defined by the Library is deemed a mode
|
|
24
|
+
of using an interface provided by the Library.
|
|
25
|
+
|
|
26
|
+
A "Combined Work" is a work produced by combining or linking an
|
|
27
|
+
Application with the Library. The particular version of the Library
|
|
28
|
+
with which the Combined Work was made is also called the "Linked
|
|
29
|
+
Version".
|
|
30
|
+
|
|
31
|
+
The "Minimal Corresponding Source" for a Combined Work means the
|
|
32
|
+
Corresponding Source for the Combined Work, excluding any source code
|
|
33
|
+
for portions of the Combined Work that, considered in isolation, are
|
|
34
|
+
based on the Application, and not on the Linked Version.
|
|
35
|
+
|
|
36
|
+
The "Corresponding Application Code" for a Combined Work means the
|
|
37
|
+
object code and/or source code for the Application, including any data
|
|
38
|
+
and utility programs needed for reproducing the Combined Work from the
|
|
39
|
+
Application, but excluding the System Libraries of the Combined Work.
|
|
40
|
+
|
|
41
|
+
1. Exception to Section 3 of the GNU GPL.
|
|
42
|
+
|
|
43
|
+
You may convey a covered work under sections 3 and 4 of this License
|
|
44
|
+
without being bound by section 3 of the GNU GPL.
|
|
45
|
+
|
|
46
|
+
2. Conveying this Library.
|
|
47
|
+
|
|
48
|
+
You may convey a copy of the Library under this License. However, if you
|
|
49
|
+
modify the Library, you may not distribute your modifications without
|
|
50
|
+
accompanying the distribution with complete Corresponding Source code.
|
|
51
|
+
|
|
52
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
53
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
54
|
+
in the Software without restriction, including without limitation the rights
|
|
55
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
56
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
57
|
+
furnished to do so, subject to the following conditions:
|
|
58
|
+
|
|
59
|
+
The above copyright notice and this permission notice shall be included in all
|
|
60
|
+
copies or substantial portions of the Software.
|
|
61
|
+
|
|
62
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
63
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
64
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
65
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
66
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
67
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
68
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Capistrano::Nextjs
|
|
2
|
+
|
|
3
|
+
Next.js integration for Capistrano with systemd support.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add this line to your application's Gemfile:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
gem 'capistrano-nextjs'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
And then execute:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
bundle install
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
Add the following to your `Capfile`:
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
require 'capistrano/pnpm'
|
|
25
|
+
require 'capistrano/nextjs'
|
|
26
|
+
install_plugin Capistrano::NextjsPlugin
|
|
27
|
+
install_plugin Capistrano::NextjsSystemd
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Add the following to your `config/deploy.rb`:
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
# Next.js configuration
|
|
34
|
+
set :nextjs_roles, :app
|
|
35
|
+
set :nextjs_env, fetch(:stage)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Available Tasks
|
|
39
|
+
|
|
40
|
+
- `cap nextjs:start` - Start Next.js application
|
|
41
|
+
- `cap nextjs:stop` - Stop Next.js application
|
|
42
|
+
- `cap nextjs:restart` - Restart Next.js application
|
|
43
|
+
- `cap nextjs:status` - Check Next.js application status
|
|
44
|
+
- `cap nextjs:install` - Install systemd service
|
|
45
|
+
- `cap nextjs:uninstall` - Uninstall systemd service
|
|
46
|
+
- `cap nextjs:build` - Build Next.js application
|
|
47
|
+
- `cap nextjs:check` - Check Next.js application (lint)
|
|
48
|
+
|
|
49
|
+
## Configuration
|
|
50
|
+
|
|
51
|
+
The gem automatically integrates with your deployment process:
|
|
52
|
+
|
|
53
|
+
- Stops the service before deployment
|
|
54
|
+
- Starts the service after successful deployment
|
|
55
|
+
- Restarts the service if deployment fails
|
|
56
|
+
|
|
57
|
+
## Requirements
|
|
58
|
+
|
|
59
|
+
- Node.js
|
|
60
|
+
- pnpm
|
|
61
|
+
- systemd (for service management)
|
|
62
|
+
|
|
63
|
+
## License
|
|
64
|
+
|
|
65
|
+
The gem is available as open source under the terms of the [LGPL-3.0 License](https://opensource.org/licenses/LGPL-3.0).
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Capistrano
|
|
4
|
+
module Nextjs
|
|
5
|
+
class Systemd < Capistrano::Plugin
|
|
6
|
+
include NextjsCommon
|
|
7
|
+
def define_tasks
|
|
8
|
+
eval_rakefile File.expand_path('../tasks/systemd.rake', __dir__)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def set_defaults
|
|
12
|
+
set_if_empty :systemctl_bin, '/bin/systemctl'
|
|
13
|
+
set_if_empty :service_unit_user, :user
|
|
14
|
+
set_if_empty :systemctl_user, fetch(:service_unit_user, :user) == :user
|
|
15
|
+
|
|
16
|
+
set_if_empty :nextjs_service_unit_name, -> { "#{fetch(:application)}_nextjs_#{fetch(:stage)}" }
|
|
17
|
+
set_if_empty :nextjs_lingering_user, -> { fetch(:lingering_user, fetch(:user)) }
|
|
18
|
+
|
|
19
|
+
## Next.js environment variables
|
|
20
|
+
set_if_empty :nextjs_service_unit_env_files, -> { fetch(:service_unit_env_files, []) }
|
|
21
|
+
set_if_empty :nextjs_service_unit_env_vars, lambda {
|
|
22
|
+
base_vars = fetch(:service_unit_env_vars, [])
|
|
23
|
+
base_vars + ["NODE_ENV=#{fetch(:nextjs_env, 'production')}"]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
set_if_empty :nextjs_service_templates_path, fetch(:service_templates_path, 'config/deploy/templates')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def systemd_command(*args)
|
|
30
|
+
command = [fetch(:systemctl_bin)]
|
|
31
|
+
|
|
32
|
+
command << '--user' unless fetch(:service_unit_user) == :system
|
|
33
|
+
|
|
34
|
+
command + args
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def sudo_if_needed(*command)
|
|
38
|
+
if fetch(:service_unit_user) == :system
|
|
39
|
+
backend.sudo command.map(&:to_s).join(' ')
|
|
40
|
+
else
|
|
41
|
+
backend.execute(*command)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def execute_systemd(*)
|
|
46
|
+
sudo_if_needed(*systemd_command(*))
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
require 'capistrano/pnpm'
|
|
5
|
+
rescue LoadError, RuntimeError
|
|
6
|
+
# Ignore missing pnpm dependency in test environment
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
require 'capistrano/plugin'
|
|
10
|
+
|
|
11
|
+
module Capistrano
|
|
12
|
+
module NextjsCommon
|
|
13
|
+
def compiled_template(config_file = 'nextjs.yml')
|
|
14
|
+
@config_file = config_file
|
|
15
|
+
local_template_directory = fetch(:nextjs_service_templates_path)
|
|
16
|
+
search_paths = [
|
|
17
|
+
File.join(local_template_directory, 'nextjs.service.capistrano.erb'),
|
|
18
|
+
File.expand_path(
|
|
19
|
+
File.join(*%w[.. templates nextjs.service.capistrano.erb]),
|
|
20
|
+
__FILE__
|
|
21
|
+
)
|
|
22
|
+
]
|
|
23
|
+
template_path = search_paths.detect { |path| File.file?(path) }
|
|
24
|
+
template = File.read(template_path)
|
|
25
|
+
ERB.new(template, trim_mode: '-').result(binding)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def pnpm_path
|
|
29
|
+
# Try to find pnpm in various locations
|
|
30
|
+
|
|
31
|
+
backend.capture(:which, :pnpm).strip
|
|
32
|
+
rescue StandardError
|
|
33
|
+
# Try nvm path
|
|
34
|
+
begin
|
|
35
|
+
backend.capture(:bash, '-c', 'source ~/.nvm/nvm.sh && which pnpm').strip
|
|
36
|
+
rescue StandardError
|
|
37
|
+
# Fallback to common paths
|
|
38
|
+
[
|
|
39
|
+
'/usr/local/bin/pnpm',
|
|
40
|
+
'/usr/bin/pnpm',
|
|
41
|
+
'pnpm'
|
|
42
|
+
].each do |path|
|
|
43
|
+
return path if backend.test(:test, '-f', path) || path == 'pnpm'
|
|
44
|
+
rescue StandardError
|
|
45
|
+
next
|
|
46
|
+
end
|
|
47
|
+
'pnpm' # Ultimate fallback
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def nextjs_config
|
|
52
|
+
'' # Next.js doesn't use config files like Sidekiq
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def switch_user(role, &)
|
|
56
|
+
su_user = nextjs_user(role)
|
|
57
|
+
if su_user == role.user
|
|
58
|
+
yield
|
|
59
|
+
else
|
|
60
|
+
as(su_user, &)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def nextjs_user(role = nil)
|
|
65
|
+
if role.nil?
|
|
66
|
+
fetch(:nextjs_user)
|
|
67
|
+
else
|
|
68
|
+
properties = role.properties
|
|
69
|
+
properties.fetch(:nextjs_user) || # local property for nextjs only
|
|
70
|
+
fetch(:nextjs_user) ||
|
|
71
|
+
properties.fetch(:run_as) || # global property across multiple capistrano gems
|
|
72
|
+
role.user
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
module Nextjs
|
|
78
|
+
class Plugin < Capistrano::Plugin
|
|
79
|
+
def define_tasks
|
|
80
|
+
eval_rakefile File.expand_path('tasks/nextjs.rake', __dir__)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def set_defaults
|
|
84
|
+
set_if_empty :nextjs_default_hooks, true
|
|
85
|
+
|
|
86
|
+
set_if_empty :nextjs_env, -> { fetch(:node_env, fetch(:stage)) }
|
|
87
|
+
set_if_empty :nextjs_roles, fetch(:nextjs_role, :app)
|
|
88
|
+
set_if_empty :nextjs_configs, %w[nextjs] # basic nextjs config
|
|
89
|
+
|
|
90
|
+
set_if_empty :nextjs_log, -> { File.join(shared_path, 'log', 'nextjs.log') }
|
|
91
|
+
set_if_empty :nextjs_error_log, -> { File.join(shared_path, 'log', 'nextjs.log') }
|
|
92
|
+
|
|
93
|
+
set_if_empty :nextjs_config_files, ['nextjs.yml']
|
|
94
|
+
|
|
95
|
+
# pnpm integration
|
|
96
|
+
append :pnpm_map_bins, 'next'
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
require_relative 'nextjs/systemd'
|
|
103
|
+
|
|
104
|
+
# Create top-level aliases for easier plugin installation
|
|
105
|
+
module Capistrano
|
|
106
|
+
NextjsPlugin = Nextjs::Plugin
|
|
107
|
+
NextjsSystemd = Nextjs::Systemd
|
|
108
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
namespace :deploy do
|
|
4
|
+
before :starting, :check_nextjs_hooks do
|
|
5
|
+
invoke 'nextjs:add_default_hooks' if fetch(:nextjs_default_hooks)
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
namespace :nextjs do
|
|
10
|
+
task :add_default_hooks do
|
|
11
|
+
after 'pnpm:install', 'nextjs:build'
|
|
12
|
+
# after 'deploy:starting', 'nextjs:quiet' if Rake::Task.task_defined?('nextjs:quiet')
|
|
13
|
+
after 'deploy:updated', 'nextjs:stop'
|
|
14
|
+
after 'deploy:published', 'nextjs:start'
|
|
15
|
+
after 'deploy:failed', 'nextjs:restart'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
desc 'Build Next.js application'
|
|
19
|
+
task :build do
|
|
20
|
+
on roles fetch(:nextjs_roles) do
|
|
21
|
+
within release_path do
|
|
22
|
+
execute :pnpm, 'build'
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
desc 'Check Next.js application'
|
|
28
|
+
task :check do
|
|
29
|
+
on roles fetch(:nextjs_roles) do
|
|
30
|
+
within current_path do
|
|
31
|
+
execute :pnpm, 'lint'
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
git_plugin = self
|
|
4
|
+
|
|
5
|
+
namespace :nextjs do
|
|
6
|
+
standard_actions = {
|
|
7
|
+
start: 'Start Next.js',
|
|
8
|
+
stop: 'Stop Next.js (graceful shutdown)',
|
|
9
|
+
status: 'Get Next.js Status',
|
|
10
|
+
restart: 'Restart Next.js'
|
|
11
|
+
|
|
12
|
+
}
|
|
13
|
+
standard_actions.each do |command, description|
|
|
14
|
+
desc description
|
|
15
|
+
task command do
|
|
16
|
+
on roles fetch(:nextjs_roles) do |role|
|
|
17
|
+
git_plugin.switch_user(role) do
|
|
18
|
+
git_plugin.config_files(role).each do |config_file|
|
|
19
|
+
git_plugin.execute_systemd(command, git_plugin.nextjs_service_file_name(config_file))
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
desc 'Stop Next.js gracefully'
|
|
27
|
+
task :quiet do
|
|
28
|
+
on roles fetch(:nextjs_roles) do |role|
|
|
29
|
+
git_plugin.switch_user(role) do
|
|
30
|
+
git_plugin.stop_nextjs_gracefully(role)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
desc 'Install Next.js systemd service'
|
|
36
|
+
task :install do
|
|
37
|
+
on roles fetch(:nextjs_roles) do |role|
|
|
38
|
+
git_plugin.switch_user(role) do
|
|
39
|
+
git_plugin.create_systemd_template(role)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
invoke 'nextjs:enable'
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
desc 'Uninstall Next.js systemd service'
|
|
46
|
+
task :uninstall do
|
|
47
|
+
invoke 'nextjs:disable'
|
|
48
|
+
on roles fetch(:nextjs_roles) do |role|
|
|
49
|
+
git_plugin.switch_user(role) do
|
|
50
|
+
git_plugin.rm_systemd_service(role)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
desc 'Enable Next.js systemd service'
|
|
56
|
+
task :enable do
|
|
57
|
+
on roles(fetch(:nextjs_roles)) do |role|
|
|
58
|
+
git_plugin.config_files(role).each do |config_file|
|
|
59
|
+
git_plugin.execute_systemd('enable', git_plugin.nextjs_service_file_name(config_file))
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
if fetch(:systemctl_user) && fetch(:nextjs_lingering_user)
|
|
63
|
+
execute :loginctl, 'enable-linger', fetch(:nextjs_lingering_user)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
desc 'Disable Next.js systemd service'
|
|
69
|
+
task :disable do
|
|
70
|
+
on roles(fetch(:nextjs_roles)) do |role|
|
|
71
|
+
git_plugin.config_files(role).each do |config_file|
|
|
72
|
+
git_plugin.execute_systemd('disable', git_plugin.nextjs_service_file_name(config_file))
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def fetch_systemd_unit_path
|
|
78
|
+
if fetch(:nextjs_systemctl_user) == :system
|
|
79
|
+
'/etc/systemd/system/'
|
|
80
|
+
else
|
|
81
|
+
home_dir = backend.capture :pwd
|
|
82
|
+
File.join(home_dir, '.config', 'systemd', 'user')
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def create_systemd_template(role)
|
|
87
|
+
systemd_path = fetch(:service_unit_path, fetch_systemd_unit_path)
|
|
88
|
+
backend.execute :mkdir, '-p', systemd_path if fetch(:systemctl_user)
|
|
89
|
+
|
|
90
|
+
config_files(role).each do |config_file|
|
|
91
|
+
ctemplate = compiled_template(config_file)
|
|
92
|
+
temp_file_name = File.join('/tmp', "nextjs.#{config_file}.service")
|
|
93
|
+
systemd_file_name = File.join(systemd_path, nextjs_service_file_name(config_file))
|
|
94
|
+
backend.upload!(StringIO.new(ctemplate), temp_file_name)
|
|
95
|
+
if fetch(:systemctl_user)
|
|
96
|
+
warn "Moving #{temp_file_name} to #{systemd_file_name}"
|
|
97
|
+
backend.execute :mv, temp_file_name, systemd_file_name
|
|
98
|
+
else
|
|
99
|
+
warn "Installing #{systemd_file_name} as root"
|
|
100
|
+
backend.execute :sudo, :mv, temp_file_name, systemd_file_name
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def rm_systemd_service(role)
|
|
106
|
+
systemd_path = fetch(:service_unit_path, fetch_systemd_unit_path)
|
|
107
|
+
|
|
108
|
+
config_files(role).each do |config_file|
|
|
109
|
+
systemd_file_name = File.join(systemd_path, nextjs_service_file_name(config_file))
|
|
110
|
+
if fetch(:systemctl_user)
|
|
111
|
+
warn "Deleting #{systemd_file_name}"
|
|
112
|
+
backend.execute :rm, '-f', systemd_file_name
|
|
113
|
+
else
|
|
114
|
+
warn "Deleting #{systemd_file_name} as root"
|
|
115
|
+
backend.execute :sudo, :rm, '-f', systemd_file_name
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def stop_nextjs_gracefully(role)
|
|
121
|
+
config_files(role).each do |config_file|
|
|
122
|
+
nextjs_service = nextjs_service_unit_name(config_file)
|
|
123
|
+
warn "Stopping #{nextjs_service} gracefully"
|
|
124
|
+
execute_systemd('stop', nextjs_service)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def nextjs_service_unit_name(config_file)
|
|
129
|
+
if config_file == 'nextjs.yml'
|
|
130
|
+
fetch(:nextjs_service_unit_name)
|
|
131
|
+
else
|
|
132
|
+
"#{fetch(:nextjs_service_unit_name)}.#{config_file.split('.')[0..-2].join('.')}"
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def nextjs_service_file_name(config_file)
|
|
137
|
+
## Remove the extension
|
|
138
|
+
config_file = config_file.split('.').join('.')
|
|
139
|
+
|
|
140
|
+
"#{nextjs_service_unit_name(config_file)}.service"
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def config_files(role)
|
|
144
|
+
role.properties.fetch(:nextjs_config_files) ||
|
|
145
|
+
fetch(:nextjs_config_files)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
|
|
2
|
+
[Unit]
|
|
3
|
+
Description=Next.js for <%= "#{fetch(:application)} (#{fetch(:stage)})" %>
|
|
4
|
+
# start us only once the network and logging subsystems are available
|
|
5
|
+
After=syslog.target network.target
|
|
6
|
+
|
|
7
|
+
[Service]
|
|
8
|
+
Type=simple
|
|
9
|
+
<%="User=#{nextjs_user}" if fetch(:nextjs_systemctl_user) == :system %>
|
|
10
|
+
WorkingDirectory=<%= current_path %>
|
|
11
|
+
ExecStart=/bin/bash -l -c 'pnpm start'
|
|
12
|
+
ExecReload=/bin/kill -TSTP $MAINPID
|
|
13
|
+
ExecStop=/bin/kill -SIGTERM $MAINPID
|
|
14
|
+
|
|
15
|
+
Restart=always
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
StandardOutput=file:<%= shared_path %>/log/nextjs.log
|
|
19
|
+
StandardError=file:<%= shared_path %>/log/nextjs-error.log
|
|
20
|
+
|
|
21
|
+
SyslogIdentifier=<%= nextjs_service_unit_name(@config_file) %>
|
|
22
|
+
[Install]
|
|
23
|
+
WantedBy=default.target
|
metadata
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: capistrano-nextjs
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Florian Crusius
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-03-03 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: capistrano
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '3.9'
|
|
20
|
+
- - ">="
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: 3.9.0
|
|
23
|
+
type: :runtime
|
|
24
|
+
prerelease: false
|
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
26
|
+
requirements:
|
|
27
|
+
- - "~>"
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '3.9'
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 3.9.0
|
|
33
|
+
- !ruby/object:Gem::Dependency
|
|
34
|
+
name: capistrano-pnpm
|
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '1.0'
|
|
40
|
+
type: :runtime
|
|
41
|
+
prerelease: false
|
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '1.0'
|
|
47
|
+
- !ruby/object:Gem::Dependency
|
|
48
|
+
name: dotenv
|
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '2.7'
|
|
54
|
+
type: :runtime
|
|
55
|
+
prerelease: false
|
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '2.7'
|
|
61
|
+
description: Next.js integration for Capistrano with systemd support, enabling automated
|
|
62
|
+
deployment and service management for Next.js applications using pnpm
|
|
63
|
+
email:
|
|
64
|
+
- florian@zauberware.com
|
|
65
|
+
executables: []
|
|
66
|
+
extensions: []
|
|
67
|
+
extra_rdoc_files: []
|
|
68
|
+
files:
|
|
69
|
+
- CHANGELOG.md
|
|
70
|
+
- LICENSE.txt
|
|
71
|
+
- README.md
|
|
72
|
+
- lib/capistrano/nextjs.rb
|
|
73
|
+
- lib/capistrano/nextjs/systemd.rb
|
|
74
|
+
- lib/capistrano/nextjs/version.rb
|
|
75
|
+
- lib/capistrano/tasks/nextjs.rake
|
|
76
|
+
- lib/capistrano/tasks/systemd.rake
|
|
77
|
+
- lib/capistrano/templates/nextjs.service.capistrano.erb
|
|
78
|
+
homepage: https://github.com/zauberware/capistrano-nextjs
|
|
79
|
+
licenses:
|
|
80
|
+
- LGPL-3.0
|
|
81
|
+
metadata:
|
|
82
|
+
rubygems_mfa_required: 'true'
|
|
83
|
+
post_install_message:
|
|
84
|
+
rdoc_options: []
|
|
85
|
+
require_paths:
|
|
86
|
+
- lib
|
|
87
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
88
|
+
requirements:
|
|
89
|
+
- - ">="
|
|
90
|
+
- !ruby/object:Gem::Version
|
|
91
|
+
version: 3.2.7
|
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0'
|
|
97
|
+
requirements: []
|
|
98
|
+
rubygems_version: 3.4.19
|
|
99
|
+
signing_key:
|
|
100
|
+
specification_version: 4
|
|
101
|
+
summary: Next.js integration for Capistrano
|
|
102
|
+
test_files: []
|