Skip to content

Commit

Permalink
Allow the --dl and #!depdir paths to use more variables
Browse files Browse the repository at this point in the history
  • Loading branch information
totten committed Nov 4, 2022
1 parent a064170 commit 814a318
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 28 deletions.
4 changes: 2 additions & 2 deletions docs/exec.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,10 @@ echo "Hello world\n";

The `--dl` can be relative. By default, it's relative to the CWD (which is
more intuitive for direct invocations), but you may make it relative to the
script with the constant `_SCRIPTDIR_`.
script with the variable `{SCRIPT_DIR}`.

```bash
#!/usr/bin/env pogo --dl='_SCRIPTDIR_/.my-script.deps'
#!/usr/bin/env pogo --dl='{SCRIPT_DIR}/.my-script.deps'
<?php
echo "Hello world\n";
```
Expand Down
18 changes: 17 additions & 1 deletion docs/pragmas.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,29 @@ Pogo accepts instructions using a `#!foo` notation. The following are supported:
* __Example__: `#!require {symfony/yaml: ~3.0, symfony/finder: ~3.0}`
* __Comment__: The `<package>` and `<version>` notations match [composer's `require`](https://getcomposer.org/doc/04-schema.md#require).

## dl
## depdir

* __Description__: Specify the location for automatically downloaded packages
* __Signature__: `#!depdir <yaml-string>`
* __Example__: `#!depdir '/tmp'
* __Example__: `#!depdir '../var/cache'
* __Comment__: If the path is relative, it is interpreted as relative to the script file. (This differs from the CLI option `--dl` which defaults to the PWD.)
The path may use the following variables:
* Environment:
* `{POGO_BASE}`: This is either env-var POGO_BASE or fallback (such as `$HOME/.cache/pogo` or `/tmp/pogo`)
* `{PHP_X}`: PHP version (MAJOR part)
* `{PHP_XY}`:PHP version (MAJOR.MINOR parts)
* `{PHP_XYZ}`:PHP version (MAJOR.MINOR.PATCH parts)
* `{ENV[...]}`: Reference any environment variable
* Script location
* `{SCRIPT_DIR}`: The folder containing the main script
* `{SCRIPT_FILE}`: The filename of the main script
* `{SCRIPT_NAME}`: The filename of the main script, but with file-extensions removed
* `_SCRIPTDIR_` (*deprecated*): The folder containing the main script
* Script content
* `{CODE_DIGEST}`: A checksum based on the raw code of the script
* `{REQUIRE_DIGEST}`: A checksum based on the `#!require` directives of the script
* `{SCRIPT_DIGEST}`: A checksum based on the content and location of the script

## ini

Expand Down
91 changes: 73 additions & 18 deletions src/Command/DownloadCommandTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,28 +65,12 @@ public function pickBaseDir(InputInterface $input, $scriptMetadata) {
$dl = $scriptMetadata->dir;
}
if ($dl) {
$dl = preg_replace('/^_SCRIPTDIR_/', dirname($scriptMetadata->file), $dl);
$dl = $this->evalPathExpr($dl, $scriptMetadata);
return PathUtil::evaluateDots(PathUtil::makeAbsolute($dl));
}

// Pick a base and calculate a hint/digested name.
$hint = basename($scriptMetadata->file) . '-' . sha1($scriptMetadata->getDigest() . $this->getCodeDigest() . realpath($scriptMetadata->file));

if (getenv('POGO_BASE')) {
if (getenv('POGO_BASE') === '.') {
$base = dirname($scriptMetadata->file) . DIRECTORY_SEPARATOR . '.pogo';
}
else {
$base = getenv('POGO_BASE');
}
}
elseif (getenv('HOME')) {
$base = getenv('HOME') . DIRECTORY_SEPARATOR . '.cache' . DIRECTORY_SEPARATOR . 'pogo';
}
else {
$base = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'pogo';
}
return $base . DIRECTORY_SEPARATOR . $hint;
return $this->evalPathExpr('{POGO_BASE}/{SCRIPT_FILE}-{SCRIPT_DIGEST}', $scriptMetadata);
}

/**
Expand All @@ -107,4 +91,75 @@ public function getCodeDigest() {
return $value;
}

/**
* Evaluate the path-expression.
*
* @param string $pathExpr
* A path expression as used by `--dl` or `#!depdir`.
* Ex: '{SCRIPT_DIR}/.cache-{SCRIPT_FILE}'
* @param \Pogo\ScriptMetadata $scriptMetadata
* @return string
*/
protected function evalPathExpr($pathExpr, \Pogo\ScriptMetadata $scriptMetadata) {
return preg_replace_callback('/(_SCRIPTDIR_|{[A-Z0-9_]+}|{ENV\[[^\]]+\]})/',
function ($m) use ($scriptMetadata) {
$var = substr($m[1], 1, -1);

if (preg_match('/^ENV\[([^\]]+)\]/', $var, $envParse)) {
return getenv($envParse[1]);
}

switch ($var) {
case 'CODE_DIGEST':
return $this->getCodeDigest();

case 'PHP_X':
[$major, $minor, $patch] = explode('.', PHP_VERSION);
return $major;

case 'PHP_XY':
[$major, $minor, $patch] = explode('.', PHP_VERSION);
return "{$major}.{$minor}";

case 'PHP_XYZ':
[$major, $minor, $patch] = explode('.', PHP_VERSION);
return "{$major}.{$minor}.{$patch}";

case 'POGO_BASE':
if (getenv('POGO_BASE')) {
if (getenv('POGO_BASE') === '.') {
return dirname($scriptMetadata->file) . DIRECTORY_SEPARATOR . '.pogo';
}
else {
return getenv('POGO_BASE');
}
}
elseif (getenv('HOME')) {
return getenv('HOME') . DIRECTORY_SEPARATOR . '.cache' . DIRECTORY_SEPARATOR . 'pogo';
}
else {
return sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'pogo';
}

case 'REQUIRE_DIGEST':
return $scriptMetadata->getDigest();

case 'SCRIPTDIR':
case 'SCRIPT_DIR':
return dirname($scriptMetadata->file);

case 'SCRIPT_FILE':
return basename($scriptMetadata->file);

case 'SCRIPT_NAME':
return preg_replace('/\.php$/', '', basename($scriptMetadata->file));

case 'SCRIPT_DIGEST':
return sha1($scriptMetadata->getDigest() . $this->getCodeDigest() . realpath($scriptMetadata->file));
}
},
$pathExpr
);
}

}
37 changes: 30 additions & 7 deletions tests/DownloadTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,45 @@ public function getRunModes(): array {
];
}

public function getCliExamples(): array {
[$phpMajor, $phpMinor] = explode('.', PHP_VERSION);

return [
['{SCRIPT_DIR}/tmp/my-{SCRIPT_NAME}-php-{PHP_XY}', 'tmp/my-dl-via-pragma-php-' . $phpMajor . '.' . $phpMinor, 'local'],
['{SCRIPT_DIR}/tmp/my-{SCRIPT_NAME}-php-{PHP_XY}', 'tmp/my-dl-via-pragma-php-' . $phpMajor . '.' . $phpMinor, 'isolate'],
// Deprecated: Underscore var
['_SCRIPTDIR_/tmp/dl-via-cli-legacy', 'tmp/dl-via-cli-legacy', 'local'],
['_SCRIPTDIR_/tmp/dl-via-cli-legacy', 'tmp/dl-via-cli-legacy', 'isolate'],
];
}

/**
* @dataProvider getRunModes
* @dataProvider getCliExamples
*/
public function testCliOption($runMode): void {
public function testCliOption($depDirExpr, $depDirRel, $runMode): void {
$script = $this->getTestDir('dl-via-pragma.php');
$depDirRel = 'tmp/dl-via-cli';
$depDirAbs = $this->getTestDir($depDirRel);

if (file_exists($depDirAbs)) {
$this->runCmd('rm -rf ' . escapeshellarg($depDirAbs));
}

$this->assertFalse(file_exists($depDirAbs));
$this->runCmd("pogo --run-mode=$runMode --dl=_SCRIPTDIR_/$depDirRel " . escapeshellarg($script));
$this->assertTrue(file_exists($depDirAbs));
$this->assertTrue(file_exists("$depDirAbs/vendor/autoload.php"));
$this->assertFalse(file_exists($depDirAbs), "Folder $depDirAbs should not exist");
$this->runCmd("pogo --run-mode=$runMode --dl=$depDirExpr " . escapeshellarg($script));
$this->assertTrue(file_exists($depDirAbs), "Folder $depDirAbs should exist");
$this->assertTrue(file_exists("$depDirAbs/vendor/autoload.php"), "File $depDirAbs/vendor/autoload.php should exist");
}

public function testEnvVar() {
$_SERVER['SOMEDIR'] = $_ENV['SOMEDIR'] = $this->getTestDir('tmp/foo');
putenv('SOMEDIR=' . $_ENV['SOMEDIR']);
try {
$this->testCliOption('{ENV[SOMEDIR]}/bar', 'tmp/foo/bar', 'local');
} finally {
unset($_SERVER['SOMEDIR']);
unset($_ENV['SOMEDIR']);
putenv('SOMEDIR');
}
}

/**
Expand Down

0 comments on commit 814a318

Please sign in to comment.