esa-cli 1.0.3 → 1.0.4-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,7 +15,7 @@ ESA CLI is a command-line tool for building with Alibaba Cloud ESA Functions and
15
15
 
16
16
  ### Prerequisites
17
17
 
18
- - Node.js: 20.x or higher (supports 20.x, 22.x)
18
+ - Node.js: 18.x or higher (supports 18.x, 20.x, 22.x)
19
19
  - OS: macOS (x86, Apple Silicon), Linux
20
20
  - Recommended to use a Node version manager like Volta or nvm to avoid permission issues and easily switch Node.js versions.
21
21
 
@@ -13,6 +13,7 @@ import chalk from 'chalk';
13
13
  import t from '../../i18n/index.js';
14
14
  import logger from '../../libs/logger.js';
15
15
  import promptParameter from '../../utils/prompt.js';
16
+ import { checkAndCleanupVersions } from '../utils.js';
16
17
  import { validateAndInitializeProject, generateCodeVersion } from '../common/utils.js';
17
18
  const commit = {
18
19
  command: 'commit [entry]',
@@ -44,6 +45,10 @@ const commit = {
44
45
  describe: 'Bundle with esbuild (use --no-bundle to skip)',
45
46
  type: 'boolean',
46
47
  default: true
48
+ })
49
+ .option('version-limit', {
50
+ describe: t('commit_option_version_limit').d('Maximum number of versions to keep. Oldest unreleased versions will be deleted if exceeded.'),
51
+ type: 'number'
47
52
  });
48
53
  },
49
54
  handler: (argv) => __awaiter(void 0, void 0, void 0, function* () {
@@ -59,7 +64,12 @@ export function handleCommit(argv) {
59
64
  const projectInfo = yield validateAndInitializeProject(argv === null || argv === void 0 ? void 0 : argv.name);
60
65
  if (!projectInfo)
61
66
  return;
62
- const { projectName } = projectInfo;
67
+ const { projectName, projectConfig } = projectInfo;
68
+ // Cleanup old versions if limit is specified
69
+ const limit = argv['version-limit'] || (projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.versionLimit);
70
+ if (limit) {
71
+ yield checkAndCleanupVersions(projectName, limit, true);
72
+ }
63
73
  let description;
64
74
  if (argv.description) {
65
75
  description = argv.description;
@@ -15,7 +15,7 @@ import { ensureRoutineExists } from '../../utils/checkIsRoutineCreated.js';
15
15
  import compress from '../../utils/compress.js';
16
16
  import { getProjectConfig } from '../../utils/fileUtils/index.js';
17
17
  import sleep from '../../utils/sleep.js';
18
- import { checkIsLoginSuccess } from '../utils.js';
18
+ import { checkAndCleanupVersions, checkIsLoginSuccess } from '../utils.js';
19
19
  function normalizeNotFoundStrategy(value) {
20
20
  if (!value)
21
21
  return undefined;
@@ -260,21 +260,26 @@ export function deployToEnvironments(name, codeVersion, env) {
260
260
  * 结合了压缩、提交和部署的完整流程
261
261
  */
262
262
  export function commitAndDeployVersion(projectName_1, scriptEntry_1, assets_1) {
263
- return __awaiter(this, arguments, void 0, function* (projectName, scriptEntry, assets, description = '', projectPath, env = 'production', minify = false, version, noBundle = false) {
263
+ return __awaiter(this, arguments, void 0, function* (projectName, scriptEntry, assets, description = '', projectPath, env = 'production', minify = false, version, noBundle = false, versionLimit) {
264
264
  var _a, _b, _c;
265
265
  const projectInfo = yield validateAndInitializeProject(projectName, projectPath);
266
266
  if (!projectInfo) {
267
267
  return false;
268
268
  }
269
- const { projectConfig } = projectInfo;
269
+ const { projectConfig, projectName: resolvedProjectName } = projectInfo;
270
+ // Cleanup old versions if limit is specified
271
+ const limit = versionLimit || (projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.versionLimit);
272
+ if (limit) {
273
+ yield checkAndCleanupVersions(resolvedProjectName, limit, !version);
274
+ }
270
275
  // 2) Use existing version or generate a new one
271
276
  if (version) {
272
277
  logger.startSubStep(`Using existing version ${version}`);
273
- const deployed = yield deployToEnvironments(projectInfo.projectName, version, env);
278
+ const deployed = yield deployToEnvironments(resolvedProjectName, version, env);
274
279
  logger.endSubStep(deployed ? 'Deploy finished' : 'Deploy failed');
275
280
  return deployed;
276
281
  }
277
- const res = yield generateCodeVersion(projectInfo.projectName, description, scriptEntry || (projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.entry), assets || ((_a = projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.assets) === null || _a === void 0 ? void 0 : _a.directory), minify || (projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.minify), projectPath, noBundle);
282
+ const res = yield generateCodeVersion(resolvedProjectName, description, scriptEntry || (projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.entry), assets || ((_a = projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.assets) === null || _a === void 0 ? void 0 : _a.directory), minify || (projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.minify), projectPath, noBundle);
278
283
  const isCommitSuccess = res === null || res === void 0 ? void 0 : res.isSuccess;
279
284
  if (!isCommitSuccess) {
280
285
  logger.endSubStep('Generate version failed');
@@ -61,6 +61,10 @@ const deploy = {
61
61
  .option('versions', {
62
62
  describe: 'Deploy two versions with percentages, format: v1:80,v2:20 or repeat --versions v1:80 --versions v2:20',
63
63
  type: 'array'
64
+ })
65
+ .option('version-limit', {
66
+ describe: t('deploy_option_version_limit').d('Maximum number of versions to keep. Oldest unreleased versions will be deleted if exceeded.'),
67
+ type: 'number'
64
68
  });
65
69
  },
66
70
  describe: `🚀 ${t('deploy_describe').d('Deploy your project')}`,
@@ -82,7 +86,7 @@ export function handleDeploy(argv) {
82
86
  outro(ok ? 'Deploy finished' : 'Deploy failed');
83
87
  exit(ok ? 0 : 1);
84
88
  }
85
- const success = yield commitAndDeployVersion(argv.name || undefined, entry, assets, argv.description || '', getRoot(), argv.environment || 'all', argv.minify, argv.version, (argv.bundle === false));
89
+ const success = yield commitAndDeployVersion(argv.name || undefined, entry, assets, argv.description || '', getRoot(), argv.environment || 'all', argv.minify, argv.version, (argv.bundle === false), argv['version-limit']);
86
90
  outro(success ? 'Deploy finished' : 'Deploy failed');
87
91
  if (success) {
88
92
  const projectConfig = getProjectConfig(getRoot());
@@ -153,3 +153,61 @@ export const getRoutineCodeVersions = (projectName) => __awaiter(void 0, void 0,
153
153
  const productionVersions = ((_m = (_l = (_k = (_j = (_h = routineDetail === null || routineDetail === void 0 ? void 0 : routineDetail.data) === null || _h === void 0 ? void 0 : _h.Envs) === null || _j === void 0 ? void 0 : _j.find((item) => item.Env === 'production')) === null || _k === void 0 ? void 0 : _k.CodeDeploy) === null || _l === void 0 ? void 0 : _l.CodeVersions) === null || _m === void 0 ? void 0 : _m.map((item) => item.CodeVersion)) || [];
154
154
  return { allVersions, stagingVersions, productionVersions };
155
155
  });
156
+ /**
157
+ * Check the total number of versions and delete the oldest unreleased ones if they exceed the limit.
158
+ * @param projectName Name of the project
159
+ * @param limit The maximum number of versions to keep
160
+ * @param isCreatingNewVersion Whether a new version will be created after cleanup
161
+ */
162
+ export function checkAndCleanupVersions(projectName_1, limit_1) {
163
+ return __awaiter(this, arguments, void 0, function* (projectName, limit, isCreatingNewVersion = true) {
164
+ if (limit <= 0)
165
+ return;
166
+ const { allVersions, stagingVersions, productionVersions } = yield getRoutineCodeVersions(projectName);
167
+ const currentCount = allVersions.length;
168
+ const targetCount = isCreatingNewVersion ? limit - 1 : limit;
169
+ if (currentCount <= targetCount) {
170
+ return;
171
+ }
172
+ const deployedVersions = new Set([...stagingVersions, ...productionVersions]);
173
+ // Filter versions that are not currently deployed
174
+ const undeployedVersions = allVersions.filter((v) => v.codeVersion && !deployedVersions.has(v.codeVersion));
175
+ // Sort by createTime (oldest first)
176
+ undeployedVersions.sort((a, b) => {
177
+ const timeA = a.createTime ? new Date(a.createTime).getTime() : 0;
178
+ const timeB = b.createTime ? new Date(b.createTime).getTime() : 0;
179
+ return timeA - timeB;
180
+ });
181
+ const numToDelete = currentCount - targetCount;
182
+ const toDelete = undeployedVersions.slice(0, numToDelete);
183
+ if (toDelete.length > 0) {
184
+ logger.info(t('cleanup_versions_start', {
185
+ count: toDelete.length.toString(),
186
+ limit: limit.toString()
187
+ }).d(`Cleaning up ${toDelete.length} oldest unreleased versions (limit: ${limit})...`));
188
+ const server = yield ApiService.getInstance();
189
+ for (const version of toDelete) {
190
+ if (!version.codeVersion)
191
+ continue;
192
+ try {
193
+ const res = yield server.deleteRoutineCodeVersion({
194
+ Name: projectName,
195
+ CodeVersion: version.codeVersion
196
+ });
197
+ if ((res === null || res === void 0 ? void 0 : res.Status) === 'OK') {
198
+ logger.success(t('deployments_delete_success').d('Delete success') +
199
+ `: ${version.codeVersion}`);
200
+ }
201
+ else {
202
+ logger.warn(t('deployments_delete_failed').d('Delete failed') +
203
+ `: ${version.codeVersion}`);
204
+ }
205
+ }
206
+ catch (error) {
207
+ logger.warn(t('deployments_delete_failed').d('Delete failed') +
208
+ `: ${version.codeVersion}`);
209
+ }
210
+ }
211
+ }
212
+ });
213
+ }
@@ -143,6 +143,9 @@ Description for Functions & Pages/version (skip interactive input)
143
143
  **--name, -n** _optional_
144
144
  Functions & Pages name
145
145
 
146
+ **--version-limit** _optional_
147
+ Maximum number of versions to keep. Oldest unreleased versions (not in staging or production) will be deleted if exceeded.
148
+
146
149
  ---
147
150
 
148
151
  ## deploy
@@ -174,6 +177,9 @@ Description of the version
174
177
  **--minify, -m** _optional_
175
178
  Whether to minify the code
176
179
 
180
+ **--version-limit** _optional_
181
+ Maximum number of versions to keep. Oldest unreleased versions (not in staging or production) will be deleted if exceeded.
182
+
177
183
  ---
178
184
 
179
185
  ## deployments
@@ -155,6 +155,9 @@ esa-cli commit [<ENTRY>] [OPTIONS]
155
155
  **--name, -n** _可选_
156
156
  **函数和Pages名称**
157
157
 
158
+ **--version-limit** _可选_
159
+ **保持的最大版本数量。如果超过此限制,将删除最旧的未发布版本(不处于仿真环境或线上环境的版本)。**
160
+
158
161
  ---
159
162
 
160
163
  ## deploy
@@ -186,6 +189,9 @@ esa-cli deploy [<ENTRY>] [OPTIONS]
186
189
  **--minify, -m** _可选_
187
190
  **是否压缩代码**
188
191
 
192
+ **--version-limit** _可选_
193
+ **保持的最大版本数量。如果超过此限制,将删除最旧的未发布版本(不处于仿真环境或线上环境的版本)。**
194
+
189
195
  ---
190
196
 
191
197
  ## deployments
@@ -1322,5 +1322,17 @@
1322
1322
  "login_get_credentials_from_environment_variables": {
1323
1323
  "en": "Get credentials from environment variables",
1324
1324
  "zh_CN": ""
1325
+ },
1326
+ "commit_option_version_limit": {
1327
+ "en": "Maximum number of versions to keep. Oldest unreleased versions will be deleted if exceeded.",
1328
+ "zh_CN": "保留的最大版本数量。如果超过此限制,最旧的未发布版本将被删除。"
1329
+ },
1330
+ "deploy_option_version_limit": {
1331
+ "en": "Maximum number of versions to keep. Oldest unreleased versions will be deleted if exceeded.",
1332
+ "zh_CN": "保留的最大版本数量。如果超过此限制,最旧的未发布版本将被删除。"
1333
+ },
1334
+ "cleanup_versions_start": {
1335
+ "en": "Cleaning up ${toDelete.length} oldest unreleased versions (limit: ${limit})...",
1336
+ "zh_CN": "正在清理 ${count} 个最旧的未发布版本(限制数量:${limit})..."
1325
1337
  }
1326
1338
  }
package/dist/libs/api.js CHANGED
@@ -11,8 +11,8 @@ import ESA, * as $ESA from '@alicloud/esa20240910';
11
11
  import * as $OpenApi from '@alicloud/openapi-client';
12
12
  import * as $Util from '@alicloud/tea-util';
13
13
  import { getApiConfig } from '../utils/fileUtils/index.js';
14
- import logger from './logger.js';
15
14
  import { CLI_USER_AGENT } from './constants.js';
15
+ import logger from './logger.js';
16
16
  class Client {
17
17
  constructor() {
18
18
  this.callApi = (action, request) => __awaiter(this, void 0, void 0, function* () {
@@ -12,8 +12,8 @@ import FormData from 'form-data';
12
12
  import fetch from 'node-fetch';
13
13
  import t from '../i18n/index.js';
14
14
  import { getApiConfig } from '../utils/fileUtils/index.js';
15
- import { Environment } from './interface.js';
16
15
  import { CLI_USER_AGENT } from './constants.js';
16
+ import { Environment } from './interface.js';
17
17
  export class ApiService {
18
18
  constructor(cliConfig) {
19
19
  var _a, _b;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "esa-cli",
3
- "version": "1.0.3",
3
+ "version": "1.0.4-beta.1",
4
4
  "description": "A CLI for operating Alibaba Cloud ESA Functions and Pages.",
5
5
  "main": "bin/enter.cjs",
6
6
  "type": "module",