@lage-run/hasher 1.2.0 → 1.3.0
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.json +10 -61
- package/CHANGELOG.md +7 -24
- package/lib/PackageTree.d.ts +9 -5
- package/lib/PackageTree.js +89 -30
- package/lib/TargetHasher.d.ts +3 -1
- package/lib/TargetHasher.js +40 -31
- package/lib/__fixtures__/monorepo-with-global-files/some-global.config.d.ts +1 -0
- package/lib/__fixtures__/monorepo-with-global-files/some-global2.config.d.ts +1 -0
- package/lib/__tests__/TargetHasher.test.js +27 -0
- package/package.json +4 -3
- package/lib/__tests__/PackageTree.test.d.ts +0 -1
- package/lib/__tests__/PackageTree.test.js +0 -93
package/CHANGELOG.json
CHANGED
|
@@ -2,82 +2,31 @@
|
|
|
2
2
|
"name": "@lage-run/hasher",
|
|
3
3
|
"entries": [
|
|
4
4
|
{
|
|
5
|
-
"date": "
|
|
6
|
-
"version": "1.
|
|
7
|
-
"tag": "@lage-run/hasher_v1.
|
|
5
|
+
"date": "Fri, 30 Aug 2024 18:39:54 GMT",
|
|
6
|
+
"version": "1.3.0",
|
|
7
|
+
"tag": "@lage-run/hasher_v1.3.0",
|
|
8
8
|
"comments": {
|
|
9
9
|
"minor": [
|
|
10
10
|
{
|
|
11
11
|
"author": "kchau@microsoft.com",
|
|
12
12
|
"package": "@lage-run/hasher",
|
|
13
|
-
"commit": "
|
|
14
|
-
"comment": "
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
"author": "beachball",
|
|
18
|
-
"package": "@lage-run/hasher",
|
|
19
|
-
"comment": "Bump @lage-run/globby to v14.0.2",
|
|
20
|
-
"commit": "not available"
|
|
13
|
+
"commit": "962af15909d64159bbc5fef954e66b6bd4c410d1",
|
|
14
|
+
"comment": "adding the ability to calculate target env globs"
|
|
21
15
|
}
|
|
22
16
|
]
|
|
23
17
|
}
|
|
24
18
|
},
|
|
25
19
|
{
|
|
26
|
-
"date": "
|
|
27
|
-
"version": "1.1
|
|
28
|
-
"tag": "@lage-run/hasher_v1.1
|
|
20
|
+
"date": "Tue, 25 Jun 2024 22:03:40 GMT",
|
|
21
|
+
"version": "1.2.1",
|
|
22
|
+
"tag": "@lage-run/hasher_v1.2.1",
|
|
29
23
|
"comments": {
|
|
30
24
|
"patch": [
|
|
31
25
|
{
|
|
32
26
|
"author": "kchau@microsoft.com_msteamsmdb",
|
|
33
27
|
"package": "@lage-run/hasher",
|
|
34
|
-
"commit": "
|
|
35
|
-
"comment": "
|
|
36
|
-
}
|
|
37
|
-
]
|
|
38
|
-
}
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
"date": "Sun, 05 May 2024 22:55:45 GMT",
|
|
42
|
-
"version": "1.1.1",
|
|
43
|
-
"tag": "@lage-run/hasher_v1.1.1",
|
|
44
|
-
"comments": {
|
|
45
|
-
"patch": [
|
|
46
|
-
{
|
|
47
|
-
"author": "kchau@microsoft.com",
|
|
48
|
-
"package": "@lage-run/hasher",
|
|
49
|
-
"commit": "1e36de04ab83fc0cde38062fc1543e4b12902166",
|
|
50
|
-
"comment": "fixing hashing issues related to rust panic"
|
|
51
|
-
}
|
|
52
|
-
]
|
|
53
|
-
}
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
"date": "Wed, 17 Apr 2024 23:20:47 GMT",
|
|
57
|
-
"version": "1.1.0",
|
|
58
|
-
"tag": "@lage-run/hasher_v1.1.0",
|
|
59
|
-
"comments": {
|
|
60
|
-
"none": [
|
|
61
|
-
{
|
|
62
|
-
"author": "elcraig@microsoft.com",
|
|
63
|
-
"package": "@lage-run/hasher",
|
|
64
|
-
"commit": "fb4fcb8419cc778210104d7d04102fc95df13d5b",
|
|
65
|
-
"comment": "Update formatting"
|
|
66
|
-
}
|
|
67
|
-
]
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
"date": "Fri, 15 Mar 2024 04:35:11 GMT",
|
|
72
|
-
"version": "1.1.0",
|
|
73
|
-
"tag": "@lage-run/hasher_v1.1.0",
|
|
74
|
-
"comments": {
|
|
75
|
-
"minor": [
|
|
76
|
-
{
|
|
77
|
-
"author": "kchau@microsoft.com",
|
|
78
|
-
"package": "@lage-run/hasher",
|
|
79
|
-
"commit": "71283d4af8888454c953e5228c97bfbb471c15ba",
|
|
80
|
-
"comment": "perf optimizations"
|
|
28
|
+
"commit": "440bbd19c317982a187383a5a35befbea71f22c5",
|
|
29
|
+
"comment": "reverting all the hasher changes"
|
|
81
30
|
}
|
|
82
31
|
]
|
|
83
32
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -1,41 +1,24 @@
|
|
|
1
1
|
# Change Log - @lage-run/hasher
|
|
2
2
|
|
|
3
|
-
This log was last generated on
|
|
3
|
+
This log was last generated on Fri, 30 Aug 2024 18:39:54 GMT and should not be manually modified.
|
|
4
4
|
|
|
5
5
|
<!-- Start content -->
|
|
6
6
|
|
|
7
|
-
## 1.
|
|
7
|
+
## 1.3.0
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Fri, 30 Aug 2024 18:39:54 GMT
|
|
10
10
|
|
|
11
11
|
### Minor changes
|
|
12
12
|
|
|
13
|
-
-
|
|
14
|
-
- Bump @lage-run/globby to v14.0.2
|
|
13
|
+
- adding the ability to calculate target env globs (kchau@microsoft.com)
|
|
15
14
|
|
|
16
|
-
## 1.1
|
|
15
|
+
## 1.2.1
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
Tue, 25 Jun 2024 22:03:40 GMT
|
|
19
18
|
|
|
20
19
|
### Patches
|
|
21
20
|
|
|
22
|
-
-
|
|
23
|
-
|
|
24
|
-
## 1.1.1
|
|
25
|
-
|
|
26
|
-
Sun, 05 May 2024 22:55:45 GMT
|
|
27
|
-
|
|
28
|
-
### Patches
|
|
29
|
-
|
|
30
|
-
- fixing hashing issues related to rust panic (kchau@microsoft.com)
|
|
31
|
-
|
|
32
|
-
## 1.1.0
|
|
33
|
-
|
|
34
|
-
Fri, 15 Mar 2024 04:35:11 GMT
|
|
35
|
-
|
|
36
|
-
### Minor changes
|
|
37
|
-
|
|
38
|
-
- perf optimizations (kchau@microsoft.com)
|
|
21
|
+
- reverting all the hasher changes (kchau@microsoft.com_msteamsmdb)
|
|
39
22
|
|
|
40
23
|
## 1.0.7
|
|
41
24
|
|
package/lib/PackageTree.d.ts
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
|
-
|
|
1
|
+
import { type PackageInfos } from "workspace-tools";
|
|
2
|
+
export interface PackageTreeOptions {
|
|
2
3
|
root: string;
|
|
4
|
+
packageInfos: PackageInfos;
|
|
3
5
|
includeUntracked: boolean;
|
|
4
6
|
}
|
|
5
7
|
/**
|
|
6
|
-
*
|
|
8
|
+
* Package Tree keeps a data structure to quickly find all files in a package.
|
|
9
|
+
*
|
|
10
|
+
* TODO: add a watcher to make sure the tree is up to date during a "watched" run.
|
|
7
11
|
*/
|
|
8
12
|
export declare class PackageTree {
|
|
9
13
|
#private;
|
|
10
14
|
private options;
|
|
11
|
-
constructor(options:
|
|
15
|
+
constructor(options: PackageTreeOptions);
|
|
12
16
|
reset(): void;
|
|
13
17
|
initialize(): Promise<void>;
|
|
14
|
-
|
|
15
|
-
|
|
18
|
+
addToPackageTree(filePaths: string[]): Promise<void>;
|
|
19
|
+
getPackageFiles(packageName: string, patterns: string[]): string[];
|
|
16
20
|
}
|
package/lib/PackageTree.js
CHANGED
|
@@ -8,8 +8,10 @@ Object.defineProperty(exports, "PackageTree", {
|
|
|
8
8
|
return PackageTree;
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
|
+
const _execa = /*#__PURE__*/ _interop_require_default(require("execa"));
|
|
11
12
|
const _path = /*#__PURE__*/ _interop_require_default(require("path"));
|
|
12
|
-
const
|
|
13
|
+
const _fs = /*#__PURE__*/ _interop_require_default(require("fs"));
|
|
14
|
+
const _micromatch = /*#__PURE__*/ _interop_require_default(require("micromatch"));
|
|
13
15
|
function _check_private_redeclaration(obj, privateCollection) {
|
|
14
16
|
if (privateCollection.has(obj)) {
|
|
15
17
|
throw new TypeError("Cannot initialize the same private elements twice on an object");
|
|
@@ -50,16 +52,6 @@ function _class_private_field_set(receiver, privateMap, value) {
|
|
|
50
52
|
_class_apply_descriptor_set(receiver, descriptor, value);
|
|
51
53
|
return value;
|
|
52
54
|
}
|
|
53
|
-
function _class_private_method_get(receiver, privateSet, fn) {
|
|
54
|
-
if (!privateSet.has(receiver)) {
|
|
55
|
-
throw new TypeError("attempted to get private field on non-instance");
|
|
56
|
-
}
|
|
57
|
-
return fn;
|
|
58
|
-
}
|
|
59
|
-
function _class_private_method_init(obj, privateSet) {
|
|
60
|
-
_check_private_redeclaration(obj, privateSet);
|
|
61
|
-
privateSet.add(obj);
|
|
62
|
-
}
|
|
63
55
|
function _define_property(obj, key, value) {
|
|
64
56
|
if (key in obj) {
|
|
65
57
|
Object.defineProperty(obj, key, {
|
|
@@ -78,43 +70,110 @@ function _interop_require_default(obj) {
|
|
|
78
70
|
default: obj
|
|
79
71
|
};
|
|
80
72
|
}
|
|
81
|
-
var
|
|
73
|
+
var _tree = /*#__PURE__*/ new WeakMap(), _packageFiles = /*#__PURE__*/ new WeakMap(), _memoizedPackageFiles = /*#__PURE__*/ new WeakMap();
|
|
82
74
|
class PackageTree {
|
|
83
75
|
reset() {
|
|
76
|
+
_class_private_field_set(this, _tree, {});
|
|
77
|
+
_class_private_field_set(this, _packageFiles, {});
|
|
84
78
|
_class_private_field_set(this, _memoizedPackageFiles, {});
|
|
85
79
|
}
|
|
86
80
|
async initialize() {
|
|
81
|
+
const { root , includeUntracked , packageInfos } = this.options;
|
|
87
82
|
this.reset();
|
|
83
|
+
// Generate path tree of all packages in workspace (scale: ~2000 * ~3)
|
|
84
|
+
for (const info of Object.values(packageInfos)){
|
|
85
|
+
const packagePath = _path.default.dirname(info.packageJsonPath);
|
|
86
|
+
const pathParts = _path.default.relative(root, packagePath).split(/[\\/]/);
|
|
87
|
+
let currentNode = _class_private_field_get(this, _tree);
|
|
88
|
+
for (const part of pathParts){
|
|
89
|
+
currentNode[part] = currentNode[part] || {};
|
|
90
|
+
currentNode = currentNode[part];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Get all files in the workspace (scale: ~2000) according to git
|
|
94
|
+
const lsFilesResults = await (0, _execa.default)("git", [
|
|
95
|
+
"ls-files",
|
|
96
|
+
"-z"
|
|
97
|
+
], {
|
|
98
|
+
cwd: root
|
|
99
|
+
});
|
|
100
|
+
if (lsFilesResults.exitCode === 0) {
|
|
101
|
+
const files = lsFilesResults.stdout.split("\0").filter((f)=>Boolean(f) && _fs.default.existsSync(_path.default.join(root, f)));
|
|
102
|
+
this.addToPackageTree(files);
|
|
103
|
+
}
|
|
104
|
+
if (includeUntracked) {
|
|
105
|
+
// Also get all untracked files in the workspace according to git
|
|
106
|
+
const lsOtherResults = await (0, _execa.default)("git", [
|
|
107
|
+
"ls-files",
|
|
108
|
+
"-o",
|
|
109
|
+
"--exclude-standard"
|
|
110
|
+
], {
|
|
111
|
+
cwd: root
|
|
112
|
+
});
|
|
113
|
+
if (lsOtherResults.exitCode === 0) {
|
|
114
|
+
const files = lsOtherResults.stdout.split("\0").filter(Boolean);
|
|
115
|
+
this.addToPackageTree(files);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
async addToPackageTree(filePaths) {
|
|
120
|
+
// key: path/to/package (packageRoot), value: array of a tuple of [file, hash]
|
|
121
|
+
const packageFiles = _class_private_field_get(this, _packageFiles);
|
|
122
|
+
for (const entry of filePaths){
|
|
123
|
+
const pathParts = entry.split(/[\\/]/);
|
|
124
|
+
let node = _class_private_field_get(this, _tree);
|
|
125
|
+
const packagePathParts = [];
|
|
126
|
+
for (const part of pathParts){
|
|
127
|
+
if (node[part]) {
|
|
128
|
+
node = node[part];
|
|
129
|
+
packagePathParts.push(part);
|
|
130
|
+
} else {
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const packageRoot = packagePathParts.join("/");
|
|
135
|
+
packageFiles[packageRoot] = packageFiles[packageRoot] || [];
|
|
136
|
+
packageFiles[packageRoot].push(entry);
|
|
137
|
+
}
|
|
88
138
|
}
|
|
89
|
-
|
|
90
|
-
const
|
|
139
|
+
getPackageFiles(packageName, patterns) {
|
|
140
|
+
const { root , packageInfos } = this.options;
|
|
141
|
+
const packagePath = _path.default.relative(root, _path.default.dirname(packageInfos[packageName].packageJsonPath)).replace(/\\/g, "/");
|
|
142
|
+
const packageFiles = _class_private_field_get(this, _packageFiles)[packagePath];
|
|
143
|
+
if (!packageFiles) {
|
|
144
|
+
return [];
|
|
145
|
+
}
|
|
146
|
+
const key = `${packageName}\0${patterns.join("\0")}`;
|
|
91
147
|
if (!_class_private_field_get(this, _memoizedPackageFiles)[key]) {
|
|
92
|
-
const
|
|
93
|
-
|
|
148
|
+
const packagePatterns = patterns.map((pattern)=>{
|
|
149
|
+
if (pattern.startsWith("!")) {
|
|
150
|
+
return `!${_path.default.join(packagePath, pattern.slice(1)).replace(/\\/g, "/")}`;
|
|
151
|
+
}
|
|
152
|
+
return _path.default.join(packagePath, pattern).replace(/\\/g, "/");
|
|
153
|
+
});
|
|
154
|
+
_class_private_field_get(this, _memoizedPackageFiles)[key] = (0, _micromatch.default)(packageFiles, packagePatterns, {
|
|
155
|
+
dot: true
|
|
156
|
+
});
|
|
94
157
|
}
|
|
95
158
|
return _class_private_field_get(this, _memoizedPackageFiles)[key];
|
|
96
159
|
}
|
|
97
|
-
cleanup() {}
|
|
98
160
|
constructor(options){
|
|
99
|
-
_class_private_method_init(this, _findFilesFromGitTree);
|
|
100
161
|
_define_property(this, "options", void 0);
|
|
162
|
+
_class_private_field_init(this, _tree, {
|
|
163
|
+
writable: true,
|
|
164
|
+
value: void 0
|
|
165
|
+
});
|
|
166
|
+
_class_private_field_init(this, _packageFiles, {
|
|
167
|
+
writable: true,
|
|
168
|
+
value: void 0
|
|
169
|
+
});
|
|
101
170
|
_class_private_field_init(this, _memoizedPackageFiles, {
|
|
102
171
|
writable: true,
|
|
103
172
|
value: void 0
|
|
104
173
|
});
|
|
105
174
|
this.options = options;
|
|
175
|
+
_class_private_field_set(this, _tree, {});
|
|
176
|
+
_class_private_field_set(this, _packageFiles, {});
|
|
106
177
|
_class_private_field_set(this, _memoizedPackageFiles, {});
|
|
107
178
|
}
|
|
108
179
|
}
|
|
109
|
-
async function findFilesFromGitTree(packagePath, patterns) {
|
|
110
|
-
const cwd = _path.default.isAbsolute(packagePath) ? packagePath : _path.default.join(this.options.root, packagePath);
|
|
111
|
-
return (0, _globby.globby)(patterns, {
|
|
112
|
-
cwd,
|
|
113
|
-
onlyFiles: true,
|
|
114
|
-
ignore: [
|
|
115
|
-
".git"
|
|
116
|
-
],
|
|
117
|
-
gitignore: true,
|
|
118
|
-
absolute: true
|
|
119
|
-
}) ?? [];
|
|
120
|
-
}
|
package/lib/TargetHasher.d.ts
CHANGED
|
@@ -32,7 +32,7 @@ export declare class TargetHasher {
|
|
|
32
32
|
private options;
|
|
33
33
|
logger: Logger | undefined;
|
|
34
34
|
fileHasher: FileHasher;
|
|
35
|
-
packageTree: PackageTree;
|
|
35
|
+
packageTree: PackageTree | undefined;
|
|
36
36
|
initializedPromise: Promise<unknown> | undefined;
|
|
37
37
|
packageInfos: PackageInfos;
|
|
38
38
|
workspaceInfo: WorkspaceInfo | undefined;
|
|
@@ -40,6 +40,8 @@ export declare class TargetHasher {
|
|
|
40
40
|
lockInfo: ParsedLock | undefined;
|
|
41
41
|
targetHashes: Record<string, string>;
|
|
42
42
|
dependencyMap: DependencyMap;
|
|
43
|
+
memoizedEnvGlobResults: Map<string, string[]>;
|
|
44
|
+
getMemorizedEnvGlobResults(envGlob: string[]): Promise<string[]>;
|
|
43
45
|
getPackageInfos(workspacePackages: WorkspaceInfo): PackageInfos;
|
|
44
46
|
expandInputPatterns(patterns: string[], target: Target): Record<string, string[]>;
|
|
45
47
|
constructor(options: TargetHasherOptions);
|
package/lib/TargetHasher.js
CHANGED
|
@@ -9,6 +9,7 @@ Object.defineProperty(exports, "TargetHasher", {
|
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
11
|
const _globhasher = require("glob-hasher");
|
|
12
|
+
const _fastglob = /*#__PURE__*/ _interop_require_default(require("fast-glob"));
|
|
12
13
|
const _fs = /*#__PURE__*/ _interop_require_default(require("fs"));
|
|
13
14
|
const _path = /*#__PURE__*/ _interop_require_default(require("path"));
|
|
14
15
|
const _workspacetools = require("workspace-tools");
|
|
@@ -37,6 +38,17 @@ function _interop_require_default(obj) {
|
|
|
37
38
|
};
|
|
38
39
|
}
|
|
39
40
|
class TargetHasher {
|
|
41
|
+
async getMemorizedEnvGlobResults(envGlob) {
|
|
42
|
+
const key = envGlob.join("\0ENV_GLOB\0");
|
|
43
|
+
const { root } = this.options;
|
|
44
|
+
if (!this.memoizedEnvGlobResults.has(key)) {
|
|
45
|
+
const files = await (0, _fastglob.default)(envGlob, {
|
|
46
|
+
cwd: root
|
|
47
|
+
});
|
|
48
|
+
this.memoizedEnvGlobResults.set(key, files);
|
|
49
|
+
}
|
|
50
|
+
return this.memoizedEnvGlobResults.get(key) ?? [];
|
|
51
|
+
}
|
|
40
52
|
getPackageInfos(workspacePackages) {
|
|
41
53
|
const { root } = this.options;
|
|
42
54
|
const packageInfos = {};
|
|
@@ -108,25 +120,24 @@ class TargetHasher {
|
|
|
108
120
|
await this.initializedPromise;
|
|
109
121
|
return;
|
|
110
122
|
}
|
|
111
|
-
this.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
});
|
|
123
|
+
this.initializedPromise = Promise.all([
|
|
124
|
+
this.fileHasher.readManifest().then(()=>this.getMemorizedEnvGlobResults(environmentGlob)).then((files)=>this.fileHasher.hash(files)).then((hash)=>this.globalInputsHash = hash),
|
|
125
|
+
(0, _workspacetools.getWorkspacesAsync)(root).then((workspaceInfo)=>this.workspaceInfo = workspaceInfo).then(()=>{
|
|
126
|
+
this.packageInfos = this.getPackageInfos(this.workspaceInfo);
|
|
127
|
+
this.dependencyMap = (0, _workspacetools.createDependencyMap)(this.packageInfos, {
|
|
128
|
+
withDevDependencies: true,
|
|
129
|
+
withPeerDependencies: false
|
|
130
|
+
});
|
|
131
|
+
this.packageTree = new _PackageTree.PackageTree({
|
|
132
|
+
root,
|
|
133
|
+
packageInfos: this.packageInfos,
|
|
134
|
+
// TODO: (optimization) false if process.env.TF_BUILD || process.env.CI
|
|
135
|
+
includeUntracked: true
|
|
136
|
+
});
|
|
137
|
+
return this.packageTree.initialize();
|
|
138
|
+
}),
|
|
139
|
+
(0, _workspacetools.parseLockFile)(root).then((lockInfo)=>this.lockInfo = lockInfo)
|
|
140
|
+
]);
|
|
130
141
|
await this.initializedPromise;
|
|
131
142
|
if (this.logger !== undefined) {
|
|
132
143
|
const globalInputsHash = (0, _hashStrings.hashStrings)(Object.values(this.globalInputsHash ?? {}));
|
|
@@ -143,11 +154,13 @@ class TargetHasher {
|
|
|
143
154
|
if (!target.inputs) {
|
|
144
155
|
throw new Error("Root-level targets must have `inputs` defined if it has cache enabled.");
|
|
145
156
|
}
|
|
146
|
-
const files = await
|
|
157
|
+
const files = await (0, _fastglob.default)(target.inputs, {
|
|
158
|
+
cwd: root
|
|
159
|
+
});
|
|
147
160
|
const fileFashes = (0, _globhasher.hash)(files, {
|
|
148
161
|
cwd: root
|
|
149
162
|
}) ?? {};
|
|
150
|
-
const hashes = Object.values(fileFashes)
|
|
163
|
+
const hashes = Object.values(fileFashes);
|
|
151
164
|
return (0, _hashStrings.hashStrings)(hashes);
|
|
152
165
|
}
|
|
153
166
|
// 1. add hash of target's inputs
|
|
@@ -171,17 +184,16 @@ class TargetHasher {
|
|
|
171
184
|
const packagePatterns = this.expandInputPatterns(inputs, target);
|
|
172
185
|
const files = [];
|
|
173
186
|
for (const [pkg, patterns] of Object.entries(packagePatterns)){
|
|
174
|
-
const
|
|
175
|
-
const packagePath = _path.default.relative(root, _path.default.dirname(this.packageInfos[pkg].packageJsonPath)).replace(/\\/g, "/");
|
|
176
|
-
const packageFiles = await this.packageTree.findFilesInPath(packagePath, patterns);
|
|
187
|
+
const packageFiles = this.packageTree.getPackageFiles(pkg, patterns);
|
|
177
188
|
files.push(...packageFiles);
|
|
178
189
|
}
|
|
179
190
|
const fileHashes = this.fileHasher.hash(files) ?? {}; // this list is sorted by file name
|
|
180
191
|
// get target hashes
|
|
181
192
|
const targetDepHashes = target.dependencies?.sort().map((targetDep)=>this.targetHashes[targetDep]);
|
|
193
|
+
const envGlobFiles = target.environmentGlob ? await this.getMemorizedEnvGlobResults(target.environmentGlob) : Object.values(this.globalInputsHash ?? {});
|
|
182
194
|
const combinedHashes = [
|
|
183
195
|
// Environmental hashes
|
|
184
|
-
...
|
|
196
|
+
...envGlobFiles,
|
|
185
197
|
`${target.id}|${JSON.stringify(this.options.cliArgs)}`,
|
|
186
198
|
this.options.cacheKey || "",
|
|
187
199
|
// File content hashes based on target.inputs
|
|
@@ -195,8 +207,7 @@ class TargetHasher {
|
|
|
195
207
|
return hashString;
|
|
196
208
|
}
|
|
197
209
|
async cleanup() {
|
|
198
|
-
this.
|
|
199
|
-
this.fileHasher.writeManifest();
|
|
210
|
+
await this.fileHasher.writeManifest();
|
|
200
211
|
}
|
|
201
212
|
constructor(options){
|
|
202
213
|
_define_property(this, "options", void 0);
|
|
@@ -210,6 +221,7 @@ class TargetHasher {
|
|
|
210
221
|
_define_property(this, "lockInfo", void 0);
|
|
211
222
|
_define_property(this, "targetHashes", void 0);
|
|
212
223
|
_define_property(this, "dependencyMap", void 0);
|
|
224
|
+
_define_property(this, "memoizedEnvGlobResults", void 0);
|
|
213
225
|
this.options = options;
|
|
214
226
|
this.packageInfos = {};
|
|
215
227
|
this.targetHashes = {};
|
|
@@ -217,14 +229,11 @@ class TargetHasher {
|
|
|
217
229
|
dependencies: new Map(),
|
|
218
230
|
dependents: new Map()
|
|
219
231
|
};
|
|
232
|
+
this.memoizedEnvGlobResults = new Map();
|
|
220
233
|
const { root , logger } = options;
|
|
221
234
|
this.logger = logger;
|
|
222
235
|
this.fileHasher = new _FileHasher.FileHasher({
|
|
223
236
|
root
|
|
224
237
|
});
|
|
225
|
-
this.packageTree = new _PackageTree.PackageTree({
|
|
226
|
-
root,
|
|
227
|
-
includeUntracked: true
|
|
228
|
-
});
|
|
229
238
|
}
|
|
230
239
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const global: boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const global: boolean;
|
|
@@ -125,4 +125,31 @@ describe("The main Hasher class", ()=>{
|
|
|
125
125
|
monorepo1.cleanup();
|
|
126
126
|
monorepo2.cleanup();
|
|
127
127
|
});
|
|
128
|
+
it("creates different hashes when the target has a different env glob", async ()=>{
|
|
129
|
+
const monorepo1 = await setupFixture("monorepo-with-global-files");
|
|
130
|
+
const hasher = new _index.TargetHasher({
|
|
131
|
+
root: monorepo1.root,
|
|
132
|
+
environmentGlob: []
|
|
133
|
+
});
|
|
134
|
+
const target = createTarget(monorepo1.root, "package-a", "build");
|
|
135
|
+
target.environmentGlob = [
|
|
136
|
+
"some-global*"
|
|
137
|
+
];
|
|
138
|
+
target.inputs = [
|
|
139
|
+
"**/*",
|
|
140
|
+
"^**/*"
|
|
141
|
+
];
|
|
142
|
+
const hash = await getHash(hasher, target);
|
|
143
|
+
const target2 = createTarget(monorepo1.root, "package-a", "build");
|
|
144
|
+
target2.environmentGlob = [
|
|
145
|
+
"some-global.*"
|
|
146
|
+
];
|
|
147
|
+
target2.inputs = [
|
|
148
|
+
"**/*",
|
|
149
|
+
"^**/*"
|
|
150
|
+
];
|
|
151
|
+
const hash2 = await getHash(hasher, target2);
|
|
152
|
+
expect(hash).not.toEqual(hash2);
|
|
153
|
+
monorepo1.cleanup();
|
|
154
|
+
});
|
|
128
155
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lage-run/hasher",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Hasher for Lage Targets",
|
|
5
5
|
"repository": {
|
|
6
6
|
"url": "https://github.com/microsoft/lage"
|
|
@@ -16,12 +16,13 @@
|
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@lage-run/target-graph": "^0.8.9",
|
|
19
|
-
"@lage-run/globby": "^14.0.2",
|
|
20
19
|
"@lage-run/logger": "^1.3.0",
|
|
21
20
|
"execa": "5.1.1",
|
|
22
21
|
"workspace-tools": "0.36.4",
|
|
22
|
+
"fast-glob": "3.3.1",
|
|
23
23
|
"glob-hasher": "^1.4.2",
|
|
24
|
-
"graceful-fs": "4.2.11"
|
|
24
|
+
"graceful-fs": "4.2.11",
|
|
25
|
+
"micromatch": "4.0.5"
|
|
25
26
|
},
|
|
26
27
|
"devDependencies": {
|
|
27
28
|
"@lage-run/monorepo-fixture": "*",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
// test out the findFilesInPath method of the PackageTree
|
|
2
|
-
"use strict";
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
const _monorepofixture = require("@lage-run/monorepo-fixture");
|
|
7
|
-
const _PackageTree = require("../PackageTree");
|
|
8
|
-
const _path = /*#__PURE__*/ _interop_require_default(require("path"));
|
|
9
|
-
function _interop_require_default(obj) {
|
|
10
|
-
return obj && obj.__esModule ? obj : {
|
|
11
|
-
default: obj
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
// given various patterns that exercises the globby functionality with extgob, etc.
|
|
15
|
-
describe("PackageTree", ()=>{
|
|
16
|
-
async function setupFixture(fixture = "monorepo") {
|
|
17
|
-
const monorepo = new _monorepofixture.Monorepo(fixture);
|
|
18
|
-
await monorepo.init(_path.default.join(fixturesPath, fixture));
|
|
19
|
-
return monorepo;
|
|
20
|
-
}
|
|
21
|
-
const fixturesPath = _path.default.join(__dirname, "../__fixtures__");
|
|
22
|
-
it("should find all files in a package using globby basic pattern", async ()=>{
|
|
23
|
-
const monorepo = await setupFixture("basic");
|
|
24
|
-
const packagePath = monorepo.root;
|
|
25
|
-
const patterns = [
|
|
26
|
-
"**/*"
|
|
27
|
-
];
|
|
28
|
-
const packageTree = new _PackageTree.PackageTree({
|
|
29
|
-
root: packagePath,
|
|
30
|
-
includeUntracked: true
|
|
31
|
-
});
|
|
32
|
-
await packageTree.initialize();
|
|
33
|
-
const files = await packageTree.findFilesInPath(packagePath, patterns);
|
|
34
|
-
expect(serializeFiles(files, packagePath)).toMatchInlineSnapshot(`
|
|
35
|
-
[
|
|
36
|
-
"node_modules/package-2/package.json",
|
|
37
|
-
"package.json",
|
|
38
|
-
"src/index.ts",
|
|
39
|
-
"yarn.lock",
|
|
40
|
-
]
|
|
41
|
-
`);
|
|
42
|
-
monorepo.cleanup();
|
|
43
|
-
});
|
|
44
|
-
it("should find all files in a package using globby with extglob", async ()=>{
|
|
45
|
-
const monorepo = await setupFixture("basic");
|
|
46
|
-
const packagePath = monorepo.root;
|
|
47
|
-
const patterns = [
|
|
48
|
-
"**/*.+(json|ts)",
|
|
49
|
-
"!yarn.lock"
|
|
50
|
-
];
|
|
51
|
-
const packageTree = new _PackageTree.PackageTree({
|
|
52
|
-
root: packagePath,
|
|
53
|
-
includeUntracked: true
|
|
54
|
-
});
|
|
55
|
-
await packageTree.initialize();
|
|
56
|
-
const files = await packageTree.findFilesInPath(packagePath, patterns);
|
|
57
|
-
expect(serializeFiles(files, packagePath)).toMatchInlineSnapshot(`
|
|
58
|
-
[
|
|
59
|
-
"node_modules/package-2/package.json",
|
|
60
|
-
"package.json",
|
|
61
|
-
"src/index.ts",
|
|
62
|
-
]
|
|
63
|
-
`);
|
|
64
|
-
monorepo.cleanup();
|
|
65
|
-
});
|
|
66
|
-
it("should find all files in a package using globby with extglob of sub-exclusion pattern", async ()=>{
|
|
67
|
-
const monorepo = await setupFixture("monorepo-with-deps");
|
|
68
|
-
const packagePath = monorepo.root;
|
|
69
|
-
const patterns = [
|
|
70
|
-
"**/!(package.json)"
|
|
71
|
-
];
|
|
72
|
-
const packageTree = new _PackageTree.PackageTree({
|
|
73
|
-
root: packagePath,
|
|
74
|
-
includeUntracked: true
|
|
75
|
-
});
|
|
76
|
-
await packageTree.initialize();
|
|
77
|
-
const files = await packageTree.findFilesInPath(packagePath, patterns);
|
|
78
|
-
expect(serializeFiles(files, packagePath)).toMatchInlineSnapshot(`
|
|
79
|
-
[
|
|
80
|
-
"node_modules/.yarn-integrity",
|
|
81
|
-
"node_modules/package-a/src/index.ts",
|
|
82
|
-
"node_modules/package-b/src/index.ts",
|
|
83
|
-
"packages/package-a/src/index.ts",
|
|
84
|
-
"packages/package-b/src/index.ts",
|
|
85
|
-
"yarn.lock",
|
|
86
|
-
]
|
|
87
|
-
`);
|
|
88
|
-
monorepo.cleanup();
|
|
89
|
-
});
|
|
90
|
-
function serializeFiles(files, packagePath) {
|
|
91
|
-
return files.map((p)=>_path.default.relative(packagePath, p)).map((p)=>p.replace(/\\/g, "/")).sort();
|
|
92
|
-
}
|
|
93
|
-
});
|