Skip to content

Commit

Permalink
Fix "Failed to extract zip" on Windows (by updating credential helper…
Browse files Browse the repository at this point in the history
…s) (#1404)

* Use new credential helpers (distributed as binaries)
* Revert the OS X version to 0.6.4
  • Loading branch information
pjcdawkins authored Feb 19, 2024
1 parent af46c37 commit 2287179
Showing 1 changed file with 71 additions and 41 deletions.
112 changes: 71 additions & 41 deletions src/CredentialHelper/Manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -171,41 +171,49 @@ private function download(array $helper, $destination) {
}

// Download from the URL.
$contents = file_get_contents($helper['url'], false, \stream_context_create($this->config->getStreamContextOptions(60)));
$contextOptions = $this->config->getStreamContextOptions(60);
$contextOptions['http']['follow_location'] = 1;
$contents = file_get_contents($helper['url'], false, \stream_context_create($contextOptions));
if (!$contents) {
throw new \RuntimeException('Failed to download credentials helper from URL: ' . $helper['url']);
}

// Work in a temporary file.
$fs = new Filesystem();
$tmpFile = $fs->tempnam(sys_get_temp_dir(), 'cli-helper-extracted');

// Work in a temporary file.
$tmpFile = $destination . '-tmp';
try {
// Extract the archive.
$this->extractBinFromArchive($contents, substr($helper['url'], -4) === '.zip', $helper['filename'], $tmpFile);
// Write the file, either directly or via extracting its archive.
if (preg_match('/(\.tar\.gz|\.tgz|\.zip)$/', $helper['url']) === 1) {
$this->extractBinFromArchive($contents, substr($helper['url'], -4) === '.zip', $helper['filename'], $tmpFile);
} else {
$fs->dumpFile($tmpFile, $contents);
}

// Verify the file hash.
if (hash_file('sha256', $tmpFile) !== $helper['sha256']) {
throw new \RuntimeException('Failed to verify downloaded file for helper: ' . $helper['url']);
$hash = hash_file('sha256', $tmpFile);
if ($hash !== $helper['sha256']) {
throw new \RuntimeException(sprintf('Failed to verify downloaded file for helper: %s (hash: %s, expected: %s)', $helper['url'], $hash, $helper['sha256']));
}

// Make the file executable and move it into place.
$fs->chmod($tmpFile, 0700);
$fs->mkdir(dirname($destination), 0700);
$fs->rename($tmpFile, $destination, true);
} finally {
$fs->remove($tmpFile);
}
}

/**
* Extracts the internal file from the package and moves it to a destination.
* Extracts the internal file from a package archive and moves it to a destination.
*
* @param string $archiveContents
* @param bool $zip
* @param string $internalFilename
* @param string $destination
*/
private function extractBinFromArchive($archiveContents, $zip, $internalFilename, $destination) {
private function extractBinFromArchive($archiveContents, $zip, $internalFilename, $destination)
{
$fs = new Filesystem();
$tmpDir = $tmpFile = $fs->tempnam(sys_get_temp_dir(), 'cli-helpers');
$fs->remove($tmpFile);
Expand Down Expand Up @@ -245,25 +253,41 @@ private function extractBinFromArchive($archiveContents, $zip, $internalFilename
*/
private function getHelpers() {
return [
'wincred' => [
'url' => 'https://github.com/docker/docker-credential-helpers/releases/download/v0.6.4/docker-credential-wincred-v0.6.4-amd64.zip',
'filename' => 'docker-credential-wincred.exe',
'sha256' => 'a4e7885d3d469b2e3c92ab72c729e35df94ed1952bc8090272107fef0652e474',
],
'secretservice' => [
'url' => 'https://github.com/docker/docker-credential-helpers/releases/download/v0.6.4/docker-credential-secretservice-v0.6.4-amd64.tar.gz',
'filename' => 'docker-credential-secretservice',
'sha256' => '1bddcc1da7ea4d6f50d8e21ba3f0d2ae518c04d90553f543e683af62e9c7c9a8',
'windows' => [
'amd64' => [
'url' => 'https://github.com/docker/docker-credential-helpers/releases/download/v0.8.1/docker-credential-wincred-v0.8.1.windows-amd64.exe',
'filename' => 'docker-credential-wincred.exe',
'sha256' => '86c3aa9120ad136e5f7d669267a8e271f0d9ec2879c75908f20a769351043a28',
],
'arm64' => [
'url' => 'https://github.com/docker/docker-credential-helpers/releases/download/v0.8.1/docker-credential-wincred-v0.8.1.windows-arm64.exe',
'filename' => 'docker-credential-wincred.exe',
'sha256' => 'a83bafb13c168de1ecae48dbfc5e6f220808be86dd4258dd72fd9bcefdf5a63c',
],
],
'osxkeychain' => [
'url' => 'https://github.com/docker/docker-credential-helpers/releases/download/v0.6.4/docker-credential-osxkeychain-v0.6.4-amd64.tar.gz',
'filename' => 'docker-credential-osxkeychain',
'sha256' => '76c4088359bbbcd25b8d0ff8436086742b6184aba6380ae57d39e5513f723b74',
'linux' => [
'amd64' => [
'url' => 'https://github.com/docker/docker-credential-helpers/releases/download/v0.8.1/docker-credential-secretservice-v0.8.1.linux-amd64',
'filename' => 'docker-credential-secretservice',
'sha256' => '9a5875bc9435c2c8f9544419c249f866b372b054294f169444f66bb925d96edc',
],
'arm64' => [
'url' => 'https://github.com/docker/docker-credential-helpers/releases/download/v0.8.1/docker-credential-secretservice-v0.8.1.linux-arm64',
'filename' => 'docker-credential-secretservice',
'sha256' => '1093ff44716b9d8c3715d0e5322ba453fd1a77faad8b7e1ba3ad159bf6e10887',
],
],
'osxkeychain-arm64' => [
'url' => 'https://github.com/docker/docker-credential-helpers/releases/download/v0.6.4/docker-credential-osxkeychain-v0.6.4-arm64.tar.gz',
'filename' => 'docker-credential-osxkeychain',
'sha256' => '902e8237747aac0eca61efa1875e65aa8552b7c95fc406cf0d2aef733dda41de',
'darwin' => [
'amd64' => [
'url' => 'https://github.com/docker/docker-credential-helpers/releases/download/v0.6.4/docker-credential-osxkeychain-v0.6.4-amd64.tar.gz',
'filename' => 'docker-credential-osxkeychain',
'sha256' => '76c4088359bbbcd25b8d0ff8436086742b6184aba6380ae57d39e5513f723b74',
],
'arm64' => [
'url' => 'https://github.com/docker/docker-credential-helpers/releases/download/v0.6.4/docker-credential-osxkeychain-v0.6.4-arm64.tar.gz',
'filename' => 'docker-credential-osxkeychain',
'sha256' => '902e8237747aac0eca61efa1875e65aa8552b7c95fc406cf0d2aef733dda41de',
],
],
];
}
Expand All @@ -275,26 +299,30 @@ private function getHelpers() {
*/
private function getHelper() {
$arch = php_uname('m');
if ($arch === 'ARM64') {
$arch = 'arm64';
} elseif (\in_array($arch, ['x86_64', 'amd64', 'AMD64'])) {
$arch = 'amd64';
}

$helpers = $this->getHelpers();

if (OsUtil::isOsX()) {
if ($arch === 'arm64') {
return $helpers['osxkeychain-arm64'];
} elseif (\in_array($arch, ['x86_64', 'amd64', 'AMD64'])) {
return $helpers['osxkeychain'];
}
if (OsUtil::isWindows()) {
$os = 'windows';
} elseif (OsUtil::isLinux()) {
$os = 'linux';
} elseif (OsUtil::isOsX()) {
$os = 'darwin';
}

if (!in_array($arch, ['x86_64', 'amd64', 'AMD64'])) {
throw new \RuntimeException('Unable to find a credentials helper for this system architecture');
if (!isset($os) || !isset($helpers[$os])) {
throw new \RuntimeException('Unable to find a credentials helper for this operating system');
}

if (OsUtil::isWindows()) {
return $helpers['wincred'];
if (!isset($helpers[$os][$arch])) {
throw new \RuntimeException(sprintf('Unable to find a credentials helper for this operating system (%s) and architecture (%s)', $os, $arch));
}

if (OsUtil::isLinux()) {
if ($os === 'linux') {
// The Linux helper probably needs an X display.
if (in_array(getenv('DISPLAY'), [false, 'none'], true)) {
throw new \RuntimeException('Unable to find a credentials helper for this system (no DISPLAY)');
Expand All @@ -315,11 +343,13 @@ private function getHelper() {
if (!$this->shell->execute('ldconfig --print-cache | grep -q libsecret')) {
throw new \RuntimeException('Unable to find a credentials helper for this system (libsecret is not installed)');
}
}

return $helpers['secretservice'];
if (substr($helpers[$os][$arch]['url'], -4) === '.zip' && !class_exists('\\ZipArchive') && !$this->shell->commandExists('unzip')) {
throw new \RuntimeException('Unable to install a credentials helper for this system (it is a .zip file and the zip extension is unavailable)');
}

throw new \RuntimeException('Unable to find a credentials helper for this system');
return $helpers[$os][$arch];
}

/**
Expand Down

0 comments on commit 2287179

Please sign in to comment.