mirror of
https://github.com/renovatebot/renovate.git
synced 2025-01-11 14:36:25 +00:00
feat(swift): Support for Package.swift files (#3911)
This commit is contained in:
parent
1d6880b1a6
commit
acd318a1d9
17 changed files with 2073 additions and 7 deletions
|
@ -438,6 +438,7 @@ const options = [
|
|||
'poetry',
|
||||
'ruby',
|
||||
'semver',
|
||||
'swift',
|
||||
],
|
||||
default: 'semver',
|
||||
cli: false,
|
||||
|
@ -1811,6 +1812,19 @@ const options = [
|
|||
env: false,
|
||||
mergeable: true,
|
||||
},
|
||||
{
|
||||
name: 'swift',
|
||||
description: 'Configuration for Package.swift files',
|
||||
stage: 'package',
|
||||
type: 'object',
|
||||
default: {
|
||||
fileMatch: ['(^|/)Package\\.swift'],
|
||||
versionScheme: 'swift',
|
||||
rangeStrategy: 'bump',
|
||||
},
|
||||
mergeable: true,
|
||||
cli: false,
|
||||
},
|
||||
];
|
||||
|
||||
function getOptions() {
|
||||
|
|
|
@ -6,7 +6,7 @@ const cacheMinutes = 10;
|
|||
async function getPkgReleases({ lookupName }) {
|
||||
try {
|
||||
const cachedResult = await renovateCache.get(cacheNamespace, lookupName);
|
||||
// istanbul ignore if
|
||||
/* istanbul ignore next line */
|
||||
if (cachedResult) return cachedResult;
|
||||
|
||||
const info = await getRemoteInfo({
|
||||
|
|
|
@ -27,6 +27,7 @@ const managerList = [
|
|||
'poetry',
|
||||
'pub',
|
||||
'sbt',
|
||||
'swift',
|
||||
'terraform',
|
||||
'travis',
|
||||
'ruby-version',
|
||||
|
|
346
lib/manager/swift/extract.js
Normal file
346
lib/manager/swift/extract.js
Normal file
|
@ -0,0 +1,346 @@
|
|||
const { isValid } = require('../../versioning/swift');
|
||||
|
||||
const regExps = {
|
||||
wildcard: /^.*?/,
|
||||
space: /(\s+|\/\/[^\n]*|\/\*.*\*\/)+/s,
|
||||
depsKeyword: /dependencies/,
|
||||
colon: /:/,
|
||||
beginSection: /\[/,
|
||||
endSection: /],?/,
|
||||
package: /\s*.\s*package\s*\(\s*/,
|
||||
urlKey: /url/,
|
||||
stringLiteral: /"[^"]+"/,
|
||||
comma: /,/,
|
||||
from: /from/,
|
||||
rangeOp: /\.\.[.<]/,
|
||||
exactVersion: /\.\s*exact\s*\(\s*/,
|
||||
};
|
||||
|
||||
const WILDCARD = 'wildcard';
|
||||
const SPACE = 'space';
|
||||
const DEPS = 'depsKeyword';
|
||||
const COLON = 'colon';
|
||||
const BEGIN_SECTION = 'beginSection';
|
||||
const END_SECTION = 'endSection';
|
||||
const PACKAGE = 'package';
|
||||
const URL_KEY = 'urlKey';
|
||||
const STRING_LITERAL = 'stringLiteral';
|
||||
const COMMA = 'comma';
|
||||
const FROM = 'from';
|
||||
const RANGE_OP = 'rangeOp';
|
||||
const EXACT_VERSION = 'exactVersion';
|
||||
|
||||
const searchLabels = {
|
||||
wildcard: WILDCARD,
|
||||
space: SPACE,
|
||||
depsKeyword: DEPS,
|
||||
colon: COLON,
|
||||
beginSection: BEGIN_SECTION,
|
||||
endSection: END_SECTION,
|
||||
package: PACKAGE,
|
||||
urlKey: URL_KEY,
|
||||
stringLiteral: STRING_LITERAL,
|
||||
comma: COMMA,
|
||||
from: FROM,
|
||||
rangeOp: RANGE_OP,
|
||||
exactVersion: EXACT_VERSION,
|
||||
};
|
||||
|
||||
function searchKeysForState(state) {
|
||||
switch (state) {
|
||||
case 'dependencies':
|
||||
return [SPACE, COLON, WILDCARD];
|
||||
case 'dependencies:':
|
||||
return [SPACE, BEGIN_SECTION, WILDCARD];
|
||||
case 'dependencies: [':
|
||||
return [SPACE, PACKAGE, END_SECTION];
|
||||
case '.package(':
|
||||
return [SPACE, URL_KEY, PACKAGE, END_SECTION];
|
||||
case '.package(url':
|
||||
return [SPACE, COLON, PACKAGE, END_SECTION];
|
||||
case '.package(url:':
|
||||
return [SPACE, STRING_LITERAL, PACKAGE, END_SECTION];
|
||||
case '.package(url: [depName]':
|
||||
return [SPACE, COMMA, PACKAGE, END_SECTION];
|
||||
case '.package(url: [depName],':
|
||||
return [
|
||||
SPACE,
|
||||
FROM,
|
||||
STRING_LITERAL,
|
||||
RANGE_OP,
|
||||
EXACT_VERSION,
|
||||
PACKAGE,
|
||||
END_SECTION,
|
||||
];
|
||||
case '.package(url: [depName], .exact(':
|
||||
return [SPACE, STRING_LITERAL, PACKAGE, END_SECTION];
|
||||
case '.package(url: [depName], from':
|
||||
return [SPACE, COLON, PACKAGE, END_SECTION];
|
||||
case '.package(url: [depName], from:':
|
||||
return [SPACE, STRING_LITERAL, PACKAGE, END_SECTION];
|
||||
case '.package(url: [depName], [value]':
|
||||
return [SPACE, RANGE_OP, PACKAGE, END_SECTION];
|
||||
case '.package(url: [depName], [rangeFrom][rangeOp]':
|
||||
return [SPACE, STRING_LITERAL, PACKAGE, END_SECTION];
|
||||
default:
|
||||
return [DEPS];
|
||||
}
|
||||
}
|
||||
|
||||
function getMatch(str, state) {
|
||||
const keys = searchKeysForState(state);
|
||||
let result = null;
|
||||
for (let i = 0; i < keys.length; i += 1) {
|
||||
const key = keys[i];
|
||||
const regex = regExps[key];
|
||||
const label = searchLabels[key];
|
||||
const match = str.match(regex);
|
||||
if (match) {
|
||||
const idx = match.index;
|
||||
const substr = match[0];
|
||||
const len = substr.length;
|
||||
if (idx === 0) {
|
||||
return { idx, len, label, substr };
|
||||
}
|
||||
if (!result || idx < result.idx) {
|
||||
result = { idx, len, label, substr };
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function getDepName(url) {
|
||||
try {
|
||||
const { host, pathname } = new URL(url);
|
||||
if (host === 'github.com' || host === 'gitlab.com') {
|
||||
return pathname
|
||||
.replace(/^\//, '')
|
||||
.replace(/\.git$/, '')
|
||||
.replace(/\/$/, '');
|
||||
}
|
||||
return url;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function extractPackageFile(content, packageFile = null) {
|
||||
if (!content) return null;
|
||||
|
||||
const result = {
|
||||
packageFile,
|
||||
};
|
||||
const deps = [];
|
||||
|
||||
let offset = 0;
|
||||
let restStr = content;
|
||||
let state = null;
|
||||
let match = getMatch(restStr, state);
|
||||
|
||||
let lookupName = null;
|
||||
let currentValue = null;
|
||||
let fileReplacePosition = null;
|
||||
|
||||
function yieldDep() {
|
||||
const depName = getDepName(lookupName);
|
||||
if (depName && currentValue && fileReplacePosition) {
|
||||
const dep = {
|
||||
datasource: 'gitTags',
|
||||
depName,
|
||||
lookupName,
|
||||
currentValue,
|
||||
fileReplacePosition,
|
||||
};
|
||||
|
||||
if (isValid(currentValue)) {
|
||||
deps.push(dep);
|
||||
}
|
||||
}
|
||||
lookupName = null;
|
||||
currentValue = null;
|
||||
fileReplacePosition = null;
|
||||
}
|
||||
|
||||
while (match) {
|
||||
const { idx, len, label, substr } = match;
|
||||
offset += idx;
|
||||
// eslint-disable-next-line default-case
|
||||
switch (state) {
|
||||
case null:
|
||||
if (deps.length) break;
|
||||
if (label === DEPS) {
|
||||
state = 'dependencies';
|
||||
}
|
||||
break;
|
||||
case 'dependencies':
|
||||
if (label === COLON) {
|
||||
state = 'dependencies:';
|
||||
} else if (label !== SPACE) {
|
||||
state = null;
|
||||
}
|
||||
break;
|
||||
case 'dependencies:':
|
||||
if (label === BEGIN_SECTION) {
|
||||
state = 'dependencies: [';
|
||||
} else if (label !== SPACE) {
|
||||
state = null;
|
||||
}
|
||||
break;
|
||||
case 'dependencies: [':
|
||||
if (label === END_SECTION) {
|
||||
yieldDep();
|
||||
state = null;
|
||||
} else if (label === PACKAGE) {
|
||||
yieldDep();
|
||||
state = '.package(';
|
||||
}
|
||||
break;
|
||||
case '.package(':
|
||||
if (label === END_SECTION) {
|
||||
yieldDep();
|
||||
state = null;
|
||||
} else if (label === URL_KEY) {
|
||||
state = '.package(url';
|
||||
} else if (label === PACKAGE) {
|
||||
yieldDep();
|
||||
}
|
||||
break;
|
||||
case '.package(url':
|
||||
if (label === END_SECTION) {
|
||||
yieldDep();
|
||||
state = null;
|
||||
} else if (label === COLON) {
|
||||
state = '.package(url:';
|
||||
} else if (label === PACKAGE) {
|
||||
yieldDep();
|
||||
state = '.package(';
|
||||
}
|
||||
break;
|
||||
case '.package(url:':
|
||||
if (label === END_SECTION) {
|
||||
yieldDep();
|
||||
state = null;
|
||||
} else if (label === STRING_LITERAL) {
|
||||
lookupName = substr.replace(/^"/, '').replace(/"$/, '');
|
||||
state = '.package(url: [depName]';
|
||||
} else if (label === PACKAGE) {
|
||||
yieldDep();
|
||||
state = '.package(';
|
||||
}
|
||||
break;
|
||||
case '.package(url: [depName]':
|
||||
if (label === END_SECTION) {
|
||||
yieldDep();
|
||||
state = null;
|
||||
} else if (label === COMMA) {
|
||||
state = '.package(url: [depName],';
|
||||
} else if (label === PACKAGE) {
|
||||
yieldDep();
|
||||
state = '.package(';
|
||||
}
|
||||
break;
|
||||
case '.package(url: [depName],':
|
||||
if (label === END_SECTION) {
|
||||
yieldDep();
|
||||
state = null;
|
||||
} else if (label === FROM) {
|
||||
fileReplacePosition = offset;
|
||||
currentValue = substr;
|
||||
state = '.package(url: [depName], from';
|
||||
} else if (label === STRING_LITERAL) {
|
||||
fileReplacePosition = offset;
|
||||
currentValue = substr;
|
||||
state = '.package(url: [depName], [value]';
|
||||
} else if (label === RANGE_OP) {
|
||||
fileReplacePosition = offset;
|
||||
currentValue = substr;
|
||||
state = '.package(url: [depName], [rangeFrom][rangeOp]';
|
||||
} else if (label === EXACT_VERSION) {
|
||||
state = '.package(url: [depName], .exact(';
|
||||
} else if (label === PACKAGE) {
|
||||
yieldDep();
|
||||
state = '.package(';
|
||||
}
|
||||
break;
|
||||
case '.package(url: [depName], .exact(':
|
||||
if (label === END_SECTION) {
|
||||
yieldDep();
|
||||
state = null;
|
||||
} else if (label === STRING_LITERAL) {
|
||||
currentValue = substr.slice(1, substr.length - 1);
|
||||
fileReplacePosition = offset;
|
||||
yieldDep();
|
||||
} else if (label === PACKAGE) {
|
||||
yieldDep();
|
||||
state = '.package(';
|
||||
}
|
||||
break;
|
||||
case '.package(url: [depName], from':
|
||||
if (label === END_SECTION) {
|
||||
yieldDep();
|
||||
state = null;
|
||||
} else if (label === COLON) {
|
||||
currentValue += substr;
|
||||
state = '.package(url: [depName], from:';
|
||||
} else if (label === SPACE) {
|
||||
currentValue += substr;
|
||||
} else if (label === PACKAGE) {
|
||||
yieldDep();
|
||||
state = '.package(';
|
||||
}
|
||||
break;
|
||||
case '.package(url: [depName], from:':
|
||||
if (label === END_SECTION) {
|
||||
yieldDep();
|
||||
state = null;
|
||||
} else if (label === STRING_LITERAL) {
|
||||
currentValue += substr;
|
||||
yieldDep();
|
||||
state = 'dependencies: [';
|
||||
} else if (label === SPACE) {
|
||||
currentValue += substr;
|
||||
} else if (label === PACKAGE) {
|
||||
yieldDep();
|
||||
state = '.package(';
|
||||
}
|
||||
break;
|
||||
case '.package(url: [depName], [value]':
|
||||
if (label === END_SECTION) {
|
||||
yieldDep();
|
||||
state = null;
|
||||
} else if (label === RANGE_OP) {
|
||||
currentValue += substr;
|
||||
state = '.package(url: [depName], [rangeFrom][rangeOp]';
|
||||
} else if (label === SPACE) {
|
||||
currentValue += substr;
|
||||
} else if (label === PACKAGE) {
|
||||
yieldDep();
|
||||
state = '.package(';
|
||||
}
|
||||
break;
|
||||
case '.package(url: [depName], [rangeFrom][rangeOp]':
|
||||
if (label === END_SECTION) {
|
||||
yieldDep();
|
||||
state = null;
|
||||
} else if (label === STRING_LITERAL) {
|
||||
currentValue += substr;
|
||||
state = 'dependencies: [';
|
||||
} else if (label === SPACE) {
|
||||
currentValue += substr;
|
||||
} else if (label === PACKAGE) {
|
||||
yieldDep();
|
||||
state = '.package(';
|
||||
}
|
||||
break;
|
||||
}
|
||||
offset += len;
|
||||
restStr = restStr.slice(idx + len);
|
||||
match = getMatch(restStr, state);
|
||||
}
|
||||
return deps.length ? { ...result, deps } : null;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
extractPackageFile,
|
||||
};
|
7
lib/manager/swift/index.js
Normal file
7
lib/manager/swift/index.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
const { extractPackageFile } = require('./extract');
|
||||
const { updateDependency } = require('./update');
|
||||
|
||||
module.exports = {
|
||||
extractPackageFile,
|
||||
updateDependency,
|
||||
};
|
30
lib/manager/swift/update.js
Normal file
30
lib/manager/swift/update.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
const { isVersion } = require('../../versioning/swift');
|
||||
|
||||
const fromParam = /^\s*from\s*:\s*"([^"]+)"\s*$/;
|
||||
|
||||
function updateDependency(fileContent, upgrade) {
|
||||
const { currentValue, newValue, fileReplacePosition } = upgrade;
|
||||
const leftPart = fileContent.slice(0, fileReplacePosition);
|
||||
const rightPart = fileContent.slice(fileReplacePosition);
|
||||
const oldVal = isVersion(currentValue) ? `"${currentValue}"` : currentValue;
|
||||
let newVal;
|
||||
if (fromParam.test(oldVal)) {
|
||||
const [, version] = oldVal.match(fromParam);
|
||||
newVal = oldVal.replace(version, newValue);
|
||||
} else if (isVersion(newValue)) {
|
||||
newVal = `"${newValue}"`;
|
||||
} else {
|
||||
newVal = newValue;
|
||||
}
|
||||
if (rightPart.indexOf(oldVal) === 0) {
|
||||
return leftPart + rightPart.replace(oldVal, newVal);
|
||||
}
|
||||
if (rightPart.indexOf(newVal) === 0) {
|
||||
return fileContent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
updateDependency,
|
||||
};
|
48
lib/versioning/swift/index.js
Normal file
48
lib/versioning/swift/index.js
Normal file
|
@ -0,0 +1,48 @@
|
|||
const semver = require('semver');
|
||||
const stable = require('semver-stable');
|
||||
const { toSemverRange, getNewValue } = require('./range');
|
||||
|
||||
const { is: isStable } = stable;
|
||||
|
||||
const {
|
||||
compare: sortVersions,
|
||||
maxSatisfying,
|
||||
minSatisfying,
|
||||
major: getMajor,
|
||||
minor: getMinor,
|
||||
patch: getPatch,
|
||||
satisfies,
|
||||
valid,
|
||||
validRange,
|
||||
ltr,
|
||||
gt: isGreaterThan,
|
||||
eq: equals,
|
||||
} = semver;
|
||||
|
||||
const isValid = input => !!valid(input) || !!validRange(toSemverRange(input));
|
||||
const isVersion = input => !!valid(input);
|
||||
const maxSatisfyingVersion = (versions, range) =>
|
||||
maxSatisfying(versions, toSemverRange(range));
|
||||
const minSatisfyingVersion = (versions, range) =>
|
||||
minSatisfying(versions, toSemverRange(range));
|
||||
const isLessThanRange = (version, range) => ltr(version, toSemverRange(range));
|
||||
const matches = (version, range) => satisfies(version, toSemverRange(range));
|
||||
|
||||
module.exports = {
|
||||
equals,
|
||||
getMajor,
|
||||
getMinor,
|
||||
getNewValue,
|
||||
getPatch,
|
||||
isCompatible: isVersion,
|
||||
isGreaterThan,
|
||||
isLessThanRange,
|
||||
isSingleVersion: isVersion,
|
||||
isStable,
|
||||
isValid,
|
||||
isVersion,
|
||||
matches,
|
||||
maxSatisfyingVersion,
|
||||
minSatisfyingVersion,
|
||||
sortVersions,
|
||||
};
|
58
lib/versioning/swift/range.js
Normal file
58
lib/versioning/swift/range.js
Normal file
|
@ -0,0 +1,58 @@
|
|||
const semver = require('semver');
|
||||
|
||||
const fromParam = /^\s*from\s*:\s*"([^"]+)"\s*$/;
|
||||
const fromRange = /^\s*"([^"]+)"\s*\.\.\.\s*$/;
|
||||
const binaryRange = /^\s*"([^"]+)"\s*(\.\.[.<])\s*"([^"]+)"\s*$/;
|
||||
const toRange = /^\s*(\.\.[.<])\s*"([^"]+)"\s*$/;
|
||||
|
||||
function toSemverRange(range) {
|
||||
if (fromParam.test(range)) {
|
||||
const [, version] = range.match(fromParam);
|
||||
if (semver.valid(version)) {
|
||||
const nextMajor = `${semver.major(version) + 1}.0.0`;
|
||||
return `>=${version} <${nextMajor}`;
|
||||
}
|
||||
} else if (fromRange.test(range)) {
|
||||
const [, version] = range.match(fromRange);
|
||||
if (semver.valid(version)) {
|
||||
return `>=${version}`;
|
||||
}
|
||||
} else if (binaryRange.test(range)) {
|
||||
const [, fromVersion, op, toVersion] = range.match(binaryRange);
|
||||
if (semver.valid(fromVersion) && semver.valid(toVersion)) {
|
||||
return op === '..<'
|
||||
? `>=${fromVersion} <${toVersion}`
|
||||
: `>=${fromVersion} <=${toVersion}`;
|
||||
}
|
||||
} else if (toRange.test(range)) {
|
||||
const [, op, toVersion] = range.match(toRange);
|
||||
if (semver.valid(toVersion)) {
|
||||
return op === '..<' ? `<${toVersion}` : `<=${toVersion}`;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getNewValue(currentValue, rangeStrategy, fromVersion, toVersion) {
|
||||
if (fromParam.test(currentValue)) {
|
||||
return toVersion;
|
||||
}
|
||||
if (fromRange.test(currentValue)) {
|
||||
const [, version] = currentValue.match(fromRange);
|
||||
return currentValue.replace(version, toVersion);
|
||||
}
|
||||
if (binaryRange.test(currentValue)) {
|
||||
const [, , , version] = currentValue.match(binaryRange);
|
||||
return currentValue.replace(version, toVersion);
|
||||
}
|
||||
if (toRange.test(currentValue)) {
|
||||
const [, , version] = currentValue.match(toRange);
|
||||
return currentValue.replace(version, toVersion);
|
||||
}
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
toSemverRange,
|
||||
getNewValue,
|
||||
};
|
|
@ -290,7 +290,8 @@
|
|||
"pep440",
|
||||
"poetry",
|
||||
"ruby",
|
||||
"semver"
|
||||
"semver",
|
||||
"swift"
|
||||
],
|
||||
"default": "semver"
|
||||
},
|
||||
|
@ -1241,6 +1242,16 @@
|
|||
"description": "Options to suppress various types of warnings and other notifications",
|
||||
"type": "array",
|
||||
"default": ["deprecationWarningIssues"]
|
||||
},
|
||||
"swift": {
|
||||
"description": "Configuration for Package.swift files",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"fileMatch": ["(^|/)Package\\.swift"],
|
||||
"versionScheme": "swift",
|
||||
"rangeStrategy": "bump"
|
||||
},
|
||||
"$ref": "#"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ Array [
|
|||
"depName": "Configuration Error",
|
||||
"message": "packageRules:
|
||||
You have included an unsupported manager in a package rule. Your list: foo.
|
||||
Supported managers are: (ansible, bazel, buildkite, bundler, cargo, circleci, composer, deps-edn, docker-compose, dockerfile, github-actions, gitlabci, gomod, gradle, gradle-wrapper, kubernetes, leiningen, maven, meteor, npm, nuget, nvm, pip_requirements, pip_setup, pipenv, poetry, pub, sbt, terraform, travis, ruby-version, homebrew).",
|
||||
Supported managers are: (ansible, bazel, buildkite, bundler, cargo, circleci, composer, deps-edn, docker-compose, dockerfile, github-actions, gitlabci, gomod, gradle, gradle-wrapper, kubernetes, leiningen, maven, meteor, npm, nuget, nvm, pip_requirements, pip_setup, pipenv, poetry, pub, sbt, swift, terraform, travis, ruby-version, homebrew).",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
|
|
@ -3,21 +3,23 @@ const { getPkgReleases } = require('../../lib/datasource/git-tags');
|
|||
|
||||
jest.mock('isomorphic-git');
|
||||
|
||||
const lookupName = 'https://github.com/vapor/vapor.git';
|
||||
const lookupName = 'vapor';
|
||||
const registryUrls = ['https://github.com/vapor/vapor.git'];
|
||||
const registryUrlsAlt = ['https://github.com/vapor/vapor/'];
|
||||
|
||||
describe('datasource/git-tags', () => {
|
||||
beforeEach(() => global.renovateCache.rmAll());
|
||||
describe('getPkgReleases', () => {
|
||||
it('returns nil if response is wrong', async () => {
|
||||
getRemoteInfo.mockReturnValue(Promise.resolve(null));
|
||||
const versions = await getPkgReleases({ lookupName });
|
||||
const versions = await getPkgReleases({ lookupName, registryUrls });
|
||||
expect(versions).toEqual(null);
|
||||
});
|
||||
it('returns nil if remote call throws exception', async () => {
|
||||
getRemoteInfo.mockImplementation(() => {
|
||||
throw new Error();
|
||||
});
|
||||
const versions = await getPkgReleases({ lookupName });
|
||||
const versions = await getPkgReleases({ lookupName, registryUrls });
|
||||
expect(versions).toEqual(null);
|
||||
});
|
||||
it('returns versions filtered from tags', async () => {
|
||||
|
@ -32,7 +34,10 @@ describe('datasource/git-tags', () => {
|
|||
},
|
||||
})
|
||||
);
|
||||
const versions = await getPkgReleases({ lookupName });
|
||||
const versions = await getPkgReleases({
|
||||
lookupName,
|
||||
registryUrls: registryUrlsAlt,
|
||||
});
|
||||
const result = versions.releases.map(x => x.version).sort();
|
||||
expect(result).toEqual(['0.0.1', '0.0.2']);
|
||||
});
|
||||
|
|
134
test/manager/swift/__snapshots__/index.spec.js.snap
Normal file
134
test/manager/swift/__snapshots__/index.spec.js.snap
Normal file
|
@ -0,0 +1,134 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`lib/manager/swift extractPackageFile() parses multiple packages 1`] = `
|
||||
Object {
|
||||
"deps": Array [
|
||||
Object {
|
||||
"currentValue": "0.1.0",
|
||||
"datasource": "gitTags",
|
||||
"depName": "avito-tech/GraphiteClient",
|
||||
"fileReplacePosition": 1177,
|
||||
"lookupName": "https://github.com/avito-tech/GraphiteClient.git",
|
||||
},
|
||||
Object {
|
||||
"currentValue": "1.0.16",
|
||||
"datasource": "gitTags",
|
||||
"depName": "IBM-Swift/BlueSignals",
|
||||
"fileReplacePosition": 1268,
|
||||
"lookupName": "https://github.com/IBM-Swift/BlueSignals.git",
|
||||
},
|
||||
Object {
|
||||
"currentValue": "3.0.6",
|
||||
"datasource": "gitTags",
|
||||
"depName": "daltoniam/Starscream",
|
||||
"fileReplacePosition": 1439,
|
||||
"lookupName": "https://github.com/daltoniam/Starscream.git",
|
||||
},
|
||||
Object {
|
||||
"currentValue": "1.4.6",
|
||||
"datasource": "gitTags",
|
||||
"depName": "httpswift/swifter",
|
||||
"fileReplacePosition": 1523,
|
||||
"lookupName": "https://github.com/httpswift/swifter.git",
|
||||
},
|
||||
Object {
|
||||
"currentValue": "from : \\"0.9.6\\"",
|
||||
"datasource": "gitTags",
|
||||
"depName": "weichsel/ZIPFoundation",
|
||||
"fileReplacePosition": 1626,
|
||||
"lookupName": "https://github.com/weichsel/ZIPFoundation/",
|
||||
},
|
||||
],
|
||||
"packageFile": null,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`lib/manager/swift extractPackageFile() parses package descriptions 1`] = `
|
||||
Object {
|
||||
"deps": Array [
|
||||
Object {
|
||||
"currentValue": "from:\\"1.2.3\\"",
|
||||
"datasource": "gitTags",
|
||||
"depName": "vapor/vapor",
|
||||
"fileReplacePosition": 64,
|
||||
"lookupName": "https://github.com/vapor/vapor.git",
|
||||
},
|
||||
],
|
||||
"packageFile": null,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`lib/manager/swift extractPackageFile() parses package descriptions 2`] = `
|
||||
Object {
|
||||
"deps": Array [
|
||||
Object {
|
||||
"currentValue": "\\"1.2.3\\"...",
|
||||
"datasource": "gitTags",
|
||||
"depName": "vapor/vapor",
|
||||
"fileReplacePosition": 64,
|
||||
"lookupName": "https://github.com/vapor/vapor.git",
|
||||
},
|
||||
],
|
||||
"packageFile": null,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`lib/manager/swift extractPackageFile() parses package descriptions 3`] = `
|
||||
Object {
|
||||
"deps": Array [
|
||||
Object {
|
||||
"currentValue": "\\"1.2.3\\"...\\"1.2.4\\"",
|
||||
"datasource": "gitTags",
|
||||
"depName": "vapor/vapor",
|
||||
"fileReplacePosition": 64,
|
||||
"lookupName": "https://github.com/vapor/vapor.git",
|
||||
},
|
||||
],
|
||||
"packageFile": null,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`lib/manager/swift extractPackageFile() parses package descriptions 4`] = `
|
||||
Object {
|
||||
"deps": Array [
|
||||
Object {
|
||||
"currentValue": "\\"1.2.3\\"..<\\"1.2.4\\"",
|
||||
"datasource": "gitTags",
|
||||
"depName": "vapor/vapor",
|
||||
"fileReplacePosition": 64,
|
||||
"lookupName": "https://github.com/vapor/vapor.git",
|
||||
},
|
||||
],
|
||||
"packageFile": null,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`lib/manager/swift extractPackageFile() parses package descriptions 5`] = `
|
||||
Object {
|
||||
"deps": Array [
|
||||
Object {
|
||||
"currentValue": "...\\"1.2.3\\"",
|
||||
"datasource": "gitTags",
|
||||
"depName": "vapor/vapor",
|
||||
"fileReplacePosition": 64,
|
||||
"lookupName": "https://github.com/vapor/vapor.git",
|
||||
},
|
||||
],
|
||||
"packageFile": null,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`lib/manager/swift extractPackageFile() parses package descriptions 6`] = `
|
||||
Object {
|
||||
"deps": Array [
|
||||
Object {
|
||||
"currentValue": "..<\\"1.2.3\\"",
|
||||
"datasource": "gitTags",
|
||||
"depName": "vapor/vapor",
|
||||
"fileReplacePosition": 64,
|
||||
"lookupName": "https://github.com/vapor/vapor.git",
|
||||
},
|
||||
],
|
||||
"packageFile": null,
|
||||
}
|
||||
`;
|
1105
test/manager/swift/_fixtures/SamplePackage.swift
Normal file
1105
test/manager/swift/_fixtures/SamplePackage.swift
Normal file
File diff suppressed because it is too large
Load diff
203
test/manager/swift/index.spec.js
Normal file
203
test/manager/swift/index.spec.js
Normal file
|
@ -0,0 +1,203 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { extractPackageFile } = require('../../../lib/manager/swift/extract');
|
||||
const { updateDependency } = require('../../../lib/manager/swift/update');
|
||||
|
||||
const pkgContent = fs.readFileSync(
|
||||
path.resolve(__dirname, `./_fixtures/SamplePackage.swift`),
|
||||
'utf8'
|
||||
);
|
||||
|
||||
describe('lib/manager/swift', () => {
|
||||
describe('extractPackageFile()', () => {
|
||||
it('returns null for empty content', () => {
|
||||
expect(extractPackageFile(null)).toBeNull();
|
||||
expect(extractPackageFile(``)).toBeNull();
|
||||
expect(extractPackageFile(`dependencies:[]`)).toBeNull();
|
||||
expect(extractPackageFile(`dependencies:["foobar"]`)).toBeNull();
|
||||
});
|
||||
it('returns null for invalid content', () => {
|
||||
expect(extractPackageFile(`dependen`)).toBeNull();
|
||||
expect(extractPackageFile(`dependencies!: `)).toBeNull();
|
||||
expect(extractPackageFile(`dependencies :`)).toBeNull();
|
||||
expect(extractPackageFile(`dependencies...`)).toBeNull();
|
||||
expect(extractPackageFile(`dependencies:!`)).toBeNull();
|
||||
expect(extractPackageFile(`dependencies:[`)).toBeNull();
|
||||
expect(extractPackageFile(`dependencies:[...`)).toBeNull();
|
||||
expect(extractPackageFile(`dependencies:[]`)).toBeNull();
|
||||
expect(extractPackageFile(`dependencies:[.package`)).toBeNull();
|
||||
expect(extractPackageFile(`dependencies:[.package.package(`)).toBeNull();
|
||||
expect(extractPackageFile(`dependencies:[.package(asdf`)).toBeNull();
|
||||
expect(extractPackageFile(`dependencies:[.package]`)).toBeNull();
|
||||
expect(extractPackageFile(`dependencies:[.package(]`)).toBeNull();
|
||||
expect(extractPackageFile(`dependencies:[.package(.package(`)).toBeNull();
|
||||
expect(extractPackageFile(`dependencies:[.package(`)).toBeNull();
|
||||
expect(extractPackageFile(`dependencies:[.package(]`)).toBeNull();
|
||||
expect(extractPackageFile(`dependencies:[.package(url],`)).toBeNull();
|
||||
expect(
|
||||
extractPackageFile(`dependencies:[.package(url.package(]`)
|
||||
).toBeNull();
|
||||
expect(
|
||||
extractPackageFile(`dependencies:[.package(url:.package(`)
|
||||
).toBeNull();
|
||||
expect(extractPackageFile(`dependencies:[.package(url:]`)).toBeNull();
|
||||
expect(extractPackageFile(`dependencies:[.package(url:"fo`)).toBeNull();
|
||||
expect(extractPackageFile(`dependencies:[.package(url:"fo]`)).toBeNull();
|
||||
expect(
|
||||
extractPackageFile(
|
||||
`dependencies:[.package(url:"https://example.com/something.git"]`
|
||||
)
|
||||
).toBeNull();
|
||||
expect(
|
||||
extractPackageFile(
|
||||
`dependencies:[.package(url:"https://github.com/vapor/vapor.git"]`
|
||||
)
|
||||
).toBeNull();
|
||||
expect(
|
||||
extractPackageFile(
|
||||
`dependencies:[.package(url:"https://github.com/vapor/vapor.git".package(]`
|
||||
)
|
||||
).toBeNull();
|
||||
expect(
|
||||
extractPackageFile(
|
||||
`dependencies:[.package(url:"https://github.com/vapor/vapor.git", ]`
|
||||
)
|
||||
).toBeNull();
|
||||
expect(
|
||||
extractPackageFile(
|
||||
`dependencies:[.package(url:"https://github.com/vapor/vapor.git", .package(]`
|
||||
)
|
||||
).toBeNull();
|
||||
expect(
|
||||
extractPackageFile(
|
||||
`dependencies:[.package(url:"https://github.com/vapor/vapor.git", .exact(]`
|
||||
)
|
||||
).toBeNull();
|
||||
expect(
|
||||
extractPackageFile(
|
||||
`dependencies:[.package(url:"https://github.com/vapor/vapor.git", from]`
|
||||
)
|
||||
).toBeNull();
|
||||
expect(
|
||||
extractPackageFile(
|
||||
`dependencies:[.package(url:"https://github.com/vapor/vapor.git", from.package(`
|
||||
)
|
||||
).toBeNull();
|
||||
expect(
|
||||
extractPackageFile(
|
||||
`dependencies:[.package(url:"https://github.com/vapor/vapor.git", from:]`
|
||||
)
|
||||
).toBeNull();
|
||||
expect(
|
||||
extractPackageFile(
|
||||
`dependencies:[.package(url:"https://github.com/vapor/vapor.git", from:.package(`
|
||||
)
|
||||
).toBeNull();
|
||||
expect(
|
||||
extractPackageFile(
|
||||
`dependencies:[.package(url:"https://github.com/vapor/vapor.git","1.2.3")]`
|
||||
)
|
||||
).toBeNull();
|
||||
});
|
||||
it('parses package descriptions', () => {
|
||||
expect(
|
||||
extractPackageFile(
|
||||
`dependencies:[.package(url:"https://github.com/vapor/vapor.git",from:"1.2.3")]`
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
extractPackageFile(
|
||||
`dependencies:[.package(url:"https://github.com/vapor/vapor.git","1.2.3"...)]`
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
extractPackageFile(
|
||||
`dependencies:[.package(url:"https://github.com/vapor/vapor.git","1.2.3"..."1.2.4")]`
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
extractPackageFile(
|
||||
`dependencies:[.package(url:"https://github.com/vapor/vapor.git","1.2.3"..<"1.2.4")]`
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
extractPackageFile(
|
||||
`dependencies:[.package(url:"https://github.com/vapor/vapor.git",..."1.2.3")]`
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
extractPackageFile(
|
||||
`dependencies:[.package(url:"https://github.com/vapor/vapor.git",..<"1.2.3")]`
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
it('parses multiple packages', () => {
|
||||
expect(extractPackageFile(pkgContent)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
describe('updateDependency()', () => {
|
||||
it('updates successfully', () => {
|
||||
[
|
||||
[
|
||||
'dependencies:[.package(url:"https://github.com/vapor/vapor.git",.exact("1.2.3")]',
|
||||
'1.2.4',
|
||||
'dependencies:[.package(url:"https://github.com/vapor/vapor.git",.exact("1.2.4")]',
|
||||
],
|
||||
[
|
||||
'dependencies:[.package(url:"https://github.com/vapor/vapor.git", from: "1.2.3")]',
|
||||
'1.2.4',
|
||||
'dependencies:[.package(url:"https://github.com/vapor/vapor.git", from: "1.2.4")]',
|
||||
],
|
||||
[
|
||||
'dependencies:[.package(url:"https://github.com/vapor/vapor.git", "1.2.3"..."1.2.4")]',
|
||||
'"1.2.3"..."1.2.5"',
|
||||
'dependencies:[.package(url:"https://github.com/vapor/vapor.git", "1.2.3"..."1.2.5")]',
|
||||
],
|
||||
[
|
||||
'dependencies:[.package(url:"https://github.com/vapor/vapor.git", "1.2.3"..<"1.2.4")]',
|
||||
'"1.2.3"..<"1.2.5"',
|
||||
'dependencies:[.package(url:"https://github.com/vapor/vapor.git", "1.2.3"..<"1.2.5")]',
|
||||
],
|
||||
[
|
||||
'dependencies:[.package(url:"https://github.com/vapor/vapor.git", ..."1.2.4")]',
|
||||
'..."1.2.5"',
|
||||
'dependencies:[.package(url:"https://github.com/vapor/vapor.git", ..."1.2.5")]',
|
||||
],
|
||||
[
|
||||
'dependencies:[.package(url:"https://github.com/vapor/vapor.git", ..<"1.2.4")]',
|
||||
'..<"1.2.5"',
|
||||
'dependencies:[.package(url:"https://github.com/vapor/vapor.git", ..<"1.2.5")]',
|
||||
],
|
||||
].forEach(([content, newValue, result]) => {
|
||||
const { deps } = extractPackageFile(content);
|
||||
const [dep] = deps;
|
||||
const upgrade = { ...dep, newValue };
|
||||
const updated = updateDependency(content, upgrade);
|
||||
expect(updated).toEqual(result);
|
||||
});
|
||||
});
|
||||
it('returns content if already updated', () => {
|
||||
const content =
|
||||
'dependencies:[.package(url:"https://github.com/vapor/vapor.git",.exact("1.2.3")]';
|
||||
const currentValue = '1.2.3';
|
||||
const newValue = '1.2.4';
|
||||
const { deps } = extractPackageFile(content);
|
||||
const [dep] = deps;
|
||||
const upgrade = { ...dep, newValue };
|
||||
const replaced = content.replace(currentValue, newValue);
|
||||
const updated = updateDependency(replaced, upgrade);
|
||||
expect(updated).toBe(replaced);
|
||||
});
|
||||
it('returns null if content is different', () => {
|
||||
const content =
|
||||
'dependencies:[.package(url:"https://github.com/vapor/vapor.git",.exact("1.2.3")]';
|
||||
const currentValue = '1.2.3';
|
||||
const newValue = '1.2.4';
|
||||
const { deps } = extractPackageFile(content);
|
||||
const [dep] = deps;
|
||||
const upgrade = { ...dep, newValue };
|
||||
const replaced = content.replace(currentValue, '1.2.5');
|
||||
expect(updateDependency(replaced, upgrade)).toBe(null);
|
||||
});
|
||||
});
|
||||
});
|
85
test/versioning/swift.spec.js
Normal file
85
test/versioning/swift.spec.js
Normal file
|
@ -0,0 +1,85 @@
|
|||
const {
|
||||
getNewValue,
|
||||
isValid,
|
||||
minSatisfyingVersion,
|
||||
maxSatisfyingVersion,
|
||||
isLessThanRange,
|
||||
matches,
|
||||
} = require('../../lib/versioning/swift');
|
||||
|
||||
describe('isValid(input)', () => {
|
||||
it('understands Swift version ranges', () => {
|
||||
expect(isValid('from: "1.2.3"')).toBe(true);
|
||||
expect(isValid('from : "1.2.3"')).toBe(true);
|
||||
expect(isValid('from:"1.2.3"')).toBe(true);
|
||||
expect(isValid(' from:"1.2.3" ')).toBe(true);
|
||||
expect(isValid(' from : "1.2.3" ')).toBe(true);
|
||||
|
||||
expect(isValid('"1.2.3"..."1.2.4"')).toBe(true);
|
||||
expect(isValid(' "1.2.3" ... "1.2.4" ')).toBe(true);
|
||||
|
||||
expect(isValid('"1.2.3"...')).toBe(true);
|
||||
expect(isValid(' "1.2.3" ... ')).toBe(true);
|
||||
|
||||
expect(isValid('..."1.2.4"')).toBe(true);
|
||||
expect(isValid(' ... "1.2.4" ')).toBe(true);
|
||||
|
||||
expect(isValid('"1.2.3"..<"1.2.4"')).toBe(true);
|
||||
expect(isValid(' "1.2.3" ..< "1.2.4" ')).toBe(true);
|
||||
|
||||
expect(isValid('..<"1.2.4"')).toBe(true);
|
||||
expect(isValid(' ..< "1.2.4" ')).toBe(true);
|
||||
});
|
||||
it('should return null for irregular versions', () => {
|
||||
expect(isValid('17.04.0')).toBeFalsy();
|
||||
});
|
||||
it('should support simple semver', () => {
|
||||
expect(isValid('1.2.3')).toBe(true);
|
||||
});
|
||||
it('should support semver with dash', () => {
|
||||
expect(isValid('1.2.3-foo')).toBe(true);
|
||||
});
|
||||
it('should reject semver without dash', () => {
|
||||
expect(isValid('1.2.3foo')).toBeFalsy();
|
||||
});
|
||||
it('should support ranges', () => {
|
||||
expect(isValid('~1.2.3')).toBeFalsy();
|
||||
expect(isValid('^1.2.3')).toBeFalsy();
|
||||
expect(isValid('from: "1.2.3"')).toBe(true);
|
||||
expect(isValid('"1.2.3"..."1.2.4"')).toBe(true);
|
||||
expect(isValid('"1.2.3"..."1.2.4"')).toBe(true);
|
||||
expect(isValid('"1.2.3"..<"1.2.4"')).toBe(true);
|
||||
expect(isValid('"1.2.3"..<"1.2.4"')).toBe(true);
|
||||
expect(isValid('..."1.2.3"')).toBe(true);
|
||||
expect(isValid('..<"1.2.4"')).toBe(true);
|
||||
expect(
|
||||
minSatisfyingVersion(['1.2.3', '1.2.4', '1.2.5'], '..<"1.2.4"')
|
||||
).toBe('1.2.3');
|
||||
expect(
|
||||
maxSatisfyingVersion(['1.2.3', '1.2.4', '1.2.5'], '..<"1.2.4"')
|
||||
).toBe('1.2.3');
|
||||
expect(
|
||||
maxSatisfyingVersion(['1.2.3', '1.2.4', '1.2.5'], '..."1.2.4"')
|
||||
).toBe('1.2.4');
|
||||
expect(isLessThanRange('1.2.3', '..."1.2.4"')).toBe(false);
|
||||
expect(isLessThanRange('1.2.3', '"1.2.4"...')).toBe(true);
|
||||
expect(matches('1.2.4', '..."1.2.4"')).toBe(true);
|
||||
expect(matches('1.2.4', '..."1.2.3"')).toBe(false);
|
||||
});
|
||||
});
|
||||
describe('getNewValue()', () => {
|
||||
it('supports range update', () => {
|
||||
[
|
||||
['1.2.3', 'auto', '1.2.3', '1.2.4', '1.2.3'],
|
||||
['from: "1.2.3"', 'auto', '1.2.3', '1.2.4', '1.2.4'],
|
||||
['"1.2.3"...', 'auto', '1.2.3', '1.2.4', '"1.2.4"...'],
|
||||
['"1.2.3"..."1.2.4"', 'auto', '1.2.3', '1.2.5', '"1.2.3"..."1.2.5"'],
|
||||
['"1.2.3"..<"1.2.4"', 'auto', '1.2.3', '1.2.5', '"1.2.3"..<"1.2.5"'],
|
||||
['..."1.2.4"', 'auto', '1.2.3', '1.2.5', '..."1.2.5"'],
|
||||
['..<"1.2.4"', 'auto', '1.2.3', '1.2.5', '..<"1.2.5"'],
|
||||
].forEach(([range, strategy, fromVersion, toVersion, result]) => {
|
||||
const newValue = getNewValue(range, strategy, fromVersion, toVersion);
|
||||
expect(newValue).toEqual(result);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -92,6 +92,9 @@ Object {
|
|||
"sbt": Array [
|
||||
Object {},
|
||||
],
|
||||
"swift": Array [
|
||||
Object {},
|
||||
],
|
||||
"terraform": Array [
|
||||
Object {},
|
||||
],
|
||||
|
|
|
@ -1080,6 +1080,22 @@ Use this field to suppress various types of warnings and other notifications fro
|
|||
|
||||
The above config will suppress the comment which is added to a PR whenever you close a PR unmerged.
|
||||
|
||||
## swift
|
||||
|
||||
Anything other than `.exact(<...>)` will be treated as range with respect to Swift specific.
|
||||
Because of this, some PR descriptions will look like `from: <...> => <...>`.
|
||||
|
||||
Examples:
|
||||
|
||||
```swift
|
||||
package(name: "<...>", from: "1.2.3") // => from: "2.0.0"
|
||||
package(name: "<...>", "1.2.3"...) // => "2.0.0"...
|
||||
package(name: "<...>", "1.2.3"..."1.3.0") // => "1.2.3"..."2.0.0"
|
||||
package(name: "<...>", "1.2.3"..<"1.3.0") // => "1.2.3"..<"2.0.0"
|
||||
package(name: "<...>", ..."1.2.3") // => ..."2.0.0"
|
||||
package(name: "<...>", ..<"1.2.3") // => ..<"2.0.0"
|
||||
```
|
||||
|
||||
## terraform
|
||||
|
||||
Currently Terraform support is limited to Terraform registry sources and github sources that include semver refs, e.g. like `github.com/hashicorp/example?ref=v1.0.0`.
|
||||
|
|
Loading…
Reference in a new issue