diff --git a/lib/config/definitions.js b/lib/config/definitions.js index c0d7d005d7db61ae74b63484781625a74b66a074..69d410a4cd746e99ca31fd0ab75df0d99a0f3f51 100644 --- a/lib/config/definitions.js +++ b/lib/config/definitions.js @@ -955,6 +955,29 @@ const options = [ }, mergeable: true, }, + { + name: 'go', + description: 'Configuration object for Go language', + stage: 'package', + type: 'json', + default: { + enabled: false, + rollbackPrs: false, + commitMessageTopic: 'module {{depNameShort}}', + }, + mergeable: true, + cli: false, + }, + { + name: 'gomod', + description: 'Configuration object for Go modules renovation', + stage: 'package', + type: 'json', + default: { + fileMatch: ['(^|/)go.mod$'], + }, + mergeable: true, + }, { name: 'supportPolicy', description: diff --git a/lib/datasource/go.js b/lib/datasource/go.js new file mode 100644 index 0000000000000000000000000000000000000000..3821af9336b75764ae5fbeecc549ec1d073272ac --- /dev/null +++ b/lib/datasource/go.js @@ -0,0 +1,48 @@ +const got = require('got'); +const github = require('./github'); + +module.exports = { + getPkgReleases, +}; + +async function getPkgReleases(purl, config) { + const { fullname: name } = purl; + logger.trace(`go.getPkgReleases(${name})`); + const pkgUrl = 'https://' + name + '?go-get=1'; + try { + const res = (await got(pkgUrl, { + retries: 5, + })).body; + const sourceMatch = res.match( + new RegExp(`<meta name="go-source" content="${name}\\s+([^\\s]+)`) + ); + if (sourceMatch) { + const [, sourceUrl] = sourceMatch; + logger.debug({ name, sourceUrl }, 'Go lookup sourceUrl'); + if (sourceUrl && sourceUrl.startsWith('https://github.com/')) { + const githubPurl = { + fullname: sourceUrl + .replace('https://github.com/', '') + .replace(/\/$/, ''), + qualifiers: {}, + }; + const githubTags = await github.getPkgReleases(githubPurl, config); + return githubTags; + } + logger.info('Unknown go source: ' + sourceUrl); + } else { + logger.trace({ depName: name }, 'No go-source header found'); + } + return null; + } catch (err) { + if (err.statusCode === 404 || err.code === 'ENOTFOUND') { + logger.info({ dependency: name }, `Dependency lookup failure: not found`); + logger.debug({ + err, + }); + return null; + } + logger.info({ err, name }, 'go lookup failure: Unknown error'); + return null; + } +} diff --git a/lib/datasource/index.js b/lib/datasource/index.js index 2f6a06d76cc0d27cc0cc094ff2d9f17c2f5851bf..f2ba48e8c8d09649bba83d4b3dfec2248f58fc10 100644 --- a/lib/datasource/index.js +++ b/lib/datasource/index.js @@ -2,6 +2,7 @@ const { parse } = require('../util/purl'); const docker = require('./docker'); const github = require('./github'); +const go = require('./go'); const npm = require('./npm'); const nuget = require('./nuget'); const packagist = require('./packagist'); @@ -10,6 +11,7 @@ const pypi = require('./pypi'); const datasources = { docker, github, + go, npm, nuget, packagist, diff --git a/lib/manager/gomod/artifacts.js b/lib/manager/gomod/artifacts.js new file mode 100644 index 0000000000000000000000000000000000000000..8b69ef4545a63bb9d374dd3934a58a6ad205a247 --- /dev/null +++ b/lib/manager/gomod/artifacts.js @@ -0,0 +1,96 @@ +const { exec } = require('child-process-promise'); +const fs = require('fs-extra'); +const upath = require('upath'); +const os = require('os'); + +module.exports = { + getArtifacts, +}; + +async function getArtifacts( + goModFileName, + updatedDeps, + newGoModContent, + config +) { + logger.debug(`gomod.getArtifacts(${goModFileName})`); + process.env.GOPATH = + process.env.GOPATH || upath.join(os.tmpdir(), '/renovate/cache/go'); + await fs.ensureDir(process.env.GOPATH); + logger.debug('Using GOPATH: ' + process.env.GOPATH); + const sumFileName = goModFileName.replace(/\.mod$/, '.sum'); + const existingGoSumContent = await platform.getFile(sumFileName); + if (!existingGoSumContent) { + logger.debug('No go.sum found'); + return null; + } + const cwd = upath.join(config.localDir, upath.dirname(goModFileName)); + let stdout; + let stderr; + try { + const localGoModFileName = upath.join(config.localDir, goModFileName); + await fs.outputFile(localGoModFileName, newGoModContent); + const localGoSumFileName = upath.join(config.localDir, sumFileName); + if (!config.gitFs) { + await fs.outputFile(localGoSumFileName, existingGoSumContent); + } + const env = + config.global && config.global.exposeEnv + ? process.env + : { + HOME: process.env.HOME, + PATH: process.env.PATH, + GOPATH: process.env.GOPATH, + }; + const startTime = process.hrtime(); + let cmd; + if (config.binarySource === 'docker') { + logger.info('Running go via docker'); + cmd = `docker run --rm `; + const volumes = [config.localDir, process.env.GOPATH]; + cmd += volumes.map(v => `-v ${v}:${v} `).join(''); + const envVars = ['GOPATH']; + cmd += envVars.map(e => `-e ${e} `); + cmd += `-w ${cwd} `; + cmd += `golang:1.11.0 go`; + } else { + logger.info('Running go via global command'); + cmd = 'go'; + } + const args = 'get'; + logger.debug({ cmd, args }, 'go get command'); + ({ stdout, stderr } = await exec(`${cmd} ${args}`, { + cwd, + shell: true, + env, + })); + const duration = process.hrtime(startTime); + const seconds = Math.round(duration[0] + duration[1] / 1e9); + logger.info( + { seconds, type: 'go.sum', stdout, stderr }, + 'Generated lockfile' + ); + // istanbul ignore if + if (config.gitFs) { + const status = await platform.getRepoStatus(); + if (!status.modified.includes(sumFileName)) { + return null; + } + } else { + const newGoSumContent = await fs.readFile(localGoSumFileName, 'utf8'); + + if (newGoSumContent === existingGoSumContent) { + logger.debug('go.sum is unchanged'); + return null; + } + } + logger.debug('Returning updated go.sum'); + return { + name: sumFileName, + contents: await fs.readFile(localGoSumFileName, 'utf8'), + }; + } catch (err) { + logger.warn({ err, message: err.message }, 'Failed to generate go.sum'); + return null; + } +} diff --git a/lib/manager/gomod/extract.js b/lib/manager/gomod/extract.js new file mode 100644 index 0000000000000000000000000000000000000000..de29ac073e4c210d7653e32a334cfaee2fadc9ab --- /dev/null +++ b/lib/manager/gomod/extract.js @@ -0,0 +1,79 @@ +const versioning = require('../../versioning'); + +const { isVersion } = versioning('semver'); + +module.exports = { + extractDependencies, +}; + +function getDep(lineNumber, match) { + const [, depName, currentValue] = match; + const dep = { + lineNumber, + depName, + depType: 'require', + currentValue, + versionScheme: 'semver', + }; + if (!isVersion(currentValue)) { + dep.skipReason = 'unsupported-version'; + } else if (depName.startsWith('gopkg.in/')) { + const [pkg] = depName.replace('gopkg.in/', '').split('.'); + dep.depNameShort = pkg; + if (pkg.includes('/')) { + dep.purl = `pkg:github/${pkg}`; + } else { + dep.purl = `pkg:github/go-${pkg}/${pkg}`; + } + } else if (depName.startsWith('github.com/')) { + dep.depNameShort = depName.replace('github.com/', ''); + dep.purl = 'pkg:' + depName.replace('github.com', 'github'); + } else { + dep.purl = `pkg:go/${depName}`; + dep.depNameShort = depName; + } + return dep; +} + +function extractDependencies(content) { + logger.debug('gomod.extractDependencies()'); + logger.trace({ content }); + const deps = []; + try { + const lines = content.split('\n'); + for (let lineNumber = 0; lineNumber < lines.length; lineNumber += 1) { + let line = lines[lineNumber]; + const requireMatch = line.match(/^require\s+([^\s]+)\s+([^\s]+)/); + if (requireMatch) { + logger.trace(`Matched single-line require on line ${lineNumber}`); + logger.debug(`require line: "${line}"`); + const dep = getDep(lineNumber, requireMatch); + deps.push(dep); + } + if (line.trim() === 'require (') { + logger.trace(`Matched multi-line require on line ${lineNumber}`); + do { + lineNumber += 1; + line = lines[lineNumber]; + const multiMatch = line.match(/^\s+([^\s]+)\s+([^\s]+)/); + logger.trace(`reqLine: "${line}"`); + if (multiMatch) { + logger.trace(`Matched multi-line require on line ${lineNumber}`); + logger.debug(`require line: "${line}"`); + const dep = getDep(lineNumber, multiMatch); + dep.multiLine = true; + deps.push(dep); + } else if (line.trim() !== ')') { + logger.debug(`No multi-line match: ${line}`); + } + } while (line.trim() !== ')'); + } + } + } catch (err) /* istanbul ignore next */ { + logger.error({ err, message: err.message }, 'Error extracting go modules'); + } + if (!deps.length) { + return null; + } + return { deps }; +} diff --git a/lib/manager/gomod/index.js b/lib/manager/gomod/index.js new file mode 100644 index 0000000000000000000000000000000000000000..a128563cde9c347ccf963a690d70f056bf8d2bd9 --- /dev/null +++ b/lib/manager/gomod/index.js @@ -0,0 +1,10 @@ +const { extractDependencies } = require('./extract'); +const { updateDependency } = require('./update'); +const { getArtifacts } = require('./artifacts'); + +module.exports = { + extractDependencies, + updateDependency, + getArtifacts, + language: 'go', +}; diff --git a/lib/manager/gomod/update.js b/lib/manager/gomod/update.js new file mode 100644 index 0000000000000000000000000000000000000000..a983c5c371569556681f2fcf8b2aa7cb2e5e3da5 --- /dev/null +++ b/lib/manager/gomod/update.js @@ -0,0 +1,38 @@ +module.exports = { + updateDependency, +}; + +function updateDependency(currentFileContent, upgrade) { + try { + logger.debug(`gomod.updateDependency: ${upgrade.newValue}`); + if (upgrade.updateType === 'major' && upgrade.newMajor > 1) { + logger.warn('Skipping major gomod upgrade'); + return currentFileContent; + } + const lines = currentFileContent.split('\n'); + const lineToChange = lines[upgrade.lineNumber]; + if (!lineToChange.includes(upgrade.depName)) { + return null; + } + let requireLine; + if (upgrade.multiLine) { + requireLine = new RegExp(/^(\s+[^\s]+\s+)[^\s]+/); + } else { + requireLine = new RegExp(/^(require\s+[^\s]+\s+)[^\s]+/); + } + if (!lineToChange.match(requireLine)) { + logger.debug('No image line found'); + return null; + } + const newLine = lineToChange.replace(requireLine, `$1${upgrade.newValue}`); + if (newLine === lineToChange) { + logger.debug('No changes necessary'); + return currentFileContent; + } + lines[upgrade.lineNumber] = newLine; + return lines.join('\n'); + } catch (err) { + logger.info({ err }, 'Error setting new go.mod version'); + return null; + } +} diff --git a/lib/manager/index.js b/lib/manager/index.js index 84ab8ba9ed3ee1378c13b3484c1bec9dd522ccd4..4779ecf24dd4a05e40ca3e95fb4e8d953b7e6228 100644 --- a/lib/manager/index.js +++ b/lib/manager/index.js @@ -6,6 +6,7 @@ const managerList = [ 'docker-compose', 'dockerfile', 'gitlabci', + 'gomod', 'kubernetes', 'meteor', 'npm', @@ -20,7 +21,7 @@ for (const manager of managerList) { managers[manager] = require(`./${manager}`); } -const languageList = ['docker', 'js', 'node', 'php', 'python']; +const languageList = ['docker', 'go', 'js', 'node', 'php', 'python']; const get = (manager, name) => managers[manager][name]; const getLanguageList = () => languageList; diff --git a/test/_fixtures/go/1/go.mod b/test/_fixtures/go/1/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..f3315c2f157dfa31832f6419accf871b32341ae4 --- /dev/null +++ b/test/_fixtures/go/1/go.mod @@ -0,0 +1,7 @@ +module github.com/renovate-tests/gomod1 + +require github.com/pkg/errors v0.7.0 +require github.com/aws/aws-sdk-go v1.15.21 +require github.com/davecgh/go-spew v1.0.0 +require golang.org/x/foo v1.0.0 +require github.com/rarkins/foo abcdef1 diff --git a/test/_fixtures/go/2/go.mod b/test/_fixtures/go/2/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..fff366badb3d016c546c7dff5f8a8c6f30cbbcf7 --- /dev/null +++ b/test/_fixtures/go/2/go.mod @@ -0,0 +1,63 @@ +module github.com/jesseduffield/lazygit + +require ( + github.com/aws/aws-sdk-go v1.15.21 + github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d + github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 + github.com/davecgh/go-spew v1.1.0 + github.com/emirpasic/gods v1.9.0 + github.com/fatih/color v1.7.0 + github.com/fsnotify/fsnotify v1.4.7 + github.com/go-ini/ini v1.38.2 + github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 + github.com/hashicorp/go-cleanhttp v0.0.0-20171218145408-d5fe4b57a186 + github.com/hashicorp/go-getter v0.0.0-20180809191950-4bda8fa99001 + github.com/hashicorp/go-safetemp v0.0.0-20180326211150-b1a1dbde6fdc + github.com/hashicorp/go-version v1.0.0 + github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce + github.com/heroku/rollrus v0.0.0-20180515183152-fc0cef2ff331 + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 + github.com/jesseduffield/go-getter v0.0.0-20180822080847-906e15686e63 + github.com/jesseduffield/gocui v0.0.0-20180921065632-03e26ff3f1de + github.com/jesseduffield/termbox-go v0.0.0-20180919093808-1e272ff78dcb + github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 + github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 + github.com/kevinburke/ssh_config v0.0.0-20180317175531-9fc7bb800b55 + github.com/magiconair/properties v1.8.0 + github.com/mattn/go-colorable v0.0.9 + github.com/mattn/go-isatty v0.0.3 + github.com/mattn/go-runewidth v0.0.2 + github.com/mgutz/str v1.2.0 + github.com/mitchellh/go-homedir v0.0.0-20180801233206-58046073cbff + github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 + github.com/mitchellh/mapstructure v0.0.0-20180715050151-f15292f7a699 + github.com/nicksnyder/go-i18n v0.0.0-20180803040939-a16b91a3ba80 + github.com/pelletier/go-buffruneio v0.2.0 + github.com/pelletier/go-toml v1.2.0 + github.com/pkg/errors v0.8.0 + github.com/pmezard/go-difflib v1.0.0 + github.com/sergi/go-diff v1.0.0 + github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 + github.com/sirupsen/logrus v1.0.6 + github.com/spf13/afero v1.1.1 + github.com/spf13/cast v1.2.0 + github.com/spf13/jwalterweatherman v0.0.0-20180814060501-14d3d4c51834 + github.com/spf13/pflag v1.0.2 + github.com/spf13/viper v1.1.0 + github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad + github.com/src-d/gcfg v1.3.0 + github.com/stretchr/testify v1.2.2 + github.com/stvp/roll v0.0.0-20170522205222-3627a5cbeaea + github.com/tcnksm/go-gitconfig v0.1.2 + github.com/ulikunitz/xz v0.5.4 + github.com/xanzy/ssh-agent v0.2.0 + golang.org/x/crypto v0.0.0-20180808211826-de0752318171 + golang.org/x/net v0.0.0-20180811021610-c39426892332 + golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0 + golang.org/x/text v0.3.0 + gopkg.in/src-d/go-billy.v4 v4.2.0 + gopkg.in/src-d/go-git.v4 v4.0.0-20180807092216-43d17e14b714 + gopkg.in/warnings.v0 v0.1.2 + gopkg.in/yaml.v2 v2.2.1 + +) diff --git a/test/datasource/__snapshots__/go.spec.js.snap b/test/datasource/__snapshots__/go.spec.js.snap new file mode 100644 index 0000000000000000000000000000000000000000..cc080147296a796471aa230e3a956f777ef943d4 --- /dev/null +++ b/test/datasource/__snapshots__/go.spec.js.snap @@ -0,0 +1,10 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`datasource/go getPkgReleases processes real data 1`] = ` +Object { + "releases": Array [ + 1, + 2, + ], +} +`; diff --git a/test/datasource/go.spec.js b/test/datasource/go.spec.js new file mode 100644 index 0000000000000000000000000000000000000000..18a2256d5fd32f33003efe9ed770c4faa5441a2f --- /dev/null +++ b/test/datasource/go.spec.js @@ -0,0 +1,75 @@ +const got = require('got'); +const datasource = require('../../lib/datasource'); +const github = require('../../lib/datasource/github'); + +jest.mock('got'); +jest.mock('../../lib/datasource/github'); + +const res1 = `<!DOCTYPE html> +<html> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> +<meta name="go-import" content="golang.org/x/text git https://go.googlesource.com/text"> +<meta name="go-source" content="golang.org/x/text https://github.com/golang/text/ https://github.com/golang/text/tree/master{/dir} https://github.com/golang/text/blob/master{/dir}/{file}#L{line}"> +<meta http-equiv="refresh" content="0; url=https://godoc.org/golang.org/x/text"> +</head> +<body> +Nothing to see here; <a href="https://godoc.org/golang.org/x/text">move along</a>. +</body> +</html>`; + +describe('datasource/go', () => { + describe('getPkgReleases', () => { + it('returns null for empty result', async () => { + got.mockReturnValueOnce(null); + expect( + await datasource.getPkgReleases('pkg:go/golang.org/foo/something') + ).toBeNull(); + }); + it('returns null for 404', async () => { + got.mockImplementationOnce(() => + Promise.reject({ + statusCode: 404, + }) + ); + expect( + await datasource.getPkgReleases('pkg:go/golang.org/foo/something') + ).toBeNull(); + }); + it('returns null for unknown error', async () => { + got.mockImplementationOnce(() => { + throw new Error(); + }); + expect( + await datasource.getPkgReleases('pkg:go/golang.org/foo/something') + ).toBeNull(); + }); + it('processes real data', async () => { + got.mockReturnValueOnce({ + body: res1, + }); + github.getPkgReleases.mockReturnValueOnce({ releases: [1, 2] }); + const res = await datasource.getPkgReleases('pkg:go/golang.org/x/text'); + expect(res).toMatchSnapshot(); + expect(res).not.toBeNull(); + expect(res).toBeDefined(); + }); + it('skips wrong package', async () => { + got.mockReturnValueOnce({ + body: res1, + }); + const res = await datasource.getPkgReleases('pkg:go/golang.org/x/sys'); + expect(res).toBeNull(); + }); + it('skips unsupported platform', async () => { + got.mockReturnValueOnce({ + body: res1.replace( + 'https://github.com/golang/text/', + 'https://google.com/golang/text/' + ), + }); + const res = await datasource.getPkgReleases('pkg:go/golang.org/x/text'); + expect(res).toBeNull(); + }); + }); +}); diff --git a/test/manager/gomod/__snapshots__/extract.spec.js.snap b/test/manager/gomod/__snapshots__/extract.spec.js.snap new file mode 100644 index 0000000000000000000000000000000000000000..8789bd978751397682ff036b1b5cdeb1ed62804b --- /dev/null +++ b/test/manager/gomod/__snapshots__/extract.spec.js.snap @@ -0,0 +1,635 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`lib/manager/gomod/extract extractDependencies() extracts multi-line requires 1`] = ` +Array [ + Object { + "currentValue": "v1.15.21", + "depName": "github.com/aws/aws-sdk-go", + "depNameShort": "aws/aws-sdk-go", + "depType": "require", + "lineNumber": 3, + "multiLine": true, + "purl": "pkg:github/aws/aws-sdk-go", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.0.0-20140422174119-9fd32a8b3d3d", + "depName": "github.com/bgentry/go-netrc", + "depNameShort": "bgentry/go-netrc", + "depType": "require", + "lineNumber": 4, + "multiLine": true, + "purl": "pkg:github/bgentry/go-netrc", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.0.0-20151120183258-bcc4c8345a21", + "depName": "github.com/cloudfoundry/jibber_jabber", + "depNameShort": "cloudfoundry/jibber_jabber", + "depType": "require", + "lineNumber": 5, + "multiLine": true, + "purl": "pkg:github/cloudfoundry/jibber_jabber", + "versionScheme": "semver", + }, + Object { + "currentValue": "v1.1.0", + "depName": "github.com/davecgh/go-spew", + "depNameShort": "davecgh/go-spew", + "depType": "require", + "lineNumber": 6, + "multiLine": true, + "purl": "pkg:github/davecgh/go-spew", + "versionScheme": "semver", + }, + Object { + "currentValue": "v1.9.0", + "depName": "github.com/emirpasic/gods", + "depNameShort": "emirpasic/gods", + "depType": "require", + "lineNumber": 7, + "multiLine": true, + "purl": "pkg:github/emirpasic/gods", + "versionScheme": "semver", + }, + Object { + "currentValue": "v1.7.0", + "depName": "github.com/fatih/color", + "depNameShort": "fatih/color", + "depType": "require", + "lineNumber": 8, + "multiLine": true, + "purl": "pkg:github/fatih/color", + "versionScheme": "semver", + }, + Object { + "currentValue": "v1.4.7", + "depName": "github.com/fsnotify/fsnotify", + "depNameShort": "fsnotify/fsnotify", + "depType": "require", + "lineNumber": 9, + "multiLine": true, + "purl": "pkg:github/fsnotify/fsnotify", + "versionScheme": "semver", + }, + Object { + "currentValue": "v1.38.2", + "depName": "github.com/go-ini/ini", + "depNameShort": "go-ini/ini", + "depType": "require", + "lineNumber": 10, + "multiLine": true, + "purl": "pkg:github/go-ini/ini", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.0.0-20130729185459-604e922904d3", + "depName": "github.com/golang-collections/collections", + "depNameShort": "golang-collections/collections", + "depType": "require", + "lineNumber": 11, + "multiLine": true, + "purl": "pkg:github/golang-collections/collections", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.0.0-20171218145408-d5fe4b57a186", + "depName": "github.com/hashicorp/go-cleanhttp", + "depNameShort": "hashicorp/go-cleanhttp", + "depType": "require", + "lineNumber": 12, + "multiLine": true, + "purl": "pkg:github/hashicorp/go-cleanhttp", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.0.0-20180809191950-4bda8fa99001", + "depName": "github.com/hashicorp/go-getter", + "depNameShort": "hashicorp/go-getter", + "depType": "require", + "lineNumber": 13, + "multiLine": true, + "purl": "pkg:github/hashicorp/go-getter", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.0.0-20180326211150-b1a1dbde6fdc", + "depName": "github.com/hashicorp/go-safetemp", + "depNameShort": "hashicorp/go-safetemp", + "depType": "require", + "lineNumber": 14, + "multiLine": true, + "purl": "pkg:github/hashicorp/go-safetemp", + "versionScheme": "semver", + }, + Object { + "currentValue": "v1.0.0", + "depName": "github.com/hashicorp/go-version", + "depNameShort": "hashicorp/go-version", + "depType": "require", + "lineNumber": 15, + "multiLine": true, + "purl": "pkg:github/hashicorp/go-version", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.0.0-20180404174102-ef8a98b0bbce", + "depName": "github.com/hashicorp/hcl", + "depNameShort": "hashicorp/hcl", + "depType": "require", + "lineNumber": 16, + "multiLine": true, + "purl": "pkg:github/hashicorp/hcl", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.0.0-20180515183152-fc0cef2ff331", + "depName": "github.com/heroku/rollrus", + "depNameShort": "heroku/rollrus", + "depType": "require", + "lineNumber": 17, + "multiLine": true, + "purl": "pkg:github/heroku/rollrus", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.0.0-20150711004518-d14ea06fba99", + "depName": "github.com/jbenet/go-context", + "depNameShort": "jbenet/go-context", + "depType": "require", + "lineNumber": 18, + "multiLine": true, + "purl": "pkg:github/jbenet/go-context", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.0.0-20180822080847-906e15686e63", + "depName": "github.com/jesseduffield/go-getter", + "depNameShort": "jesseduffield/go-getter", + "depType": "require", + "lineNumber": 19, + "multiLine": true, + "purl": "pkg:github/jesseduffield/go-getter", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.0.0-20180921065632-03e26ff3f1de", + "depName": "github.com/jesseduffield/gocui", + "depNameShort": "jesseduffield/gocui", + "depType": "require", + "lineNumber": 20, + "multiLine": true, + "purl": "pkg:github/jesseduffield/gocui", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.0.0-20180919093808-1e272ff78dcb", + "depName": "github.com/jesseduffield/termbox-go", + "depNameShort": "jesseduffield/termbox-go", + "depType": "require", + "lineNumber": 21, + "multiLine": true, + "purl": "pkg:github/jesseduffield/termbox-go", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.0.0-20160202185014-0b12d6b521d8", + "depName": "github.com/jmespath/go-jmespath", + "depNameShort": "jmespath/go-jmespath", + "depType": "require", + "lineNumber": 22, + "multiLine": true, + "purl": "pkg:github/jmespath/go-jmespath", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.0.0-20170510131534-ae77be60afb1", + "depName": "github.com/kardianos/osext", + "depNameShort": "kardianos/osext", + "depType": "require", + "lineNumber": 23, + "multiLine": true, + "purl": "pkg:github/kardianos/osext", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.0.0-20180317175531-9fc7bb800b55", + "depName": "github.com/kevinburke/ssh_config", + "depNameShort": "kevinburke/ssh_config", + "depType": "require", + "lineNumber": 24, + "multiLine": true, + "purl": "pkg:github/kevinburke/ssh_config", + "versionScheme": "semver", + }, + Object { + "currentValue": "v1.8.0", + "depName": "github.com/magiconair/properties", + "depNameShort": "magiconair/properties", + "depType": "require", + "lineNumber": 25, + "multiLine": true, + "purl": "pkg:github/magiconair/properties", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.0.9", + "depName": "github.com/mattn/go-colorable", + "depNameShort": "mattn/go-colorable", + "depType": "require", + "lineNumber": 26, + "multiLine": true, + "purl": "pkg:github/mattn/go-colorable", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.0.3", + "depName": "github.com/mattn/go-isatty", + "depNameShort": "mattn/go-isatty", + "depType": "require", + "lineNumber": 27, + "multiLine": true, + "purl": "pkg:github/mattn/go-isatty", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.0.2", + "depName": "github.com/mattn/go-runewidth", + "depNameShort": "mattn/go-runewidth", + "depType": "require", + "lineNumber": 28, + "multiLine": true, + "purl": "pkg:github/mattn/go-runewidth", + "versionScheme": "semver", + }, + Object { + "currentValue": "v1.2.0", + "depName": "github.com/mgutz/str", + "depNameShort": "mgutz/str", + "depType": "require", + "lineNumber": 29, + "multiLine": true, + "purl": "pkg:github/mgutz/str", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.0.0-20180801233206-58046073cbff", + "depName": "github.com/mitchellh/go-homedir", + "depNameShort": "mitchellh/go-homedir", + "depType": "require", + "lineNumber": 30, + "multiLine": true, + "purl": "pkg:github/mitchellh/go-homedir", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.0.0-20171004221916-a61a99592b77", + "depName": "github.com/mitchellh/go-testing-interface", + "depNameShort": "mitchellh/go-testing-interface", + "depType": "require", + "lineNumber": 31, + "multiLine": true, + "purl": "pkg:github/mitchellh/go-testing-interface", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.0.0-20180715050151-f15292f7a699", + "depName": "github.com/mitchellh/mapstructure", + "depNameShort": "mitchellh/mapstructure", + "depType": "require", + "lineNumber": 32, + "multiLine": true, + "purl": "pkg:github/mitchellh/mapstructure", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.0.0-20180803040939-a16b91a3ba80", + "depName": "github.com/nicksnyder/go-i18n", + "depNameShort": "nicksnyder/go-i18n", + "depType": "require", + "lineNumber": 33, + "multiLine": true, + "purl": "pkg:github/nicksnyder/go-i18n", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.2.0", + "depName": "github.com/pelletier/go-buffruneio", + "depNameShort": "pelletier/go-buffruneio", + "depType": "require", + "lineNumber": 34, + "multiLine": true, + "purl": "pkg:github/pelletier/go-buffruneio", + "versionScheme": "semver", + }, + Object { + "currentValue": "v1.2.0", + "depName": "github.com/pelletier/go-toml", + "depNameShort": "pelletier/go-toml", + "depType": "require", + "lineNumber": 35, + "multiLine": true, + "purl": "pkg:github/pelletier/go-toml", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.8.0", + "depName": "github.com/pkg/errors", + "depNameShort": "pkg/errors", + "depType": "require", + "lineNumber": 36, + "multiLine": true, + "purl": "pkg:github/pkg/errors", + "versionScheme": "semver", + }, + Object { + "currentValue": "v1.0.0", + "depName": "github.com/pmezard/go-difflib", + "depNameShort": "pmezard/go-difflib", + "depType": "require", + "lineNumber": 37, + "multiLine": true, + "purl": "pkg:github/pmezard/go-difflib", + "versionScheme": "semver", + }, + Object { + "currentValue": "v1.0.0", + "depName": "github.com/sergi/go-diff", + "depNameShort": "sergi/go-diff", + "depType": "require", + "lineNumber": 38, + "multiLine": true, + "purl": "pkg:github/sergi/go-diff", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.0.0-20170330084843-e180dbdc8da0", + "depName": "github.com/shibukawa/configdir", + "depNameShort": "shibukawa/configdir", + "depType": "require", + "lineNumber": 39, + "multiLine": true, + "purl": "pkg:github/shibukawa/configdir", + "versionScheme": "semver", + }, + Object { + "currentValue": "v1.0.6", + "depName": "github.com/sirupsen/logrus", + "depNameShort": "sirupsen/logrus", + "depType": "require", + "lineNumber": 40, + "multiLine": true, + "purl": "pkg:github/sirupsen/logrus", + "versionScheme": "semver", + }, + Object { + "currentValue": "v1.1.1", + "depName": "github.com/spf13/afero", + "depNameShort": "spf13/afero", + "depType": "require", + "lineNumber": 41, + "multiLine": true, + "purl": "pkg:github/spf13/afero", + "versionScheme": "semver", + }, + Object { + "currentValue": "v1.2.0", + "depName": "github.com/spf13/cast", + "depNameShort": "spf13/cast", + "depType": "require", + "lineNumber": 42, + "multiLine": true, + "purl": "pkg:github/spf13/cast", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.0.0-20180814060501-14d3d4c51834", + "depName": "github.com/spf13/jwalterweatherman", + "depNameShort": "spf13/jwalterweatherman", + "depType": "require", + "lineNumber": 43, + "multiLine": true, + "purl": "pkg:github/spf13/jwalterweatherman", + "versionScheme": "semver", + }, + Object { + "currentValue": "v1.0.2", + "depName": "github.com/spf13/pflag", + "depNameShort": "spf13/pflag", + "depType": "require", + "lineNumber": 44, + "multiLine": true, + "purl": "pkg:github/spf13/pflag", + "versionScheme": "semver", + }, + Object { + "currentValue": "v1.1.0", + "depName": "github.com/spf13/viper", + "depNameShort": "spf13/viper", + "depType": "require", + "lineNumber": 45, + "multiLine": true, + "purl": "pkg:github/spf13/viper", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.0.0-20160624110644-59b7046e48ad", + "depName": "github.com/spkg/bom", + "depNameShort": "spkg/bom", + "depType": "require", + "lineNumber": 46, + "multiLine": true, + "purl": "pkg:github/spkg/bom", + "versionScheme": "semver", + }, + Object { + "currentValue": "v1.3.0", + "depName": "github.com/src-d/gcfg", + "depNameShort": "src-d/gcfg", + "depType": "require", + "lineNumber": 47, + "multiLine": true, + "purl": "pkg:github/src-d/gcfg", + "versionScheme": "semver", + }, + Object { + "currentValue": "v1.2.2", + "depName": "github.com/stretchr/testify", + "depNameShort": "stretchr/testify", + "depType": "require", + "lineNumber": 48, + "multiLine": true, + "purl": "pkg:github/stretchr/testify", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.0.0-20170522205222-3627a5cbeaea", + "depName": "github.com/stvp/roll", + "depNameShort": "stvp/roll", + "depType": "require", + "lineNumber": 49, + "multiLine": true, + "purl": "pkg:github/stvp/roll", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.1.2", + "depName": "github.com/tcnksm/go-gitconfig", + "depNameShort": "tcnksm/go-gitconfig", + "depType": "require", + "lineNumber": 50, + "multiLine": true, + "purl": "pkg:github/tcnksm/go-gitconfig", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.5.4", + "depName": "github.com/ulikunitz/xz", + "depNameShort": "ulikunitz/xz", + "depType": "require", + "lineNumber": 51, + "multiLine": true, + "purl": "pkg:github/ulikunitz/xz", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.2.0", + "depName": "github.com/xanzy/ssh-agent", + "depNameShort": "xanzy/ssh-agent", + "depType": "require", + "lineNumber": 52, + "multiLine": true, + "purl": "pkg:github/xanzy/ssh-agent", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.0.0-20180808211826-de0752318171", + "depName": "golang.org/x/crypto", + "depNameShort": "golang.org/x/crypto", + "depType": "require", + "lineNumber": 53, + "multiLine": true, + "purl": "pkg:go/golang.org/x/crypto", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.0.0-20180811021610-c39426892332", + "depName": "golang.org/x/net", + "depNameShort": "golang.org/x/net", + "depType": "require", + "lineNumber": 54, + "multiLine": true, + "purl": "pkg:go/golang.org/x/net", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.0.0-20180810173357-98c5dad5d1a0", + "depName": "golang.org/x/sys", + "depNameShort": "golang.org/x/sys", + "depType": "require", + "lineNumber": 55, + "multiLine": true, + "purl": "pkg:go/golang.org/x/sys", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.3.0", + "depName": "golang.org/x/text", + "depNameShort": "golang.org/x/text", + "depType": "require", + "lineNumber": 56, + "multiLine": true, + "purl": "pkg:go/golang.org/x/text", + "versionScheme": "semver", + }, + Object { + "currentValue": "v4.2.0", + "depName": "gopkg.in/src-d/go-billy.v4", + "depNameShort": "src-d/go-billy", + "depType": "require", + "lineNumber": 57, + "multiLine": true, + "purl": "pkg:github/src-d/go-billy", + "versionScheme": "semver", + }, + Object { + "currentValue": "v4.0.0-20180807092216-43d17e14b714", + "depName": "gopkg.in/src-d/go-git.v4", + "depNameShort": "src-d/go-git", + "depType": "require", + "lineNumber": 58, + "multiLine": true, + "purl": "pkg:github/src-d/go-git", + "versionScheme": "semver", + }, + Object { + "currentValue": "v0.1.2", + "depName": "gopkg.in/warnings.v0", + "depNameShort": "warnings", + "depType": "require", + "lineNumber": 59, + "multiLine": true, + "purl": "pkg:github/go-warnings/warnings", + "versionScheme": "semver", + }, + Object { + "currentValue": "v2.2.1", + "depName": "gopkg.in/yaml.v2", + "depNameShort": "yaml", + "depType": "require", + "lineNumber": 60, + "multiLine": true, + "purl": "pkg:github/go-yaml/yaml", + "versionScheme": "semver", + }, +] +`; + +exports[`lib/manager/gomod/extract extractDependencies() extracts single-line requires 1`] = ` +Array [ + Object { + "currentValue": "v0.7.0", + "depName": "github.com/pkg/errors", + "depNameShort": "pkg/errors", + "depType": "require", + "lineNumber": 2, + "purl": "pkg:github/pkg/errors", + "versionScheme": "semver", + }, + Object { + "currentValue": "v1.15.21", + "depName": "github.com/aws/aws-sdk-go", + "depNameShort": "aws/aws-sdk-go", + "depType": "require", + "lineNumber": 3, + "purl": "pkg:github/aws/aws-sdk-go", + "versionScheme": "semver", + }, + Object { + "currentValue": "v1.0.0", + "depName": "github.com/davecgh/go-spew", + "depNameShort": "davecgh/go-spew", + "depType": "require", + "lineNumber": 4, + "purl": "pkg:github/davecgh/go-spew", + "versionScheme": "semver", + }, + Object { + "currentValue": "v1.0.0", + "depName": "golang.org/x/foo", + "depNameShort": "golang.org/x/foo", + "depType": "require", + "lineNumber": 5, + "purl": "pkg:go/golang.org/x/foo", + "versionScheme": "semver", + }, + Object { + "currentValue": "abcdef1", + "depName": "github.com/rarkins/foo", + "depType": "require", + "lineNumber": 6, + "skipReason": "unsupported-version", + "versionScheme": "semver", + }, +] +`; diff --git a/test/manager/gomod/__snapshots__/update.spec.js.snap b/test/manager/gomod/__snapshots__/update.spec.js.snap new file mode 100644 index 0000000000000000000000000000000000000000..6c94f43d179e95e78b9dcf29c4b263480bde8624 --- /dev/null +++ b/test/manager/gomod/__snapshots__/update.spec.js.snap @@ -0,0 +1,12 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`manager/gomod/update updateDependency replaces two values in one file 1`] = ` +"module github.com/renovate-tests/gomod1 + +require github.com/pkg/errors v0.8.0 +require github.com/aws/aws-sdk-go v1.15.36 +require github.com/davecgh/go-spew v1.0.0 +require golang.org/x/foo v1.0.0 +require github.com/rarkins/foo abcdef1 +" +`; diff --git a/test/manager/gomod/artifacts.spec.js b/test/manager/gomod/artifacts.spec.js new file mode 100644 index 0000000000000000000000000000000000000000..133f1cf3edbc97002bdc42c833017eb2b0bba3f0 --- /dev/null +++ b/test/manager/gomod/artifacts.spec.js @@ -0,0 +1,60 @@ +jest.mock('fs-extra'); +jest.mock('child-process-promise'); + +const fs = require('fs-extra'); +const { exec } = require('child-process-promise'); +const gomod = require('../../../lib/manager/gomod/artifacts'); + +const config = { + localDir: '/tmp/github/some/repo', +}; + +describe('.getArtifacts()', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + it('returns if no go.sum found', async () => { + expect(await gomod.getArtifacts('gomod.json', [], '{}', config)).toBeNull(); + }); + it('returns null if unchanged', async () => { + platform.getFile.mockReturnValueOnce('Current go.sum'); + exec.mockReturnValueOnce({ + stdout: '', + stderror: '', + }); + fs.readFile = jest.fn(() => 'Current go.sum'); + expect(await gomod.getArtifacts('gomod.json', [], '{}', config)).toBeNull(); + }); + it('returns updated go.sum', async () => { + platform.getFile.mockReturnValueOnce('Current go.sum'); + exec.mockReturnValueOnce({ + stdout: '', + stderror: '', + }); + fs.readFile = jest.fn(() => 'New go.sum'); + expect( + await gomod.getArtifacts('gomod.json', [], '{}', config) + ).not.toBeNull(); + }); + it('supports docker mode', async () => { + platform.getFile.mockReturnValueOnce('Current go.sum'); + exec.mockReturnValueOnce({ + stdout: '', + stderror: '', + }); + fs.readFile = jest.fn(() => 'New go.sum'); + expect( + await gomod.getArtifacts('gomod.json', [], '{}', { + ...config, + binarySource: 'docker', + }) + ).not.toBeNull(); + }); + it('catches errors', async () => { + platform.getFile.mockReturnValueOnce('Current go.sum'); + fs.outputFile = jest.fn(() => { + throw new Error('not found'); + }); + expect(await gomod.getArtifacts('gomod.json', [], '{}', config)).toBeNull(); + }); +}); diff --git a/test/manager/gomod/extract.spec.js b/test/manager/gomod/extract.spec.js new file mode 100644 index 0000000000000000000000000000000000000000..afeb2b69b07db3280afd94d831b3002e4ac60e09 --- /dev/null +++ b/test/manager/gomod/extract.spec.js @@ -0,0 +1,29 @@ +const fs = require('fs'); +const { extractDependencies } = require('../../../lib/manager/gomod/extract'); + +const gomod1 = fs.readFileSync('test/_fixtures/go/1/go.mod', 'utf8'); +const gomod2 = fs.readFileSync('test/_fixtures/go/2/go.mod', 'utf8'); + +describe('lib/manager/gomod/extract', () => { + describe('extractDependencies()', () => { + let config; + beforeEach(() => { + config = {}; + }); + it('returns null for empty', () => { + expect(extractDependencies('nothing here', config)).toBe(null); + }); + it('extracts single-line requires', () => { + const res = extractDependencies(gomod1, config).deps; + expect(res).toMatchSnapshot(); + expect(res).toHaveLength(5); + expect(res.filter(e => e.skipReason).length).toBe(1); + }); + it('extracts multi-line requires', () => { + const res = extractDependencies(gomod2, config).deps; + expect(res).toMatchSnapshot(); + expect(res).toHaveLength(58); + expect(res.filter(e => e.skipReason).length).toBe(0); + }); + }); +}); diff --git a/test/manager/gomod/update.spec.js b/test/manager/gomod/update.spec.js new file mode 100644 index 0000000000000000000000000000000000000000..5e5f146d764dd8bba0212fd2a79dc066ca9bc956 --- /dev/null +++ b/test/manager/gomod/update.spec.js @@ -0,0 +1,91 @@ +const fs = require('fs'); +const goUpdate = require('../../../lib/manager/gomod/update'); + +const gomod1 = fs.readFileSync('test/_fixtures/go/1/go.mod', 'utf8'); +const gomod2 = fs.readFileSync('test/_fixtures/go/2/go.mod', 'utf8'); + +describe('manager/gomod/update', () => { + describe('updateDependency', () => { + it('replaces existing value', () => { + const upgrade = { + depName: 'github.com/pkg/errors', + lineNumber: 2, + newValue: 'v0.8.0', + }; + const res = goUpdate.updateDependency(gomod1, upgrade); + expect(res).not.toEqual(gomod1); + expect(res.includes(upgrade.newValue)).toBe(true); + }); + it('replaces two values in one file', () => { + const upgrade1 = { + depName: 'github.com/pkg/errors', + lineNumber: 2, + newValue: 'v0.8.0', + }; + const res1 = goUpdate.updateDependency(gomod1, upgrade1); + expect(res1).not.toEqual(gomod1); + expect(res1.includes(upgrade1.newValue)).toBe(true); + const upgrade2 = { + depName: 'github.com/aws/aws-sdk-go', + lineNumber: 3, + newValue: 'v1.15.36', + }; + const res2 = goUpdate.updateDependency(res1, upgrade2); + expect(res2).not.toEqual(res1); + expect(res2).toMatchSnapshot(); + }); + it('returns same', () => { + const upgrade = { + depName: 'github.com/pkg/errors', + lineNumber: 2, + newValue: 'v0.7.0', + }; + const res = goUpdate.updateDependency(gomod1, upgrade); + expect(res).toEqual(gomod1); + }); + it('skips major updates > 1', () => { + const upgrade = { + depName: 'github.com/pkg/errors', + lineNumber: 2, + newMajor: 2, + updateType: 'major', + newValue: 'v2.0.0', + }; + const res = goUpdate.updateDependency(gomod1, upgrade); + expect(res).toEqual(gomod1); + }); + it('returns null if mismatch', () => { + const upgrade = { + depName: 'github.com/aws/aws-sdk-go', + lineNumber: 2, + newValue: 'v1.15.36', + }; + const res = goUpdate.updateDependency(gomod1, upgrade); + expect(res).toBe(null); + }); + it('returns null if error', () => { + const res = goUpdate.updateDependency(null, null); + expect(res).toBe(null); + }); + it('replaces multiline', () => { + const upgrade = { + depName: 'github.com/fatih/color', + lineNumber: 8, + multiLine: true, + newValue: 'v1.8.0', + }; + const res = goUpdate.updateDependency(gomod2, upgrade); + expect(res).not.toEqual(gomod2); + expect(res.includes(upgrade.newValue)).toBe(true); + }); + it('handles multiline mismatch', () => { + const upgrade = { + depName: 'github.com/fatih/color', + lineNumber: 8, + newValue: 'v1.8.0', + }; + const res = goUpdate.updateDependency(gomod2, upgrade); + expect(res).toBe(null); + }); + }); +}); diff --git a/test/workers/repository/extract/__snapshots__/index.spec.js.snap b/test/workers/repository/extract/__snapshots__/index.spec.js.snap index 34aef0e89333c893a752b935b1adc6711bec38a9..ea9f223de419c5b076473e527abfe493212429fa 100644 --- a/test/workers/repository/extract/__snapshots__/index.spec.js.snap +++ b/test/workers/repository/extract/__snapshots__/index.spec.js.snap @@ -23,6 +23,9 @@ Object { "gitlabci": Array [ Object {}, ], + "gomod": Array [ + Object {}, + ], "kubernetes": Array [ Object {}, ], diff --git a/website/docs/configuration-options.md b/website/docs/configuration-options.md index ef961ad4fbc1b6979f04a019f97f689ca4110d9c..2d3f65dee3ddd7dd005208a99c17d18224696d19 100644 --- a/website/docs/configuration-options.md +++ b/website/docs/configuration-options.md @@ -215,6 +215,14 @@ The primary use case for this option is if you are following a pre-release tag o Add to this configuration setting if you need to override any of the GitLab CI default settings. Use the `docker` config object instead if you wish for configuration to apply across all Docker-related package managers. +## go + +Configuration added here applies for all Go-related updates, however currently the only supported package manager for Go is the native Go Modules (`go mod`). + +## gomod + +Configuration for Go Modules (`go mod`). Supercedes anything in the `go` config object. + ## group The default configuration for groups are essentially internal to Renovate and you normally shouldn't need to modify them. However, you may choose to _add_ settings to any group by defining your own `group` configuration object.