Skip to content

Commit

Permalink
Use auth for GitLab private repos, and fix GitLab failover behavior (#…
Browse files Browse the repository at this point in the history
…157)

* - When retrieving a ZIP from a private repository, use authentication, if provided
- Fix the GitLab failover behavior (used to bail-out on private GitLab repositories without trying to use a token if one was provided)

* "fix" a phpcs warning

* handle GitLab subgroup

* use GitLab as a Github fall-back when requesting package-name without precision about Git hosting

* phpcs fixes
  • Loading branch information
drzraf authored Sep 6, 2023
1 parent 1e9b181 commit f295538
Showing 1 changed file with 30 additions and 13 deletions.
43 changes: 30 additions & 13 deletions src/Package_Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -244,13 +244,15 @@ public function install( $args, $assoc_args ) {
// Download the remote ZIP file to a temp directory
$temp = false;
if ( false !== strpos( $package_name, '://' ) ) {
$temp = Utils\get_temp_dir() . uniqid( 'wp-cli-package_', true /*more_entropy*/ ) . '.zip';
$options = [
$temp = Utils\get_temp_dir() . uniqid( 'wp-cli-package_', true /*more_entropy*/ ) . '.zip';
$options = [
'timeout' => 600,
'filename' => $temp,
'insecure' => $insecure,
];
$response = Utils\http_request( 'GET', $package_name, null, [], $options );
$gitlab_token = getenv( 'GITLAB_TOKEN' ); // Use GITLAB_TOKEN if available to avoid authorization failures or rate-limiting.
$headers = $gitlab_token && strpos( $package_name, '://gitlab.com/' ) !== false ? [ 'PRIVATE-TOKEN' => $gitlab_token ] : [];
$response = Utils\http_request( 'GET', $package_name, null, $headers, $options );
if ( 20 !== (int) substr( $response->status_code, 0, 2 ) ) {
@unlink( $temp ); // @codingStandardsIgnoreLine
WP_CLI::error( sprintf( "Couldn't download package from '%s' (HTTP code %d).", $package_name, $response->status_code ) );
Expand Down Expand Up @@ -847,6 +849,16 @@ private function get_package_by_shortened_identifier( $package_name, $insecure =
return $url;
}

// Fall back to GitLab URL if we had no match yet.
$url = "https://gitlab.com/{$package_name}.git";
$gitlab_token = getenv( 'GITLAB_TOKEN' ); // Use GITLAB_TOKEN if available to avoid authorization failures or rate-limiting.
$headers = $github_token ? [ 'Authorization' => 'token ' . $github_token ] : [];
$headers = $gitlab_token && strpos( $package_name, '://gitlab.com/' ) !== false ? [ 'PRIVATE-TOKEN' => $gitlab_token ] : [];
$response = Utils\http_request( 'GET', $url, null /*data*/, $headers, $options );
if ( 20 === (int) substr( $response->status_code, 0, 2 ) ) {
return $url;
}

return false;
}

Expand Down Expand Up @@ -1161,7 +1173,9 @@ private function check_github_package_name( $package_name, $version = '', $insec
*/
private function check_git_package_name( $package_name, $url = '', $version = '', $insecure = false ) {
if ( $url && ( strpos( $url, '://gitlab.com/' ) !== false ) || ( strpos( $url, '[email protected]:' ) !== false ) ) {
return $this->check_gitlab_package_name( $package_name, $version, $insecure );
$matches = [];
preg_match( '#gitlab.com[:/](.*?)\.git#', $url, $matches );
return $this->check_gitlab_package_name( $matches[1], $version, $insecure );
}

return $this->check_github_package_name( $package_name, $version, $insecure );
Expand All @@ -1175,24 +1189,27 @@ private function check_git_package_name( $package_name, $url = '', $version = ''
* @param bool $insecure Optional. Whether to insecurely retry downloads that failed TLS handshake. Defaults
* to false.
*/
private function check_gitlab_package_name( $package_name, $version = '', $insecure = false ) {
private function check_gitlab_package_name( $project_name, $version = '', $insecure = false ) {
$options = [ 'insecure' => $insecure ];
// Generate raw git URL of composer.json file.
$raw_content_public_url = 'https://gitlab.com/' . $package_name . '/-/raw/' . $this->get_raw_git_version( $version ) . '/composer.json';
$raw_content_private_url = 'https://gitlab.com/api/v4/projects/' . rawurlencode( $package_name ) . '/repository/files/composer.json/raw?ref=' . $this->get_raw_git_version( $version );
$raw_content_public_url = 'https://gitlab.com/' . $project_name . '/-/raw/' . $this->get_raw_git_version( $version ) . '/composer.json';
$raw_content_private_url = 'https://gitlab.com/api/v4/projects/' . rawurlencode( $project_name ) . '/repository/files/composer.json/raw?ref=' . $this->get_raw_git_version( $version );

$options = [ 'insecure' => $insecure ];
$matches = [];
preg_match( '#([^:\/]+\/[^\/]+$)#', $project_name, $matches );
$package_name = $matches[1];

$response = Utils\http_request( 'GET', $raw_content_public_url, null /*data*/, [], $options );
if ( $response->status_code < 200 || $response->status_code >= 300 ) {
$gitlab_token = getenv( 'GITLAB_TOKEN' ); // Use GITLAB_TOKEN if available to avoid authorization failures or rate-limiting.
$response = Utils\http_request( 'GET', $raw_content_public_url, null /*data*/, [], $options );
if ( ! $gitlab_token && ( $response->status_code < 200 || $response->status_code >= 300 ) ) {
// Could not get composer.json. Possibly private so warn and return best guess from input (always xxx/xxx).
WP_CLI::warning( sprintf( "Couldn't download composer.json file from '%s' (HTTP code %d). Presuming package name is '%s'.", $raw_content_public_url, $response->status_code, $package_name ) );
return $package_name;
}

if ( strpos( $response->headers['content-type'], 'text/html' ) === 0 ) {
$gitlab_token = getenv( 'GITLAB_TOKEN' ); // Use GITLAB_TOKEN if available to avoid authorization failures or rate-limiting.
$headers = $gitlab_token ? [ 'PRIVATE-TOKEN' => $gitlab_token ] : [];
$response = Utils\http_request( 'GET', $raw_content_private_url, null /*data*/, $headers, $options );
$headers = $gitlab_token ? [ 'PRIVATE-TOKEN' => $gitlab_token ] : [];
$response = Utils\http_request( 'GET', $raw_content_private_url, null /*data*/, $headers, $options );

if ( $response->status_code < 200 || $response->status_code >= 300 ) {
// Could not get composer.json. Possibly private so warn and return best guess from input (always xxx/xxx).
Expand Down

0 comments on commit f295538

Please sign in to comment.