@haus-tech/haus-workflow 1.5.1 → 1.5.2
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 +7 -0
- package/dist/cli.js +50 -15
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.5.2](https://github.com/WeAreHausTech/haus-workflow/compare/v1.5.1...v1.5.2) (2026-08-10)
|
|
4
|
+
|
|
5
|
+
### Bug Fixes
|
|
6
|
+
|
|
7
|
+
- **scanner:** gate version-numbered stack tags on actual pinned major ([#211](https://github.com/WeAreHausTech/haus-workflow/issues/211)) ([ffd68ce](https://github.com/WeAreHausTech/haus-workflow/commit/ffd68ce2ab13d8a68496b107ebf01f09ccfe1fc9))
|
|
8
|
+
- **workspace:** stop link-context self-reference recursion ([#210](https://github.com/WeAreHausTech/haus-workflow/issues/210)) ([79136a0](https://github.com/WeAreHausTech/haus-workflow/commit/79136a0a186cd1d751954368832b258009504e69))
|
|
9
|
+
|
|
3
10
|
## [1.5.1](https://github.com/WeAreHausTech/haus-workflow/compare/v1.5.0...v1.5.1) (2026-08-10)
|
|
4
11
|
|
|
5
12
|
### Bug Fixes
|
package/dist/cli.js
CHANGED
|
@@ -4713,6 +4713,9 @@ function resolveAbsPath(workspaceRoot, folder, overrides) {
|
|
|
4713
4713
|
if (override) return path31.resolve(workspaceRoot, override);
|
|
4714
4714
|
return path31.resolve(workspaceRoot, folder);
|
|
4715
4715
|
}
|
|
4716
|
+
function isSelfMember(workspaceRoot, member) {
|
|
4717
|
+
return path31.resolve(member.absPath) === path31.resolve(workspaceRoot);
|
|
4718
|
+
}
|
|
4716
4719
|
async function readPathOverrides(workspaceRoot) {
|
|
4717
4720
|
const filePath = path31.join(workspaceRoot, REPOS_LOCAL_FILE);
|
|
4718
4721
|
const result = await readJsonDetailed(filePath);
|
|
@@ -4778,9 +4781,9 @@ async function readMembers(rootInfo) {
|
|
|
4778
4781
|
const workspaceRoot = rootInfo.mainRoot;
|
|
4779
4782
|
const overrides = await readPathOverrides(workspaceRoot);
|
|
4780
4783
|
const fromYaml = await readFromWorkspaceYaml(workspaceRoot, overrides);
|
|
4781
|
-
if (fromYaml) return fromYaml;
|
|
4784
|
+
if (fromYaml) return fromYaml.filter((m) => !isSelfMember(workspaceRoot, m));
|
|
4782
4785
|
const fromManifest = await readFromManifest(workspaceRoot, overrides);
|
|
4783
|
-
if (fromManifest) return fromManifest;
|
|
4786
|
+
if (fromManifest) return fromManifest.filter((m) => !isSelfMember(workspaceRoot, m));
|
|
4784
4787
|
return [];
|
|
4785
4788
|
}
|
|
4786
4789
|
|
|
@@ -4865,6 +4868,15 @@ function compareVersions(a, b) {
|
|
|
4865
4868
|
}
|
|
4866
4869
|
return semver.compare(normalizedA, normalizedB);
|
|
4867
4870
|
}
|
|
4871
|
+
function resolveMajorVersion(range) {
|
|
4872
|
+
if (!range) return null;
|
|
4873
|
+
try {
|
|
4874
|
+
const min = semver.minVersion(range);
|
|
4875
|
+
return min ? min.major : null;
|
|
4876
|
+
} catch {
|
|
4877
|
+
return null;
|
|
4878
|
+
}
|
|
4879
|
+
}
|
|
4868
4880
|
|
|
4869
4881
|
// src/scanner/detect-package-manager.ts
|
|
4870
4882
|
import path33 from "node:path";
|
|
@@ -4896,6 +4908,11 @@ function detectPackageManager(root, packageManagerField) {
|
|
|
4896
4908
|
var dep = (value) => ({ kind: "dep", value });
|
|
4897
4909
|
var depPrefix = (value) => ({ kind: "depPrefix", value });
|
|
4898
4910
|
var depAbsent = (value) => ({ kind: "depAbsent", value });
|
|
4911
|
+
var depMajor = (value, major) => ({
|
|
4912
|
+
kind: "depMajor",
|
|
4913
|
+
value,
|
|
4914
|
+
major
|
|
4915
|
+
});
|
|
4899
4916
|
var fileEndsWith = (value) => ({
|
|
4900
4917
|
kind: "file",
|
|
4901
4918
|
value,
|
|
@@ -4935,6 +4952,8 @@ function matchSignal(sig, ctx) {
|
|
|
4935
4952
|
return false;
|
|
4936
4953
|
case "depAbsent":
|
|
4937
4954
|
return !ctx.deps.has(sig.value);
|
|
4955
|
+
case "depMajor":
|
|
4956
|
+
return ctx.deps.has(sig.value) && resolveMajorVersion(ctx.depVersions.get(sig.value) ?? "") === sig.major;
|
|
4938
4957
|
case "content":
|
|
4939
4958
|
return ctx.contentBlob.includes(sig.value);
|
|
4940
4959
|
case "file":
|
|
@@ -4986,9 +5005,9 @@ var ROLE_RULES = [
|
|
|
4986
5005
|
];
|
|
4987
5006
|
var STACK_RULES = [
|
|
4988
5007
|
{ stack: ["frontend", "nextjs"], any: [dep("next")] },
|
|
4989
|
-
{ stack: ["frontend", "react19"], any: [
|
|
5008
|
+
{ stack: ["frontend", "react19"], any: [depMajor("react", 19)] },
|
|
4990
5009
|
{ stack: ["frontend", "vue"], any: [dep("vue")] },
|
|
4991
|
-
{ stack: ["frontend", "vite8"], any: [
|
|
5010
|
+
{ stack: ["frontend", "vite8"], any: [depMajor("vite", 8)] },
|
|
4992
5011
|
{ stack: ["frontend", "react-router-v7"], all: [dep("react-router"), dep("@react-router/node")] },
|
|
4993
5012
|
{
|
|
4994
5013
|
stack: ["frontend", "tailwindcss"],
|
|
@@ -4998,7 +5017,7 @@ var STACK_RULES = [
|
|
|
4998
5017
|
stack: ["frontend", "shadcn"],
|
|
4999
5018
|
all: [fileEndsWith("components.json"), dep("class-variance-authority")]
|
|
5000
5019
|
},
|
|
5001
|
-
{ stack: ["tooling", "typescript5"], any: [
|
|
5020
|
+
{ stack: ["tooling", "typescript5"], any: [depMajor("typescript", 5)] },
|
|
5002
5021
|
{ stack: ["backend", "sanity"], any: [dep("sanity"), dep("next-sanity"), dep("@sanity/client")] },
|
|
5003
5022
|
{ stack: ["backend", "strapi"], any: [dep("@strapi/strapi"), depPrefix("@strapi/")] },
|
|
5004
5023
|
{ stack: ["backend", "prisma"], any: [dep("prisma"), dep("@prisma/client")] },
|
|
@@ -5023,9 +5042,15 @@ var STACK_RULES = [
|
|
|
5023
5042
|
stack: ["databases", "supabase"],
|
|
5024
5043
|
any: [dep("@supabase/supabase-js"), depPrefix("@supabase/")]
|
|
5025
5044
|
},
|
|
5026
|
-
{ stack: ["backend", "vendure3"], any: [
|
|
5045
|
+
{ stack: ["backend", "vendure3"], any: [depMajor("@vendure/core", 3)] },
|
|
5027
5046
|
{ stack: ["backend", "nestjs"], any: [dep("@nestjs/core")] },
|
|
5028
5047
|
{ stack: ["backend", "nestjs"], any: [content("NestFactory")] },
|
|
5048
|
+
// Deliberately NOT major-gated: this rule catches vendure *plugin* repos that use
|
|
5049
|
+
// the @VendurePlugin decorator without depending on @vendure/core directly (a
|
|
5050
|
+
// peer/transitive dep, common in a plugin package). depMajor requires the name to
|
|
5051
|
+
// be a direct dependency (see matchSignal's depMajor case), which this repo shape
|
|
5052
|
+
// doesn't satisfy — falling back to content detection is the only signal available,
|
|
5053
|
+
// so it stays presence/content-based rather than gated on a major we can't read.
|
|
5029
5054
|
{ stack: ["backend", "vendure3"], any: [content("@VendurePlugin")] },
|
|
5030
5055
|
{ stack: ["backend", "graphql"], any: [dep("graphql"), dep("@nestjs/graphql")] },
|
|
5031
5056
|
{
|
|
@@ -5127,17 +5152,21 @@ function describeUnknownDetection(unsupportedSignals, isLinkedWorktree) {
|
|
|
5127
5152
|
function blocked(rel) {
|
|
5128
5153
|
return SENSITIVE_PATH_REGEXES.some((x) => x.test(rel));
|
|
5129
5154
|
}
|
|
5130
|
-
function
|
|
5131
|
-
const
|
|
5155
|
+
function dependencyVersionMap(pkg, composer) {
|
|
5156
|
+
const versions = /* @__PURE__ */ new Map();
|
|
5132
5157
|
const pushObj = (obj) => {
|
|
5133
5158
|
if (!isRecord2(obj)) return;
|
|
5134
|
-
for (const key of Object.keys(obj))
|
|
5159
|
+
for (const key of Object.keys(obj)) {
|
|
5160
|
+
if (versions.has(key)) continue;
|
|
5161
|
+
const value = obj[key];
|
|
5162
|
+
versions.set(key, typeof value === "string" ? value : "");
|
|
5163
|
+
}
|
|
5135
5164
|
};
|
|
5136
5165
|
pushObj(pkg?.dependencies);
|
|
5137
5166
|
pushObj(pkg?.devDependencies);
|
|
5138
5167
|
pushObj(composer?.require);
|
|
5139
5168
|
pushObj(composer?.["require-dev"]);
|
|
5140
|
-
return
|
|
5169
|
+
return versions;
|
|
5141
5170
|
}
|
|
5142
5171
|
function finalizeRoles(registryRoles, deps, files) {
|
|
5143
5172
|
const roles = new Set(registryRoles);
|
|
@@ -5269,10 +5298,16 @@ async function scanProject(root) {
|
|
|
5269
5298
|
const composer = await readJson(path36.join(root, "composer.json"));
|
|
5270
5299
|
const files = await listFiles(root, SAFE_FILES);
|
|
5271
5300
|
const safeFiles = files.filter((f) => !blocked(f));
|
|
5272
|
-
const
|
|
5301
|
+
const depVersions = dependencyVersionMap(pkg, composer);
|
|
5302
|
+
const deps = [...depVersions.keys()].sort();
|
|
5273
5303
|
const packageManager = detectPackageManager(root, String(pkg?.packageManager ?? ""));
|
|
5274
5304
|
const contentBlob = await buildContentBlob(root, safeFiles);
|
|
5275
|
-
const detection = runDetection({
|
|
5305
|
+
const detection = runDetection({
|
|
5306
|
+
deps: new Set(deps),
|
|
5307
|
+
depVersions,
|
|
5308
|
+
files: safeFiles,
|
|
5309
|
+
contentBlob
|
|
5310
|
+
});
|
|
5276
5311
|
const roles = finalizeRoles(detection.roles, deps, safeFiles);
|
|
5277
5312
|
const stacks = detection.stacks;
|
|
5278
5313
|
if (packageManager === "yarn") stacks.packageManagers.push("yarn4");
|
|
@@ -7330,18 +7365,18 @@ async function findRepoRoots(workspaceRoot, maxDepth = DEFAULT_MAX_DEPTH) {
|
|
|
7330
7365
|
return repoRoots;
|
|
7331
7366
|
}
|
|
7332
7367
|
async function discoverRepos(workspaceRoot, maxDepth = DEFAULT_MAX_DEPTH) {
|
|
7333
|
-
const repoRoots = await findRepoRoots(workspaceRoot, maxDepth);
|
|
7368
|
+
const repoRoots = (await findRepoRoots(workspaceRoot, maxDepth)).filter((dir) => dir !== ".");
|
|
7334
7369
|
return mapWithConcurrency(repoRoots, async (relDir) => {
|
|
7335
7370
|
const absDir = path44.resolve(workspaceRoot, relDir);
|
|
7336
7371
|
const pkg = await readJson(path44.join(absDir, "package.json"));
|
|
7337
|
-
const name = typeof pkg?.name === "string" && pkg.name.length > 0 ? pkg.name : path44.basename(
|
|
7372
|
+
const name = typeof pkg?.name === "string" && pkg.name.length > 0 ? pkg.name : path44.basename(absDir);
|
|
7338
7373
|
let role = "auto";
|
|
7339
7374
|
try {
|
|
7340
7375
|
const scan = await scanProject(absDir);
|
|
7341
7376
|
if (scan.repoRoles.length > 0) role = scan.repoRoles.join("+");
|
|
7342
7377
|
} catch {
|
|
7343
7378
|
}
|
|
7344
|
-
return { name, path: relDir
|
|
7379
|
+
return { name, path: relDir, role };
|
|
7345
7380
|
});
|
|
7346
7381
|
}
|
|
7347
7382
|
function mergeWorkspaceConfig(existing, discovered, opts = {}) {
|