1<?php
2
3// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0.
4// See the LICENCE file in the repository root for full licence text.
5
6namespace App\Models;
7
8use App\Libraries\Commentable;
9use Carbon\Carbon;
10
11/**
12 * @property bool $allow_bancho
13 * @property int $allow_ranking
14 * @property int $build_id
15 * @property \Illuminate\Database\Eloquent\Collection $changelogEntries ChangelogEntry
16 * @property \Illuminate\Database\Eloquent\Collection $changelogs Changelog
17 * @property string $commentable_identifier
18 * @property Comment $comments
19 * @property \Carbon\Carbon $date
20 * @property mixed|null $hash
21 * @property mixed|null $last_hash
22 * @property \Illuminate\Database\Eloquent\Collection $propagationHistories BuildPropagationHistory
23 * @property int|null $stream_id
24 * @property int $test_build
25 * @property UpdateStream $updateStream
26 * @property int $users
27 * @property string|null $version
28 */
29class Build extends Model implements Commentable
30{
31 use Traits\CommentableDefaults;
32
33 public $timestamps = false;
34
35 protected $table = 'osu_builds';
36 protected $primaryKey = 'build_id';
37
38 protected $casts = [
39 'allow_bancho' => 'boolean',
40 'date' => 'datetime',
41 ];
42
43 private $cache = [];
44
45 public static function importFromGithubNewRelease($data)
46 {
47 $repository = Repository::where([
48 'name' => $data['repository']['full_name'],
49 'build_on_release' => true,
50 ])->first();
51
52 // abort on unknown or non-auto build repository
53 if ($repository === null) {
54 return;
55 }
56
57 $tag = explode('-', $data['release']['tag_name']);
58 $version = $tag[0];
59 $streamName = $tag[1] ?? null;
60
61 if ($streamName !== null) {
62 $stream = UpdateStream::where('name', '=', $streamName)->first();
63 }
64
65 if (!isset($stream)) {
66 $stream = $repository->mainUpdateStream;
67 }
68
69 if (!isset($stream)) {
70 return;
71 }
72
73 $build = $stream->builds()->firstOrCreate([
74 'version' => $version,
75 ]);
76
77 $lastChange = Carbon::parse($data['release']['created_at']);
78
79 $changelogEntry = new ChangelogEntry();
80
81 $newChangelogEntryIds = $stream
82 ->changelogEntries()
83 ->orphans($stream->getKey())
84 ->where($changelogEntry->qualifyColumn('created_at'), '<=', $lastChange)
85 ->pluck($changelogEntry->qualifyColumn('id'));
86
87 $build->changelogEntries()->attach($newChangelogEntryIds);
88
89 return $build;
90 }
91
92 public function updateStream()
93 {
94 return $this->belongsTo(UpdateStream::class, 'stream_id', 'stream_id');
95 }
96
97 // FIXME: Need to match stream_id as well. It's currently checked in transformer.
98 public function changelogs()
99 {
100 return $this->hasMany(Changelog::class, 'build', 'version');
101 }
102
103 public function defaultChangelogs()
104 {
105 return $this->changelogs()->default();
106 }
107
108 public function changelogEntries()
109 {
110 return $this->belongsToMany(ChangelogEntry::class, null, 'build_id');
111 }
112
113 public function defaultChangelogEntries()
114 {
115 return $this->changelogEntries()->default();
116 }
117
118 public function scopeDefault($query)
119 {
120 $query->whereIn('stream_id', $GLOBALS['cfg']['osu']['changelog']['update_streams']);
121 }
122
123 public function propagationHistories()
124 {
125 return $this->hasMany(BuildPropagationHistory::class);
126 }
127
128 public function scopePropagationHistory($query)
129 {
130 $query->default()->where('allow_bancho', true);
131 }
132
133 public function scopeSearch($query, $params)
134 {
135 if (isset($params['stream'])) {
136 $stream = UpdateStream::where('name', '=', $params['stream'])->first();
137
138 if ($stream === null) {
139 return $query->none();
140 }
141
142 $query->where('stream_id', '=', $stream->getKey());
143 } else {
144 $stream = null;
145 }
146
147 if (isset($params['from'])) {
148 $query->where('build_id', '>=', function ($q) use ($params, $stream) {
149 $q->from($this->getTable())
150 ->where('version', '=', $params['from'])
151 ->select('build_id')
152 ->limit(1);
153
154 if ($stream !== null) {
155 $q->where('stream_id', '=', $stream->getKey());
156 }
157 });
158 }
159
160 if (isset($params['to'])) {
161 $query->where('build_id', '<=', function ($q) use ($params, $stream) {
162 $q->from($this->getTable())
163 ->where('version', '=', $params['to'])
164 ->select('build_id')
165 ->limit(1);
166
167 if ($stream !== null) {
168 $q->where('stream_id', '=', $stream->getKey());
169 }
170 });
171 }
172
173 if (isset($params['max_id'])) {
174 $query->where('build_id', '<=', $params['max_id']);
175 }
176
177 if (isset($params['limit'])) {
178 $query->limit($params['limit']);
179 }
180 }
181
182 public function commentLocked(): bool
183 {
184 return false;
185 }
186
187 public function commentableTitle()
188 {
189 if ($this->stream_id === null || $this->updateStream === null) {
190 return $this->displayVersion();
191 }
192
193 return "{$this->updateStream->pretty_name} {$this->displayVersion()}";
194 }
195
196 public function notificationCover()
197 {
198 // no image
199 }
200
201 public function platform(): string
202 {
203 $version = $this->version;
204 $suffixPos = strpos($version, '-');
205
206 return $suffixPos === false
207 ? ''
208 : substr($version, $suffixPos + 1);
209 }
210
211 public function url()
212 {
213 return build_url($this);
214 }
215
216 public function versionNext()
217 {
218 if (!array_key_exists('versionNext', $this->cache)) {
219 $this->cache['versionNext'] = static
220 ::default()
221 ->where('build_id', '>', $this->build_id)
222 ->where('stream_id', $this->stream_id)
223 ->orderBy('build_id', 'ASC')
224 ->first();
225 }
226
227 return $this->cache['versionNext'];
228 }
229
230 public function versionPrevious()
231 {
232 if (!array_key_exists('versionPrevious', $this->cache)) {
233 $this->cache['versionPrevious'] = static
234 ::default()
235 ->where('build_id', '<', $this->build_id)
236 ->where('stream_id', $this->stream_id)
237 ->orderBy('build_id', 'DESC')
238 ->first();
239 }
240
241 return $this->cache['versionPrevious'];
242 }
243
244 public function displayVersion()
245 {
246 return preg_replace('#[^0-9.]#', '', $this->version);
247 }
248}