capistrano-node-dotenv 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/.github/workflows/release.yml +25 -0
- data/.gitignore +10 -0
- data/.rubocop.yml +88 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +25 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +235 -0
- data/Rakefile +25 -0
- data/capistrano-node-dotenv.gemspec +28 -0
- data/lib/capistrano/node/dotenv/helpers.rb +177 -0
- data/lib/capistrano/node/dotenv/parser.rb +102 -0
- data/lib/capistrano/node/dotenv/paths.rb +19 -0
- data/lib/capistrano/node/dotenv/tasks/backup.rake +27 -0
- data/lib/capistrano/node/dotenv/tasks/check.rake +12 -0
- data/lib/capistrano/node/dotenv/tasks/check_config_present.rake +12 -0
- data/lib/capistrano/node/dotenv/tasks/check_dotenv_file_exists.rake +12 -0
- data/lib/capistrano/node/dotenv/tasks/check_git_tracking.rake +12 -0
- data/lib/capistrano/node/dotenv/tasks/create_local.rake +37 -0
- data/lib/capistrano/node/dotenv/tasks/dotenv_symlink.rake +10 -0
- data/lib/capistrano/node/dotenv/tasks/get.rake +55 -0
- data/lib/capistrano/node/dotenv/tasks/get_stage.rake +18 -0
- data/lib/capistrano/node/dotenv/tasks/load.rake +10 -0
- data/lib/capistrano/node/dotenv/tasks/rollback.rake +23 -0
- data/lib/capistrano/node/dotenv/tasks/setup.rake +19 -0
- data/lib/capistrano/node/dotenv/tasks/update.rake +41 -0
- data/lib/capistrano/node/dotenv/version.rb +9 -0
- data/lib/capistrano/node/dotenv.rb +21 -0
- data/scripts/bump_version.rb +172 -0
- metadata +159 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: fd4e85e20f0d755f17df29ea83a5e1de85c2b79431e55fb88aaa8a0b40b4d96a
|
|
4
|
+
data.tar.gz: 406b1bd86cd41345fd1c3521712465cc7441eb6c6b963dffd920fc55a024a0d3
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 52723bdc7a45285bb5ec6599fd0f63cbdcdab0140a27580699a36ee8e0f4445161bcf86ba218f770383f2936b978799fdb4019f328b334552fc7b15bee920ee6
|
|
7
|
+
data.tar.gz: cf4705e7b70b3bf1d339b3db871302b386ea43be90ac11c09ac096116cbd6ccf45cf8da6a91a730339c739e00e167cdd1e1cbe34811a69ba6dd880c3d446013e
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: write
|
|
9
|
+
id-token: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
publish:
|
|
13
|
+
name: Push gem to RubyGems.org
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Set up Ruby
|
|
20
|
+
uses: ruby/setup-ruby@v1
|
|
21
|
+
with:
|
|
22
|
+
ruby-version: '3.3'
|
|
23
|
+
bundler-cache: true
|
|
24
|
+
|
|
25
|
+
- uses: rubygems/release-gem@v1
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# RuboCop configuration for ms-graph-mailer gem
|
|
2
|
+
|
|
3
|
+
AllCops:
|
|
4
|
+
NewCops: enable
|
|
5
|
+
TargetRubyVersion: 3.1
|
|
6
|
+
SuggestExtensions: false
|
|
7
|
+
Exclude:
|
|
8
|
+
- 'vendor/**/*'
|
|
9
|
+
- 'tmp/**/*'
|
|
10
|
+
- 'bin/**/*'
|
|
11
|
+
|
|
12
|
+
# Metrics
|
|
13
|
+
|
|
14
|
+
Metrics/BlockLength:
|
|
15
|
+
Max: 50
|
|
16
|
+
Exclude:
|
|
17
|
+
- 'spec/**/*'
|
|
18
|
+
- '*.gemspec'
|
|
19
|
+
|
|
20
|
+
Metrics/ClassLength:
|
|
21
|
+
Max: 150
|
|
22
|
+
|
|
23
|
+
Metrics/MethodLength:
|
|
24
|
+
Max: 40
|
|
25
|
+
Exclude:
|
|
26
|
+
- 'spec/**/*'
|
|
27
|
+
|
|
28
|
+
Metrics/AbcSize:
|
|
29
|
+
Max: 65
|
|
30
|
+
Exclude:
|
|
31
|
+
- 'spec/**/*'
|
|
32
|
+
|
|
33
|
+
Metrics/CyclomaticComplexity:
|
|
34
|
+
Max: 10
|
|
35
|
+
|
|
36
|
+
Metrics/PerceivedComplexity:
|
|
37
|
+
Max: 10
|
|
38
|
+
|
|
39
|
+
# Style
|
|
40
|
+
|
|
41
|
+
Style/Documentation:
|
|
42
|
+
Enabled: false
|
|
43
|
+
|
|
44
|
+
Style/StringLiterals:
|
|
45
|
+
EnforcedStyle: single_quotes
|
|
46
|
+
|
|
47
|
+
Style/StringLiteralsInInterpolation:
|
|
48
|
+
EnforcedStyle: single_quotes
|
|
49
|
+
|
|
50
|
+
Style/FrozenStringLiteralComment:
|
|
51
|
+
Enabled: true
|
|
52
|
+
EnforcedStyle: always
|
|
53
|
+
|
|
54
|
+
Style/TrailingCommaInArrayLiteral:
|
|
55
|
+
EnforcedStyleForMultiline: no_comma
|
|
56
|
+
|
|
57
|
+
Style/TrailingCommaInHashLiteral:
|
|
58
|
+
EnforcedStyleForMultiline: no_comma
|
|
59
|
+
|
|
60
|
+
Style/TrailingCommaInArguments:
|
|
61
|
+
EnforcedStyleForMultiline: no_comma
|
|
62
|
+
|
|
63
|
+
# Layout
|
|
64
|
+
|
|
65
|
+
Layout/LineLength:
|
|
66
|
+
Max: 150
|
|
67
|
+
AllowedPatterns:
|
|
68
|
+
- '\s+# '
|
|
69
|
+
|
|
70
|
+
Layout/EmptyLinesAroundAttributeAccessor:
|
|
71
|
+
Enabled: true
|
|
72
|
+
|
|
73
|
+
Layout/SpaceAroundMethodCallOperator:
|
|
74
|
+
Enabled: true
|
|
75
|
+
|
|
76
|
+
# Lint
|
|
77
|
+
|
|
78
|
+
Gemspec/DevelopmentDependencies:
|
|
79
|
+
Enabled: false
|
|
80
|
+
|
|
81
|
+
Gemspec/RequireMFA:
|
|
82
|
+
Enabled: false
|
|
83
|
+
|
|
84
|
+
Lint/RaiseException:
|
|
85
|
+
Enabled: true
|
|
86
|
+
|
|
87
|
+
Lint/StructNewOverride:
|
|
88
|
+
Enabled: true
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.3.5
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
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] - 2026-03-02
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Initial release
|
|
13
|
+
- `dotenv:setup` – upload local `config/application.yml` to the remote shared folder as a `.env` file
|
|
14
|
+
- `dotenv:update` – interactively compare local config with remote `.env` and push selected changes
|
|
15
|
+
- `dotenv:get` – interactively compare remote `.env` with local config and pull selected changes
|
|
16
|
+
- `dotenv:init` – bootstrap local `config/application.yml` by pulling `.env` files from all stages
|
|
17
|
+
- `dotenv:show` – print the content of the remote `.env` file for the current stage
|
|
18
|
+
- `dotenv:backup` – create a timestamped backup of the remote `.env` file (retains latest 5)
|
|
19
|
+
- `dotenv:rollback` – restore the remote `.env` from the most recent backup
|
|
20
|
+
- `dotenv:check` – pre-deploy checks (file exists, not git-tracked, stage config present)
|
|
21
|
+
- Automatic symlinking of the remote `.env` via `deploy:started` hook
|
|
22
|
+
- Section-marker format in generated `.env` files for deterministic parsing (`# Begin/End <stage> envs`)
|
|
23
|
+
- `Parser` module for reading and writing section-annotated `.env` files
|
|
24
|
+
- `Helpers` module with backup management, hash diffing, and interactive prompts
|
|
25
|
+
- `Paths` module for resolving local and remote file paths via Capistrano `fetch`
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 zauberware
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# capistrano-node-dotenv
|
|
2
|
+
|
|
3
|
+
A [Capistrano](https://capistranorb.com/) plugin for managing `.env` files on remote servers running Node.js applications. It handles uploading, syncing, backing up, and rolling back the `.env` file that lives in your deployment's shared folder.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
<details>
|
|
8
|
+
<summary>Click to expand</summary>
|
|
9
|
+
|
|
10
|
+
- [How it works](#how-it-works)
|
|
11
|
+
- [Installation](#installation)
|
|
12
|
+
- [Configuration](#configuration)
|
|
13
|
+
- [Tasks](#tasks)
|
|
14
|
+
- [dotenv:setup](#dotenvsetup)
|
|
15
|
+
- [dotenv:update](#dotenvupdate)
|
|
16
|
+
- [dotenv:get](#dotenvget)
|
|
17
|
+
- [dotenv:init](#dotenvinit)
|
|
18
|
+
- [dotenv:show](#dotenvshow)
|
|
19
|
+
- [dotenv:backup](#dotenvbackup)
|
|
20
|
+
- [dotenv:rollback](#dotenvrollback)
|
|
21
|
+
- [dotenv:check](#dotenvcheck)
|
|
22
|
+
- [Automatic symlinking](#automatic-symlinking)
|
|
23
|
+
- [Backup behaviour](#backup-behaviour)
|
|
24
|
+
- [Requirements](#requirements)
|
|
25
|
+
- [Contributing](#contributing)
|
|
26
|
+
- [License](#license)
|
|
27
|
+
|
|
28
|
+
</details>
|
|
29
|
+
|
|
30
|
+
## How it works
|
|
31
|
+
|
|
32
|
+
The gem stores your environment configuration locally in `config/application.yml` — a YAML file where top-level keys are global variables and nested keys are stage-specific variables. On deploy, the relevant keys are extracted, assembled into a proper `.env` file, and uploaded to the server's shared folder. The `.env` file is automatically added to Capistrano's `linked_files` so it gets symlinked into each release.
|
|
33
|
+
|
|
34
|
+
### Local config format (`config/application.yml`)
|
|
35
|
+
|
|
36
|
+
```yaml
|
|
37
|
+
# Global variables shared across all stages
|
|
38
|
+
DATABASE_URL: postgres://localhost/myapp
|
|
39
|
+
|
|
40
|
+
# Stage-specific variables
|
|
41
|
+
production:
|
|
42
|
+
NODE_ENV: production
|
|
43
|
+
API_KEY: secret-prod
|
|
44
|
+
|
|
45
|
+
staging:
|
|
46
|
+
NODE_ENV: staging
|
|
47
|
+
API_KEY: secret-staging
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Remote `.env` file format
|
|
51
|
+
|
|
52
|
+
The generated `.env` file uses section markers so the gem can parse it back:
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
# Begin global envs 2026-03-02 10:00:00
|
|
56
|
+
DATABASE_URL=postgres://localhost/myapp
|
|
57
|
+
# End global envs 2026-03-02 10:00:00
|
|
58
|
+
# Begin production envs 2026-03-02 10:00:00
|
|
59
|
+
NODE_ENV=production
|
|
60
|
+
API_KEY=secret-prod
|
|
61
|
+
# End production envs 2026-03-02 10:00:00
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
[↑](#)
|
|
65
|
+
|
|
66
|
+
## Installation
|
|
67
|
+
|
|
68
|
+
Add to your `Gemfile`:
|
|
69
|
+
|
|
70
|
+
```ruby
|
|
71
|
+
gem 'capistrano-node-dotenv'
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Then run:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
bundle install
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Require the gem in your `Capfile`:
|
|
81
|
+
|
|
82
|
+
```ruby
|
|
83
|
+
require 'capistrano/node/dotenv'
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Make sure `config/application.yml` is **not** tracked by git:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
git rm --cached config/application.yml
|
|
90
|
+
echo 'config/application.yml' >> .gitignore
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
[↑](#)
|
|
94
|
+
|
|
95
|
+
## Configuration
|
|
96
|
+
|
|
97
|
+
Set these variables in `config/deploy.rb` or a stage file. All have sensible defaults.
|
|
98
|
+
|
|
99
|
+
| Variable | Default | Description |
|
|
100
|
+
| ---------------------- | ------------------------------------- | -------------------------------------------------------- |
|
|
101
|
+
| `dotenv_local_path` | `config/application.yml` | Path to the local YAML config file |
|
|
102
|
+
| `dotenv_remote_path` | `.env` | Path to the `.env` file relative to the shared folder |
|
|
103
|
+
| `dotenv_env` | `fetch(:node_env) \|\| fetch(:stage)` | The environment/stage key to extract from the local YAML |
|
|
104
|
+
| `dotenv_remote_backup` | `false` | Whether to automatically back up on deploy |
|
|
105
|
+
|
|
106
|
+
Example:
|
|
107
|
+
|
|
108
|
+
```ruby
|
|
109
|
+
# config/deploy.rb
|
|
110
|
+
set :dotenv_local_path, 'config/application.yml'
|
|
111
|
+
set :dotenv_remote_path, '.env'
|
|
112
|
+
set :dotenv_env, -> { fetch(:stage) }
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
[↑](#)
|
|
116
|
+
|
|
117
|
+
## Tasks
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
cap dotenv:setup # Upload local config/application.yml to the server (first-time or full overwrite)
|
|
121
|
+
cap dotenv:update # Compare local vs remote .env interactively and push selected changes
|
|
122
|
+
cap dotenv:get # Compare remote vs local .env and optionally pull remote values into local
|
|
123
|
+
cap dotenv:init # Build local config/application.yml by pulling .env files from all stages
|
|
124
|
+
cap dotenv:show # Print the content of the remote .env file for the current stage
|
|
125
|
+
cap dotenv:backup # Create a timestamped backup of the remote .env file (keeps latest 5)
|
|
126
|
+
cap dotenv:rollback # Roll back the remote .env file to the most recent backup
|
|
127
|
+
cap dotenv:check # Run all pre-deploy checks (file exists, not git-tracked, stage config present)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### dotenv:setup
|
|
131
|
+
|
|
132
|
+
Reads `config/application.yml`, extracts global keys and stage-specific keys, generates a `.env` file with section markers, and uploads it to `shared/<dotenv_remote_path>`. Use this the first time you deploy or when you want to fully overwrite the remote file.
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
cap production dotenv:setup
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### dotenv:update
|
|
139
|
+
|
|
140
|
+
Compares your local `config/application.yml` with the current remote `.env`. Displays any differences and interactively asks whether to overwrite the remote globals and/or stage section. Automatically creates a backup before writing.
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
cap production dotenv:update
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### dotenv:get
|
|
147
|
+
|
|
148
|
+
The reverse of `dotenv:update`. Compares the remote `.env` with your local file and interactively asks whether to pull remote values back into `config/application.yml`.
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
cap production dotenv:get
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### dotenv:init
|
|
155
|
+
|
|
156
|
+
Iterates over all configured Capistrano stages, downloads each stage's `.env` file, and merges everything into a local `config/application.yml`. Useful for bootstrapping a fresh local environment from existing servers.
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
cap production dotenv:init
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### dotenv:show
|
|
163
|
+
|
|
164
|
+
Prints the raw content of the remote `.env` file to stdout.
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
cap production dotenv:show
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### dotenv:backup
|
|
171
|
+
|
|
172
|
+
Creates a timestamped backup (e.g. `.env-2026-03-02-10-00-00.bak`) in the shared folder. Automatically removes older backups, keeping only the 5 most recent.
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
cap production dotenv:backup
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### dotenv:rollback
|
|
179
|
+
|
|
180
|
+
Restores the remote `.env` from the most recent `.bak` file. Does nothing if no backup exists.
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
cap production dotenv:rollback
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### dotenv:check
|
|
187
|
+
|
|
188
|
+
Runs three pre-deploy checks in sequence:
|
|
189
|
+
|
|
190
|
+
1. **`check_dotenv_file_exists`** – Verifies that `config/application.yml` exists locally.
|
|
191
|
+
2. **`check_git_tracking`** – Ensures the file is not accidentally committed to git.
|
|
192
|
+
3. **`check_config_present`** – Confirms that the current stage's config is not empty.
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
cap production dotenv:check
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
[↑](#)
|
|
199
|
+
|
|
200
|
+
## Automatic symlinking
|
|
201
|
+
|
|
202
|
+
The gem hooks into `deploy:started` and automatically adds the remote `.env` path to `linked_files`. No manual configuration in `deploy.rb` is needed — the file will be symlinked into every release directory.
|
|
203
|
+
|
|
204
|
+
[↑](#)
|
|
205
|
+
|
|
206
|
+
## Backup behaviour
|
|
207
|
+
|
|
208
|
+
- Backups are named `<filename>-YYYY-MM-DD-HH-MM-SS.bak` and stored next to the `.env` in the shared folder.
|
|
209
|
+
- At most 5 backups are retained; older ones are deleted automatically.
|
|
210
|
+
- `dotenv:update` always creates a backup before writing to the remote file.
|
|
211
|
+
|
|
212
|
+
[↑](#)
|
|
213
|
+
|
|
214
|
+
## Requirements
|
|
215
|
+
|
|
216
|
+
- Ruby `>= 3.1.4, < 3.4.0`
|
|
217
|
+
- Capistrano 3.x
|
|
218
|
+
|
|
219
|
+
[↑](#)
|
|
220
|
+
|
|
221
|
+
## Contributing
|
|
222
|
+
|
|
223
|
+
1. Fork it ( https://github.com/zauberware/capistrano-node-dotenv/fork )
|
|
224
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
225
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
226
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
227
|
+
5. Create a new Pull Request
|
|
228
|
+
|
|
229
|
+
[↑](#)
|
|
230
|
+
|
|
231
|
+
## License
|
|
232
|
+
|
|
233
|
+
MIT — see [LICENSE.txt](LICENSE.txt).
|
|
234
|
+
|
|
235
|
+
[↑](#)
|
data/Rakefile
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'bundler/gem_tasks'
|
|
4
|
+
require 'rspec/core/rake_task'
|
|
5
|
+
|
|
6
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
7
|
+
|
|
8
|
+
task default: :spec
|
|
9
|
+
|
|
10
|
+
namespace :version do
|
|
11
|
+
desc 'Bump patch version (0.1.0 -> 0.1.1)'
|
|
12
|
+
task :patch do
|
|
13
|
+
ruby 'scripts/bump_version.rb patch'
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
desc 'Bump minor version (0.1.0 -> 0.2.0)'
|
|
17
|
+
task :minor do
|
|
18
|
+
ruby 'scripts/bump_version.rb minor'
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
desc 'Bump major version (0.1.0 -> 1.0.0)'
|
|
22
|
+
task :major do
|
|
23
|
+
ruby 'scripts/bump_version.rb major'
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
$LOAD_PATH.push File.expand_path('lib', __dir__)
|
|
4
|
+
|
|
5
|
+
require 'capistrano/node/dotenv/version'
|
|
6
|
+
|
|
7
|
+
Gem::Specification.new do |s|
|
|
8
|
+
s.name = 'capistrano-node-dotenv'
|
|
9
|
+
s.version = Capistrano::Node::Dotenv::VERSION
|
|
10
|
+
s.platform = Gem::Platform::RUBY
|
|
11
|
+
s.authors = ['Florian Crusius']
|
|
12
|
+
s.email = ['florian@zauberware.com']
|
|
13
|
+
s.license = 'MIT'
|
|
14
|
+
s.homepage = 'https://github.com/zauberware/capistrano-node-dotenv'
|
|
15
|
+
s.summary = 'dotenv tasks for node applications'
|
|
16
|
+
s.description = 'A collection of dotenv tasks for node applications'
|
|
17
|
+
s.files = `git ls-files`.split("\n")
|
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
|
19
|
+
s.require_paths = ['lib']
|
|
20
|
+
|
|
21
|
+
s.required_ruby_version = '>= 3.1.4', '< 3.4.0'
|
|
22
|
+
s.add_development_dependency 'bundler', '~> 2.0'
|
|
23
|
+
s.add_development_dependency 'rake', '~> 13.0'
|
|
24
|
+
s.add_development_dependency 'rspec', '~> 3.0'
|
|
25
|
+
s.add_development_dependency 'rubocop', '~> 1.0'
|
|
26
|
+
s.add_development_dependency 'rubocop-rake', '~> 0.6'
|
|
27
|
+
s.add_development_dependency 'rubocop-rspec', '~> 2.0'
|
|
28
|
+
end
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Capistrano
|
|
4
|
+
module Node
|
|
5
|
+
module Dotenv
|
|
6
|
+
module Helpers
|
|
7
|
+
def remote_file_exists?
|
|
8
|
+
test("[ -f #{dotenv_remote_path} ]")
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def remote_backup_exists?
|
|
12
|
+
count_remote_files.positive?
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def rollback_remote_backup
|
|
16
|
+
latest_backup = latest_remote_backup
|
|
17
|
+
puts "mv #{dotenv_remote_path.parent.join(latest_backup)} #{dotenv_remote_path}"
|
|
18
|
+
execute :mv, dotenv_remote_path.parent.join(latest_backup), dotenv_remote_path
|
|
19
|
+
execute :ls, '-la', dotenv_remote_path.parent
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def latest_remote_backup
|
|
23
|
+
command = "ls -a1t #{dotenv_remote_path.parent} | grep -E '#{backup_regex}' | head -n 1"
|
|
24
|
+
capture(command).strip
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def backup_regex
|
|
28
|
+
# filename we are looking for "#{dotenv_remote_path.basename}-yyyy-mm-dd-HH-MM-SS.bak"
|
|
29
|
+
"#{dotenv_remote_path.basename}-[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}.bak"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def count_remote_files
|
|
33
|
+
command = "ls -a1 #{dotenv_remote_path.parent} | grep -E '#{backup_regex}' | wc -l"
|
|
34
|
+
capture(command).to_i
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def create_remote_backup
|
|
38
|
+
backup_file = "#{dotenv_remote_path.basename}-#{Time.now.strftime('%Y-%m-%d-%H-%M-%S')}.bak"
|
|
39
|
+
# puts "cp #{dotenv_remote_path} #{dotenv_remote_path.parent.join(backup_file)}"
|
|
40
|
+
execute :cp, dotenv_remote_path, dotenv_remote_path.parent.join(backup_file)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def cleanup_remote_backups
|
|
44
|
+
number_of_backups = count_remote_files
|
|
45
|
+
return unless number_of_backups > 5
|
|
46
|
+
|
|
47
|
+
diff = number_of_backups - 5
|
|
48
|
+
# remove older backups and keep the latest 5
|
|
49
|
+
command = "ls -a1t #{dotenv_remote_path.parent} | grep -E '#{backup_regex}' | tail -n #{diff}"
|
|
50
|
+
|
|
51
|
+
capture(command).split("\n").each do |file|
|
|
52
|
+
execute "rm #{dotenv_remote_path.parent.join(file)}"
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def local_dotenv(_env)
|
|
57
|
+
@local_dotenv ||= YAML.load(ERB.new(File.read(dotenv_local_path)).result)
|
|
58
|
+
|
|
59
|
+
@local_dotenv || {}
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def local_yaml
|
|
63
|
+
YAML.safe_load_file(dotenv_local_path) || {}
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def dotenv_env
|
|
67
|
+
fetch(:dotenv_env).to_s
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def dotenv_content
|
|
71
|
+
local_dotenv(dotenv_env).to_yaml
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def configs(yaml, env)
|
|
75
|
+
env_str = env.to_s
|
|
76
|
+
stage_yml = yaml[env_str]&.sort.to_h
|
|
77
|
+
global_yml = remove_nested(yaml)&.sort.to_h
|
|
78
|
+
|
|
79
|
+
other_stages_yml = stages.each_with_object({}) do |f, hash|
|
|
80
|
+
f_str = f.to_s
|
|
81
|
+
next if f_str == env_str
|
|
82
|
+
|
|
83
|
+
hash[f_str] = yaml[f_str]&.sort.to_h
|
|
84
|
+
end.compact
|
|
85
|
+
|
|
86
|
+
[global_yml, stage_yml, other_stages_yml]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def remove_nested(hash)
|
|
90
|
+
hash.each_with_object({}) do |(key, value), new_hash|
|
|
91
|
+
new_hash[key] = value unless value.is_a?(Hash)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def sort_with_nested(hash)
|
|
96
|
+
hash.each_with_object({}) do |(key, value), new_hash|
|
|
97
|
+
new_hash[key] = value.is_a?(Hash) ? sort_with_nested(value) : value
|
|
98
|
+
end.sort.to_h
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def compare_hashes(hash1, hash2)
|
|
102
|
+
all_keys = hash1.keys | hash2.keys # Union of all keys from both hashes
|
|
103
|
+
all_keys.each_with_object({}) do |key, changes_hash|
|
|
104
|
+
old_value = hash2[key].nil? ? 'nil' : hash2[key].to_s
|
|
105
|
+
new_value = hash1[key].nil? ? 'nil' : hash1[key].to_s
|
|
106
|
+
|
|
107
|
+
changes_hash[key] = { old: old_value, new: new_value } if old_value != new_value
|
|
108
|
+
end.tap { |changes| return changes.empty? ? nil : changes }
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# selection helpers
|
|
112
|
+
def ask_to_overwrite(question)
|
|
113
|
+
answer = ''
|
|
114
|
+
until %w[y n].include?(answer)
|
|
115
|
+
print "#{question}? (y/N): "
|
|
116
|
+
answer = $stdin.gets.strip.downcase
|
|
117
|
+
end
|
|
118
|
+
answer == 'y'
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# info helpers
|
|
122
|
+
|
|
123
|
+
def print_changes(changes, message)
|
|
124
|
+
return unless changes
|
|
125
|
+
|
|
126
|
+
puts "#{message}:\n\n"
|
|
127
|
+
changes.each do |key, diff|
|
|
128
|
+
puts "#{key}: #{diff[:old]} => #{diff[:new]}"
|
|
129
|
+
end
|
|
130
|
+
puts "\n"
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# error helpers
|
|
134
|
+
|
|
135
|
+
def check_git_tracking_error
|
|
136
|
+
puts
|
|
137
|
+
puts "Error - please remove '#{fetch(:dotenv_local_path)}' from git:"
|
|
138
|
+
puts
|
|
139
|
+
puts " $ git rm --cached #{fetch(:dotenv_local_path)}"
|
|
140
|
+
puts
|
|
141
|
+
puts 'and gitignore it:'
|
|
142
|
+
puts
|
|
143
|
+
puts " $ echo '#{fetch(:dotenv_local_path)}' >> .gitignore"
|
|
144
|
+
puts
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def check_config_present_error
|
|
148
|
+
puts
|
|
149
|
+
puts "Error - '#{dotenv_env}' config not present in '#{fetch(:dotenv_local_path)}'."
|
|
150
|
+
puts 'Please populate it.'
|
|
151
|
+
puts
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def check_dotenv_file_exists_error
|
|
155
|
+
puts
|
|
156
|
+
puts "Error - '#{fetch(:dotenv_local_path)}' file does not exists, and it's required."
|
|
157
|
+
puts
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# file helpers
|
|
161
|
+
def write_to_file(file, content)
|
|
162
|
+
File.write(file, content)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def write_combined_yaml(yamls_combined)
|
|
166
|
+
if yamls_combined.empty?
|
|
167
|
+
info 'No data to write.'
|
|
168
|
+
else
|
|
169
|
+
# write to new file
|
|
170
|
+
info 'writing to config/application.yml'
|
|
171
|
+
write_to_file(dotenv_local_path, yamls_combined.to_yaml)
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|