-
-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathmonorepo-environment.ts
163 lines (147 loc) · 5.33 KB
/
monorepo-environment.ts
1
2
3
4
5
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
import fs from 'fs';
import path from 'path';
import type { ExecaReturnValue } from 'execa';
import YAML from 'yaml';
import { TOOL_EXECUTABLE_PATH, TSX_PATH } from './constants.js';
import Environment, {
EnvironmentOptions,
PackageSpecification,
} from './environment.js';
import LocalMonorepo from './local-monorepo.js';
import { debug, knownKeysOf } from './utils.js';
/**
* A set of configuration options for a {@link MonorepoEnvironment}. In addition
* to the options listed in {@link EnvironmentOptions}, these include:
*
* @property packages - The known packages within this repo (including the
* root).
* @property workspaces - The known workspaces within this repo.
*/
export type MonorepoEnvironmentOptions<
WorkspacePackageNickname extends string,
> = {
packages: Record<WorkspacePackageNickname, PackageSpecification>;
workspaces: Record<string, string[]>;
} & EnvironmentOptions;
/**
* The release specification data.
*
* @property packages - The workspace packages within this repo that will be
* released.
*/
type ReleaseSpecification<WorkspacePackageNickname extends string> = {
packages: Partial<Record<WorkspacePackageNickname, string>>;
};
/**
* This class configures the environment such that the "local" repo becomes a
* monorepo.
*/
export default class MonorepoEnvironment<
WorkspacePackageNickname extends string,
> extends Environment<LocalMonorepo<WorkspacePackageNickname>> {
readFileWithinPackage: LocalMonorepo<WorkspacePackageNickname>['readFileWithinPackage'];
writeFileWithinPackage: LocalMonorepo<WorkspacePackageNickname>['writeFileWithinPackage'];
readJsonFileWithinPackage: LocalMonorepo<WorkspacePackageNickname>['readJsonFileWithinPackage'];
updateJsonFileWithinPackage: LocalMonorepo<WorkspacePackageNickname>['updateJsonFileWithinPackage'];
#packages: MonorepoEnvironmentOptions<WorkspacePackageNickname>['packages'];
constructor(options: MonorepoEnvironmentOptions<WorkspacePackageNickname>) {
super(options);
this.#packages = options.packages;
this.readFileWithinPackage = this.localRepo.readFileWithinPackage.bind(
this.localRepo,
);
this.writeFileWithinPackage = this.localRepo.writeFileWithinPackage.bind(
this.localRepo,
);
this.readJsonFileWithinPackage =
this.localRepo.readJsonFileWithinPackage.bind(this.localRepo);
this.updateJsonFileWithinPackage =
this.localRepo.updateJsonFileWithinPackage.bind(this.localRepo);
}
/**
* Runs the tool within the context of the project, editing the generated
* release spec template automatically with the given information before
* continuing.
*
* @param args - The arguments to this function.
* @param args.args - Additional arguments to pass to the command.
* @param args.releaseSpecification - An object which specifies which packages
* should be bumped, where keys are the *nicknames* of packages as specified
* in the set of options passed to `withMonorepoProjectEnvironment`. Will be
* used to fill in the release spec file that the tool generates.
* @returns The result of the command.
*/
async runTool({
args: additionalArgs = [],
releaseSpecification: releaseSpecificationWithPackageNicknames,
}: {
args?: string[];
releaseSpecification: ReleaseSpecification<WorkspacePackageNickname>;
}): Promise<ExecaReturnValue<string>> {
const releaseSpecificationPath = path.join(
this.directoryPath,
'release-spec',
);
const releaseSpecificationWithPackageNames = {
packages: knownKeysOf(
releaseSpecificationWithPackageNicknames.packages,
).reduce((obj, packageNickname) => {
const packageSpecification = this.#packages[packageNickname];
const versionSpecifier =
releaseSpecificationWithPackageNicknames.packages[packageNickname];
return { ...obj, [packageSpecification.name]: versionSpecifier };
}, {}),
};
await fs.promises.writeFile(
releaseSpecificationPath,
YAML.stringify(releaseSpecificationWithPackageNames),
);
const releaseSpecificationEditorPath = path.join(
this.directoryPath,
'release-spec-editor',
);
await fs.promises.writeFile(
releaseSpecificationEditorPath,
`
#!/bin/sh
if [ -z "$1" ]; then
echo "ERROR: Must provide a path to edit."
exit 1
fi
cat "${releaseSpecificationPath}" > "$1"
`.trim(),
);
await fs.promises.chmod(releaseSpecificationEditorPath, 0o777);
const args = [
TOOL_EXECUTABLE_PATH,
'--project-directory',
this.localRepo.getWorkingDirectoryPath(),
'--temp-directory',
this.tempDirectoryPath,
...additionalArgs,
];
const env = {
EDITOR: releaseSpecificationEditorPath,
};
const result = await this.localRepo.runCommand(TSX_PATH, args, { env });
debug(
['---- START OUTPUT -----', result.all, '---- END OUTPUT -----'].join(
'\n',
),
);
return result;
}
protected buildLocalRepo({
packages,
workspaces,
createInitialCommit = true,
}: MonorepoEnvironmentOptions<WorkspacePackageNickname>): LocalMonorepo<WorkspacePackageNickname> {
return new LocalMonorepo<WorkspacePackageNickname>({
environmentDirectoryPath: this.directoryPath,
remoteRepoDirectoryPath: this.remoteRepo.getWorkingDirectoryPath(),
packages,
workspaces,
createInitialCommit,
});
}
}