renovate/lib/modules/manager/cocoapods/artifacts.ts

150 lines
3.8 KiB
TypeScript

import { quote } from 'shlex';
import upath from 'upath';
import { TEMPORARY_ERROR } from '../../../constants/error-messages';
import { logger } from '../../../logger';
import { coerceArray } from '../../../util/array';
import { exec } from '../../../util/exec';
import type { ExecOptions } from '../../../util/exec/types';
import {
ensureCacheDir,
getSiblingFileName,
readLocalFile,
writeLocalFile,
} from '../../../util/fs';
import { getRepoStatus } from '../../../util/git';
import { newlineRegex, regEx } from '../../../util/regex';
import type { UpdateArtifact, UpdateArtifactsResult } from '../types';
const pluginRegex = regEx(`^\\s*plugin\\s*(['"])(?<plugin>[^'"]+)(['"])`);
function getPluginCommands(content: string): string[] {
const result = new Set<string>();
const lines: string[] = content.split(newlineRegex);
lines.forEach((line) => {
const match = pluginRegex.exec(line);
if (match?.groups) {
const { plugin } = match.groups;
result.add(`gem install ${quote(plugin)}`);
}
});
return [...result];
}
export async function updateArtifacts({
packageFileName,
updatedDeps,
newPackageFileContent,
config,
}: UpdateArtifact): Promise<UpdateArtifactsResult[] | null> {
logger.debug(`cocoapods.getArtifacts(${packageFileName})`);
if (updatedDeps.length < 1) {
logger.debug('CocoaPods: empty update - returning null');
return null;
}
const lockFileName = getSiblingFileName(packageFileName, 'Podfile.lock');
try {
await writeLocalFile(packageFileName, newPackageFileContent);
} catch (err) {
logger.warn({ err }, 'Podfile could not be written');
return [
{
artifactError: {
lockFile: lockFileName,
stderr: err.message,
},
},
];
}
const existingLockFileContent = await readLocalFile(lockFileName, 'utf8');
if (!existingLockFileContent) {
logger.debug(`Lockfile not found: ${lockFileName}`);
return null;
}
const match = regEx(/^COCOAPODS: (?<cocoapodsVersion>.*)$/m).exec(
existingLockFileContent,
);
const cocoapods = match?.groups?.cocoapodsVersion ?? null;
const cmd = [...getPluginCommands(newPackageFileContent), 'pod install'];
const execOptions: ExecOptions = {
userConfiguredEnv: config.env,
cwdFile: packageFileName,
extraEnv: {
CP_HOME_DIR: await ensureCacheDir('cocoapods'),
},
docker: {},
toolConstraints: [
{
toolName: 'ruby',
},
{
toolName: 'cocoapods',
constraint: cocoapods,
},
],
};
try {
await exec(cmd, execOptions);
} catch (err) {
// istanbul ignore if
if (err.message === TEMPORARY_ERROR) {
throw err;
}
return [
{
artifactError: {
lockFile: lockFileName,
stderr: err.stderr || err.stdout || err.message,
},
},
];
}
const status = await getRepoStatus();
if (!status.modified.includes(lockFileName)) {
return null;
}
logger.debug(`Returning updated lockfile: ${lockFileName}`);
const lockFileContent = await readLocalFile(lockFileName);
const res: UpdateArtifactsResult[] = [
{
file: {
type: 'addition',
path: lockFileName,
contents: lockFileContent,
},
},
];
const podsDir = upath.join(upath.dirname(packageFileName), 'Pods');
const podsManifestFileName = upath.join(podsDir, 'Manifest.lock');
if (await readLocalFile(podsManifestFileName, 'utf8')) {
for (const f of status.modified.concat(status.not_added)) {
if (f.startsWith(podsDir)) {
res.push({
file: {
type: 'addition',
path: f,
contents: await readLocalFile(f),
},
});
}
}
for (const f of coerceArray(status.deleted)) {
res.push({
file: {
type: 'deletion',
path: f,
},
});
}
}
return res;
}