├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── composer.json
├── composer.lock
├── index.htm
├── lib
├── ApiResponse.php
├── AppVeyor.php
├── ArtifactArchiver.php
├── ArtifactFileUtils.php
├── ArtifactManifest.php
├── CircleCI.php
├── Filesystem.php
├── GPG.php
├── GitHub.php
├── GitHubAuth.php
├── Jenkins.php
├── NPM.php
├── Release.php
├── SecureSign.php
├── Str.php
├── Version.php
└── config.php
├── logs
└── .gitkeep
├── nginx
├── _cors.conf
├── nightly.yarnpkg.com.conf
├── release.yarnpkg.com.conf
└── yarn.fyi.conf
├── nightly
├── artifacts
│ └── .gitkeep
├── bootstrap.php
├── deb-incoming
│ └── .gitkeep
└── public
│ ├── archive_appveyor.php
│ ├── archive_circleci.php
│ ├── builds.php
│ ├── footer.php
│ ├── header.php
│ ├── latest-version.php
│ ├── latest_redirect.php
│ ├── release_appveyor.php
│ ├── release_circleci.php
│ └── sign_releases.php
├── release
└── public
│ ├── index.php
│ ├── log.php
│ ├── login.php
│ ├── metrics.php
│ ├── promote_rc.php
│ ├── release_type.php
│ └── run_webhook.php
├── update-deb.sh
└── views
├── ErrorPage.php
└── PageLayout.php
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.sh eol=lf
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | temp/
2 | vendor/
3 | nightly/artifacts/*.deb
4 | nightly/artifacts/*.msi
5 | nightly/artifacts/*.tar.gz
6 | nightly/artifacts/*.rpm
7 | nightly/artifacts/yarn-*.js
8 | nightly/artifacts/*.asc
9 | logs/
10 | nightly/artifacts/latest.json
11 | nightly/deb-incoming/
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD License
2 |
3 | For Yarn software
4 |
5 | Copyright (c) 2016-present, Yarn Contributors. All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification,
8 | are permitted provided that the following conditions are met:
9 |
10 | * Redistributions of source code must retain the above copyright notice, this
11 | list of conditions and the following disclaimer.
12 |
13 | * Redistributions in binary form must reproduce the above copyright notice,
14 | this list of conditions and the following disclaimer in the documentation
15 | and/or other materials provided with the distribution.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
21 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
24 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Nightly Builds + Release Infrastructure
2 |
3 | This repo also contains infrastructure for releasing Yarn, both the stable release as well as nightly builds. This is hosted at https://nightly.yarnpkg.com/
4 |
5 | Available endpoints on `nightly.yarnpkg.com`:
6 |
7 | * `/archive_appveyor`: Archives all master builds from AppVeyor (https://ci.appveyor.com/project/kittens/yarn) onto the nightly builds site. Called as a webhook from the AppVeyor build
8 | * `/archive_circleci`: Archives all master builds from CircleCI (https://circleci.com/gh/yarnpkg/yarn) onto the nightly builds site. Called as a webhook from the CircleCI build
9 | * `/latest.json`: Contains the version numbers and URLs to all the latest nightly builds
10 | * `/latest.[type]` (eg. `/latest.tar.gz`, `/latest.msi`): Redirects to the latest nightly build of this type
11 | * `/latest-version`: Returns the version number of the latest nightly build
12 | * `/latest-[type]-version` (eg. `/latest-tar-version`, `/latest-msi-version`): Returns the version number of the latest nightly build containing a file of this format. This is useful because the Windows and Linux builds are performed separately, so the version number of the latest MSI may differ from the other version numbers.
13 | * `/[type]-builds` (eg. `/tar-builds`, `/msi-builds`): Returns a list of all the nightly builds available for this type. Used on the nightly builds page (https://yarnpkg.com/en/docs/nightly).
14 | * `/release_appveyor`: Handles stable release builds from AppVeyor. Grabs the MSI from AppVeyor, Authenticode signs it, then uploads it to the GitHub release. Called as a webhook from the AppVeyor build
15 | * `/release_circleci`: Similar to `release_appveyor`, except for CircleCI builds. Called a webhook from the CircleCI build
16 | * `/sign_releases`: GPG signs all `.tar.gz` and `.js` files for all GitHub releases, attaching the signatures as `.asc` files to the GitHub releases
17 |
18 | Directories in this repo:
19 | * `nginx`: Nginx configuration for `nightly.yarnpkg.com` and `yarn.fyi`
20 | * `api`: Publicly accessible endpoints for `nightly.yarnpkg.com`
21 | * `lib`: Libraries used by the release site
22 | * `config.php`: Contains all configuration for the release infra. Includes API tokens (AppVeyor, CircleCI, GitHub), GPG IDs to use when signing files, and path to the Authenticode key for signing the Windows installer.
23 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "require": {
3 | "guzzlehttp/guzzle": "~6.0",
4 | "analog/analog": "^1.0",
5 | "rize/uri-template": "^0.3.0",
6 | "jimdo/prometheus_client_php": "^0.9.1"
7 | },
8 | "autoload": {
9 | "classmap": ["lib/", "views/"]
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/composer.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_readme": [
3 | "This file locks the dependencies of your project to a known state",
4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5 | "This file is @generated automatically"
6 | ],
7 | "content-hash": "9465b3f37043f040f6bce19c9ad8ed23",
8 | "packages": [
9 | {
10 | "name": "analog/analog",
11 | "version": "1.0.10-stable",
12 | "source": {
13 | "type": "git",
14 | "url": "https://github.com/jbroadway/analog.git",
15 | "reference": "65dce6b5aff8c77307c783e607c04a35df8259d6"
16 | },
17 | "dist": {
18 | "type": "zip",
19 | "url": "https://api.github.com/repos/jbroadway/analog/zipball/65dce6b5aff8c77307c783e607c04a35df8259d6",
20 | "reference": "65dce6b5aff8c77307c783e607c04a35df8259d6",
21 | "shasum": ""
22 | },
23 | "require": {
24 | "php": ">=5.3.2",
25 | "psr/log": "1.*"
26 | },
27 | "type": "library",
28 | "autoload": {
29 | "psr-0": {
30 | "Analog": "lib/"
31 | }
32 | },
33 | "notification-url": "https://packagist.org/downloads/",
34 | "license": [
35 | "MIT"
36 | ],
37 | "authors": [
38 | {
39 | "name": "Johnny Broadway",
40 | "email": "johnny@johnnybroadway.com",
41 | "homepage": "http://www.johnnybroadway.com/"
42 | }
43 | ],
44 | "description": "PHP logging class that can be extended via closures. Includes several pre-built handlers including file, mail, syslog, HTTP post, and MongoDB.",
45 | "homepage": "https://github.com/jbroadway/analog",
46 | "keywords": [
47 | "alerts",
48 | "debug",
49 | "debugging",
50 | "error",
51 | "log",
52 | "logger",
53 | "logging",
54 | "syslog"
55 | ],
56 | "time": "2017-09-17T02:30:30+00:00"
57 | },
58 | {
59 | "name": "guzzlehttp/guzzle",
60 | "version": "6.3.2",
61 | "source": {
62 | "type": "git",
63 | "url": "https://github.com/guzzle/guzzle.git",
64 | "reference": "68d0ea14d5a3f42a20e87632a5f84931e2709c90"
65 | },
66 | "dist": {
67 | "type": "zip",
68 | "url": "https://api.github.com/repos/guzzle/guzzle/zipball/68d0ea14d5a3f42a20e87632a5f84931e2709c90",
69 | "reference": "68d0ea14d5a3f42a20e87632a5f84931e2709c90",
70 | "shasum": ""
71 | },
72 | "require": {
73 | "guzzlehttp/promises": "^1.0",
74 | "guzzlehttp/psr7": "^1.4",
75 | "php": ">=5.5"
76 | },
77 | "require-dev": {
78 | "ext-curl": "*",
79 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4",
80 | "psr/log": "^1.0"
81 | },
82 | "suggest": {
83 | "psr/log": "Required for using the Log middleware"
84 | },
85 | "type": "library",
86 | "extra": {
87 | "branch-alias": {
88 | "dev-master": "6.3-dev"
89 | }
90 | },
91 | "autoload": {
92 | "files": [
93 | "src/functions_include.php"
94 | ],
95 | "psr-4": {
96 | "GuzzleHttp\\": "src/"
97 | }
98 | },
99 | "notification-url": "https://packagist.org/downloads/",
100 | "license": [
101 | "MIT"
102 | ],
103 | "authors": [
104 | {
105 | "name": "Michael Dowling",
106 | "email": "mtdowling@gmail.com",
107 | "homepage": "https://github.com/mtdowling"
108 | }
109 | ],
110 | "description": "Guzzle is a PHP HTTP client library",
111 | "homepage": "http://guzzlephp.org/",
112 | "keywords": [
113 | "client",
114 | "curl",
115 | "framework",
116 | "http",
117 | "http client",
118 | "rest",
119 | "web service"
120 | ],
121 | "time": "2018-03-26T16:33:04+00:00"
122 | },
123 | {
124 | "name": "guzzlehttp/promises",
125 | "version": "v1.3.1",
126 | "source": {
127 | "type": "git",
128 | "url": "https://github.com/guzzle/promises.git",
129 | "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646"
130 | },
131 | "dist": {
132 | "type": "zip",
133 | "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646",
134 | "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646",
135 | "shasum": ""
136 | },
137 | "require": {
138 | "php": ">=5.5.0"
139 | },
140 | "require-dev": {
141 | "phpunit/phpunit": "^4.0"
142 | },
143 | "type": "library",
144 | "extra": {
145 | "branch-alias": {
146 | "dev-master": "1.4-dev"
147 | }
148 | },
149 | "autoload": {
150 | "psr-4": {
151 | "GuzzleHttp\\Promise\\": "src/"
152 | },
153 | "files": [
154 | "src/functions_include.php"
155 | ]
156 | },
157 | "notification-url": "https://packagist.org/downloads/",
158 | "license": [
159 | "MIT"
160 | ],
161 | "authors": [
162 | {
163 | "name": "Michael Dowling",
164 | "email": "mtdowling@gmail.com",
165 | "homepage": "https://github.com/mtdowling"
166 | }
167 | ],
168 | "description": "Guzzle promises library",
169 | "keywords": [
170 | "promise"
171 | ],
172 | "time": "2016-12-20T10:07:11+00:00"
173 | },
174 | {
175 | "name": "guzzlehttp/psr7",
176 | "version": "1.4.2",
177 | "source": {
178 | "type": "git",
179 | "url": "https://github.com/guzzle/psr7.git",
180 | "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c"
181 | },
182 | "dist": {
183 | "type": "zip",
184 | "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c",
185 | "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c",
186 | "shasum": ""
187 | },
188 | "require": {
189 | "php": ">=5.4.0",
190 | "psr/http-message": "~1.0"
191 | },
192 | "provide": {
193 | "psr/http-message-implementation": "1.0"
194 | },
195 | "require-dev": {
196 | "phpunit/phpunit": "~4.0"
197 | },
198 | "type": "library",
199 | "extra": {
200 | "branch-alias": {
201 | "dev-master": "1.4-dev"
202 | }
203 | },
204 | "autoload": {
205 | "psr-4": {
206 | "GuzzleHttp\\Psr7\\": "src/"
207 | },
208 | "files": [
209 | "src/functions_include.php"
210 | ]
211 | },
212 | "notification-url": "https://packagist.org/downloads/",
213 | "license": [
214 | "MIT"
215 | ],
216 | "authors": [
217 | {
218 | "name": "Michael Dowling",
219 | "email": "mtdowling@gmail.com",
220 | "homepage": "https://github.com/mtdowling"
221 | },
222 | {
223 | "name": "Tobias Schultze",
224 | "homepage": "https://github.com/Tobion"
225 | }
226 | ],
227 | "description": "PSR-7 message implementation that also provides common utility methods",
228 | "keywords": [
229 | "http",
230 | "message",
231 | "request",
232 | "response",
233 | "stream",
234 | "uri",
235 | "url"
236 | ],
237 | "time": "2017-03-20T17:10:46+00:00"
238 | },
239 | {
240 | "name": "jimdo/prometheus_client_php",
241 | "version": "v0.9.1",
242 | "source": {
243 | "type": "git",
244 | "url": "https://github.com/Jimdo/prometheus_client_php.git",
245 | "reference": "cf3ea93197400a97464ff9bee78cf2169f035327"
246 | },
247 | "dist": {
248 | "type": "zip",
249 | "url": "https://api.github.com/repos/Jimdo/prometheus_client_php/zipball/cf3ea93197400a97464ff9bee78cf2169f035327",
250 | "reference": "cf3ea93197400a97464ff9bee78cf2169f035327",
251 | "shasum": ""
252 | },
253 | "require": {
254 | "guzzlehttp/guzzle": "^6.2",
255 | "php": ">=5.6.3"
256 | },
257 | "require-dev": {
258 | "phpunit/phpunit": "4.1.0"
259 | },
260 | "suggest": {
261 | "ext-apc": "Required if using APCu.",
262 | "ext-redis": "Required if using Redis."
263 | },
264 | "type": "library",
265 | "autoload": {
266 | "psr-0": {
267 | "Prometheus\\": "src/"
268 | }
269 | },
270 | "notification-url": "https://packagist.org/downloads/",
271 | "license": [
272 | "Apache-2.0"
273 | ],
274 | "authors": [
275 | {
276 | "name": "Joscha",
277 | "email": "joscha@schnipseljagd.org"
278 | },
279 | {
280 | "name": "Jan Brauer",
281 | "email": "jan.brauer@jimdo.com"
282 | }
283 | ],
284 | "time": "2017-11-06T11:00:07+00:00"
285 | },
286 | {
287 | "name": "psr/http-message",
288 | "version": "1.0.1",
289 | "source": {
290 | "type": "git",
291 | "url": "https://github.com/php-fig/http-message.git",
292 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
293 | },
294 | "dist": {
295 | "type": "zip",
296 | "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
297 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
298 | "shasum": ""
299 | },
300 | "require": {
301 | "php": ">=5.3.0"
302 | },
303 | "type": "library",
304 | "extra": {
305 | "branch-alias": {
306 | "dev-master": "1.0.x-dev"
307 | }
308 | },
309 | "autoload": {
310 | "psr-4": {
311 | "Psr\\Http\\Message\\": "src/"
312 | }
313 | },
314 | "notification-url": "https://packagist.org/downloads/",
315 | "license": [
316 | "MIT"
317 | ],
318 | "authors": [
319 | {
320 | "name": "PHP-FIG",
321 | "homepage": "http://www.php-fig.org/"
322 | }
323 | ],
324 | "description": "Common interface for HTTP messages",
325 | "homepage": "https://github.com/php-fig/http-message",
326 | "keywords": [
327 | "http",
328 | "http-message",
329 | "psr",
330 | "psr-7",
331 | "request",
332 | "response"
333 | ],
334 | "time": "2016-08-06T14:39:51+00:00"
335 | },
336 | {
337 | "name": "psr/log",
338 | "version": "1.0.2",
339 | "source": {
340 | "type": "git",
341 | "url": "https://github.com/php-fig/log.git",
342 | "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d"
343 | },
344 | "dist": {
345 | "type": "zip",
346 | "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d",
347 | "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d",
348 | "shasum": ""
349 | },
350 | "require": {
351 | "php": ">=5.3.0"
352 | },
353 | "type": "library",
354 | "extra": {
355 | "branch-alias": {
356 | "dev-master": "1.0.x-dev"
357 | }
358 | },
359 | "autoload": {
360 | "psr-4": {
361 | "Psr\\Log\\": "Psr/Log/"
362 | }
363 | },
364 | "notification-url": "https://packagist.org/downloads/",
365 | "license": [
366 | "MIT"
367 | ],
368 | "authors": [
369 | {
370 | "name": "PHP-FIG",
371 | "homepage": "http://www.php-fig.org/"
372 | }
373 | ],
374 | "description": "Common interface for logging libraries",
375 | "homepage": "https://github.com/php-fig/log",
376 | "keywords": [
377 | "log",
378 | "psr",
379 | "psr-3"
380 | ],
381 | "time": "2016-10-10T12:19:37+00:00"
382 | },
383 | {
384 | "name": "rize/uri-template",
385 | "version": "0.3.2",
386 | "source": {
387 | "type": "git",
388 | "url": "https://github.com/rize/UriTemplate.git",
389 | "reference": "9e5fdd5c47147aa5adf7f760002ee591ed37b9ca"
390 | },
391 | "dist": {
392 | "type": "zip",
393 | "url": "https://api.github.com/repos/rize/UriTemplate/zipball/9e5fdd5c47147aa5adf7f760002ee591ed37b9ca",
394 | "reference": "9e5fdd5c47147aa5adf7f760002ee591ed37b9ca",
395 | "shasum": ""
396 | },
397 | "require": {
398 | "php": ">=5.3.0"
399 | },
400 | "require-dev": {
401 | "phpunit/phpunit": "~4.0.0"
402 | },
403 | "type": "library",
404 | "autoload": {
405 | "psr-0": {
406 | "Rize\\UriTemplate": "src/"
407 | }
408 | },
409 | "notification-url": "https://packagist.org/downloads/",
410 | "license": [
411 | "MIT"
412 | ],
413 | "authors": [
414 | {
415 | "name": "Marut K",
416 | "homepage": "http://twitter.com/rezigned"
417 | }
418 | ],
419 | "description": "PHP URI Template (RFC 6570) supports both expansion & extraction",
420 | "keywords": [
421 | "RFC 6570",
422 | "template",
423 | "uri"
424 | ],
425 | "time": "2017-06-14T03:57:53+00:00"
426 | }
427 | ],
428 | "packages-dev": [],
429 | "aliases": [],
430 | "minimum-stability": "stable",
431 | "stability-flags": [],
432 | "prefer-stable": false,
433 | "prefer-lowest": false,
434 | "platform": [],
435 | "platform-dev": []
436 | }
437 |
--------------------------------------------------------------------------------
/index.htm:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/lib/ApiResponse.php:
--------------------------------------------------------------------------------
1 | getMessage();
33 | Analog::warning('Could not open GitHub issue: '.$github_ex->getMessage());
34 | }
35 | }
36 | die();
37 | }
38 |
39 | /**
40 | * Once this is called, any errors returned to the client (either through
41 | * ApiResponse::error or through throwing an exception) will also open a
42 | * GitHub issue for the failure. $issue_creator should return the fields for
43 | * the GitHub issue API (title, body, etc.)
44 | */
45 | public static function enableCreateGitHubIssueOnError($issue_creator) {
46 | static::$githubIssueCallback = $issue_creator;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/lib/AppVeyor.php:
--------------------------------------------------------------------------------
1 | buildVersion;
22 | if (empty($build_version)) {
23 | ApiResponse::error('400', 'No build version found');
24 | }
25 |
26 | // Now, load this build from their API and revalidate it, to ensure it's legit
27 | // and the client isn't tricking us.
28 | $verified_build = static::call(
29 | 'projects/%s/%s/build/%s',
30 | Config::APPVEYOR_USERNAME,
31 | Config::APPVEYOR_PROJECT_SLUG,
32 | $build_version
33 | );
34 | static::validateBuild($verified_build->build, $verified_build->project);
35 | return $verified_build;
36 | }
37 |
38 | /**
39 | * Gets the most recent build for the specified tag.
40 | */
41 | public static function getBuildForTag($tag) {
42 | return static::call(
43 | 'projects/%s/%s/branch/%s',
44 | Config::APPVEYOR_USERNAME,
45 | Config::APPVEYOR_PROJECT_SLUG,
46 | $tag
47 | );
48 | }
49 |
50 | public static function getArtifactsForJob($job_id) {
51 | $artifacts = static::call('buildjobs/%s/artifacts', $job_id);
52 | foreach ($artifacts as $artifact) {
53 | $filename = basename($artifact->fileName);
54 | $urls[$filename] = sprintf(
55 | 'https://ci.appveyor.com/api/buildjobs/%s/artifacts/%s',
56 | $job_id,
57 | $artifact->fileName
58 | );
59 | }
60 | return $urls;
61 | }
62 |
63 | public static function validateBuild($build, $project) {
64 | if (
65 | (
66 | $build->branch !== Config::BRANCH &&
67 | !preg_match(Config::RELEASE_TAG_FORMAT, $build->branch)
68 | ) ||
69 | $project->repositoryName !== Config::ORG_NAME.'/'.Config::REPO_NAME
70 | ) {
71 | ApiResponse::sendAndLog(sprintf(
72 | '[#%s] Not archiving; this build is not on the correct branch: %s/%s',
73 | $build->version ?? $build->buildVersion,
74 | $project->repositoryName,
75 | $build->branch
76 | ));
77 | }
78 | if (!empty($build->pullRequestId)) {
79 | ApiResponse::sendAndLog(sprintf(
80 | '[#%s] Not archiving; this is a pull request (%s)',
81 | $build->version ?? $build->buildVersion,
82 | $build->pullRequestId
83 | ));
84 | }
85 | }
86 |
87 | public static function call(string $uri, ...$uri_args) {
88 | $client = new Client([
89 | 'base_uri' => 'https://ci.appveyor.com/api/',
90 | ]);
91 | $response = $client->get(vsprintf($uri, $uri_args));
92 | return json_decode((string)$response->getBody());
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/lib/ArtifactArchiver.php:
--------------------------------------------------------------------------------
1 | $url) {
18 | $file_handle = fopen($dir.$filename, 'w');
19 | $requests[$filename] = $artifact_client->getAsync($url, [
20 | 'sink' => $file_handle,
21 | ]);
22 | $file_handles[] = $file_handle;
23 | }
24 | $results = Promise\unwrap($requests);
25 |
26 | // Guzzle locks the download files, and GPG also tries to lock them :/
27 | // Easiest way to fix this is to explicitly close all the handles once
28 | // Guzzle is done with them.
29 | foreach ($file_handles as $file_handle) {
30 | try {
31 | fclose($file_handle);
32 | } catch (Exception $e) {
33 | // Ignore, maybe file was already closed.
34 | }
35 | }
36 | return $results;
37 | }
38 |
39 | /**
40 | * Archives a build locally. $artifacts is an array of filename => URL to
41 | * download the artifact from the build server.
42 | */
43 | public static function archiveBuild(array $artifacts, $build_identifier) {
44 | $output = '';
45 | $results = static::downloadArtifacts($artifacts, Config::ARTIFACT_PATH);
46 |
47 | // Update latest.json to point to the newest files
48 | $latest = ArtifactManifest::exists()
49 | ? ArtifactManifest::load()
50 | : (object)[];
51 | foreach ($artifacts as $filename => $_) {
52 | $output .= $filename.'... ';
53 | $full_path = Config::ARTIFACT_PATH.$filename;
54 |
55 | $file = new SplFileInfo($full_path);
56 | $metadata = ArtifactFileUtils::getMetadata($file);
57 | if (!$metadata) {
58 | unlink($full_path); // Scary!
59 | $output .= "Skipped (unknown type)\n";
60 | continue;
61 | }
62 |
63 | $latest->{$metadata['type']} = $metadata;
64 |
65 | // GPG sign the artifact
66 | $signature = GPG::sign($full_path, Config::GPG_NIGHTLY);
67 | file_put_contents($full_path.'.asc', $signature);
68 |
69 | // If it's a Debian package, also copy it to the incoming directory.
70 | // This is used to populate the Debian repository.
71 | if ($metadata['type'] === 'deb') {
72 | copy(
73 | Config::ARTIFACT_PATH.$filename,
74 | Config::DEBIAN_INCOMING_PATH.$filename
75 | );
76 | $output .= 'Queued for adding to Debian repo, ';
77 | }
78 |
79 | $output .= "Done.\n";
80 | }
81 | ArtifactManifest::save($latest);
82 |
83 | $output .= sprintf("\nArchiving of build %s completed!", $build_identifier);
84 | echo $output;
85 | Analog::info($output);
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/lib/ArtifactFileUtils.php:
--------------------------------------------------------------------------------
1 | legacy-)?v?(?P[0-9\.]+[-_](?P[0-9]+)\.(?P