Fall back to PHAR when Composer tool installation fails

This commit is contained in:
Shivam Mathur committed 2026-10-10 12:24:10 +05:30
1 parent 40d4c7a0d3
commit db75472672
8 files changed
+180 -19

No files matched your search

+50
View File
@@ -385,6 +385,56 @@ describe('Tools tests', () => {
}
);
it.each`
release | resolved
${'phpstan'} | ${'latest/download'}
${'phpstan:0.12.68'} | ${'download/0.12.68'}
${'phpstan:^0.12.68'} | ${'download/0.12.68'}
${'phpstan:0.12'} | ${'download/1.2.3'}
${'phpstan:0.12.x'} | ${'download/1.2.3'}
${'phpstan:0.12.*'} | ${'download/1.2.3'}
`(
'resolving PHPStan PHAR fallback for $release',
async ({release, resolved}) => {
unsetComposerAuthEnv();
for (const os of ['linux', 'darwin', 'win32']) {
const data = await tools.getData(release, '8.0', os);
const script = await tools.addPackage(data);
expect(script).toContain(
'scoped https://github.com/phpstan/phpstan/releases/' +
resolved +
'/phpstan.phar "-V"'
);
expect(data.type).toBe('composer');
expect(data.release).toContain('phpstan');
}
}
);
it('keeps Composer available when the fallback version lookup fails', async () => {
process.env['GITHUB_TOKEN'] = 'invalid_token';
const data = await tools.getData('phpstan:0.12', '8.0', 'linux');
expect(await tools.addPackage(data)).toBe(
'add_composer_tool phpstan "phpstan:0.12.*" phpstan/ scoped'
);
unsetComposerAuthEnv();
});
it('uses fallback settings independently of the Composer repository', async () => {
const data = await tools.getData('vendor/tool:1.2.3', '8.0', 'linux', {
fallback: {
type: 'phar',
repository: 'releases/tool',
domain: 'https://example.com',
extension: '.phar',
version_prefix: 'v'
}
});
expect(await tools.addPackage(data)).toBe(
'add_composer_tool tool tool:1.2.3 vendor/ global https://example.com/releases/tool/v1.2.3/tool.phar ""'
);
});
it.each`
version | php_version | os | script
${'latest'} | ${'8.0'} | ${'linux'} | ${'add_tool https://github.com/phar-io/phive/releases/download/3.2.1/phive-3.2.1.phar phive'}
+1 -1
View File
@@ -1 +1 @@
import{createRequire as e}from"module";var t={26:function(e,t){(function(e,s){true?s(t):0})(this,function(e){"use strict";const t=/^[v^~<>=]*?(\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+))?(?:-([\da-z\-]+(?:\.[\da-z\-]+)*))?(?:\+[\da-z\-]+(?:\.[\da-z\-]+)*)?)?)?$/i;const validateAndParse=e=>{if(typeof e!=="string"){throw new TypeError("Invalid argument expected string")}const s=e.match(t);if(!s){throw new Error(`Invalid argument not valid semver ('${e}' received)`)}s.shift();return s};const isWildcard=e=>e==="*"||e==="x"||e==="X";const tryParse=e=>{const t=parseInt(e,10);return isNaN(t)?e:t};const forceType=(e,t)=>typeof e!==typeof t?[String(e),String(t)]:[e,t];const compareStrings=(e,t)=>{if(isWildcard(e)||isWildcard(t))return 0;const[s,r]=forceType(tryParse(e),tryParse(t));if(s>r)return 1;if(s<r)return-1;return 0};const compareSegments=(e,t)=>{for(let s=0;s<Math.max(e.length,t.length);s++){const r=compareStrings(e[s]||"0",t[s]||"0");if(r!==0)return r}return 0};const compareVersions=(e,t)=>{const s=validateAndParse(e);const r=validateAndParse(t);const n=s.pop();const i=r.pop();const o=compareSegments(s,r);if(o!==0)return o;if(n&&i){return compareSegments(n.split("."),i.split("."))}else if(n||i){return n?-1:1}return 0};const compare=(e,t,r)=>{assertValidOperator(r);const n=compareVersions(e,t);return s[r].includes(n)};const s={">":[1],">=":[0,1],"=":[0],"<=":[-1,0],"<":[-1],"!=":[-1,1]};const r=Object.keys(s);const assertValidOperator=e=>{if(typeof e!=="string"){throw new TypeError(`Invalid operator type, expected string but got ${typeof e}`)}if(r.indexOf(e)===-1){throw new Error(`Invalid operator, expected one of ${r.join("|")}`)}};const satisfies=(e,t)=>{t=t.replace(/([><=]+)\s+/g,"$1");if(t.includes("||")){return t.split("||").some(t=>satisfies(e,t))}else if(t.includes(" - ")){const[s,r]=t.split(" - ",2);return satisfies(e,`>=${s} <=${r}`)}else if(t.includes(" ")){return t.trim().replace(/\s{2,}/g," ").split(" ").every(t=>satisfies(e,t))}const s=t.match(/^([<>=~^]+)/);const r=s?s[1]:"=";if(r!=="^"&&r!=="~")return compare(e,t,r);const[n,i,o,,a]=validateAndParse(e);const[c,l,d,,u]=validateAndParse(t);const p=[n,i,o];const f=[c,l!==null&&l!==void 0?l:"x",d!==null&&d!==void 0?d:"x"];if(u){if(!a)return false;if(compareSegments(p,f)!==0)return false;if(compareSegments(a.split("."),u.split("."))===-1)return false}const h=f.findIndex(e=>e!=="0")+1;const w=r==="~"?2:h>1?h:1;if(compareSegments(p.slice(0,w),f.slice(0,w))!==0)return false;if(compareSegments(p.slice(w),f.slice(w))===-1)return false;return true};const validate=e=>typeof e==="string"&&/^[v\d]/.test(e)&&t.test(e);const validateStrict=e=>typeof e==="string"&&/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/.test(e);e.compare=compare;e.compareVersions=compareVersions;e.satisfies=satisfies;e.validate=validate;e.validateStrict=validateStrict})}};var s={};function __nccwpck_require__(e){var r=s[e];if(r!==undefined){return r.exports}var n=s[e]={exports:{}};var i=true;try{t[e].call(n.exports,n,n.exports,__nccwpck_require__);i=false}finally{if(i)delete s[e]}return n.exports}if(typeof __nccwpck_require__!=="undefined")__nccwpck_require__.ab=decodeURIComponent(new URL(".",import.meta.url).pathname).slice(import.meta.url.match(/^file:\/\/\/\w:/)?1:0,-1)+"/";(()=>{__nccwpck_require__.d=(e,t)=>{for(var s in t){if(__nccwpck_require__.o(t,s)&&!__nccwpck_require__.o(e,s)){Object.defineProperty(e,s,{enumerable:true,get:t[s]})}}}})();(()=>{__nccwpck_require__.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t)})();var r={};__nccwpck_require__.d(r,{IT:()=>getScript,eF:()=>run,GJ:()=>setEnv});const n=e(import.meta.url)("url");const i=e(import.meta.url)("path");const o=e(import.meta.url)("string_decoder");const a=e(import.meta.url)("os");const c=e(import.meta.url)("events");const l=e(import.meta.url)("child_process");const d=e(import.meta.url)("assert");const u=e(import.meta.url)("fs");var p=undefined&&undefined.__awaiter||function(e,t,s,r){function adopt(e){return e instanceof s?e:new s(function(t){t(e)})}return new(s||(s=Promise))(function(s,n){function fulfilled(e){try{step(r.next(e))}catch(e){n(e)}}function rejected(e){try{step(r["throw"](e))}catch(e){n(e)}}function step(e){e.done?s(e.value):adopt(e.value).then(fulfilled,rejected)}step((r=r.apply(e,t||[])).next())})};const{chmod:f,copyFile:h,lstat:w,mkdir:m,open:g,readdir:v,rename:y,rm:_,rmdir:x,stat:$,symlink:b,unlink:E}=u.promises;const P=process.platform==="win32";function readlink(e){return p(this,void 0,void 0,function*(){const t=yield fs.promises.readlink(e);if(P&&!t.endsWith("\\")){return`${t}\\`}return t})}const k=268435456;const S=u.constants.O_RDONLY;function exists(e){return p(this,void 0,void 0,function*(){try{yield $(e)}catch(e){if(e.code==="ENOENT"){return false}throw e}return true})}function isDirectory(e){return p(this,arguments,void 0,function*(e,t=false){const s=t?yieLine truncated
import{createRequire as e}from"module";var t={26:function(e,t){(function(e,s){true?s(t):0})(this,function(e){"use strict";const t=/^[v^~<>=]*?(\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+))?(?:-([\da-z\-]+(?:\.[\da-z\-]+)*))?(?:\+[\da-z\-]+(?:\.[\da-z\-]+)*)?)?)?$/i;const validateAndParse=e=>{if(typeof e!=="string"){throw new TypeError("Invalid argument expected string")}const s=e.match(t);if(!s){throw new Error(`Invalid argument not valid semver ('${e}' received)`)}s.shift();return s};const isWildcard=e=>e==="*"||e==="x"||e==="X";const tryParse=e=>{const t=parseInt(e,10);return isNaN(t)?e:t};const forceType=(e,t)=>typeof e!==typeof t?[String(e),String(t)]:[e,t];const compareStrings=(e,t)=>{if(isWildcard(e)||isWildcard(t))return 0;const[s,r]=forceType(tryParse(e),tryParse(t));if(s>r)return 1;if(s<r)return-1;return 0};const compareSegments=(e,t)=>{for(let s=0;s<Math.max(e.length,t.length);s++){const r=compareStrings(e[s]||"0",t[s]||"0");if(r!==0)return r}return 0};const compareVersions=(e,t)=>{const s=validateAndParse(e);const r=validateAndParse(t);const n=s.pop();const i=r.pop();const o=compareSegments(s,r);if(o!==0)return o;if(n&&i){return compareSegments(n.split("."),i.split("."))}else if(n||i){return n?-1:1}return 0};const compare=(e,t,r)=>{assertValidOperator(r);const n=compareVersions(e,t);return s[r].includes(n)};const s={">":[1],">=":[0,1],"=":[0],"<=":[-1,0],"<":[-1],"!=":[-1,1]};const r=Object.keys(s);const assertValidOperator=e=>{if(typeof e!=="string"){throw new TypeError(`Invalid operator type, expected string but got ${typeof e}`)}if(r.indexOf(e)===-1){throw new Error(`Invalid operator, expected one of ${r.join("|")}`)}};const satisfies=(e,t)=>{t=t.replace(/([><=]+)\s+/g,"$1");if(t.includes("||")){return t.split("||").some(t=>satisfies(e,t))}else if(t.includes(" - ")){const[s,r]=t.split(" - ",2);return satisfies(e,`>=${s} <=${r}`)}else if(t.includes(" ")){return t.trim().replace(/\s{2,}/g," ").split(" ").every(t=>satisfies(e,t))}const s=t.match(/^([<>=~^]+)/);const r=s?s[1]:"=";if(r!=="^"&&r!=="~")return compare(e,t,r);const[n,i,o,,a]=validateAndParse(e);const[c,l,d,,u]=validateAndParse(t);const p=[n,i,o];const f=[c,l!==null&&l!==void 0?l:"x",d!==null&&d!==void 0?d:"x"];if(u){if(!a)return false;if(compareSegments(p,f)!==0)return false;if(compareSegments(a.split("."),u.split("."))===-1)return false}const h=f.findIndex(e=>e!=="0")+1;const w=r==="~"?2:h>1?h:1;if(compareSegments(p.slice(0,w),f.slice(0,w))!==0)return false;if(compareSegments(p.slice(w),f.slice(w))===-1)return false;return true};const validate=e=>typeof e==="string"&&/^[v\d]/.test(e)&&t.test(e);const validateStrict=e=>typeof e==="string"&&/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/.test(e);e.compare=compare;e.compareVersions=compareVersions;e.satisfies=satisfies;e.validate=validate;e.validateStrict=validateStrict})}};var s={};function __nccwpck_require__(e){var r=s[e];if(r!==undefined){return r.exports}var n=s[e]={exports:{}};var i=true;try{t[e].call(n.exports,n,n.exports,__nccwpck_require__);i=false}finally{if(i)delete s[e]}return n.exports}if(typeof __nccwpck_require__!=="undefined")__nccwpck_require__.ab=decodeURIComponent(new URL(".",import.meta.url).pathname).slice(import.meta.url.match(/^file:\/\/\/\w:/)?1:0,-1)+"/";(()=>{__nccwpck_require__.d=(e,t)=>{for(var s in t){if(__nccwpck_require__.o(t,s)&&!__nccwpck_require__.o(e,s)){Object.defineProperty(e,s,{enumerable:true,get:t[s]})}}}})();(()=>{__nccwpck_require__.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t)})();var r={};__nccwpck_require__.d(r,{IT:()=>getScript,eF:()=>run,GJ:()=>setEnv});const n=e(import.meta.url)("url");const i=e(import.meta.url)("path");const o=e(import.meta.url)("string_decoder");const a=e(import.meta.url)("os");const c=e(import.meta.url)("events");const l=e(import.meta.url)("child_process");const d=e(import.meta.url)("assert");const u=e(import.meta.url)("fs");var p=undefined&&undefined.__awaiter||function(e,t,s,r){function adopt(e){return e instanceof s?e:new s(function(t){t(e)})}return new(s||(s=Promise))(function(s,n){function fulfilled(e){try{step(r.next(e))}catch(e){n(e)}}function rejected(e){try{step(r["throw"](e))}catch(e){n(e)}}function step(e){e.done?s(e.value):adopt(e.value).then(fulfilled,rejected)}step((r=r.apply(e,t||[])).next())})};const{chmod:f,copyFile:h,lstat:w,mkdir:m,open:g,readdir:v,rename:y,rm:_,rmdir:x,stat:b,symlink:$,unlink:E}=u.promises;const P=process.platform==="win32";function readlink(e){return p(this,void 0,void 0,function*(){const t=yield fs.promises.readlink(e);if(P&&!t.endsWith("\\")){return`${t}\\`}return t})}const k=268435456;const S=u.constants.O_RDONLY;function exists(e){return p(this,void 0,void 0,function*(){try{yield b(e)}catch(e){if(e.code==="ENOENT"){return false}throw e}return true})}function isDirectory(e){return p(this,arguments,void 0,function*(e,t=false){const s=t?yieLine truncated
+9 -1
View File
@@ -155,7 +155,15 @@
"phpstan": {
"type": "composer",
"repository": "phpstan/phpstan",
"scope": "scoped"
"scope": "scoped",
"fallback": {
"type": "phar",
"repository": "phpstan/phpstan",
"extension": ".phar",
"domain": "https://github.com",
"version_prefix": "",
"version_parameter": "-V"
}
},
"pie": {
"type": "phar",
+44 -1
View File
@@ -17,7 +17,15 @@
"scope": "scoped",
"type": "composer",
"version_parameter": "--version",
"version_prefix": "v"
"version_prefix": "v",
"fallback": {
"type": "phar",
"repository": "user/tool",
"domain": "https://github.com",
"extension": ".phar",
"version_prefix": "v",
"version_parameter": "--version"
}
}
}
],
@@ -120,12 +128,47 @@
"examples": [
"v"
]
},
"fallback": {
"$ref": "#/$defs/pharFallback"
}
},
"required": [
"type"
],
"additionalProperties": true
},
"pharFallback": {
"type": "object",
"title": "The fallback schema",
"description": "PHAR installation settings used when Composer installation fails.",
"properties": {
"type": {
"const": "phar"
},
"repository": {
"$ref": "#/$defs/tool/properties/repository"
},
"domain": {
"$ref": "#/$defs/tool/properties/domain"
},
"extension": {
"$ref": "#/$defs/tool/properties/extension"
},
"fetch_latest": {
"$ref": "#/$defs/tool/properties/fetch_latest"
},
"version_parameter": {
"$ref": "#/$defs/tool/properties/version_parameter"
},
"version_prefix": {
"$ref": "#/$defs/tool/properties/version_prefix"
}
},
"required": [
"type"
],
"additionalProperties": false
}
}
}
+23 -4
View File
@@ -375,23 +375,33 @@ Function Add-ComposerToolHelper() {
composer global config --no-plugins allow-plugins."$prefix$tool" true >$null 2>&1
}
composer global require $prefix$release $composer_args >$null 2>&1
if($LASTEXITCODE -ne 0) {
return
}
return composer global show $prefix$tool 2>&1 | findstr '^versions'
} else {
$release_stream = [System.IO.MemoryStream]::New([System.Text.Encoding]::ASCII.GetBytes($release))
$scoped_dir_suffix = (Get-FileHash -InputStream $release_stream -Algorithm sha256).Hash
$scoped_dir = "$composer_bin\_tools\$tool-$scoped_dir_suffix"
$unix_scoped_dir = $scoped_dir.replace('\', '/')
if(-not(Test-Path $scoped_dir)) {
if(-not(Test-Path $scoped_dir\vendor\autoload.php)) {
New-Item -ItemType Directory -Force -Path $scoped_dir > $null 2>&1
Set-Content -Path $scoped_dir\composer.json -Value "{}"
if((composer show $prefix$tool $tool_version -d $unix_scoped_dir -a 2>&1 | findstr '^type *: *composer-plugin') -and ($composer_args -ne '')) {
composer config -d $unix_scoped_dir --no-plugins allow-plugins."$prefix$tool" true >$null 2>&1
}
composer require $prefix$release -d $unix_scoped_dir $composer_args >$null 2>&1
if($LASTEXITCODE -ne 0) {
return
}
}
$log = composer show $prefix$tool -d $unix_scoped_dir 2>&1 | findstr '^versions'
if(-not $log) {
return
}
[System.Environment]::SetEnvironmentVariable(($tool.replace('-', '_') + '_bin'), "$scoped_dir\vendor\bin")
Add-Path $scoped_dir\vendor\bin
return composer show $prefix$tool -d $unix_scoped_dir 2>&1 | findstr '^versions'
return $log
}
}
@@ -417,7 +427,13 @@ Function Add-ComposerTool() {
[ValidateNotNull()]
[ValidateLength(1, [int]::MaxValue)]
[string]
$scope
$scope,
[Parameter(Position = 4, Mandatory = $false)]
[string]
$fallback_url,
[Parameter(Position = 5, Mandatory = $false)]
[string]
$fallback_version_parameter
)
$composer_args = ""
if($composer_version.split('.')[0] -ne "1") {
@@ -433,10 +449,13 @@ Function Add-ComposerTool() {
if(Test-Path $composer_bin\composer) {
Copy-Item -Path "$bin_dir\composer" -Destination "$composer_bin\composer" -Force
}
Add-ToolsHelper $tool
if($log) {
$tool_version = Get-ToolVersion "Write-Output" "$log"
Add-ToolsHelper $tool
Add-Log $tick $tool "Added $tool $tool_version"
} elseif($fallback_url) {
Add-Log "!" $tool "Could not setup $tool using Composer. Falling back to PHAR."
Add-Tool $fallback_url $tool $fallback_version_parameter
} else {
Add-Log $cross $tool "Could not setup $tool"
}
+17 -9
View File
@@ -293,19 +293,20 @@ add_composer_tool_helper() {
if composer global show "$prefix$tool" "$tool_version" -a 2>&1 | grep -qE '^type *: *composer-plugin' && [ -n "$composer_args" ]; then
composer global config --no-plugins allow-plugins."$prefix$tool" true >/dev/null 2>&1
fi
composer global require "$prefix$release" "$composer_args" >/dev/null 2>&1
composer global require "$prefix$release" "$composer_args" >/dev/null 2>&1 || return 1
composer global show "$prefix$tool" 2>&1 | grep -E ^versions | sudo tee /tmp/composer.log >/dev/null 2>&1
else
scoped_dir="$composer_bin/_tools/$tool-$(echo -n "$release" | shasum -a 256 | cut -d ' ' -f 1)"
if ! [ -d "$scoped_dir" ]; then
if ! [ -f "$scoped_dir/vendor/autoload.php" ]; then
mkdir -p "$scoped_dir"
echo '{}' | tee "$scoped_dir/composer.json" >/dev/null
if composer show "$prefix$tool" "$tool_version" -d "$scoped_dir" -a 2>&1 | grep -qE '^type *: *composer-plugin' && [ -n "$composer_args" ]; then
composer config -d "$scoped_dir" --no-plugins allow-plugins."$prefix$tool" true >/dev/null 2>&1
fi
composer require "$prefix$release" -d "$scoped_dir" "$composer_args" >/dev/null 2>&1
composer show "$prefix$tool" -d "$scoped_dir" 2>&1 | grep -E ^versions | sudo tee /tmp/composer.log >/dev/null 2>&1
composer require "$prefix$release" -d "$scoped_dir" "$composer_args" >/dev/null 2>&1 || return 1
fi
composer show "$prefix$tool" -d "$scoped_dir" 2>&1 | grep -E ^versions | sudo tee /tmp/composer.log >/dev/null 2>&1
[ -s /tmp/composer.log ] || return 1
add_path "$scoped_dir"/vendor/bin
fi
}
@@ -316,6 +317,7 @@ add_composer_tool() {
release=$2
prefix=$3
scope=$4
local fallback_url=${5:-} fallback_version_parameter=${6:-}
composer_args=
composer_major_version=$(cut -d'.' -f 1 /tmp/composer_version)
if [ "$composer_major_version" != "1" ]; then
@@ -326,11 +328,17 @@ add_composer_tool() {
return
fi
fi
add_composer_tool_helper "$tool" "$release" "$prefix" "$scope" "$composer_args"
tool_version=$(get_tool_version cat /tmp/composer.log)
([ -s /tmp/composer.log ] && add_log "$tick" "$tool" "Added $tool $tool_version"
) || add_log "$cross" "$tool" "Could not setup $tool"
add_tools_helper "$tool"
sudo rm -f /tmp/composer.log
if add_composer_tool_helper "$tool" "$release" "$prefix" "$scope" "$composer_args" && [ -s /tmp/composer.log ]; then
tool_version=$(get_tool_version cat /tmp/composer.log)
add_tools_helper "$tool"
add_log "$tick" "$tool" "Added $tool $tool_version"
elif [ -n "$fallback_url" ]; then
add_log "!" "$tool" "Could not setup $tool using Composer. Falling back to PHAR."
add_tool "$fallback_url" "$tool" "$fallback_version_parameter"
else
add_log "$cross" "$tool" "Could not setup $tool"
fi
if [ -e "$composer_bin/composer" ]; then
sudo cp -a "$tool_path_dir/composer" "$composer_bin"
fi
+2
View File
@@ -20,6 +20,8 @@ Function Step-Log($message) {
Function Add-Log($mark, $subject, $message) {
if ($mark -eq $tick) {
printf "\033[32;1m%s \033[0m\033[34;1m%s \033[0m\033[90;1m%s \033[0m\n" $mark $subject $message
} elseif ($mark -eq "!") {
printf "\033[33;1m%s \033[0m\033[34;1m%s \033[0m\033[90;1m%s \033[0m\n" $mark $subject $message
} else {
printf "\033[31;1m%s \033[0m\033[34;1m%s \033[0m\033[90;1m%s \033[0m\n" $mark $subject $message
if($env:fail_fast -eq 'true') {
+34 -3
View File
@@ -51,6 +51,7 @@ export interface ToolData {
url: string;
uri?: string;
error?: string;
fallback?: ToolFallback;
}
/**
@@ -75,8 +76,19 @@ interface ToolConfig {
version_parameter?: string;
version_prefix?: string;
packagist?: string;
fallback?: ToolFallback;
}
type ToolFallback = Pick<
ToolConfig,
| 'repository'
| 'domain'
| 'extension'
| 'fetch_latest'
| 'version_parameter'
| 'version_prefix'
> & {type: 'phar'};
/**
* Regex to match a checksum suffix in a tool release - tool:version@sha256:<hash>
*/
@@ -417,7 +429,23 @@ export async function addPackage(data: ToolData): Promise<string> {
const args = [parts[1], data.release, parts[0] + '/', data.scope]
.map(a => utils.safeArg(a, data.os))
.join(' ');
return command + args;
if (!data.fallback) return command + args;
const fallback = await getData(
data.release.replace(/\.\*$/, ''),
data.php_version,
data.os,
data.fallback
);
// A failed version lookup must not prevent a successful Composer install.
if (fallback.error) return command + args;
return (
command +
args +
' ' +
utils.safeArg(fallback.url, data.os) +
' ' +
(fallback.version_parameter || '""')
);
}
/**
@@ -673,7 +701,8 @@ export async function addWPCLI(data: ToolData): Promise<string> {
export async function getData(
release: string,
php_version: string,
os: string
os: string,
overrides: ToolConfig = {}
): Promise<ToolData> {
const json_file_path = path.join(dirname, '../src/configs/tools.json');
const json_file: string = fs.readFileSync(json_file_path, 'utf8');
@@ -705,6 +734,7 @@ export async function getData(
config = {tool};
}
}
config = {...config, ...overrides};
const github = 'https://github.com';
const domain = config.domain ?? github;
const data: ToolData = {
@@ -730,7 +760,8 @@ export async function getData(
packagist: config.packagist ?? config.repository ?? '',
type: config.type,
function: config.function,
alias: config.alias
alias: config.alias,
fallback: config.fallback
};
data.checksum = checksum_data.checksum;
data.error = checksum_data.error;