@wpmoo/odoo 0.8.58 → 0.8.60
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/README.md +61 -6
- package/dist/args.js +1 -0
- package/dist/cli.js +83 -2
- package/dist/doctor.js +156 -5
- package/dist/environment.js +79 -4
- package/dist/help.js +10 -2
- package/dist/repo-actions.js +74 -22
- package/dist/safe-reset.js +88 -21
- package/dist/scaffold.js +12 -3
- package/dist/source-actions.js +42 -0
- package/dist/source-manifest.js +338 -0
- package/dist/templates.js +15 -6
- package/docs/generated-environment-verification.md +49 -1
- package/package.json +1 -1
package/dist/repo-actions.js
CHANGED
|
@@ -1,17 +1,54 @@
|
|
|
1
1
|
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { addSourceRepoToAddonsYaml, removeSourceRepoFromAddonsYaml } from './addons-yaml.js';
|
|
4
|
-
import { readEnvironmentMetadata } from './environment.js';
|
|
4
|
+
import { readEnvironmentMetadata, removeSourceRepoMetadata, upsertSourceRepoMetadata } from './environment.js';
|
|
5
5
|
import { ensureRemoteHasBranch, ensureSubmodule, hasUncommittedChanges, realGit, removeSubmodule, stageAll, } from './git.js';
|
|
6
6
|
import { isValidPathSegment, validateRepoPath } from './path-validation.js';
|
|
7
7
|
import { inferRepoPath } from './repo-url.js';
|
|
8
|
+
import { removeSourceManifestEntry, upsertSourceManifestEntry } from './source-manifest.js';
|
|
8
9
|
export const addonsYamlHeader = `# Addons activated from source submodules.
|
|
9
10
|
#
|
|
10
|
-
# Source repos are managed as Git submodules under odoo/custom/src/private.
|
|
11
|
+
# Source repos are managed as Git submodules under odoo/custom/src/private (product code).
|
|
12
|
+
# OCA/external source repos can be placed under odoo/custom/src/oca and odoo/custom/src/external.
|
|
11
13
|
# Do not duplicate these same repos in repos.yaml.
|
|
12
14
|
`;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
const validSourceTypes = ['private', 'oca', 'external'];
|
|
16
|
+
function normalizeSourceType(value) {
|
|
17
|
+
return validSourceTypes.includes(value) ? value : 'private';
|
|
18
|
+
}
|
|
19
|
+
function sourceSubmodulePath(sourceType, repoPath) {
|
|
20
|
+
return `odoo/custom/src/${sourceType}/${validateRepoPath(repoPath)}`;
|
|
21
|
+
}
|
|
22
|
+
function resolveSourceTypeFromSubmodulePath(submodulePath) {
|
|
23
|
+
const match = /^odoo\/custom\/src\/(private|oca|external)\//.exec(submodulePath);
|
|
24
|
+
if (!match)
|
|
25
|
+
return undefined;
|
|
26
|
+
return match[1];
|
|
27
|
+
}
|
|
28
|
+
async function listGitmoduleRepos(target) {
|
|
29
|
+
try {
|
|
30
|
+
const gitmodules = await readFile(join(target, '.gitmodules'), 'utf8');
|
|
31
|
+
return [...gitmodules.matchAll(/^\s*path\s*=\s*odoo\/custom\/src\/(private|oca|external)\/(.+)$/gm)]
|
|
32
|
+
.map((match) => ({ sourceType: match[1], path: match[2].trim() }))
|
|
33
|
+
.filter((entry) => isValidPathSegment(entry.path));
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async function resolveSubmodulePathFromConfig(target, repoPath, sourceType) {
|
|
40
|
+
if (sourceType) {
|
|
41
|
+
return sourceSubmodulePath(sourceType, validateRepoPath(repoPath));
|
|
42
|
+
}
|
|
43
|
+
const repoMatches = (await listGitmoduleRepos(target)).filter((repo) => repo.path === repoPath);
|
|
44
|
+
if (repoMatches.length === 1) {
|
|
45
|
+
return sourceSubmodulePath(repoMatches[0].sourceType, repoPath);
|
|
46
|
+
}
|
|
47
|
+
if (repoMatches.length > 1) {
|
|
48
|
+
const sorted = repoMatches.map((repo) => repo.sourceType).sort();
|
|
49
|
+
throw new Error(`Source repo ${repoPath} exists in multiple source directories: ${sorted.join(', ')}. Provide --source-type to disambiguate.`);
|
|
50
|
+
}
|
|
51
|
+
return sourceSubmodulePath('private', repoPath);
|
|
15
52
|
}
|
|
16
53
|
export async function readAddonsYaml(target) {
|
|
17
54
|
try {
|
|
@@ -55,20 +92,36 @@ export async function syncComposeOdooConfAddonsPath(target) {
|
|
|
55
92
|
}
|
|
56
93
|
export async function addModuleRepo(options, git = realGit) {
|
|
57
94
|
const repoPath = validateRepoPath(options.repoPath?.trim() || inferRepoPath(options.repoUrl));
|
|
58
|
-
const
|
|
95
|
+
const sourceType = normalizeSourceType(options.sourceType);
|
|
96
|
+
const submodulePath = sourceSubmodulePath(sourceType, repoPath);
|
|
59
97
|
await ensureRemoteHasBranch(git, options.target, options.repoUrl, options.odooVersion, options.initEmptyRepos);
|
|
60
|
-
await mkdir(join(options.target, 'odoo/custom/src
|
|
98
|
+
await mkdir(join(options.target, 'odoo/custom/src', sourceType), { recursive: true });
|
|
61
99
|
await ensureSubmodule(git, options.target, options.repoUrl, options.odooVersion, submodulePath);
|
|
62
100
|
const listedRepos = await listModuleRepos(options.target);
|
|
63
101
|
if (!listedRepos.includes(repoPath)) {
|
|
64
102
|
throw new Error(`Source repo was added but is not registered in .gitmodules: ${repoPath}`);
|
|
65
103
|
}
|
|
104
|
+
await upsertSourceRepoMetadata(options.target, {
|
|
105
|
+
url: options.repoUrl,
|
|
106
|
+
path: repoPath,
|
|
107
|
+
addons: [repoPath],
|
|
108
|
+
sourceType,
|
|
109
|
+
});
|
|
110
|
+
await upsertSourceManifestEntry(options.target, {
|
|
111
|
+
type: sourceType,
|
|
112
|
+
path: repoPath,
|
|
113
|
+
url: options.repoUrl,
|
|
114
|
+
branch: options.odooVersion,
|
|
115
|
+
addons: [repoPath],
|
|
116
|
+
});
|
|
66
117
|
if (!(await isComposeEnvironment(options.target))) {
|
|
67
118
|
const addonsYaml = await readAddonsYaml(options.target);
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
119
|
+
if (sourceType === 'private') {
|
|
120
|
+
await writeAddonsYaml(options.target, addSourceRepoToAddonsYaml(addonsYaml, {
|
|
121
|
+
path: repoPath,
|
|
122
|
+
addons: [repoPath],
|
|
123
|
+
}));
|
|
124
|
+
}
|
|
72
125
|
}
|
|
73
126
|
await syncComposeOdooConfAddonsPath(options.target);
|
|
74
127
|
if (options.stage) {
|
|
@@ -76,28 +129,27 @@ export async function addModuleRepo(options, git = realGit) {
|
|
|
76
129
|
}
|
|
77
130
|
}
|
|
78
131
|
export async function listModuleRepos(target) {
|
|
79
|
-
|
|
80
|
-
const gitmodules = await readFile(join(target, '.gitmodules'), 'utf8');
|
|
81
|
-
return [...gitmodules.matchAll(/^\s*path\s*=\s*odoo\/custom\/src\/private\/(.+)$/gm)]
|
|
82
|
-
.map((match) => match[1].trim())
|
|
83
|
-
.filter((repoPath) => repoPath && isValidPathSegment(repoPath))
|
|
84
|
-
.sort();
|
|
85
|
-
}
|
|
86
|
-
catch {
|
|
87
|
-
return [];
|
|
88
|
-
}
|
|
132
|
+
return (await listGitmoduleRepos(target)).map((repo) => repo.path).sort();
|
|
89
133
|
}
|
|
90
134
|
export async function removeModuleRepo(options, git = realGit) {
|
|
91
135
|
const repoPath = validateRepoPath(options.repoPath);
|
|
92
|
-
const
|
|
136
|
+
const sourceType = options.sourceType ? normalizeSourceType(options.sourceType) : undefined;
|
|
137
|
+
const submodulePath = await resolveSubmodulePathFromConfig(options.target, repoPath, sourceType);
|
|
93
138
|
const fullSubmodulePath = join(options.target, submodulePath);
|
|
139
|
+
const resolvedSourceType = sourceType ?? resolveSourceTypeFromSubmodulePath(submodulePath);
|
|
94
140
|
if (await hasUncommittedChanges(git, fullSubmodulePath)) {
|
|
95
141
|
throw new Error(`Cannot remove ${repoPath}: submodule has uncommitted changes.`);
|
|
96
142
|
}
|
|
97
143
|
await removeSubmodule(git, options.target, submodulePath);
|
|
144
|
+
await removeSourceRepoMetadata(options.target, repoPath, resolvedSourceType);
|
|
145
|
+
if (resolvedSourceType) {
|
|
146
|
+
await removeSourceManifestEntry(options.target, resolvedSourceType, repoPath);
|
|
147
|
+
}
|
|
98
148
|
if (!(await isComposeEnvironment(options.target))) {
|
|
99
149
|
const addonsYaml = await readAddonsYaml(options.target);
|
|
100
|
-
|
|
150
|
+
if (resolvedSourceType === 'private') {
|
|
151
|
+
await writeAddonsYaml(options.target, removeSourceRepoFromAddonsYaml(addonsYaml, repoPath));
|
|
152
|
+
}
|
|
101
153
|
}
|
|
102
154
|
await syncComposeOdooConfAddonsPath(options.target);
|
|
103
155
|
if (options.stage) {
|
package/dist/safe-reset.js
CHANGED
|
@@ -1,12 +1,46 @@
|
|
|
1
|
-
import { chmod, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
1
|
+
import { chmod, mkdir, readFile, stat, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { basename, join } from 'node:path';
|
|
3
|
-
import { readEnvironmentMetadata } from './environment.js';
|
|
3
|
+
import { environmentMetadata, readEnvironmentMetadata } from './environment.js';
|
|
4
4
|
import { applyExternalAsset, writeTextFile } from './external-assets.js';
|
|
5
5
|
import { plannedExternalAssetOptions, renderComposeEnvExample } from './external-templates.js';
|
|
6
6
|
import { realGit, stageAll } from './git.js';
|
|
7
7
|
import { isValidPathSegment, validateAddonName, validateRepoPath } from './path-validation.js';
|
|
8
|
-
import {
|
|
8
|
+
import { readAddonsYaml } from './repo-actions.js';
|
|
9
9
|
import { generatedFiles } from './scaffold.js';
|
|
10
|
+
import { listGitmoduleSources } from './source-manifest.js';
|
|
11
|
+
const safeResetProtectedPaths = [
|
|
12
|
+
'data',
|
|
13
|
+
'backups',
|
|
14
|
+
'.env',
|
|
15
|
+
'.gitmodules',
|
|
16
|
+
'odoo/custom/src/private',
|
|
17
|
+
'odoo/custom/src/oca',
|
|
18
|
+
'odoo/custom/src/external',
|
|
19
|
+
'odoo/custom/patches',
|
|
20
|
+
'odoo/custom/manifests',
|
|
21
|
+
].map((path) => path.replace(/\/$/, ''));
|
|
22
|
+
const safeResetProtectedGeneratedReadmes = new Set([
|
|
23
|
+
'odoo/custom/src/private/README.md',
|
|
24
|
+
'odoo/custom/src/oca/README.md',
|
|
25
|
+
'odoo/custom/src/external/README.md',
|
|
26
|
+
'odoo/custom/patches/README.md',
|
|
27
|
+
'odoo/custom/manifests/README.md',
|
|
28
|
+
]);
|
|
29
|
+
function isProtectedGeneratedFile(filePath) {
|
|
30
|
+
return safeResetProtectedGeneratedReadmes.has(filePath);
|
|
31
|
+
}
|
|
32
|
+
function mergeEnvironmentMetadata(target, options) {
|
|
33
|
+
const generated = environmentMetadata(options);
|
|
34
|
+
return readFile(join(target, '.wpmoo/odoo.json'), 'utf8')
|
|
35
|
+
.then((content) => JSON.parse(content))
|
|
36
|
+
.then((existing) => {
|
|
37
|
+
if (!existing || typeof existing !== 'object' || Array.isArray(existing)) {
|
|
38
|
+
return `${JSON.stringify(generated, null, 2)}\n`;
|
|
39
|
+
}
|
|
40
|
+
return `${JSON.stringify({ ...existing, ...generated, sourceRepos: generated.sourceRepos }, null, 2)}\n`;
|
|
41
|
+
})
|
|
42
|
+
.catch(() => `${JSON.stringify(generated, null, 2)}\n`);
|
|
43
|
+
}
|
|
10
44
|
export function renderSafeResetPreview(target, stage) {
|
|
11
45
|
return [
|
|
12
46
|
'Safe reset will refresh generated WPMoo environment files.',
|
|
@@ -29,8 +63,12 @@ export function renderSafeResetPreview(target, stage) {
|
|
|
29
63
|
'- source repo folders under odoo/custom/src/private',
|
|
30
64
|
'- module source code',
|
|
31
65
|
'- Git history, remotes, or branches',
|
|
66
|
+
'- .env, data, and backups',
|
|
67
|
+
'- custom source layout directories (oca, external, patches, manifests)',
|
|
32
68
|
'- Legacy compose template files may remain until manually removed: docs/assets/, test/, .github/',
|
|
33
69
|
'',
|
|
70
|
+
'Preview-only output; files are not changed until reset is executed.',
|
|
71
|
+
'',
|
|
34
72
|
stage ? 'Generated changes will be staged with git add .' : 'Generated changes will not be staged.',
|
|
35
73
|
].join('\n');
|
|
36
74
|
}
|
|
@@ -42,9 +80,7 @@ function safeResetExternalAssetOptions(options) {
|
|
|
42
80
|
...assetOptions,
|
|
43
81
|
exclude: [
|
|
44
82
|
...(assetOptions.exclude ?? []),
|
|
45
|
-
|
|
46
|
-
'.gitmodules',
|
|
47
|
-
'odoo/custom/src/private',
|
|
83
|
+
...safeResetProtectedPaths,
|
|
48
84
|
],
|
|
49
85
|
}));
|
|
50
86
|
}
|
|
@@ -74,35 +110,59 @@ function parseRepoPathsFromAddonsYaml(addonsYaml) {
|
|
|
74
110
|
.filter((repoPath) => repoPath && isValidPathSegment(repoPath))
|
|
75
111
|
.map(validateRepoPath);
|
|
76
112
|
}
|
|
77
|
-
async function readSubmoduleUrl(target, repoPath) {
|
|
113
|
+
async function readSubmoduleUrl(target, repoPath, sourceType) {
|
|
78
114
|
const safeRepoPath = validateRepoPath(repoPath);
|
|
79
115
|
try {
|
|
80
116
|
const gitmodules = await readFile(join(target, '.gitmodules'), 'utf8');
|
|
81
|
-
const escapedPath = `odoo/custom/src
|
|
117
|
+
const escapedPath = `odoo/custom/src/${sourceType}/${safeRepoPath}`;
|
|
82
118
|
const sections = gitmodules.split(/\n(?=\[submodule )/);
|
|
83
119
|
const section = sections.find((value) => value.includes(`path = ${escapedPath}`));
|
|
84
120
|
const url = section?.match(/^\s*url\s*=\s*(.+)$/m)?.[1]?.trim();
|
|
85
|
-
return url || `odoo/custom/src
|
|
121
|
+
return url || `odoo/custom/src/${sourceType}/${safeRepoPath}`;
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return `odoo/custom/src/${sourceType}/${safeRepoPath}`;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
async function pathExists(path) {
|
|
128
|
+
try {
|
|
129
|
+
await stat(path);
|
|
130
|
+
return true;
|
|
86
131
|
}
|
|
87
132
|
catch {
|
|
88
|
-
return
|
|
133
|
+
return false;
|
|
89
134
|
}
|
|
90
135
|
}
|
|
91
136
|
async function inferOptions(target) {
|
|
92
137
|
const metadata = await readEnvironmentMetadata(target);
|
|
93
138
|
const addonsYaml = await readAddonsYaml(target);
|
|
94
|
-
const
|
|
139
|
+
const gitmoduleSources = await listGitmoduleSources(target);
|
|
95
140
|
const addonRepos = parseRepoPathsFromAddonsYaml(addonsYaml);
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
141
|
+
const sourceByKey = new Map();
|
|
142
|
+
for (const repo of metadata?.sourceRepos ?? []) {
|
|
143
|
+
if (isValidPathSegment(repo.path)) {
|
|
144
|
+
const sourceType = repo.sourceType ?? 'private';
|
|
145
|
+
const path = validateRepoPath(repo.path);
|
|
146
|
+
sourceByKey.set(`${sourceType}:${path}`, { sourceType, path });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
for (const repo of gitmoduleSources) {
|
|
150
|
+
sourceByKey.set(`${repo.type}:${repo.path}`, { sourceType: repo.type, path: repo.path });
|
|
151
|
+
}
|
|
152
|
+
for (const repoPath of addonRepos) {
|
|
153
|
+
sourceByKey.set(`private:${repoPath}`, { sourceType: 'private', path: repoPath });
|
|
154
|
+
}
|
|
155
|
+
const sourceLocations = [...sourceByKey.values()];
|
|
156
|
+
const product = metadata?.product ?? sourceLocations[0]?.path ?? titleFromTarget(target);
|
|
157
|
+
const sourceRepos = await Promise.all(sourceLocations.map(async ({ sourceType, path }) => ({
|
|
158
|
+
path,
|
|
159
|
+
sourceType,
|
|
160
|
+
url: metadata?.sourceRepos
|
|
161
|
+
.find((repo) => repo.path === path && (repo.sourceType ?? 'private') === sourceType)
|
|
162
|
+
?.url.trim() ||
|
|
163
|
+
gitmoduleSources.find((repo) => repo.path === path && repo.type === sourceType)?.url ||
|
|
164
|
+
(await readSubmoduleUrl(target, path, sourceType)),
|
|
165
|
+
addons: parseAddonsForRepo(addonsYaml, path),
|
|
106
166
|
})));
|
|
107
167
|
return {
|
|
108
168
|
product,
|
|
@@ -130,6 +190,12 @@ export async function safeResetEnvironment(options, git = realGit) {
|
|
|
130
190
|
const files = generatedFiles(scaffoldOptions);
|
|
131
191
|
const externalAssets = safeResetExternalAssetOptions(scaffoldOptions);
|
|
132
192
|
for (const file of files) {
|
|
193
|
+
if (file.path === '.wpmoo/odoo.json') {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (isProtectedGeneratedFile(file.path) && (await pathExists(join(options.target, file.path)))) {
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
133
199
|
if (file.path === 'odoo/custom/src/addons.yaml') {
|
|
134
200
|
continue;
|
|
135
201
|
}
|
|
@@ -143,6 +209,7 @@ export async function safeResetEnvironment(options, git = realGit) {
|
|
|
143
209
|
for (const assetOptions of externalAssets) {
|
|
144
210
|
await applyExternalAsset(assetOptions, git);
|
|
145
211
|
}
|
|
212
|
+
await writeTextFile(join(options.target, '.wpmoo/odoo.json'), await mergeEnvironmentMetadata(options.target, scaffoldOptions));
|
|
146
213
|
await writeTextFile(join(options.target, '.env.example'), renderComposeEnvExample(scaffoldOptions));
|
|
147
214
|
if (options.stage) {
|
|
148
215
|
await stageAll(git, options.target);
|
package/dist/scaffold.js
CHANGED
|
@@ -6,14 +6,19 @@ import { plannedExternalAssetOptions, renderComposeEnvExample } from './external
|
|
|
6
6
|
import { cloneRepository, ensureSubmodule, ensureRemoteHasBranch, realGit, stageAll, syncSubmodules, } from './git.js';
|
|
7
7
|
import { renderAgents, renderAppstoreRelease, renderGitignore, renderMooDelegationScript, renderPlaceholder, renderReadme, } from './templates.js';
|
|
8
8
|
import { validateAddonName, validateRepoPath } from './path-validation.js';
|
|
9
|
+
import { renderSourceManifest, sourceManifestEntriesFromMetadata } from './source-manifest.js';
|
|
9
10
|
function validateSourceRepo(repo) {
|
|
10
11
|
const path = validateRepoPath(repo.path);
|
|
11
12
|
return {
|
|
12
13
|
...repo,
|
|
13
14
|
path,
|
|
15
|
+
sourceType: repo.sourceType ?? 'private',
|
|
14
16
|
addons: repo.addons.map(validateAddonName),
|
|
15
17
|
};
|
|
16
18
|
}
|
|
19
|
+
function sourceRepoSubmodulePath(repo) {
|
|
20
|
+
return `odoo/custom/src/${repo.sourceType ?? 'private'}/${repo.path}`;
|
|
21
|
+
}
|
|
17
22
|
function validateScaffoldOptions(options) {
|
|
18
23
|
return {
|
|
19
24
|
...options,
|
|
@@ -56,6 +61,10 @@ export function generatedFiles(options) {
|
|
|
56
61
|
{ path: 'README.md', content: renderReadme(safeOptions) },
|
|
57
62
|
{ path: 'AGENTS.md', content: renderAgents(safeOptions) },
|
|
58
63
|
{ path: 'docs/appstore-release.md', content: renderAppstoreRelease(safeOptions) },
|
|
64
|
+
{
|
|
65
|
+
path: 'odoo/custom/manifests/sources.yaml',
|
|
66
|
+
content: renderSourceManifest(sourceManifestEntriesFromMetadata(safeOptions.sourceRepos, safeOptions.odooVersion)),
|
|
67
|
+
},
|
|
59
68
|
];
|
|
60
69
|
return [
|
|
61
70
|
...files,
|
|
@@ -113,7 +122,7 @@ export async function scaffold(options, git = realGit) {
|
|
|
113
122
|
const externalAssets = plannedExternalAssetOptions(safeOptions);
|
|
114
123
|
const plannedCommands = [
|
|
115
124
|
...externalAssets.map((assetOptions) => renderExternalAssetCommand(assetOptions)),
|
|
116
|
-
...safeOptions.sourceRepos.map((repo) => `git submodule add -b ${safeOptions.odooVersion} ${repo.url}
|
|
125
|
+
...safeOptions.sourceRepos.map((repo) => `git submodule add -b ${safeOptions.odooVersion} ${repo.url} ${sourceRepoSubmodulePath(repo)}`),
|
|
117
126
|
];
|
|
118
127
|
if (safeOptions.stage) {
|
|
119
128
|
plannedCommands.push('git add .');
|
|
@@ -136,9 +145,9 @@ export async function scaffold(options, git = realGit) {
|
|
|
136
145
|
for (const repo of safeOptions.sourceRepos) {
|
|
137
146
|
await ensureRemoteHasBranch(git, safeOptions.target, repo.url, safeOptions.odooVersion, safeOptions.initEmptyRepos);
|
|
138
147
|
}
|
|
139
|
-
await mkdir(join(safeOptions.target, 'odoo/custom/src/private'), { recursive: true });
|
|
140
148
|
for (const repo of safeOptions.sourceRepos) {
|
|
141
|
-
await
|
|
149
|
+
await mkdir(join(safeOptions.target, 'odoo/custom/src', repo.sourceType ?? 'private'), { recursive: true });
|
|
150
|
+
await ensureSubmodule(git, safeOptions.target, repo.url, safeOptions.odooVersion, sourceRepoSubmodulePath(repo));
|
|
142
151
|
}
|
|
143
152
|
await syncSubmodules(git, safeOptions.target);
|
|
144
153
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { defaultOdooVersion, readEnvironmentMetadata, replaceSourceRepos } from './environment.js';
|
|
2
|
+
import { realGit, stageAll } from './git.js';
|
|
3
|
+
import { listGitmoduleSources, readSourceManifest, sourceManifestEntriesFromMetadata, sourceReposFromManifest, syncManifestFromMetadataAndGitmodules, writeSourceManifest, } from './source-manifest.js';
|
|
4
|
+
export async function listSources(target) {
|
|
5
|
+
const metadata = await readEnvironmentMetadata(target);
|
|
6
|
+
const manifest = await readSourceManifest(target);
|
|
7
|
+
if (manifest.sources.length > 0) {
|
|
8
|
+
return manifest.sources;
|
|
9
|
+
}
|
|
10
|
+
if (metadata?.sourceRepos.length) {
|
|
11
|
+
return sourceManifestEntriesFromMetadata(metadata.sourceRepos, metadata.odooVersion);
|
|
12
|
+
}
|
|
13
|
+
return syncManifestFromMetadataAndGitmodules([], metadata?.odooVersion ?? defaultOdooVersion, await listGitmoduleSources(target));
|
|
14
|
+
}
|
|
15
|
+
export function renderSourceList(entries) {
|
|
16
|
+
if (entries.length === 0) {
|
|
17
|
+
return 'No source repositories configured.';
|
|
18
|
+
}
|
|
19
|
+
return entries
|
|
20
|
+
.map((entry) => {
|
|
21
|
+
const branch = entry.branch ? ` @ ${entry.branch}` : '';
|
|
22
|
+
const addons = entry.addons.length ? ` addons: ${entry.addons.join(', ')}` : '';
|
|
23
|
+
return `${entry.type}/${entry.path}${branch} -> ${entry.url}${addons}`;
|
|
24
|
+
})
|
|
25
|
+
.join('\n');
|
|
26
|
+
}
|
|
27
|
+
export async function syncSources(options, git = realGit) {
|
|
28
|
+
const metadata = await readEnvironmentMetadata(options.target);
|
|
29
|
+
const manifest = await readSourceManifest(options.target);
|
|
30
|
+
const gitmodules = await listGitmoduleSources(options.target);
|
|
31
|
+
const fallbackBranch = metadata?.odooVersion ?? defaultOdooVersion;
|
|
32
|
+
const baseRepos = metadata?.sourceRepos.length ? metadata.sourceRepos : sourceReposFromManifest(manifest.sources);
|
|
33
|
+
const entries = syncManifestFromMetadataAndGitmodules(baseRepos, fallbackBranch, gitmodules);
|
|
34
|
+
await writeSourceManifest(options.target, entries);
|
|
35
|
+
if (metadata) {
|
|
36
|
+
await replaceSourceRepos(options.target, sourceReposFromManifest(entries));
|
|
37
|
+
}
|
|
38
|
+
if (options.stage) {
|
|
39
|
+
await stageAll(git, options.target);
|
|
40
|
+
}
|
|
41
|
+
return entries;
|
|
42
|
+
}
|