Skip to content

Commit a53c639

Browse files
authored
Merge pull request #489 from ziglang/mirrors
Mirrors!
2 parents ca30d0e + c0b8db3 commit a53c639

File tree

16 files changed

+898
-4
lines changed

16 files changed

+898
-4
lines changed

.github/workflows/check-mirrors.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Check Mirrors
2+
on:
3+
schedule: [{ cron: "0 22 * * *" }]
4+
workflow_dispatch: null
5+
pull_request: { paths: ["assets/community-mirrors.ziggy"] }
6+
jobs:
7+
check:
8+
runs-on: [self-hosted, website]
9+
steps:
10+
- uses: actions/checkout@v2
11+
- name: Check Mirrors
12+
run: |
13+
cd check-mirrors
14+
/home/ci/deps/zig-linux-x86_64-0.14.0/zig build run -- ../assets/community-mirrors.ziggy "$GITHUB_STEP_SUMMARY"
15+
notify-failure:
16+
runs-on: [self-hosted, website]
17+
needs: [check]
18+
if: ${{ always() && (needs.check.result == 'failure' || needs.check.result == 'timed_out') }}
19+
steps:
20+
- name: Send Notification
21+
env:
22+
PUSHOVER_USER: ${{ secrets.MIRROR_CHECK_PUSHOVER_USER }}
23+
PUSHOVER_TOKEN: ${{ secrets.MIRROR_CHECK_PUSHOVER_TOKEN }}
24+
run: curl -s
25+
-F user="$PUSHOVER_USER"
26+
-F token="$PUSHOVER_TOKEN"
27+
-F message="Zig download mirror validation failed"
28+
https://api.pushover.net/1/messages.json

MIRRORS.md

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
> [!NOTE]
2+
> Are you trying to download Zig from a mirror? If so, you're in the wrong place! This documentation is for anyone who wants to *host* a community mirror.
3+
> For instructions on *using* the mirrors, check out the [Community Mirrors](https://ziglang.org/download/community-mirrors/) page on the Zig website.
4+
5+
# Community Mirrors
6+
7+
The Zig mirrors in this repository are all community-maintained and unaffiliated with the ZSF. Anyone is welcome to host their own mirror and add it to the
8+
list. Because Zig's tarballs are cryptographically signed, no mirror need be trusted. Of course, if a mirror is found to be malicious, it will be removed from
9+
the list.
10+
11+
The following rules define how a mirror works and the requirements it is expected to fulfil.
12+
13+
* A mirror provides a single base URL, which we will call `X`.
14+
* `X` **may** include a path component, but is not required to. For instance, `https://foo.bar/zig/` is okay,
15+
as is `https://zig.baz.qux/`.
16+
* The mirror **must** support HTTPS with a valid signed certificate. `X` **must** start with `https://`.
17+
* The mirror **must** cache tarballs locally. For instance, it may not simply forward all requests to another mirror.
18+
* The mirror **may** routinely evict its local tarball caches based on any reasonable factor, such as age, access frequency, or the existence of newer versions. This does not affect whether the mirror may respond with 404 Not Found for requests to these files (see below).
19+
* The mirror **must** download tarballs from `https://ziglang.org/` or a valid mirror of it.
20+
* Typically, it is best to download only from `https://ziglang.org/`. One possible exception is when a pre-release is requested which is more likely to be available from an alternative mirror.
21+
* When a mirror receives a GET request for `X/<filename>`, the behavior depends on `<filename>`.
22+
* Parse the file name to extract the Zig version string. More details on this are [below](#parsing-versions).
23+
* Determine whether this is a "normal" or "pre-release" [Semantic Version](https://semver.org/).
24+
* If this is a "normal" version, respond with 200 OK and the file found at `https://ziglang.org/download/<version>/<filename>`.
25+
* If the release version is "0.5.0" or older (according to Semantic Versioning), the mirror **may** respond with 404 Not Found, but is not required to.
26+
* Otherwise, the mirror **must** return the tarball.
27+
* These requirements apply equally to source tarballs (e.g. `zig-0.14.1.tar.xz`), bootstrap source tarballs (e.g. `zig-bootstrap-0.14.1.tar.xz`), and binary tarballs (e.g. `zig-x86_64-linux-0.14.1.tar.xz`).
28+
* Otherwise (i.e. if this is a "pre-release" version), respond with 200 OK and the file found at `https://ziglang.org/builds/<filename>`.
29+
* If the version is older than the latest "release" version (according to Semantic Versioning), the mirror **may** respond with 404 Not Found, but is not required to.
30+
* Otherwise, the mirror **must** return the tarball.
31+
* These requirements apply equally to source tarballs (e.g. `zig-0.15.0-dev.671+c907866d5.tar.xz`), bootstrap source tarballs (e.g. `zig-bootstrap-0.15.0-dev.671+c907866d5.tar.xz`), and binary tarballs (e.g. `zig-x86_64-linux-0.15.0-dev.671+c907866d5.tar.xz`).
32+
* Invalid accesses, for instance to malformed filenames, **may** cause a 404 Not Found response.
33+
* Accesses to file names which do not end with ".zip", ".tar.xz", ".zip.minisig", or ".tar.xz.minisig", **may** cause a 404 Not Found response.
34+
* Files provided by the mirror **must** be bit-for-bit identical to their `https://ziglang.org/` counterparts.
35+
* If a mirror is required to serve a tarball which is has not yet cached locally, it **must** immediately download it from its source at `https://ziglang.org`, and respond with that downloaded file.
36+
* The mirror **may** rate-limit accesses. If an access failed due to rate-limiting, the mirror **should** respond with 429 Too Many Requests.
37+
* The mirror **may** undergo maintenance, upgrades, and other scheduled downtime. If an access fails for this reason, where possible, the mirror **should** respond with 503 Unavailable. The mirror **should** try to minimize such downtime.
38+
* The mirror **may** undergo occasional unintended and unscheduled downtime. The mirror **must** go to all efforts to minimize such outages, and **must** resolve such outages within a reasonable time upon being notified of them.
39+
* The mirror **may** observe the query parameter named `source` to learn about the origin of its traffic.
40+
* Clients are encouraged, but not required, to provide this query parameter to indicate what service triggered the request. For instance, the `mlugg/setup-zig` GitHub Action passes this query parameter as `?source=github-mlugg-setup-zig`.
41+
42+
## Parsing Versions
43+
44+
Mirrors are required to parse tarball file names to extract the Zig version from them. Unfortunately,
45+
this is complicated a little by two factors: the existence of "source" tarballs, and the fact that
46+
Zig's tarball naming schema changed with the 0.14.1 release.
47+
48+
If you have access to Regular Expressions, a simple strategy is to search the tarball name for
49+
`/\d+\.\d+\.\d+(-dev\.\d+\+[0-9a-f]+)?/`. This is probably inefficient, but that is unlikely to
50+
matter in practice.
51+
52+
An alternative strategy which is simpler but requires slightly more code is as follows:
53+
* Strip a supported extension (`.zip`, `.tar.xz`, `.zip.minisig`, `.tar.xz.minisig`) from the end of the file name.
54+
* Find the last occurrence of "-" in the file name. If that byte is followed by the string "dev", find the previous occurence of "-" instead.
55+
* The string after that "-" byte is the Zig version.
56+
57+
It is strongly discouraged for checks to rely on specific architecture or OS names, since the set of targets for which tarballs are provided could be extended at any time.
58+
59+
## Adding A Mirror
60+
61+
Once a mirror complies with the requirements listed above, you can add it to the list of mirrors on ziglang.org by modifying the list in
62+
`assets/community-mirrors.ziggy` and opening a pull request with your changes. The diff should just add a single entry to the end of the list:
63+
64+
```diff
65+
[
66+
{
67+
.url = "https://a.com",
68+
.username = "a",
69+
.email = "a@a.com",
70+
},
71+
{
72+
.url = "https://b.com/zig",
73+
.username = "b",
74+
.email = "b@b.com",
75+
},
76+
+ {
77+
+ .url = "https://mymirror.net",
78+
+ .username = "my-github-username",
79+
+ .email = "my@email.com",
80+
+ },
81+
]
82+
```
83+
84+
Note that the Zig core team always reserves the right to exclude mirrors from this list for any (or no) reason.
85+
86+
## Community-Written Solutions
87+
88+
Some members of the Zig community have written software for hosting Zig mirrors, making it easier to comply with the requirements listed above. If you have an
89+
open-source repository which would fit here, feel free to open a pull request adding it below.
90+
91+
> [!WARNING]
92+
> The Zig Software Foundation has not written or audited this code. As such, no guarantees can be made regarding its correctness, and it should **not** be
93+
> implicitly trusted. You are strongly encouraged to audit this software before using it, particularly if it will not be run in an isolated environment.
94+
95+
* [hexops/wrench](https://github.com/hexops/wrench?tab=readme-ov-file#run-your-own-ziglangorgdownload-mirror)

assets/community-mirrors.ziggy

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[
2+
{
3+
.url = "https://pkg.machengine.org/zig",
4+
.username = "emidoots",
5+
.email = "emi@hexops.com",
6+
},
7+
{
8+
.url = "https://zigmirror.hryx.net/zig",
9+
.username = "hryx",
10+
.email = "codroid@gmail.com",
11+
},
12+
{
13+
.url = "https://zig.linus.dev/zig",
14+
.username = "linusg",
15+
.email = "mail@linusgroh.de",
16+
},
17+
]

assets/css/style.css

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ blockquote {
467467
}
468468

469469

470-
code.zig {
470+
code {
471471
--light-yellow: #e5c07b;
472472
--dark-yellow: #d19a66;
473473
--blue: #61afef;
@@ -517,3 +517,49 @@ video {
517517
margin-top: 1em;
518518
display: inline-block;
519519
}
520+
521+
code.python {
522+
color: var(--cyan);
523+
}
524+
525+
code.python .punctuation {
526+
color: var(--cyan);
527+
}
528+
529+
code.python .string {
530+
color: var(--dark-yellow);
531+
}
532+
533+
code.python .variable,
534+
code.python .parameter,
535+
code.python .exception,
536+
code.python .field {
537+
color: var(--light-yellow);
538+
}
539+
540+
code.python .keyword.function {
541+
color: var(--light-red);
542+
}
543+
544+
code.python .bracket {
545+
color: var(--cyan);
546+
}
547+
548+
code.python .function {
549+
color: var(--blue);
550+
}
551+
552+
code.python .builtin {
553+
color: var(--magenta);
554+
}
555+
556+
code.python .number {
557+
color: var(--magenta);
558+
}
559+
560+
code.python .operator,
561+
code.python .qualifier,
562+
code.python .keyword,
563+
code.python .attribute {
564+
color: var(--light-red);
565+
}

build.zig.zon

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
.hash = "doctest-0.1.0-lY-6k0W2AQB2cw2vD3306oCziQrVnG013naZd8hMkuAQ",
1414
},
1515
.zine = .{
16-
.url = "git+https://github.com/kristoff-it/zine#5408d3dff6107fc449a6d24d90f379b6fffdb4e6",
17-
.hash = "zine-0.9.0-ou6nIBcqFgCkkL8OQnHRzEKboVcAvIp76bnf6n9qeBvU",
16+
.url = "git+https://github.com/kristoff-it/zine#ff0b7b1222fe6529fc36e876a71a99379ad761c4",
17+
.hash = "zine-0.9.0-ou6nIEJTFgDwdlQnB6NtTnZneWKgg-qoVPaXyU2PjWF2",
1818
},
1919
},
2020
}

check-mirrors/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# check-mirrors
2+
3+
This is a small utility which checks the functionality of all listed community mirrors. It does this
4+
by fetching a subset of available tarballs and signatures from each mirror, and checking that the
5+
returned files exactly match the corresponding files served by ziglang.org.
6+
7+
This check is automatically run once per day by the 'check-mirrors' workflow. It also runs when a PR
8+
modifies the mirror list.

check-mirrors/build.zig

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
pub fn build(b: *std.Build) void {
2+
const target = b.standardTargetOptions(.{});
3+
const optimize = b.standardOptimizeOption(.{});
4+
5+
const ziggy = b.dependency("ziggy", .{
6+
.target = target,
7+
.optimize = optimize,
8+
});
9+
10+
const exe = b.addExecutable(.{
11+
.name = "check-mirrors",
12+
.root_module = b.createModule(.{
13+
.root_source_file = b.path("main.zig"),
14+
.target = target,
15+
.optimize = optimize,
16+
.imports = &.{
17+
.{ .name = "ziggy", .module = ziggy.module("ziggy") },
18+
},
19+
}),
20+
});
21+
b.installArtifact(exe);
22+
23+
const run = b.addRunArtifact(exe);
24+
if (b.args) |args| run.addArgs(args);
25+
b.step("run", "Run check-mirrors").dependOn(&run.step);
26+
}
27+
28+
const std = @import("std");

check-mirrors/build.zig.zon

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
.{
2+
.name = .check_mirrors,
3+
.version = "0.0.0",
4+
.fingerprint = 0xcb408e1ecbee16fd, // Changing this has security and trust implications.
5+
.minimum_zig_version = "0.14.0",
6+
.dependencies = .{
7+
.ziggy = .{
8+
.url = "git+https://github.com/kristoff-it/ziggy.git#fe3bf9389e7ff213cf3548caaf9c6f3d4bb38647",
9+
.hash = "ziggy-0.1.0-kTg8vwBEBgDFzbstERaPLr5TzM1DhfDza1we_eEXuLDL",
10+
},
11+
},
12+
.paths = .{
13+
"build.zig",
14+
"build.zig.zon",
15+
"src",
16+
},
17+
}

0 commit comments

Comments
 (0)