diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b3ee507c..680d71c0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,3 +59,61 @@ jobs: - name: Run tests run: npm run ci + # publish-beta: + # runs-on: ubuntu-latest + + # strategy: + # fail-fast: false + # matrix: + # package: + # - component-interface + # - component-manager + # - credential + # - diff + # - downloads + # - engine + # - ignore-walk + # - load-application + # - load-component + # - logger + # - orm + # - parse-spec + # - progress-bar + # - registry + # - utils + # - zip + + # steps: + # - uses: actions/checkout@v2 + # with: + # fetch-depth: 2 + # - name: Set up Node.js + # uses: actions/setup-node@v2 + # with: + # node-version: '18' + # registry-url: https://registry.npmjs.org/ + + # - name: Install pnpm + # run: npm install -g pnpm + + # - name: Install dependencies + # run: npm run install:all + + # - name: Build packages + # run: npm run build + + # - name: Publish package + # run: | + # PACKAGE_DIR=packages/${{ matrix.package }} + # PACKAGE_JSON="${PACKAGE_DIR}/package.json" + + # # 检查 package.json 是否被修改 + # if git diff HEAD^ HEAD --name-only | grep -q "${PACKAGE_JSON}"; then + # echo "Publishing ${{ matrix.package }}..." + # cd $PACKAGE_DIR + # pnpm publish --tag=beta + # else + # echo "No changes in ${{ matrix.package }}/package.json" + # fi + # env: + # NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} diff --git a/packages/component-interface/package.json b/packages/component-interface/package.json index b1df58d4..3cf1a772 100644 --- a/packages/component-interface/package.json +++ b/packages/component-interface/package.json @@ -1,6 +1,6 @@ { "name": "@serverless-devs/component-interface", - "version": "0.0.4", + "version": "0.0.5", "description": "request for serverless-devs", "main": "lib/index.js", "scripts": { diff --git a/packages/component-interface/src/index.ts b/packages/component-interface/src/index.ts index 4489cf7f..6b1ae9b2 100644 --- a/packages/component-interface/src/index.ts +++ b/packages/component-interface/src/index.ts @@ -21,4 +21,5 @@ export interface IInputs { args: string[]; cwd: string; outputs?: Record; + output?: Record; // 当前步骤输出 } diff --git a/packages/credential/package.json b/packages/credential/package.json index e96c6aa3..e8023eca 100644 --- a/packages/credential/package.json +++ b/packages/credential/package.json @@ -1,6 +1,6 @@ { "name": "@serverless-devs/credential", - "version": "0.0.7", + "version": "0.0.9-beta.1", "description": "credential for serverless-devs", "main": "lib/index.js", "scripts": { diff --git a/packages/credential/src/constant.ts b/packages/credential/src/constant.ts index 56be9fc3..affa24d5 100644 --- a/packages/credential/src/constant.ts +++ b/packages/credential/src/constant.ts @@ -1,6 +1,5 @@ import path from 'path'; import os from 'os'; -import * as setType from './actions/set/type'; const Crypto = require('crypto-js'); diff --git a/packages/engine/package.json b/packages/engine/package.json index 2d1919b9..0c25d087 100644 --- a/packages/engine/package.json +++ b/packages/engine/package.json @@ -1,6 +1,6 @@ { "name": "@serverless-devs/engine", - "version": "0.1.3", + "version": "0.1.4-beta.9", "description": "a engine lib for serverless-devs", "main": "lib/index.js", "scripts": { diff --git a/packages/engine/src/actions/index.ts b/packages/engine/src/actions/index.ts index e59b3e47..3cd9fc86 100644 --- a/packages/engine/src/actions/index.ts +++ b/packages/engine/src/actions/index.ts @@ -1,7 +1,7 @@ import { IAction, IActionLevel, IActionType, IAllowFailure, IComponentAction, IHookType, IPluginAction, IRunAction, getInputs } from '@serverless-devs/parse-spec'; import { isEmpty, filter, includes, set, get } from 'lodash'; import * as utils from '@serverless-devs/utils'; -import { DevsError, ETrackerType } from '@serverless-devs/utils'; +import { DevsError, ETrackerType, isDevsDebugMode } from '@serverless-devs/utils'; import fs from 'fs-extra'; import { spawn } from 'child_process'; import loadComponent from '@serverless-devs/load-component'; @@ -12,7 +12,7 @@ import { ILoggerInstance } from '@serverless-devs/logger'; import { EXIT_CODE } from '../constants'; import { IStepOptions } from '../types'; -const debug = require('@serverless-cd/debug')('serverless-devs:engine'); +const debug = isDevsDebugMode() ? require('@serverless-cd/debug')('serverless-devs:engine') : (i: any) => {}; interface IRecord { magic: Record; // 记录魔法变量 @@ -284,7 +284,7 @@ You can still use them now, but we suggest to modify them.`) const [componentName, command] = _; // Load the specified component. - const instance = await loadComponent(componentName, { logger: this.logger }); + const instance = await loadComponent(componentName, { logger: this.logger, cleanCache: true }); // Check if the specified command exists for the component. if (instance[command]) { diff --git a/packages/engine/src/constants/index.ts b/packages/engine/src/constants/index.ts index fae793f8..f3ef5289 100644 --- a/packages/engine/src/constants/index.ts +++ b/packages/engine/src/constants/index.ts @@ -8,3 +8,7 @@ export const EXIT_CODE = { // shell 执行错误 RUN: 101, }; + +export const INFO_EXP_PATTERN = /\$\{resources\.([^.{}]+)\.info(\.[^.{}]+)+}/g; +export const COMPONENT_EXP_PATTERN = /\$\{components\.([^.{}]+)\.output(\.[^.{}]+)+}/g; +export const OUTPUT_EXP_PATTERN = /\$\{resources\.([^.{}]+)\.output(\.[^.{}]+)+}/g; diff --git a/packages/engine/src/index.ts b/packages/engine/src/index.ts index 2834afaa..6754209c 100644 --- a/packages/engine/src/index.ts +++ b/packages/engine/src/index.ts @@ -1,5 +1,5 @@ import { createMachine, interpret } from 'xstate'; -import { isEmpty, get, each, map, isFunction, has, uniqueId, filter, omit, includes, set, isNil, isUndefined, keys, size } from 'lodash'; +import { isEmpty, get, each, map, isFunction, has, uniqueId, filter, omit, includes, set, isNil, isUndefined, keys, size, cloneDeep, find } from 'lodash'; import { IStepOptions, IRecord, IStatus, IEngineOptions, IContext, IEngineError, STEP_STATUS } from './types'; import { getProcessTime, getCredential, stringify, getAllowFailure } from './utils'; import ParseSpec, { getInputs, ISpec, IHookType, IStep as IParseStep, IActionLevel } from '@serverless-devs/parse-spec'; @@ -9,14 +9,14 @@ import Actions from './actions'; import Credential from '@serverless-devs/credential'; import loadComponent from '@serverless-devs/load-component'; import Logger, { ILoggerInstance } from '@serverless-devs/logger'; -import { DevsError, ETrackerType, emoji, getAbsolutePath, getRootHome, getUserAgent, traceid } from '@serverless-devs/utils'; -import { EXIT_CODE } from './constants'; +import { DevsError, ETrackerType, emoji, getAbsolutePath, getRootHome, getUserAgent, traceid, isDevsDebugMode } from '@serverless-devs/utils'; +import { EXIT_CODE, INFO_EXP_PATTERN, COMPONENT_EXP_PATTERN } from './constants'; import assert from 'assert'; import Ajv from 'ajv'; export * from './types'; export { verify, preview, init } from './utils'; -const debug = require('@serverless-cd/debug')('serverless-devs:engine'); +const debug = isDevsDebugMode() ? require('@serverless-cd/debug')('serverless-devs:engine') : (i: any) => {}; /** * Engine Class @@ -38,6 +38,7 @@ class Engine { private parseSpecInstance!: ParseSpec; private globalActionInstance!: Actions; // 全局的 action private actionInstance!: Actions; // 项目的 action + private info: Record = {}; // 存储全局变量 constructor(private options: IEngineOptions) { debug('engine start'); @@ -65,10 +66,11 @@ class Engine { each(this.options.env, (value, key) => { process.env[key] = value; }); - const { steps: _steps } = this.spec; + const { steps: _steps, allSteps } = this.spec; // 参数校验 await this.validate(); this.context.steps = await this.download(_steps); + this.context.allSteps = allSteps ? await this.download(allSteps) : []; } /** @@ -103,7 +105,7 @@ class Engine { this.context.credential = credential; // 处理 global-pre try { - this.globalActionInstance.setValue('magic', this.getFilterContext()); + this.globalActionInstance.setValue('magic', await this.getFilterContext()); this.globalActionInstance.setValue('command', command); await this.globalActionInstance.start(IHookType.PRE, { access, credential }); } catch (error) { @@ -117,6 +119,9 @@ class Engine { this.context.steps = map(this.context.steps, item => { return { ...item, stepCount: uniqueId(), status: STEP_STATUS.PENDING, done: false }; }); + this.context.allSteps = map(this.context.allSteps, item => { + return { ...item, stepCount: uniqueId(), status: STEP_STATUS.PENDING, done: false }; + }); const res: IContext = await new Promise(async resolve => { // Every states object has two fixed states, "init" and "final". const states: any = { @@ -329,21 +334,77 @@ class Engine { }); } + /** + * 20240624 New feature + * ${resources.xx.info.xx} read from `info` command's output. + * This function will check current step content to determine + * whether to execute `info` command of a certain resource. + * + * @param item - The current step being processed. + * @returns + */ + private async getInfo(item: IStepOptions | undefined) { + if (!item) return; + const { props } = item; + const compString = JSON.stringify(props); + const matches = compString.matchAll(INFO_EXP_PATTERN); + const resourceNames = new Set(); + for (const match of matches) { + if (match[1]) { + resourceNames.add(match[1]); + } + } + // support ${components.xx.output.xx} + const comMatches = compString.matchAll(COMPONENT_EXP_PATTERN); + for (const match of comMatches) { + if (match[1]) { + resourceNames.add(match[1]); + } + } + if (isEmpty(resourceNames)) return; + const resourcesItems: IStepOptions[] = map(Array.from(resourceNames), (name) => { + return this.context.allSteps.find((obj) => obj.projectName === name) as IStepOptions; + }); + await Promise.all(map(resourcesItems, async (item) => { + if (!item) return; + const projectInfo = get(this.info, item.projectName); + if (!projectInfo || isEmpty(projectInfo) || isNil(projectInfo)) { + this.logger.write(`${chalk.gray(`execute info command of [${item.projectName}]...`)}`); + const spec = cloneDeep(this.spec); + spec['command'] = 'info'; + const res: any = await this.doSrc(item, {}, spec); + set(this.info, item.projectName, res); + this.logger.write(`${chalk.gray(`[${item.projectName}] info command executed.`)}`); + } + })); + } /** * Generates a context data for the given step containing details like cwd, vars, and other steps' outputs and props. * @param item - The current step being processed. * @returns - The generated context data. */ - private getFilterContext(item?: IStepOptions) { + private async getFilterContext(item?: IStepOptions) { + await this.getInfo(item); const data = { cwd: path.dirname(this.spec.yaml.path), vars: this.spec.yaml.vars, resources: {}, + components: {}, __runtime: this.options.verify ? 'engine' : 'parse', __steps: this.context.steps, } as Record; - for (const obj of this.context.steps) { - data.resources[obj.projectName] = { output: obj.output || {}, props: obj.props || {} }; + for (const obj of this.context.allSteps) { + const stepItem = find(this.context.steps, (obj2) => obj2.projectName === obj.projectName); + data.resources[obj.projectName] = { + output: obj.output || get(stepItem, 'output') || {}, + props: obj.props || get(stepItem, 'props') || {}, + info: this.info[obj.projectName] || {}, + vars: obj.vars || get(stepItem, 'vars') || {}, + }; + // support ${components.xx.output.xx} + data.components[obj.projectName] = { + output: this.info[obj.projectName] || {}, + } } if (item) { data.credential = item.credential; @@ -353,6 +414,7 @@ class Engine { component: item.component, props: data.resources[item.projectName].props, output: data.resources[item.projectName].output, + vars: data.resources[item.projectName].vars, }; } return data; @@ -419,7 +481,7 @@ class Engine { // project success hook try { // 项目的output, 再次获取魔法变量 - this.actionInstance.setValue('magic', this.getFilterContext(item)); + this.actionInstance.setValue('magic', await this.getFilterContext(item)); const res = await this.actionInstance?.start(IHookType.SUCCESS, { ...this.record.componentProps, output: get(item, 'output', {}), @@ -488,7 +550,7 @@ class Engine { }); // Set values for the action instance. - this.actionInstance.setValue('magic', this.getFilterContext(item)); + this.actionInstance.setValue('magic', await this.getFilterContext(item)); this.actionInstance.setValue('step', item); this.actionInstance.setValue('command', command); @@ -559,7 +621,7 @@ class Engine { * @returns An object containing properties related to the project step. */ private async getProps(item: IStepOptions) { - const magic = this.getFilterContext(item); + const magic = await this.getFilterContext(item); debug(`magic context: ${JSON.stringify(magic)}`); const newInputs = getInputs(item.props, magic); const { projectName, command } = this.spec; @@ -595,9 +657,9 @@ class Engine { * @param data - Additional data which may contain plugin output. * @returns Result of the executed action, if applicable. */ - private async doSrc(item: IStepOptions, data: Record = {}) { + private async doSrc(item: IStepOptions, data: Record = {}, spec = this.spec) { // Extract command and projectName from the specification. - const { command = '', projectName, yaml } = this.spec; + const { command = '', projectName, yaml } = spec; // Retrieve properties for the given project step. const newInputs = await this.getProps(item); diff --git a/packages/engine/src/types.ts b/packages/engine/src/types.ts index cbe32678..62394b73 100644 --- a/packages/engine/src/types.ts +++ b/packages/engine/src/types.ts @@ -73,6 +73,7 @@ export interface IContext { error: IEngineError[]; // 记录step的错误信息 output: Record; // 记录step的输出 credential: Record; // 尝试获取到的密钥信息 + allSteps: IStepOptions[]; // 记录所有step } export type IEngineError = Error | AssertionError | DevsError; diff --git a/packages/load-application/package.json b/packages/load-application/package.json index a877defd..3cf90291 100644 --- a/packages/load-application/package.json +++ b/packages/load-application/package.json @@ -1,6 +1,6 @@ { "name": "@serverless-devs/load-application", - "version": "0.0.13", + "version": "0.0.14-beta.5", "description": "load application for serverless-devs", "main": "lib/index.js", "scripts": { @@ -18,12 +18,12 @@ }, "dependencies": { "@serverless-cd/debug": "^4.3.4", - "@serverless-devs/art-template": "^4.13.15", + "@serverless-devs/art-template": "^4.13.16-beta.12", "@serverless-devs/credential": "workspace:^", "@serverless-devs/downloads": "workspace:^", "@serverless-devs/utils": "workspace:^", "art-template": "^4.13.2", - "axios": "^1.4.0", + "axios": "^1.6.0", "chalk": "^4.1.2", "fs-extra": "^11.1.0", "inquirer": "^8.2.4", diff --git a/packages/load-application/src/index.ts b/packages/load-application/src/index.ts index 76fa34cd..8bc86a79 100644 --- a/packages/load-application/src/index.ts +++ b/packages/load-application/src/index.ts @@ -4,7 +4,8 @@ import assert from 'assert'; import { IOptions } from './types'; import { includes, get } from 'lodash'; import { REGISTRY } from './constant'; -const debug = require('@serverless-cd/debug')('serverless-devs:load-appliaction'); +import { isDevsDebugMode } from '@serverless-devs/utils'; +const debug = isDevsDebugMode() ? require('@serverless-cd/debug')('serverless-devs:load-application') : (i: any) => {}; export default async (template: string, options: IOptions = {}) => { debug(`load application, template: ${template}, options: ${JSON.stringify(options)}`); diff --git a/packages/load-application/src/v2.ts b/packages/load-application/src/v2.ts index 253b3375..535546fe 100644 --- a/packages/load-application/src/v2.ts +++ b/packages/load-application/src/v2.ts @@ -3,19 +3,18 @@ import fs from 'fs-extra'; import axios from 'axios'; import download from '@serverless-devs/downloads'; import artTemplate from 'art-template'; -import { getYamlContent, isCiCdEnvironment, getYamlPath } from '@serverless-devs/utils'; +import { getYamlContent, isCiCdEnvironment, getYamlPath, isDevsDebugMode } from '@serverless-devs/utils'; import { isEmpty, includes, split, get, has, set, sortBy, map, concat, keys, find, startsWith } from 'lodash'; import parse from './parse'; import { IProvider, IOptions } from './types'; import { CONFIGURE_LATER, DEFAULT_MAGIC_ACCESS, REGISTRY } from './constant'; -import { getInputs, getAllCredential, getDefaultValue } from './utils'; -import YAML from 'yaml'; +import { getAllCredential, getDefaultValue } from './utils'; import inquirer from 'inquirer'; import chalk from 'chalk'; import Credential from '@serverless-devs/credential'; -import { gray, RANDOM_PATTERN } from './constant'; +import { gray } from './constant'; import assert from 'assert'; -const debug = require('@serverless-cd/debug')('serverless-devs:load-appliaction'); +const debug = isDevsDebugMode() ? require('@serverless-cd/debug')('serverless-devs:load-application') : (i: any) => {}; class LoadApplication { private provider: `${IProvider}`; @@ -163,7 +162,6 @@ class LoadApplication { const hookPath = path.join(this.tempPath, 'hook'); if (!fs.existsSync(hookPath)) return; const { logger } = this.options; - const { parameters, access } = this.options; const hook = await require(hookPath); const data = { provider: this.provider, diff --git a/packages/load-application/src/v3.ts b/packages/load-application/src/v3.ts index 434eb9a9..35903555 100644 --- a/packages/load-application/src/v3.ts +++ b/packages/load-application/src/v3.ts @@ -3,7 +3,7 @@ import fs from 'fs-extra'; import download from '@serverless-devs/downloads'; import _artTemplate from 'art-template'; import _devsArtTemplate from '@serverless-devs/art-template'; -import { getYamlContent, registry, isCiCdEnvironment, getYamlPath } from '@serverless-devs/utils'; +import { getYamlContent, registry, isCiCdEnvironment, getYamlPath, isDevsDebugMode } from '@serverless-devs/utils'; import { isEmpty, includes, split, get, has, set, sortBy, map, concat, keys, startsWith, merge } from 'lodash'; import axios from 'axios'; import parse from './parse'; @@ -14,7 +14,7 @@ import inquirer from 'inquirer'; import chalk from 'chalk'; import Credential from '@serverless-devs/credential'; import { CONFIGURE_LATER, DEFAULT_MAGIC_ACCESS, GITHUB_REGISTRY, gray } from './constant'; -const debug = require('@serverless-cd/debug')('serverless-devs:load-appliaction'); +const debug = isDevsDebugMode() ? require('@serverless-cd/debug')('serverless-devs:load-appliaction') : (i: any) => {}; class LoadApplication { /** diff --git a/packages/load-component/package.json b/packages/load-component/package.json index d3206769..a1609bb5 100644 --- a/packages/load-component/package.json +++ b/packages/load-component/package.json @@ -1,6 +1,6 @@ { "name": "@serverless-devs/load-component", - "version": "0.0.7", + "version": "0.0.8-beta.1", "description": "request for serverless-devs", "main": "lib/index.js", "scripts": { diff --git a/packages/load-component/src/index.ts b/packages/load-component/src/index.ts index cef33dbe..44d4edf4 100644 --- a/packages/load-component/src/index.ts +++ b/packages/load-component/src/index.ts @@ -2,19 +2,19 @@ import fs from 'fs-extra'; import { buildComponentInstance, getProvider, getZipballUrl, getComponentCachePath } from './utils'; import download from '@serverless-devs/downloads'; import { get } from 'lodash'; -import { readJson, registry, getLockFile } from '@serverless-devs/utils'; +import { readJson, registry, getLockFile, isDevsDebugMode } from '@serverless-devs/utils'; import assert from 'assert'; import semver from 'semver'; -const debug = require('@serverless-cd/debug')('serverless-devs:load-component'); +const debug = isDevsDebugMode() ? require('@serverless-cd/debug')('serverless-devs:load-component') : (i: any) => {}; export class Component { - constructor(private name: string, private params: Record = {}) { + constructor(private name: string, private params: Record = {}, private cleanCache: boolean = false) { assert(name, 'component name is required'); } async run() { // 本地路径 if (fs.existsSync(this.name)) { - return await buildComponentInstance(this.name, this.params); + return await buildComponentInstance(this.name, this.params, this.cleanCache); } return await this.getDevComponent(); } @@ -50,7 +50,7 @@ export class Component { debug(`componentCachePath: ${componentCachePath}`); const lockPath = getLockFile(componentCachePath); if (fs.existsSync(lockPath)) { - return await buildComponentInstance(componentCachePath, this.params); + return await buildComponentInstance(componentCachePath, this.params, this.cleanCache); } const { zipballUrl = componentName, version = componentVersion } = await getZipballUrl(componentName, componentVersion); debug(`zipballUrl: ${zipballUrl}`); @@ -62,12 +62,13 @@ export class Component { headers: registry.getSignHeaders(), }); fs.writeFileSync(lockPath, JSON.stringify({ version, lastUpdateCheck: Date.now() })); - return await buildComponentInstance(componentCachePath, this.params); + return await buildComponentInstance(componentCachePath, this.params, this.cleanCache); } } const loadComponent = async (name: string, params?: Record) => { - return await new Component(name, params).run(); + const { cleanCache, ...rest } = params || { cleanCache: false }; + return await new Component(name, rest, cleanCache).run(); }; export default loadComponent; diff --git a/packages/load-component/src/utils/index.ts b/packages/load-component/src/utils/index.ts index 84a3fc93..ccfaa397 100644 --- a/packages/load-component/src/utils/index.ts +++ b/packages/load-component/src/utils/index.ts @@ -2,10 +2,10 @@ import path from 'path'; import fs from 'fs-extra'; import { get, includes, find, split, filter, isEmpty } from 'lodash'; import axios from 'axios'; -import { getRootHome, getYamlContent, registry } from '@serverless-devs/utils'; +import { getRootHome, getYamlContent, registry, isDevsDebugMode } from '@serverless-devs/utils'; import { BASE_URL } from '../constant'; import assert from 'assert'; -const debug = require('@serverless-cd/debug')('serverless-devs:load-component'); +const debug = isDevsDebugMode() ? require('@serverless-cd/debug')('serverless-devs:load-component') : (i: any) => {}; const getUrlWithLatest = (name: string) => `${BASE_URL}/packages/${name}/release/latest`; const getUrlWithVersion = (name: string, versionId: string) => `${BASE_URL}/packages/${name}/release/tags/${versionId}`; @@ -40,12 +40,14 @@ const getEntryFile = async (componentPath: string) => { ); }; -export const buildComponentInstance = async (componentPath: string, params?: any) => { +export const buildComponentInstance = async (componentPath: string, params?: any, cleanCache: boolean = false) => { const requirePath = await getEntryFile(componentPath); // bug: `- component: fc invoke` timeout. Delete require cache - try { - delete require.cache[requirePath]; - } catch {} + if (cleanCache && require.cache[requirePath]) { + try { + delete require.cache[requirePath]; + } catch {} + } const baseChildComponent = await require(requirePath); const ChildComponent = baseChildComponent.default || baseChildComponent; diff --git a/packages/parse-spec/package.json b/packages/parse-spec/package.json index d5b21f03..3ec331c5 100644 --- a/packages/parse-spec/package.json +++ b/packages/parse-spec/package.json @@ -1,6 +1,6 @@ { "name": "@serverless-devs/parse-spec", - "version": "0.0.27", + "version": "0.0.28-beta.8", "description": "a parse yaml spec lib for serverless-devs", "main": "lib/index.js", "scripts": { @@ -22,7 +22,7 @@ }, "dependencies": { "@serverless-cd/debug": "^4.3.4", - "@serverless-devs/art-template": "^4.13.15", + "@serverless-devs/art-template": "^4.13.16-beta.12", "@serverless-devs/credential": "workspace:^", "@serverless-devs/utils": "workspace:^", "chalk": "^4.1.2", @@ -30,7 +30,8 @@ "dotenv-expand": "^10.0.0", "extend2": "^1.0.1", "fs-extra": "^11.1.0", - "lodash": "^4.17.21" + "lodash": "^4.17.21", + "js-yaml": "^4.1.0" }, "devDependencies": { "@types/lodash": "^4.14.195" diff --git a/packages/parse-spec/src/contants/index.ts b/packages/parse-spec/src/contants/index.ts index 672946e8..bf49cdb7 100644 --- a/packages/parse-spec/src/contants/index.ts +++ b/packages/parse-spec/src/contants/index.ts @@ -3,6 +3,8 @@ import path from 'path'; export const REGX = /\${([\w\W]*?)}/; export const REGXG = /\${([\w\W]*?)}/g; +export const INFO_EXP_PATTERN = /\$\{resources\.([^.{}]+)\.info(\.[^.{}]+)+}/g +export const OUTPUT_EXP_PATTERN = /\$\{resources\.([^.{}]+)\.output(\.[^.{}]+)+}/g; export const ENVIRONMENT_KEY = 'env'; export const ENVIRONMENT_FILE_NAME = 'env.yaml'; export const ENVIRONMENT_FILE_PATH = path.join(utils.getRootHome(), 'cache', 'default-env.json'); diff --git a/packages/parse-spec/src/index.ts b/packages/parse-spec/src/index.ts index d856d07c..4cc7aec4 100644 --- a/packages/parse-spec/src/index.ts +++ b/packages/parse-spec/src/index.ts @@ -1,22 +1,22 @@ export { default as getInputs } from './get-inputs'; export * from './types'; export * from './contants'; +export { ParseSpecForContent } from './utils'; import * as utils from '@serverless-devs/utils'; import fs from 'fs-extra'; import path from 'path'; import dotenv from 'dotenv'; import { expand } from 'dotenv-expand'; import { getDefaultYamlPath, isExtendMode } from './utils'; -const compile = require('@serverless-devs/art-template/lib/devs-compile'); import Order from './order'; import ParseContent from './parse-content'; import { each, filter, find, get, has, includes, isArray, isEmpty, isString, keys, map, set, split } from 'lodash'; import { ISpec, IYaml, IActionType, IActionLevel, IStep, IRecord } from './types'; import { ENVIRONMENT_FILE_NAME, ENVIRONMENT_FILE_PATH, ENVIRONMENT_KEY, REGX } from './contants'; import assert from 'assert'; -import { DevsError, ETrackerType } from '@serverless-devs/utils'; +import { DevsError, ETrackerType, isDevsDebugMode } from '@serverless-devs/utils'; const extend2 = require('extend2'); -const debug = require('@serverless-cd/debug')('serverless-devs:parse-spec'); +const debug = isDevsDebugMode() ? require('@serverless-cd/debug')('serverless-devs:parse-spec') : (i: any) => {}; interface IOptions { argv?: string[]; @@ -202,10 +202,10 @@ class ParseSpec { // 再次解析参数,比如projectNames this.parseArgv(); if (!this.yaml.use3x) return this.v1(); - const { steps, content, originSteps } = await new ParseContent(this.yaml.content, this.getParsedContentOptions(this.yaml.path)).start(); + const { steps, content, originSteps, allSteps } = await new ParseContent(this.yaml.content, this.getParsedContentOptions(this.yaml.path)).start(); const services = get(this.yaml.content, 'services', {}); if (isEmpty(steps) && !isEmpty(services)) { - this.options.logger.tips('Check https://manual.serverless-devs.com/user-guide/spec/ for more details. Use the \'s cli fc3 s2tos3\' command for automatic YAML transformation.'); + this.options.logger.tips('Check https://docs.serverless-devs.com/user-guide/spec/ for more details. Use the \'s cli fc3 s2tos3\' command for automatic YAML transformation.'); throw new DevsError(`Keyword 'services' has been replaced by 'resources' in 3.0.0 YAML.`, { trackerType: ETrackerType.parseException, }); @@ -221,6 +221,7 @@ class ParseSpec { const result = { steps: this.record.projectName ? steps : this.doFlow(steps, originSteps), yaml: this.yaml, + allSteps: allSteps, ...this.record, }; debug(`parse result: ${JSON.stringify(result)}`); @@ -300,6 +301,7 @@ class ParseSpec { private matchFlow(flow: string) { const useMagic = REGX.test(flow); if (useMagic) { + const compile = require('@serverless-devs/art-template/lib/devs-compile'); return compile(flow, { command: this.record.command }); } return flow === this.record.command; @@ -363,6 +365,7 @@ class ParseSpec { private matchAction(action: string) { const useMagic = REGX.test(action); if (useMagic) { + const compile = require('@serverless-devs/art-template/lib/devs-compile'); const newAction = compile(action, { command: this.record.command }); const [type, command] = split(newAction, '-'); return { diff --git a/packages/parse-spec/src/order.ts b/packages/parse-spec/src/order.ts index e0a78842..d4d8c942 100644 --- a/packages/parse-spec/src/order.ts +++ b/packages/parse-spec/src/order.ts @@ -1,7 +1,8 @@ import { includes, map, split, set, sortBy, isEmpty, get, cloneDeep, unset, isObject } from 'lodash'; import { REGXG } from './contants'; import { IStep } from './types'; -const debug = require('@serverless-cd/debug')('serverless-devs:parse-spec'); +import { isDevsDebugMode } from '@serverless-devs/utils'; +const debug = isDevsDebugMode() ? require('@serverless-cd/debug')('serverless-devs:parse-spec') : (i: any) => {}; class Order { private useOrder = false; // 是否使用分析出来的order @@ -59,8 +60,11 @@ class Order { if (matchResult) { for (const item of matchResult) { const newItem = item.replace(REGXG, '$1'); + const prefix = split(newItem, '.')[0]; const projectName = split(newItem, '.')[1]; - if (includes(projectNameList, projectName)) { + const output = split(newItem, '.')[2]; + // support ${components.xx.output} + if (prefix === 'resources' && includes(projectNameList, projectName) && output === 'output') { this.useOrder = true; set(dependencies, topKey, { ...dependencies[topKey], [projectName]: 1 }); } diff --git a/packages/parse-spec/src/parse-content.ts b/packages/parse-spec/src/parse-content.ts index 31cf2dca..636afcbd 100644 --- a/packages/parse-spec/src/parse-content.ts +++ b/packages/parse-spec/src/parse-content.ts @@ -2,10 +2,10 @@ import path from 'path'; import getInputs from './get-inputs'; import { IStep } from './types'; import { getCredential } from './utils'; -import { each, get, omit, set, pickBy, cloneDeep, isEmpty } from 'lodash'; -const compile = require('@serverless-devs/art-template/lib/devs-compile'); +import { isDevsDebugMode } from '@serverless-devs/utils'; +import { each, get, omit, set, pickBy, cloneDeep, isEmpty, find } from 'lodash'; const extend2 = require('extend2'); -const debug = require('@serverless-cd/debug')('serverless-devs:parse-spec'); +const debug = isDevsDebugMode() ? require('@serverless-cd/debug')('serverless-devs:parse-spec') : (i: any) => {}; interface IOptions { logger?: any; @@ -40,20 +40,26 @@ class ParseContent { } private getMagicProps(item: Partial) { const resources = get(this.content, 'resources', {}); + // support ${components.xxx.output} + const components = get(this.content, 'components', {}); const temp = {} as Record; + const tempCom = {} as Record; each(resources, (item, key) => { - set(temp, `${key}.props`, item.props || {}); + set(temp, `${key}`, item || {}); + }); + each(components, (item, key) => { + set(tempCom, `${key}.props`, item.props || {}); }); const name = item.projectName as string; const res = { ...this.getCommonMagic(), resources: temp, + components: tempCom, that: { name, access: item.access, component: item.component, - props: temp[name].props, - output: temp[name].output, + ...temp[name] }, }; // parse props magic @@ -70,12 +76,12 @@ class ParseContent { ...this.content, ...getInputs(rest, this.getCommonMagic()), }; - const steps = []; - const originSteps = []; - // projectName 存在,说明指定了项目 - const temp = this.options.projectName ? { [this.options.projectName]: resources[this.options.projectName] } : resources; - for (const project in temp) { + // support resources.info, all steps are needed + const allSteps = []; + const allOriginSteps = []; + for (const project in resources) { const element = resources[project]; + const compile = require('@serverless-devs/art-template/lib/devs-compile'); const component = compile(get(element, 'component'), this.getCommonMagic()); let template = get(this.content.template, get(element, 'extend.name'), {}); template = getInputs(omit(template, get(element, 'extend.ignore', [])), this.getCommonMagic()); @@ -106,14 +112,14 @@ class ParseContent { [project]: real, }, }; - steps.push({ + allSteps.push({ ...real, projectName: project, component, access, credential: this.credential, }); - originSteps.push({ + allOriginSteps.push({ ...element, projectName: project, component, @@ -121,7 +127,10 @@ class ParseContent { credential: this.credential, }); } - return { steps, content: this.content, originSteps }; + // projectName 存在,说明指定了项目 + const steps = this.options.projectName ? [find(allSteps, (item) => item.projectName === this.options.projectName)] : allSteps; + const originSteps = this.options.projectName ? [find(allOriginSteps, (item) => item.projectName === this.options.projectName)] : allOriginSteps; + return { steps, content: this.content, originSteps, allSteps }; } private getAccess() { // 全局的 -a > env.yaml 的 access > s.yaml 的 access > default diff --git a/packages/parse-spec/src/types.ts b/packages/parse-spec/src/types.ts index eea6f925..60f0d5ba 100644 --- a/packages/parse-spec/src/types.ts +++ b/packages/parse-spec/src/types.ts @@ -2,6 +2,7 @@ export interface IStep { projectName: string; component: string; props: Record; + vars?: Record; actions?: Record; order: number; access: string | undefined; @@ -81,6 +82,7 @@ export interface IYaml { export type ISpec = IRecord & { steps: IStep[]; yaml: IYaml; + allSteps?: IStep[]; }; export interface IRecord { projectName?: string; diff --git a/packages/parse-spec/src/utils/index.ts b/packages/parse-spec/src/utils/index.ts index ad88b701..fd6fd383 100644 --- a/packages/parse-spec/src/utils/index.ts +++ b/packages/parse-spec/src/utils/index.ts @@ -3,6 +3,7 @@ import * as utils from '@serverless-devs/utils'; import Credential from '@serverless-devs/credential'; import { get } from 'lodash'; import { ETrackerType } from '@serverless-devs/utils'; +import jsYaml from 'js-yaml'; export function getDefaultYamlPath() { const spath = utils.getYamlPath('s'); @@ -29,3 +30,362 @@ export async function getCredential(access: string | undefined, logger: any) { return {}; } } + +/** + * Parse-spec class for the use of apis + * @author neil.zxy + * @date 2024-07-23 17:44 + */ +import fs from 'fs-extra'; +import dotenv from 'dotenv'; +import { expand } from 'dotenv-expand'; +import Order from '../order'; +import ParseContent from '../parse-content'; +import { each, filter, find, has, includes, isArray, isEmpty, isString, keys, map, set, split } from 'lodash'; +import { ISpec, IYaml, IActionType, IActionLevel, IStep, IRecord } from '../types'; +import { ENVIRONMENT_FILE_NAME, ENVIRONMENT_FILE_PATH, ENVIRONMENT_KEY, REGX } from '../contants'; +import assert from 'assert'; +import { DevsError } from '@serverless-devs/utils'; +const extend2 = require('extend2'); + +interface IOptions { + argv?: string[]; + logger?: any; +} + +export class ParseSpecForContent { + private yaml = {} as IYaml; + private record = {} as IRecord; + constructor(yamlContent: string = '', private options: IOptions = {}) { + this.options.argv = this.options.argv || process.argv.slice(2); + this.options.logger = this.options.logger || console; + this.yaml.path = ''; + this.yaml.content = jsYaml.load(yamlContent) || {}; + } + private async doYamlinit() { + await this.doExtend(); + this.doEnvironment(); + this.yaml.access = get(this.yaml.content, 'access'); + const projectKey = this.yaml.use3x ? 'resources' : 'services'; + const projects = get(this.yaml.content, projectKey, {}); + this.yaml.projectNames = keys(get(this.yaml.content, projectKey, {})); + this.yaml.vars = get(this.yaml.content, 'vars', {}); + this.yaml.flow = get(this.yaml.content, 'flow', {}); + this.yaml.useFlow = false; + this.yaml.appName = get(this.yaml.content, 'name'); + + // 兼容2.0: 加入项目的.env环境变量 + for (const i of this.yaml.projectNames) { + const code = get(projects, `${i}.props.code`); + if (code && isString(code)) { + const codePath = utils.getAbsolutePath(get(projects, `${i}.props.code`, '')); + expand(dotenv.config({ path: path.join(codePath, '.env') })); + } + } + + expand(dotenv.config({ path: path.join(path.dirname(this.yaml.path), '.env') })); + } + private async doExtend() { + // this.yaml = { path: '' } + // this.yaml.content = utils.getYamlContent(this.yaml.path); + this.yaml.extend = get(this.yaml.content, 'extend'); + this.yaml.useExtend = isExtendMode(this.yaml.extend, path.dirname(this.yaml.path)); + if (this.yaml.useExtend) { + // if useExtend, 则直接解析前后内容 + const extendPath = utils.getAbsolutePath(this.yaml.extend, path.dirname(this.yaml.path)); + expand(dotenv.config({ path: path.join(extendPath, '.env') })); + const extendContent = utils.getYamlContent(extendPath); + this.yaml.use3x = String(get(this.yaml.content, 'edition', get(extendContent, 'edition'))) === '3.0.0'; + // 1.x 不做extend动作 + if (!this.yaml.use3x) return; + const { resources: extendResource, ...extendRest } = extendContent; + const { resources: currentResource, ...currentRest } = this.yaml.content; + const tempRest = extend2(true, {}, extendRest, currentRest); + const base = await new ParseContent({ ...extendContent, ...tempRest }, this.getParsedContentOptions(extendPath)).start(); + const current = await new ParseContent({ ...this.yaml.content, ...tempRest }, this.getParsedContentOptions(this.yaml.path)).start(); + this.yaml.content = extend2(true, {}, get(base, 'content'), get(current, 'content')); + return; + } + this.yaml.use3x = String(get(this.yaml.content, 'edition')) === '3.0.0'; + } + /** + * # 指定--env时: + - s.yaml使用extend,则报错 + - 如果s.yaml声明了env yaml,则使用指定的env.yaml, 否则使用默认的env.yaml + - 如果env.yaml不存在,则报错 + - 指定的env找不到,则报错 + - s deploy --env test + + # 不指定--env时 + ## s.yaml使用extend + - s.yaml声明了env yaml, 则报错,没声明env yaml,则不使用多环境 + - s deploy + ## s.yaml未使用extend + - s.yaml声明了env yaml,如果env.yaml不存在,则报错 + - 使用指定env.yaml的默认环境,如果没有指定默认环境,则报错 + - 如果指定的默认环境找不到,则报错。 + - s deploy + */ + private doEnvironment() { + if (!this.yaml.use3x) return; + const envInfo = this.record.env ? this.doEnvWithSpecify() : this.doEnvWithNotSpecify(); + // 不使用多环境, 则直接返回 + if (isEmpty(envInfo)) return; + const { project, environment } = envInfo as { project: string; environment: Record }; + set(environment, 'overlays.resources.region', get(environment, 'region')); + set(environment, '__project', project); + this.yaml.environment = environment; + } + // not specify --env + private doEnvWithNotSpecify() { + if (this.yaml.useExtend) { + if (has(this.yaml.content, ENVIRONMENT_KEY)) { + throw new DevsError('Environment and extend is conflict', { + trackerType: ETrackerType.parseException, + }); + } + } + // default-env.json is not exist + if (!fs.existsSync(ENVIRONMENT_FILE_PATH)) { + return {}; + } + // 若存在环境变量,默认项目为devsProject + const devsProject = process.env.ALIYUN_DEVS_REMOTE_PROJECT_NAME; + const project = devsProject ? devsProject : get(this.yaml.content, 'name'); + const defaultEnvContent = require(ENVIRONMENT_FILE_PATH); + const defaultEnv = get(find(defaultEnvContent, { project: project }), 'default'); + // project is not found in default-env.json + if (isEmpty(defaultEnv)) { + return {}; + } + const envPath: string = utils.getAbsolutePath(get(this.yaml.content, ENVIRONMENT_KEY) || 'env.yaml', path.dirname(this.yaml.path)); + const envYamlContent = utils.getYamlContent(envPath); + if (isEmpty(envYamlContent)) { + this.options.logger.warnOnce(`Environment file [${envPath}] is not found, run without environment.`); + return {}; + } + const { environments } = envYamlContent; + const environment = find(environments, item => item.name === defaultEnv); + // default env is not found in env.yaml + if (isEmpty(environment)) { + this.options.logger.warnOnce(`Default env [${defaultEnv}] is not found, run without environment.`); + return {}; + } + return { project, environment }; + } + // specify --env + private doEnvWithSpecify() { + // env and extend is conflict + if (this.yaml.useExtend) { + // TODO: @封崇 + throw new DevsError('Environment and extend is conflict', { + trackerType: ETrackerType.parseException, + }); + } + const envPath: string = utils.getAbsolutePath(get(this.yaml.content, ENVIRONMENT_KEY, ENVIRONMENT_FILE_NAME), path.dirname(this.yaml.path)); + const envYamlContent = utils.getYamlContent(envPath); + // env file is not exist + if (isEmpty(envYamlContent)) { + this.options.logger.warnOnce(`Environment file [${envPath}] is not found, run without environment.`); + return {}; + } + const { environments } = envYamlContent; + // 若存在环境变量,默认项目为devsProject + const devsProject = process.env.ALIYUN_DEVS_REMOTE_PROJECT_NAME; + const project = devsProject ? devsProject : get(this.yaml.content, 'name'); + const environment = find(environments, item => item.name === this.record.env); + // env name is not found + if (isEmpty(environment)) { + this.options.logger.warnOnce(`Env [${this.record.env}] was not found, run without environment.`); + return {}; + } + return { project, environment }; + } + private getParsedContentOptions(basePath: string) { + return { + logger: this.options.logger, + basePath, + projectName: this.record.projectName, + access: this.record.access, + environment: this.yaml.environment, + }; + } + async start(): Promise { + // 第一次尝试解析参数,比如全局access给 extend 用 + // 将命令行参数更新给 this.record + this.parseArgv(); + // 处理继承、多环境后, 将 s.yaml 文件信息传入 this.yaml + await this.doYamlinit(); + // 再次解析参数,比如projectNames + this.parseArgv(); + if (!this.yaml.use3x) return this.v1(); + const { steps, content, originSteps, allSteps } = await new ParseContent(this.yaml.content, this.getParsedContentOptions(this.yaml.path)).start(); + const services = get(this.yaml.content, 'services', {}); + if (isEmpty(steps) && !isEmpty(services)) { + this.options.logger.tips('Check https://docs.serverless-devs.com/user-guide/spec/ for more details. Use the \'s cli fc3 s2tos3\' command for automatic YAML transformation.'); + throw new DevsError(`Keyword 'services' has been replaced by 'resources' in 3.0.0 YAML.`, { + trackerType: ETrackerType.parseException, + }); + } + // steps 存放每个FC组件/函数的 yaml 配置 ([content.resource] => steps) + // content 为 yaml 已解析的整体完整信息 + // originSteps 为 steps 的未解析版 + // 获取到真实值后,重新赋值 + this.yaml.content = content; + this.yaml.vars = get(this.yaml.content, 'vars', {}); + const actions = get(this.yaml.content, 'actions', {}); + this.yaml.actions = this.parseActions(actions); + const result = { + steps: this.record.projectName ? steps : this.doFlow(steps, originSteps), + yaml: this.yaml, + allSteps: allSteps, + ...this.record, + }; + return result; + } + // 简单兼容v1版本,不需要做魔法变量解析 + private v1(): ISpec { + const steps = []; + const services = get(this.yaml.content, 'services', {}); + for (const project in services) { + const element = services[project]; + steps.push({ + ...element, + projectName: project, + }); + } + return { + steps: this.record.projectName ? filter(steps, item => item.projectName === this.record.projectName) : steps, + yaml: this.yaml, + ...this.record, + }; + } + private parseArgv() { + const argv = utils.parseArgv(this.options.argv as string[]); + const { _ } = argv; + this.record.access = get(argv, 'access'); + this.record.version = get(argv, 'version'); + this.record.help = get(argv, 'help'); + this.record.output = get(argv, 'output'); + this.record.skipActions = get(argv, 'skip-actions'); + this.record.debug = get(argv, 'debug'); + this.record.env = get(argv, 'env'); + if (includes(this.yaml.projectNames, _[0])) { + this.record.projectName = _[0]; + this.record.command = _[1]; + return; + } + this.record.command = _[0]; + } + private doFlow(steps: IStep[], originSteps: IStep[]) { + const newSteps: IStep[] = []; + const flowObj = find(this.yaml.flow, (item, key) => this.matchFlow(key)); + const orderInstance = new Order(originSteps).start(); + const { steps: orderSteps, dependencies, useOrder } = orderInstance.sort(steps); + if (!flowObj) return orderSteps; + const projectOrder = {} as Record; + const fn = (projects: string[] = [], index: number) => { + assert(isArray(projects), `flow ${this.record.command} data format is invalid.`); + for (const project of projects) { + const step = find(steps, item => item.projectName === project); + assert(step, `Resource ${project} is not found. Please check the content of flow.`); + newSteps.push({ ...step, flowId: index }); + projectOrder[step.projectName] = index; + } + }; + each(flowObj, fn); + this.yaml.useFlow = true; + // 指定flow后,如果存在依赖关系,校验是否可以正常执行 + if (useOrder) { + for (const p1 in dependencies) { + const ele = dependencies[p1]; + for (const p2 in ele) { + if (projectOrder[p2] >= projectOrder[p1]) { + throw new Error(`flow is invalid, ${p2} must be executed before ${p1}`); + } + } + } + } + return newSteps; + } + private matchFlow(flow: string) { + const useMagic = REGX.test(flow); + if (useMagic) { + const compile = require('@serverless-devs/art-template/lib/devs-compile'); + return compile(flow, { command: this.record.command }); + } + return flow === this.record.command; + } + parseActions(actions: Record = {}, level: string = IActionLevel.GLOBAL) { + const actionList = []; + for (const action in actions) { + const element = actions[action]; + if (!isArray(element)) { + throw new DevsError(`${level} action ${action} is invalid, it must be array`, { + trackerType: ETrackerType.parseException, + }); + } + const actionInfo = this.matchAction(action); + if (actionInfo.validate) { + actionList.push( + ...map(element, item => { + if (item[IActionType.RUN]) { + const { run, ...rest } = item; + return { + ...rest, + value: run, + path: utils.getAbsolutePath(get(item, 'path', './'), path.dirname(this.yaml.path)), + actionType: IActionType.RUN, + hookType: actionInfo.type, + level, + projectName: this.record.projectName, + }; + } + if (item[IActionType.PLUGIN]) { + const { plugin, ...rest } = item; + const value = utils.getAbsolutePath(plugin, path.dirname(this.yaml.path)); + return { + ...rest, + value: fs.existsSync(value) ? value : plugin, + actionType: IActionType.PLUGIN, + hookType: actionInfo.type, + level, + projectName: this.record.projectName, + }; + } + if (item[IActionType.COMPONENT]) { + const { component, ...rest } = item; + return { + ...rest, + value: component, + actionType: IActionType.COMPONENT, + hookType: actionInfo.type, + level, + projectName: this.record.projectName, + }; + } + }), + ); + } + } + return actionList; + } + private matchAction(action: string) { + const useMagic = REGX.test(action); + if (useMagic) { + const compile = require('@serverless-devs/art-template/lib/devs-compile'); + const newAction = compile(action, { command: this.record.command }); + const [type, command] = split(newAction, '-'); + return { + validate: command === 'true', + type, + }; + } + const [type, command] = split(action, '-'); + return { + validate: command === this.record.command, + type, + }; + } +} + diff --git a/packages/utils/package.json b/packages/utils/package.json index 0106ec0a..894a31f0 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@serverless-devs/utils", - "version": "0.0.15", + "version": "0.0.16-beta.1", "description": "utils for serverless-devs", "main": "lib/index.js", "scripts": { diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 5be0289f..a3c50e44 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -18,3 +18,4 @@ export { default as readJson } from './read-json'; export { default as emoji } from './emoji'; export const getLockFile = (basePath: string) => path.join(basePath, '.s.lock'); export { default as getUserAgent } from './get-user-agent'; +export { default as isDevsDebugMode } from './is-devs-debug-mode'; \ No newline at end of file diff --git a/packages/utils/src/is-devs-debug-mode.ts b/packages/utils/src/is-devs-debug-mode.ts new file mode 100644 index 00000000..9e5e15f9 --- /dev/null +++ b/packages/utils/src/is-devs-debug-mode.ts @@ -0,0 +1,16 @@ +const packagesList = [ + 'serverless-devs:engine', + 'serverless-devs:parse-spec', + 'serverless-devs:load-component', + 'serverless-devs:load-appliation', +]; + +const isDevsDebugMode = (): boolean => { + if (process.env.DEBUG && packagesList.includes(process.env.DEBUG)) { + return true; + } + return false; +}; + +export default isDevsDebugMode; + \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d3b4dc9e..06404ada 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -196,8 +196,8 @@ importers: specifier: ^4.3.4 version: 4.3.4 '@serverless-devs/art-template': - specifier: ^4.13.15 - version: 4.13.15 + specifier: ^4.13.16-beta.12 + version: 4.13.16-beta.12 '@serverless-devs/credential': specifier: workspace:^ version: link:../credential @@ -211,8 +211,8 @@ importers: specifier: ^4.13.2 version: 4.13.2 axios: - specifier: ^1.4.0 - version: 1.4.0 + specifier: ^1.6.0 + version: 1.7.2 chalk: specifier: ^4.1.2 version: 4.1.2 @@ -249,7 +249,7 @@ importers: version: link:../utils axios: specifier: ^1.4.0 - version: 1.4.0 + version: 1.7.2 fs-extra: specifier: ^11.1.0 version: 11.1.1 @@ -313,8 +313,8 @@ importers: specifier: ^4.3.4 version: 4.3.4 '@serverless-devs/art-template': - specifier: ^4.13.15 - version: 4.13.15 + specifier: ^4.13.16-beta.12 + version: 4.13.16-beta.12 '@serverless-devs/credential': specifier: workspace:^ version: link:../credential @@ -336,6 +336,9 @@ importers: fs-extra: specifier: ^11.1.0 version: 11.1.1 + js-yaml: + specifier: ^4.1.0 + version: 4.1.0 lodash: specifier: ^4.17.21 version: 4.17.21 @@ -1119,8 +1122,8 @@ packages: - supports-color dev: false - /@serverless-devs/art-template@4.13.15: - resolution: {integrity: sha512-vgu6L7wzEc8Ju39kPkNAS77UK7aU9q412Hvh8mfVpJ/j7X46jKRe8DR+7NCmJ8rYGwKbhNb0UwmsRfpdmBHS6w==} + /@serverless-devs/art-template@4.13.16-beta.12: + resolution: {integrity: sha512-hu5Kr/PakpeAMgrvF6do5k6elAP3qbNa6HEJBxTFuRIdxc4HtD6vzbWoje4jYGKAvuSDRrvmxMfpj2YL5TC//w==} engines: {node: '>= 1.0.0'} dependencies: '@serverless-devs/utils': 0.0.14 @@ -1500,10 +1503,10 @@ packages: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} dev: false - /axios@1.4.0: - resolution: {integrity: sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==} + /axios@1.7.2: + resolution: {integrity: sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==} dependencies: - follow-redirects: 1.15.2 + follow-redirects: 1.15.6 form-data: 4.0.0 proxy-from-env: 1.1.0 transitivePeerDependencies: @@ -2315,8 +2318,8 @@ packages: resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} dev: false - /follow-redirects@1.15.2: - resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} + /follow-redirects@1.15.6: + resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} engines: {node: '>=4.0'} peerDependencies: debug: '*'