Skip to content

Commit 70f9094

Browse files
authored
Merge pull request #244 from laravel/improve-windows-support
feat: add windows to tests CI check
2 parents 7acc03d + 0eea496 commit 70f9094

File tree

6 files changed

+115
-106
lines changed

6 files changed

+115
-106
lines changed

.github/workflows/tests.yml

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,27 +14,36 @@ permissions:
1414

1515
jobs:
1616
tests:
17-
runs-on: ubuntu-22.04
17+
runs-on: ${{ matrix.os }}
18+
defaults:
19+
run:
20+
shell: bash
1821

1922
strategy:
2023
fail-fast: true
2124
matrix:
25+
os: [ ubuntu-22.04, windows-latest ]
2226
php: [ 8.2, 8.3, 8.4 ]
2327
laravel: [ 11, 12 ]
2428

25-
name: PHP ${{ matrix.php }} - Laravel ${{ matrix.laravel }}
29+
name: PHP ${{ matrix.php }} - Laravel ${{ matrix.laravel }} - ${{ matrix.os }}
2630

2731
steps:
32+
- name: Set git to use LF
33+
run: |
34+
git config --global core.autocrlf false
35+
git config --global core.eol lf
36+
2837
- name: Checkout code
2938
uses: actions/checkout@v4
3039

3140
- name: Setup PHP
3241
uses: shivammathur/setup-php@v2
3342
with:
3443
php-version: ${{ matrix.php }}
35-
extensions: dom, curl, libxml, mbstring, zip
44+
extensions: dom, curl, libxml, mbstring, zip, fileinfo, pdo, sqlite, pdo_sqlite
3645
ini-values: error_reporting=E_ALL
37-
tools: composer:v2
46+
tools: composer:v2.8
3847
coverage: none
3948

4049
- name: Setup SSH Keys
@@ -46,32 +55,41 @@ jobs:
4655
4756
- name: Install dependencies
4857
run: |
49-
composer update --prefer-dist --no-interaction --no-progress --with="illuminate/contracts:^${{ matrix.laravel }}"
58+
composer update --prefer-dist --no-interaction --no-progress --with='illuminate/contracts:^${{ matrix.laravel }}'
5059
5160
- name: Execute tests
5261
run: vendor/bin/pest
5362
test-l10:
54-
runs-on: ubuntu-22.04
63+
runs-on: ${{ matrix.os }}
64+
defaults:
65+
run:
66+
shell: bash
5567

5668
strategy:
5769
fail-fast: true
5870
matrix:
71+
os: [ ubuntu-22.04, windows-latest ]
5972
php: [ 8.1, 8.2 ]
6073
laravel: [ 10 ]
6174

62-
name: PHP ${{ matrix.php }} - Laravel ${{ matrix.laravel }}
75+
name: PHP ${{ matrix.php }} - Laravel ${{ matrix.laravel }} - ${{ matrix.os }}
6376

6477
steps:
78+
- name: Set git to use LF
79+
run: |
80+
git config --global core.autocrlf false
81+
git config --global core.eol lf
82+
6583
- name: Checkout code
6684
uses: actions/checkout@v4
6785

6886
- name: Setup PHP
6987
uses: shivammathur/setup-php@v2
7088
with:
7189
php-version: ${{ matrix.php }}
72-
extensions: dom, curl, libxml, mbstring, zip
90+
extensions: dom, curl, libxml, mbstring, zip, fileinfo, pdo, sqlite, pdo_sqlite
7391
ini-values: error_reporting=E_ALL
74-
tools: composer:v2
92+
tools: composer:v2.8
7593
coverage: none
7694

7795
- name: Setup SSH Keys
@@ -83,7 +101,7 @@ jobs:
83101
84102
- name: Install dependencies
85103
run: |
86-
composer update --prefer-dist --no-interaction --no-progress --with="illuminate/contracts:^${{ matrix.laravel }}"
104+
composer update --prefer-dist --no-interaction --no-progress --with="illuminate/contracts:^${{ matrix.laravel }}.0"
87105
88106
- name: Execute tests
89107
run: vendor/bin/pest

src/Install/GuidelineComposer.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,8 @@ protected function guidelinePath(string $path): ?string
296296
}
297297

298298
// The path is not a custom guideline, check if the user has an override for this
299-
$relativePath = ltrim(str_replace([realpath(__DIR__.'/../../'), '.ai/'], '', $path), '/');
299+
$basePath = realpath(__DIR__.'/../../');
300+
$relativePath = ltrim(str_replace([$basePath, '.ai'.DIRECTORY_SEPARATOR, '.ai/'], '', $path), '/\\');
300301
$customPath = $this->prependUserGuidelinePath($relativePath);
301302

302303
return file_exists($customPath) ? $customPath : $path;

src/Install/Mcp/FileWriter.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,9 @@ protected function generateServerJson(string $key, array $serverConfig, int $bas
188188
{
189189
$json = json_encode($serverConfig, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
190190

191+
// Normalize line endings to Unix style
192+
$json = str_replace("\r\n", "\n", $json);
193+
191194
// If no indentation needed, return as-is
192195
if (empty($baseIndent)) {
193196
return '"'.$key.'": '.$json;
@@ -380,6 +383,11 @@ protected function writeJsonConfig(array $config): bool
380383
{
381384
$json = json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
382385

386+
// Normalize line endings to Unix style
387+
if ($json) {
388+
$json = str_replace("\r\n", "\n", $json);
389+
}
390+
383391
return $json && $this->writeFile($json);
384392
}
385393

tests/Feature/Console/InstallCommandMultiselectTest.php

Lines changed: 74 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -2,110 +2,91 @@
22

33
declare(strict_types=1);
44

5-
namespace Tests\Feature\Console;
6-
75
use Laravel\Prompts\Key;
86
use Laravel\Prompts\Prompt;
9-
use Tests\TestCase;
107

11-
class InstallCommandMultiselectTest extends TestCase
12-
{
13-
/**
14-
* Test that multiselect returns keys when given an associative array.
15-
*/
16-
public function test_multiselect_returns_keys_for_associative_array(): void
17-
{
18-
// Mock the prompt to simulate user selecting options
19-
// Note: mcp_server is already selected by default, so we don't toggle it
20-
Prompt::fake([
21-
Key::DOWN, // Move to second option (ai_guidelines)
22-
Key::SPACE, // Select second option
23-
Key::ENTER, // Submit
24-
]);
8+
test('multiselect returns keys for associative array', function () {
9+
// Mock the prompt to simulate user selecting options
10+
// Note: mcp_server is already selected by default, so we don't toggle it
11+
Prompt::fake([
12+
Key::DOWN, // Move to second option (ai_guidelines)
13+
Key::SPACE, // Select second option
14+
Key::ENTER, // Submit
15+
]);
2516

26-
$result = \Laravel\Prompts\multiselect(
27-
label: 'What shall we install?',
28-
options: [
29-
'mcp_server' => 'Boost MCP Server',
30-
'ai_guidelines' => 'Package AI Guidelines',
31-
'style_guidelines' => 'Laravel Style AI Guidelines',
32-
],
33-
default: ['mcp_server']
34-
);
17+
$result = \Laravel\Prompts\multiselect(
18+
label: 'What shall we install?',
19+
options: [
20+
'mcp_server' => 'Boost MCP Server',
21+
'ai_guidelines' => 'Package AI Guidelines',
22+
'style_guidelines' => 'Laravel Style AI Guidelines',
23+
],
24+
default: ['mcp_server']
25+
);
3526

36-
// Assert that we get the keys, not the values
37-
$this->assertIsArray($result);
38-
$this->assertCount(2, $result, 'Should have 2 items selected');
39-
$this->assertContains('mcp_server', $result);
40-
$this->assertContains('ai_guidelines', $result);
41-
$this->assertNotContains('Boost MCP Server', $result);
42-
$this->assertNotContains('Package AI Guidelines', $result);
43-
}
27+
// Assert that we get the keys, not the values
28+
expect($result)->toBeArray();
29+
expect($result)->toHaveCount(2, 'Should have 2 items selected');
30+
expect($result)->toContain('mcp_server');
31+
expect($result)->toContain('ai_guidelines');
32+
expect($result)->not->toContain('Boost MCP Server');
33+
expect($result)->not->toContain('Package AI Guidelines');
34+
})->skipOnWindows();
4435

45-
/**
46-
* Test multiselect with numeric indexed array returns values.
47-
*/
48-
public function test_multiselect_returns_values_for_indexed_array(): void
49-
{
50-
Prompt::fake([
51-
Key::SPACE, // Select first option
52-
Key::DOWN, // Move to second option
53-
Key::SPACE, // Select second option
54-
Key::ENTER, // Submit
55-
]);
36+
test('multiselect returns values for indexed array', function () {
37+
Prompt::fake([
38+
Key::SPACE, // Select first option
39+
Key::DOWN, // Move to second option
40+
Key::SPACE, // Select second option
41+
Key::ENTER, // Submit
42+
]);
5643

57-
$result = \Laravel\Prompts\multiselect(
58-
label: 'Select options',
59-
options: ['Option 1', 'Option 2', 'Option 3'],
60-
default: []
61-
);
44+
$result = \Laravel\Prompts\multiselect(
45+
label: 'Select options',
46+
options: ['Option 1', 'Option 2', 'Option 3'],
47+
default: []
48+
);
6249

63-
// For indexed arrays, it returns the actual values
64-
$this->assertIsArray($result);
65-
$this->assertContains('Option 1', $result);
66-
$this->assertContains('Option 2', $result);
67-
}
50+
// For indexed arrays, it returns the actual values
51+
expect($result)->toBeArray();
52+
expect($result)->toContain('Option 1');
53+
expect($result)->toContain('Option 2');
54+
})->skipOnWindows();
6855

69-
/**
70-
* Test that demonstrates multiselect behavior is consistent with InstallCommand expectations.
71-
* This ensures Laravel 10/11 compatibility.
72-
*/
73-
public function test_multiselect_behavior_matches_install_command_expectations(): void
74-
{
75-
// Test the exact same structure used in InstallCommand::selectBoostFeatures()
76-
// Note: mcp_server and ai_guidelines are already selected by default
77-
Prompt::fake([
78-
Key::DOWN, // Move to ai_guidelines (already selected)
79-
Key::DOWN, // Move to style_guidelines
80-
Key::SPACE, // Select style_guidelines
81-
Key::ENTER, // Submit
82-
]);
56+
test('multiselect behavior matches install command expectations', function () {
57+
// Test the exact same structure used in InstallCommand::selectBoostFeatures()
58+
// Note: mcp_server and ai_guidelines are already selected by default
59+
Prompt::fake([
60+
Key::DOWN, // Move to ai_guidelines (already selected)
61+
Key::DOWN, // Move to style_guidelines
62+
Key::SPACE, // Select style_guidelines
63+
Key::ENTER, // Submit
64+
]);
8365

84-
$toInstallOptions = [
85-
'mcp_server' => 'Boost MCP Server',
86-
'ai_guidelines' => 'Package AI Guidelines (i.e. Framework, Inertia, Pest)',
87-
'style_guidelines' => 'Laravel Style AI Guidelines',
88-
];
66+
$toInstallOptions = [
67+
'mcp_server' => 'Boost MCP Server',
68+
'ai_guidelines' => 'Package AI Guidelines (i.e. Framework, Inertia, Pest)',
69+
'style_guidelines' => 'Laravel Style AI Guidelines',
70+
];
8971

90-
$result = \Laravel\Prompts\multiselect(
91-
label: 'What shall we install?',
92-
options: $toInstallOptions,
93-
default: ['mcp_server', 'ai_guidelines'],
94-
required: true,
95-
hint: 'Style guidelines are best for new projects',
96-
);
72+
$result = \Laravel\Prompts\multiselect(
73+
label: 'What shall we install?',
74+
options: $toInstallOptions,
75+
default: ['mcp_server', 'ai_guidelines'],
76+
required: true,
77+
hint: 'Style guidelines are best for new projects',
78+
);
9779

98-
// Verify we get keys that can be used with in_array checks
99-
$this->assertIsArray($result);
100-
$this->assertCount(3, $result); // All 3 selected (2 default + 1 added)
80+
// Verify we get keys that can be used with in_array checks
81+
expect($result)->toBeArray();
82+
expect($result)->toHaveCount(3); // All 3 selected (2 default + 1 added)
10183

102-
// These are the exact checks used in InstallCommand
103-
$this->assertTrue(in_array('mcp_server', $result, true));
104-
$this->assertTrue(in_array('ai_guidelines', $result, true));
105-
$this->assertTrue(in_array('style_guidelines', $result, true));
84+
// These are the exact checks used in InstallCommand
85+
expect(in_array('mcp_server', $result, true))->toBeTrue();
86+
expect(in_array('ai_guidelines', $result, true))->toBeTrue();
87+
expect(in_array('style_guidelines', $result, true))->toBeTrue();
10688

107-
// Verify it doesn't contain the display values
108-
$this->assertFalse(in_array('Boost MCP Server', $result, true));
109-
$this->assertFalse(in_array('Package AI Guidelines (i.e. Framework, Inertia, Pest)', $result, true));
110-
}
111-
}
89+
// Verify it doesn't contain the display values
90+
expect(in_array('Boost MCP Server', $result, true))->toBeFalse();
91+
expect(in_array('Package AI Guidelines (i.e. Framework, Inertia, Pest)', $result, true))->toBeFalse();
92+
})->skipOnWindows();

tests/Feature/Mcp/Tools/DatabaseSchemaTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
}
2323

2424
// Build a throw-away table that we expect in the dump.
25+
Schema::dropIfExists('examples');
2526
Schema::create('examples', function (Blueprint $table) {
2627
$table->id();
2728
$table->string('name');

tests/Unit/Install/GuidelineWriterTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747

4848
expect(fn () => $writer->write('test guidelines'))
4949
->toThrow(RuntimeException::class, 'Failed to create directory: /root/boost_test');
50-
});
50+
})->skipOnWindows();
5151

5252
test('it writes guidelines to new file', function () {
5353
$tempFile = tempnam(sys_get_temp_dir(), 'boost_test_');
@@ -150,7 +150,7 @@
150150

151151
expect(fn () => $writer->write('test guidelines'))
152152
->toThrow(RuntimeException::class, "Failed to open file: {$dirPath}");
153-
});
153+
})->skipOnWindows();
154154

155155
test('it preserves file content structure with proper spacing', function () {
156156
$tempFile = tempnam(sys_get_temp_dir(), 'boost_test_');

0 commit comments

Comments
 (0)