@apso/cli 0.32.0 → 0.34.0
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/dist/lib/api/client.js +8 -1
- package/dist/lib/api/services.d.ts +11 -0
- package/dist/lib/api/services.js +9 -0
- package/dist/lib/deploy/server-push.d.ts +1 -1
- package/dist/lib/deploy/server-push.js +23 -2
- package/dist/lib/templates/entities/entity.eta +1 -1
- package/dist/lib/templates/graphql/gql-dto-graphql.eta +1 -1
- package/dist/lib/templates/guards/scope.guard.eta +1 -1
- package/dist/lib/templates/rest/controller-rest-spec.eta +1 -1
- package/dist/lib/templates/rest/controller-rest.eta +1 -1
- package/dist/lib/templates/rest/service-rest.eta +3 -3
- package/dist/lib/utils/template.d.ts +8 -0
- package/dist/lib/utils/template.js +17 -4
- package/npm-shrinkwrap.json +2 -2
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
package/dist/lib/api/client.js
CHANGED
|
@@ -287,7 +287,14 @@ async function apiRequest(path, options = {}) {
|
|
|
287
287
|
}
|
|
288
288
|
// Handle server errors with retry
|
|
289
289
|
if (response.status >= 500) {
|
|
290
|
-
|
|
290
|
+
// The BFF returns its message in `.error`; the backend uses `.message`.
|
|
291
|
+
const errObj = errorData;
|
|
292
|
+
const serverMsg = errorData.message ||
|
|
293
|
+
(typeof errObj.error === "string"
|
|
294
|
+
? errObj.error
|
|
295
|
+
: undefined) ||
|
|
296
|
+
"Server error";
|
|
297
|
+
lastError = new ApiClientError(serverMsg, response.status, errorData.details);
|
|
291
298
|
await sleep(getBackoffDelay(attempt));
|
|
292
299
|
continue;
|
|
293
300
|
}
|
|
@@ -138,5 +138,16 @@ export declare const codeApi: {
|
|
|
138
138
|
bucket?: string;
|
|
139
139
|
key?: string;
|
|
140
140
|
}>;
|
|
141
|
+
/**
|
|
142
|
+
* Record S3 metadata after the presigned PUT completes. Without this the
|
|
143
|
+
* service row's s3_code_key stays null and a server-side push fails with
|
|
144
|
+
* "No S3 code found". Mirrors the browser upload flow (upload-url -> PUT ->
|
|
145
|
+
* finalize).
|
|
146
|
+
*/
|
|
147
|
+
finalize(serviceId: string): Promise<{
|
|
148
|
+
success: boolean;
|
|
149
|
+
version?: string;
|
|
150
|
+
size?: number;
|
|
151
|
+
}>;
|
|
141
152
|
};
|
|
142
153
|
export {};
|
package/dist/lib/api/services.js
CHANGED
|
@@ -377,4 +377,13 @@ exports.codeApi = {
|
|
|
377
377
|
async getUploadUrl(serviceId) {
|
|
378
378
|
return client_1.api.get(`/api/services/${serviceId}/code/upload-url`);
|
|
379
379
|
},
|
|
380
|
+
/**
|
|
381
|
+
* Record S3 metadata after the presigned PUT completes. Without this the
|
|
382
|
+
* service row's s3_code_key stays null and a server-side push fails with
|
|
383
|
+
* "No S3 code found". Mirrors the browser upload flow (upload-url -> PUT ->
|
|
384
|
+
* finalize).
|
|
385
|
+
*/
|
|
386
|
+
async finalize(serviceId) {
|
|
387
|
+
return client_1.api.post(`/api/services/${serviceId}/code/finalize`, {});
|
|
388
|
+
},
|
|
380
389
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
/// <reference types="node" />
|
|
3
|
-
/** Zip the project source into a buffer, skipping build/vcs/dependency
|
|
3
|
+
/** Zip the project source into a buffer, skipping build/vcs/dependency/secret files. */
|
|
4
4
|
export declare function zipProject(cwd: string): Buffer;
|
|
5
5
|
/**
|
|
6
6
|
* Server-side push: upload the project zip to S3 and have the platform push it
|
|
@@ -15,12 +15,28 @@ const EXCLUDE_DIRS = new Set([
|
|
|
15
15
|
".apso",
|
|
16
16
|
"coverage",
|
|
17
17
|
]);
|
|
18
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* Never push local secrets to the repo. `.env` and `.env.*` (e.g. `.env.local`)
|
|
20
|
+
* routinely hold DB passwords and session secrets; `.env.example` is the one
|
|
21
|
+
* env file that is meant to be committed. The backend injects real production
|
|
22
|
+
* env at runtime, so these are never needed in the pushed code.
|
|
23
|
+
*/
|
|
24
|
+
function isSecretEnvFile(basename) {
|
|
25
|
+
if (basename === ".env.example" || basename === ".env.sample")
|
|
26
|
+
return false;
|
|
27
|
+
return basename === ".env" || basename.startsWith(".env.");
|
|
28
|
+
}
|
|
29
|
+
/** Zip the project source into a buffer, skipping build/vcs/dependency/secret files. */
|
|
19
30
|
function zipProject(cwd) {
|
|
20
31
|
const zip = new adm_zip_1.default();
|
|
21
32
|
zip.addLocalFolder(cwd, "", (filename) => {
|
|
22
33
|
const parts = filename.split(path.sep);
|
|
23
|
-
|
|
34
|
+
if (parts.some((p) => EXCLUDE_DIRS.has(p)))
|
|
35
|
+
return false;
|
|
36
|
+
const base = parts[parts.length - 1];
|
|
37
|
+
if (isSecretEnvFile(base))
|
|
38
|
+
return false;
|
|
39
|
+
return true;
|
|
24
40
|
});
|
|
25
41
|
return zip.toBuffer();
|
|
26
42
|
}
|
|
@@ -41,6 +57,11 @@ async function serverSidePush(opts) {
|
|
|
41
57
|
if (!res.ok) {
|
|
42
58
|
throw new Error(`Code upload failed (${res.status}). ${await res.text().catch(() => "")}`);
|
|
43
59
|
}
|
|
60
|
+
// Record the S3 metadata (bucket/key/version) on the service row. The
|
|
61
|
+
// presigned PUT lands the zip in S3 but does NOT persist where it went; the
|
|
62
|
+
// backend push reads s3_code_key from the DB, so without this it fails with
|
|
63
|
+
// "No S3 code found". Same three-step flow the browser upload uses.
|
|
64
|
+
await services_1.codeApi.finalize(opts.serviceId);
|
|
44
65
|
await services_1.githubApi.push(opts.serviceId, opts.connectionId, {
|
|
45
66
|
branch: opts.branch,
|
|
46
67
|
message: opts.message,
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
import { JSONResolver } from 'graphql-scalars';
|
|
38
38
|
<% } %>
|
|
39
39
|
import { Type } from 'class-transformer';
|
|
40
|
-
import { CrudValidationGroups } from '@
|
|
40
|
+
import { CrudValidationGroups } from '@apso/crud';
|
|
41
41
|
<% if (it.importEnums) { %>
|
|
42
42
|
import * as enums from '../enums';
|
|
43
43
|
<% } %>
|
|
@@ -21,7 +21,7 @@ import { Type } from 'class-transformer';
|
|
|
21
21
|
|
|
22
22
|
import { ObjectType, Field, Int, GraphQLISODateTime, ID } from '@nestjs/graphql';
|
|
23
23
|
import { JSONResolver } from 'graphql-scalars';
|
|
24
|
-
import { CrudValidationGroups } from '@
|
|
24
|
+
import { CrudValidationGroups } from '@apso/crud';
|
|
25
25
|
<% if (it.importEnums) { %>
|
|
26
26
|
import * as enums from '../../enums';
|
|
27
27
|
<% } %>
|
|
@@ -293,7 +293,7 @@ export class ScopeGuard implements CanActivate {
|
|
|
293
293
|
if (scope.direct) {
|
|
294
294
|
const scopeValue = this.getScopeValue(request, scope.contextKey);
|
|
295
295
|
if (scopeValue) {
|
|
296
|
-
// Add filter for @
|
|
296
|
+
// Add filter for @apso/crud
|
|
297
297
|
request.query[`filter.${scope.field}`] = `$eq:${scopeValue}`;
|
|
298
298
|
}
|
|
299
299
|
}
|
|
@@ -4,7 +4,7 @@ import { Test, TestingModule } from '@nestjs/testing';
|
|
|
4
4
|
import {<%= it.entityName %>, <%= it.entityName %>Create} from './dtos/<%= it.entityName%>.dto'
|
|
5
5
|
import {<%= it.ctrlName %>} from "./<%= it.entityName %>.controller";
|
|
6
6
|
import {<%= it.svcName %>} from "./<%= it.entityName %>.service";
|
|
7
|
-
import { CrudRequest } from '@
|
|
7
|
+
import { CrudRequest } from '@apso/crud';
|
|
8
8
|
|
|
9
9
|
const baseRequest: CrudRequest = {
|
|
10
10
|
parsed: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<%~ includeFile('./header.eta') %>
|
|
2
2
|
|
|
3
3
|
import { Controller, Body } from "@nestjs/common";
|
|
4
|
-
import { Crud, CrudController, Override, ParsedRequest, CrudRequest, ParsedBody, CreateManyDto } from '@
|
|
4
|
+
import { Crud, CrudController, Override, ParsedRequest, CrudRequest, ParsedBody, CreateManyDto } from '@apso/crud';
|
|
5
5
|
import { ApiTags, ApiResponse, ApiOperation, ApiBody } from '@nestjs/swagger';
|
|
6
6
|
import {<%= it.entityName %>, <%= it.entityName %>Create, <%= it.entityName %>Update} from './dtos/<%= it.entityName%>.dto'
|
|
7
7
|
import {<%= it.svcName %>} from './<%= it.entityName %>.service';
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import { Injectable, NotFoundException } from "@nestjs/common";
|
|
4
4
|
import { InjectRepository } from "@nestjs/typeorm";
|
|
5
|
-
import { TypeOrmCrudService } from "@
|
|
6
|
-
import { QueryOptions, CrudRequest } from '@
|
|
7
|
-
import { ParsedRequestParams } from '@
|
|
5
|
+
import { TypeOrmCrudService } from "@apso/crud-typeorm";
|
|
6
|
+
import { QueryOptions, CrudRequest } from '@apso/crud';
|
|
7
|
+
import { ParsedRequestParams } from '@apso/crud-request';
|
|
8
8
|
import { Repository, DeepPartial } from 'typeorm';
|
|
9
9
|
|
|
10
10
|
import {<%= it.entityName %>} from './<%= it.entityName%>.entity'
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { TargetLanguage } from "../types";
|
|
2
2
|
export declare const TEMPLATE_REPOS: Record<TargetLanguage, string>;
|
|
3
|
+
/**
|
|
4
|
+
* Git ref (tag or branch) to clone for each template. Pinning to a tag makes
|
|
5
|
+
* the CLI-version-to-template pairing explicit instead of a date cutoff on
|
|
6
|
+
* template main. v2.0.0 is the first TS template revision on published
|
|
7
|
+
* @apso/crud ^1.0.1 (dual-dialect: nestjsx + PostgREST); the Python and Go
|
|
8
|
+
* template repos are untagged and don't carry the dialect work.
|
|
9
|
+
*/
|
|
10
|
+
export declare const TEMPLATE_REFS: Record<TargetLanguage, string>;
|
|
3
11
|
export declare const PROJECT_NAME_PATTERN: RegExp;
|
|
4
12
|
/**
|
|
5
13
|
* Clone a template repository into the given project path.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.initGitRepo = exports.ensureEnvFile = exports.cloneTemplate = exports.PROJECT_NAME_PATTERN = exports.TEMPLATE_REPOS = void 0;
|
|
3
|
+
exports.initGitRepo = exports.ensureEnvFile = exports.cloneTemplate = exports.PROJECT_NAME_PATTERN = exports.TEMPLATE_REFS = exports.TEMPLATE_REPOS = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const fs = tslib_1.__importStar(require("fs"));
|
|
6
6
|
const path = tslib_1.__importStar(require("path"));
|
|
@@ -10,6 +10,18 @@ exports.TEMPLATE_REPOS = {
|
|
|
10
10
|
python: "https://github.com/apsoai/service-template-python.git",
|
|
11
11
|
go: "https://github.com/apsoai/service-template-go.git",
|
|
12
12
|
};
|
|
13
|
+
/**
|
|
14
|
+
* Git ref (tag or branch) to clone for each template. Pinning to a tag makes
|
|
15
|
+
* the CLI-version-to-template pairing explicit instead of a date cutoff on
|
|
16
|
+
* template main. v2.0.0 is the first TS template revision on published
|
|
17
|
+
* @apso/crud ^1.0.1 (dual-dialect: nestjsx + PostgREST); the Python and Go
|
|
18
|
+
* template repos are untagged and don't carry the dialect work.
|
|
19
|
+
*/
|
|
20
|
+
exports.TEMPLATE_REFS = {
|
|
21
|
+
typescript: "v2.0.0",
|
|
22
|
+
python: "main",
|
|
23
|
+
go: "main",
|
|
24
|
+
};
|
|
13
25
|
exports.PROJECT_NAME_PATTERN = /^[A-Za-z][\w-]*$/;
|
|
14
26
|
/**
|
|
15
27
|
* Clone a template repository into the given project path.
|
|
@@ -17,7 +29,8 @@ exports.PROJECT_NAME_PATTERN = /^[A-Za-z][\w-]*$/;
|
|
|
17
29
|
*/
|
|
18
30
|
function cloneTemplate(projectPath, language, log) {
|
|
19
31
|
const repoUrl = exports.TEMPLATE_REPOS[language];
|
|
20
|
-
|
|
32
|
+
const templateRef = exports.TEMPLATE_REFS[language];
|
|
33
|
+
log(`Cloning ${language} service template (${templateRef})...`);
|
|
21
34
|
if (fs.existsSync(projectPath)) {
|
|
22
35
|
throw new Error(`Directory already exists: ${projectPath}`);
|
|
23
36
|
}
|
|
@@ -27,12 +40,12 @@ function cloneTemplate(projectPath, language, log) {
|
|
|
27
40
|
catch (error) {
|
|
28
41
|
throw new Error(`Failed to create directory: ${error.message}`);
|
|
29
42
|
}
|
|
30
|
-
const cloneResult = shelljs_1.default.exec(`git clone --depth=1 --branch=
|
|
43
|
+
const cloneResult = shelljs_1.default.exec(`git clone --depth=1 --branch="${templateRef}" "${repoUrl}" "${projectPath}"`, { silent: true });
|
|
31
44
|
if (cloneResult.code !== 0) {
|
|
32
45
|
shelljs_1.default.rm("-rf", projectPath);
|
|
33
46
|
throw new Error(`Failed to clone the template repository from GitHub.\n` +
|
|
34
47
|
`Error Output:\n${cloneResult.stderr}\n\n` +
|
|
35
|
-
`Please check your network connection and ensure
|
|
48
|
+
`Please check your network connection and ensure ref "${templateRef}" exists at ${repoUrl}`);
|
|
36
49
|
}
|
|
37
50
|
shelljs_1.default.rm("-rf", path.join(projectPath, ".git"));
|
|
38
51
|
ensureEnvFile(projectPath, log);
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apso/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.34.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@apso/cli",
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.34.0",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@biomejs/biome": "^1.9.4",
|
package/oclif.manifest.json
CHANGED