import { isNonEmptyArray } from '@sindresorhus/is'; import { TEMPORARY_ERROR } from '../../../constants/error-messages.ts'; import { logger } from '../../../logger/index.ts'; import { exec } from '../../../util/exec/index.ts'; import type { ExecOptions } from '../../../util/exec/types.ts'; import { deleteLocalFile, getSiblingFileName, readLocalFile, writeLocalFile, } from '../../../util/fs/index.ts'; import { getRepoStatus } from '../../../util/git/index.ts'; import type { UpdateArtifact, UpdateArtifactsResult } from '../types.ts'; export async function updateArtifacts({ packageFileName, updatedDeps, newPackageFileContent, config, }: UpdateArtifact): Promise { logger.debug(`apm.updateArtifacts(${packageFileName})`); const { isLockFileMaintenance } = config; if (!isNonEmptyArray(updatedDeps) && !isLockFileMaintenance) { logger.debug('apm: no updated deps - returning null'); return null; } const lockFileName = getSiblingFileName(packageFileName, 'apm.lock.yaml'); const existingLockFileContent = await readLocalFile(lockFileName, 'utf8'); if (!existingLockFileContent) { logger.debug('apm: no lock file found'); return null; } try { await writeLocalFile(packageFileName, newPackageFileContent); if (isLockFileMaintenance) { await deleteLocalFile(lockFileName); } const execOptions: ExecOptions = { cwdFile: packageFileName, docker: {}, toolConstraints: [ { toolName: 'apm', constraint: config.constraints?.apm }, ], }; await exec('apm install', execOptions); // `apm install` regenerates the lockfile and re-deploys the harness // directories (`.github/`, `.claude/`, ...) that APM consumers commit, so // return every file it changed - not just the lockfile - or the committed // instruction files go stale after a bump. `apm_modules/` is the gitignored // cache, so it is not reported here. const status = await getRepoStatus(); const res: UpdateArtifactsResult[] = []; for (const path of [...status.modified, ...status.not_added]) { // the manifest itself is committed as an updated package file if (path === packageFileName) { continue; } res.push({ file: { type: 'addition', path, contents: await readLocalFile(path) }, }); } for (const path of status.deleted) { res.push({ file: { type: 'deletion', path } }); } if (!res.length) { logger.debug('apm: no changed files after install'); return null; } return res; } catch (err) { if (err.message === TEMPORARY_ERROR) { throw err; } logger.debug({ err }, `Failed to update ${lockFileName}`); return [ { artifactError: { fileName: lockFileName, stderr: `${err}`, }, }, ]; } }