@friggframework/devtools 2.0.0-next.47 → 2.0.0-next.48
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/frigg-cli/README.md +1290 -0
- package/frigg-cli/__tests__/unit/commands/build.test.js +279 -0
- package/frigg-cli/__tests__/unit/commands/db-setup.test.js +548 -0
- package/frigg-cli/__tests__/unit/commands/deploy.test.js +320 -0
- package/frigg-cli/__tests__/unit/commands/doctor.test.js +309 -0
- package/frigg-cli/__tests__/unit/commands/install.test.js +400 -0
- package/frigg-cli/__tests__/unit/commands/ui.test.js +346 -0
- package/frigg-cli/__tests__/unit/dependencies.test.js +74 -0
- package/frigg-cli/__tests__/unit/utils/database-validator.test.js +366 -0
- package/frigg-cli/__tests__/unit/utils/error-messages.test.js +304 -0
- package/frigg-cli/__tests__/unit/version-detection.test.js +171 -0
- package/frigg-cli/__tests__/utils/mock-factory.js +270 -0
- package/frigg-cli/__tests__/utils/prisma-mock.js +194 -0
- package/frigg-cli/__tests__/utils/test-fixtures.js +463 -0
- package/frigg-cli/__tests__/utils/test-setup.js +287 -0
- package/frigg-cli/build-command/index.js +66 -0
- package/frigg-cli/db-setup-command/index.js +193 -0
- package/frigg-cli/deploy-command/SPEC-DEPLOY-DRY-RUN.md +981 -0
- package/frigg-cli/deploy-command/index.js +302 -0
- package/frigg-cli/doctor-command/index.js +335 -0
- package/frigg-cli/generate-command/__tests__/generate-command.test.js +301 -0
- package/frigg-cli/generate-command/azure-generator.js +43 -0
- package/frigg-cli/generate-command/gcp-generator.js +47 -0
- package/frigg-cli/generate-command/index.js +332 -0
- package/frigg-cli/generate-command/terraform-generator.js +555 -0
- package/frigg-cli/generate-iam-command.js +118 -0
- package/frigg-cli/index.js +173 -0
- package/frigg-cli/index.test.js +158 -0
- package/frigg-cli/init-command/backend-first-handler.js +756 -0
- package/frigg-cli/init-command/index.js +93 -0
- package/frigg-cli/init-command/template-handler.js +143 -0
- package/frigg-cli/install-command/backend-js.js +33 -0
- package/frigg-cli/install-command/commit-changes.js +16 -0
- package/frigg-cli/install-command/environment-variables.js +127 -0
- package/frigg-cli/install-command/environment-variables.test.js +136 -0
- package/frigg-cli/install-command/index.js +54 -0
- package/frigg-cli/install-command/install-package.js +13 -0
- package/frigg-cli/install-command/integration-file.js +30 -0
- package/frigg-cli/install-command/logger.js +12 -0
- package/frigg-cli/install-command/template.js +90 -0
- package/frigg-cli/install-command/validate-package.js +75 -0
- package/frigg-cli/jest.config.js +124 -0
- package/frigg-cli/package.json +63 -0
- package/frigg-cli/repair-command/index.js +564 -0
- package/frigg-cli/start-command/index.js +149 -0
- package/frigg-cli/start-command/start-command.test.js +297 -0
- package/frigg-cli/test/init-command.test.js +180 -0
- package/frigg-cli/test/npm-registry.test.js +319 -0
- package/frigg-cli/ui-command/index.js +154 -0
- package/frigg-cli/utils/app-resolver.js +319 -0
- package/frigg-cli/utils/backend-path.js +25 -0
- package/frigg-cli/utils/database-validator.js +154 -0
- package/frigg-cli/utils/error-messages.js +257 -0
- package/frigg-cli/utils/npm-registry.js +167 -0
- package/frigg-cli/utils/process-manager.js +199 -0
- package/frigg-cli/utils/repo-detection.js +405 -0
- package/infrastructure/create-frigg-infrastructure.js +125 -12
- package/infrastructure/docs/PRE-DEPLOYMENT-HEALTH-CHECK-SPEC.md +1317 -0
- package/infrastructure/domains/shared/resource-discovery.enhanced.test.js +306 -0
- package/infrastructure/domains/shared/resource-discovery.js +31 -2
- package/infrastructure/domains/shared/utilities/base-definition-factory.js +1 -1
- package/infrastructure/domains/shared/utilities/prisma-layer-manager.js +109 -5
- package/infrastructure/domains/shared/utilities/prisma-layer-manager.test.js +310 -4
- package/infrastructure/domains/shared/validation/plugin-validator.js +187 -0
- package/infrastructure/domains/shared/validation/plugin-validator.test.js +323 -0
- package/infrastructure/infrastructure-composer.js +22 -0
- package/layers/prisma/.build-complete +3 -0
- package/package.json +18 -7
- package/management-ui/package-lock.json +0 -16517
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Version Detection Wrapper
|
|
4
|
+
// This code runs when frigg-cli is installed globally
|
|
5
|
+
// It checks for a local installation and prefers it if newer
|
|
6
|
+
(function versionDetection() {
|
|
7
|
+
// Skip version detection if explicitly disabled (prevents recursion)
|
|
8
|
+
if (process.env.FRIGG_CLI_SKIP_VERSION_CHECK === 'true') {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const semver = require('semver');
|
|
15
|
+
const { spawn } = require('child_process');
|
|
16
|
+
|
|
17
|
+
// Get the directory from which the command was invoked
|
|
18
|
+
const cwd = process.cwd();
|
|
19
|
+
|
|
20
|
+
// Try to find local frigg-cli installation
|
|
21
|
+
const localCliPath = path.join(cwd, 'node_modules', '@friggframework', 'frigg-cli');
|
|
22
|
+
const localCliPackageJson = path.join(localCliPath, 'package.json');
|
|
23
|
+
const localCliIndex = path.join(localCliPath, 'index.js');
|
|
24
|
+
|
|
25
|
+
// Get global version (this package)
|
|
26
|
+
const globalVersion = require('./package.json').version;
|
|
27
|
+
|
|
28
|
+
// Check if local installation exists
|
|
29
|
+
if (fs.existsSync(localCliPackageJson) && fs.existsSync(localCliIndex)) {
|
|
30
|
+
try {
|
|
31
|
+
const localPackage = JSON.parse(fs.readFileSync(localCliPackageJson, 'utf8'));
|
|
32
|
+
const localVersion = localPackage.version;
|
|
33
|
+
|
|
34
|
+
// Compare versions
|
|
35
|
+
const comparison = semver.compare(localVersion, globalVersion);
|
|
36
|
+
|
|
37
|
+
if (comparison >= 0) {
|
|
38
|
+
// Local version is newer or equal - use it
|
|
39
|
+
console.log(`Using local frigg-cli@${localVersion} (global: ${globalVersion})`);
|
|
40
|
+
|
|
41
|
+
// Execute local CLI as subprocess to avoid module resolution issues
|
|
42
|
+
const args = process.argv.slice(2); // Remove 'node' and script path
|
|
43
|
+
const child = spawn(process.execPath, [localCliIndex, ...args], {
|
|
44
|
+
stdio: 'inherit',
|
|
45
|
+
env: {
|
|
46
|
+
...process.env,
|
|
47
|
+
FRIGG_CLI_SKIP_VERSION_CHECK: 'true', // Prevent recursion
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
child.on('exit', (code) => {
|
|
52
|
+
process.exit(code || 0);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Signal that we've delegated to local CLI
|
|
56
|
+
process.on('SIGINT', () => {
|
|
57
|
+
child.kill('SIGINT');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Prevent further execution
|
|
61
|
+
return true; // Indicates we've delegated
|
|
62
|
+
} else {
|
|
63
|
+
// Global version is newer - warn user
|
|
64
|
+
console.warn(`⚠️ Version mismatch: global frigg-cli@${globalVersion} is newer than local frigg-cli@${localVersion}`);
|
|
65
|
+
console.warn(` Consider updating local version: npm install @friggframework/frigg-cli@latest`);
|
|
66
|
+
}
|
|
67
|
+
} catch (error) {
|
|
68
|
+
// Failed to read local package.json or compare versions
|
|
69
|
+
// Continue with global version silently
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Return false to indicate we should continue with global version
|
|
74
|
+
return false;
|
|
75
|
+
})();
|
|
76
|
+
|
|
77
|
+
const { Command } = require('commander');
|
|
78
|
+
const { initCommand } = require('./init-command');
|
|
79
|
+
const { installCommand } = require('./install-command');
|
|
80
|
+
const { startCommand } = require('./start-command'); // Assuming you have a startCommand module
|
|
81
|
+
const { buildCommand } = require('./build-command');
|
|
82
|
+
const { deployCommand } = require('./deploy-command');
|
|
83
|
+
const { generateIamCommand } = require('./generate-iam-command');
|
|
84
|
+
const { uiCommand } = require('./ui-command');
|
|
85
|
+
const { dbSetupCommand } = require('./db-setup-command');
|
|
86
|
+
const { doctorCommand } = require('./doctor-command');
|
|
87
|
+
const { repairCommand } = require('./repair-command');
|
|
88
|
+
|
|
89
|
+
const program = new Command();
|
|
90
|
+
|
|
91
|
+
program
|
|
92
|
+
.command('init [templateName]')
|
|
93
|
+
.description('Initialize a new Frigg application')
|
|
94
|
+
.option('-t, --template <template>', 'template to use', 'backend-only')
|
|
95
|
+
.option('-n, --name <name>', 'project name')
|
|
96
|
+
.option('-d, --directory <directory>', 'target directory')
|
|
97
|
+
.action(initCommand);
|
|
98
|
+
|
|
99
|
+
program
|
|
100
|
+
.command('install [apiModuleName]')
|
|
101
|
+
.description('Install an API module')
|
|
102
|
+
.action(installCommand);
|
|
103
|
+
|
|
104
|
+
program
|
|
105
|
+
.command('start')
|
|
106
|
+
.description('Run the backend and optional frontend')
|
|
107
|
+
.option('-s, --stage <stage>', 'deployment stage', 'dev')
|
|
108
|
+
.option('-v, --verbose', 'enable verbose output')
|
|
109
|
+
.action(startCommand);
|
|
110
|
+
|
|
111
|
+
program
|
|
112
|
+
.command('build')
|
|
113
|
+
.description('Build the serverless application')
|
|
114
|
+
.option('-s, --stage <stage>', 'deployment stage', 'dev')
|
|
115
|
+
.option('-v, --verbose', 'enable verbose output')
|
|
116
|
+
.option('-p, --production', 'build for production (enables AWS discovery)')
|
|
117
|
+
.action(buildCommand);
|
|
118
|
+
|
|
119
|
+
program
|
|
120
|
+
.command('deploy')
|
|
121
|
+
.description('Deploy the serverless application')
|
|
122
|
+
.option('-s, --stage <stage>', 'deployment stage', 'dev')
|
|
123
|
+
.option('-v, --verbose', 'enable verbose output')
|
|
124
|
+
.option('-f, --force', 'force deployment (bypasses caching for layers and functions)')
|
|
125
|
+
.option('--skip-doctor', 'skip post-deployment health check')
|
|
126
|
+
.action(deployCommand);
|
|
127
|
+
|
|
128
|
+
program
|
|
129
|
+
.command('generate-iam')
|
|
130
|
+
.description('Generate IAM CloudFormation template based on app definition')
|
|
131
|
+
.option('-o, --output <path>', 'output directory', 'backend/infrastructure')
|
|
132
|
+
.option('-u, --user <name>', 'deployment user name', 'frigg-deployment-user')
|
|
133
|
+
.option('-s, --stack-name <name>', 'CloudFormation stack name', 'frigg-deployment-iam')
|
|
134
|
+
.option('-v, --verbose', 'enable verbose output')
|
|
135
|
+
.action(generateIamCommand);
|
|
136
|
+
|
|
137
|
+
program
|
|
138
|
+
.command('ui')
|
|
139
|
+
.description('Launch the Frigg Management UI')
|
|
140
|
+
.option('-p, --port <port>', 'port to run the UI on', '3210')
|
|
141
|
+
.option('--no-open', 'do not open browser automatically')
|
|
142
|
+
.action(uiCommand);
|
|
143
|
+
|
|
144
|
+
program
|
|
145
|
+
.command('db:setup')
|
|
146
|
+
.description('Set up database schema and generate Prisma client')
|
|
147
|
+
.option('-s, --stage <stage>', 'deployment stage', 'development')
|
|
148
|
+
.option('-v, --verbose', 'enable verbose output')
|
|
149
|
+
.action(dbSetupCommand);
|
|
150
|
+
|
|
151
|
+
program
|
|
152
|
+
.command('doctor [stackName]')
|
|
153
|
+
.description('Run health check on deployed CloudFormation stack')
|
|
154
|
+
.option('-r, --region <region>', 'AWS region (defaults to AWS_REGION env var or us-east-1)')
|
|
155
|
+
.option('-f, --format <format>', 'output format (console or json)', 'console')
|
|
156
|
+
.option('-o, --output <path>', 'save report to file')
|
|
157
|
+
.option('-v, --verbose', 'enable verbose output')
|
|
158
|
+
.action(doctorCommand);
|
|
159
|
+
|
|
160
|
+
program
|
|
161
|
+
.command('repair <stackName>')
|
|
162
|
+
.description('Repair infrastructure issues (import orphaned resources, reconcile property drift)')
|
|
163
|
+
.option('-r, --region <region>', 'AWS region (defaults to AWS_REGION env var or us-east-1)')
|
|
164
|
+
.option('--import', 'import orphaned resources into stack')
|
|
165
|
+
.option('--reconcile', 'reconcile property drift')
|
|
166
|
+
.option('--mode <mode>', 'reconciliation mode (template or resource)', 'template')
|
|
167
|
+
.option('-y, --yes', 'skip confirmation prompts')
|
|
168
|
+
.option('-v, --verbose', 'enable verbose output')
|
|
169
|
+
.action(repairCommand);
|
|
170
|
+
|
|
171
|
+
program.parse(process.argv);
|
|
172
|
+
|
|
173
|
+
module.exports = { initCommand, installCommand, startCommand, buildCommand, deployCommand, generateIamCommand, uiCommand, dbSetupCommand, doctorCommand, repairCommand };
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
const { Command } = require('commander');
|
|
2
|
+
const { installCommand } = require('./index');
|
|
3
|
+
const { validatePackageExists } = require('./install-command/validate-package');
|
|
4
|
+
const { findNearestBackendPackageJson, validateBackendPath } = require('@friggframework/core');
|
|
5
|
+
const { installPackage } = require('./install-command/install-package');
|
|
6
|
+
const { createIntegrationFile } = require('./install-command/integration-file');
|
|
7
|
+
const { updateBackendJsFile } = require('./install-command/backend-js');
|
|
8
|
+
const { commitChanges } = require('./install-command/commit-changes');
|
|
9
|
+
const { logInfo, logError } = require('./install-command/logger');
|
|
10
|
+
|
|
11
|
+
describe('CLI Command Tests', () => {
|
|
12
|
+
it('should successfully install an API module when all steps complete without errors', async () => {
|
|
13
|
+
const mockApiModuleName = 'testModule';
|
|
14
|
+
const mockPackageName = `@friggframework/api-module-${mockApiModuleName}`;
|
|
15
|
+
const mockBackendPath = '/mock/backend/path';
|
|
16
|
+
|
|
17
|
+
jest.mock('./install-command/validate-package', () => ({
|
|
18
|
+
validatePackageExists: jest.fn().mockResolvedValue(true),
|
|
19
|
+
}));
|
|
20
|
+
jest.mock('./utils/backend-path', () => ({
|
|
21
|
+
findNearestBackendPackageJson: jest
|
|
22
|
+
.fn()
|
|
23
|
+
.mockReturnValue(mockBackendPath),
|
|
24
|
+
validateBackendPath: jest.fn().mockReturnValue(true),
|
|
25
|
+
}));
|
|
26
|
+
jest.mock('./install-command/install-package', () => ({
|
|
27
|
+
installPackage: jest.fn().mockReturnValue(true),
|
|
28
|
+
}));
|
|
29
|
+
jest.mock('./install-command/integration-file', () => ({
|
|
30
|
+
createIntegrationFile: jest.fn().mockReturnValue(true),
|
|
31
|
+
}));
|
|
32
|
+
jest.mock('./install-command/backend-js', () => ({
|
|
33
|
+
updateBackendJsFile: jest.fn().mockReturnValue(true),
|
|
34
|
+
}));
|
|
35
|
+
jest.mock('./install-command/commit-changes', () => ({
|
|
36
|
+
commitChanges: jest.fn().mockReturnValue(true),
|
|
37
|
+
}));
|
|
38
|
+
jest.mock('./install-command/logger', () => ({
|
|
39
|
+
logInfo: jest.fn(),
|
|
40
|
+
logError: jest.fn(),
|
|
41
|
+
}));
|
|
42
|
+
|
|
43
|
+
const program = new Command();
|
|
44
|
+
program
|
|
45
|
+
.command('install <apiModuleName>')
|
|
46
|
+
.description('Install an API module')
|
|
47
|
+
.action(installCommand);
|
|
48
|
+
|
|
49
|
+
await program.parseAsync(['node', 'install', mockApiModuleName]);
|
|
50
|
+
|
|
51
|
+
expect(validatePackageExists).toHaveBeenCalledWith(mockPackageName);
|
|
52
|
+
expect(findNearestBackendPackageJson).toHaveBeenCalled();
|
|
53
|
+
expect(validateBackendPath).toHaveBeenCalledWith(mockBackendPath);
|
|
54
|
+
expect(installPackage).toHaveBeenCalledWith(
|
|
55
|
+
mockBackendPath,
|
|
56
|
+
mockPackageName
|
|
57
|
+
);
|
|
58
|
+
expect(createIntegrationFile).toHaveBeenCalledWith(
|
|
59
|
+
mockBackendPath,
|
|
60
|
+
mockApiModuleName
|
|
61
|
+
);
|
|
62
|
+
expect(updateBackendJsFile).toHaveBeenCalledWith(
|
|
63
|
+
mockBackendPath,
|
|
64
|
+
mockApiModuleName
|
|
65
|
+
);
|
|
66
|
+
expect(commitChanges).toHaveBeenCalledWith(
|
|
67
|
+
mockBackendPath,
|
|
68
|
+
mockApiModuleName
|
|
69
|
+
);
|
|
70
|
+
expect(logInfo).toHaveBeenCalledWith(
|
|
71
|
+
`Successfully installed ${mockPackageName} and updated the project.`
|
|
72
|
+
);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should log an error and exit with code 1 if the package does not exist', async () => {
|
|
76
|
+
const mockExit = jest
|
|
77
|
+
.spyOn(process, 'exit')
|
|
78
|
+
.mockImplementation(() => {});
|
|
79
|
+
const mockLogError = jest
|
|
80
|
+
.spyOn(require('./install-command/logger'), 'logError')
|
|
81
|
+
.mockImplementation(() => {});
|
|
82
|
+
const mockValidatePackageExists = jest
|
|
83
|
+
.spyOn(
|
|
84
|
+
require('./install-command/validate-package'),
|
|
85
|
+
'validatePackageExists'
|
|
86
|
+
)
|
|
87
|
+
.mockImplementation(() => {
|
|
88
|
+
throw new Error('Package not found');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const program = new Command();
|
|
92
|
+
program
|
|
93
|
+
.command('install <apiModuleName>')
|
|
94
|
+
.description('Install an API module')
|
|
95
|
+
.action(installCommand);
|
|
96
|
+
|
|
97
|
+
await program.parseAsync(['node', 'install', 'nonexistent-package']);
|
|
98
|
+
|
|
99
|
+
expect(mockValidatePackageExists).toHaveBeenCalledWith(
|
|
100
|
+
'@friggframework/api-module-nonexistent-package'
|
|
101
|
+
);
|
|
102
|
+
expect(mockLogError).toHaveBeenCalledWith(
|
|
103
|
+
'An error occurred:',
|
|
104
|
+
expect.any(Error)
|
|
105
|
+
);
|
|
106
|
+
expect(mockExit).toHaveBeenCalledWith(1);
|
|
107
|
+
|
|
108
|
+
mockExit.mockRestore();
|
|
109
|
+
mockLogError.mockRestore();
|
|
110
|
+
mockValidatePackageExists.mockRestore();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should log an error and exit with code 1 if the backend path is invalid', async () => {
|
|
114
|
+
const mockLogError = jest
|
|
115
|
+
.spyOn(require('./install-command/logger'), 'logError')
|
|
116
|
+
.mockImplementation(() => {});
|
|
117
|
+
const mockProcessExit = jest
|
|
118
|
+
.spyOn(process, 'exit')
|
|
119
|
+
.mockImplementation(() => {});
|
|
120
|
+
const mockValidatePackageExists = jest
|
|
121
|
+
.spyOn(
|
|
122
|
+
require('./install-command/validate-package'),
|
|
123
|
+
'validatePackageExists'
|
|
124
|
+
)
|
|
125
|
+
.mockResolvedValue(true);
|
|
126
|
+
const mockFindNearestBackendPackageJson = jest
|
|
127
|
+
.spyOn(
|
|
128
|
+
require('./utils/backend-path'),
|
|
129
|
+
'findNearestBackendPackageJson'
|
|
130
|
+
)
|
|
131
|
+
.mockReturnValue('/invalid/path');
|
|
132
|
+
const mockValidateBackendPath = jest
|
|
133
|
+
.spyOn(require('./utils/backend-path'), 'validateBackendPath')
|
|
134
|
+
.mockImplementation(() => {
|
|
135
|
+
throw new Error('Invalid backend path');
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const program = new Command();
|
|
139
|
+
program
|
|
140
|
+
.command('install <apiModuleName>')
|
|
141
|
+
.description('Install an API module')
|
|
142
|
+
.action(installCommand);
|
|
143
|
+
|
|
144
|
+
await program.parseAsync(['node', 'install', 'test-module']);
|
|
145
|
+
|
|
146
|
+
expect(mockLogError).toHaveBeenCalledWith(
|
|
147
|
+
'An error occurred:',
|
|
148
|
+
expect.any(Error)
|
|
149
|
+
);
|
|
150
|
+
expect(mockProcessExit).toHaveBeenCalledWith(1);
|
|
151
|
+
|
|
152
|
+
mockLogError.mockRestore();
|
|
153
|
+
mockProcessExit.mockRestore();
|
|
154
|
+
mockValidatePackageExists.mockRestore();
|
|
155
|
+
mockFindNearestBackendPackageJson.mockRestore();
|
|
156
|
+
mockValidateBackendPath.mockRestore();
|
|
157
|
+
});
|
|
158
|
+
});
|