resources: Add caching for faster runs and offline use
Also honour any XDG variables if set (such as Wikimedia CI does) so that gate jobs can automatically persist the cache. Bug: T203694 Change-Id: I156af1c5097b8a87be228345fc0a0b80bb3ae338
This commit is contained in:
parent
dd6beafe61
commit
3540897a6f
2 changed files with 54 additions and 8 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -50,6 +50,7 @@ sftp-config.json
|
|||
# Building & testing
|
||||
npm-debug.log
|
||||
node_modules/
|
||||
/resources/lib/.foreign
|
||||
/tests/phpunit/phpunit.phar
|
||||
/tests/selenium/log
|
||||
.eslintcache
|
||||
|
|
|
|||
|
|
@ -30,10 +30,12 @@ class ForeignResourceManager {
|
|||
private $registryFile;
|
||||
private $libDir;
|
||||
private $tmpParentDir;
|
||||
private $cacheDir;
|
||||
private $infoPrinter;
|
||||
private $errorPrinter;
|
||||
private $verbosePrinter;
|
||||
private $action;
|
||||
private $registry;
|
||||
|
||||
/**
|
||||
* @param string $registryFile Path to YAML file
|
||||
|
|
@ -60,8 +62,11 @@ class ForeignResourceManager {
|
|||
|
||||
// Use a temporary directory under the destination directory instead
|
||||
// of wfTempDir() because PHP's rename() does not work across file
|
||||
// systems, as the user's /tmp and $IP may be on different filesystems.
|
||||
$this->tmpParentDir = "{$this->libDir}/.tmp";
|
||||
// systems, and the user's /tmp and $IP may be on different filesystems.
|
||||
$this->tmpParentDir = "{$this->libDir}/.foreign/tmp";
|
||||
|
||||
$cacheHome = getenv( 'XDG_CACHE_HOME' ) ? realpath( getenv( 'XDG_CACHE_HOME' ) ) : false;
|
||||
$this->cacheDir = $cacheHome ? "$cacheHome/mw-foreign" : "{$this->libDir}/.foreign/cache";
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -76,14 +81,14 @@ class ForeignResourceManager {
|
|||
}
|
||||
$this->action = $action;
|
||||
|
||||
$registry = $this->parseBasicYaml( file_get_contents( $this->registryFile ) );
|
||||
$this->registry = $this->parseBasicYaml( file_get_contents( $this->registryFile ) );
|
||||
if ( $module === 'all' ) {
|
||||
$modules = $registry;
|
||||
} elseif ( isset( $registry[ $module ] ) ) {
|
||||
$modules = [ $module => $registry[ $module ] ];
|
||||
$modules = $this->registry;
|
||||
} elseif ( isset( $this->registry[ $module ] ) ) {
|
||||
$modules = [ $module => $this->registry[ $module ] ];
|
||||
} else {
|
||||
$this->error( "Unknown module name.\n\nMust be one of:\n" .
|
||||
wordwrap( implode( ', ', array_keys( $registry ) ), 80 ) .
|
||||
wordwrap( implode( ', ', array_keys( $this->registry ) ), 80 ) .
|
||||
'.'
|
||||
);
|
||||
return false;
|
||||
|
|
@ -127,8 +132,8 @@ class ForeignResourceManager {
|
|||
}
|
||||
}
|
||||
|
||||
$this->cleanUp();
|
||||
$this->output( "\nDone!\n" );
|
||||
$this->cleanUp();
|
||||
if ( $this->hasErrors ) {
|
||||
// The verify mode should check all modules/files and fail after, not during.
|
||||
return false;
|
||||
|
|
@ -137,7 +142,29 @@ class ForeignResourceManager {
|
|||
return true;
|
||||
}
|
||||
|
||||
private function cacheKey( $src, $integrity ) {
|
||||
$key = basename( $src ) . '_' . substr( $integrity, -12 );
|
||||
$key = preg_replace( '/[.\/+?=_-]+/', '_', $key );
|
||||
return rtrim( $key, '_' );
|
||||
}
|
||||
|
||||
/** @return string|false */
|
||||
private function cacheGet( $key ) {
|
||||
return Wikimedia\quietCall( 'file_get_contents', "{$this->cacheDir}/$key.data" );
|
||||
}
|
||||
|
||||
private function cacheSet( $key, $data ) {
|
||||
wfMkdirParents( $this->cacheDir );
|
||||
file_put_contents( "{$this->cacheDir}/$key.data", $data, LOCK_EX );
|
||||
}
|
||||
|
||||
private function fetch( $src, $integrity ) {
|
||||
$key = $this->cacheKey( $src, $integrity );
|
||||
$data = $this->cacheGet( $key );
|
||||
if ( $data ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$req = MWHttpRequest::factory( $src, [ 'method' => 'GET', 'followRedirects' => false ] );
|
||||
if ( !$req->execute()->isOK() ) {
|
||||
throw new Exception( "Failed to download resource at {$src}" );
|
||||
|
|
@ -150,6 +177,7 @@ class ForeignResourceManager {
|
|||
$actualIntegrity = $algo . '-' . base64_encode( hash( $algo, $data, true ) );
|
||||
if ( $integrity === $actualIntegrity ) {
|
||||
$this->verbose( "... passed integrity check for {$src}\n" );
|
||||
$this->cacheSet( $key, $data );
|
||||
} else {
|
||||
if ( $this->action === 'make-sri' ) {
|
||||
$this->output( "Integrity for {$src}\n\tintegrity: ${actualIntegrity}\n" );
|
||||
|
|
@ -277,6 +305,23 @@ class ForeignResourceManager {
|
|||
|
||||
private function cleanUp() {
|
||||
wfRecursiveRemoveDir( $this->tmpParentDir );
|
||||
|
||||
// Prune the cache of files we don't recognise.
|
||||
$knownKeys = [];
|
||||
foreach ( $this->registry as $info ) {
|
||||
if ( $info['type'] === 'file' || $info['type'] === 'tar' ) {
|
||||
$knownKeys[] = $this->cacheKey( $info['src'], $info['integrity'] );
|
||||
} elseif ( $info['type'] === 'multi-file' ) {
|
||||
foreach ( $info['files'] as $file ) {
|
||||
$knownKeys[] = $this->cacheKey( $file['src'], $file['integrity'] );
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ( glob( "{$this->cacheDir}/*" ) as $cacheFile ) {
|
||||
if ( !in_array( basename( $cacheFile, '.data' ), $knownKeys ) ) {
|
||||
unlink( $cacheFile );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in a new issue