Compare commits

...

10 Commits

Author SHA1 Message Date
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
13 changed files with 130 additions and 39 deletions

View File

@@ -18,9 +18,9 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Setup Node.js ${{ matrix.node-version }} - name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4 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@v4 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@v3 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@v3 uses: github/codeql-action/analyze@v4

View File

@@ -27,7 +27,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 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@v4 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@v4 uses: actions/checkout@v6
# : --------------------------------------------------------------- # : ---------------------------------------------------------------
# : START E2E Test Specific - steps # : START E2E Test Specific - steps

View File

@@ -49,9 +49,9 @@ jobs:
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Setup Node.js ${{ matrix.NODE_VERSION }} - name: Setup Node.js ${{ matrix.NODE_VERSION }}
uses: actions/setup-node@v4 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@v4 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

@@ -16,9 +16,9 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4 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,9 @@ 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@v4 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

23
.github/workflows/update-major-tag.yml vendored Normal file
View File

@@ -0,0 +1,23 @@
name: Update major version tag
on:
release:
types: [published]
jobs:
update-tag:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Update major version tag
run: |
VERSION="${{ github.event.release.tag_name }}"
MAJOR="v$(echo "$VERSION" | sed 's/^v//' | cut -d. -f1)"
echo "Updating $MAJOR tag to point to $VERSION"
git tag -f "$MAJOR" "$VERSION"
git push -f origin "$MAJOR"

2
dist/index.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +1,26 @@
# [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) ## [5.1.2](https://github.com/easingthemes/ssh-deploy/compare/v5.1.1...v5.1.2) (2026-04-02)

28
package-lock.json generated
View File

@@ -1,16 +1,13 @@
{ {
"name": "@draganfilipovic/ssh-deploy", "name": "@draganfilipovic/ssh-deploy",
"version": "4.1.10", "version": "5.1.1",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@draganfilipovic/ssh-deploy", "name": "@draganfilipovic/ssh-deploy",
"version": "4.1.10", "version": "5.1.1",
"license": "MIT", "license": "MIT",
"dependencies": {
"rsyncwrapper": "^3.0.1"
},
"devDependencies": { "devDependencies": {
"@vercel/ncc": "^0.36.0", "@vercel/ncc": "^0.36.0",
"eslint": "^8.30.0", "eslint": "^8.30.0",
@@ -129,6 +126,7 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz",
"integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==",
"dev": true, "dev": true,
"peer": true,
"bin": { "bin": {
"acorn": "bin/acorn" "acorn": "bin/acorn"
}, },
@@ -459,6 +457,7 @@
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.31.0.tgz", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.31.0.tgz",
"integrity": "sha512-0tQQEVdmPZ1UtUKXjX7EMm9BlgJ08G90IhWh0PKDCb3ZLsgAOHI8fYSIzYVZej92zsgq+ft0FGsxhJ3xo2tbuA==", "integrity": "sha512-0tQQEVdmPZ1UtUKXjX7EMm9BlgJ08G90IhWh0PKDCb3ZLsgAOHI8fYSIzYVZej92zsgq+ft0FGsxhJ3xo2tbuA==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"@eslint/eslintrc": "^1.4.1", "@eslint/eslintrc": "^1.4.1",
"@humanwhocodes/config-array": "^0.11.8", "@humanwhocodes/config-array": "^0.11.8",
@@ -579,6 +578,7 @@
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz",
"integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"array-includes": "^3.1.4", "array-includes": "^3.1.4",
"array.prototype.flat": "^1.2.5", "array.prototype.flat": "^1.2.5",
@@ -1698,14 +1698,6 @@
"url": "https://github.com/sponsors/isaacs" "url": "https://github.com/sponsors/isaacs"
} }
}, },
"node_modules/rsyncwrapper": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/rsyncwrapper/-/rsyncwrapper-3.0.1.tgz",
"integrity": "sha512-fkGmeEJRbKveT/6bBqTVzzHS1wtbGQwL6qnwT/+1AtMAsEV5dX1fSAiOJVZrDOnVsOr2lFl8ga1MZLoHekV3yg==",
"engines": {
"node": ">=8.1.3"
}
},
"node_modules/run-parallel": { "node_modules/run-parallel": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
@@ -2083,7 +2075,8 @@
"version": "8.8.1", "version": "8.8.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz",
"integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==",
"dev": true "dev": true,
"peer": true
}, },
"acorn-jsx": { "acorn-jsx": {
"version": "5.3.2", "version": "5.3.2",
@@ -2328,6 +2321,7 @@
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.31.0.tgz", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.31.0.tgz",
"integrity": "sha512-0tQQEVdmPZ1UtUKXjX7EMm9BlgJ08G90IhWh0PKDCb3ZLsgAOHI8fYSIzYVZej92zsgq+ft0FGsxhJ3xo2tbuA==", "integrity": "sha512-0tQQEVdmPZ1UtUKXjX7EMm9BlgJ08G90IhWh0PKDCb3ZLsgAOHI8fYSIzYVZej92zsgq+ft0FGsxhJ3xo2tbuA==",
"dev": true, "dev": true,
"peer": true,
"requires": { "requires": {
"@eslint/eslintrc": "^1.4.1", "@eslint/eslintrc": "^1.4.1",
"@humanwhocodes/config-array": "^0.11.8", "@humanwhocodes/config-array": "^0.11.8",
@@ -2428,6 +2422,7 @@
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz",
"integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==",
"dev": true, "dev": true,
"peer": true,
"requires": { "requires": {
"array-includes": "^3.1.4", "array-includes": "^3.1.4",
"array.prototype.flat": "^1.2.5", "array.prototype.flat": "^1.2.5",
@@ -3221,11 +3216,6 @@
"glob": "^7.1.3" "glob": "^7.1.3"
} }
}, },
"rsyncwrapper": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/rsyncwrapper/-/rsyncwrapper-3.0.1.tgz",
"integrity": "sha512-fkGmeEJRbKveT/6bBqTVzzHS1wtbGQwL6qnwT/+1AtMAsEV5dX1fSAiOJVZrDOnVsOr2lFl8ga1MZLoHekV3yg=="
},
"run-parallel": { "run-parallel": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@draganfilipovic/ssh-deploy", "name": "@draganfilipovic/ssh-deploy",
"version": "5.1.2", "version": "6.0.0",
"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,9 +29,6 @@
"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", "@vercel/ncc": "^0.36.0",
"eslint": "^8.30.0", "eslint": "^8.30.0",

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) => {