Compare commits

...

68 Commits
v4.1.7 ... main

Author SHA1 Message Date
github-actions
2cc5b27bf3 chore(release): 6.0.3 [skip ci]
## [6.0.3](https://github.com/easingthemes/ssh-deploy/compare/v6.0.2...v6.0.3) (2026-04-02)

### Bug Fixes

* keep @semantic-release/github plugin for GitHub Releases ([0cffff4](0cffff4878))
* update major version tag as post-release step ([6306dda](6306ddad7c))
2026-04-02 21:50:15 +00:00
Dragan Filipović
066a59ba0d Merge pull request #213 from easingthemes/fix/update-major-tag-on-release
fix: update major version tag as post-release step
2026-04-02 23:49:42 +02:00
Dragan Filipovic
0cffff4878 fix: keep @semantic-release/github plugin for GitHub Releases
This plugin creates proper GitHub Release pages with changelogs,
which is valuable independently of workflow triggering.
2026-04-02 23:48:36 +02:00
Dragan Filipovic
6306ddad7c fix: update major version tag as post-release step
Move major tag update (e.g. v6) into the release workflow as a
conditional step, gated on semantic-release actually publishing.
This avoids the GITHUB_TOKEN limitation where events from the
default token don't trigger separate workflows.

Also reverts @semantic-release/github from .releaserc since we no
longer rely on the release event to trigger a separate workflow.
2026-04-02 23:47:01 +02:00
github-actions
ffb309fccf chore(release): 6.0.2 [skip ci]
## [6.0.2](https://github.com/easingthemes/ssh-deploy/compare/v6.0.1...v6.0.2) (2026-04-02)

### Bug Fixes

* add @semantic-release/github plugin to create GitHub Releases ([9e4918b](9e4918b4e1))
2026-04-02 21:41:16 +00:00
Dragan Filipović
0f3da31792 Merge pull request #212 from easingthemes/fix/add-github-release-plugin
fix: add @semantic-release/github plugin to create GitHub Releases
2026-04-02 23:40:48 +02:00
Dragan Filipovic
9e4918b4e1 fix: add @semantic-release/github plugin to create GitHub Releases
The update-major-tag workflow triggers on `release: published`, but
semantic-release was only creating git tags (via @semantic-release/git)
without creating actual GitHub Release objects. This meant the workflow
never fired.
2026-04-02 23:40:00 +02:00
github-actions
5d8aeab1a5 chore(release): 6.0.1 [skip ci]
## [6.0.1](https://github.com/easingthemes/ssh-deploy/compare/v6.0.0...v6.0.1) (2026-04-02)

### Bug Fixes

* upgrade devDependencies and migrate to eslint 10 flat config ([c88faf5](c88faf5656))
2026-04-02 21:32:53 +00:00
Dragan Filipović
0f723444d0 Merge pull request #211 from easingthemes/chore/upgrade-devdeps-eslint10
chore: upgrade devDependencies and migrate to eslint 10 flat config
2026-04-02 23:32:28 +02:00
Dragan Filipovic
c88faf5656 fix: upgrade devDependencies and migrate to eslint 10 flat config
- Upgrade eslint 8.31 → 10.1 with new flat config (eslint.config.js)
- Upgrade @vercel/ncc 0.36 → 0.38.4
- Replace eslint-config-airbnb-base + eslint-plugin-import with @eslint/js + @stylistic/eslint-plugin
- Remove stale overrides in package.json (word-wrap, semver)
- Remove obsolete eslint-disable comments from source files
- Add { cause } to rethrown error in rsyncCli.js
- Add .gitignore and CLAUDE.md
- Resolves all 3 npm audit vulnerabilities (flatted, minimatch, js-yaml)
2026-04-02 23:31:02 +02:00
github-actions
d346ae8045 chore(release): 6.0.0 [skip ci]
# [6.0.0](https://github.com/easingthemes/ssh-deploy/compare/v5.1.2...v6.0.0) (2026-04-02)

* feat!: replace rsyncwrapper with direct child_process.spawn ([b11fb7f](b11fb7f911))
* feat!: replace rsyncwrapper with local rsync module ([71b8eb3](71b8eb300f))

### Bug Fixes

* add proc.on('error') handler to prevent hanging on spawn failure ([c81b43c](c81b43c5bf))

### BREAKING CHANGES

* rsyncwrapper dependency removed, rsync command is now
constructed and executed via a local module using child_process.spawn.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* rsyncwrapper dependency removed, rsync command is now
constructed and executed directly via child_process.spawn.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 21:21:22 +00:00
Dragan Filipović
7a10983690 Merge pull request #209 from easingthemes/refactor/remove-rsyncwrapper
feat!: replace rsyncwrapper with direct child_process.spawn
2026-04-02 23:20:52 +02:00
Dragan Filipovic
aea56817cd Merge remote-tracking branch 'origin/main' into refactor/remove-rsyncwrapper 2026-04-02 23:19:51 +02:00
Dragan Filipovic
a4b077c853 chore: revert e2e-manual to use published action 2026-04-02 23:19:12 +02:00
Dragan Filipovic
33880d2926 test: use local action in e2e-manual for branch testing 2026-04-02 23:17:55 +02:00
Dragan Filipovic
c81b43c5bf fix: add proc.on('error') handler to prevent hanging on spawn failure 2026-04-02 23:09:25 +02:00
Dragan Filipovic
71b8eb300f feat!: replace rsyncwrapper with local rsync module
Add src/rsync.js as a drop-in replacement for rsyncwrapper, using
child_process.spawn directly. Only implements the options this project
uses. Single line change in rsyncCli.js to swap the import.

BREAKING CHANGE: rsyncwrapper dependency removed, rsync command is now
constructed and executed via a local module using child_process.spawn.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 23:03:21 +02:00
Dragan Filipovic
76668b2cd6 chore: add workflow to auto-update major version tag on release
Adds update-major-tag.yml that triggers on any published release and
force-updates the major version tag (e.g., v6) to point to the latest
release (e.g., v6.0.1). Enables users to pin to @v6 instead of full
version numbers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 22:59:30 +02:00
Dragan Filipovic
be39df3f93 chore: update GitHub Actions to latest major versions
- actions/checkout v4 → v6
- actions/setup-node v4 → v6
- cycjimmy/semantic-release-action v4 → v6 (pinned semantic_version: 24)
- github/codeql-action v3 → v4
- actions/stale v9 (already latest)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 22:56:10 +02:00
Dragan Filipovic
b11fb7f911 feat!: replace rsyncwrapper with direct child_process.spawn
Remove the outdated rsyncwrapper dependency (unmaintained, uses
deprecated util._extend) and replace with a built-in buildRsyncCommand()
+ spawn implementation. Zero runtime dependencies.

BREAKING CHANGE: rsyncwrapper dependency removed, rsync command is now
constructed and executed directly via child_process.spawn.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 22:50:21 +02:00
github-actions
922253577e chore(release): 5.1.2 [skip ci]
## [5.1.2](https://github.com/easingthemes/ssh-deploy/compare/v5.1.1...v5.1.2) (2026-04-02)

### Bug Fixes

* update Node.js runtime from 20 to 24 ([681efb5](681efb59f1)), closes [#207](https://github.com/easingthemes/ssh-deploy/issues/207)
2026-04-02 20:41:50 +00:00
Dragan Filipović
1326d0a611 Merge pull request #208 from easingthemes/fix/node24-compatibility
fix: update Node.js runtime from 20 to 24
2026-04-02 22:41:26 +02:00
Dragan Filipovic
681efb59f1 fix: update Node.js runtime from 20 to 24
GitHub Actions is deprecating Node.js 20 runners starting June 2, 2026.
Update action runtime and CI workflows to Node.js 24.

Closes #207

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 22:40:21 +02:00
Dragan Filipović
2644554bce Update stale.yml
Some checks failed
Build / build (20.x, ubuntu-latest) (push) Has been cancelled
e2e Test / e2e (push) Has been cancelled
e2e Test / e2e-v3 (push) Has been cancelled
Release / Test, Build and Release (20.x, ubuntu-latest) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Failing after 8m10s
Close stale issues / stale (push) Failing after 14m57s
2026-02-10 21:38:30 +01:00
github-actions
a1aa0b6cf9 chore(release): 5.1.1 [skip ci]
## [5.1.1](https://github.com/easingthemes/ssh-deploy/compare/v5.1.0...v5.1.1) (2024-07-24)

### Bug Fixes

* Update README.md ([f007431](f007431332))
2024-07-24 17:04:53 +00:00
Dragan Filipović
f007431332 fix: Update README.md 2024-07-24 19:04:28 +02:00
github-actions
ece05a2275 chore(release): 5.1.0 [skip ci]
# [5.1.0](https://github.com/easingthemes/ssh-deploy/compare/v5.0.3...v5.1.0) (2024-07-24)

### Features

* Add deleteFile function to helpers module ([1befdb1](1befdb1c6b))
* apply deleteFile function to remoteCmd ([b82eced](b82eced457))
2024-07-24 16:55:35 +00:00
Dragan Filipović
b99511bf85 Merge pull request #184 from Armadillidiid/feat/delete-script-after-exec
Feature: Delete Script After Execution
2024-07-24 18:55:06 +02:00
Emmanuel Isenah
b82eced457 feat: apply deleteFile function to remoteCmd 2024-03-17 22:21:09 +01:00
Emmanuel Isenah
1befdb1c6b feat: Add deleteFile function to helpers module 2024-03-17 22:19:34 +01:00
github-actions
01a39e3348 chore(release): 5.0.3 [skip ci]
## [5.0.3](https://github.com/easingthemes/ssh-deploy/compare/v5.0.2...v5.0.3) (2024-02-27)

### Bug Fixes

* trigger automated release ([4d8bbf0](4d8bbf0deb))
2024-02-27 08:58:54 +00:00
Dragan Filipović
f517598798 Merge pull request #180 from alekw/main
fix: Update actions to Node.js 20
2024-02-27 09:58:22 +01:00
Aleksander Wolak
4d8bbf0deb fix: trigger automated release 2024-02-26 23:55:50 +01:00
Aleksander Wolak
428f2b152b fix own action version 2024-02-26 23:48:33 +01:00
github-actions
8c3965fd5f chore(release): 5.0.2 [skip ci]
## [5.0.2](https://github.com/easingthemes/ssh-deploy/compare/v5.0.1...v5.0.2) (2024-02-18)

### Bug Fixes

* added the missing declarations [#177](https://github.com/easingthemes/ssh-deploy/issues/177) ([bb271fe](bb271fe4c6))
* Fix default values used incorrectly. ([a1b383f](a1b383f560))
2024-02-18 17:44:58 +00:00
Dragan Filipović
186d0277e9 Merge pull request #178 from skyArony/fix-#177
fix: added the missing declarations #177
2024-02-18 18:44:30 +01:00
skyArony
a1b383f560 fix: Fix default values used incorrectly. 2024-02-18 22:43:02 +08:00
Aleksander Wolak
d42d2576ab further fixes 2024-02-16 01:06:08 +01:00
Aleksander Wolak
bed42ef321 further fixes 2024-02-16 00:56:27 +01:00
Aleksander Wolak
07d369f5c8 update well known actions 2024-02-16 00:53:05 +01:00
skyArony
bb271fe4c6 fix: added the missing declarations #177 2024-02-02 16:31:41 +08:00
github-actions
3b7118ee8b chore(release): 5.0.1 [skip ci]
## [5.0.1](https://github.com/easingthemes/ssh-deploy/compare/v5.0.0...v5.0.1) (2024-01-31)

### Bug Fixes

* Add info for Permission denied issue. ([845b578](845b578606))
2024-01-31 01:00:14 +00:00
Dragan Filipović
845b578606 fix: Add info for Permission denied issue. 2024-01-31 01:59:36 +01:00
Dragan Filipović
725ce37b9f Update stale to 90 days 2024-01-18 21:46:42 +01:00
github-actions
16bb35ed0b chore(release): 5.0.0 [skip ci]
# [5.0.0](https://github.com/easingthemes/ssh-deploy/compare/v4.1.10...v5.0.0) (2023-12-12)

* Merge pull request #173 from jeromelachaud/main ([ac1908e](ac1908e5d2)), closes [#173](https://github.com/easingthemes/ssh-deploy/issues/173)

### BREAKING CHANGES

* update to use nodeJS v20
* update to use nodeJS v20
2023-12-12 21:29:39 +00:00
Dragan Filipović
ac1908e5d2 Merge pull request #173 from jeromelachaud/main
BREAKING CHANGE: update to use nodeJS v20

BREAKING CHANGE: update to use nodeJS v20
2023-12-12 22:29:07 +01:00
Jerome Lachaud
d77e3dfdc8 BREAKING CHANGE: update to use nodeJS v20 2023-10-25 13:19:20 +00:00
github-actions
aa1c48118d chore(release): 4.1.10 [skip ci]
## [4.1.10](https://github.com/easingthemes/ssh-deploy/compare/v4.1.9...v4.1.10) (2023-09-30)

### Bug Fixes

* normalize line endings in SSH key for the underlying OS ([3f5d9aa](3f5d9aab1a))
2023-09-30 15:13:25 +00:00
Dragan Filipović
448aa45aa1 Merge pull request #156 from mansandersson/fix-key-line-endings
Normalize line endings in SSH key for the underlying OS
2023-09-30 17:12:46 +02:00
Måns Andersson
3f5d9aab1a fix: normalize line endings in SSH key for the underlying OS 2023-09-28 20:05:00 +02:00
github-actions
2942523001 chore(release): 4.1.9 [skip ci]
## [4.1.9](https://github.com/easingthemes/ssh-deploy/compare/v4.1.8...v4.1.9) (2023-09-24)

### Bug Fixes

* add compiled file ([627ac29](627ac29ece))
* add uuid for ssh scripts ([66f6e4b](66f6e4b367))
2023-09-24 15:37:24 +00:00
Dragan Filipović
ef20db4468 Merge pull request #167 from easingthemes/feature/#131-use-unique-file-name-for-scripts
fix: add uuid for ssh scripts
2023-09-24 17:36:52 +02:00
Dragan Filipovic
627ac29ece fix: add compiled file 2023-09-24 17:35:00 +02:00
Dragan Filipovic
66f6e4b367 fix: add uuid for ssh scripts 2023-09-24 17:29:49 +02:00
Dragan Filipović
d91ea7e0f7 Merge pull request #144 from shoshins/70-no-private-key-password-option
#70 - There is no Password option for Rsync SSH. Documentation is up…
2023-09-24 17:16:11 +02:00
Dragan Filipović
28d946e487 Merge pull request #154 from olivertappin/main
Correct typo in README.md
2023-09-24 16:58:48 +02:00
Dragan Filipović
3ab5951ec1 Merge pull request #141 from dbryan0516/remote-cmd-error
Added SCRIPT_BEFORE_REQUIRED and SCRIPT_AFTER_REQUIRED flags
2023-09-24 16:58:09 +02:00
Dragan Filipović
cf583aab4f Merge pull request #153 from Triloworld/patch-2
Update README.md
2023-08-03 19:18:58 +02:00
Oliver Tappin
8ba86462c0 Correct typo in README.md 2023-06-28 20:36:01 +01:00
Patryk Padus
e7f0312c8c Update README.md
In reference of this issue and comment: https://github.com/easingthemes/ssh-deploy/issues/149
2023-06-23 14:52:23 +02:00
Sergey Shoshin
35ccd2ccc4 #70 - There is no Password option for Rsync SSH. Documentation is updated 2023-04-25 18:11:13 +04:00
Dylan Bryan
ed53b09c84 Added build file 2023-04-18 17:33:08 -04:00
Dylan Bryan
f9797b28d5 Added SCRIPT_BEFORE_REQUIRED and SCRIPT_AFTER_REQUIRED flags for error throwing on remote cmd errors 2023-04-18 17:24:01 -04:00
github-actions
3884c8554f chore(release): 4.1.8 [skip ci]
## [4.1.8](https://github.com/easingthemes/ssh-deploy/compare/v4.1.7...v4.1.8) (2023-02-21)

### Bug Fixes

* rebuild and update readme ([98025d6](98025d680e))
2023-02-21 22:13:02 +00:00
Dragan Filipović
168ff9f4b8 Merge pull request #124 from easingthemes/bugfix/rebuild
fix: rebuild and update readme
2023-02-21 23:12:21 +01:00
Dragan Filipovic
98025d680e fix: rebuild and update readme 2023-02-21 23:11:54 +01:00
Dragan Filipović
88cc5ade97 Merge pull request #123 from sdelfi/fix/update-dist
chore: build dist
2023-02-21 23:06:19 +01:00
Stanislav Salnikov
ad7eefb5ae chore: build dist 2023-02-21 23:03:40 +01:00
25 changed files with 1078 additions and 2545 deletions

View File

@@ -1,25 +0,0 @@
module.exports = {
env: {
commonjs: true,
es6: true,
node: true
},
extends: [
'airbnb-base'
],
globals: {
Atomics: 'readonly',
SharedArrayBuffer: 'readonly'
},
parserOptions: {
ecmaVersion: 2018
},
rules: {
'comma-dangle': [
'error',
'never'
],
'no-console': 'off',
'object-curly-newline': 'off'
}
};

View File

@@ -14,13 +14,13 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest] os: [ubuntu-latest]
node-version: [16.x] node-version: [24.x]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v6
- name: Setup Node.js ${{ matrix.node-version }} - name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3 uses: actions/setup-node@v6
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
- name: Install dependencies - name: Install dependencies

View File

@@ -31,11 +31,11 @@ jobs:
language: [ 'javascript' ] language: [ 'javascript' ]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v6
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v2 uses: github/codeql-action/init@v4
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
@@ -44,4 +44,4 @@ jobs:
npm run build --if-present npm run build --if-present
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2 uses: github/codeql-action/analyze@v4

View File

@@ -27,7 +27,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v6
# ---------------------------------------------------------------- # ----------------------------------------------------------------
# START E2E Test Specific - steps # START E2E Test Specific - steps

View File

@@ -15,7 +15,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v6
# : --------------------------------------------------------------- # : ---------------------------------------------------------------
# : START E2E Test Specific - steps # : START E2E Test Specific - steps
@@ -111,7 +111,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v6
# : --------------------------------------------------------------- # : ---------------------------------------------------------------
# : START E2E Test Specific - steps # : START E2E Test Specific - steps
@@ -169,7 +169,7 @@ jobs:
# ---------------------------------------------------------------- # ----------------------------------------------------------------
- name: e2e Test ssh-deploy action - Target 1 - name: e2e Test ssh-deploy action - Target 1
uses: easingthemes/ssh-deploy@v3 uses: easingthemes/ssh-deploy@main
env: env:
# Shared ENV Vars created in previous steps # Shared ENV Vars created in previous steps
REMOTE_USER: ${{ env.TEST_USER }} REMOTE_USER: ${{ env.TEST_USER }}

View File

@@ -49,9 +49,9 @@ jobs:
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v3 uses: actions/checkout@v6
- name: Setup Node.js ${{ matrix.NODE_VERSION }} - name: Setup Node.js ${{ matrix.NODE_VERSION }}
uses: actions/setup-node@v3 uses: actions/setup-node@v6
with: with:
node-version: ${{ matrix.NODE_VERSION }} node-version: ${{ matrix.NODE_VERSION }}
- name: Commit trigger - name: Commit trigger
@@ -64,8 +64,9 @@ jobs:
- name: Run Tests - name: Run Tests
run: npm test --if-present run: npm test --if-present
- name: Create a release - ${{ github.event.inputs.version }} - name: Create a release - ${{ github.event.inputs.version }}
uses: cycjimmy/semantic-release-action@v3 uses: cycjimmy/semantic-release-action@v6
with: with:
semantic_version: 24
dry_run: ${{ github.event.inputs.dryRun == 'true' }} dry_run: ${{ github.event.inputs.dryRun == 'true' }}
extra_plugins: | extra_plugins: |
@semantic-release/changelog @semantic-release/changelog

View File

@@ -12,13 +12,13 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [ ubuntu-latest ] os: [ ubuntu-latest ]
node-version: [ 16.x ] node-version: [ 24.x ]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v6
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v3 uses: actions/setup-node@v6
with: with:
node-version: ${{ matrix['node-version'] }} node-version: ${{ matrix['node-version'] }}
- name: Install dependencies - name: Install dependencies
@@ -28,8 +28,10 @@ jobs:
- name: Run Tests - name: Run Tests
run: npm test --if-present run: npm test --if-present
- name: Release - name: Release
uses: cycjimmy/semantic-release-action@v3 id: semantic
uses: cycjimmy/semantic-release-action@v6
with: with:
semantic_version: 24
dry_run: false dry_run: false
extra_plugins: | extra_plugins: |
@semantic-release/changelog @semantic-release/changelog
@@ -42,3 +44,11 @@ jobs:
GIT_COMMITTER_NAME: github-actions GIT_COMMITTER_NAME: github-actions
GIT_COMMITTER_EMAIL: github-actions@github.com GIT_COMMITTER_EMAIL: github-actions@github.com
CI: true CI: true
- name: Update major version tag
if: steps.semantic.outputs.new_release_published == 'true'
run: |
MAJOR="v${{ steps.semantic.outputs.new_release_major_version }}"
TAG="v${{ steps.semantic.outputs.new_release_version }}"
echo "Updating $MAJOR tag to point to $TAG"
git tag -f "$MAJOR" "$TAG"
git push -f origin "$MAJOR"

View File

@@ -17,10 +17,10 @@ jobs:
issues: write issues: write
steps: steps:
- uses: actions/stale@v7 - uses: actions/stale@v9
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.' stale-issue-message: 'This issue is stale because it has been open 150 days with no activity. Remove stale label or comment or this will be closed in 30 days.'
days-before-stale: 30 days-before-stale: 150
days-before-close: 5 days-before-close: 30
stale-issue-label: 'stale' stale-issue-label: 'stale'

3
.gitignore vendored
View File

@@ -20,3 +20,6 @@ node_modules/
# IDE # IDE
.idea .idea
.vscode .vscode
# AI
.claude/settings.local.json

View File

@@ -17,6 +17,7 @@
{ {
"assets": ["docs/CHANGELOG.md", "package.json"] "assets": ["docs/CHANGELOG.md", "package.json"]
} }
] ],
"@semantic-release/github"
] ]
} }

40
CLAUDE.md Normal file
View File

@@ -0,0 +1,40 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
GitHub Action for deploying files via rsync over SSH, with optional remote script execution before/after deployment. Published as `easingthemes/ssh-deploy` on the GitHub Marketplace.
## Commands
- **Build (lint + bundle):** `npm run build`
- **Lint only:** `npm run lint`
- **Lint with autofix:** `npm run lint:fix`
- **No unit test suite** — testing is done via e2e workflows in CI (Docker-based SSH server, see `.github/workflows/e2e.yml`)
The build step runs ESLint then bundles `src/index.js` into `dist/index.js` using `@vercel/ncc`. The `dist/index.js` is the actual entrypoint executed by GitHub Actions (defined in `action.yml`).
## Architecture
The action runs as a single Node.js 24 process with this execution flow:
1. **`src/inputs.js`** — Reads all config from environment variables (both `INPUT_*` and bare names). Converts `SNAKE_CASE` input names to `camelCase`. Splits multi-value inputs: `SOURCE` and `ARGS` on spaces, `EXCLUDE` and `SSH_CMD_ARGS` on commas. Prepends `GITHUB_WORKSPACE` to source paths. Exports a single `inputs` object used by all other modules.
2. **`src/index.js`** — Orchestration entry point. Pipeline: validate inputs → write SSH key → optionally update known_hosts → run SCRIPT_BEFORE → rsync → run SCRIPT_AFTER.
3. **`src/sshKey.js`** — Writes the SSH private key to `~/.ssh/<deploy_key_name>` with mode `0400`. Creates `known_hosts` file. Uses `ssh-keyscan` to add the remote host when before/after scripts are configured.
4. **`src/rsyncCli.js`** — Validates rsync is installed (auto-installs via apt-get if missing). Uses the local `src/rsync.js` module (child_process.spawn) to execute the rsync transfer.
5. **`src/remoteCmd.js`** — Writes script content to a temporary `.sh` file in the workspace, executes it on the remote host via `ssh ... 'bash -s' < script.sh`, then deletes the local script file. Passes `RSYNC_STDOUT` env var to after-scripts.
6. **`src/helpers.js`** — File I/O utilities (`writeToFile`, `deleteFile`), input validation, and `snakeToCamel` converter.
## Key Conventions
- **CommonJS modules** — the project uses `require`/`module.exports`, not ES modules.
- **ESLint 10 flat config** (`eslint.config.js`) — uses `@eslint/js` recommended + `@stylistic/eslint-plugin` for formatting. Notable rules: no trailing commas, `console` is allowed.
- **Semantic release** on `main` branch via `.releaserc` — use conventional commit messages (`fix:`, `feat:`, etc.). npm publish is disabled; releases are git tags + changelog only.
- **`dist/index.js` must be committed** — it's the bundled action entrypoint. Run `npm run build` and commit the updated dist after any source changes.
- **Inputs are environment variables**, not `@actions/core` — the action reads directly from `process.env` rather than using the GitHub Actions toolkit.

View File

@@ -28,6 +28,8 @@ The keys should be generated using the PEM format. You can use this command
``` ```
ssh-keygen -m PEM -t rsa -b 4096 ssh-keygen -m PEM -t rsa -b 4096
``` ```
**Please Note:** You should not set a Passphrase (keep it empty) for the private key you generated.
Because rsync ssh (used for deploy) does not support private key password to be entered as a command line parameter.
##### 2. `REMOTE_HOST` [required] ##### 2. `REMOTE_HOST` [required]
@@ -65,25 +67,33 @@ Execution is preformed by storing commands in `.sh` file and executing it via `.
If you have issues with `ssh` connection, use this var, eg `SCRIPT_BEFORE: ls`. If you have issues with `ssh` connection, use this var, eg `SCRIPT_BEFORE: ls`.
This will force `known_hosts` update, adding your host via `ssh-keyscan`. This will force `known_hosts` update, adding your host via `ssh-keyscan`.
##### 10. `SCRIPT_AFTER` (optional, default '') ##### 10. `SCRIPT_BEFORE_REQUIRED` (optional, default false)
If set to `true`, Job will fail if SCRIPT_BEFORE fails.
##### 11. `SCRIPT_AFTER` (optional, default '')
Script to run on host machine after rsync. Script to run on host machine after rsync.
Rsync output is stored in `$RSYNC_STDOUT` env variable. Rsync output is stored in `$RSYNC_STDOUT` env variable.
##### 11. `SSH_CMD_ARGS` (optional, default '-o StrictHostKeyChecking=no') ##### 12. `SCRIPT_AFTER_REQUIRED` (optional, default false)
If set to `true`, Job will fail if SCRIPT_AFTER fails.
##### 13. `SSH_CMD_ARGS` (optional, default '-o StrictHostKeyChecking=no')
A list of ssh arguments, they must be prefixed with -o and separated by a comma, for example: -o SomeArgument=no, -o SomeOtherArgument=5 A list of ssh arguments, they must be prefixed with -o and separated by a comma, for example: -o SomeArgument=no, -o SomeOtherArgument=5
# Usage # Usage
Use the latest version from Marketplace,eg: ssh-deploy@v2 Use the latest version from Marketplace,eg: ssh-deploy@v5.1.0
or use the latest version from a branch, eg: ssh-deploy@main or use the latest version from a branch, eg: ssh-deploy@main
``` ```
- name: Deploy to Staging server - name: Deploy to Staging server
uses: easingthemes/ssh-deploy@main uses: easingthemes/ssh-deploy@main
env: with:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
ARGS: "-rlgoDzvc -i" ARGS: "-rlgoDzvc -i"
SOURCE: "dist/" SOURCE: "dist/"
@@ -113,18 +123,18 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v3
- name: Install Node.js - name: Install Node.js
uses: actions/setup-node@v1 uses: actions/setup-node@v3
with: with:
node-version: '10.x' node-version: '16.x'
- name: Install npm dependencies - name: Install npm dependencies
run: npm install run: npm install
- name: Run build task - name: Run build task
run: npm run build --if-present run: npm run build --if-present
- name: Deploy to Server - name: Deploy to Server
uses: easingthemes/ssh-deploy@main uses: easingthemes/ssh-deploy@main
env: with:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
ARGS: "-rlgoDzvc -i --delete" ARGS: "-rlgoDzvc -i --delete"
SOURCE: "dist/" SOURCE: "dist/"
@@ -142,13 +152,18 @@ Almost 95% of the issues are related to wrong SSH connection or `rsync` params a
These issues are not related to the action itself. These issues are not related to the action itself.
- Check manually your ssh connection from your client before opening a bug report. - Check manually your ssh connection from your client before opening a bug report.
- Check `rsync` params for your use-case. Default params are not going to be enough wor everyone, it highly depends on your setup. - Check `rsync` params for your use-case. Default params are not necessarily going to be enough for everyone, it highly depends on your setup.
- Check manually your rsync command from your client before opening a bug report. - Check manually your rsync command from your client before opening a bug report.
- `Deployment Failed, Permission denied (publickey,password)`: This issue occures in some cases, it is related to OS and ssh. This action can only provide a workaround:
- Use `SCRIPT_BEFORE` param, eg `SCRIPT_BEFORE: ls`. This will force `known_hosts` update, adding your host via `ssh-keyscan`.
- Or manually add public key to authorized_keys and add a new line to a private key.
I've added e2e test for this action. I've added e2e test for this action.
Real example is executed on every PR merge to `main`. Real example is executed on every PR merge to `main`.
Check actions tab for example. Check actions tab for example.
When opening an issue, please add example of your step with env vars. You can add dummy values.
More info for SSH keys: https://www.ssh.com/ssh/public-key-authentication More info for SSH keys: https://www.ssh.com/ssh/public-key-authentication
## Tips ## Tips

View File

@@ -39,15 +39,23 @@ inputs:
description: "Script to run on host machine before rsync" description: "Script to run on host machine before rsync"
required: false required: false
default: "" default: ""
SCRIPT_BEFORE_REQUIRED:
description: "If not an empty string, the action will fail if the before script fails. Note: The string 'false' will be treated as true"
required: false
default: ""
SCRIPT_AFTER: SCRIPT_AFTER:
description: "Script to run on host machine after rsync" description: "Script to run on host machine after rsync"
required: false required: false
default: "" default: ""
SCRIPT_AFTER_REQUIRED:
description: "If not an empty string, the action will fail if the after script fails. Note: The string 'false' will be treated as true"
required: false
default: ""
outputs: outputs:
status: status:
description: "Status" description: "Status"
runs: runs:
using: "node16" using: "node24"
main: "dist/index.js" main: "dist/index.js"
branding: branding:
color: "green" color: "green"

2
dist/index.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +1,125 @@
## [6.0.3](https://github.com/easingthemes/ssh-deploy/compare/v6.0.2...v6.0.3) (2026-04-02)
### Bug Fixes
* keep @semantic-release/github plugin for GitHub Releases ([0cffff4](https://github.com/easingthemes/ssh-deploy/commit/0cffff4878a082b939d7fa0db84034a64b498430))
* update major version tag as post-release step ([6306dda](https://github.com/easingthemes/ssh-deploy/commit/6306ddad7ccb78a18c3a18668fbb0b9b0adb911f))
## [6.0.2](https://github.com/easingthemes/ssh-deploy/compare/v6.0.1...v6.0.2) (2026-04-02)
### Bug Fixes
* add @semantic-release/github plugin to create GitHub Releases ([9e4918b](https://github.com/easingthemes/ssh-deploy/commit/9e4918b4e1c18dfdf7b93e70301baaefeb15bab3))
## [6.0.1](https://github.com/easingthemes/ssh-deploy/compare/v6.0.0...v6.0.1) (2026-04-02)
### Bug Fixes
* upgrade devDependencies and migrate to eslint 10 flat config ([c88faf5](https://github.com/easingthemes/ssh-deploy/commit/c88faf565603544c354d0d27ab6948a6d2048c40))
# [6.0.0](https://github.com/easingthemes/ssh-deploy/compare/v5.1.2...v6.0.0) (2026-04-02)
* feat!: replace rsyncwrapper with direct child_process.spawn ([b11fb7f](https://github.com/easingthemes/ssh-deploy/commit/b11fb7f9113ccfec263a5645d472d0351e4874da))
* feat!: replace rsyncwrapper with local rsync module ([71b8eb3](https://github.com/easingthemes/ssh-deploy/commit/71b8eb300f807e47a87dc96ace9e083d606c05e7))
### Bug Fixes
* add proc.on('error') handler to prevent hanging on spawn failure ([c81b43c](https://github.com/easingthemes/ssh-deploy/commit/c81b43c5bf3a389da4168d71e71ddf2f118c2939))
### BREAKING CHANGES
* rsyncwrapper dependency removed, rsync command is now
constructed and executed via a local module using child_process.spawn.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* rsyncwrapper dependency removed, rsync command is now
constructed and executed directly via child_process.spawn.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
## [5.1.2](https://github.com/easingthemes/ssh-deploy/compare/v5.1.1...v5.1.2) (2026-04-02)
### Bug Fixes
* update Node.js runtime from 20 to 24 ([681efb5](https://github.com/easingthemes/ssh-deploy/commit/681efb59f1b7a4f41e72cf98a4e30f8860ab66f9)), closes [#207](https://github.com/easingthemes/ssh-deploy/issues/207)
## [5.1.1](https://github.com/easingthemes/ssh-deploy/compare/v5.1.0...v5.1.1) (2024-07-24)
### Bug Fixes
* Update README.md ([f007431](https://github.com/easingthemes/ssh-deploy/commit/f007431332cb2dae49153363ad22fb9f90f4aa75))
# [5.1.0](https://github.com/easingthemes/ssh-deploy/compare/v5.0.3...v5.1.0) (2024-07-24)
### Features
* Add deleteFile function to helpers module ([1befdb1](https://github.com/easingthemes/ssh-deploy/commit/1befdb1c6bf3282aa34e6caa431cb2da23d2b17d))
* apply deleteFile function to remoteCmd ([b82eced](https://github.com/easingthemes/ssh-deploy/commit/b82eced4571cb3f63369d51760a81820ffb1bc7f))
## [5.0.3](https://github.com/easingthemes/ssh-deploy/compare/v5.0.2...v5.0.3) (2024-02-27)
### Bug Fixes
* trigger automated release ([4d8bbf0](https://github.com/easingthemes/ssh-deploy/commit/4d8bbf0debaade9fb03b8dc3be3c020955557b12))
## [5.0.2](https://github.com/easingthemes/ssh-deploy/compare/v5.0.1...v5.0.2) (2024-02-18)
### Bug Fixes
* added the missing declarations [#177](https://github.com/easingthemes/ssh-deploy/issues/177) ([bb271fe](https://github.com/easingthemes/ssh-deploy/commit/bb271fe4c69eeeacb986a38cdb3347104143c61f))
* Fix default values used incorrectly. ([a1b383f](https://github.com/easingthemes/ssh-deploy/commit/a1b383f560a7f52a65da3670e61efe6e02f8639a))
## [5.0.1](https://github.com/easingthemes/ssh-deploy/compare/v5.0.0...v5.0.1) (2024-01-31)
### Bug Fixes
* Add info for Permission denied issue. ([845b578](https://github.com/easingthemes/ssh-deploy/commit/845b578606c0c5a956c70caf61e00a7d2b13ee37))
# [5.0.0](https://github.com/easingthemes/ssh-deploy/compare/v4.1.10...v5.0.0) (2023-12-12)
* Merge pull request #173 from jeromelachaud/main ([ac1908e](https://github.com/easingthemes/ssh-deploy/commit/ac1908e5d2dc749496fdbe8a918aa073e3357d85)), closes [#173](https://github.com/easingthemes/ssh-deploy/issues/173)
### BREAKING CHANGES
* update to use nodeJS v20
* update to use nodeJS v20
## [4.1.10](https://github.com/easingthemes/ssh-deploy/compare/v4.1.9...v4.1.10) (2023-09-30)
### Bug Fixes
* normalize line endings in SSH key for the underlying OS ([3f5d9aa](https://github.com/easingthemes/ssh-deploy/commit/3f5d9aab1a743bd426a4d132d07f1f5e9ed0310c))
## [4.1.9](https://github.com/easingthemes/ssh-deploy/compare/v4.1.8...v4.1.9) (2023-09-24)
### Bug Fixes
* add compiled file ([627ac29](https://github.com/easingthemes/ssh-deploy/commit/627ac29ece9dc2f1185a50d1002bc2c968fc973c))
* add uuid for ssh scripts ([66f6e4b](https://github.com/easingthemes/ssh-deploy/commit/66f6e4b367ea39479c285234797a4e86c90d9abd))
## [4.1.8](https://github.com/easingthemes/ssh-deploy/compare/v4.1.7...v4.1.8) (2023-02-21)
### Bug Fixes
* rebuild and update readme ([98025d6](https://github.com/easingthemes/ssh-deploy/commit/98025d680e96a5c6c805e377a1b81de2f626aa1e))
## [4.1.7](https://github.com/easingthemes/ssh-deploy/compare/v4.1.6...v4.1.7) (2023-02-21) ## [4.1.7](https://github.com/easingthemes/ssh-deploy/compare/v4.1.6...v4.1.7) (2023-02-21)

52
eslint.config.js Normal file
View File

@@ -0,0 +1,52 @@
const js = require('@eslint/js');
const stylistic = require('@stylistic/eslint-plugin');
const globals = require('globals');
module.exports = [
js.configs.recommended,
{
plugins: {
'@stylistic': stylistic
},
languageOptions: {
ecmaVersion: 2020,
sourceType: 'commonjs',
globals: {
...globals.node,
...globals.commonjs,
...globals.es2020,
Atomics: 'readonly',
SharedArrayBuffer: 'readonly'
}
},
rules: {
// Stylistic rules (migrated from airbnb-base)
'@stylistic/comma-dangle': ['error', 'never'],
'@stylistic/indent': ['error', 2, { SwitchCase: 1 }],
'@stylistic/semi': ['error', 'always'],
'@stylistic/quotes': ['error', 'single', { avoidEscape: true }],
'@stylistic/arrow-parens': ['error', 'always'],
'@stylistic/brace-style': ['error', '1tbs', { allowSingleLine: true }],
'@stylistic/comma-spacing': ['error', { before: false, after: true }],
'@stylistic/key-spacing': ['error', { beforeColon: false, afterColon: true }],
'@stylistic/keyword-spacing': ['error', { before: true, after: true }],
'@stylistic/eol-last': ['error', 'always'],
'@stylistic/no-trailing-spaces': 'error',
'@stylistic/space-before-blocks': 'error',
'@stylistic/space-infix-ops': 'error',
'@stylistic/arrow-spacing': ['error', { before: true, after: true }],
// JS rules (migrated from airbnb-base)
'no-console': 'off',
'no-var': 'error',
'prefer-const': 'error',
'prefer-arrow-callback': 'error',
'prefer-template': 'error',
'object-shorthand': ['error', 'always'],
'no-shadow': 'error',
'no-use-before-define': ['error', { functions: true, classes: true, variables: true }],
'camelcase': ['error', { properties: 'never' }],
'no-unused-vars': ['error', { vars: 'all', args: 'after-used', ignoreRestSiblings: true }]
}
}
];

3138
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "@draganfilipovic/ssh-deploy", "name": "@draganfilipovic/ssh-deploy",
"version": "4.1.7", "version": "6.0.3",
"description": "Fast NodeJS action to deploy specific directory from `GITHUB_WORKSPACE` to a server via rsync over ssh.", "description": "Fast NodeJS action to deploy specific directory from `GITHUB_WORKSPACE` to a server via rsync over ssh.",
"main": "dist/index.js", "main": "dist/index.js",
"files": [ "files": [
@@ -29,13 +29,11 @@
"url": "https://github.com/easingthemes/ssh-deploy/issues" "url": "https://github.com/easingthemes/ssh-deploy/issues"
}, },
"homepage": "https://github.com/easingthemes/ssh-deploy#readme", "homepage": "https://github.com/easingthemes/ssh-deploy#readme",
"dependencies": {
"rsyncwrapper": "^3.0.1"
},
"devDependencies": { "devDependencies": {
"@vercel/ncc": "^0.36.0", "@eslint/js": "^10.0.1",
"eslint": "^8.30.0", "@stylistic/eslint-plugin": "^5.10.0",
"eslint-config-airbnb-base": "^15.0.0", "@vercel/ncc": "^0.38.4",
"eslint-plugin-import": "^2.26.0" "eslint": "^10.1.0",
"globals": "^17.4.0"
} }
} }

View File

@@ -1,4 +1,4 @@
const { existsSync, mkdirSync, writeFileSync } = require('fs'); const { existsSync, mkdirSync, writeFileSync, unlink } = require('fs');
const { join } = require('path'); const { join } = require('path');
const validateDir = (dir) => { const validateDir = (dir) => {
@@ -45,6 +45,29 @@ const writeToFile = ({ dir, filename, content, isRequired, mode = '0644' }) => {
} }
}; };
const deleteFile = ({ dir, filename, isRequired }) => {
validateDir(dir);
const filePath = join(dir, filename);
if (existsSync(filePath)) {
const message = `⚠️ [FILE] ${filePath} Required file exist.`;
handleError(message, isRequired);
return;
}
try {
console.log(`[FILE] Deleting ${filePath} file ...`);
unlink(filePath, (error) => {
if (error) {
throw new Error(error);
}
});
} catch (error) {
const message = `⚠️[FILE] Deleting file error. filePath: ${filePath}, message: ${error.message}`;
handleError(message, isRequired);
}
};
const validateRequiredInputs = (inputs) => { const validateRequiredInputs = (inputs) => {
const inputKeys = Object.keys(inputs); const inputKeys = Object.keys(inputs);
const validInputs = inputKeys.filter((inputKey) => { const validInputs = inputKeys.filter((inputKey) => {
@@ -66,6 +89,7 @@ const snakeToCamel = (str) => str.replace(/[^a-zA-Z0-9]+(.)/g, (m, chr) => chr.t
module.exports = { module.exports = {
writeToFile, writeToFile,
deleteFile,
validateRequiredInputs, validateRequiredInputs,
snakeToCamel snakeToCamel
}; };

View File

@@ -10,7 +10,8 @@ const run = async () => {
source, remoteUser, remoteHost, remotePort, source, remoteUser, remoteHost, remotePort,
deployKeyName, sshPrivateKey, deployKeyName, sshPrivateKey,
args, exclude, sshCmdArgs, args, exclude, sshCmdArgs,
scriptBefore, scriptAfter, scriptBefore, scriptBeforeRequired,
scriptAfter, scriptAfterRequired,
rsyncServer rsyncServer
} = inputs; } = inputs;
// Validate required inputs // Validate required inputs
@@ -24,16 +25,15 @@ const run = async () => {
} }
// Check Script before // Check Script before
if (scriptBefore) { if (scriptBefore) {
await remoteCmdBefore(scriptBefore, privateKeyPath); await remoteCmdBefore(scriptBefore, privateKeyPath, scriptBeforeRequired);
} }
/* eslint-disable object-property-newline */
await sshDeploy({ await sshDeploy({
source, rsyncServer, exclude, remotePort, source, rsyncServer, exclude, remotePort,
privateKeyPath, args, sshCmdArgs privateKeyPath, args, sshCmdArgs
}); });
// Check script after // Check script after
if (scriptAfter) { if (scriptAfter) {
await remoteCmdAfter(scriptAfter, privateKeyPath); await remoteCmdAfter(scriptAfter, privateKeyPath, scriptAfterRequired);
} }
}; };

View File

@@ -4,7 +4,7 @@ const inputNames = [
'REMOTE_HOST', 'REMOTE_USER', 'REMOTE_PORT', 'REMOTE_HOST', 'REMOTE_USER', 'REMOTE_PORT',
'SSH_PRIVATE_KEY', 'DEPLOY_KEY_NAME', 'SSH_PRIVATE_KEY', 'DEPLOY_KEY_NAME',
'SOURCE', 'TARGET', 'ARGS', 'SSH_CMD_ARGS', 'EXCLUDE', 'SOURCE', 'TARGET', 'ARGS', 'SSH_CMD_ARGS', 'EXCLUDE',
'SCRIPT_BEFORE', 'SCRIPT_AFTER']; 'SCRIPT_BEFORE', 'SCRIPT_AFTER', 'SCRIPT_BEFORE_REQUIRED', 'SCRIPT_AFTER_REQUIRED'];
const githubWorkspace = process.env.GITHUB_WORKSPACE; const githubWorkspace = process.env.GITHUB_WORKSPACE;
const remoteUser = process.env.REMOTE_USER || process.env.INPUT_REMOTE_USER; const remoteUser = process.env.REMOTE_USER || process.env.INPUT_REMOTE_USER;
@@ -27,7 +27,6 @@ inputNames.forEach((input) => {
const inputVal = process.env[input] || process.env[`INPUT_${input}`] || defaultInputs[inputName]; const inputVal = process.env[input] || process.env[`INPUT_${input}`] || defaultInputs[inputName];
const validVal = inputVal === undefined ? defaultInputs[inputName] : inputVal; const validVal = inputVal === undefined ? defaultInputs[inputName] : inputVal;
let extendedVal = validVal; let extendedVal = validVal;
// eslint-disable-next-line default-case
switch (inputName) { switch (inputName) {
case 'source': case 'source':
extendedVal = validVal.split(' ').map((src) => `${githubWorkspace}/${src}`); extendedVal = validVal.split(' ').map((src) => `${githubWorkspace}/${src}`);

View File

@@ -1,6 +1,7 @@
const { exec } = require('child_process'); const { exec } = require('child_process');
const crypto = require('crypto');
const { sshServer, githubWorkspace, remotePort } = require('./inputs'); const { sshServer, githubWorkspace, remotePort } = require('./inputs');
const { writeToFile } = require('./helpers'); const { writeToFile, deleteFile } = require('./helpers');
const handleError = (message, isRequired, callback) => { const handleError = (message, isRequired, callback) => {
if (isRequired) { if (isRequired) {
@@ -10,9 +11,9 @@ const handleError = (message, isRequired, callback) => {
} }
}; };
// eslint-disable-next-line max-len
const remoteCmd = async (content, privateKeyPath, isRequired, label) => new Promise((resolve, reject) => { const remoteCmd = async (content, privateKeyPath, isRequired, label) => new Promise((resolve, reject) => {
const filename = `local_ssh_script-${label}.sh`; const uuid = crypto.randomUUID();
const filename = `local_ssh_script-${label}-${uuid}.sh`;
try { try {
writeToFile({ dir: githubWorkspace, filename, content }); writeToFile({ dir: githubWorkspace, filename, content });
const dataLimit = 10000; const dataLimit = 10000;
@@ -28,6 +29,8 @@ const remoteCmd = async (content, privateKeyPath, isRequired, label) => new Prom
} else { } else {
const limited = data.substring(0, dataLimit); const limited = data.substring(0, dataLimit);
console.log('✅ [CMD] Remote script executed. \n', limited, stderr); console.log('✅ [CMD] Remote script executed. \n', limited, stderr);
deleteFile({ dir: githubWorkspace, filename });
console.log('✅ [FILE] Script file deleted.');
resolve(limited); resolve(limited);
} }
} }

56
src/rsync.js Normal file
View File

@@ -0,0 +1,56 @@
const { spawn } = require('child_process');
const escapeSpaces = (str) => (typeof str === 'string' ? str.replace(/\b\s/g, '\\ ') : str);
const buildRsyncCommand = ({ src, dest, excludeFirst, port, privateKey, args, sshCmdArgs }) => {
const cmdParts = [];
const sources = Array.isArray(src) ? src : [src];
cmdParts.push(...sources.map(escapeSpaces));
cmdParts.push(escapeSpaces(dest));
let sshCmd = `ssh -p ${port || 22} -i ${privateKey}`;
if (sshCmdArgs && sshCmdArgs.length > 0) {
sshCmd += ` ${sshCmdArgs.join(' ')}`;
}
cmdParts.push('--rsh', `"${sshCmd}"`);
cmdParts.push('--recursive');
if (Array.isArray(excludeFirst)) {
excludeFirst.forEach((pattern) => {
if (pattern) cmdParts.push(`--exclude=${escapeSpaces(pattern)}`);
});
}
if (Array.isArray(args)) {
cmdParts.push(...args);
}
return `rsync ${[...new Set(cmdParts)].join(' ')}`;
};
module.exports = (options, callback) => {
const cmd = buildRsyncCommand(options);
const noop = () => {};
const onStdout = options.onStdout || noop;
const onStderr = options.onStderr || noop;
let stdout = '';
let stderr = '';
const proc = spawn('/bin/sh', ['-c', cmd]);
proc.stdout.on('data', (data) => { onStdout(data); stdout += data; });
proc.stderr.on('data', (data) => { onStderr(data); stderr += data; });
proc.on('exit', (code) => {
let error = null;
if (code !== 0) {
error = new Error(`rsync exited with code ${code}`);
error.code = code;
}
callback(error, stdout, stderr, cmd);
});
proc.on('error', (err) => callback(err, stdout, stderr, cmd));
};

View File

@@ -1,5 +1,5 @@
const { execSync } = require('child_process'); const { execSync } = require('child_process');
const nodeRsync = require('rsyncwrapper'); const nodeRsync = require('./rsync');
const nodeRsyncPromise = async (config) => new Promise((resolve, reject) => { const nodeRsyncPromise = async (config) => new Promise((resolve, reject) => {
const logCMD = (cmd) => { const logCMD = (cmd) => {
@@ -46,7 +46,7 @@ const validateRsync = async () => {
execSync('sudo DEBIAN_FRONTEND=noninteractive apt-get -y update && sudo DEBIAN_FRONTEND=noninteractive apt-get --no-install-recommends -y install rsync', { stdio: 'inherit' }); execSync('sudo DEBIAN_FRONTEND=noninteractive apt-get -y update && sudo DEBIAN_FRONTEND=noninteractive apt-get --no-install-recommends -y install rsync', { stdio: 'inherit' });
console.log('✅ [CLI] Rsync installed. \n'); console.log('✅ [CLI] Rsync installed. \n');
} catch (error) { } catch (error) {
throw new Error(`⚠️ [CLI] Rsync installation failed. Aborting ... error: ${error.message}`); throw new Error(`⚠️ [CLI] Rsync installation failed. Aborting ... error: ${error.message}`, { cause: error });
} }
}; };
@@ -65,7 +65,6 @@ const rsyncCli = async ({
}; };
// RSYNC COMMAND // RSYNC COMMAND
/* eslint-disable object-property-newline */
return nodeRsyncPromise({ return nodeRsyncPromise({
...defaultOptions, ...defaultOptions,
src: source, dest: rsyncServer, excludeFirst: exclude, port: remotePort, src: source, dest: rsyncServer, excludeFirst: exclude, port: remotePort,

View File

@@ -1,5 +1,6 @@
const { join } = require('path'); const { join } = require('path');
const { execSync } = require('child_process'); const { execSync } = require('child_process');
const { EOL } = require('os');
const { writeToFile } = require('./helpers'); const { writeToFile } = require('./helpers');
const KNOWN_HOSTS = 'known_hosts'; const KNOWN_HOSTS = 'known_hosts';
@@ -19,7 +20,7 @@ const addSshKey = (content, deployKeyName) => {
const { dir, filename } = getPrivateKeyPath(deployKeyName); const { dir, filename } = getPrivateKeyPath(deployKeyName);
writeToFile({ dir, filename: KNOWN_HOSTS, content: '' }); writeToFile({ dir, filename: KNOWN_HOSTS, content: '' });
console.log('✅ [SSH] known_hosts file ensured', dir); console.log('✅ [SSH] known_hosts file ensured', dir);
writeToFile({ dir, filename, content: `${content}\r\n`, isRequired: true, mode: '0400' }); writeToFile({ dir, filename, content: `${content}${EOL}`, isRequired: true, mode: '0400' });
console.log('✅ [SSH] key added to `.ssh` dir ', dir, filename); console.log('✅ [SSH] key added to `.ssh` dir ', dir, filename);
}; };