@contractspec/bundle.workspace 4.1.3 → 4.2.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/dist/index.js +871 -11
- package/dist/node/index.js +871 -11
- package/dist/services/upgrade/guided-upgrade.d.ts +10 -0
- package/dist/services/upgrade/guided-upgrade.test.d.ts +1 -0
- package/dist/services/upgrade/index.d.ts +1 -0
- package/dist/services/upgrade/types.d.ts +24 -0
- package/dist/services/upgrade/upgrade-service.d.ts +2 -0
- package/dist/services/versioning/index.d.ts +3 -0
- package/dist/services/versioning/release-files.d.ts +19 -0
- package/dist/services/versioning/release-formatters.d.ts +8 -0
- package/dist/services/versioning/release-plan.d.ts +7 -0
- package/dist/services/versioning/release-service.d.ts +13 -0
- package/dist/services/versioning/release-service.test.d.ts +1 -0
- package/dist/services/versioning/release-service.types.d.ts +55 -0
- package/package.json +13 -13
package/dist/index.js
CHANGED
|
@@ -9763,7 +9763,7 @@ function generateWorkflowSpec(data) {
|
|
|
9763
9763
|
${transition.condition ? ` condition: '${escapeString4(transition.condition)}',` : ""}
|
|
9764
9764
|
}`).join(`,
|
|
9765
9765
|
`);
|
|
9766
|
-
return `import type { WorkflowSpec } from '@contractspec/lib.contracts-spec/workflow';
|
|
9766
|
+
return `import type { WorkflowSpec } from '@contractspec/lib.contracts-spec/workflow/spec';
|
|
9767
9767
|
|
|
9768
9768
|
/**
|
|
9769
9769
|
* Workflow generated via contractspec CLI.
|
|
@@ -9836,11 +9836,9 @@ function generateWorkflowRunnerTemplate({
|
|
|
9836
9836
|
runnerName,
|
|
9837
9837
|
workflowName
|
|
9838
9838
|
}) {
|
|
9839
|
-
return `import {
|
|
9840
|
-
|
|
9841
|
-
|
|
9842
|
-
WorkflowRunner,
|
|
9843
|
-
} from '@contractspec/lib.contracts-spec/workflow';
|
|
9839
|
+
return `import { InMemoryStateStore } from '@contractspec/lib.contracts-spec/workflow/adapters';
|
|
9840
|
+
import { WorkflowRunner } from '@contractspec/lib.contracts-spec/workflow/runner';
|
|
9841
|
+
import { WorkflowRegistry } from '@contractspec/lib.contracts-spec/workflow/spec';
|
|
9844
9842
|
import { ${exportName} } from '${specImportPath}';
|
|
9845
9843
|
|
|
9846
9844
|
/**
|
|
@@ -14667,8 +14665,10 @@ __export(exports_upgrade, {
|
|
|
14667
14665
|
getPackageUpgradeCommand: () => getPackageUpgradeCommand,
|
|
14668
14666
|
getDefaultVersioningConfig: () => getDefaultVersioningConfig,
|
|
14669
14667
|
getDefaultHooksConfig: () => getDefaultHooksConfig,
|
|
14668
|
+
applyGuidedUpgrade: () => applyGuidedUpgrade,
|
|
14670
14669
|
applyConfigUpgrades: () => applyConfigUpgrades,
|
|
14671
|
-
analyzeUpgrades: () => analyzeUpgrades
|
|
14670
|
+
analyzeUpgrades: () => analyzeUpgrades,
|
|
14671
|
+
analyzeGuidedUpgrade: () => analyzeGuidedUpgrade
|
|
14672
14672
|
});
|
|
14673
14673
|
|
|
14674
14674
|
// src/services/upgrade/upgrade-service.ts
|
|
@@ -14676,14 +14676,16 @@ var API_HOST = process.env["CONTRACTSPEC_API_HOST"] ?? "api.contractspec.io";
|
|
|
14676
14676
|
var LATEST_SCHEMA_URL = `https://${API_HOST}/schemas/contractsrc.json`;
|
|
14677
14677
|
async function analyzeUpgrades(adapters, options) {
|
|
14678
14678
|
const { fs: fs5, logger: logger3 } = adapters;
|
|
14679
|
+
const packageRoot = findPackageRoot(options.workspaceRoot);
|
|
14679
14680
|
const workspaceRoot = findWorkspaceRoot(options.workspaceRoot);
|
|
14680
14681
|
const packageManager = detectPackageManager(workspaceRoot);
|
|
14681
14682
|
logger3.info("Analyzing available upgrades...", {
|
|
14682
14683
|
workspaceRoot,
|
|
14684
|
+
packageRoot,
|
|
14683
14685
|
packageManager
|
|
14684
14686
|
});
|
|
14685
|
-
const packages = await analyzePackages(fs5,
|
|
14686
|
-
const configUpgrades = await analyzeConfig(fs5,
|
|
14687
|
+
const packages = await analyzePackages(fs5, packageRoot);
|
|
14688
|
+
const configUpgrades = await analyzeConfig(fs5, packageRoot);
|
|
14687
14689
|
const hasUpgrades = packages.length > 0 || configUpgrades.length > 0;
|
|
14688
14690
|
return {
|
|
14689
14691
|
packageManager,
|
|
@@ -14762,6 +14764,22 @@ async function analyzeConfig(fs5, workspaceRoot) {
|
|
|
14762
14764
|
isNew: true
|
|
14763
14765
|
});
|
|
14764
14766
|
}
|
|
14767
|
+
if (!config["release"]) {
|
|
14768
|
+
upgrades.push({
|
|
14769
|
+
key: "release",
|
|
14770
|
+
currentValue: undefined,
|
|
14771
|
+
suggestedValue: getDefaultReleaseConfig(),
|
|
14772
|
+
isNew: true
|
|
14773
|
+
});
|
|
14774
|
+
}
|
|
14775
|
+
if (!config["upgrade"]) {
|
|
14776
|
+
upgrades.push({
|
|
14777
|
+
key: "upgrade",
|
|
14778
|
+
currentValue: undefined,
|
|
14779
|
+
suggestedValue: getDefaultUpgradeConfig(),
|
|
14780
|
+
isNew: true
|
|
14781
|
+
});
|
|
14782
|
+
}
|
|
14765
14783
|
return upgrades;
|
|
14766
14784
|
} catch {
|
|
14767
14785
|
return [];
|
|
@@ -14769,11 +14787,11 @@ async function analyzeConfig(fs5, workspaceRoot) {
|
|
|
14769
14787
|
}
|
|
14770
14788
|
async function applyConfigUpgrades(adapters, options) {
|
|
14771
14789
|
const { fs: fs5, logger: logger3 } = adapters;
|
|
14772
|
-
const
|
|
14790
|
+
const packageRoot = findPackageRoot(options.workspaceRoot);
|
|
14773
14791
|
if (options.dryRun) {
|
|
14774
14792
|
logger3.info("Dry run - no changes will be made");
|
|
14775
14793
|
}
|
|
14776
|
-
const configPath = fs5.join(
|
|
14794
|
+
const configPath = fs5.join(packageRoot, ".contractsrc.json");
|
|
14777
14795
|
if (!await fs5.exists(configPath)) {
|
|
14778
14796
|
return {
|
|
14779
14797
|
success: false,
|
|
@@ -14806,6 +14824,14 @@ async function applyConfigUpgrades(adapters, options) {
|
|
|
14806
14824
|
config["hooks"] = getDefaultHooksConfig();
|
|
14807
14825
|
sectionsUpgraded++;
|
|
14808
14826
|
}
|
|
14827
|
+
if (!config["release"]) {
|
|
14828
|
+
config["release"] = getDefaultReleaseConfig();
|
|
14829
|
+
sectionsUpgraded++;
|
|
14830
|
+
}
|
|
14831
|
+
if (!config["upgrade"]) {
|
|
14832
|
+
config["upgrade"] = getDefaultUpgradeConfig();
|
|
14833
|
+
sectionsUpgraded++;
|
|
14834
|
+
}
|
|
14809
14835
|
if (sectionsUpgraded === 0) {
|
|
14810
14836
|
return {
|
|
14811
14837
|
success: true,
|
|
@@ -14865,6 +14891,446 @@ function getDefaultHooksConfig() {
|
|
|
14865
14891
|
"pre-commit": ["contractspec validate", "contractspec integrity check"]
|
|
14866
14892
|
};
|
|
14867
14893
|
}
|
|
14894
|
+
function getDefaultReleaseConfig() {
|
|
14895
|
+
return {
|
|
14896
|
+
enforceOn: "release-branch",
|
|
14897
|
+
requireChangesetForPublished: true,
|
|
14898
|
+
requireReleaseCapsule: true,
|
|
14899
|
+
publishArtifacts: [
|
|
14900
|
+
"manifest.json",
|
|
14901
|
+
"patch-notes.md",
|
|
14902
|
+
"customer-guide.md",
|
|
14903
|
+
"upgrade-manifest.json"
|
|
14904
|
+
],
|
|
14905
|
+
agentTargets: ["codex"]
|
|
14906
|
+
};
|
|
14907
|
+
}
|
|
14908
|
+
function getDefaultUpgradeConfig() {
|
|
14909
|
+
return {
|
|
14910
|
+
manifestPaths: ["generated/releases/upgrade-manifest.json"],
|
|
14911
|
+
defaultAgentTarget: "codex",
|
|
14912
|
+
enableInteractiveGuidance: true,
|
|
14913
|
+
applyCodemods: true
|
|
14914
|
+
};
|
|
14915
|
+
}
|
|
14916
|
+
// src/services/upgrade/guided-upgrade.ts
|
|
14917
|
+
import {
|
|
14918
|
+
GeneratedReleaseManifestSchema
|
|
14919
|
+
} from "@contractspec/lib.contracts-spec";
|
|
14920
|
+
import {
|
|
14921
|
+
ContractsrcSchema as ContractsrcSchema3,
|
|
14922
|
+
DEFAULT_CONTRACTSRC as DEFAULT_CONTRACTSRC4
|
|
14923
|
+
} from "@contractspec/lib.contracts-spec/workspace-config/contractsrc-schema";
|
|
14924
|
+
|
|
14925
|
+
// src/services/versioning/release-plan.ts
|
|
14926
|
+
import {
|
|
14927
|
+
compareVersions,
|
|
14928
|
+
countUpgradePlanStepLevels,
|
|
14929
|
+
createAgentPromptBundles,
|
|
14930
|
+
dedupeUpgradePlanSteps,
|
|
14931
|
+
sortReleaseManifest
|
|
14932
|
+
} from "@contractspec/lib.contracts-spec";
|
|
14933
|
+
function selectUpgradeReleases(manifest, packages) {
|
|
14934
|
+
if (packages.length === 0) {
|
|
14935
|
+
return sortReleaseManifest(manifest);
|
|
14936
|
+
}
|
|
14937
|
+
const packageVersions = new Map(packages.map((pkg) => [pkg.name, pkg.currentVersion ?? "0.0.0"]));
|
|
14938
|
+
return sortReleaseManifest({
|
|
14939
|
+
...manifest,
|
|
14940
|
+
releases: manifest.releases.filter((release) => release.packages.some((pkg) => {
|
|
14941
|
+
const currentVersion = packageVersions.get(pkg.name);
|
|
14942
|
+
if (!currentVersion || !pkg.version) {
|
|
14943
|
+
return false;
|
|
14944
|
+
}
|
|
14945
|
+
return compareVersions(pkg.version, currentVersion) === 1;
|
|
14946
|
+
}))
|
|
14947
|
+
});
|
|
14948
|
+
}
|
|
14949
|
+
function createUpgradePlan(manifest, packages, agentTargets, renderPrompt) {
|
|
14950
|
+
const releases = selectUpgradeReleases(manifest, packages);
|
|
14951
|
+
const targetPackages = buildUpgradeTargets(releases, packages);
|
|
14952
|
+
const steps = dedupeUpgradePlanSteps(releases.flatMap((release) => release.upgradeSteps));
|
|
14953
|
+
const counts = countUpgradePlanStepLevels(steps);
|
|
14954
|
+
const basePlan = {
|
|
14955
|
+
generatedAt: new Date().toISOString(),
|
|
14956
|
+
targetPackages,
|
|
14957
|
+
releases,
|
|
14958
|
+
steps,
|
|
14959
|
+
autofixCount: counts.auto,
|
|
14960
|
+
manualCount: counts.manual,
|
|
14961
|
+
assistedCount: counts.assisted,
|
|
14962
|
+
agentPrompts: []
|
|
14963
|
+
};
|
|
14964
|
+
const agentPrompts = createAgentPromptBundles(basePlan, agentTargets, renderPrompt);
|
|
14965
|
+
return {
|
|
14966
|
+
...basePlan,
|
|
14967
|
+
agentPrompts
|
|
14968
|
+
};
|
|
14969
|
+
}
|
|
14970
|
+
function buildUpgradeTargets(releases, packages) {
|
|
14971
|
+
const currentVersions = new Map(packages.map((pkg) => [pkg.name, pkg.currentVersion]));
|
|
14972
|
+
const targets = new Map;
|
|
14973
|
+
for (const release of releases) {
|
|
14974
|
+
for (const pkg of release.packages) {
|
|
14975
|
+
const current = currentVersions.get(pkg.name);
|
|
14976
|
+
const existing = targets.get(pkg.name);
|
|
14977
|
+
if (!existing) {
|
|
14978
|
+
targets.set(pkg.name, {
|
|
14979
|
+
name: pkg.name,
|
|
14980
|
+
currentVersion: current,
|
|
14981
|
+
targetVersion: pkg.version
|
|
14982
|
+
});
|
|
14983
|
+
continue;
|
|
14984
|
+
}
|
|
14985
|
+
if (pkg.version && existing.targetVersion && compareVersions(pkg.version, existing.targetVersion) === 1) {
|
|
14986
|
+
existing.targetVersion = pkg.version;
|
|
14987
|
+
}
|
|
14988
|
+
}
|
|
14989
|
+
}
|
|
14990
|
+
return Array.from(targets.values()).sort((left, right) => left.name.localeCompare(right.name));
|
|
14991
|
+
}
|
|
14992
|
+
|
|
14993
|
+
// src/services/versioning/release-formatters.ts
|
|
14994
|
+
function formatPackages(entry) {
|
|
14995
|
+
return entry.packages.map((pkg) => {
|
|
14996
|
+
const version = pkg.version ? `@${pkg.version}` : "";
|
|
14997
|
+
return `- ${pkg.name}${version} (${pkg.releaseType})`;
|
|
14998
|
+
});
|
|
14999
|
+
}
|
|
15000
|
+
function formatSteps(steps) {
|
|
15001
|
+
return steps.map((step) => ` - ${step}`);
|
|
15002
|
+
}
|
|
15003
|
+
function renderMaintainerSummary(entry) {
|
|
15004
|
+
const lines = [
|
|
15005
|
+
`### ${entry.summary}`,
|
|
15006
|
+
`- Slug: ${entry.slug}`,
|
|
15007
|
+
`- Date: ${entry.date}`,
|
|
15008
|
+
`- Breaking: ${entry.isBreaking ? "yes" : "no"}`,
|
|
15009
|
+
...formatPackages(entry)
|
|
15010
|
+
];
|
|
15011
|
+
if (entry.deprecations.length > 0) {
|
|
15012
|
+
lines.push("- Deprecations:");
|
|
15013
|
+
lines.push(...entry.deprecations.map((item) => ` - ${item}`));
|
|
15014
|
+
}
|
|
15015
|
+
return lines.join(`
|
|
15016
|
+
`);
|
|
15017
|
+
}
|
|
15018
|
+
function renderCustomerPatchNote(entry) {
|
|
15019
|
+
const lines = [`### ${entry.summary}`, ...formatPackages(entry)];
|
|
15020
|
+
for (const instruction of entry.migrationInstructions) {
|
|
15021
|
+
lines.push(`- ${instruction.title}: ${instruction.summary}`);
|
|
15022
|
+
}
|
|
15023
|
+
return lines.join(`
|
|
15024
|
+
`);
|
|
15025
|
+
}
|
|
15026
|
+
function renderMigrationGuide(entry) {
|
|
15027
|
+
const lines = [`### ${entry.summary}`];
|
|
15028
|
+
if (entry.migrationInstructions.length === 0) {
|
|
15029
|
+
lines.push("- No manual migration steps recorded.");
|
|
15030
|
+
return lines.join(`
|
|
15031
|
+
`);
|
|
15032
|
+
}
|
|
15033
|
+
for (const instruction of entry.migrationInstructions) {
|
|
15034
|
+
lines.push(`- ${instruction.title}: ${instruction.summary}`);
|
|
15035
|
+
lines.push(...formatSteps(instruction.steps));
|
|
15036
|
+
}
|
|
15037
|
+
return lines.join(`
|
|
15038
|
+
`);
|
|
15039
|
+
}
|
|
15040
|
+
function renderPatchNotes(manifest) {
|
|
15041
|
+
return ["# Patch Notes", "", ...manifest.releases.map(renderMaintainerSummary)].join(`
|
|
15042
|
+
|
|
15043
|
+
`);
|
|
15044
|
+
}
|
|
15045
|
+
function renderCustomerGuide(manifest) {
|
|
15046
|
+
return [
|
|
15047
|
+
"# Customer Upgrade Guide",
|
|
15048
|
+
"",
|
|
15049
|
+
...manifest.releases.map(renderMigrationGuide)
|
|
15050
|
+
].join(`
|
|
15051
|
+
|
|
15052
|
+
`);
|
|
15053
|
+
}
|
|
15054
|
+
function renderUpgradeChecklist(plan) {
|
|
15055
|
+
if (plan.steps.length === 0) {
|
|
15056
|
+
return "- No release-managed upgrade steps were found.";
|
|
15057
|
+
}
|
|
15058
|
+
return plan.steps.map((step) => [`- [${step.level}] ${step.title}: ${step.summary}`, ...formatSteps(step.instructions)].join(`
|
|
15059
|
+
`)).join(`
|
|
15060
|
+
`);
|
|
15061
|
+
}
|
|
15062
|
+
function renderUpgradePrompt(agent, plan) {
|
|
15063
|
+
const targets = plan.targetPackages.map((pkg) => `${pkg.name}: ${pkg.currentVersion ?? "unknown"} -> ${pkg.targetVersion ?? "latest"}`).join(`
|
|
15064
|
+
`);
|
|
15065
|
+
return [
|
|
15066
|
+
`Apply the ContractSpec upgrade plan in this workspace using ${agent}.`,
|
|
15067
|
+
"",
|
|
15068
|
+
"Target packages:",
|
|
15069
|
+
targets || "- No package version targets were inferred.",
|
|
15070
|
+
"",
|
|
15071
|
+
"Required steps:",
|
|
15072
|
+
renderUpgradeChecklist(plan)
|
|
15073
|
+
].join(`
|
|
15074
|
+
`);
|
|
15075
|
+
}
|
|
15076
|
+
|
|
15077
|
+
// src/services/upgrade/guided-upgrade.ts
|
|
15078
|
+
var IMPORT_REWRITE_GLOB = "**/*.{ts,tsx,js,jsx,mjs,cjs}";
|
|
15079
|
+
var DEFAULT_UPGRADE_CONFIG = DEFAULT_CONTRACTSRC4.upgrade ?? {
|
|
15080
|
+
manifestPaths: ["generated/releases/upgrade-manifest.json"],
|
|
15081
|
+
defaultAgentTarget: "codex",
|
|
15082
|
+
enableInteractiveGuidance: true,
|
|
15083
|
+
applyCodemods: true
|
|
15084
|
+
};
|
|
15085
|
+
async function analyzeGuidedUpgrade(adapters, options) {
|
|
15086
|
+
const { fs: fs5 } = adapters;
|
|
15087
|
+
const packageRoot = findPackageRoot(options.workspaceRoot);
|
|
15088
|
+
const workspaceRoot = findWorkspaceRoot(options.workspaceRoot);
|
|
15089
|
+
const config = await readWorkspaceUpgradeConfig(fs5, packageRoot, workspaceRoot);
|
|
15090
|
+
const manifestResolution = await resolveUpgradeManifest(fs5, packageRoot, workspaceRoot, options.manifestPaths ?? config.manifestPaths);
|
|
15091
|
+
const analysis = await analyzeUpgrades(adapters, {
|
|
15092
|
+
workspaceRoot: packageRoot,
|
|
15093
|
+
dryRun: options.dryRun
|
|
15094
|
+
});
|
|
15095
|
+
const agentTargets = [
|
|
15096
|
+
options.agentTarget ?? config.defaultAgentTarget ?? "codex"
|
|
15097
|
+
];
|
|
15098
|
+
const plan = createUpgradePlan(manifestResolution.manifest, analysis.packages.map((pkg) => ({
|
|
15099
|
+
name: pkg.name,
|
|
15100
|
+
currentVersion: pkg.currentVersion
|
|
15101
|
+
})), agentTargets, renderUpgradePrompt);
|
|
15102
|
+
const augmentedPlan = augmentUpgradePlan(plan, analysis.packages, analysis.configUpgrades);
|
|
15103
|
+
return {
|
|
15104
|
+
packageManager: analysis.packageManager,
|
|
15105
|
+
manifestPath: manifestResolution.path,
|
|
15106
|
+
plan: augmentedPlan,
|
|
15107
|
+
humanChecklist: buildChecklist(augmentedPlan)
|
|
15108
|
+
};
|
|
15109
|
+
}
|
|
15110
|
+
async function applyGuidedUpgrade(adapters, options) {
|
|
15111
|
+
const { fs: fs5, logger: logger3 } = adapters;
|
|
15112
|
+
const packageRoot = findPackageRoot(options.workspaceRoot);
|
|
15113
|
+
const analysis = await analyzeGuidedUpgrade(adapters, options);
|
|
15114
|
+
const appliedAutofixes = [];
|
|
15115
|
+
if (!options.dryRun) {
|
|
15116
|
+
for (const step of analysis.plan.steps) {
|
|
15117
|
+
if (step.level !== "auto") {
|
|
15118
|
+
continue;
|
|
15119
|
+
}
|
|
15120
|
+
for (const fix of step.autofixes ?? []) {
|
|
15121
|
+
const applied = await applyAutofix(fs5, packageRoot, fix);
|
|
15122
|
+
if (applied) {
|
|
15123
|
+
appliedAutofixes.push(fix.id);
|
|
15124
|
+
}
|
|
15125
|
+
}
|
|
15126
|
+
}
|
|
15127
|
+
logger3.info("Applied guided upgrade autofixes", {
|
|
15128
|
+
count: appliedAutofixes.length,
|
|
15129
|
+
workspaceRoot: packageRoot
|
|
15130
|
+
});
|
|
15131
|
+
}
|
|
15132
|
+
const remainingSteps = analysis.plan.steps.filter((step) => step.level !== "auto");
|
|
15133
|
+
return {
|
|
15134
|
+
success: true,
|
|
15135
|
+
packagesUpgraded: appliedAutofixes.filter((id) => id.startsWith("pkg:")).length,
|
|
15136
|
+
configSectionsUpgraded: appliedAutofixes.filter((id) => id.startsWith("config:")).length,
|
|
15137
|
+
summary: options.dryRun ? `Would apply ${analysis.plan.autofixCount} deterministic upgrade step(s)` : `Applied ${appliedAutofixes.length} deterministic upgrade autofix(es)`,
|
|
15138
|
+
appliedAutofixes,
|
|
15139
|
+
remainingSteps,
|
|
15140
|
+
humanChecklist: analysis.humanChecklist,
|
|
15141
|
+
promptBundle: analysis.plan.agentPrompts[0],
|
|
15142
|
+
plan: analysis.plan,
|
|
15143
|
+
manifestPath: analysis.manifestPath
|
|
15144
|
+
};
|
|
15145
|
+
}
|
|
15146
|
+
async function resolveUpgradeManifest(fs5, packageRoot, workspaceRoot, manifestPaths) {
|
|
15147
|
+
const candidatePaths = manifestPaths ?? ["generated/releases/upgrade-manifest.json"];
|
|
15148
|
+
const searchRoots = Array.from(new Set([packageRoot, workspaceRoot]));
|
|
15149
|
+
for (const candidatePath of candidatePaths) {
|
|
15150
|
+
const resolvedPaths = candidatePath.startsWith("/") ? [candidatePath] : searchRoots.map((root) => fs5.join(root, candidatePath));
|
|
15151
|
+
for (const resolvedPath of resolvedPaths) {
|
|
15152
|
+
if (!await fs5.exists(resolvedPath)) {
|
|
15153
|
+
continue;
|
|
15154
|
+
}
|
|
15155
|
+
const manifest = GeneratedReleaseManifestSchema.parse(JSON.parse(await fs5.readFile(resolvedPath)));
|
|
15156
|
+
return { path: resolvedPath, manifest };
|
|
15157
|
+
}
|
|
15158
|
+
}
|
|
15159
|
+
return {
|
|
15160
|
+
manifest: GeneratedReleaseManifestSchema.parse({
|
|
15161
|
+
generatedAt: new Date().toISOString(),
|
|
15162
|
+
releases: []
|
|
15163
|
+
})
|
|
15164
|
+
};
|
|
15165
|
+
}
|
|
15166
|
+
async function readWorkspaceUpgradeConfig(fs5, packageRoot, workspaceRoot) {
|
|
15167
|
+
for (const configPath of Array.from(new Set([
|
|
15168
|
+
fs5.join(packageRoot, ".contractsrc.json"),
|
|
15169
|
+
fs5.join(workspaceRoot, ".contractsrc.json")
|
|
15170
|
+
]))) {
|
|
15171
|
+
if (!await fs5.exists(configPath)) {
|
|
15172
|
+
continue;
|
|
15173
|
+
}
|
|
15174
|
+
try {
|
|
15175
|
+
const parsed = JSON.parse(await fs5.readFile(configPath));
|
|
15176
|
+
const validated = ContractsrcSchema3.safeParse(parsed);
|
|
15177
|
+
if (validated.success) {
|
|
15178
|
+
return {
|
|
15179
|
+
...DEFAULT_UPGRADE_CONFIG,
|
|
15180
|
+
...validated.data.upgrade
|
|
15181
|
+
};
|
|
15182
|
+
}
|
|
15183
|
+
} catch {
|
|
15184
|
+
continue;
|
|
15185
|
+
}
|
|
15186
|
+
}
|
|
15187
|
+
return DEFAULT_UPGRADE_CONFIG;
|
|
15188
|
+
}
|
|
15189
|
+
function augmentUpgradePlan(plan, packages, configUpgrades) {
|
|
15190
|
+
const packageFixes = [];
|
|
15191
|
+
for (const pkg of packages) {
|
|
15192
|
+
const target = plan.targetPackages.find((entry) => entry.name === pkg.name);
|
|
15193
|
+
if (!target?.targetVersion || target.targetVersion === pkg.currentVersion) {
|
|
15194
|
+
continue;
|
|
15195
|
+
}
|
|
15196
|
+
packageFixes.push({
|
|
15197
|
+
id: `pkg:${pkg.name}`,
|
|
15198
|
+
kind: "package-json",
|
|
15199
|
+
title: `Update ${pkg.name}`,
|
|
15200
|
+
summary: `Update ${pkg.name} to ${target.targetVersion}`,
|
|
15201
|
+
packageName: pkg.name,
|
|
15202
|
+
dependencyType: pkg.isDevDependency ? "devDependencies" : "dependencies",
|
|
15203
|
+
from: pkg.currentVersion,
|
|
15204
|
+
to: target.targetVersion
|
|
15205
|
+
});
|
|
15206
|
+
}
|
|
15207
|
+
const configFixes = configUpgrades.map((upgrade) => ({
|
|
15208
|
+
id: `config:${upgrade.key}`,
|
|
15209
|
+
kind: "contractsrc",
|
|
15210
|
+
title: `Update ${upgrade.key}`,
|
|
15211
|
+
summary: `Update .contractsrc.json at ${upgrade.key}`,
|
|
15212
|
+
configPath: upgrade.key,
|
|
15213
|
+
value: upgrade.suggestedValue
|
|
15214
|
+
}));
|
|
15215
|
+
const implicitSteps = [];
|
|
15216
|
+
if (packageFixes.length > 0) {
|
|
15217
|
+
implicitSteps.push({
|
|
15218
|
+
id: "upgrade-contractspec-packages",
|
|
15219
|
+
title: "Update ContractSpec packages",
|
|
15220
|
+
summary: "Apply package version upgrades from the release manifest.",
|
|
15221
|
+
level: "auto",
|
|
15222
|
+
instructions: packageFixes.map((fix) => `${fix.packageName}: ${fix.from ?? "current"} -> ${fix.to ?? "latest"}`),
|
|
15223
|
+
packages: packageFixes.map((fix) => fix.packageName ?? ""),
|
|
15224
|
+
autofixes: packageFixes
|
|
15225
|
+
});
|
|
15226
|
+
}
|
|
15227
|
+
if (configFixes.length > 0) {
|
|
15228
|
+
implicitSteps.push({
|
|
15229
|
+
id: "upgrade-contractsrc-config",
|
|
15230
|
+
title: "Refresh .contractsrc.json defaults",
|
|
15231
|
+
summary: "Bring workspace release and upgrade config in line with current defaults.",
|
|
15232
|
+
level: "auto",
|
|
15233
|
+
instructions: configFixes.map((fix) => `${fix.configPath} -> updated`),
|
|
15234
|
+
autofixes: configFixes
|
|
15235
|
+
});
|
|
15236
|
+
}
|
|
15237
|
+
const steps = [...implicitSteps, ...plan.steps];
|
|
15238
|
+
const autoCount = steps.filter((step) => step.level === "auto").length;
|
|
15239
|
+
const assistedCount = steps.filter((step) => step.level === "assisted").length;
|
|
15240
|
+
const manualCount = steps.filter((step) => step.level === "manual").length;
|
|
15241
|
+
return {
|
|
15242
|
+
...plan,
|
|
15243
|
+
steps,
|
|
15244
|
+
autofixCount: autoCount,
|
|
15245
|
+
assistedCount,
|
|
15246
|
+
manualCount,
|
|
15247
|
+
agentPrompts: createUpgradePlan({
|
|
15248
|
+
generatedAt: plan.generatedAt,
|
|
15249
|
+
releases: plan.releases
|
|
15250
|
+
}, plan.targetPackages.map((pkg) => ({
|
|
15251
|
+
name: pkg.name,
|
|
15252
|
+
currentVersion: pkg.currentVersion
|
|
15253
|
+
})), plan.agentPrompts.map((prompt) => prompt.agent), renderUpgradePrompt).agentPrompts
|
|
15254
|
+
};
|
|
15255
|
+
}
|
|
15256
|
+
function buildChecklist(plan) {
|
|
15257
|
+
return plan.steps.map((step) => `${step.title}: ${step.summary}`);
|
|
15258
|
+
}
|
|
15259
|
+
async function applyAutofix(fs5, workspaceRoot, fix) {
|
|
15260
|
+
switch (fix.kind) {
|
|
15261
|
+
case "package-json":
|
|
15262
|
+
return applyPackageJsonAutofix(fs5, workspaceRoot, fix);
|
|
15263
|
+
case "contractsrc":
|
|
15264
|
+
return applyContractsrcAutofix(fs5, workspaceRoot, fix);
|
|
15265
|
+
case "import-rewrite":
|
|
15266
|
+
return applyImportRewriteAutofix(fs5, workspaceRoot, fix);
|
|
15267
|
+
case "codemod":
|
|
15268
|
+
return false;
|
|
15269
|
+
}
|
|
15270
|
+
}
|
|
15271
|
+
async function applyPackageJsonAutofix(fs5, workspaceRoot, fix) {
|
|
15272
|
+
const packageJsonPath = fs5.join(workspaceRoot, "package.json");
|
|
15273
|
+
if (!await fs5.exists(packageJsonPath)) {
|
|
15274
|
+
return false;
|
|
15275
|
+
}
|
|
15276
|
+
const packageJson = JSON.parse(await fs5.readFile(packageJsonPath));
|
|
15277
|
+
const dependencyType = fix.dependencyType ?? "dependencies";
|
|
15278
|
+
const dependencies2 = packageJson[dependencyType] ?? {};
|
|
15279
|
+
if (!fix.packageName || !fix.to || !dependencies2[fix.packageName]) {
|
|
15280
|
+
return false;
|
|
15281
|
+
}
|
|
15282
|
+
dependencies2[fix.packageName] = fix.to;
|
|
15283
|
+
packageJson[dependencyType] = dependencies2;
|
|
15284
|
+
await fs5.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2) + `
|
|
15285
|
+
`);
|
|
15286
|
+
return true;
|
|
15287
|
+
}
|
|
15288
|
+
async function applyContractsrcAutofix(fs5, workspaceRoot, fix) {
|
|
15289
|
+
const configPath = fs5.join(workspaceRoot, ".contractsrc.json");
|
|
15290
|
+
const config = await fs5.exists(configPath) ? JSON.parse(await fs5.readFile(configPath)) : {};
|
|
15291
|
+
if (!fix.configPath) {
|
|
15292
|
+
return false;
|
|
15293
|
+
}
|
|
15294
|
+
setNestedValue(config, fix.configPath, fix.value);
|
|
15295
|
+
await fs5.writeFile(configPath, JSON.stringify(config, null, 2) + `
|
|
15296
|
+
`);
|
|
15297
|
+
return true;
|
|
15298
|
+
}
|
|
15299
|
+
async function applyImportRewriteAutofix(fs5, workspaceRoot, fix) {
|
|
15300
|
+
if (!fix.from || !fix.to) {
|
|
15301
|
+
return false;
|
|
15302
|
+
}
|
|
15303
|
+
const files = await fs5.glob({
|
|
15304
|
+
pattern: fix.path ?? IMPORT_REWRITE_GLOB,
|
|
15305
|
+
cwd: workspaceRoot,
|
|
15306
|
+
absolute: true
|
|
15307
|
+
});
|
|
15308
|
+
let changed = false;
|
|
15309
|
+
for (const filePath of files) {
|
|
15310
|
+
const content = await fs5.readFile(filePath);
|
|
15311
|
+
if (!content.includes(fix.from)) {
|
|
15312
|
+
continue;
|
|
15313
|
+
}
|
|
15314
|
+
await fs5.writeFile(filePath, content.split(fix.from).join(fix.to));
|
|
15315
|
+
changed = true;
|
|
15316
|
+
}
|
|
15317
|
+
return changed;
|
|
15318
|
+
}
|
|
15319
|
+
function setNestedValue(target, path9, value) {
|
|
15320
|
+
const segments = path9.split(".");
|
|
15321
|
+
let current = target;
|
|
15322
|
+
for (const segment of segments.slice(0, -1)) {
|
|
15323
|
+
const next = current[segment];
|
|
15324
|
+
if (!next || typeof next !== "object" || Array.isArray(next)) {
|
|
15325
|
+
current[segment] = {};
|
|
15326
|
+
}
|
|
15327
|
+
current = current[segment];
|
|
15328
|
+
}
|
|
15329
|
+
const lastSegment = segments.at(-1);
|
|
15330
|
+
if (lastSegment) {
|
|
15331
|
+
current[lastSegment] = value;
|
|
15332
|
+
}
|
|
15333
|
+
}
|
|
14868
15334
|
// src/services/verification-cache/adapters/filesystem.ts
|
|
14869
15335
|
import {
|
|
14870
15336
|
existsSync as existsSync3,
|
|
@@ -16233,8 +16699,16 @@ var verifyService = new VerifyService;
|
|
|
16233
16699
|
// src/services/versioning/index.ts
|
|
16234
16700
|
var exports_versioning = {};
|
|
16235
16701
|
__export(exports_versioning, {
|
|
16702
|
+
renderUpgradePrompt: () => renderUpgradePrompt,
|
|
16703
|
+
renderUpgradeChecklist: () => renderUpgradeChecklist,
|
|
16704
|
+
renderPatchNotes: () => renderPatchNotes,
|
|
16705
|
+
renderMigrationGuide: () => renderMigrationGuide,
|
|
16706
|
+
renderMaintainerSummary: () => renderMaintainerSummary,
|
|
16707
|
+
renderCustomerPatchNote: () => renderCustomerPatchNote,
|
|
16708
|
+
renderCustomerGuide: () => renderCustomerGuide,
|
|
16236
16709
|
parseConventionalCommit: () => parseConventionalCommit,
|
|
16237
16710
|
isConventionalCommit: () => isConventionalCommit,
|
|
16711
|
+
initReleaseArtifacts: () => initReleaseArtifacts,
|
|
16238
16712
|
getHighestBumpType: () => getHighestBumpType,
|
|
16239
16713
|
getBumpTypeFromCommit: () => getBumpTypeFromCommit,
|
|
16240
16714
|
generateChangelogs: () => generateChangelogs,
|
|
@@ -16245,6 +16719,8 @@ __export(exports_versioning, {
|
|
|
16245
16719
|
filterBumpableCommits: () => filterBumpableCommits,
|
|
16246
16720
|
commitsToChangeEntries: () => commitsToChangeEntries,
|
|
16247
16721
|
commitToChangeEntry: () => commitToChangeEntry,
|
|
16722
|
+
checkReleaseArtifacts: () => checkReleaseArtifacts,
|
|
16723
|
+
buildReleaseArtifacts: () => buildReleaseArtifacts,
|
|
16248
16724
|
applyVersionBump: () => applyVersionBump,
|
|
16249
16725
|
analyzeVersionsFromCommits: () => analyzeVersionsFromCommits,
|
|
16250
16726
|
analyzeVersions: () => analyzeVersions,
|
|
@@ -17021,6 +17497,390 @@ function generateRandomName() {
|
|
|
17021
17497
|
const random = Math.floor(Math.random() * 1000);
|
|
17022
17498
|
return `${adj}-${noun}-${verb}-${random}`;
|
|
17023
17499
|
}
|
|
17500
|
+
// src/services/versioning/release-service.ts
|
|
17501
|
+
import {
|
|
17502
|
+
GeneratedReleaseManifestSchema as GeneratedReleaseManifestSchema2
|
|
17503
|
+
} from "@contractspec/lib.contracts-spec";
|
|
17504
|
+
import {
|
|
17505
|
+
ContractsrcSchema as ContractsrcSchema4,
|
|
17506
|
+
DEFAULT_CONTRACTSRC as DEFAULT_CONTRACTSRC5
|
|
17507
|
+
} from "@contractspec/lib.contracts-spec/workspace-config/contractsrc-schema";
|
|
17508
|
+
|
|
17509
|
+
// src/services/versioning/release-files.ts
|
|
17510
|
+
import { ReleaseCapsuleSchema } from "@contractspec/lib.contracts-spec";
|
|
17511
|
+
import { dump, load } from "js-yaml";
|
|
17512
|
+
var CHANGESET_PATTERN = ".changeset/*.md";
|
|
17513
|
+
var RELEASE_CAPSULE_PATTERN = ".changeset/*.release.yaml";
|
|
17514
|
+
var CHANGESET_FRONTMATTER = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/;
|
|
17515
|
+
var CHANGESET_RELEASE_LINE = /^["']?([^"':]+(?:\/[^"':]+)?)["']?\s*:\s*(major|minor|patch|none)\s*$/;
|
|
17516
|
+
async function listChangesetFiles(fs5, workspaceRoot) {
|
|
17517
|
+
const files = await fs5.glob({
|
|
17518
|
+
pattern: CHANGESET_PATTERN,
|
|
17519
|
+
cwd: workspaceRoot,
|
|
17520
|
+
absolute: true
|
|
17521
|
+
});
|
|
17522
|
+
return files.filter((file) => fs5.basename(file) !== "README.md");
|
|
17523
|
+
}
|
|
17524
|
+
async function readChangesets(fs5, workspaceRoot) {
|
|
17525
|
+
const files = await listChangesetFiles(fs5, workspaceRoot);
|
|
17526
|
+
const changesets = [];
|
|
17527
|
+
for (const filePath of files) {
|
|
17528
|
+
const content = await fs5.readFile(filePath);
|
|
17529
|
+
changesets.push(parseChangesetContent(fs5.basename(filePath), content));
|
|
17530
|
+
}
|
|
17531
|
+
return changesets;
|
|
17532
|
+
}
|
|
17533
|
+
async function readReleaseCapsules(fs5, workspaceRoot, changesets) {
|
|
17534
|
+
const files = await fs5.glob({
|
|
17535
|
+
pattern: RELEASE_CAPSULE_PATTERN,
|
|
17536
|
+
cwd: workspaceRoot,
|
|
17537
|
+
absolute: true
|
|
17538
|
+
});
|
|
17539
|
+
const fallbackPackages = new Map(changesets.map((changeset) => [changeset.slug, changeset.packages]));
|
|
17540
|
+
const capsules = new Map;
|
|
17541
|
+
for (const filePath of files) {
|
|
17542
|
+
const slug = fs5.basename(filePath).replace(/\.release\.yaml$/, "");
|
|
17543
|
+
const parsed = load(await fs5.readFile(filePath));
|
|
17544
|
+
const capsule = normalizeCapsule(parsed, slug, fallbackPackages.get(slug) ?? []);
|
|
17545
|
+
capsules.set(slug, capsule);
|
|
17546
|
+
}
|
|
17547
|
+
return capsules;
|
|
17548
|
+
}
|
|
17549
|
+
async function discoverWorkspacePackages(fs5, workspaceRoot) {
|
|
17550
|
+
const packageJsonFiles = await fs5.glob({
|
|
17551
|
+
patterns: ["package.json", "packages/*/*/package.json"],
|
|
17552
|
+
cwd: workspaceRoot,
|
|
17553
|
+
absolute: true
|
|
17554
|
+
});
|
|
17555
|
+
const packages = [];
|
|
17556
|
+
for (const filePath of packageJsonFiles) {
|
|
17557
|
+
const manifest = JSON.parse(await fs5.readFile(filePath));
|
|
17558
|
+
if (!manifest.name || !manifest.version || manifest.private === true) {
|
|
17559
|
+
continue;
|
|
17560
|
+
}
|
|
17561
|
+
const dir = fs5.relative(workspaceRoot, fs5.dirname(filePath)) || ".";
|
|
17562
|
+
packages.push({ name: manifest.name, dir, version: manifest.version });
|
|
17563
|
+
}
|
|
17564
|
+
return packages.sort((left, right) => left.dir.localeCompare(right.dir));
|
|
17565
|
+
}
|
|
17566
|
+
function matchChangedFilesToPackages(changedFiles, packages) {
|
|
17567
|
+
const names = new Set;
|
|
17568
|
+
for (const changedFile of changedFiles) {
|
|
17569
|
+
const match = packages.find((pkg) => pkg.dir === "." ? !changedFile.startsWith("packages/") : changedFile === pkg.dir || changedFile.startsWith(`${pkg.dir}/`));
|
|
17570
|
+
if (match) {
|
|
17571
|
+
names.add(match.name);
|
|
17572
|
+
}
|
|
17573
|
+
}
|
|
17574
|
+
return Array.from(names).sort((left, right) => left.localeCompare(right));
|
|
17575
|
+
}
|
|
17576
|
+
function renderChangesetMarkdown(summary, packages) {
|
|
17577
|
+
const header = packages.map((pkg) => `"${pkg.name}": ${pkg.releaseType}`).join(`
|
|
17578
|
+
`);
|
|
17579
|
+
return `---
|
|
17580
|
+
${header}
|
|
17581
|
+
---
|
|
17582
|
+
|
|
17583
|
+
${summary}
|
|
17584
|
+
`;
|
|
17585
|
+
}
|
|
17586
|
+
function renderReleaseCapsuleYaml(capsule) {
|
|
17587
|
+
return dump(ReleaseCapsuleSchema.parse(capsule), {
|
|
17588
|
+
noRefs: true,
|
|
17589
|
+
lineWidth: 100
|
|
17590
|
+
});
|
|
17591
|
+
}
|
|
17592
|
+
function parseChangesetContent(fileName, content) {
|
|
17593
|
+
const slug = fileName.replace(/\.md$/, "");
|
|
17594
|
+
const match = content.match(CHANGESET_FRONTMATTER);
|
|
17595
|
+
if (!match) {
|
|
17596
|
+
return { slug, summary: content.trim(), packages: [] };
|
|
17597
|
+
}
|
|
17598
|
+
const header = match[1] ?? "";
|
|
17599
|
+
const summary = (match[2] ?? "").trim();
|
|
17600
|
+
const packages = [];
|
|
17601
|
+
for (const line of header.split(`
|
|
17602
|
+
`)) {
|
|
17603
|
+
const parsed = line.trim().match(CHANGESET_RELEASE_LINE);
|
|
17604
|
+
if (!parsed) {
|
|
17605
|
+
continue;
|
|
17606
|
+
}
|
|
17607
|
+
const packageName = parsed[1];
|
|
17608
|
+
const releaseType = parsed[2];
|
|
17609
|
+
if (!packageName || releaseType === "none") {
|
|
17610
|
+
continue;
|
|
17611
|
+
}
|
|
17612
|
+
packages.push({ name: packageName, releaseType });
|
|
17613
|
+
}
|
|
17614
|
+
return { slug, summary, packages };
|
|
17615
|
+
}
|
|
17616
|
+
function normalizeCapsule(value, slug, fallbackPackages) {
|
|
17617
|
+
const record = typeof value === "object" && value !== null ? value : {};
|
|
17618
|
+
const legacyPackageNames = Array.isArray(record["packageNames"]) ? record["packageNames"].filter((entry) => typeof entry === "string").map((name) => ({
|
|
17619
|
+
name,
|
|
17620
|
+
releaseType: record["releaseType"] ?? "patch"
|
|
17621
|
+
})) : [];
|
|
17622
|
+
return ReleaseCapsuleSchema.parse({
|
|
17623
|
+
...record,
|
|
17624
|
+
slug,
|
|
17625
|
+
packages: Array.isArray(record["packages"]) && record["packages"].length > 0 ? record["packages"] : legacyPackageNames.length > 0 ? legacyPackageNames : fallbackPackages
|
|
17626
|
+
});
|
|
17627
|
+
}
|
|
17628
|
+
|
|
17629
|
+
// src/services/versioning/release-service.ts
|
|
17630
|
+
var DEFAULT_OUTPUT_DIR = "generated/releases";
|
|
17631
|
+
var DEFAULT_RELEASE_CONFIG = DEFAULT_CONTRACTSRC5.release ?? {
|
|
17632
|
+
enforceOn: "release-branch",
|
|
17633
|
+
requireChangesetForPublished: true,
|
|
17634
|
+
requireReleaseCapsule: true,
|
|
17635
|
+
publishArtifacts: [],
|
|
17636
|
+
agentTargets: ["codex"]
|
|
17637
|
+
};
|
|
17638
|
+
async function initReleaseArtifacts(adapters2, options = {}) {
|
|
17639
|
+
const { fs: fs5, git: git3, logger: logger3 } = adapters2;
|
|
17640
|
+
const workspaceRoot = findWorkspaceRoot(options.workspaceRoot);
|
|
17641
|
+
const workspacePackages = await discoverWorkspacePackages(fs5, workspaceRoot);
|
|
17642
|
+
const changedFiles = options.baseline ? await git3.diffFiles(options.baseline) : [];
|
|
17643
|
+
const packageNames = options.packages && options.packages.length > 0 ? options.packages : matchChangedFilesToPackages(changedFiles, workspacePackages);
|
|
17644
|
+
const commitAnalysis = options.baseline ? await analyzeVersionsFromCommits(adapters2, {
|
|
17645
|
+
baseline: options.baseline,
|
|
17646
|
+
workspaceRoot
|
|
17647
|
+
}) : null;
|
|
17648
|
+
const impact = options.baseline ? await detectImpact(adapters2, {
|
|
17649
|
+
baseline: options.baseline,
|
|
17650
|
+
workspaceRoot
|
|
17651
|
+
}) : null;
|
|
17652
|
+
const inferredReleaseType = (impact?.summary.breaking ?? 0) > 0 ? "major" : commitAnalysis?.suggestedBumpType ?? inferReleaseType(packageNames);
|
|
17653
|
+
const releaseType = options.releaseType ?? inferredReleaseType;
|
|
17654
|
+
const summary = options.summary ?? `Describe the ${releaseType} release for ${packageNames[0] ?? "the workspace"}`;
|
|
17655
|
+
const slug = options.slug ?? slugify(summary);
|
|
17656
|
+
const packages = packageNames.map((name) => ({
|
|
17657
|
+
name,
|
|
17658
|
+
releaseType,
|
|
17659
|
+
version: workspacePackages.find((pkg) => pkg.name === name)?.version
|
|
17660
|
+
}));
|
|
17661
|
+
const capsule = {
|
|
17662
|
+
schemaVersion: "1",
|
|
17663
|
+
slug,
|
|
17664
|
+
summary,
|
|
17665
|
+
isBreaking: releaseType === "major" || (impact?.summary.breaking ?? 0) > 0,
|
|
17666
|
+
packages,
|
|
17667
|
+
affectedRuntimes: [],
|
|
17668
|
+
affectedFrameworks: [],
|
|
17669
|
+
audiences: [
|
|
17670
|
+
{ kind: "maintainer", summary },
|
|
17671
|
+
{ kind: "customer", summary }
|
|
17672
|
+
],
|
|
17673
|
+
deprecations: [],
|
|
17674
|
+
migrationInstructions: [],
|
|
17675
|
+
upgradeSteps: [],
|
|
17676
|
+
validation: {
|
|
17677
|
+
commands: [
|
|
17678
|
+
"contractspec impact --baseline main --format markdown",
|
|
17679
|
+
"contractspec version analyze --baseline main"
|
|
17680
|
+
],
|
|
17681
|
+
evidence: []
|
|
17682
|
+
}
|
|
17683
|
+
};
|
|
17684
|
+
const changesetPath = fs5.join(workspaceRoot, ".changeset", `${slug}.md`);
|
|
17685
|
+
const capsulePath = fs5.join(workspaceRoot, ".changeset", `${slug}.release.yaml`);
|
|
17686
|
+
if (!options.force) {
|
|
17687
|
+
if (await fs5.exists(changesetPath)) {
|
|
17688
|
+
throw new Error(`Changeset already exists: ${changesetPath}`);
|
|
17689
|
+
}
|
|
17690
|
+
if (await fs5.exists(capsulePath)) {
|
|
17691
|
+
throw new Error(`Release capsule already exists: ${capsulePath}`);
|
|
17692
|
+
}
|
|
17693
|
+
}
|
|
17694
|
+
const changesetContent = renderChangesetMarkdown(summary, packages);
|
|
17695
|
+
const capsuleContent = renderReleaseCapsuleYaml(capsule);
|
|
17696
|
+
if (!options.dryRun) {
|
|
17697
|
+
await fs5.writeFile(changesetPath, changesetContent);
|
|
17698
|
+
await fs5.writeFile(capsulePath, capsuleContent);
|
|
17699
|
+
}
|
|
17700
|
+
logger3.info("Initialized release artifacts", { slug, workspaceRoot });
|
|
17701
|
+
return {
|
|
17702
|
+
slug,
|
|
17703
|
+
changesetPath,
|
|
17704
|
+
capsulePath,
|
|
17705
|
+
changesetContent,
|
|
17706
|
+
capsuleContent,
|
|
17707
|
+
packages,
|
|
17708
|
+
releaseType,
|
|
17709
|
+
isBreaking: capsule.isBreaking
|
|
17710
|
+
};
|
|
17711
|
+
}
|
|
17712
|
+
async function buildReleaseArtifacts(adapters2, options = {}) {
|
|
17713
|
+
const { fs: fs5, logger: logger3 } = adapters2;
|
|
17714
|
+
const workspaceRoot = findWorkspaceRoot(options.workspaceRoot);
|
|
17715
|
+
const config = await readWorkspaceReleaseConfig(fs5, workspaceRoot);
|
|
17716
|
+
const outputDir = fs5.join(workspaceRoot, options.outputDir ?? DEFAULT_OUTPUT_DIR);
|
|
17717
|
+
const changesets = await readChangesets(fs5, workspaceRoot);
|
|
17718
|
+
const capsules = await readReleaseCapsules(fs5, workspaceRoot, changesets);
|
|
17719
|
+
const workspacePackages = await discoverWorkspacePackages(fs5, workspaceRoot);
|
|
17720
|
+
const releases = await Promise.all(Array.from(capsules.entries()).map(async ([slug, capsule]) => {
|
|
17721
|
+
const filePath = fs5.join(workspaceRoot, ".changeset", `${slug}.release.yaml`);
|
|
17722
|
+
const stats = await fs5.stat(filePath);
|
|
17723
|
+
return {
|
|
17724
|
+
slug,
|
|
17725
|
+
version: resolveReleaseVersion(capsule, workspacePackages),
|
|
17726
|
+
summary: capsule.summary,
|
|
17727
|
+
date: stats.mtime.toISOString().split("T")[0] ?? new Date().toISOString(),
|
|
17728
|
+
isBreaking: capsule.isBreaking,
|
|
17729
|
+
packages: capsule.packages.map((pkg) => ({
|
|
17730
|
+
...pkg,
|
|
17731
|
+
version: workspacePackages.find((candidate) => candidate.name === pkg.name)?.version ?? pkg.version
|
|
17732
|
+
})),
|
|
17733
|
+
affectedRuntimes: [...capsule.affectedRuntimes],
|
|
17734
|
+
affectedFrameworks: [...capsule.affectedFrameworks],
|
|
17735
|
+
audiences: [...capsule.audiences],
|
|
17736
|
+
deprecations: [...capsule.deprecations],
|
|
17737
|
+
migrationInstructions: [...capsule.migrationInstructions],
|
|
17738
|
+
upgradeSteps: [...capsule.upgradeSteps],
|
|
17739
|
+
validation: capsule.validation
|
|
17740
|
+
};
|
|
17741
|
+
}));
|
|
17742
|
+
const manifest = GeneratedReleaseManifestSchema2.parse({
|
|
17743
|
+
generatedAt: new Date().toISOString(),
|
|
17744
|
+
releases
|
|
17745
|
+
});
|
|
17746
|
+
const agentTargets = options.agentTargets ?? config.agentTargets ?? ["codex"];
|
|
17747
|
+
const upgradePlan = createUpgradePlan(manifest, [], agentTargets, renderUpgradePrompt);
|
|
17748
|
+
const manifestPath = fs5.join(outputDir, "manifest.json");
|
|
17749
|
+
const upgradeManifestPath = fs5.join(outputDir, "upgrade-manifest.json");
|
|
17750
|
+
const patchNotesPath = fs5.join(outputDir, "patch-notes.md");
|
|
17751
|
+
const customerGuidePath = fs5.join(outputDir, "customer-guide.md");
|
|
17752
|
+
const promptPaths = Object.fromEntries(upgradePlan.agentPrompts.map((prompt) => [
|
|
17753
|
+
prompt.agent,
|
|
17754
|
+
fs5.join(outputDir, "prompts", `${prompt.agent}.md`)
|
|
17755
|
+
]));
|
|
17756
|
+
if (!options.dryRun) {
|
|
17757
|
+
await fs5.writeFile(manifestPath, JSON.stringify(manifest, null, 2));
|
|
17758
|
+
await fs5.writeFile(upgradeManifestPath, JSON.stringify(manifest, null, 2));
|
|
17759
|
+
await fs5.writeFile(patchNotesPath, renderPatchNotes(manifest));
|
|
17760
|
+
await fs5.writeFile(customerGuidePath, renderCustomerGuide(manifest));
|
|
17761
|
+
for (const prompt of upgradePlan.agentPrompts) {
|
|
17762
|
+
await fs5.writeFile(promptPaths[prompt.agent] ?? "", prompt.prompt);
|
|
17763
|
+
}
|
|
17764
|
+
}
|
|
17765
|
+
logger3.info("Built release artifacts", {
|
|
17766
|
+
outputDir,
|
|
17767
|
+
releasesBuilt: manifest.releases.length
|
|
17768
|
+
});
|
|
17769
|
+
return {
|
|
17770
|
+
outputDir,
|
|
17771
|
+
manifestPath,
|
|
17772
|
+
upgradeManifestPath,
|
|
17773
|
+
patchNotesPath,
|
|
17774
|
+
customerGuidePath,
|
|
17775
|
+
promptPaths,
|
|
17776
|
+
manifest,
|
|
17777
|
+
upgradePlan,
|
|
17778
|
+
releasesBuilt: manifest.releases.length
|
|
17779
|
+
};
|
|
17780
|
+
}
|
|
17781
|
+
async function checkReleaseArtifacts(adapters2, options = {}) {
|
|
17782
|
+
const { fs: fs5 } = adapters2;
|
|
17783
|
+
const workspaceRoot = findWorkspaceRoot(options.workspaceRoot);
|
|
17784
|
+
const config = await readWorkspaceReleaseConfig(fs5, workspaceRoot);
|
|
17785
|
+
const outputDir = fs5.join(workspaceRoot, options.outputDir ?? DEFAULT_OUTPUT_DIR);
|
|
17786
|
+
const checks = [];
|
|
17787
|
+
const warnings = [];
|
|
17788
|
+
const errors = [];
|
|
17789
|
+
const changesets = await readChangesets(fs5, workspaceRoot);
|
|
17790
|
+
const capsules = await readReleaseCapsules(fs5, workspaceRoot, changesets);
|
|
17791
|
+
recordCheck(checks, changesets.length > 0, "changesets", changesets.length > 0 ? `Found ${changesets.length} release changeset(s).` : "No release changesets found.");
|
|
17792
|
+
for (const changeset of changesets) {
|
|
17793
|
+
if (!capsules.has(changeset.slug) && config.requireReleaseCapsule) {
|
|
17794
|
+
errors.push(`Missing release capsule for changeset ${changeset.slug}.`);
|
|
17795
|
+
}
|
|
17796
|
+
}
|
|
17797
|
+
for (const [slug, capsule] of capsules) {
|
|
17798
|
+
if (capsule.isBreaking && capsule.migrationInstructions.length === 0) {
|
|
17799
|
+
errors.push(`Breaking release ${slug} is missing migration instructions.`);
|
|
17800
|
+
}
|
|
17801
|
+
if (capsule.validation.commands.length === 0) {
|
|
17802
|
+
errors.push(`Release capsule ${slug} is missing validation commands.`);
|
|
17803
|
+
}
|
|
17804
|
+
if (capsule.validation.evidence.length === 0) {
|
|
17805
|
+
errors.push(`Release capsule ${slug} is missing validation evidence.`);
|
|
17806
|
+
}
|
|
17807
|
+
}
|
|
17808
|
+
if (options.baseline) {
|
|
17809
|
+
try {
|
|
17810
|
+
const impact = await detectImpact(adapters2, {
|
|
17811
|
+
baseline: options.baseline,
|
|
17812
|
+
workspaceRoot
|
|
17813
|
+
});
|
|
17814
|
+
recordCheck(checks, true, "impact", `Impact status: ${impact.status}`);
|
|
17815
|
+
if (impact.summary.breaking > 0 && !Array.from(capsules.values()).some((capsule) => capsule.isBreaking)) {
|
|
17816
|
+
errors.push("Breaking impact detected without a breaking release capsule.");
|
|
17817
|
+
}
|
|
17818
|
+
} catch (error) {
|
|
17819
|
+
recordCheck(checks, false, "impact", failureMessage(error));
|
|
17820
|
+
errors.push(`Impact detection failed: ${failureMessage(error)}`);
|
|
17821
|
+
}
|
|
17822
|
+
try {
|
|
17823
|
+
const analysis = await analyzeVersions(adapters2, {
|
|
17824
|
+
baseline: options.baseline,
|
|
17825
|
+
workspaceRoot
|
|
17826
|
+
});
|
|
17827
|
+
recordCheck(checks, true, "versioning", `${analysis.specsNeedingBump} spec(s) need version review.`);
|
|
17828
|
+
} catch (error) {
|
|
17829
|
+
recordCheck(checks, false, "versioning", failureMessage(error));
|
|
17830
|
+
errors.push(`Version analysis failed: ${failureMessage(error)}`);
|
|
17831
|
+
}
|
|
17832
|
+
}
|
|
17833
|
+
if (options.strict) {
|
|
17834
|
+
for (const artifact of config.publishArtifacts ?? []) {
|
|
17835
|
+
const artifactPath = fs5.join(outputDir, artifact);
|
|
17836
|
+
if (!await fs5.exists(artifactPath)) {
|
|
17837
|
+
errors.push(`Missing generated release artifact: ${artifactPath}`);
|
|
17838
|
+
}
|
|
17839
|
+
}
|
|
17840
|
+
}
|
|
17841
|
+
recordCheck(checks, errors.length === 0, "capsules", errors.length === 0 ? "All release capsules are complete." : errors[0] ?? "");
|
|
17842
|
+
if (changesets.length === 0) {
|
|
17843
|
+
warnings.push("No pending release changesets were found.");
|
|
17844
|
+
}
|
|
17845
|
+
return {
|
|
17846
|
+
success: errors.length === 0,
|
|
17847
|
+
errors,
|
|
17848
|
+
warnings,
|
|
17849
|
+
checks
|
|
17850
|
+
};
|
|
17851
|
+
}
|
|
17852
|
+
async function readWorkspaceReleaseConfig(fs5, workspaceRoot) {
|
|
17853
|
+
const configPath = fs5.join(workspaceRoot, ".contractsrc.json");
|
|
17854
|
+
if (!await fs5.exists(configPath)) {
|
|
17855
|
+
return DEFAULT_RELEASE_CONFIG;
|
|
17856
|
+
}
|
|
17857
|
+
try {
|
|
17858
|
+
const parsed = JSON.parse(await fs5.readFile(configPath));
|
|
17859
|
+
const validated = ContractsrcSchema4.safeParse(parsed);
|
|
17860
|
+
return validated.success ? {
|
|
17861
|
+
...DEFAULT_RELEASE_CONFIG,
|
|
17862
|
+
...validated.data.release
|
|
17863
|
+
} : DEFAULT_RELEASE_CONFIG;
|
|
17864
|
+
} catch {
|
|
17865
|
+
return DEFAULT_RELEASE_CONFIG;
|
|
17866
|
+
}
|
|
17867
|
+
}
|
|
17868
|
+
function inferReleaseType(packageNames) {
|
|
17869
|
+
return packageNames.length > 0 ? "minor" : "patch";
|
|
17870
|
+
}
|
|
17871
|
+
function resolveReleaseVersion(capsule, workspacePackages) {
|
|
17872
|
+
const version = capsule.packages.map((pkg) => workspacePackages.find((candidate) => candidate.name === pkg.name)?.version ?? pkg.version).find((value) => typeof value === "string");
|
|
17873
|
+
return version ?? "0.0.0";
|
|
17874
|
+
}
|
|
17875
|
+
function slugify(value) {
|
|
17876
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
|
|
17877
|
+
}
|
|
17878
|
+
function recordCheck(checks, ok, name, message) {
|
|
17879
|
+
checks.push({ name, ok, message });
|
|
17880
|
+
}
|
|
17881
|
+
function failureMessage(error) {
|
|
17882
|
+
return error instanceof Error ? error.message : String(error);
|
|
17883
|
+
}
|
|
17024
17884
|
// src/services/vibe/index.ts
|
|
17025
17885
|
var exports_vibe = {};
|
|
17026
17886
|
__export(exports_vibe, {
|