Skip to content

Commit 3ac3439

Browse files
simonhampclaude
andauthored
Pull mobile v3 release notes from GitHub for releases after v3.1 (#389)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 4d5bb5a commit 3ac3439

3 files changed

Lines changed: 151 additions & 7 deletions

File tree

app/Support/GitHub.php

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ class GitHub
1717

1818
public const PACKAGE_PHP_BIN = 'nativephp/php-bin';
1919

20+
public const PACKAGE_MOBILE_AIR = 'nativephp/mobile-air';
21+
2022
public function __construct(
2123
private string $package
2224
) {}
@@ -43,6 +45,11 @@ public static function phpBin(): static
4345
return new static(static::PACKAGE_PHP_BIN);
4446
}
4547

48+
public static function mobileAir(): static
49+
{
50+
return new static(static::PACKAGE_MOBILE_AIR);
51+
}
52+
4653
public function latestVersion()
4754
{
4855
$release = Cache::remember(
@@ -63,6 +70,15 @@ public function releases(): Collection
6370
) ?? collect();
6471
}
6572

73+
public function releasesAfter(string $version): Collection
74+
{
75+
$version = ltrim($version, 'v');
76+
77+
return $this->releases()->filter(
78+
fn (Release $release) => version_compare(ltrim((string) $release->tag_name, 'v'), $version, '>')
79+
)->values();
80+
}
81+
6682
private function fetchLatestVersion(): ?Release
6783
{
6884
// Make a request to GitHub
@@ -83,14 +99,24 @@ private function getCacheKey(string $string): string
8399

84100
private function fetchReleases(): ?Collection
85101
{
86-
// Make a request to GitHub
87-
$response = Http::get('https://api.github.com/repos/'.$this->package.'/releases');
102+
$releases = collect();
103+
$page = 1;
88104

89-
// Check if the request was successful
90-
if ($response->failed()) {
91-
return collect();
92-
}
105+
do {
106+
$response = Http::get('https://api.github.com/repos/'.$this->package.'/releases', [
107+
'per_page' => 100,
108+
'page' => $page,
109+
]);
110+
111+
if ($response->failed()) {
112+
return $releases;
113+
}
114+
115+
$pageReleases = $response->json();
116+
$releases = $releases->concat($pageReleases);
117+
$page++;
118+
} while (count($pageReleases) === 100);
93119

94-
return collect($response->json())->map(fn (array $release) => new Release($release));
120+
return $releases->map(fn (array $release) => new Release($release));
95121
}
96122
}

resources/views/docs/mobile/3/getting-started/changelog.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@ order: 2
55

66
For changes prior to v3, see the [v2 documentation](/docs/mobile/2/getting-started/changelog).
77

8+
@forelse (\App\Support\GitHub::mobileAir()->releasesAfter('3.1.0') as $release)
9+
## {{ $release->name ?: $release->tag_name }}
10+
**Released: {{ \Carbon\Carbon::parse($release->published_at)->format('F j, Y') }}**
11+
12+
{{ $release->getBodyForMarkdown() }}
13+
---
14+
@empty
15+
@endforelse
16+
817
## v3.1 — Persistent Runtime & Performance
918

1019
### New Features
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<?php
2+
3+
namespace Tests\Feature\Support;
4+
5+
use App\Support\GitHub;
6+
use Illuminate\Support\Facades\Cache;
7+
use Illuminate\Support\Facades\Http;
8+
use Tests\TestCase;
9+
10+
class GitHubTest extends TestCase
11+
{
12+
protected function setUp(): void
13+
{
14+
parent::setUp();
15+
16+
Cache::flush();
17+
}
18+
19+
public function test_mobile_air_fetches_releases_from_the_correct_repo(): void
20+
{
21+
Http::fake([
22+
'api.github.com/repos/nativephp/mobile-air/releases*' => Http::response([
23+
[
24+
'id' => 1,
25+
'name' => 'v3.2.0',
26+
'tag_name' => 'v3.2.0',
27+
'body' => 'New release',
28+
'published_at' => '2026-05-01T00:00:00Z',
29+
],
30+
]),
31+
]);
32+
33+
$releases = GitHub::mobileAir()->releases();
34+
35+
$this->assertCount(1, $releases);
36+
$this->assertEquals('v3.2.0', $releases->first()->tag_name);
37+
38+
Http::assertSent(fn ($request) => str_starts_with($request->url(), 'https://api.github.com/repos/nativephp/mobile-air/releases?'));
39+
}
40+
41+
public function test_releases_paginates_through_every_page(): void
42+
{
43+
$firstPage = array_map(
44+
fn (int $i) => ['id' => $i, 'tag_name' => "v1.{$i}.0", 'name' => "v1.{$i}.0", 'body' => '', 'published_at' => '2026-01-01T00:00:00Z'],
45+
range(1, 100),
46+
);
47+
$secondPage = [
48+
['id' => 101, 'tag_name' => 'v0.9.0', 'name' => 'v0.9.0', 'body' => '', 'published_at' => '2025-12-01T00:00:00Z'],
49+
];
50+
51+
$sequence = Http::sequence()
52+
->push($firstPage)
53+
->push($secondPage);
54+
55+
Http::fake([
56+
'api.github.com/repos/nativephp/mobile-air/releases*' => $sequence,
57+
]);
58+
59+
$releases = GitHub::mobileAir()->releases();
60+
61+
$this->assertCount(101, $releases);
62+
$this->assertEquals('v0.9.0', $releases->last()->tag_name);
63+
Http::assertSentCount(2);
64+
}
65+
66+
public function test_releases_after_filters_out_versions_at_or_below_the_given_version(): void
67+
{
68+
Http::fake([
69+
'api.github.com/repos/nativephp/mobile-air/releases*' => Http::response([
70+
['id' => 1, 'tag_name' => 'v4.0.0', 'name' => 'v4.0.0', 'body' => '', 'published_at' => '2026-06-01T00:00:00Z'],
71+
['id' => 2, 'tag_name' => 'v3.2.0', 'name' => 'v3.2.0', 'body' => '', 'published_at' => '2026-05-01T00:00:00Z'],
72+
['id' => 3, 'tag_name' => 'v3.1.1', 'name' => 'v3.1.1', 'body' => '', 'published_at' => '2026-04-15T00:00:00Z'],
73+
['id' => 4, 'tag_name' => 'v3.1.0', 'name' => 'v3.1.0', 'body' => '', 'published_at' => '2026-04-01T00:00:00Z'],
74+
['id' => 5, 'tag_name' => 'v3.0.0', 'name' => 'v3.0.0', 'body' => '', 'published_at' => '2026-03-01T00:00:00Z'],
75+
]),
76+
]);
77+
78+
$releases = GitHub::mobileAir()->releasesAfter('3.1.0');
79+
80+
$this->assertEquals(
81+
['v4.0.0', 'v3.2.0', 'v3.1.1'],
82+
$releases->pluck('tag_name')->all()
83+
);
84+
}
85+
86+
public function test_releases_after_handles_tag_names_without_a_leading_v(): void
87+
{
88+
Http::fake([
89+
'api.github.com/repos/nativephp/mobile-air/releases*' => Http::response([
90+
['id' => 1, 'tag_name' => '3.2.0', 'name' => '3.2.0', 'body' => '', 'published_at' => '2026-05-01T00:00:00Z'],
91+
['id' => 2, 'tag_name' => '3.1.0', 'name' => '3.1.0', 'body' => '', 'published_at' => '2026-04-01T00:00:00Z'],
92+
]),
93+
]);
94+
95+
$releases = GitHub::mobileAir()->releasesAfter('v3.1.0');
96+
97+
$this->assertCount(1, $releases);
98+
$this->assertEquals('3.2.0', $releases->first()->tag_name);
99+
}
100+
101+
public function test_releases_after_returns_empty_collection_when_request_fails(): void
102+
{
103+
Http::fake([
104+
'api.github.com/repos/nativephp/mobile-air/releases*' => Http::response(null, 500),
105+
]);
106+
107+
$this->assertTrue(GitHub::mobileAir()->releasesAfter('3.1.0')->isEmpty());
108+
}
109+
}

0 commit comments

Comments
 (0)