import { isObject, isString } from '@sindresorhus/is'; import { logger } from '../../../logger/index.ts'; import { coerceObject } from '../../../util/object.ts'; import { parseSingleYaml } from '../../../util/yaml.ts'; import { getDep } from '../dockerfile/extract.ts'; import type { ExtractConfig, PackageFileContent } from '../types.ts'; import type { WoodpeckerConfig } from './types.ts'; function woodpeckerVersionDecider( woodpeckerConfig: WoodpeckerConfig, ): (keyof WoodpeckerConfig)[] { const keys = ['clone', 'steps', 'pipeline', 'services']; return Object.keys(woodpeckerConfig).filter((key) => keys.includes(key), ) as (keyof WoodpeckerConfig)[]; } export function extractPackageFile( content: string, packageFile: string, extractConfig: ExtractConfig, ): PackageFileContent | null { logger.debug('woodpecker.extractPackageFile()'); let config: WoodpeckerConfig; try { // TODO: use schema (#9610) config = parseSingleYaml(content); if (!config) { logger.debug( { packageFile }, 'Null config when parsing Woodpecker Configuration content', ); return null; } if (!isObject(config)) { logger.debug( { packageFile, type: typeof config }, 'Unexpected type for Woodpecker Configuration content', ); return null; } } catch (err) { logger.debug( { packageFile, err }, 'Error parsing Woodpecker Configuration config YAML', ); return null; } const pipelineKeys = woodpeckerVersionDecider(config); if (pipelineKeys.length === 0) { logger.debug({ packageFile }, "Couldn't identify dependencies"); return null; } // Image name/tags for services are only eligible for update if they don't // use variables and if the image is not built locally const deps = pipelineKeys.flatMap((pipelineKey) => Object.values(coerceObject(config[pipelineKey])) .filter((step) => isString(step?.image)) .map((step) => getDep(step.image, true, extractConfig.registryAliases)), ); logger.trace({ deps }, 'Woodpecker Configuration image'); return deps.length ? { deps } : null; }