@cocreate/cli 1.54.2 → 1.56.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/.github/workflows/automated.yml +23 -35
- package/CHANGELOG.md +40 -0
- package/CoCreate.config.js +0 -4
- package/package.json +26 -5
- package/release.config.js +11 -3
- package/src/addMeta.js +62 -71
- package/src/coc.js +182 -113
- package/src/commands/clone.js +77 -28
- package/src/commands/delete.js +70 -0
- package/src/commands/download.js +72 -0
- package/src/commands/gitignore.js +57 -0
- package/src/commands/install.js +2 -5
- package/src/commands/link.js +212 -77
- package/src/commands/{symlink.js → other/symlink.js} +1 -1
- package/src/commands/upload.js +123 -89
- package/src/config.template.js +106 -0
- package/src/execute.js +102 -59
- package/src/getConfig.js +79 -19
- package/src/getOS.js +22 -0
- package/src/getPackageManager.js +46 -0
- package/src/postinstall.js +129 -0
- package/src/commands/fs/gitignore.js +0 -32
- /package/src/commands/{symlink-crm.js → other/symlink-crm.js} +0 -0
package/src/coc.js
CHANGED
|
@@ -7,6 +7,7 @@ const execute = require("./execute");
|
|
|
7
7
|
const addMeta = require("./addMeta");
|
|
8
8
|
const { color } = require("./fonts");
|
|
9
9
|
const { getConfig } = require("./getConfig");
|
|
10
|
+
const fsp = require('fs').promises;
|
|
10
11
|
|
|
11
12
|
// Configuration object for storing options
|
|
12
13
|
let config = {};
|
|
@@ -16,140 +17,208 @@ const argv = process.argv.slice(2);
|
|
|
16
17
|
|
|
17
18
|
// Define available command-line options
|
|
18
19
|
const availableOptions = {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
"--self": { key: "self", type: "boolean" },
|
|
21
|
+
"-s": { key: "self", type: "boolean" },
|
|
22
|
+
"--directory": { key: "directory", type: "string" },
|
|
23
|
+
"-d": { key: "directory", type: "string" },
|
|
24
|
+
"--config": { key: "config", type: "string" },
|
|
25
|
+
"-c": { key: "config", type: "string" }
|
|
25
26
|
};
|
|
26
27
|
|
|
27
28
|
const options = {};
|
|
28
29
|
const commandParts = [];
|
|
29
30
|
for (let i = 0; i < argv.length; i++) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
31
|
+
let option = availableOptions[argv[i]];
|
|
32
|
+
if (option) {
|
|
33
|
+
if (option.type === "boolean") {
|
|
34
|
+
options[option.key] = true;
|
|
35
|
+
} else if (option.type === "string") {
|
|
36
|
+
options[option.key] = argv[i + 1];
|
|
37
|
+
argv.splice(i + 1, 1);
|
|
38
|
+
}
|
|
39
|
+
} else {
|
|
40
|
+
if (argv[i].match(/[\s'"]/)) {
|
|
41
|
+
commandParts.push(`'${argv[i].replace(/'/g, "\\'")}'`);
|
|
42
|
+
} else {
|
|
43
|
+
commandParts.push(argv[i]);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
command = commandParts.join(" ");
|
|
48
|
+
let command = commandParts.join(" ");
|
|
48
49
|
|
|
49
50
|
/**
|
|
50
51
|
* Main function to execute commands across repositories.
|
|
51
52
|
* @param {Object} config - The configuration object.
|
|
52
|
-
* @param {
|
|
53
|
-
* @param {string} [directory=null] - The directory path of the configuration.
|
|
53
|
+
* @param {Object} options - CLI options/flags.
|
|
54
54
|
*/
|
|
55
55
|
async function main(config = {}, options) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
56
|
+
let directory = options["directory"] || process.cwd();
|
|
57
|
+
|
|
58
|
+
// Check if any repository targets exist in the configuration
|
|
59
|
+
if (!config.repositories && !config.modules && !config.projects) {
|
|
60
|
+
let configString = options["config"];
|
|
61
|
+
if (configString) {
|
|
62
|
+
try {
|
|
63
|
+
config = JSON.parse(configString);
|
|
64
|
+
console.warn(
|
|
65
|
+
`${color.yellow}using supplied JSON configuration${color.reset}`
|
|
66
|
+
);
|
|
67
|
+
} catch (error) {
|
|
68
|
+
directory = configString;
|
|
69
|
+
config = await getConfig(directory);
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
config = await getConfig(directory);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!config.configPath) {
|
|
76
|
+
const packageJson = await getConfig(directory, "package.json");
|
|
77
|
+
if (packageJson.configPath) {
|
|
78
|
+
config.configPath = packageJson.configPath;
|
|
79
|
+
const repoUrl =
|
|
80
|
+
packageJson.repository && packageJson.repository.url.substring(12);
|
|
81
|
+
|
|
82
|
+
// Fallback to old format if reading strictly from an old package.json pointer
|
|
83
|
+
config.repositories = [
|
|
84
|
+
{
|
|
85
|
+
path: packageJson.configPath,
|
|
86
|
+
repo: repoUrl,
|
|
87
|
+
entry: packageJson.main
|
|
88
|
+
}
|
|
89
|
+
];
|
|
90
|
+
} else {
|
|
91
|
+
console.warn(
|
|
92
|
+
`${color.yellow}CoCreate.config.js not found. Generating a new one...${color.reset}`
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
const templatePath = path.resolve(__dirname, './config.template.js');
|
|
96
|
+
let templateConfig;
|
|
97
|
+
try {
|
|
98
|
+
templateConfig = require(templatePath);
|
|
99
|
+
} catch (e) {
|
|
100
|
+
console.error(`${color.red}Error loading config.template.js:`, e, color.reset);
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Customize the template with repository information from package.json
|
|
105
|
+
const currentRepoPath = '.'; // Current directory where config is generated
|
|
106
|
+
const repoUrl = packageJson.repository && packageJson.repository.url ? packageJson.repository.url.substring(packageJson.repository.url.indexOf('://') + 3) : '';
|
|
107
|
+
const projectName = packageJson.name || 'current';
|
|
108
|
+
|
|
109
|
+
// Use the new projects format for auto-generation
|
|
110
|
+
templateConfig.projects = {
|
|
111
|
+
[projectName]: {
|
|
112
|
+
path: currentRepoPath,
|
|
113
|
+
repo: repoUrl,
|
|
114
|
+
exclude: ["git"]
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// Set initial organization_id and key from environment or placeholders
|
|
119
|
+
templateConfig.organization_id = process.env.organization_id || "YOUR_ORGANIZATION_ID";
|
|
120
|
+
templateConfig.key = process.env.key || "YOUR_API_KEY";
|
|
121
|
+
|
|
122
|
+
// Resolve host domain if available from environment variable HOST_DOMAIN
|
|
123
|
+
if (process.env.host_domain) {
|
|
124
|
+
templateConfig.host = templateConfig.host || { $branch: {} };
|
|
125
|
+
templateConfig.host.$branch = templateConfig.host.$branch || {};
|
|
126
|
+
templateConfig.host.$branch.master = `wss://${process.env.host_domain}`;
|
|
127
|
+
templateConfig.host.$branch.dev = `wss://dev.${process.env.host_domain}`;
|
|
128
|
+
templateConfig.host.$branch.test = `wss://test.${process.env.host_domain}`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const newConfigPath = path.join(directory, 'CoCreate.config.js');
|
|
132
|
+
const configContent = `module.exports = ${JSON.stringify(templateConfig, null, 2)};`;
|
|
133
|
+
|
|
134
|
+
await fsp.writeFile(newConfigPath, configContent);
|
|
135
|
+
|
|
136
|
+
console.log(`${color.green}Generated CoCreate.config.js at ${newConfigPath}${color.reset}`);
|
|
137
|
+
|
|
138
|
+
// Use the newly generated template config
|
|
139
|
+
config = templateConfig;
|
|
140
|
+
config.configPath = newConfigPath; // Ensure configPath is set to the new file
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
directory = path.dirname(config.configPath);
|
|
146
|
+
console.warn(
|
|
147
|
+
`${color.yellow}using config ${config.configPath} ${color.reset}`
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
config.hideMessage = false;
|
|
151
|
+
|
|
152
|
+
// Add metadata to traditional repositories
|
|
153
|
+
if (config.repositories && config.repositories.length) {
|
|
154
|
+
config.repositories = await addMeta(config.repositories, [], directory);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Add metadata to modules
|
|
158
|
+
if (config.modules) {
|
|
159
|
+
const moduleRepos = Object.values(config.modules).filter(r => typeof r === 'object' && r !== null && r.path);
|
|
160
|
+
if (moduleRepos.length) {
|
|
161
|
+
await addMeta(moduleRepos, [], directory);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Add metadata to projects
|
|
166
|
+
if (config.projects) {
|
|
167
|
+
const projectRepos = Object.values(config.projects).filter(r => typeof r === 'object' && r !== null && r.path);
|
|
168
|
+
if (projectRepos.length) {
|
|
169
|
+
await addMeta(projectRepos, [], directory);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Execute the command across targeted repositories/modules/projects
|
|
174
|
+
const failed = await execute(command, config);
|
|
175
|
+
|
|
176
|
+
// Handle any failed command executions
|
|
177
|
+
if (failed && failed.length > 0) {
|
|
178
|
+
console.log(
|
|
179
|
+
color.red +
|
|
180
|
+
"\n **************** failures **************** " +
|
|
181
|
+
color.reset
|
|
182
|
+
);
|
|
183
|
+
for (const failure of failed) {
|
|
184
|
+
console.log(
|
|
185
|
+
color.red + `${failure.name || failure.path}: ${failure.error}` + color.reset
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Prompt user to retry failed commands
|
|
190
|
+
await promptRetry(failed, config, options);
|
|
191
|
+
}
|
|
126
192
|
}
|
|
127
193
|
|
|
128
194
|
/**
|
|
129
195
|
* Prompt the user to retry failed commands.
|
|
130
196
|
* @param {Array} failed - List of failed commands.
|
|
131
197
|
* @param {Object} config - Configuration object.
|
|
132
|
-
* @param {
|
|
198
|
+
* @param {Object} options - CLI options.
|
|
133
199
|
*/
|
|
134
200
|
async function promptRetry(failed, config, options) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
201
|
+
const rl = readline.createInterface({
|
|
202
|
+
input: process.stdin,
|
|
203
|
+
output: process.stdout
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
rl.question(
|
|
207
|
+
"Do you want to retry the failed commands? (yes/no): ",
|
|
208
|
+
async (answer) => {
|
|
209
|
+
rl.close();
|
|
210
|
+
if (answer.toLowerCase() === "yes" || answer.toLowerCase() === "y") {
|
|
211
|
+
// Ensure we only retry the failed ones by clearing the existing modules/projects
|
|
212
|
+
config.repositories = failed;
|
|
213
|
+
delete config.modules;
|
|
214
|
+
delete config.projects;
|
|
215
|
+
await main(config, options);
|
|
216
|
+
} else {
|
|
217
|
+
process.exit(0);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
);
|
|
152
221
|
}
|
|
153
222
|
|
|
154
223
|
// Call the main function with initial configuration
|
|
155
|
-
main(config, options);
|
|
224
|
+
main(config, options);
|
package/src/commands/clone.js
CHANGED
|
@@ -1,32 +1,81 @@
|
|
|
1
1
|
const spawn = require("../spawn");
|
|
2
2
|
const path = require("path");
|
|
3
|
-
|
|
3
|
+
const fs = require("fs");
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* CLI-compatible clone command that resolves paths dynamically,
|
|
7
|
+
* clones missing repositories, and silently skips already cloned ones.
|
|
8
|
+
*/
|
|
5
9
|
module.exports = async function gitClone(repos, args) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
10
|
+
const failed = [];
|
|
11
|
+
const cwdPath = path.resolve(process.cwd());
|
|
12
|
+
|
|
13
|
+
for (let i = 0; i < repos.length; i++) {
|
|
14
|
+
const repo = repos[i];
|
|
15
|
+
if (!repo) continue;
|
|
16
|
+
|
|
17
|
+
const rawPath = repo.path || repo.absolutePath || repo.directory;
|
|
18
|
+
if (!rawPath) {
|
|
19
|
+
failed.push({
|
|
20
|
+
name: repo.name || `Repo #${i}`,
|
|
21
|
+
error: "No path property defined in the configuration"
|
|
22
|
+
});
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
repo.absolutePath = repo.absolutePath || path.resolve(cwdPath, rawPath);
|
|
27
|
+
repo.directory = repo.directory || path.dirname(repo.absolutePath);
|
|
28
|
+
repo.name = repo.name || path.basename(repo.absolutePath);
|
|
29
|
+
|
|
30
|
+
if (cwdPath === repo.absolutePath) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const relativeRepoPath = path.relative(cwdPath, repo.absolutePath);
|
|
35
|
+
|
|
36
|
+
// Clean skip logging using path
|
|
37
|
+
if (fs.existsSync(repo.absolutePath)) {
|
|
38
|
+
console.log(`Already exists: ${relativeRepoPath} (Skipped)`);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!fs.existsSync(repo.directory)) {
|
|
43
|
+
try {
|
|
44
|
+
fs.mkdirSync(repo.directory, { recursive: true });
|
|
45
|
+
} catch (err) {
|
|
46
|
+
failed.push({
|
|
47
|
+
name: repo.name,
|
|
48
|
+
error: `Failed to create directory ${repo.directory}: ${err.message}`
|
|
49
|
+
});
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (!repo.repo) {
|
|
55
|
+
failed.push({
|
|
56
|
+
name: repo.name,
|
|
57
|
+
error: "No git repository URL provided in config"
|
|
58
|
+
});
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let gitUrl = repo.repo;
|
|
63
|
+
if (!gitUrl.startsWith("http://") && !gitUrl.startsWith("https://")) {
|
|
64
|
+
gitUrl = "https://" + gitUrl;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Clean success logging after process completes successfully
|
|
68
|
+
let exitCode = await spawn("git", ["clone", gitUrl], {
|
|
69
|
+
stdio: "inherit",
|
|
70
|
+
cwd: repo.directory
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
if (exitCode !== 0) {
|
|
74
|
+
failed.push({ name: repo.name, error: `cloning failed` });
|
|
75
|
+
} else {
|
|
76
|
+
console.log(`Successfully cloned: ${relativeRepoPath}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return failed;
|
|
81
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const file = require("@cocreate/file");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const { getConfig } = require("../getConfig");
|
|
5
|
+
|
|
6
|
+
module.exports = async function del(directory, args) {
|
|
7
|
+
if (args && !Array.isArray(args)) args = [args];
|
|
8
|
+
|
|
9
|
+
directory = process.cwd();
|
|
10
|
+
|
|
11
|
+
// Process targets
|
|
12
|
+
if (!args || !args.length) {
|
|
13
|
+
const CoCreateConfig = await getConfig(directory);
|
|
14
|
+
if (CoCreateConfig && CoCreateConfig.configPath) {
|
|
15
|
+
const projectRoot = path.dirname(CoCreateConfig.configPath);
|
|
16
|
+
let rel = path.relative(projectRoot, process.cwd()).replace(/\\/g, '/');
|
|
17
|
+
|
|
18
|
+
// In the DB, 'src' is the root, so we must remove 'src' from the relative path
|
|
19
|
+
if (rel === 'src') {
|
|
20
|
+
rel = '';
|
|
21
|
+
} else if (rel.startsWith('src/')) {
|
|
22
|
+
rel = rel.substring(4);
|
|
23
|
+
} else if (rel === 'src') {
|
|
24
|
+
rel = '';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let target = '/' + rel.replace(/^\/+/, '');
|
|
28
|
+
|
|
29
|
+
await confirmAndDelete(CoCreateConfig, target);
|
|
30
|
+
} else {
|
|
31
|
+
console.log("Failed to read or parse CoCreate.config.js.");
|
|
32
|
+
}
|
|
33
|
+
} else {
|
|
34
|
+
// When explicit args are given, resolve config but do NOT change CWD
|
|
35
|
+
const CoCreateConfig = await getConfig(directory);
|
|
36
|
+
if (!CoCreateConfig || !CoCreateConfig.configPath) {
|
|
37
|
+
console.log("Failed to read or parse CoCreate.config.js for authentication.");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
for (let arg of args) {
|
|
42
|
+
let targetPath = String(arg).replace(/\\/g, '/');
|
|
43
|
+
|
|
44
|
+
// Normalize to DB path format
|
|
45
|
+
if (!targetPath.startsWith('/')) {
|
|
46
|
+
targetPath = '/' + targetPath.replace(/^\/+/,'');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
await confirmAndDelete(CoCreateConfig, targetPath);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function confirmAndDelete(CoCreateConfig, target) {
|
|
54
|
+
try {
|
|
55
|
+
if (typeof file.delete !== 'function') {
|
|
56
|
+
console.error('Delete not supported: @cocreate/file exposes no delete API.');
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const res = await file.delete(
|
|
61
|
+
CoCreateConfig,
|
|
62
|
+
CoCreateConfig.configPath,
|
|
63
|
+
target
|
|
64
|
+
);
|
|
65
|
+
console.log('Deleted:', (res && res.length) ? res.length : res, 'items for', target);
|
|
66
|
+
} catch (err) {
|
|
67
|
+
console.error('Delete failed for', target, err);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
const file = require("@cocreate/file");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const { getConfig } = require("../getConfig");
|
|
5
|
+
|
|
6
|
+
module.exports = async function download(directory, args) {
|
|
7
|
+
if (args && !Array.isArray(args)) args = [args];
|
|
8
|
+
|
|
9
|
+
directory = process.cwd();
|
|
10
|
+
|
|
11
|
+
// Process targets
|
|
12
|
+
if (!args || !args.length) {
|
|
13
|
+
const CoCreateConfig = await getConfig(directory);
|
|
14
|
+
if (CoCreateConfig && CoCreateConfig.configPath) {
|
|
15
|
+
const projectRoot = path.dirname(CoCreateConfig.configPath);
|
|
16
|
+
let rel = path.relative(projectRoot, process.cwd()).replace(/\\/g, '/');
|
|
17
|
+
|
|
18
|
+
// In the DB, 'src' is the root, so we must remove 'src' from the relative path
|
|
19
|
+
if (rel === 'src') {
|
|
20
|
+
rel = '';
|
|
21
|
+
} else if (rel.startsWith('src/')) {
|
|
22
|
+
rel = rel.substring(4);
|
|
23
|
+
} else if (rel === 'src') {
|
|
24
|
+
rel = '';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let target = '/' + rel.replace(/^\/+/, '');
|
|
28
|
+
|
|
29
|
+
await fetchAndWrite(CoCreateConfig, target);
|
|
30
|
+
} else {
|
|
31
|
+
console.log("Failed to read or parse CoCreate.config.js.");
|
|
32
|
+
}
|
|
33
|
+
} else {
|
|
34
|
+
// When an explicit argument is given, resolve config but do NOT change CWD
|
|
35
|
+
// so that the files are downloaded into the folder output was initiated from.
|
|
36
|
+
const CoCreateConfig = await getConfig(directory);
|
|
37
|
+
if (!CoCreateConfig || !CoCreateConfig.configPath) {
|
|
38
|
+
console.log("Failed to read or parse CoCreate.config.js for authentication.");
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
for (let arg of args) {
|
|
43
|
+
let targetPath = String(arg).replace(/\\/g, '/');
|
|
44
|
+
|
|
45
|
+
// Just use the requested string directly as the DB path
|
|
46
|
+
if (!targetPath.startsWith('/')) {
|
|
47
|
+
targetPath = '/' + targetPath.replace(/^\/+/, '');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
await fetchAndWrite(CoCreateConfig, targetPath);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function fetchAndWrite(CoCreateConfig, target) {
|
|
55
|
+
try {
|
|
56
|
+
if (typeof file.download !== 'function') {
|
|
57
|
+
console.error('Download not supported: @cocreate/file exposes no download API.');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const downloaded = await file.download(
|
|
62
|
+
CoCreateConfig,
|
|
63
|
+
CoCreateConfig.configPath,
|
|
64
|
+
target
|
|
65
|
+
);
|
|
66
|
+
console.log('Downloaded:', downloaded && downloaded.length ? downloaded.length : 0, 'files for', target);
|
|
67
|
+
} catch (err) {
|
|
68
|
+
console.error('Download failed for', target, err);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Updates .gitignore files across multiple repositories with descriptive path logging.
|
|
6
|
+
*/
|
|
7
|
+
module.exports = async function updateGitignore(repos, args) {
|
|
8
|
+
const failed = [];
|
|
9
|
+
const cwd = process.cwd();
|
|
10
|
+
const templatePath = path.join(cwd, ".gitignore");
|
|
11
|
+
|
|
12
|
+
if (!fs.existsSync(templatePath)) {
|
|
13
|
+
throw new Error(`No .gitignore template found in: ${cwd}`);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let templateContent;
|
|
17
|
+
try {
|
|
18
|
+
templateContent = fs.readFileSync(templatePath, "utf8");
|
|
19
|
+
} catch (error) {
|
|
20
|
+
throw new Error(`Failed to read the template file: ${error.message}`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
for (let i = 0; i < repos.length; i++) {
|
|
24
|
+
const repo = repos[i];
|
|
25
|
+
if (!repo) continue;
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const rawPath = repo.absolutePath || repo.directory || repo.path;
|
|
29
|
+
if (!rawPath) continue;
|
|
30
|
+
|
|
31
|
+
const resolvedPath = path.resolve(cwd, rawPath);
|
|
32
|
+
|
|
33
|
+
if (!fs.existsSync(resolvedPath)) continue;
|
|
34
|
+
if (path.resolve(resolvedPath) === path.resolve(cwd)) continue;
|
|
35
|
+
|
|
36
|
+
const gitignorePath = path.join(resolvedPath, ".gitignore");
|
|
37
|
+
|
|
38
|
+
if (fs.existsSync(gitignorePath)) {
|
|
39
|
+
fs.unlinkSync(gitignorePath);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
fs.writeFileSync(gitignorePath, templateContent, "utf8");
|
|
43
|
+
|
|
44
|
+
// Log the clean relative path from where the command was executed
|
|
45
|
+
const relativeLogPath = path.relative(cwd, gitignorePath);
|
|
46
|
+
console.log(`Successfully updated: ${relativeLogPath}`);
|
|
47
|
+
|
|
48
|
+
} catch (error) {
|
|
49
|
+
failed.push({
|
|
50
|
+
name: repo.name || `Repo #${i}`,
|
|
51
|
+
error: error.message
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return failed;
|
|
57
|
+
};
|
package/src/commands/install.js
CHANGED
|
@@ -4,15 +4,12 @@ module.exports = async function (repos, args) {
|
|
|
4
4
|
let failed = [];
|
|
5
5
|
try {
|
|
6
6
|
let cloneFailed = await require("./clone.js")(repos, args);
|
|
7
|
-
if (cloneFailed) failed.push(cloneFailed);
|
|
7
|
+
if (cloneFailed && cloneFailed.length) failed.push(...cloneFailed);
|
|
8
8
|
|
|
9
9
|
repos = await addMeta(repos, failed);
|
|
10
10
|
|
|
11
|
-
let symlinkFailed = await require("./symlink.js")(repos, args);
|
|
12
|
-
if (symlinkFailed) failed.push(symlinkFailed);
|
|
13
|
-
|
|
14
11
|
let linkFailed = await require("./link.js")(repos, args);
|
|
15
|
-
if (linkFailed) failed.push(linkFailed);
|
|
12
|
+
if (linkFailed && linkFailed.length) failed.push(...linkFailed);
|
|
16
13
|
} catch (err) {
|
|
17
14
|
console.error(err);
|
|
18
15
|
failed.push({ name: "general", error: err.message });
|