diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md index 8f9d38a9791d3f1bcb2ba2ee95d6b4904fee3ba8..7a9c29fbd4dc5743bbad6953b71fc593c7ccb9f8 100644 --- a/docs/usage/configuration-options.md +++ b/docs/usage/configuration-options.md @@ -45,6 +45,10 @@ Alias values must be properly formatted URIs. Add configuration here if you want to enable or disable something in particular for Ansible files and override the default Docker settings. +## ansible-galaxy + +Add configuration here if you want to enable or disable something in particular for Ansible requirements files. Includes and webserver based dependencies in requirement files are not supported at this moment. + ## assignAutomerge By default, Renovate will not assign reviewers and assignees to an automerge-enabled PR unless it fails status checks. By configuring this setting to `true`, Renvoate will instead always assign reviewers and assignees for automerging PRs at time of creation. diff --git a/lib/constants/data-binary-source.ts b/lib/constants/data-binary-source.ts index 726d6a00dfae2bd6ab6a25499b1af2fbfed8091b..53738b88a6e1b449939839c4f72cbb0bfb4cafa5 100644 --- a/lib/constants/data-binary-source.ts +++ b/lib/constants/data-binary-source.ts @@ -1,4 +1,5 @@ // Datasource +export const DATASOURCE_ANSIBLE_GALAXY = 'ansible-galaxy'; export const DATASOURCE_CARGO = 'cargo'; export const DATASOURCE_CDNJS = 'cdnjs'; export const DATASOURCE_DART = 'dart'; diff --git a/lib/datasource/ansible-galaxy/__fixtures__/empty b/lib/datasource/ansible-galaxy/__fixtures__/empty new file mode 100644 index 0000000000000000000000000000000000000000..af986f82896e47c514aeef2e26aba759b8ca6df4 --- /dev/null +++ b/lib/datasource/ansible-galaxy/__fixtures__/empty @@ -0,0 +1,8 @@ +{ + "count": 0, + "next": null, + "next_link": null, + "previous": null, + "previous_link": null, + "results": [] +} diff --git a/lib/datasource/ansible-galaxy/__fixtures__/timezone b/lib/datasource/ansible-galaxy/__fixtures__/timezone new file mode 100644 index 0000000000000000000000000000000000000000..c926af82dd17f6206fee8ad8787d63421a86d0ab --- /dev/null +++ b/lib/datasource/ansible-galaxy/__fixtures__/timezone @@ -0,0 +1,138 @@ +{ + "count": 1, + "next": null, + "next_link": null, + "previous": null, + "previous_link": null, + "results": [ + { + "id": 1429, + "url": "/api/v1/roles/1429/", + "related": { + "content_type": "/api/v1/content_types/11/", + "dependencies": "/api/v1/roles/1429/dependencies/", + "imports": "/api/v1/roles/1429/imports/" + }, + "summary_fields": { + "content_type": { + "id": 11, + "name": "role", + "description": "Role" + }, + "dependencies": [], + "namespace": { + "id": 2193, + "name": "yatesr", + "avatar_url": "https://avatars1.githubusercontent.com/u/3984903?v=4", + "location": null, + "company": null, + "email": null, + "html_url": "https://github.com/yatesr", + "is_vendor": false + }, + "platforms": [ + { + "name": "Debian", + "release": "jessie" + }, + { + "name": "Debian", + "release": "squeeze" + }, + { + "name": "Debian", + "release": "wheezy" + }, + { + "name": "EL", + "release": "6" + }, + { + "name": "EL", + "release": "7" + }, + { + "name": "Fedora", + "release": "19" + }, + { + "name": "Fedora", + "release": "20" + }, + { + "name": "Ubuntu", + "release": "precise" + }, + { + "name": "Ubuntu", + "release": "trusty" + } + ], + "provider_namespace": { + "id": 2174, + "name": "yatesr" + }, + "repository": { + "id": 27861, + "name": "timezone", + "original_name": "ansible-timezone", + "stargazers_count": 18, + "watchers_count": 3, + "forks_count": 15, + "open_issues_count": 3, + "travis_status_url": "", + "travis_build_url": "", + "format": "role", + "deprecated": false, + "community_score": null, + "quality_score": 5.0, + "community_survey_count": 0 + }, + "tags": [ + "system" + ], + "versions": [ + { + "id": 7863, + "name": "1.0.0", + "release_date": "2015-11-17T00:43:42Z" + }, + { + "id": 38901, + "name": "1.1.0", + "release_date": "2017-09-25T00:27:37Z" + }, + { + "id": 107503, + "name": "1.2.0", + "release_date": "2019-10-28T01:40:43Z" + } + ], + "videos": [] + }, + "created": "2014-08-21T17:01:14.940065Z", + "modified": "2019-10-28T01:51:11.167031Z", + "name": "timezone", + "role_type": "ANS", + "is_valid": true, + "min_ansible_version": "1.9", + "license": "license (Apache 2.0)", + "company": "", + "description": "Role for managing timezone.", + "travis_status_url": "", + "download_count": 871145, + "imported": "2019-10-27T21:51:11.157558-04:00", + "active": true, + "github_user": "yatesr", + "github_repo": "ansible-timezone", + "github_branch": "master", + "stargazers_count": 18, + "forks_count": 15, + "open_issues_count": 3, + "commit": "02845a002839261295b99f508d9ad989bc611cb7", + "commit_message": "Code quality with molecule, tox, and Travis. (#9)\n\n* Set role_name explicitly to avoid using repo name\r\n\r\n* Remove symlinks\r\n\r\nThe symlinks in this role do not play nicely with PBR, which is a\r\ndependency of Kayobe, a project that uses this role.\r\n\r\n* yam", + "commit_url": "https://api.github.com/repos/yatesr/ansible-timezone/git/commits/02845a002839261295b99f508d9ad989bc611cb7", + "issue_tracker_url": "https://github.com/yatesr/ansible-timezone/issues" + } + ] +} diff --git a/lib/datasource/ansible-galaxy/__snapshots__/index.spec.ts.snap b/lib/datasource/ansible-galaxy/__snapshots__/index.spec.ts.snap new file mode 100644 index 0000000000000000000000000000000000000000..18d01e478db7d809232323c25c7fc8c650290c25 --- /dev/null +++ b/lib/datasource/ansible-galaxy/__snapshots__/index.spec.ts.snap @@ -0,0 +1,22 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`datasource/ansible-galaxy getPkgReleases processes real data 1`] = ` +Object { + "dependencyUrl": "https://galaxy.ansible.com/yatesr/timezone", + "releases": Array [ + Object { + "releaseTimestamp": "2015-11-17T00:43:42Z", + "version": "1.0.0", + }, + Object { + "releaseTimestamp": "2017-09-25T00:27:37Z", + "version": "1.1.0", + }, + Object { + "releaseTimestamp": "2019-10-28T01:40:43Z", + "version": "1.2.0", + }, + ], + "sourceUrl": "https://github.com/yatesr/ansible-timezone", +} +`; diff --git a/lib/datasource/ansible-galaxy/index.spec.ts b/lib/datasource/ansible-galaxy/index.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..db9a0ea5fbac5b4d8ce63d63f9606656855179fe --- /dev/null +++ b/lib/datasource/ansible-galaxy/index.spec.ts @@ -0,0 +1,84 @@ +import fs from 'fs'; + +import _got from '../../util/got'; +import { getPkgReleases } from './index'; + +const got: any = _got; + +const res1 = fs.readFileSync( + 'lib/datasource/ansible-galaxy/__fixtures__/timezone', + 'utf8' +); +const empty = fs.readFileSync( + 'lib/datasource/ansible-galaxy/__fixtures__/empty', + 'utf8' +); + +jest.mock('../../util/got'); + +describe('datasource/ansible-galaxy', () => { + describe('getPkgReleases', () => { + beforeEach(() => { + global.repoCache = {}; + }); + it('returns null for empty result', async () => { + got.mockReturnValueOnce(null); + expect( + await getPkgReleases({ lookupName: 'non_existent_crate' }) + ).toBeNull(); + }); + it('returns null for missing fields', async () => { + got.mockReturnValueOnce({ + body: undefined, + }); + expect( + await getPkgReleases({ lookupName: 'non_existent_crate' }) + ).toBeNull(); + }); + it('returns null for empty list', async () => { + got.mockReturnValueOnce({ + body: '\n', + }); + expect( + await getPkgReleases({ lookupName: 'non_existent_crate' }) + ).toBeNull(); + }); + it('returns null for 404', async () => { + got.mockImplementationOnce(() => + Promise.reject({ + statusCode: 404, + }) + ); + expect(await getPkgReleases({ lookupName: 'some_crate' })).toBeNull(); + }); + it('returns null for unknown error', async () => { + got.mockImplementationOnce(() => { + throw new Error(); + }); + expect(await getPkgReleases({ lookupName: 'some_crate' })).toBeNull(); + }); + it('processes real data', async () => { + got.mockReturnValueOnce({ + body: res1, + }); + const res = await getPkgReleases({ lookupName: 'yatesr.timezone' }); + expect(res).toMatchSnapshot(); + expect(res).not.toBeNull(); + expect(res).toBeDefined(); + }); + it('return null if searching random username and project name', async () => { + got.mockReturnValueOnce({ + body: empty, + }); + const res = await getPkgReleases({ lookupName: 'foo.bar' }); + expect(res).toBeNull(); + }); + it('returns null if lookupName is undefined', async () => { + got.mockReturnValueOnce({ + body: res1, + }); + const res = await getPkgReleases({ lookupName: undefined }); + expect(res).toBeNull(); + }); + }); +}); diff --git a/lib/datasource/ansible-galaxy/index.ts b/lib/datasource/ansible-galaxy/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..ccc5915fc1cd73d54d32e8d634c1ea0827aa13ce --- /dev/null +++ b/lib/datasource/ansible-galaxy/index.ts @@ -0,0 +1,108 @@ +import { logger } from '../../logger'; +import got from '../../util/got'; +import { PkgReleaseConfig, ReleaseResult, Release } from '../common'; + +export async function getPkgReleases({ + lookupName, +}: PkgReleaseConfig): Promise<ReleaseResult | null> { + if (!lookupName) { + return null; + } + + const cacheNamespace = 'datasource-ansible-galaxy'; + const cacheKey = lookupName; + const cachedResult = await renovateCache.get<ReleaseResult>( + cacheNamespace, + cacheKey + ); + // istanbul ignore if + if (cachedResult) { + return cachedResult; + } + + const lookUp = lookupName.split('.'); + const userName = lookUp[0]; + const projectName = lookUp[1]; + + const baseUrl = 'https://galaxy.ansible.com/'; + const galaxyAPIUrl = + baseUrl + + 'api/v1/roles/?owner__username=' + + userName + + '&name=' + + projectName; + const galaxyProjectUrl = baseUrl + userName + '/' + projectName; + + try { + let res: any = await got(galaxyAPIUrl, { + hostType: 'ansible-galaxy', + }); + if (!res || !res.body) { + logger.warn( + { dependency: lookupName }, + `Received invalid crate data from ${galaxyAPIUrl}` + ); + return null; + } + + res = res.body; + const response = JSON.parse(res); + + // istanbul ignore if + if (response.results.length > 1) { + logger.warn( + { dependency: lookupName }, + `Received multiple results from ${galaxyAPIUrl}` + ); + return null; + } + if (response.results.length === 0) { + logger.warn( + { dependency: lookupName }, + `Received no results from ${galaxyAPIUrl}` + ); + return null; + } + + const resultObject = response.results[0]; + const versions = resultObject.summary_fields.versions; + + const result: ReleaseResult = { + releases: [], + }; + + result.dependencyUrl = galaxyProjectUrl; + if (resultObject.github_user && resultObject.github_repo) { + result.sourceUrl = + 'https://github.com/' + + resultObject.github_user + + '/' + + resultObject.github_repo; + } + + result.releases = versions.map( + (version: { name: string; release_date: string }) => { + const release: Release = { + version: version.name, + releaseTimestamp: version.release_date, + }; + + return release; + } + ); + + const cacheMinutes = 10; + await renovateCache.set(cacheNamespace, cacheKey, result, cacheMinutes); + return result; + } catch (err) { + if (err.statusCode === 404 || err.code === 'ENOTFOUND') { + logger.info({ lookupName }, `Dependency lookup failure: not found`); + return null; + } + logger.warn( + { err, lookupName }, + 'ansible-galaxy lookup failure: Unknown error' + ); + return null; + } +} diff --git a/lib/manager/ansible-galaxy/__fixtures__/helmRequirements.yml b/lib/manager/ansible-galaxy/__fixtures__/helmRequirements.yml new file mode 100644 index 0000000000000000000000000000000000000000..fadfba305018ea5036bfb0d70f31b96a07c58897 --- /dev/null +++ b/lib/manager/ansible-galaxy/__fixtures__/helmRequirements.yml @@ -0,0 +1,13 @@ +dependencies: + - name: etcd + version: 0.6.2 + repository: https://kubernetes-charts-incubator.storage.googleapis.com/ + condition: etcd.deployChart + - name: zookeeper + version: 1.0.0 + repository: https://kubernetes-charts-incubator.storage.googleapis.com/ + condition: zookeeper.deployChart + - name: consul + version: 3.6.1 + repository: https://kubernetes-charts.storage.googleapis.com/ + condition: consul.deployChart diff --git a/lib/manager/ansible-galaxy/__fixtures__/requirements01.yml b/lib/manager/ansible-galaxy/__fixtures__/requirements01.yml new file mode 100644 index 0000000000000000000000000000000000000000..8f89b8d529533e9b13812cbe5447a02963f1930a --- /dev/null +++ b/lib/manager/ansible-galaxy/__fixtures__/requirements01.yml @@ -0,0 +1,38 @@ +# from galaxy +- src: yatesr.timezone + version: "0.1.0" + +# GitHub repo, accessible by http(s) +- version: master + src: https://github.com/bennojoy/nginx + name: nginx_role + +# Git repo, accessible by git+http(s) +- src: git+http://bitbucket.org/willthames/git-ansible-galaxy + version: "v1.4" + +# Git repo, accessible by git protocol +- scm: git + src: git@gitlab.company.com:mygroup/ansible-base.git + version: "0.1" + +# include +- include: <path_to_requirements>/webserver.yml + +# Test quotes +- scm: "git" + src: 'git@gitlab.company.com:mygroup/ansible-base.git' + version: "0.14" + +# Test quotes +- scm: "git" + src: "company.com:mygroup/ansible-base.git" + version: "0.14" + +# not fitting release +- test: yatesr.timezone + version: "0.1.0" + +# not valid src string +- src: timezone + version: "0.1.0" diff --git a/lib/manager/ansible-galaxy/__fixtures__/requirements02.yml b/lib/manager/ansible-galaxy/__fixtures__/requirements02.yml new file mode 100644 index 0000000000000000000000000000000000000000..7bf5266aa8968e5b863e76e08262309afbcc5911 --- /dev/null +++ b/lib/manager/ansible-galaxy/__fixtures__/requirements02.yml @@ -0,0 +1,6 @@ +# from galaxy +- src: yatesr.timezone + version: "0.1.0" +- scm: git + src: git@gitlab.company.com:mygroup/ansible-base.git + version: "0.1" diff --git a/lib/manager/ansible-galaxy/__snapshots__/extract.spec.ts.snap b/lib/manager/ansible-galaxy/__snapshots__/extract.spec.ts.snap new file mode 100644 index 0000000000000000000000000000000000000000..4eb8d550dc072f299dd872db2676cc2e5516d2ad --- /dev/null +++ b/lib/manager/ansible-galaxy/__snapshots__/extract.spec.ts.snap @@ -0,0 +1,102 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`lib/manager/ansible-galaxy/extract extractPackageFile() extracts dependencies from a not beautified requirements file 1`] = ` +Array [ + Object { + "currentValue": "0.1.0", + "datasource": "ansible-galaxy", + "depName": "yatesr.timezone", + "lookupName": "yatesr.timezone", + "managerData": Object { + "lineNumber": 2, + "name": null, + "scm": null, + "src": "yatesr.timezone", + "version": "0.1.0", + }, + }, + Object { + "currentValue": "0.1", + "datasource": "git-tags", + "depName": "mygroup/ansible-base", + "lookupName": "git@gitlab.company.com:mygroup/ansible-base.git", + "managerData": Object { + "lineNumber": 5, + "name": null, + "scm": "git", + "src": "git@gitlab.company.com:mygroup/ansible-base.git", + "version": "0.1", + }, + }, +] +`; + +exports[`lib/manager/ansible-galaxy/extract extractPackageFile() extracts multiple dependencies from requirements.yml 1`] = ` +Array [ + Object { + "currentValue": "0.1.0", + "datasource": "ansible-galaxy", + "depName": "yatesr.timezone", + "lookupName": "yatesr.timezone", + "managerData": Object { + "lineNumber": 2, + "name": null, + "scm": null, + "src": "yatesr.timezone", + "version": "0.1.0", + }, + }, + Object { + "currentValue": "master", + "datasource": "git-tags", + "depName": "nginx_role", + "lookupName": "https://github.com/bennojoy/nginx", + "managerData": Object { + "lineNumber": 5, + "name": "nginx_role", + "scm": null, + "src": "https://github.com/bennojoy/nginx", + "version": "master", + }, + }, + Object { + "currentValue": "v1.4", + "datasource": "git-tags", + "depName": "willthames/git-ansible-galaxy", + "lookupName": "http://bitbucket.org/willthames/git-ansible-galaxy", + "managerData": Object { + "lineNumber": 11, + "name": null, + "scm": null, + "src": "git+http://bitbucket.org/willthames/git-ansible-galaxy", + "version": "v1.4", + }, + }, + Object { + "currentValue": "0.1", + "datasource": "git-tags", + "depName": "mygroup/ansible-base", + "lookupName": "git@gitlab.company.com:mygroup/ansible-base.git", + "managerData": Object { + "lineNumber": 16, + "name": null, + "scm": "git", + "src": "git@gitlab.company.com:mygroup/ansible-base.git", + "version": "0.1", + }, + }, + Object { + "currentValue": "0.14", + "datasource": "git-tags", + "depName": "mygroup/ansible-base", + "lookupName": "git@gitlab.company.com:mygroup/ansible-base.git", + "managerData": Object { + "lineNumber": 24, + "name": null, + "scm": "git", + "src": "git@gitlab.company.com:mygroup/ansible-base.git", + "version": "0.14", + }, + }, +] +`; diff --git a/lib/manager/ansible-galaxy/extract.spec.ts b/lib/manager/ansible-galaxy/extract.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..a9beb05a4fe3a2387a037f112787f4855cadccce --- /dev/null +++ b/lib/manager/ansible-galaxy/extract.spec.ts @@ -0,0 +1,41 @@ +import { readFileSync } from 'fs'; +import extractPackageFile from './extract'; + +const yamlFile1 = readFileSync( + 'lib/manager/ansible-galaxy/__fixtures__/requirements01.yml', + 'utf8' +); +const yamlFile2 = readFileSync( + 'lib/manager/ansible-galaxy/__fixtures__/requirements02.yml', + 'utf8' +); +const helmRequirements = readFileSync( + 'lib/manager/ansible-galaxy/__fixtures__/helmRequirements.yml', + 'utf8' +); + +describe('lib/manager/ansible-galaxy/extract', () => { + describe('extractPackageFile()', () => { + it('returns null for empty', () => { + expect(extractPackageFile('nothing here')).toBeNull(); + }); + it('extracts multiple dependencies from requirements.yml', () => { + const res = extractPackageFile(yamlFile1); + expect(res.deps).toMatchSnapshot(); + expect(res.deps).toHaveLength(5); + }); + it('extracts dependencies from a not beautified requirements file', () => { + const res = extractPackageFile(yamlFile2); + expect(res.deps).toMatchSnapshot(); + expect(res.deps).toHaveLength(2); + }); + it('check if an empty file returns null', () => { + const res = extractPackageFile('\n'); + expect(res).toBeNull(); + }); + it('check if a requirements file of other systems returns null', () => { + const res = extractPackageFile(helmRequirements); + expect(res).toBeNull(); + }); + }); +}); diff --git a/lib/manager/ansible-galaxy/extract.ts b/lib/manager/ansible-galaxy/extract.ts new file mode 100644 index 0000000000000000000000000000000000000000..7e5c7187328cae23143ca5dc277daaf201249e93 --- /dev/null +++ b/lib/manager/ansible-galaxy/extract.ts @@ -0,0 +1,129 @@ +import { logger } from '../../logger'; +import { PackageFile, PackageDependency } from '../common'; +import * as semver from '../../versioning/semver'; +import * as git from '../../versioning/git'; +import { + DATASOURCE_GIT_TAGS, + DATASOURCE_ANSIBLE_GALAXY, +} from '../../constants/data-binary-source'; + +function interpretLine( + lineMatch: RegExpMatchArray, + lineNumber: number, + dependency: PackageDependency +): PackageDependency { + const localDependency: PackageDependency = dependency; + const key = lineMatch[2]; + const value = lineMatch[3].replace(/["']/g, ''); + switch (key) { + case 'name': { + localDependency.managerData.name = value; + break; + } + case 'version': { + localDependency.managerData.version = value; + localDependency.currentValue = value; + localDependency.managerData.lineNumber = lineNumber; + break; + } + case 'scm': { + localDependency.managerData.scm = value; + break; + } + case 'src': { + localDependency.managerData.src = value; + break; + } + default: { + return null; + } + } + return localDependency; +} + +function finalize(dependency: PackageDependency): boolean { + const dep = dependency; + if (dependency.managerData.version === null) { + dep.skipReason = 'no-version'; + return false; + } + + const source: string = dep.managerData.src; + const sourceMatch: RegExpMatchArray = new RegExp( + /^(git|http|git\+http)s?(:\/\/|@).*(\/|:)(.+\/[^.]+)\/?(\.git)?$/ + ).exec(source); + if (sourceMatch) { + dep.datasource = DATASOURCE_GIT_TAGS; + dep.depName = sourceMatch[4]; + // remove leading `git+` from URLs like `git+https://...` + dep.lookupName = source.replace(/git\+/, ''); + } else if (new RegExp(/.+\..+/).exec(source)) { + dep.datasource = DATASOURCE_ANSIBLE_GALAXY; + dep.depName = dep.managerData.src; + dep.lookupName = dep.managerData.src; + } else { + dep.skipReason = 'no-source-match'; + return false; + } + if (dep.managerData.name !== null) { + dep.depName = dep.managerData.name; + } + + if ( + (dep.datasource === DATASOURCE_GIT_TAGS && + !git.api.isValid(dependency.managerData.version)) || + (dep.datasource === DATASOURCE_ANSIBLE_GALAXY && + !semver.isValid(dependency.managerData.version)) + ) { + dep.skipReason = 'invalid-version'; + return false; + } + return true; +} + +export default function extractPackageFile( + content: string +): PackageFile | null { + logger.trace('ansible-galaxy.extractPackageFile()'); + const newBlockRegEx = new RegExp(/^\s*-\s*((\w+):\s*(.*))$/); + const blockLineRegEx = new RegExp(/^\s*((\w+):\s*(.*))$/); + const deps: PackageDependency[] = []; + const lines = content.split('\n'); + try { + for (let lineNumber = 0; lineNumber < lines.length; lineNumber += 1) { + let lineMatch = newBlockRegEx.exec(lines[lineNumber]); + if (lineMatch) { + const dep: PackageDependency = { + managerData: { + name: null, + version: null, + scm: null, + src: null, + }, + }; + do { + const localdep = interpretLine(lineMatch, lineNumber, dep); + if (localdep == null) { + break; + } + const line = lines[lineNumber + 1]; + + if (!line) break; + lineMatch = blockLineRegEx.exec(line); + if (lineMatch) lineNumber += 1; + } while (lineMatch); + if (finalize(dep)) { + deps.push(dep); + } + } + } + + if (!deps.length) { + return null; + } + return { deps }; + } catch (err) /* istanbul ignore next */ { + logger.info({ err }, 'Error extracting ansible-galaxy deps'); + return null; + } +} diff --git a/lib/manager/ansible-galaxy/index.ts b/lib/manager/ansible-galaxy/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..3ba1b84067ae39b221ed2c15974192a954b5e799 --- /dev/null +++ b/lib/manager/ansible-galaxy/index.ts @@ -0,0 +1,8 @@ +import extractPackageFile from './extract'; +import updateDependency from './update'; + +export { extractPackageFile, updateDependency }; + +export const defaultConfig = { + fileMatch: ['(^|/)requirements.ya?ml$'], +}; diff --git a/lib/manager/ansible-galaxy/update.spec.ts b/lib/manager/ansible-galaxy/update.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..4997715152fb9d46c0c9afd2f1d559c4d331cfc7 --- /dev/null +++ b/lib/manager/ansible-galaxy/update.spec.ts @@ -0,0 +1,54 @@ +import { readFileSync } from 'fs'; +import updateDependency from './update'; + +const yamlFile1 = readFileSync( + 'lib/manager/ansible-galaxy/__fixtures__/requirements01.yml', + 'utf8' +); + +describe('manager/ansible/update', () => { + describe('updateDependency', () => { + it('updating value', () => { + const upgrade = { + managerData: { lineNumber: 2 }, + depName: 'yatesr.timezone', + newValue: '1.29.3', + }; + const res = updateDependency({ fileContent: yamlFile1, upgrade }); + expect(res).not.toEqual(yamlFile1); + expect(res.includes(upgrade.newValue)).toBe(true); + }); + it('replaces existing value from docker_service', () => { + const upgrade = { + managerData: { lineNumber: 11 }, + depName: 'willthames/git-ansible-galaxy', + newValue: 'v2.0', + }; + const res = updateDependency({ fileContent: yamlFile1, upgrade }); + expect(res).not.toEqual(yamlFile1); + expect(res.includes(upgrade.newValue)).toBe(true); + }); + it('returns same', () => { + const upgrade = { + managerData: { lineNumber: 16 }, + depName: 'mygroup/ansible-base', + newValue: '0.1', + }; + const res = updateDependency({ fileContent: yamlFile1, upgrade }); + expect(res).toEqual(yamlFile1); + }); + it('returns null if mismatch', () => { + const upgrade = { + managerData: { lineNumber: 19 }, + depName: 'mygroup/ansible-base', + newValue: '0.1', + }; + const res = updateDependency({ fileContent: yamlFile1, upgrade }); + expect(res).toBeNull(); + }); + it('returns null if error', () => { + const res = updateDependency({ fileContent: null, upgrade: null }); + expect(res).toBeNull(); + }); + }); +}); diff --git a/lib/manager/ansible-galaxy/update.ts b/lib/manager/ansible-galaxy/update.ts new file mode 100644 index 0000000000000000000000000000000000000000..7df0e7ea149f7191e6487e9d09cd044db09e6958 --- /dev/null +++ b/lib/manager/ansible-galaxy/update.ts @@ -0,0 +1,33 @@ +import { logger } from '../../logger'; +import { UpdateDependencyConfig } from '../common'; + +export default function updateDependency({ + fileContent, + upgrade, +}: UpdateDependencyConfig): string | null { + try { + logger.debug(`ansible-galaxy.updateDependency(): ${upgrade.newValue}`); + const lines = fileContent.split('\n'); + const lineToChange = lines[upgrade.managerData.lineNumber]; + const regexMatchNewBlock = new RegExp( + /^(\s?-?\s?(src|scm|version|name):\s*["']?)([^"']*)(["']?.*)$/ + ); + if (!regexMatchNewBlock.exec(lineToChange)) { + logger.debug('No version line found'); + return null; + } + const newLine = lineToChange.replace( + regexMatchNewBlock, + `$1${upgrade.newValue}$4` + ); + if (newLine === lineToChange) { + logger.debug('No changes necessary'); + return fileContent; + } + lines[upgrade.managerData.lineNumber] = newLine; + return lines.join('\n'); + } catch (err) { + logger.info({ err }, 'Error setting new ansible-galaxy role version value'); + return null; + } +}