@fishawack/lab-env 5.4.0-beta.1 → 5.5.0-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/CHANGELOG.md +44 -0
- package/_Ai/python-3.md +31 -28
- package/_Ai/workspace-@.md +4 -0
- package/_Test/_fixtures/content/config.json +36 -0
- package/_Test/_fixtures/content/content-0/blah.txt +1 -0
- package/_Test/content.js +517 -0
- package/cli.js +1 -0
- package/commands/content.js +115 -1
- package/commands/create/cmds/provision.js +1 -1
- package/commands/create/libs/aws-cloudfront-request.js +1 -1
- package/commands/create/libs/aws-cloudfront-response.js +1 -1
- package/commands/create/libs/vars.js +4 -0
- package/commands/create/templates/elasticbeanstalk/.platform/httpd/conf.d/security_headers-wordpress.conf +1 -1
- package/commands/create/templates/elasticbeanstalk/.platform/httpd/conf.d/security_headers.conf +1 -1
- package/commands/create/templates/elasticbeanstalk/.platform/nginx/conf.d/security_headers.conf +1 -1
- package/commands/helpers/content-pull.js +209 -0
- package/commands/helpers/content-request.js +223 -0
- package/commands/scan.js +49 -0
- package/commands/test.js +2 -15
- package/commands/workspace.js +160 -63
- package/package.json +3 -1
- package/python/0/docker-compose.yml +13 -0
package/commands/workspace.js
CHANGED
|
@@ -28,9 +28,135 @@ const generateWorkspaceName = (projects, customName) => {
|
|
|
28
28
|
return `workspace-${baseName}`;
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
+
const linkInstructionFiles = (workspaceDir, projects, cwd) => {
|
|
32
|
+
const instructionsDir = path.join(workspaceDir, ".github", "instructions");
|
|
33
|
+
fs.mkdirSync(instructionsDir, { recursive: true });
|
|
34
|
+
|
|
35
|
+
const instructionFiles = projects.flatMap((project) =>
|
|
36
|
+
glob.sync(`${project}/.github/instructions/*.md`, { cwd }),
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
let linked = 0;
|
|
40
|
+
instructionFiles.forEach((filePath) => {
|
|
41
|
+
const fileName = path.basename(filePath);
|
|
42
|
+
const targetPath = path.join(instructionsDir, fileName);
|
|
43
|
+
if (fs.existsSync(targetPath)) return;
|
|
44
|
+
createSymlink(path.join(cwd, filePath), targetPath);
|
|
45
|
+
linked++;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
return linked;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const createWorkspace = (workspaceDir, workspaceName, projects, cwd) => {
|
|
52
|
+
fs.mkdirSync(workspaceDir);
|
|
53
|
+
console.log(`Created workspace directory: ${workspaceName}`);
|
|
54
|
+
|
|
55
|
+
// Create project symlinks
|
|
56
|
+
projects.forEach((project) => {
|
|
57
|
+
createSymlink(
|
|
58
|
+
path.join(cwd, project),
|
|
59
|
+
path.join(workspaceDir, project),
|
|
60
|
+
);
|
|
61
|
+
console.log(`Linked project: ${project}`);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Setup AI instructions
|
|
65
|
+
const linked = linkInstructionFiles(workspaceDir, projects, cwd);
|
|
66
|
+
|
|
67
|
+
console.log(
|
|
68
|
+
linked
|
|
69
|
+
? `Linked ${linked} instruction file(s) to workspace`
|
|
70
|
+
: "No AI instruction files found in projects",
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
_.copyAIInstructionsFile("workspace-@", workspaceDir);
|
|
74
|
+
_.copyAIInstructionsFile("mcp-@", workspaceDir);
|
|
75
|
+
|
|
76
|
+
// Open in VS Code
|
|
77
|
+
try {
|
|
78
|
+
execSync("code .", { cwd: workspaceDir, stdio: "ignore" });
|
|
79
|
+
console.log(`\nOpened workspace in VS Code: ${workspaceName}`);
|
|
80
|
+
} catch {
|
|
81
|
+
// Silently ignore VS Code errors
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
console.log(`\nWorkspace setup complete: ${workspaceName}`);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const addToWorkspace = (workspaceDir, workspaceName, projects, cwd) => {
|
|
88
|
+
let added = 0;
|
|
89
|
+
|
|
90
|
+
projects.forEach((project) => {
|
|
91
|
+
const targetPath = path.join(workspaceDir, project);
|
|
92
|
+
if (fs.existsSync(targetPath)) {
|
|
93
|
+
console.log(`Already linked, skipping: ${project}`);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
createSymlink(path.join(cwd, project), targetPath);
|
|
97
|
+
console.log(`Linked project: ${project}`);
|
|
98
|
+
added++;
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const linked = linkInstructionFiles(workspaceDir, projects, cwd);
|
|
102
|
+
|
|
103
|
+
if (linked)
|
|
104
|
+
console.log(`Linked ${linked} instruction file(s) to workspace`);
|
|
105
|
+
if (!added) console.log("\nNo new projects to add");
|
|
106
|
+
else
|
|
107
|
+
console.log(
|
|
108
|
+
`\nAdded ${added} project(s) to workspace: ${workspaceName}`,
|
|
109
|
+
);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const removeFromWorkspace = (workspaceDir, workspaceName, projects) => {
|
|
113
|
+
const instructionsDir = path.join(workspaceDir, ".github", "instructions");
|
|
114
|
+
let removed = 0;
|
|
115
|
+
|
|
116
|
+
projects.forEach((project) => {
|
|
117
|
+
const targetPath = path.join(workspaceDir, project);
|
|
118
|
+
if (!fs.existsSync(targetPath)) {
|
|
119
|
+
console.warn(`Not linked, skipping: ${project}`);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
fs.unlinkSync(targetPath);
|
|
123
|
+
console.log(`Unlinked project: ${project}`);
|
|
124
|
+
removed++;
|
|
125
|
+
|
|
126
|
+
// Remove instruction file symlinks that point into this project
|
|
127
|
+
if (fs.existsSync(instructionsDir)) {
|
|
128
|
+
const projectInstructionsDir = path.join(
|
|
129
|
+
project,
|
|
130
|
+
".github",
|
|
131
|
+
"instructions",
|
|
132
|
+
);
|
|
133
|
+
fs.readdirSync(instructionsDir).forEach((file) => {
|
|
134
|
+
const filePath = path.join(instructionsDir, file);
|
|
135
|
+
const stat = fs.lstatSync(filePath);
|
|
136
|
+
if (!stat.isSymbolicLink()) return;
|
|
137
|
+
|
|
138
|
+
const linkTarget = path.resolve(
|
|
139
|
+
instructionsDir,
|
|
140
|
+
fs.readlinkSync(filePath),
|
|
141
|
+
);
|
|
142
|
+
if (linkTarget.includes(projectInstructionsDir)) {
|
|
143
|
+
fs.unlinkSync(filePath);
|
|
144
|
+
console.log(`Unlinked instruction file: ${file}`);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
if (!removed) console.log("\nNo projects to remove");
|
|
151
|
+
else
|
|
152
|
+
console.log(
|
|
153
|
+
`\nRemoved ${removed} project(s) from workspace: ${workspaceName}`,
|
|
154
|
+
);
|
|
155
|
+
};
|
|
156
|
+
|
|
31
157
|
module.exports = [
|
|
32
158
|
"workspace <projects..>",
|
|
33
|
-
"creates workspace directory with project symlinks and AI instruction files",
|
|
159
|
+
"creates or updates workspace directory with project symlinks and AI instruction files",
|
|
34
160
|
(yargs) => {
|
|
35
161
|
yargs
|
|
36
162
|
.positional("projects", {
|
|
@@ -43,82 +169,53 @@ module.exports = [
|
|
|
43
169
|
describe:
|
|
44
170
|
"custom workspace name (will be prefixed with 'workspace-')",
|
|
45
171
|
type: "string",
|
|
172
|
+
})
|
|
173
|
+
.option("delete", {
|
|
174
|
+
alias: "d",
|
|
175
|
+
describe: "remove the specified projects from the workspace",
|
|
176
|
+
type: "boolean",
|
|
177
|
+
default: false,
|
|
178
|
+
})
|
|
179
|
+
.check((argv) => {
|
|
180
|
+
if (argv.delete && !argv.name)
|
|
181
|
+
throw new Error(
|
|
182
|
+
"--name (-n) is required when using --delete (-d)",
|
|
183
|
+
);
|
|
184
|
+
return true;
|
|
46
185
|
});
|
|
47
186
|
},
|
|
48
187
|
(argv) => {
|
|
49
|
-
const { projects, name } = argv;
|
|
188
|
+
const { projects, name, delete: isDelete } = argv;
|
|
50
189
|
const cwd = process.cwd();
|
|
51
190
|
|
|
52
191
|
// Validation
|
|
53
192
|
if (!projects?.length)
|
|
54
193
|
exitWithError("At least one project name is required");
|
|
55
194
|
|
|
56
|
-
const missingProjects = projects.filter(
|
|
57
|
-
(project) => !fs.existsSync(path.join(cwd, project)),
|
|
58
|
-
);
|
|
59
|
-
if (missingProjects.length)
|
|
60
|
-
exitWithError(
|
|
61
|
-
`Project directories not found: ${missingProjects.join(", ")}`,
|
|
62
|
-
);
|
|
63
|
-
|
|
64
|
-
// Setup workspace
|
|
65
195
|
const workspaceName = generateWorkspaceName(projects, name);
|
|
66
196
|
const workspaceDir = path.join(cwd, workspaceName);
|
|
67
197
|
|
|
68
|
-
if (
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
fs.mkdirSync(workspaceDir);
|
|
74
|
-
console.log(`Created workspace directory: ${workspaceName}`);
|
|
75
|
-
|
|
76
|
-
// Create project symlinks
|
|
77
|
-
projects.forEach((project) => {
|
|
78
|
-
createSymlink(
|
|
79
|
-
path.join(cwd, project),
|
|
80
|
-
path.join(workspaceDir, project),
|
|
81
|
-
);
|
|
82
|
-
console.log(`Linked project: ${project}`);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
// Setup AI instructions
|
|
86
|
-
const instructionsDir = path.join(
|
|
87
|
-
workspaceDir,
|
|
88
|
-
".github",
|
|
89
|
-
"instructions",
|
|
90
|
-
);
|
|
91
|
-
fs.mkdirSync(instructionsDir, { recursive: true });
|
|
92
|
-
|
|
93
|
-
const instructionFiles = projects.flatMap((project) =>
|
|
94
|
-
glob.sync(`${project}/.github/instructions/*.md`, { cwd }),
|
|
95
|
-
);
|
|
198
|
+
if (isDelete) {
|
|
199
|
+
if (!fs.existsSync(workspaceDir))
|
|
200
|
+
exitWithError(
|
|
201
|
+
`Workspace directory '${workspaceName}' does not exist`,
|
|
202
|
+
);
|
|
96
203
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
path.join(cwd,
|
|
101
|
-
path.join(instructionsDir, fileName),
|
|
204
|
+
removeFromWorkspace(workspaceDir, workspaceName, projects);
|
|
205
|
+
} else {
|
|
206
|
+
const missingProjects = projects.filter(
|
|
207
|
+
(project) => !fs.existsSync(path.join(cwd, project)),
|
|
102
208
|
);
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
// Open in VS Code
|
|
115
|
-
try {
|
|
116
|
-
execSync("code .", { cwd: workspaceDir, stdio: "ignore" });
|
|
117
|
-
console.log(`\nOpened workspace in VS Code: ${workspaceName}`);
|
|
118
|
-
} catch {
|
|
119
|
-
// Silently ignore VS Code errors
|
|
209
|
+
if (missingProjects.length)
|
|
210
|
+
exitWithError(
|
|
211
|
+
`Project directories not found: ${missingProjects.join(", ")}`,
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
if (fs.existsSync(workspaceDir)) {
|
|
215
|
+
addToWorkspace(workspaceDir, workspaceName, projects, cwd);
|
|
216
|
+
} else {
|
|
217
|
+
createWorkspace(workspaceDir, workspaceName, projects, cwd);
|
|
218
|
+
}
|
|
120
219
|
}
|
|
121
|
-
|
|
122
|
-
console.log(`\nWorkspace setup complete: ${workspaceName}`);
|
|
123
220
|
},
|
|
124
221
|
];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fishawack/lab-env",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.5.0-beta.1",
|
|
4
4
|
"description": "Docker manager for FW",
|
|
5
5
|
"main": "cli.js",
|
|
6
6
|
"scripts": {
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"@aws-sdk/client-elastic-beanstalk": "^3.395.0",
|
|
27
27
|
"@aws-sdk/client-iam": "^3.150.0",
|
|
28
28
|
"@aws-sdk/client-s3": "^3.141.0",
|
|
29
|
+
"@aws-sdk/credential-providers": "^3.1011.0",
|
|
29
30
|
"apache-md5": "^1.1.8",
|
|
30
31
|
"axios": "^0.21.4",
|
|
31
32
|
"chalk": "4.1.0",
|
|
@@ -40,6 +41,7 @@
|
|
|
40
41
|
"lodash": "^4.17.21",
|
|
41
42
|
"nodemailer": "^6.9.15",
|
|
42
43
|
"ora": "5.4.1",
|
|
44
|
+
"p-limit": "^7.3.0",
|
|
43
45
|
"semver": "7.3.4",
|
|
44
46
|
"update-notifier": "^6.0.2",
|
|
45
47
|
"yargs": "16.2.0"
|
|
@@ -27,6 +27,19 @@ services:
|
|
|
27
27
|
- $CWD/:/workspace
|
|
28
28
|
- venv:/workspace/.venv
|
|
29
29
|
- $FW_DIR/.ssh:/home/py/.ssh
|
|
30
|
+
healthcheck:
|
|
31
|
+
test:
|
|
32
|
+
[
|
|
33
|
+
"CMD",
|
|
34
|
+
"wget",
|
|
35
|
+
"-q",
|
|
36
|
+
"--spider",
|
|
37
|
+
"http://localhost:${PORT_PY:-8000}/v1/health",
|
|
38
|
+
]
|
|
39
|
+
interval: 10s
|
|
40
|
+
timeout: 5s
|
|
41
|
+
retries: 5
|
|
42
|
+
start_period: 15s
|
|
30
43
|
networks:
|
|
31
44
|
default:
|
|
32
45
|
driver: "bridge"
|