Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,25 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6

- name: Retrieve dependencies from cache
uses: actions/cache@v4
id: cacheNpm
- name: Restore npm cache
uses: actions/cache@v5
with:
path: ~/.npm
key: npm-v22-${{ runner.os }}-refs/heads/main-${{ hashFiles('package.json') }}
key: npm-no-lock-v1-${{ runner.os }}-node22-${{ hashFiles('package.json') }}
restore-keys: |
npm-no-lock-v1-${{ runner.os }}-node22-

- name: Install Node.js and npm
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: 22.x
package-manager-cache: false
registry-url: https://registry.npmjs.org

- name: Install dependencies
run: npm install
run: npm install --package-lock=false

# Store the name of the release
# See https://stackoverflow.com/questions/58177786/get-the-current-pushed-tag-in-github-actions
Expand Down Expand Up @@ -70,9 +72,10 @@ jobs:
name: npm-package

- name: Install Node.js and npm
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: 22.x
package-manager-cache: false
registry-url: https://registry.npmjs.org

- name: Publish new version
Expand Down
51 changes: 24 additions & 27 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 30

Expand All @@ -26,23 +26,22 @@ jobs:
git checkout main
git checkout pr

- name: Retrieve dependencies from cache
id: cacheNpm
uses: actions/cache@v4
- name: Restore npm cache
uses: actions/cache@v5
with:
path: ~/.npm
key: npm-v22-${{ runner.os }}-${{ github.ref }}-${{ hashFiles('package.json') }}
key: npm-no-lock-v1-${{ runner.os }}-node22-${{ hashFiles('package.json') }}
restore-keys: |
npm-v22-${{ runner.os }}-${{ github.ref }}-
npm-v22-${{ runner.os }}-refs/heads/main-
npm-no-lock-v1-${{ runner.os }}-node22-

- name: Install Node.js and npm
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: 22.x
package-manager-cache: false

- name: Install dependencies
run: npm install
run: npm install --package-lock=false

- name: Validate formatting
run: npm run prettier-check:updated
Expand All @@ -64,25 +63,24 @@ jobs:
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6

- name: Retrieve dependencies from cache
id: cacheNpm
uses: actions/cache@v4
- name: Restore npm cache
uses: actions/cache@v5
with:
path: ~/.npm
key: npm-v20-${{ runner.os }}-${{ github.ref }}-${{ hashFiles('package.json') }}
key: npm-no-lock-v1-${{ runner.os }}-node22-${{ hashFiles('package.json') }}
restore-keys: |
npm-v20-${{ runner.os }}-${{ github.ref }}-
npm-v20-${{ runner.os }}-refs/heads/main-
npm-no-lock-v1-${{ runner.os }}-node22-

- name: Install Node.js and npm
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: 22.x
package-manager-cache: false

- name: Install dependencies
run: npm install
run: npm install --package-lock=false

- name: Unit tests
run: npm test
Expand All @@ -92,25 +90,24 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6

- name: Retrieve dependencies from cache
id: cacheNpm
uses: actions/cache@v4
- name: Restore npm cache
uses: actions/cache@v5
with:
path: ~/.npm
key: npm-v20-${{ runner.os }}-${{ github.ref }}-${{ hashFiles('package.json') }}
key: npm-no-lock-v1-${{ runner.os }}-node20-${{ hashFiles('package.json') }}
restore-keys: |
npm-v20-${{ runner.os }}-${{ github.ref }}-
npm-v20-${{ runner.os }}-refs/heads/main-
npm-no-lock-v1-${{ runner.os }}-node20-

- name: Install Node.js and npm
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: 20.x
package-manager-cache: false

- name: Install dependencies
run: npm install
run: npm install --package-lock=false

- name: Unit tests
run: npm test
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/coverage
/dist
npm-debug.log
package-lock.json
yarn.lock
/package-lock.json
/yarn.lock
node_modules
.serverless
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
This repository contains the code for Serverless Framework Compose.

Compose requires Node.js 20 or later.
Requires Node.js `^20.19.0 || >=22.12.0`.

There is no standalone binary version, the package is only available via NPM.

Expand Down
9 changes: 3 additions & 6 deletions bin/serverless-compose
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,13 @@ const isMainModule = !EvalError.$composeCommandStartTime;
if (isMainModule) EvalError.$composeCommandStartTime = process.hrtime();

const isSupportedNodeVersion = require('../src/cli/is-supported-node-version');
const minimumSupportedVersionMajor = 20;
const minimumSupportedVersionMinor = 0;

if (!isSupportedNodeVersion(process.version)) {
const composeVersion = require('../package.json').version;
process.stderr.write(
`Error: Serverless Framework Compose v${composeVersion} does not support ` +
`Node.js ${process.version}. Please upgrade Node.js to the latest ` +
`LTS release. Minimum supported version: ` +
`v${minimumSupportedVersionMajor}.${minimumSupportedVersionMinor}.0.\n`
`Node.js ${process.version}. Please use a supported release. ` +
`Supported versions: ${isSupportedNodeVersion.supportedRange}.\n`
);
process.exit(1);
}
Expand All @@ -30,4 +27,4 @@ const crashAsync = (error) => {
const { runComponents } = require('../src');

// Compose is npm-only, so there is no standalone or local-fallback bootstrap path here.
runComponents().catch(crashAsync);
runComponents(process.argv.slice(2)).catch(crashAsync);
16 changes: 3 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"prettify": "prettier --write --ignore-path .gitignore \"**/*.{css,html,js,json,md,yaml,yml}\"",
"prettier-check:updated": "pipe-git-updated --ext=css --ext=html --ext=js --ext=json --ext=md --ext=yaml --ext=yml --base=main -- prettier -c",
"lint:updated": "pipe-git-updated --ext=js --base=main -- eslint",
"test": "mocha \"test/**/*.test.js\""
"test": "mocha --config test/mocha/unit.cjs"
},
"license": "MIT",
"dependencies": {
Expand Down Expand Up @@ -73,7 +73,7 @@
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-n": "^17.24.0",
"git-list-updated": "^1.2.1",
"mocha": "^11.7.5",
"mocha": "12.0.0-beta-9.2",
Comment thread
GrahamCampbell marked this conversation as resolved.
"prettier": "^2.8.8",
"proxyquire": "^2.1.3",
"sinon": "^13.0.2",
Expand Down Expand Up @@ -343,18 +343,8 @@
}
]
},
"mocha": {
"require": [
"./test/lib/setup/patch",
"./test/lib/setup/log",
"./test/lib/setup/mock-homedir",
"./test/lib/setup/mock-cwd",
"./test/lib/setup/restore-env"
],
"timeout": 10000
},
"engines": {
"node": ">=20.0.0"
"node": "^20.19.0 || >=22.12.0"
},
"directories": {
"test": "test"
Expand Down
6 changes: 3 additions & 3 deletions src/ComponentsService.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ const validateGraph = (graph) => {
}
};

const getAllComponents = async (obj = {}) => {
const getAllComponents = async (obj = {}, root = process.cwd()) => {
const allComponents = {};

for (const [key, val] of Object.entries(obj.services)) {
Expand All @@ -90,7 +90,7 @@ const getAllComponents = async (obj = {}) => {

// Local component (starts with '.')
if (val.component[0] === '.') {
const localComponentPath = resolve(process.cwd(), val.component);
const localComponentPath = resolve(root, val.component);
if (!(await utils.fileExists(localComponentPath))) {
throw new ServerlessError(
`The component "${val.component}" (used by service "${key}") is invalid: file not found`,
Expand Down Expand Up @@ -237,7 +237,7 @@ class ComponentsService {
}

async init() {
const allComponents = await getAllComponents(this.configuration);
const allComponents = await getAllComponents(this.configuration, this.context.root);
await validateComponents(allComponents);
this.allComponents = setDependencies(allComponents);

Expand Down
4 changes: 2 additions & 2 deletions src/Context.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const ServerlessError = require('./serverless-error');
class Context {
constructor(config) {
this.version = packageJson.version;
this.root = path.resolve(config.root) || process.cwd();
this.root = path.resolve(config.root || process.cwd());
this.output = new Output(config.verbose || false, config.disableIO);
/** @type {string} */
this.stage = config.stage;
Expand All @@ -42,7 +42,7 @@ class Context {

async setupStateStorage() {
if (!this.configuration.state) {
this.stateStorage = new LocalStateStorage(this.stage);
this.stateStorage = new LocalStateStorage(this.root, this.stage);
return;
}

Expand Down
14 changes: 10 additions & 4 deletions src/cli/is-supported-node-version.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
'use strict';

module.exports = (version) => {
const major = Number(version.split('.')[0].slice(1));
const minor = Number(version.split('.')[1]);
const semver = require('semver');

return major > 20 || (major === 20 && minor >= 0);
const supportedRange = '^20.19.0 || >=22.12.0';

const isSupportedNodeVersion = (version) => {
const normalizedVersion = semver.coerce(version);

return Boolean(normalizedVersion && semver.satisfies(normalizedVersion.version, supportedRange));
};

module.exports = isSupportedNodeVersion;
module.exports.supportedRange = supportedRange;
7 changes: 2 additions & 5 deletions src/configuration/resolve-path.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,9 @@ const ServerlessError = require('../serverless-error');

const supportedExtensions = new Set(['yml', 'yaml', 'json', 'js', 'ts']);

module.exports = async () => {
module.exports = async (cwd = process.cwd()) => {
for (const extension of supportedExtensions) {
const eventualServiceConfigPath = path.resolve(
process.cwd(),
`serverless-compose.${extension}`
);
const eventualServiceConfigPath = path.resolve(cwd, `serverless-compose.${extension}`);
if (await fileExists(eventualServiceConfigPath)) return eventualServiceConfigPath;
}
// As the default will be `serverless-compose.yml`, let's provide users with a more actionable error message,
Expand Down
13 changes: 7 additions & 6 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
'use strict';

// Setup log writing
require('./utils/serverless-utils/log-reporters/node');

const args = require('minimist')(process.argv.slice(2));
const minimist = require('minimist');
const renderHelp = require('./render-help');
const Context = require('./Context');
const ComponentsService = require('./ComponentsService');
Expand All @@ -14,6 +11,7 @@ const resolveConfigurationPath = require('./configuration/resolve-path');
const readConfiguration = require('./configuration/read');
const { validateConfiguration } = require('./configuration/validate');
const validateOptions = require('./validate-options');
const initializeNodeLogging = require('./utils/serverless-utils/log-reporters/node');

let options;
let method;
Expand All @@ -39,7 +37,10 @@ require('signal-exit/signals').forEach((signal) => {
});
});

const runComponents = async () => {
const runComponents = async (argv = process.argv.slice(2)) => {
const args = minimist(argv);
initializeNodeLogging({ argv, env: process.env, stdin: process.stdin, stdout: process.stdout });

if (args.help || args._[0] === 'help') {
await renderHelp();
return;
Expand All @@ -63,7 +64,7 @@ const runComponents = async () => {
}
delete options._; // remove the method name if any

const configurationPath = await resolveConfigurationPath();
const configurationPath = await resolveConfigurationPath(process.cwd());
const configuration = await readConfiguration(configurationPath);
validateConfiguration(configuration, configurationPath);

Expand Down
4 changes: 2 additions & 2 deletions src/state/LocalStateStorage.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ const path = require('path');
const fsp = require('fs').promises;

class LocalStateStorage extends BaseStateStorage {
constructor(stage) {
constructor(root, stage) {
super();
this.stateRoot = path.join(process.cwd(), '.serverless');
this.stateRoot = path.join(root, '.serverless');
this.stage = stage;
}

Expand Down
Loading