mirror of
https://github.com/shivammathur/setup-php.git
synced 2026-10-15 20:06:58 +00:00
Require full tool versions for checksum pinning
This commit is contained in:
4 files changed
+70
-20
No files matched your search
@@ -272,7 +272,7 @@ These tools can be set up globally using the `tools` input. It accepts a string
|
||||
tools: composer:2.9.8@sha256:59b2c50e10cafa0d8efc19ede9a326d782f096c674a26baf98cf042ce23de890
|
||||
```
|
||||
|
||||
Checksum verification is supported for tools which are downloaded as phar archives. It is not supported for tools set up using `composer` packages or custom package scripts, specifying a checksum for these tools will result in an error. For checksum pinning to be effective, pin the tool to an exact version, as mutable versions like `latest` or `major.minor` can resolve to a different release with a different checksum.
|
||||
Checksum verification is supported only for tools downloaded as phar archives with a full version, such as `tool:1.2.3` or `tool:1.2.3-beta1`. Specifying a checksum with an omitted version, a variable tag such as `latest`, `stable`, `preview` or `snapshot`, or a partial version such as `2`, `2.x`, `2.9` or `2.9.x` results in an error. These versions can resolve to different releases with different checksums. Checksum verification is not supported for tools set up using `composer` packages or custom package scripts; specifying a checksum for these tools also results in an error.
|
||||
|
||||
- The latest stable version of `composer` is set up by default. You can set up the required `composer` version by specifying the major version `v1` or `v2`, or the version in `major.minor` or `semver` format. Additionally, for composer `snapshot` and `preview` can also be specified to set up the respective releases.
|
||||
|
||||
|
||||
+54
-15
@@ -222,20 +222,21 @@ describe('Tools tests', () => {
|
||||
});
|
||||
|
||||
it.each`
|
||||
release | expected_release | checksum | error
|
||||
${'tool:1.2.3'} | ${'tool:1.2.3'} | ${undefined} | ${undefined}
|
||||
${'tool:1.2.3@sha256:' + 'a'.repeat(64)} | ${'tool:1.2.3'} | ${'sha256:' + 'a'.repeat(64)} | ${undefined}
|
||||
${'tool:1.2.3@sha256:' + 'A'.repeat(64)} | ${'tool:1.2.3'} | ${'sha256:' + 'a'.repeat(64)} | ${undefined}
|
||||
${'tool:1.2.3@SHA256:' + 'a'.repeat(64)} | ${'tool:1.2.3'} | ${'sha256:' + 'a'.repeat(64)} | ${undefined}
|
||||
${'tool:1.2.3@sha512:' + 'b'.repeat(128)} | ${'tool:1.2.3'} | ${'sha512:' + 'b'.repeat(128)} | ${undefined}
|
||||
${'composer:2.9.8@sha256:' + 'c'.repeat(64)} | ${'composer:2.9.8'} | ${'sha256:' + 'c'.repeat(64)} | ${undefined}
|
||||
${'tool:1.2.3@sha256:xyz'} | ${'tool:1.2.3'} | ${undefined} | ${'Invalid sha256 checksum, expected 64 hexadecimal characters'}
|
||||
${'tool:1.2.3@sha256:' + 'a'.repeat(63)} | ${'tool:1.2.3'} | ${undefined} | ${'Invalid sha256 checksum, expected 64 hexadecimal characters'}
|
||||
${'tool:1.2.3@sha512:' + 'b'.repeat(64)} | ${'tool:1.2.3'} | ${undefined} | ${'Invalid sha512 checksum, expected 128 hexadecimal characters'}
|
||||
${'tool:1.2.3@sha384:' + 'b'.repeat(96)} | ${'tool:1.2.3'} | ${undefined} | ${'Unsupported checksum algorithm sha384, expected sha256 or sha512'}
|
||||
${'tool:1.2.3@md5:' + 'b'.repeat(32)} | ${'tool:1.2.3'} | ${undefined} | ${'Unsupported checksum algorithm md5, expected sha256 or sha512'}
|
||||
${'tool:1.2.3@sha256' + 'a'.repeat(64)} | ${'tool:1.2.3'} | ${undefined} | ${'Invalid checksum syntax, expected @sha256:<hash> or @sha512:<hash>'}
|
||||
${'tool:1.0@dev'} | ${'tool:1.0@dev'} | ${undefined} | ${undefined}
|
||||
release | expected_release | checksum | error
|
||||
${'tool:1.2.3'} | ${'tool:1.2.3'} | ${undefined} | ${undefined}
|
||||
${'tool:1.2.3@sha256:' + 'a'.repeat(64)} | ${'tool:1.2.3'} | ${'sha256:' + 'a'.repeat(64)} | ${undefined}
|
||||
${'tool:1.2.3@sha256:' + 'A'.repeat(64)} | ${'tool:1.2.3'} | ${'sha256:' + 'a'.repeat(64)} | ${undefined}
|
||||
${'tool:1.2.3@SHA256:' + 'a'.repeat(64)} | ${'tool:1.2.3'} | ${'sha256:' + 'a'.repeat(64)} | ${undefined}
|
||||
${'tool:1.2.3@sha512:' + 'b'.repeat(128)} | ${'tool:1.2.3'} | ${'sha512:' + 'b'.repeat(128)} | ${undefined}
|
||||
${'tool:v1.2.3-beta.1+build.2@sha256:' + 'a'.repeat(64)} | ${'tool:v1.2.3-beta.1+build.2'} | ${'sha256:' + 'a'.repeat(64)} | ${undefined}
|
||||
${'composer:2.9.8@sha256:' + 'c'.repeat(64)} | ${'composer:2.9.8'} | ${'sha256:' + 'c'.repeat(64)} | ${undefined}
|
||||
${'tool:1.2.3@sha256:xyz'} | ${'tool:1.2.3'} | ${undefined} | ${'Invalid sha256 checksum, expected 64 hexadecimal characters'}
|
||||
${'tool:1.2.3@sha256:' + 'a'.repeat(63)} | ${'tool:1.2.3'} | ${undefined} | ${'Invalid sha256 checksum, expected 64 hexadecimal characters'}
|
||||
${'tool:1.2.3@sha512:' + 'b'.repeat(64)} | ${'tool:1.2.3'} | ${undefined} | ${'Invalid sha512 checksum, expected 128 hexadecimal characters'}
|
||||
${'tool:1.2.3@sha384:' + 'b'.repeat(96)} | ${'tool:1.2.3'} | ${undefined} | ${'Unsupported checksum algorithm sha384, expected sha256 or sha512'}
|
||||
${'tool:1.2.3@md5:' + 'b'.repeat(32)} | ${'tool:1.2.3'} | ${undefined} | ${'Unsupported checksum algorithm md5, expected sha256 or sha512'}
|
||||
${'tool:1.2.3@sha256' + 'a'.repeat(64)} | ${'tool:1.2.3'} | ${undefined} | ${'Invalid checksum syntax, expected @sha256:<hash> or @sha512:<hash>'}
|
||||
${'tool:1.0@dev'} | ${'tool:1.0@dev'} | ${undefined} | ${undefined}
|
||||
`(
|
||||
'checking extractChecksum: $release',
|
||||
({release, expected_release, checksum, error}) => {
|
||||
@@ -836,7 +837,7 @@ describe('Tools tests', () => {
|
||||
${'cs2pr:1.2.3@sha256:' + 'd'.repeat(64)} | ${'linux'} | ${'add_tool https://github.com/staabm/annotate-pull-request-from-checkstyle/releases/download/1.2.3/cs2pr cs2pr "-V" sha256:' + 'd'.repeat(64)}
|
||||
${'phive:0.15.3@sha512:' + 'c'.repeat(128)} | ${'darwin'} | ${'add_tool https://github.com/phar-io/phive/releases/download/0.15.3/phive-0.15.3.phar phive "status" sha512:' + 'c'.repeat(128)}
|
||||
${'phinx:1.2.3@sha256:' + 'a'.repeat(64)} | ${'linux'} | ${'add_log "$cross" "phinx" "Checksum verification is not supported for phinx"'}
|
||||
${'pecl@sha256:' + 'a'.repeat(64)} | ${'linux'} | ${'add_log "$cross" "pecl" "Checksum verification is not supported for pecl"'}
|
||||
${'pecl:1.2.3@sha256:' + 'a'.repeat(64)} | ${'linux'} | ${'add_log "$cross" "pecl" "Checksum verification is not supported for pecl"'}
|
||||
${'phpunit:9.5.0@sha256:invalid'} | ${'linux'} | ${'add_log "$cross" "phpunit" "Invalid sha256 checksum, expected 64 hexadecimal characters"'}
|
||||
${'composer:2.9.8@SHA256:' + 'b'.repeat(64)} | ${'linux'} | ${'composer 2.9.8 sha256:' + 'b'.repeat(64)}
|
||||
${'composer:2.9.8@sha384:' + 'b'.repeat(96)} | ${'linux'} | ${'add_log "$cross" "composer" "Unsupported checksum algorithm sha384, expected sha256 or sha512"'}
|
||||
@@ -849,6 +850,44 @@ describe('Tools tests', () => {
|
||||
}
|
||||
);
|
||||
|
||||
describe.each(['linux', 'darwin', 'win32'])(
|
||||
'Checksum version requirements on %s',
|
||||
os => {
|
||||
it.each([
|
||||
'',
|
||||
':latest',
|
||||
':stable',
|
||||
':preview',
|
||||
':snapshot',
|
||||
':2',
|
||||
':2.x',
|
||||
':2.9',
|
||||
':2.9.x',
|
||||
':^2.9.8'
|
||||
])(
|
||||
'rejects a checksum on a non-full version %s without an unpinned install',
|
||||
async version => {
|
||||
for (const tool of ['composer', 'phpunit']) {
|
||||
const release = `${tool}${version}@sha256:${'a'.repeat(64)}`;
|
||||
const data = await tools.getData(release, '8.4', os);
|
||||
expect(data.error).toBe(
|
||||
'Checksum pinning requires a full version, for example tool:1.2.3'
|
||||
);
|
||||
expect(data.url).toBe('');
|
||||
const script = await tools.addTools(release, '8.4', os);
|
||||
expect(script).toContain(data.error);
|
||||
const installations = script
|
||||
.split('\n')
|
||||
.filter(line => /^add[-_]tool /i.test(line));
|
||||
expect(
|
||||
installations.some(line => new RegExp(` ${tool}( |$)`).test(line))
|
||||
).toBe(false);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
it.each`
|
||||
type | tool_function | supported
|
||||
${'phar'} | ${undefined} | ${true}
|
||||
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
(()=>{var e={472:function(e,t,r){"use strict";var n=this&&this.__createBinding||(Object.create?function(e,t,r,n){if(n===undefined)n=r;var s=Object.getOwnPropertyDescriptor(t,r);if(!s||("get"in s?!t.__esModule:s.writable||s.configurable)){s={enumerable:true,get:function(){return t[r]}}}Object.defineProperty(e,n,s)}:function(e,t,r,n){if(n===undefined)n=r;e[n]=t[r]});var s=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var i=this&&this.__importStar||function(){var ownKeys=function(e){ownKeys=Object.getOwnPropertyNames||function(e){var t=[];for(var r in e)if(Object.prototype.hasOwnProperty.call(e,r))t[t.length]=r;return t};return ownKeys(e)};return function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var r=ownKeys(e),i=0;i<r.length;i++)if(r[i]!=="default")n(t,e,r[i]);s(t,e);return t}}();Object.defineProperty(t,"__esModule",{value:true});t.addINIValuesUnix=addINIValuesUnix;t.addINIValuesWindows=addINIValuesWindows;t.addINIValues=addINIValues;const o=i(r(277));async function addINIValuesUnix(e){const t=await o.CSVArray(e);let r="";await o.asyncForEach(t,async function(e){r+="\n"+await o.addLog("$tick",e,"Added to php.ini","linux")});return'echo "'+t.map(e=>o.escapeForShell(e,"linux")).join("\n")+'" | sudo tee -a "${pecl_file:-${ini_file[@]}}" >/dev/null 2>&1'+r}async function addINIValuesWindows(e){const t=await o.CSVArray(e);let r="\n";await o.asyncForEach(t,async function(e){r+=await o.addLog("$tick",e,"Added to php.ini","win32")+"\n"});return'Add-Content "$php_dir\\php.ini" "'+t.map(e=>o.escapeForShell(e,"win32")).join("\n")+'"'+r}async function addINIValues(e,t,r=false){let n="\n";switch(r){case true:n+=await o.stepLog("Add php.ini values",t)+await o.suppressOutput(t)+"\n";break;case false:default:n+=await o.stepLog("Add php.ini values",t)+"\n";break}switch(t){case"win32":return n+await addINIValuesWindows(e);case"darwin":case"linux":return n+await addINIValuesUnix(e);default:return await o.log("Platform "+t+" is not supported",t,"error")}}},469:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.issueCommand=issueCommand;t.error=error;t.setFailed=setFailed;t.getInput=getInput;const n=r(857);function toCommandValue(e){if(e instanceof Error){return e.toString()}return e}function escapeData(e){return toCommandValue(e).replace(/%/g,"%25").replace(/\r/g,"%0D").replace(/\n/g,"%0A")}function escapeProperty(e){return e.replace(/%/g,"%25").replace(/\r/g,"%0D").replace(/\n/g,"%0A").replace(/:/g,"%3A").replace(/,/g,"%2C")}function issueCommand(e,t,r){let s=`::${e}`;if(t&&Object.keys(t).length>0){s+=" ";const e=Object.entries(t).filter(([,e])=>e).map(([e,t])=>`${e}=${escapeProperty(t)}`).join(",");s+=e}s+=`::${escapeData(r)}`;process.stdout.write(s+n.EOL)}function error(e){issueCommand("error",{},e)}function setFailed(e){process.exitCode=1;error(e)}function getInput(e,t=false){const r=process.env[`INPUT_${e.replace(/ /g,"_").toUpperCase()}`]||"";if(t&&!r){throw new Error(`Input required and not supplied: ${e}`)}return r.trim()}},524:function(e,t,r){"use strict";var n=this&&this.__createBinding||(Object.create?function(e,t,r,n){if(n===undefined)n=r;var s=Object.getOwnPropertyDescriptor(t,r);if(!s||("get"in s?!t.__esModule:s.writable||s.configurable)){s={enumerable:true,get:function(){return t[r]}}}Object.defineProperty(e,n,s)}:function(e,t,r,n){if(n===undefined)n=r;e[n]=t[r]});var s=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var i=this&&this.__importStar||function(){var ownKeys=function(e){ownKeys=Object.getOwnPropertyNames||function(e){var t=[];for(var r in e)if(Object.prototype.hasOwnProperty.call(e,r))t[t.length]=r;return t};return ownKeys(e)};return function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var r=ownKeys(e),i=0;i<r.length;i++)if(r[i]!=="default")n(t,e,r[i]);s(t,e);return t}}();Object.defineProperty(t,"__esModule",{value:true});t.checkXdebugError=checkXdebugError;t.addCoverageXdebug=addCoverageXdebug;t.addCoveragePCOV=addCoveragePCOV;t.disableCoverage=disableCoverage;t.addCoverage=addCoverage;const o=i(r(277));const a=i(r(252));const c=i(r(472));async function checkXdebugError(e,t){if(/^5\.[3-6]$|^7\.[0-1]$/.test(t)&&e=="xdebug3"||/^8\.[0-9]$/.test(t)&&e=="xdebug2"){return e+" is not supported on PHP "+t}return""}async function addCoverageXdebug(e,t,r,n){let s="\n";const i=await checkXdebugError(e,t);if(!i){s+=await a.addExtension(":pcov:false",t,r,true)+n;e=e=="xdebug3"?"xdebug":e;s+=await a.addExtension(e,t,r,true)+n;s+=await o.setVariable("xdebug_version","php -r \"echo phpversion('xdebug');\"",r);s+=await o.getCommand(r,"extension_log")+'xdebug "Xdebug $xdebug_version enabled as coverage driver"'}else{s+=await o.addLog("$cross",e,i,r)}return s}async function addCoveragePCOV(e,t,r){let n="\n";switch(true){default:n+=await a.addExtension(":xdebug:Line truncated
|
||||
(()=>{var e={472:function(e,t,r){"use strict";var n=this&&this.__createBinding||(Object.create?function(e,t,r,n){if(n===undefined)n=r;var s=Object.getOwnPropertyDescriptor(t,r);if(!s||("get"in s?!t.__esModule:s.writable||s.configurable)){s={enumerable:true,get:function(){return t[r]}}}Object.defineProperty(e,n,s)}:function(e,t,r,n){if(n===undefined)n=r;e[n]=t[r]});var s=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var i=this&&this.__importStar||function(){var ownKeys=function(e){ownKeys=Object.getOwnPropertyNames||function(e){var t=[];for(var r in e)if(Object.prototype.hasOwnProperty.call(e,r))t[t.length]=r;return t};return ownKeys(e)};return function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var r=ownKeys(e),i=0;i<r.length;i++)if(r[i]!=="default")n(t,e,r[i]);s(t,e);return t}}();Object.defineProperty(t,"__esModule",{value:true});t.addINIValuesUnix=addINIValuesUnix;t.addINIValuesWindows=addINIValuesWindows;t.addINIValues=addINIValues;const o=i(r(277));async function addINIValuesUnix(e){const t=await o.CSVArray(e);let r="";await o.asyncForEach(t,async function(e){r+="\n"+await o.addLog("$tick",e,"Added to php.ini","linux")});return'echo "'+t.map(e=>o.escapeForShell(e,"linux")).join("\n")+'" | sudo tee -a "${pecl_file:-${ini_file[@]}}" >/dev/null 2>&1'+r}async function addINIValuesWindows(e){const t=await o.CSVArray(e);let r="\n";await o.asyncForEach(t,async function(e){r+=await o.addLog("$tick",e,"Added to php.ini","win32")+"\n"});return'Add-Content "$php_dir\\php.ini" "'+t.map(e=>o.escapeForShell(e,"win32")).join("\n")+'"'+r}async function addINIValues(e,t,r=false){let n="\n";switch(r){case true:n+=await o.stepLog("Add php.ini values",t)+await o.suppressOutput(t)+"\n";break;case false:default:n+=await o.stepLog("Add php.ini values",t)+"\n";break}switch(t){case"win32":return n+await addINIValuesWindows(e);case"darwin":case"linux":return n+await addINIValuesUnix(e);default:return await o.log("Platform "+t+" is not supported",t,"error")}}},469:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.issueCommand=issueCommand;t.error=error;t.setFailed=setFailed;t.getInput=getInput;const n=r(857);function toCommandValue(e){if(e instanceof Error){return e.toString()}return e}function escapeData(e){return toCommandValue(e).replace(/%/g,"%25").replace(/\r/g,"%0D").replace(/\n/g,"%0A")}function escapeProperty(e){return e.replace(/%/g,"%25").replace(/\r/g,"%0D").replace(/\n/g,"%0A").replace(/:/g,"%3A").replace(/,/g,"%2C")}function issueCommand(e,t,r){let s=`::${e}`;if(t&&Object.keys(t).length>0){s+=" ";const e=Object.entries(t).filter(([,e])=>e).map(([e,t])=>`${e}=${escapeProperty(t)}`).join(",");s+=e}s+=`::${escapeData(r)}`;process.stdout.write(s+n.EOL)}function error(e){issueCommand("error",{},e)}function setFailed(e){process.exitCode=1;error(e)}function getInput(e,t=false){const r=process.env[`INPUT_${e.replace(/ /g,"_").toUpperCase()}`]||"";if(t&&!r){throw new Error(`Input required and not supplied: ${e}`)}return r.trim()}},524:function(e,t,r){"use strict";var n=this&&this.__createBinding||(Object.create?function(e,t,r,n){if(n===undefined)n=r;var s=Object.getOwnPropertyDescriptor(t,r);if(!s||("get"in s?!t.__esModule:s.writable||s.configurable)){s={enumerable:true,get:function(){return t[r]}}}Object.defineProperty(e,n,s)}:function(e,t,r,n){if(n===undefined)n=r;e[n]=t[r]});var s=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var i=this&&this.__importStar||function(){var ownKeys=function(e){ownKeys=Object.getOwnPropertyNames||function(e){var t=[];for(var r in e)if(Object.prototype.hasOwnProperty.call(e,r))t[t.length]=r;return t};return ownKeys(e)};return function(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var r=ownKeys(e),i=0;i<r.length;i++)if(r[i]!=="default")n(t,e,r[i]);s(t,e);return t}}();Object.defineProperty(t,"__esModule",{value:true});t.checkXdebugError=checkXdebugError;t.addCoverageXdebug=addCoverageXdebug;t.addCoveragePCOV=addCoveragePCOV;t.disableCoverage=disableCoverage;t.addCoverage=addCoverage;const o=i(r(277));const a=i(r(252));const c=i(r(472));async function checkXdebugError(e,t){if(/^5\.[3-6]$|^7\.[0-1]$/.test(t)&&e=="xdebug3"||/^8\.[0-9]$/.test(t)&&e=="xdebug2"){return e+" is not supported on PHP "+t}return""}async function addCoverageXdebug(e,t,r,n){let s="\n";const i=await checkXdebugError(e,t);if(!i){s+=await a.addExtension(":pcov:false",t,r,true)+n;e=e=="xdebug3"?"xdebug":e;s+=await a.addExtension(e,t,r,true)+n;s+=await o.setVariable("xdebug_version","php -r \"echo phpversion('xdebug');\"",r);s+=await o.getCommand(r,"extension_log")+'xdebug "Xdebug $xdebug_version enabled as coverage driver"'}else{s+=await o.addLog("$cross",e,i,r)}return s}async function addCoveragePCOV(e,t,r){let n="\n";switch(true){default:n+=await a.addExtension(":xdebug:Line truncated
|
||||
+14
-3
@@ -118,6 +118,12 @@ export function extractChecksum(release: string): {
|
||||
error: `Invalid ${algo} checksum, expected ${hash_length} hexadecimal characters`
|
||||
};
|
||||
}
|
||||
if (!/^[^:]+:v?\d+\.\d+\.\d+(?:-[\w.-]+)?(?:\+[\w.-]+)?$/.test(release)) {
|
||||
return {
|
||||
release,
|
||||
error: 'Checksum pinning requires a full version, for example tool:1.2.3'
|
||||
};
|
||||
}
|
||||
return {release, checksum: `${algo}:${hash}`};
|
||||
}
|
||||
|
||||
@@ -308,9 +314,13 @@ export async function filterList(tools_list: string[]): Promise<string[]> {
|
||||
const regex_any = /^composer($|:.*)/;
|
||||
const regex_valid =
|
||||
/^composer:?($|preview$|snapshot$|v?\d+(\.\d+)?$|v?\d+\.\d+\.\d+[\w-]*$)/;
|
||||
const matches: string[] = tools_list.filter(tool =>
|
||||
regex_valid.test(extractChecksum(tool).release)
|
||||
);
|
||||
const matches: string[] = tools_list.filter(tool => {
|
||||
const parsed = extractChecksum(tool);
|
||||
return (
|
||||
regex_valid.test(parsed.release) ||
|
||||
(regex_any.test(parsed.release) && parsed.error !== undefined)
|
||||
);
|
||||
});
|
||||
let composer = 'composer';
|
||||
tools_list = tools_list.filter(
|
||||
tool => !regex_any.test(extractChecksum(tool).release)
|
||||
@@ -721,6 +731,7 @@ export async function getData(
|
||||
data.checksum = checksum_data.checksum;
|
||||
data.error = checksum_data.error;
|
||||
data.release = await getRelease(release, data);
|
||||
if (data.error !== undefined) return data;
|
||||
data.version = version
|
||||
? await getVersion(version, data)
|
||||
: await getLatestVersion(data);
|
||||
|
||||
Reference in new issue
Block a user