Skip to content

Commit 2382578

Browse files
committed
feat: add phar archive distribution support
Add support for distributing slic as a self-contained phar archive, enabling simpler installation and self-update via GitHub Releases. - Add is_phar() and slic_data_dir() to separate writable data paths from the read-only phar filesystem (~/.slic for phar, SLIC_ROOT_DIR for git clone) - Update all writable file references (.env.slic.run, .build-version, .remote-version, .cache, .architecture_*, .env.slic.stacks) to use slic_data_dir() - Add phar self-update in upgrade command via GitHub Releases API with atomic file replacement - Add box.json for humbug/box phar builder - Add GitHub Actions workflow to build and release slic.phar on tag push
1 parent 4e0127e commit 2382578

File tree

12 files changed

+357
-50
lines changed

12 files changed

+357
-50
lines changed

.github/workflows/release-phar.yml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: Build and release slic.phar
2+
3+
on:
4+
push:
5+
tags:
6+
- '[0-9]+.[0-9]+.[0-9]+'
7+
- 'v[0-9]+.[0-9]+.[0-9]+'
8+
9+
permissions:
10+
contents: write
11+
12+
jobs:
13+
build-phar:
14+
name: Build phar and create release
15+
runs-on: ubuntu-latest
16+
steps:
17+
- name: Checkout code
18+
uses: actions/checkout@v4
19+
20+
- name: Setup PHP
21+
uses: shivammathur/setup-php@v2
22+
with:
23+
php-version: '8.1'
24+
tools: composer:v2
25+
extensions: phar
26+
ini-values: phar.readonly=0
27+
28+
- name: Install humbug/box
29+
run: composer global require humbug/box:^4.0
30+
31+
- name: Build phar
32+
run: box compile
33+
34+
- name: Verify phar works
35+
run: php slic.phar help
36+
37+
- name: Create GitHub release
38+
uses: softprops/action-gh-release@v2
39+
with:
40+
files: slic.phar
41+
generate_release_notes: true

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,6 @@ _plugins
4444

4545
# PHPUnit cache
4646
.phpunit.result.cache
47+
48+
# Phar build artifacts
49+
*.phar

box.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"main": "slic.php",
3+
"output": "slic.phar",
4+
"shebang": "#!/usr/bin/env php",
5+
"directories": [
6+
"src",
7+
"includes",
8+
"containers",
9+
"completions"
10+
],
11+
"files": [
12+
"slic.php",
13+
"version.php",
14+
".env.slic",
15+
"slic-stack.yml",
16+
"slic-stack.site.yml",
17+
"slic-stack.worktree.yml"
18+
],
19+
"blacklist": [
20+
"tests",
21+
"docs",
22+
"vendor",
23+
".github",
24+
".git",
25+
"_wordpress",
26+
"_plugins",
27+
".cache",
28+
".idea",
29+
".data"
30+
],
31+
"compactors": [
32+
"KevinGH\\Box\\Compactor\\Php"
33+
],
34+
"compression": "NONE",
35+
"chmod": "0755"
36+
}

slic.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@
291291
$subcommand = $args( 'subcommand', 'help' );
292292

293293
// Both these variables will be used by commands.
294-
$run_settings_file = root( '/.env.slic.run' );
294+
$run_settings_file = \StellarWP\Slic\slic_data_dir() . '/.env.slic.run';
295295
$cli_name = basename( $argv[0] );
296296

297297
if ( 'help' !== $subcommand ) {

src/commands/composer.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
echo magenta( "Error: set-version requires a Composer version number, either 1 or 2." . PHP_EOL );
5959
exit( 1 );
6060
}
61-
$run_settings_file = root( '/.env.slic.run' );
61+
$run_settings_file = slic_data_dir() . '/.env.slic.run';
6262
write_env_file( $run_settings_file, [ 'SLIC_COMPOSER_VERSION' => (int) $version ], true );
6363
echo colorize( "Composer version set to $version\n" );
6464

@@ -69,7 +69,7 @@
6969
exit( slic_realtime()( [ 'exec', 'slic', $composer_bin, '--version' ] ) );
7070
case 'reset-version':
7171
$version = 2;
72-
$run_settings_file = root( '/.env.slic.run' );
72+
$run_settings_file = slic_data_dir() . '/.env.slic.run';
7373
write_env_file( $run_settings_file, [ 'SLIC_COMPOSER_VERSION' => (int) $version ], true );
7474
echo colorize( "Composer version reset to default: $default_version\n" );
7575

src/commands/php-version.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
}
6666

6767
// Read .env.slic.run to get runtime value.
68-
$run_env_file = root( '/.env.slic.run' );
68+
$run_env_file = slic_data_dir() . '/.env.slic.run';
6969
$runtime_version = null;
7070
if ( file_exists( $run_env_file ) ) {
7171
$run_env = read_env_file( $run_env_file );

src/commands/reset.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838

3939
quietly_tear_down_stack();
4040

41-
$run_settings_file = root( '/.env.slic.run' );
41+
$run_settings_file = slic_data_dir() . '/.env.slic.run';
4242
echo "Removing {$run_settings_file} ... ";
4343
echo ( ! file_exists( $run_settings_file ) || unlink( $run_settings_file ) ) ?
4444
light_cyan( 'done' )

src/commands/upgrade.php

Lines changed: 132 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,139 @@
1717
return;
1818
}
1919

20-
$today = date( 'Y-m-d' );
21-
chdir( SLIC_ROOT_DIR );
22-
$status = passthru( 'git checkout main && git pull' );
20+
if ( is_phar() ) {
21+
phar_self_update();
22+
} else {
23+
git_upgrade();
24+
}
25+
26+
/**
27+
* Upgrades slic via git pull on the main branch.
28+
*/
29+
function git_upgrade() {
30+
chdir( SLIC_ROOT_DIR );
31+
$status = passthru( 'git checkout main && git pull' );
2332

24-
if ( ! $status ) {
25-
unlink( SLIC_ROOT_DIR . '/.remote-version' );
33+
if ( ! $status ) {
34+
$remote_version_file = slic_data_dir() . '/.remote-version';
35+
if ( file_exists( $remote_version_file ) ) {
36+
unlink( $remote_version_file );
37+
}
2638

27-
$status = passthru( 'php slic update' );
39+
$status = passthru( PHP_BINARY . ' ' . escapeshellarg( $GLOBALS['argv'][0] ) . ' update' );
40+
}
41+
42+
exit( $status );
2843
}
2944

30-
exit( $status );
45+
/**
46+
* Self-updates the phar archive from GitHub Releases.
47+
*/
48+
function phar_self_update() {
49+
$current_version = CLI_VERSION;
50+
51+
echo colorize( "Checking for updates..." . PHP_EOL );
52+
53+
$latest_version = fetch_latest_github_release_version();
54+
55+
if ( null === $latest_version ) {
56+
echo magenta( "Error: Could not check for updates. Please try again later." . PHP_EOL );
57+
exit( 1 );
58+
}
59+
60+
if ( version_compare( $latest_version, $current_version, '<=' ) ) {
61+
echo light_cyan( "You are already running the latest version ({$current_version})." . PHP_EOL );
62+
exit( 0 );
63+
}
64+
65+
echo colorize( "Updating from <yellow>{$current_version}</yellow> to <yellow>{$latest_version}</yellow>..." . PHP_EOL );
66+
67+
// Fetch the release to get the slic.phar asset URL.
68+
$context = stream_context_create( [
69+
'http' => [
70+
'method' => 'GET',
71+
'header' => "User-Agent: slic-cli\r\nAccept: application/vnd.github.v3+json\r\n",
72+
'timeout' => 10,
73+
],
74+
] );
75+
76+
$response = @file_get_contents( 'https://api.github.com/repos/stellarwp/slic/releases/latest', false, $context );
77+
78+
if ( false === $response ) {
79+
echo magenta( "Error: Could not fetch release information." . PHP_EOL );
80+
exit( 1 );
81+
}
82+
83+
$release = json_decode( $response, true );
84+
85+
if ( ! is_array( $release ) || empty( $release['assets'] ) ) {
86+
echo magenta( "Error: No assets found in the latest release." . PHP_EOL );
87+
exit( 1 );
88+
}
89+
90+
// Find the slic.phar asset.
91+
$phar_url = null;
92+
foreach ( $release['assets'] as $asset ) {
93+
if ( $asset['name'] === 'slic.phar' ) {
94+
$phar_url = $asset['browser_download_url'];
95+
break;
96+
}
97+
}
98+
99+
if ( null === $phar_url ) {
100+
echo magenta( "Error: slic.phar not found in the latest release assets." . PHP_EOL );
101+
exit( 1 );
102+
}
103+
104+
// Download the new phar.
105+
$download_context = stream_context_create( [
106+
'http' => [
107+
'method' => 'GET',
108+
'header' => "User-Agent: slic-cli\r\n",
109+
'timeout' => 60,
110+
'follow_location' => true,
111+
],
112+
] );
113+
114+
$new_phar = @file_get_contents( $phar_url, false, $download_context );
115+
116+
if ( false === $new_phar || empty( $new_phar ) ) {
117+
echo magenta( "Error: Failed to download the new phar." . PHP_EOL );
118+
exit( 1 );
119+
}
120+
121+
// Get the path to the currently running phar.
122+
$phar_path = \Phar::running( false );
123+
124+
if ( empty( $phar_path ) ) {
125+
echo magenta( "Error: Could not determine the running phar path." . PHP_EOL );
126+
exit( 1 );
127+
}
128+
129+
// Write the new phar to a temp file first, then rename for atomicity.
130+
$tmp_path = $phar_path . '.tmp';
131+
132+
if ( false === file_put_contents( $tmp_path, $new_phar ) ) {
133+
echo magenta( "Error: Could not write the new phar to {$tmp_path}." . PHP_EOL );
134+
exit( 1 );
135+
}
136+
137+
// Make the temp file executable.
138+
chmod( $tmp_path, 0755 );
139+
140+
// Replace the old phar with the new one.
141+
if ( ! rename( $tmp_path, $phar_path ) ) {
142+
unlink( $tmp_path );
143+
echo magenta( "Error: Could not replace the phar at {$phar_path}." . PHP_EOL );
144+
exit( 1 );
145+
}
146+
147+
// Clear the cached remote version.
148+
$remote_version_file = slic_data_dir() . '/.remote-version';
149+
if ( file_exists( $remote_version_file ) ) {
150+
unlink( $remote_version_file );
151+
}
152+
153+
echo light_cyan( "Successfully updated slic to version {$latest_version}." . PHP_EOL );
154+
exit( 0 );
155+
}

0 commit comments

Comments
 (0)