@forge/lint
6.0.2-next.5-experimental-a1600ff6.0.2-next.3-experimental-fda9df6
out/lint/linters/server-side-linter/server-side-linter.js+
out/lint/linters/server-side-linter/server-side-linter.jsNew file+209
Index: package/out/lint/linters/server-side-linter/server-side-linter.js
===================================================================
--- package/out/lint/linters/server-side-linter/server-side-linter.js
+++ package/out/lint/linters/server-side-linter/server-side-linter.js
@@ -0,0 +1,209 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.ServerSideLinter = exports.InvalidManifestError = exports.MissingManifestError = exports.NoManifestURLError = void 0;
+const tslib_1 = require("tslib");
+const manifest_1 = require("@forge/manifest");
+const linter_interface_1 = require("../../linter-interface");
+const abstract_linter_1 = require("../../abstract-linter");
+const cli_shared_1 = require("@forge/cli-shared");
+const tmp_1 = tslib_1.__importDefault(require("tmp"));
+const node_fs_1 = require("node:fs");
+class NoManifestURLError extends Error {
+ constructor() {
+ super(cli_shared_1.Text.artifact.error.noManifestUrl);
+ }
+}
+exports.NoManifestURLError = NoManifestURLError;
+class MissingManifestError extends cli_shared_1.UserError {
+ constructor() {
+ super(cli_shared_1.Text.lint.errorMissingManifest);
+ }
+}
+exports.MissingManifestError = MissingManifestError;
+class InvalidManifestError extends cli_shared_1.UserError {
+ constructor() {
+ super(cli_shared_1.Text.lint.errorInvalidManifest);
+ }
+}
+exports.InvalidManifestError = InvalidManifestError;
+class ZipArchiver {
+ archive;
+ tempFile = tmp_1.default.fileSync({ postfix: '.zip' });
+ resolves = [];
+ rejects = [];
+ constructor(archive) {
+ this.archive = archive;
+ const output = (0, node_fs_1.createWriteStream)(this.tempFile.name).on('close', this.onClose);
+ this.archive.pipe(output);
+ this.archive.on('warning', this.onArchiveError);
+ this.archive.on('error', this.onArchiveError);
+ }
+ static async create() {
+ const { ZipArchive } = await Promise.resolve().then(() => tslib_1.__importStar(require('archiver')));
+ return new ZipArchiver(new ZipArchive());
+ }
+ addFile(fileName, contents) {
+ this.archive.append(contents, { name: fileName });
+ }
+ finalise() {
+ return new Promise((resolve, reject) => {
+ this.resolves.push(resolve);
+ this.rejects.push(reject);
+ return this.archive.finalize();
+ });
+ }
+ onArchiveError = (err) => {
+ const error = new Error(err.message);
+ for (const reject of this.rejects) {
+ reject(error);
+ }
+ };
+ onClose = () => {
+ for (const resolve of this.resolves) {
+ resolve(this.tempFile.name);
+ }
+ };
+}
+class ManifestCheckGraphqlClient {
+ graphqlClient;
+ constructor(graphqlClient) {
+ this.graphqlClient = graphqlClient;
+ }
+ async createAppManifestUploadUrl(appId, environmentKey) {
+ const mutation = `
+ mutation forge_cli_createAppManifestUploadUrl($input: CreateAppManifestUploadUrlInput!) {
+ createAppManifestUploadUrl(input: $input) {
+ success
+ preSignedUrl
+ errors {
+ message
+ extensions {
+ errorType
+ statusCode
+ }
+ }
+ }
+ }`;
+ const { response: { createAppManifestUploadUrl: { success, preSignedUrl, errors } }, requestId } = await this.graphqlClient.mutate(mutation, {
+ input: {
+ appId,
+ environmentKey
+ }
+ });
+ if (!success) {
+ const error = (0, cli_shared_1.getError)(errors);
+ throw new cli_shared_1.GraphQlMutationError(`${error.message} (requestId: ${requestId || 'unknown'})`, {
+ requestId,
+ code: error.code,
+ statusCode: error.statusCode
+ });
+ }
+ if (!preSignedUrl) {
+ throw new NoManifestURLError();
+ }
+ return preSignedUrl;
+ }
+ async getAppPreDeploymentCheck(appId, environmentKey, manifestUrl, bypassRules) {
+ const query = `
+ query forge_cli_appPreDeploymentCheck($input: AppPreDeploymentCheckInput!) {
+ appPreDeploymentCheck(input: $input) {
+ outcome {
+ rule
+ category
+ value
+ reason
+ changes {
+ key
+ added
+ removed
+ }
+ errors {
+ key
+ message
+ line
+ column
+ }
+ }
+ }
+ }
+ `;
+ const result = await this.graphqlClient.query(query, {
+ input: {
+ appId,
+ environmentKey,
+ manifestUrl,
+ bypassRules
+ }
+ });
+ if (!result.appPreDeploymentCheck) {
+ throw new MissingManifestError();
+ }
+ if (!result.appPreDeploymentCheck.outcome) {
+ throw new InvalidManifestError();
+ }
+ return result.appPreDeploymentCheck.outcome.filter((p) => p !== null);
+ }
+}
+class ServerSideLinter extends abstract_linter_1.AbstractLinter {
+ environment;
+ manifest;
+ client;
+ fileReader;
+ archiver;
+ fileUploader;
+ approveRules;
+ manifestUploadUrl;
+ constructor(environment, manifest, client, fileReader, archiver, fileUploader, logger, approveRules) {
+ super(logger);
+ this.environment = environment;
+ this.manifest = manifest;
+ this.client = client;
+ this.fileReader = fileReader;
+ this.archiver = archiver;
+ this.fileUploader = fileUploader;
+ this.approveRules = approveRules;
+ }
+ static async instance(environment, manifest, graphqlClient, logger, approveRules) {
+ const fileSystemReader = new cli_shared_1.FileSystemReader();
+ return new ServerSideLinter(environment, manifest, new ManifestCheckGraphqlClient(graphqlClient), fileSystemReader, await ZipArchiver.create(), new cli_shared_1.HttpFileUploader(fileSystemReader), logger, approveRules);
+ }
+ mapServerSideResponse(results, fixer) {
+ const manifestLintResults = new linter_interface_1.LintResult(manifest_1.MANIFEST_FILE, fixer);
+ manifestLintResults.batchAdd(...(this.getResultsForFixer(results).map(({ rule, reason, category }) => {
+ return {
+ class: category.toLowerCase(),
+ message: reason,
+ reference: rule,
+ line: 0,
+ column: 0,
+ metadata: {}
+ };
+ }) ?? []).sort((a, b) => a.line - b.line));
+ return manifestLintResults;
+ }
+ async batchExecuteImpl() {
+ if (!this.manifestUploadUrl) {
+ throw new Error('Bootstrap failed');
+ }
+ const results = await this.client.getAppPreDeploymentCheck(this.manifest.app.id, this.environment, this.manifestUploadUrl, this.approveRules ? this.approveRules : []);
+ return [this.mapServerSideResponse(results, this.getFixer())];
+ }
+ async bootstrap() {
+ const manifestContent = this.fileReader.readBinaryFile(cli_shared_1.manifestFileName);
+ if (!manifestContent) {
+ throw new Error('No Manifest content found');
+ }
+ this.archiver.addFile(cli_shared_1.manifestFileName, manifestContent);
+ const archivePath = await this.archiver.finalise();
+ const uploadUrl = await this.client.createAppManifestUploadUrl(this.manifest.app.id, this.environment);
+ await this.fileUploader.uploadFromPath(uploadUrl, archivePath);
+ this.manifestUploadUrl = uploadUrl;
+ }
+ getFixer() {
+ return undefined;
+ }
+ getResultsForFixer(results) {
+ return results?.filter((outcome) => outcome.value) || [];
+ }
+}
+exports.ServerSideLinter = ServerSideLinter;