fix: reuse env catalog across hash metadata drift
This commit is contained in:
@@ -130,24 +130,60 @@ export async function createCiPlan(options = {}) {
|
||||
const codeInputHash = envReuse ? await hashGitPaths(repoRoot, targetRef, codeInputPaths, {
|
||||
skipPath: (filePath) => isTestOnlyPath(filePath) || isDocsOnlyPath(filePath)
|
||||
}) : null;
|
||||
const environmentImage = envReuse ? envReuseImageRef(registryPrefix, model.serviceId, environmentInputHash) : null;
|
||||
const currentEnvironmentImage = envReuse ? envReuseImageRef(registryPrefix, model.serviceId, environmentInputHash) : null;
|
||||
const catalogEnvironmentImage = envReuse ? environmentImageFromCatalog(model.serviceId, catalogRecord) : null;
|
||||
const catalogEnvironmentDigest = envReuse ? environmentDigestFromCatalog(catalogRecord) : null;
|
||||
const catalogEnvironmentSourceCommitId = envReuse ? text(catalogRecord?.sourceCommitId) : "";
|
||||
const catalogEnvironmentInputHashAtSource = envReuse && catalogEnvironmentSourceCommitId
|
||||
? await hashEnvReuseInputsIfCommitExists(repoRoot, catalogEnvironmentSourceCommitId, envInputPaths, serviceEnvReuseRecipe, {
|
||||
skipPath: (filePath) => isTestOnlyPath(filePath) || isDocsOnlyPath(filePath),
|
||||
semanticPackageJson: true
|
||||
})
|
||||
: null;
|
||||
const environmentSourceUnchanged = envReuse && Boolean(catalogEnvironmentInputHashAtSource) && catalogEnvironmentInputHashAtSource === environmentInputHash;
|
||||
let environmentImage = currentEnvironmentImage;
|
||||
let environmentDigest = envReuse ? environmentDigestFromCatalogForHash(catalogRecord, environmentInputHash) : null;
|
||||
const environmentInputChanged = envReuse ? catalogRecord?.environmentInputHash !== environmentInputHash : null;
|
||||
const reuseRegistry = envReuse && reuseRegistryProbe && environmentImage
|
||||
const currentReuseRegistry = envReuse && reuseRegistryProbe && currentEnvironmentImage
|
||||
? await reuseRegistryProbe({
|
||||
serviceId: model.serviceId,
|
||||
image: environmentImage,
|
||||
image: currentEnvironmentImage,
|
||||
digest: environmentDigest,
|
||||
registryPrefix,
|
||||
timeoutMs: Number.isFinite(registryProbeTimeoutMs) ? registryProbeTimeoutMs : 3000
|
||||
})
|
||||
: null;
|
||||
let reuseRegistry = currentReuseRegistry;
|
||||
let environmentMetadataHashDrift = false;
|
||||
const catalogEnvironmentReusable = envReuse
|
||||
&& environmentInputChanged === true
|
||||
&& environmentSourceUnchanged
|
||||
&& Boolean(catalogEnvironmentImage)
|
||||
&& /^sha256:[a-f0-9]{64}$/u.test(catalogEnvironmentDigest ?? "");
|
||||
if (catalogEnvironmentReusable && currentReuseRegistry?.status !== "present") {
|
||||
const catalogReuseRegistry = reuseRegistryProbe && catalogEnvironmentImage
|
||||
? await reuseRegistryProbe({
|
||||
serviceId: model.serviceId,
|
||||
image: catalogEnvironmentImage,
|
||||
digest: catalogEnvironmentDigest,
|
||||
registryPrefix,
|
||||
timeoutMs: Number.isFinite(registryProbeTimeoutMs) ? registryProbeTimeoutMs : 3000
|
||||
})
|
||||
: null;
|
||||
if (!reuseRegistryProbe || catalogReuseRegistry?.status === "present") {
|
||||
environmentImage = catalogEnvironmentImage;
|
||||
environmentDigest = catalogReuseRegistry?.digest ?? catalogEnvironmentDigest;
|
||||
reuseRegistry = catalogReuseRegistry ? { ...catalogReuseRegistry, reuseSource: "catalog-metadata-hash-drift" } : null;
|
||||
environmentMetadataHashDrift = true;
|
||||
}
|
||||
}
|
||||
if (reuseRegistry?.status === "present" && /^sha256:[a-f0-9]{64}$/u.test(reuseRegistry.digest ?? "")) {
|
||||
environmentDigest = reuseRegistry.digest;
|
||||
}
|
||||
const reuseRegistryUnavailable = Boolean(reuseRegistry && reuseRegistry.status !== "present");
|
||||
const digestReady = /^sha256:[a-f0-9]{64}$/u.test(environmentDigest ?? "");
|
||||
const environmentReady = envReuse && Boolean(environmentImage) && (reuseRegistryProbe ? reuseRegistry?.status === "present" && digestReady : !environmentInputChanged && digestReady);
|
||||
const effectiveEnvironmentInputChanged = environmentInputChanged === true && !environmentMetadataHashDrift;
|
||||
const environmentReady = envReuse && Boolean(environmentImage) && (reuseRegistryProbe ? reuseRegistry?.status === "present" && digestReady : !effectiveEnvironmentInputChanged && digestReady);
|
||||
const envChanged = envReuse ? Boolean(relevantEnvMatches.length > 0 || !environmentReady || reuseRegistryUnavailable) : null;
|
||||
const codeChanged = envReuse ? relevantCodeMatches.length > 0 || catalogRecord?.codeInputHash !== codeInputHash : null;
|
||||
const componentInputPaths = uniqueSorted([
|
||||
@@ -230,6 +266,10 @@ export async function createCiPlan(options = {}) {
|
||||
envChanged,
|
||||
runtimeConfigChanged,
|
||||
environmentInputChanged,
|
||||
effectiveEnvironmentInputChanged,
|
||||
environmentMetadataHashDrift,
|
||||
catalogEnvironmentInputHashAtSource,
|
||||
environmentSourceUnchanged,
|
||||
codeChanged,
|
||||
reuseRegistry,
|
||||
environmentInputHash,
|
||||
@@ -242,7 +282,7 @@ export async function createCiPlan(options = {}) {
|
||||
envChangedPaths: relevantEnvMatches,
|
||||
codeChangedPaths: relevantCodeMatches,
|
||||
reason: affected
|
||||
? reasonForService({ directMatches, sharedMatches, runtimeDepMatches, buildSystemMatches, catalogReuse, envReuse, envChanged, runtimeConfigChanged, environmentInputChanged, codeChanged, reuseRegistry })
|
||||
? reasonForService({ directMatches, sharedMatches, runtimeDepMatches, buildSystemMatches, catalogReuse, envReuse, envChanged, runtimeConfigChanged, environmentInputChanged: effectiveEnvironmentInputChanged, codeChanged, reuseRegistry })
|
||||
: reasonForUnchanged(globalChange),
|
||||
reuse: catalogReuse
|
||||
});
|
||||
@@ -721,6 +761,15 @@ async function hashEnvReuseInputs(repoRoot, targetRef, paths, recipe, options =
|
||||
});
|
||||
}
|
||||
|
||||
async function hashEnvReuseInputsIfCommitExists(repoRoot, targetRef, paths, recipe, options = {}) {
|
||||
try {
|
||||
await gitValue(repoRoot, ["rev-parse", "--verify", `${targetRef}^{commit}`]);
|
||||
return await hashEnvReuseInputs(repoRoot, targetRef, paths, recipe, options);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function envReuseIdentityRecipe(recipe) {
|
||||
const identity = JSON.parse(JSON.stringify(recipe ?? {}));
|
||||
if (identity.downloadStack && typeof identity.downloadStack === "object") {
|
||||
|
||||
Reference in New Issue
Block a user