Files
Sebastian Poxhofer c32e2391a0 refactor(linters): add prefer-coerce-object rule (#45493)
Refs #45216

The object counterpart to `prefer-coerce-array`: `x ?? {}` becomes
`coerceObject(x)`. The rule flags a `??` whose default is a bare `{}`
literal, so a populated default or an asserted `({} as Foo)` still
passes; a `x ?? y ?? {}` chain maps onto `coerceObject(x, y)`.

Every hit across `lib/` converted cleanly, so no disable comments were
needed.
2026-08-28 09:27:59 +00:00

245 lines
7.2 KiB
TypeScript

import { isTruthy } from '@sindresorhus/is';
import { logger } from '../../../../logger/index.ts';
import { coerceArray } from '../../../../util/array.ts';
import { coerceObject } from '../../../../util/object.ts';
import * as p from '../../../../util/promises.ts';
import { regEx } from '../../../../util/regex.ts';
import { getDefaultVersioning } from '../../../datasource/common.ts';
import type { GetPkgReleasesConfig } from '../../../datasource/index.ts';
import { getPkgReleases } from '../../../datasource/index.ts';
import { get as getVersioning } from '../../../versioning/index.ts';
import type {
UpdateArtifact,
UpdateArtifactsResult,
Upgrade,
} from '../../types.ts';
import { massageProviderLookupName } from '../util.ts';
import { TerraformProviderHash } from './hash.ts';
import type { ProviderLock, ProviderLockUpdate } from './types.ts';
import {
extractLocks,
findLockFile,
isPinnedVersion,
massageNewValue,
readLockFile,
sortConstraints,
writeLockUpdates,
} from './util.ts';
async function updateAllLocks(
locks: ProviderLock[],
): Promise<ProviderLockUpdate[]> {
const updates = await p.map(
locks,
async (lock) => {
const updateConfig: GetPkgReleasesConfig = {
datasource: 'terraform-provider',
packageName: lock.packageName,
registryUrls: [lock.registryUrl],
};
const { releases } = coerceObject(await getPkgReleases(updateConfig));
if (!releases) {
return null;
}
const versioning = getVersioning(
getDefaultVersioning('terraform-provider'),
);
const versionsList = releases.map((release) => release.version);
const newVersion = versioning.getSatisfyingVersion(
versionsList,
lock.constraints,
);
// if the new version is the same as the last, signal that no update is needed
if (!newVersion || newVersion === lock.version) {
return null;
}
const update: ProviderLockUpdate = {
newVersion,
newConstraint: lock.constraints,
newHashes: coerceArray(
await TerraformProviderHash.createHashes(
lock.registryUrl,
lock.packageName,
newVersion,
),
),
...lock,
};
return update;
},
{ concurrency: 4 },
);
return updates.filter(isTruthy);
}
export function getNewConstraint(
dep: Upgrade<Record<string, unknown>>,
oldConstraint: string | undefined,
): string | undefined {
const {
currentValue,
currentVersion,
newValue: rawNewValue,
newVersion,
packageName,
} = dep;
const newValue = massageNewValue(rawNewValue);
if (oldConstraint && currentValue && newValue && currentValue === newValue) {
logger.debug(
`Leaving constraints "${oldConstraint}" unchanged for "${packageName}" as current and new values are the same`,
);
return oldConstraint;
}
if (
oldConstraint &&
currentValue &&
newValue &&
oldConstraint.includes(currentValue)
) {
logger.debug(
`Updating constraint "${oldConstraint}" to replace "${currentValue}" with "${newValue}" for "${packageName}"`,
);
//remove surplus .0 version
return sortConstraints(
oldConstraint.replace(
regEx(`(,\\s|^)${RegExp.escape(currentValue)}(\\.0)*`),
`$1${newValue}`,
),
);
}
if (
oldConstraint &&
currentVersion &&
newVersion &&
oldConstraint.includes(currentVersion)
) {
logger.debug(
`Updating constraint "${oldConstraint}" to replace "${currentVersion}" with "${newVersion}" for "${packageName}"`,
);
return sortConstraints(oldConstraint.replace(currentVersion, newVersion));
}
if (isPinnedVersion(newValue)) {
logger.debug(`Pinning constraint for "${packageName}" to "${newVersion}"`);
return newVersion;
}
logger.debug(
`Could not detect constraint to update for "${packageName}" so setting to newValue "${newValue}"`,
);
return newValue;
}
export async function updateArtifacts({
packageFileName,
updatedDeps,
config,
}: UpdateArtifact): Promise<UpdateArtifactsResult[] | null> {
logger.debug(`terraform.updateArtifacts(${packageFileName})`);
const lockFilePath = await findLockFile(packageFileName);
if (!lockFilePath) {
logger.debug('No .terraform.lock.hcl found');
return null;
}
try {
const lockFileContent = await readLockFile(lockFilePath);
if (!lockFileContent) {
logger.debug('No .terraform.lock.hcl found');
return null;
}
const locks = extractLocks(lockFileContent);
if (!locks) {
logger.debug('No Locks in .terraform.lock.hcl found');
return null;
}
const updates: ProviderLockUpdate[] = [];
if (config.isLockFileMaintenance) {
// update all locks in the file during maintenance --> only update version in constraints
const maintenanceUpdates = await updateAllLocks(locks);
updates.push(...maintenanceUpdates);
} else {
const providerDeps = updatedDeps.filter((dep) =>
// TODO #22198
['provider', 'required_provider'].includes(dep.depType!),
);
logger.debug(`Found ${providerDeps.length} provider deps`);
for (const dep of providerDeps) {
massageProviderLookupName(dep);
const { registryUrls, newVersion, packageName } = dep;
const updateLock = locks.find(
(value) => value.packageName === packageName,
);
/* v8 ignore next -- needs test */
if (!updateLock) {
logger.debug(`Skipping. No lock found for "${packageName}"`);
continue;
}
if (dep.isLockfileUpdate) {
const versioning = getVersioning(dep.versioning);
const satisfyingVersion = versioning.getSatisfyingVersion(
[dep.newVersion!],
updateLock.constraints,
);
if (!satisfyingVersion) {
logger.debug(
`Skipping. Lockfile update with "${newVersion}" does not statisfy constraints "${updateLock.constraints}" for "${packageName}"`,
);
continue;
}
}
// use registryURL defined in the update and fall back to the one defined in the lockfile
const registryUrl = registryUrls?.[0] ?? updateLock.registryUrl;
const newConstraint = getNewConstraint(dep, updateLock.constraints);
const update: ProviderLockUpdate = {
// TODO #22198
newVersion: newVersion!,
newConstraint: newConstraint!,
newHashes: coerceArray(
await TerraformProviderHash.createHashes(
registryUrl,
updateLock.packageName,
newVersion!,
),
),
...updateLock,
};
updates.push(update);
}
}
// if no updates have been found or there are failed hashes abort
if (
updates.length === 0 ||
updates.some((value) => !value.newHashes?.length)
) {
logger.debug('No updates found or hash creation failed');
return null;
}
logger.debug(`Writing updates to ${lockFilePath}`);
const res = writeLockUpdates(updates, lockFilePath, lockFileContent);
return [res];
} catch (err) {
return [
{
artifactError: {
fileName: lockFilePath,
stderr: err.message,
},
},
];
}
}