Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f65cd2faa | ||
|
|
01abc78c0f | ||
|
|
63daa89bcb | ||
|
|
748c65d592 | ||
|
|
91ad350543 | ||
|
|
e281e4024a | ||
|
|
3a84022d8f | ||
|
|
c16b061fe7 | ||
|
|
bc8fc88849 | ||
|
|
e349124fc4 | ||
|
|
93a328a007 | ||
|
|
597e112e2b | ||
|
|
b0406b82f1 | ||
|
|
b10af101ed | ||
|
|
635cdd6c2c | ||
|
|
91b1cef4b2 | ||
|
|
1858aa143c | ||
|
|
93fbabcb60 | ||
|
|
aff9324c5c | ||
|
|
da49d87193 | ||
|
|
83886de13c | ||
|
|
df3e843284 | ||
|
|
5798ba209e | ||
|
|
8984945062 | ||
|
|
b8778d8a89 | ||
|
|
5b2524fe4d | ||
|
|
6f3babddd3 | ||
|
|
6134bd3735 | ||
|
|
b44db64e1e | ||
|
|
9d9bfd868d | ||
|
|
fb19e0fa84 | ||
|
|
88ab8d85cb | ||
|
|
e49f05cd1d | ||
|
|
573e129dee | ||
|
|
c01152b3fc | ||
|
|
62ef8d3be8 | ||
|
|
29bdf93a39 | ||
|
|
0f0f7be986 | ||
|
|
81d6eba98b | ||
|
|
08ba86bf79 | ||
|
|
8e9427d775 | ||
|
|
54c59cfd8a | ||
|
|
4d5cb3f2a4 | ||
|
|
77d41657aa | ||
|
|
5a5b8d2bd2 | ||
|
|
d2de4c18de | ||
|
|
037298b5a7 | ||
|
|
68bd653d9c | ||
|
|
c7432500fc | ||
|
|
d0d3e68ec4 | ||
|
|
0da4e7d52e | ||
|
|
ef6ef9e6a2 | ||
|
|
aad3b35a3a | ||
|
|
dc20a5e020 | ||
|
|
d0cf2b0811 | ||
|
|
6e79b7f740 | ||
|
|
e736186084 | ||
|
|
56930b532a | ||
|
|
1bf7fa9614 | ||
|
|
25ab2d7ea1 | ||
|
|
a1198c687e | ||
|
|
aa6d96e803 | ||
|
|
b49e1fd97d | ||
|
|
8a6980d33b | ||
|
|
8514f5774b | ||
|
|
32d977bf99 | ||
|
|
d0bd59f735 | ||
|
|
21155c06fb | ||
|
|
e0ac4551c2 | ||
|
|
e799b04b82 | ||
|
|
c2d42eba1b | ||
|
|
070bddc417 | ||
|
|
f3d5967ae6 | ||
|
|
72c0912c7b | ||
|
|
89020e2062 | ||
|
|
a617775941 | ||
|
|
b9e03dbb7b | ||
|
|
96d7f7673b | ||
|
|
a2a18e41c3 | ||
|
|
e2fcb0f5c2 | ||
|
|
ed1a281c9c | ||
|
|
9844193547 | ||
|
|
e9d402b60c | ||
|
|
71323383d8 | ||
|
|
3b34bef27e | ||
|
|
23402771cf | ||
|
|
6480bf8b8d | ||
|
|
f64587952e | ||
|
|
ad5e3b12b7 | ||
|
|
f1cdfcb3e8 | ||
|
|
4931f3a819 | ||
|
|
7b74fe93b1 | ||
|
|
4ccc44d612 | ||
|
|
08e63434d8 | ||
|
|
40dc440cbb | ||
|
|
7581c129bb | ||
|
|
259d022945 | ||
|
|
e44816b65a | ||
|
|
b300e7efd4 | ||
|
|
78853c19a6 | ||
|
|
e53ad8220b | ||
|
|
43fb2513a2 | ||
|
|
c909eea98f | ||
|
|
1f1240298a | ||
|
|
dfd2102274 | ||
|
|
35ce93db53 | ||
|
|
3f3103d070 | ||
|
|
ed4aa85442 | ||
|
|
43010d3eb2 | ||
|
|
546deac561 | ||
|
|
2eca4bc2ec | ||
|
|
a2ae910123 | ||
|
|
88b20af247 | ||
|
|
6a3454953c | ||
|
|
ceb5b2275d | ||
|
|
e980583b96 | ||
|
|
5c094254e5 | ||
|
|
d8a3327ee3 | ||
|
|
239ce420fa | ||
|
|
13d6b9fbaa | ||
|
|
ee1620f438 | ||
|
|
a0dba26666 | ||
|
|
82e29462a9 | ||
|
|
725ff7a410 | ||
|
|
64b5237f7c | ||
|
|
7d17c4d63a | ||
|
|
aab9aa4c22 | ||
|
|
6d9a7492e2 | ||
|
|
96c7c46e41 | ||
|
|
09f69374d9 | ||
|
|
4e8a65e584 | ||
|
|
bba4bda2d7 | ||
|
|
6263473f81 | ||
|
|
1fc4bd47d5 | ||
|
|
7558c0945c | ||
|
|
467b17bf38 | ||
|
|
3d113f473a | ||
|
|
792fa822f4 | ||
|
|
7b65e9b10f | ||
|
|
cd1f1c3970 | ||
|
|
be266234cb | ||
|
|
cf1f608745 | ||
|
|
f1f5aba33a | ||
|
|
0062a1e27a | ||
|
|
e79b92ff2c | ||
|
|
ce14e90beb | ||
|
|
93b3f04390 | ||
|
|
bc40e7bafe | ||
|
|
5628be3e6b | ||
|
|
58fbad4a85 | ||
|
|
7645804495 | ||
|
|
e5d840dabf | ||
|
|
b22a9c42a3 | ||
|
|
6ab145fce5 | ||
|
|
c240394cde | ||
|
|
d57056b937 | ||
|
|
245ab9b4f6 | ||
|
|
61e99d5df7 | ||
|
|
59f2388a0f | ||
|
|
5b0ae21c96 | ||
|
|
097ccd28b0 | ||
|
|
31d77513fe | ||
|
|
5ba617927f | ||
|
|
7257e32237 | ||
|
|
18102bf315 | ||
|
|
7ec0a0516d | ||
|
|
c3839da0ef | ||
|
|
2f677e9337 | ||
|
|
8295b4c1ef | ||
|
|
cacc3360e0 | ||
|
|
964325b819 | ||
|
|
9ce6a3f9a0 | ||
|
|
693205032b | ||
|
|
e8b243b60d | ||
|
|
409cac606e | ||
|
|
d4463c5a82 | ||
|
|
e001cceadd | ||
|
|
2b62bd35d9 | ||
|
|
219c9302d9 | ||
|
|
e9f25bb1d7 | ||
|
|
bb97027039 | ||
|
|
27bfc9da13 | ||
|
|
fa61a38111 | ||
|
|
169ac59750 | ||
|
|
7c850a3056 | ||
|
|
e3ee0a7c45 | ||
|
|
3f115b7e06 | ||
|
|
a864f877ec | ||
|
|
41874a0e0c | ||
|
|
48ed3ea061 | ||
|
|
8e5a70b52b | ||
|
|
d31c2d7992 | ||
|
|
9f691e7e83 | ||
|
|
c5d504a734 | ||
|
|
6c2a827c34 | ||
|
|
90f06bd473 | ||
|
|
3395149802 | ||
|
|
3fc6964447 | ||
|
|
3749d679bd | ||
|
|
d933c333e6 | ||
|
|
c86a73887a | ||
|
|
83c4269800 | ||
|
|
89d90c3376 | ||
|
|
6b71454d1b | ||
|
|
57f7902809 | ||
|
|
7c2b9f3b80 | ||
|
|
2a4307638f | ||
|
|
5108050b31 | ||
|
|
706f3a6cb1 | ||
|
|
6d05ae816f | ||
|
|
f5487d5b08 | ||
|
|
4ede2e8aa7 | ||
|
|
9e1a2b70a8 | ||
|
|
969533780c | ||
|
|
31e019dff8 | ||
|
|
cccfc99d37 | ||
|
|
9c12738fb0 | ||
|
|
6d9e3a2064 | ||
|
|
9f7346e2e5 | ||
|
|
a04b258daf | ||
|
|
1b4ab4efe8 | ||
|
|
0eee668de4 | ||
|
|
a5fdef87fb | ||
|
|
44fa384ad6 | ||
|
|
f25ae007a7 | ||
|
|
181b9af7de | ||
|
|
c6c309d01b | ||
|
|
a258ef03d9 | ||
|
|
acf5d8896a | ||
|
|
4dcb8fed29 | ||
|
|
f56752d74f | ||
|
|
455a8b800f | ||
|
|
238d9cd1f4 | ||
|
|
ddab3fa269 | ||
|
|
a301fcc469 | ||
|
|
c1ce388d41 | ||
|
|
8bbf748e1f | ||
|
|
50c23cb7d9 | ||
|
|
8d5c1ed7a3 | ||
|
|
b6c1af58cd | ||
|
|
3cf1b6aa8f | ||
|
|
a11db16db3 | ||
|
|
dca26bf044 | ||
|
|
95b8f15c6b | ||
|
|
be6b796a20 | ||
|
|
90e26686bd | ||
|
|
2cd564f16b | ||
|
|
c64087836d | ||
|
|
40f3d42984 | ||
|
|
9a7b8a0ffc | ||
|
|
b571298b58 | ||
|
|
fefd3e8c6b | ||
|
|
4364dbfba3 | ||
|
|
8c837ede87 | ||
|
|
36b3c994e6 | ||
|
|
1ab3acaf70 | ||
|
|
ca4f82c665 | ||
|
|
bcf749c4d8 | ||
|
|
ff69e902c8 | ||
|
|
97170b869d | ||
|
|
e09a094d25 | ||
|
|
c5ba9aaf03 | ||
|
|
f7709827f6 | ||
|
|
72250afe73 | ||
|
|
602a58cfd1 | ||
|
|
f8a41e6c8b | ||
|
|
5495b461fd | ||
|
|
d1e80f8485 | ||
|
|
65aaaafe56 | ||
|
|
634fc6cb7a | ||
|
|
fb9a47061c | ||
|
|
0cebbbfad8 | ||
|
|
52069c3e49 | ||
|
|
8479dec66f | ||
|
|
f489d26358 | ||
|
|
4904b24c69 | ||
|
|
38421f5a8d | ||
|
|
3488a88696 | ||
|
|
2708b6a02f | ||
|
|
aaf6ccefcc | ||
|
|
b0e3d98b52 | ||
|
|
803907aaa5 | ||
|
|
ec056f9f02 | ||
|
|
63f2fa9f01 | ||
|
|
1ec378ac96 | ||
|
|
04c0ee173a | ||
|
|
3516095141 | ||
|
|
0eb6ca75c3 | ||
|
|
26079e71ed | ||
|
|
7d2b14f53e | ||
|
|
b9bd159dcf | ||
|
|
52980b81e0 | ||
|
|
4fbe64e376 | ||
|
|
5aacb47cd4 | ||
|
|
ff18bb42d5 | ||
|
|
9121109717 | ||
|
|
0faf62da1c | ||
|
|
5b03ea2d0c | ||
|
|
5dad8648b3 | ||
|
|
7f1bb6920b | ||
|
|
7bd4f6207b | ||
|
|
1d4cb83ca2 | ||
|
|
37f573d08f | ||
|
|
847fd2dcbe | ||
|
|
2c41bdf9fd | ||
|
|
7704c01acf | ||
|
|
c2f369fb2e | ||
|
|
c13658c430 | ||
|
|
5c1b79862e | ||
|
|
8366f89925 | ||
|
|
2917b77de1 | ||
|
|
b75251443c | ||
|
|
5d26a0bde3 | ||
|
|
7339aa2992 | ||
|
|
0096a8a835 | ||
|
|
5728b12cbb | ||
|
|
830bc7884b | ||
|
|
7e795012ed | ||
|
|
666479ad05 | ||
|
|
b498e0a5fc | ||
|
|
ebcadb8dd6 | ||
|
|
b1ad307e93 | ||
|
|
395b1422e0 | ||
|
|
1910ab8709 | ||
|
|
08ca24a176 | ||
|
|
b27aeaa93f | ||
|
|
ae217daafd | ||
|
|
d6501160e1 | ||
|
|
c3669e5412 | ||
|
|
0e7ed706b6 | ||
|
|
678eb2d1af | ||
|
|
55e203b1cd | ||
|
|
e9f14ea77f | ||
|
|
190d8f9a60 | ||
|
|
bbe01b52ad | ||
|
|
0d8411b352 | ||
|
|
890d330600 | ||
|
|
7fd746624a | ||
|
|
7ce014f8a9 | ||
|
|
e62054006e | ||
|
|
b956bda63b | ||
|
|
ce12d21fe3 | ||
|
|
34f8420003 | ||
|
|
ed256db6f7 | ||
|
|
4fb24a8263 | ||
|
|
2cc76d1dc5 | ||
|
|
ef5c5fe50b | ||
|
|
c1c783574c | ||
|
|
4b3311b800 | ||
|
|
b022d5b14f | ||
|
|
3a8c438ec5 | ||
|
|
adea72cbdf | ||
|
|
bc79048b9a | ||
|
|
f6dade0df2 | ||
|
|
fb9ab126f7 | ||
|
|
4d975b9813 | ||
|
|
e7a95dcbef | ||
|
|
266b6039d4 | ||
|
|
22c6f14ea6 | ||
|
|
0f556b3a89 | ||
|
|
d6a1ddd703 | ||
|
|
f539b5a584 | ||
|
|
b07b1fdef0 | ||
|
|
b6a69cc40e | ||
|
|
bf91022ab9 | ||
|
|
876f7d1dc7 | ||
|
|
415352eed1 | ||
|
|
c444610bb8 | ||
|
|
c3c335bcca | ||
|
|
9dbee00d1f | ||
|
|
d86ecb6c4f | ||
|
|
2a04908a1a | ||
|
|
1c69d4ffc9 | ||
|
|
5cfb57fa2c | ||
|
|
a683d6c60f | ||
|
|
d4beb82c4c | ||
|
|
1a48aa24dc | ||
|
|
59ab4e8fe2 | ||
|
|
6ca0d02406 | ||
|
|
3448e187ca | ||
|
|
d1fed6cd56 | ||
|
|
647fa375e6 | ||
|
|
e1aa98d1ed | ||
|
|
815bdc50f7 | ||
|
|
28c8bf4684 | ||
|
|
8eadd70568 | ||
|
|
25fb233630 | ||
|
|
7dfe89f942 | ||
|
|
bcc5317bac | ||
|
|
b66cfbbee9 | ||
|
|
9b51200853 | ||
|
|
d8c85d1484 | ||
|
|
d96322f963 | ||
|
|
006ca9fa3c | ||
|
|
3cbaffd145 | ||
|
|
38890426e1 | ||
|
|
645ea37852 | ||
|
|
3d4b3488bb | ||
|
|
f64dc8688a | ||
|
|
3b73fc7a62 | ||
|
|
8bcff08bf2 | ||
|
|
53564d9cfe | ||
|
|
2181eec3ce | ||
|
|
98f8ab5da3 | ||
|
|
efe164dd3e |
No files matched your search
@@ -1,5 +1,12 @@
|
||||
# Every change needs a review from one of these before it merges (branch protection:
|
||||
# "Require review from Code Owners"). Add trusted contributors to the first line.
|
||||
* @ludovic111 @rdbtCVS
|
||||
# "Require review from Code Owners"). The reviewers are the Horizon Team, GitHub team horizon-rust/horizon-team
|
||||
# (Ludovic decides who is in it). /.github/ and takedowns stay Ludovic's.
|
||||
* @ludovic111 @horizon-rust/horizon-team
|
||||
/.github/ @ludovic111
|
||||
/revoked.toml @ludovic111
|
||||
# What decides which builds the native-builds environment signs, and under which key (native-build.yml,
|
||||
# horizon#11859): the signing key's owner reviews it.
|
||||
/scripts/native_build.py @ludovic111
|
||||
/scripts/test_native_build.py @ludovic111
|
||||
/prebuilt.toml @ludovic111
|
||||
/.github/workflows/native-build.yml @ludovic111
|
||||
@@ -4,5 +4,5 @@ contact_links:
|
||||
url: https://playhorizon.dev/mods/submit
|
||||
about: Mods, modpacks and resource packs for Horizon. Each submission is reviewed.
|
||||
- name: Horizon itself
|
||||
url: https://github.com/ludovic111/horizon/issues
|
||||
url: https://github.com/horizon-rust/horizon/issues
|
||||
about: Bugs in the game or the launcher.
|
||||
@@ -0,0 +1,37 @@
|
||||
name: Takedown request (copyright)
|
||||
description: A project in the registry uses your work without your permission.
|
||||
title: "Takedown: "
|
||||
labels: [takedown]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for telling us. A project that uses your work without permission is removed
|
||||
(see [MOD-POLICY.md](https://github.com/horizon-rust/horizon-mods/blob/main/MOD-POLICY.md)).
|
||||
This issue is public: to write privately instead, use [lu4ovic.com](https://lu4ovic.com/#contact).
|
||||
- type: input
|
||||
id: project
|
||||
attributes:
|
||||
label: Project id
|
||||
description: The last part of its page's address, playhorizon.dev/mods/<id>
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: work
|
||||
attributes:
|
||||
label: Your work
|
||||
description: What it is, where it is published, and what the project copies from it.
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: contact
|
||||
attributes:
|
||||
label: How to reach you
|
||||
description: An email or profile, if not this GitHub account.
|
||||
- type: checkboxes
|
||||
id: rights
|
||||
attributes:
|
||||
label: Statement
|
||||
options:
|
||||
- label: I own the rights to this work, or I act for whoever does, and I believe this use is not allowed.
|
||||
required: true
|
||||
@@ -3,6 +3,11 @@
|
||||
**Project:** <!-- id, and new project or update -->
|
||||
|
||||
- [ ] I made it, or I am allowed to publish it, under the licence the entry names.
|
||||
- [ ] If it ports or translates another mod, the original's licence allows it (or its authors agreed), and my page
|
||||
credits and links the original.
|
||||
- [ ] It has no code, assets or data from Minecraft, Mojang or Microsoft, and nothing that harms players.
|
||||
- [ ] I read and accept the [mod policy](https://github.com/horizon-rust/horizon-mods/blob/main/MOD-POLICY.md): I am this
|
||||
project's author or publisher and responsible for it; the Horizon project is not.
|
||||
- [ ] The `source` link has the code this file was built from.
|
||||
- [ ] I ran `cargo horizon-mod check <file>` and it passed.
|
||||
|
||||
|
||||
@@ -29,13 +29,9 @@ jobs:
|
||||
with:
|
||||
path: head
|
||||
persist-credentials: false
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin
|
||||
~/.cargo/horizon-rev
|
||||
key: cargo-horizon-mod-${{ env.HORIZON_REV }}
|
||||
# main's scripts, not the pull request's.
|
||||
# main's scripts, not the pull request's. No actions/cache: this job decides whether a submission passes,
|
||||
# and main's cache entries can be written by runs that build other people's code (native-build.yml), so
|
||||
# the tool is built fresh and the files are checked against their SHA-256 (fetch.sh).
|
||||
- run: base/scripts/install-tool.sh
|
||||
- name: Who submitted it
|
||||
id: author
|
||||
@@ -60,6 +56,8 @@ jobs:
|
||||
| tee -a "$GITHUB_STEP_SUMMARY" || status=1
|
||||
echo '```' >> "$GITHUB_STEP_SUMMARY"
|
||||
exit $status
|
||||
# Published files come from the registry-files release publish keeps: downloads from a mod's own
|
||||
# release would count as a player's.
|
||||
- name: Download its files
|
||||
run: base/scripts/fetch.sh head
|
||||
- name: Check each new file
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
# Prebuilt native builds (horizon#11859): the game compiled for one exact set of registry native mods, signed, so
|
||||
# players' launchers download it instead of compiling for 30 to 60 minutes.
|
||||
# - Every day after the Nightly, `dispatch` starts one run per set of prebuilt.toml on the newest Nightly. A run
|
||||
# builds ONE set, and only a set prebuilt.toml lists (`plan` and `sign` both refuse any other).
|
||||
# - On GitHub's runners only:
|
||||
# plan (no secrets): resolves the Nightly and each mod against the published index (newest unrevoked version,
|
||||
# not broken_on that commit), computes each target's request key as the launcher does, leaves out what is
|
||||
# published, downloads the mods' files and the launcher release named by vars.NATIVE_BUILD_LAUNCHER and
|
||||
# checks that release's signed SHA256SUMS against Horizon's release keys (pinned below, by SHA-256).
|
||||
# build (no secrets, a token that can only read, no cache): runs that launcher headless
|
||||
# (`native-build --prebuilt-out`): its pinned Rust, the Nightly's source, the release profile, the same
|
||||
# pins, hook points and left-out mods as a player's build. A mod crate's build script runs here, so this
|
||||
# job is UNTRUSTED: with the runner's own token it can replace any artifact of the run (`plan` included)
|
||||
# and write cache entries on main.
|
||||
# sign (environment `native-builds`, main only): trusts nothing a build job could touch. What it signs comes
|
||||
# from the plan job's OUTPUTS only (no later job can change them); it recomputes each key from the base,
|
||||
# the mods and the target, derives every file name from key and target, downloads `build-<target>` by
|
||||
# its exact name and refuses anything in it but that one archive. It runs only Horizon's
|
||||
# tools/ci/sign_native_build.sh (sha256sum and OpenSSL) at a pinned commit, checked by SHA-256 before
|
||||
# the key is in reach. A build can only ever be signed as its own set's.
|
||||
# Caches: no job here restores one, and no job of horizon-mods that holds a write token may restore one either
|
||||
# (publish.yml, native-compat.yml): the build job above can write cache entries on main, and restoring an entry
|
||||
# unpacks a tar anywhere on the runner.
|
||||
name: native-build
|
||||
on:
|
||||
schedule:
|
||||
- cron: "30 19 * * *"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
mods:
|
||||
description: "One set of prebuilt.toml, exactly as listed (`id` or `id@version`, space-separated). Empty: every set (one run each)"
|
||||
default: ""
|
||||
base:
|
||||
description: "The Nightly: `latest` or nightly-<date>-<short>"
|
||||
default: latest
|
||||
targets:
|
||||
description: "`all`, or target triples separated by commas"
|
||||
default: all
|
||||
permissions:
|
||||
contents: read
|
||||
concurrency:
|
||||
group: native-build-${{ inputs.mods || 'dispatch' }}-${{ inputs.base || 'latest' }}
|
||||
cancel-in-progress: false
|
||||
env:
|
||||
# A launcher release with `native-build --prebuilt-out` (Horizon #11859), e.g. [email protected].
|
||||
LAUNCHER: ${{ vars.NATIVE_BUILD_LAUNCHER }}
|
||||
# The Horizon commit this workflow takes its signer and keys from: its own pin, never vars.HORIZON_REV (shared with
|
||||
# publish and the checks) and never a branch. Each file is checked against its SHA-256 here before it is used.
|
||||
# Moving the pin means reviewing the files' diff and updating the three hashes (`sha256sum`).
|
||||
SIGN_REV: c794b87841b58f72ade9842b46757d843246054c
|
||||
SIGN_SCRIPT_SHA256: 95ebefe4b0beeb1b4282a09352260c48f1793b13e370414e91175aa789fc2278 # tools/ci/sign_native_build.sh
|
||||
SIGN_KEYS_SHA256: f75dc6fe23343a4e97fe2453853e67f20ddcce1bc1f2c240a56b8b6d6681f611 # crates/horizon-release-sign/native-build-keys.txt
|
||||
RELEASE_KEYS_SHA256: bb1f43a3eb14a2c0d89d7d823a3352dd4b7ac01f50d55451e282ea178fb61f6f # crates/horizon-release-sign/release-keys.txt
|
||||
# The largest archive signed: the launcher refuses bigger ones before checking (launcher/src/native_prebuilt.rs).
|
||||
MAX_ARCHIVE: "536870912"
|
||||
jobs:
|
||||
dispatch:
|
||||
# Idle until a launcher with native-build --prebuilt-out is released and named.
|
||||
if: (github.event_name == 'schedule' || inputs.mods == '') && vars.NATIVE_BUILD_LAUNCHER != ''
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: One run per set
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
BASE: ${{ inputs.base || 'latest' }}
|
||||
run: |
|
||||
python3 scripts/native_build.py sets | while read -r set; do
|
||||
echo "native-build: $set"
|
||||
gh workflow run native-build.yml --repo "$GITHUB_REPOSITORY" --ref main -f mods="$set" -f base="$BASE"
|
||||
done
|
||||
plan:
|
||||
if: github.event_name == 'workflow_dispatch' && inputs.mods != ''
|
||||
runs-on: ubuntu-latest
|
||||
# The sign job reads these, never the `plan` artifact: build jobs can replace an artifact, not a job's outputs.
|
||||
outputs:
|
||||
matrix: ${{ steps.plan.outputs.matrix }}
|
||||
any: ${{ steps.plan.outputs.any }}
|
||||
tag: ${{ steps.plan.outputs.tag }}
|
||||
base: ${{ steps.plan.outputs.base }}
|
||||
mods: ${{ steps.plan.outputs.mods }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- run: python3 -m unittest scripts/test_native_build.py
|
||||
- id: plan
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
MODS: ${{ inputs.mods }}
|
||||
BASE: ${{ inputs.base }}
|
||||
TARGETS: ${{ inputs.targets }}
|
||||
run: python3 scripts/native_build.py plan --mods "$MODS" --base "$BASE" --targets "$TARGETS" --out plan
|
||||
- name: The launcher, checked against Horizon's release keys
|
||||
if: steps.plan.outputs.any == 'true'
|
||||
run: |
|
||||
set -euo pipefail
|
||||
[ -n "$LAUNCHER" ] || { echo "::error::vars.NATIVE_BUILD_LAUNCHER names no launcher release"; exit 1; }
|
||||
mkdir -p plan/launcher && cd plan/launcher
|
||||
base="https://github.com/horizon-rust/horizon/releases/download/$LAUNCHER"
|
||||
curl -sSfLO "$base/SHA256SUMS" && curl -sSfLO "$base/SHA256SUMS.sig"
|
||||
for asset in $(jq -r '.builds[].launcher' ../plan.json | sort -u); do curl -sSfLO "$base/$asset"; done
|
||||
curl -sSfL -o release-keys.txt \
|
||||
"https://raw.githubusercontent.com/horizon-rust/horizon/$SIGN_REV/crates/horizon-release-sign/release-keys.txt"
|
||||
echo "$RELEASE_KEYS_SHA256 release-keys.txt" | sha256sum -c
|
||||
grep -v '^#' release-keys.txt | grep . > keys
|
||||
{ printf 'horizon release %s\n' "$LAUNCHER"; cat SHA256SUMS; } > message
|
||||
ok=
|
||||
while read -r key; do
|
||||
{ printf '\x30\x2a\x30\x05\x06\x03\x2b\x65\x70\x03\x21\x00'; echo "$key" | base64 -d; } > pub.der
|
||||
while read -r sig; do
|
||||
echo "$sig" | base64 -d > sig.bin
|
||||
openssl pkeyutl -verify -rawin -pubin -keyform DER -inkey pub.der -in message -sigfile sig.bin >/dev/null 2>&1 && ok=1
|
||||
done < SHA256SUMS.sig
|
||||
done < keys
|
||||
[ -n "$ok" ] || { echo "::error::$LAUNCHER: SHA256SUMS isn't signed by a Horizon release key"; exit 1; }
|
||||
sha256sum --ignore-missing -c SHA256SUMS
|
||||
rm -f message pub.der sig.bin keys release-keys.txt SHA256SUMS SHA256SUMS.sig
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: plan
|
||||
path: plan
|
||||
build:
|
||||
needs: plan
|
||||
if: needs.plan.outputs.any == 'true'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include: ${{ fromJson(needs.plan.outputs.matrix) }}
|
||||
runs-on: ${{ matrix.runner }}
|
||||
timeout-minutes: 180
|
||||
permissions:
|
||||
contents: read
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: plan
|
||||
path: plan
|
||||
- if: runner.os == 'Linux'
|
||||
# The game's ALSA and udev headers; the launcher links xkbcommon and xcb even headless.
|
||||
run: sudo apt-get update && sudo apt-get install -y libasound2-dev libudev-dev pkg-config libxkbcommon-x11-0 libxcb1
|
||||
- name: Build the set as a player's launcher would
|
||||
env:
|
||||
TARGET: ${{ matrix.target }}
|
||||
KEY: ${{ matrix.key }}
|
||||
LAUNCHER_ASSET: ${{ matrix.launcher }}
|
||||
ARCHIVE: ${{ matrix.archive }}
|
||||
TAG: ${{ needs.plan.outputs.tag }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
scratch="$RUNNER_TEMP/native"
|
||||
mkdir -p "$scratch/launcher" "$scratch/out"
|
||||
case "$LAUNCHER_ASSET" in
|
||||
*.tar.gz) tar xzf "plan/launcher/$LAUNCHER_ASSET" -C "$scratch/launcher"; launcher="$scratch/launcher/horizon-launcher" ;;
|
||||
*macos*) unzip -q "plan/launcher/$LAUNCHER_ASSET" -d "$scratch/launcher"; launcher="$scratch/launcher/Horizon.app/Contents/MacOS/horizon-launcher" ;;
|
||||
*) unzip -q "plan/launcher/$LAUNCHER_ASSET" -d "$scratch/launcher"; launcher="$scratch/launcher/horizon-launcher.exe" ;;
|
||||
esac
|
||||
export HORIZON_LAUNCHER_HOME="$scratch/home" HORIZON_HOME="$scratch/game" XDG_DATA_HOME="$scratch/data" \
|
||||
XDG_CONFIG_HOME="$scratch/config" HORIZON_LAUNCHER_NO_SELF_UPDATE=1 HORIZON_NO_AUDIO=1
|
||||
py=$(command -v python3 || command -v python) # Windows runners name it python
|
||||
"$launcher" install "$TAG"
|
||||
"$launcher" native-build --prebuilt-out "$scratch/out/build" --version "$TAG" --profile release \
|
||||
--trust-native-code plan/hnmods
|
||||
got=$("$py" -c 'import hashlib,sys; print(hashlib.sha256(open(sys.argv[1],"rb").read()).hexdigest())' "$scratch/out/build/request.txt")
|
||||
[ "$got" = "$KEY" ] || { echo "::error::the launcher built request $got, the plan asked for $KEY"; exit 1; }
|
||||
mkdir -p dist
|
||||
tar czf "dist/$ARCHIVE" -C "$scratch/out/build" .
|
||||
ls -l dist
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build-${{ matrix.target }}
|
||||
path: dist/${{ matrix.archive }}
|
||||
if-no-files-found: error
|
||||
sign:
|
||||
needs: [plan, build]
|
||||
if: always() && needs.plan.outputs.any == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
environment: native-builds
|
||||
permissions:
|
||||
contents: write
|
||||
actions: read
|
||||
env:
|
||||
# Job outputs of plan, the only input of this job besides the archives themselves.
|
||||
BASE: ${{ needs.plan.outputs.base }}
|
||||
TAG: ${{ needs.plan.outputs.tag }}
|
||||
MODS: ${{ needs.plan.outputs.mods }}
|
||||
MATRIX: ${{ needs.plan.outputs.matrix }}
|
||||
GH_REPO: ${{ github.repository }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: |
|
||||
scripts/native_build.py
|
||||
prebuilt.toml
|
||||
sparse-checkout-cone-mode: false
|
||||
persist-credentials: false
|
||||
path: registry
|
||||
- name: The signer, at its pinned commit and hashes
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p "$RUNNER_TEMP/signer" && cd "$RUNNER_TEMP/signer"
|
||||
raw="https://raw.githubusercontent.com/horizon-rust/horizon/$SIGN_REV"
|
||||
curl -sSfL -o sign_native_build.sh "$raw/tools/ci/sign_native_build.sh"
|
||||
curl -sSfL -o native-build-keys.txt "$raw/crates/horizon-release-sign/native-build-keys.txt"
|
||||
printf '%s %s\n' "$SIGN_SCRIPT_SHA256" sign_native_build.sh "$SIGN_KEYS_SHA256" native-build-keys.txt | sha256sum -c
|
||||
- name: What to sign, recomputed from the plan job's outputs
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# Checks the mods are a prebuilt.toml set and every row's key is that set's for its target; prints
|
||||
# "<key> <target>". Fails the job on any mismatch.
|
||||
python3 -I registry/scripts/native_build.py signable > "$RUNNER_TEMP/rows"
|
||||
cat "$RUNNER_TEMP/rows"
|
||||
- name: Each target's build, by its exact artifact name
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
while read -r key target <&3; do
|
||||
name="${key:0:16}-$target.tar.gz"
|
||||
dir="$RUNNER_TEMP/builds/$target"
|
||||
mkdir -p "$dir"
|
||||
if ! gh run download "$GITHUB_RUN_ID" --name "build-$target" --dir "$dir"; then
|
||||
echo "::warning::$target: no build"
|
||||
continue
|
||||
fi
|
||||
# Exactly one regular file, named from the key and target; anything else in the artifact is refused.
|
||||
listing=$(cd "$dir" && find . -mindepth 1 -printf '%y %P\n')
|
||||
if [ "$listing" != "f $name" ]; then
|
||||
echo "::error::$target: build-$target holds something other than $name alone:"; echo "$listing"; exit 1
|
||||
fi
|
||||
size=$(stat -c %s "$dir/$name")
|
||||
[ "$size" -le "$MAX_ARCHIVE" ] || { echo "::error::$target: $name is $size bytes, over $MAX_ARCHIVE"; exit 1; }
|
||||
echo "$target: $name, $size bytes, sha256 $(sha256sum < "$dir/$name" | cut -d' ' -f1)"
|
||||
done 3< "$RUNNER_TEMP/rows"
|
||||
- name: Sign, then publish
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
NATIVE_BUILD_SIGNING_KEY: ${{ secrets.NATIVE_BUILD_SIGNING_KEY }}
|
||||
NATIVE_BUILD_SIGNING_KEY_NEXT: ${{ secrets.NATIVE_BUILD_SIGNING_KEY_NEXT }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
signer="$RUNNER_TEMP/signer"
|
||||
# Checked again in the step that holds the key: nothing between the steps may have changed them.
|
||||
(cd "$signer" && printf '%s %s\n' "$SIGN_SCRIPT_SHA256" sign_native_build.sh "$SIGN_KEYS_SHA256" native-build-keys.txt \
|
||||
| sha256sum -c --quiet)
|
||||
release="native-builds-${BASE:0:12}"
|
||||
if ! gh release view "$release" >/dev/null 2>&1; then
|
||||
gh release create "$release" --prerelease --latest=false --title "Prebuilt native builds on $TAG" \
|
||||
--notes "The game built with registry native mods on Horizon $BASE, signed by native-build.yml (horizon#11859). The Horizon launcher downloads and checks these; nothing here is for downloading by hand."
|
||||
fi
|
||||
missing=0
|
||||
while read -r key target <&3; do
|
||||
dir="$RUNNER_TEMP/builds/$target"
|
||||
name="${key:0:16}-$target.tar.gz"
|
||||
sig="${key:0:16}-$target.sig"
|
||||
if [ ! -f "$dir/$name" ]; then missing=1; continue; fi
|
||||
NATIVE_BUILD_KEYS="$signer/native-build-keys.txt" bash "$signer/sign_native_build.sh" "$key" "$target" "$dir/$name"
|
||||
# The signature last: the launcher looks for it first, so a half-uploaded build is never offered.
|
||||
gh release upload "$release" "$dir/$name"
|
||||
gh release upload "$release" "$dir/$sig"
|
||||
done 3< "$RUNNER_TEMP/rows"
|
||||
exit $missing
|
||||
@@ -0,0 +1,130 @@
|
||||
# Native mods on the newest Nightly, as players' launchers build them (scripts/native_compat.py):
|
||||
# - on a pull request, each new native-mod version, applied together with the most downloaded native mods
|
||||
# and compiled (`cargo check`); its support modules' names against every registry mod's;
|
||||
# - every day after the Nightly, every native mod's newest version, applied, then the rest compiled
|
||||
# (`cargo check`): the ones horizon-mixins refuses alone and the ones whose own code doesn't compile go
|
||||
# to the `native-health` release, which publish adds to the index (`broken_on`) so the launcher warns
|
||||
# before an install.
|
||||
# Both compile other people's crates, whose build scripts run: those jobs hold a token that can only read and
|
||||
# run main's script, never the pull request's. The daily result goes to the release from a separate job that
|
||||
# runs nothing of theirs and restores no cache (a build can write cache entries on main).
|
||||
name: native-compat
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths: ["mods/*.toml"]
|
||||
schedule:
|
||||
- cron: "40 18 * * *"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
horizon:
|
||||
description: "Horizon commit, or `latest` (the newest Nightly)"
|
||||
default: latest
|
||||
upload:
|
||||
description: "Publish the result (native-health release); `false` for a trial run"
|
||||
default: "true"
|
||||
permissions:
|
||||
contents: read
|
||||
concurrency:
|
||||
group: native-compat-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
env:
|
||||
# The Rust the launcher builds native mods with (Horizon's launcher/src/rust_toolchain.rs).
|
||||
RUST: "1.99.1"
|
||||
jobs:
|
||||
check:
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 120
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.base.sha }}
|
||||
path: base
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
path: head
|
||||
persist-credentials: false
|
||||
- name: Does it change a native mod?
|
||||
id: native
|
||||
run: |
|
||||
if [ -n "$(python3 base/scripts/native_compat.py new base head)" ]; then
|
||||
echo "changed=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
- if: steps.native.outputs.changed == 'true'
|
||||
run: rustup toolchain install $RUST --profile minimal && rustup default $RUST
|
||||
- if: steps.native.outputs.changed == 'true'
|
||||
run: sudo apt-get update && sudo apt-get install -y libasound2-dev libudev-dev pkg-config
|
||||
- if: steps.native.outputs.changed == 'true'
|
||||
name: On the newest Nightly, with the popular native mods
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -o pipefail
|
||||
python3 base/scripts/native_compat.py check base head | tee -a "$GITHUB_STEP_SUMMARY"
|
||||
health:
|
||||
if: github.event_name != 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
# Each mod horizon-mixins refuses costs one more apply of the whole set.
|
||||
timeout-minutes: 300
|
||||
# Read only: every registry native mod's build script runs in this job.
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- run: python3 -m unittest discover -s scripts -p 'test_*.py'
|
||||
- run: rustup toolchain install $RUST --profile minimal && rustup default $RUST
|
||||
- run: sudo apt-get update && sudo apt-get install -y libasound2-dev libudev-dev pkg-config
|
||||
# Yesterday's dependencies, checked, and horizon-mixins: only Horizon's own crates and the mods compile again.
|
||||
# Safe only because this job holds nothing: it builds untrusted code anyway.
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry/index
|
||||
~/.cargo/registry/cache
|
||||
~/.cargo/git/db
|
||||
native/target/compat/cargo
|
||||
native/target/horizon-mixins
|
||||
key: native-health-${{ env.RUST }}-${{ github.run_id }}
|
||||
restore-keys: native-health-${{ env.RUST }}-
|
||||
- name: Every native mod's newest version on the newest Nightly
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
HORIZON: ${{ inputs.horizon || 'latest' }}
|
||||
run: |
|
||||
set -o pipefail
|
||||
python3 scripts/native_compat.py health --horizon "$HORIZON" --out native-health.json | tee -a "$GITHUB_STEP_SUMMARY"
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: native-health
|
||||
path: native-health.json
|
||||
if-no-files-found: error
|
||||
health-publish:
|
||||
needs: health
|
||||
if: inputs.upload != 'false'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
GH_REPO: ${{ github.repository }}
|
||||
steps:
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: native-health
|
||||
path: health
|
||||
- name: Keep it for publish
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# Data from a job that ran other people's code: only the one file, only the shape publish reads.
|
||||
[ "$(cd health && find . -mindepth 1 -printf '%y %P\n')" = "f native-health.json" ]
|
||||
jq -e '(.horizon | type == "string" and test("^[0-9a-f]{40}$"))
|
||||
and (.broken | type == "object")
|
||||
and ([.broken | to_entries[] | (.key | test("^[a-z0-9_-]+$")) and (.value.version | type == "string")] | all)' \
|
||||
health/native-health.json > /dev/null
|
||||
gh release view native-health >/dev/null 2>&1 \
|
||||
|| gh release create native-health --prerelease --title "Native mods' health" \
|
||||
--notes "native-health.json: the native mods that don't apply on the newest Nightly (native-compat.yml). publish adds them to the index as broken_on."
|
||||
gh release upload native-health health/native-health.json --clobber
|
||||
@@ -0,0 +1,119 @@
|
||||
# Horizon's own native mods (native/<name>/): each changed folder's tests and .hnmod, and the game with it applied
|
||||
# on the Horizon it was tested on; every mod together, as a player's set. Every Monday, all of them on the newest
|
||||
# Nightly, to catch a change of the hook points early.
|
||||
name: native
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths: ["native/**", ".github/workflows/native.yml"]
|
||||
push:
|
||||
branches: [main]
|
||||
paths: ["native/**", ".github/workflows/native.yml"]
|
||||
schedule:
|
||||
- cron: "17 5 * * 1"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
horizon:
|
||||
description: "Horizon commit, tag, or `latest` (the newest Nightly); empty: the mods' tested commit"
|
||||
default: latest
|
||||
permissions:
|
||||
contents: read
|
||||
concurrency:
|
||||
group: native-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
env:
|
||||
# The Rust the launcher builds native mods with (launcher/src/rust_toolchain.rs).
|
||||
RUST: "1.99.1"
|
||||
jobs:
|
||||
changes:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
mods: ${{ steps.mods.outputs.mods }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- id: mods
|
||||
env:
|
||||
BASE: ${{ github.event.pull_request.base.sha || github.event.before }}
|
||||
EVENT: ${{ github.event_name }}
|
||||
run: |
|
||||
all=$(ls -d native/*/compatibility.json | cut -d/ -f2 | jq -R . | jq -sc .)
|
||||
if [ "$EVENT" = schedule ] || [ "$EVENT" = workflow_dispatch ] || [ -z "$BASE" ] \
|
||||
|| ! git cat-file -e "$BASE" 2>/dev/null; then
|
||||
echo "mods=$all" >> "$GITHUB_OUTPUT"; exit 0
|
||||
fi
|
||||
changed=$(git diff --name-only "$BASE" HEAD)
|
||||
# The shared tools or this workflow: every mod.
|
||||
if grep -qE '^(native/tools/|\.github/workflows/native\.yml)' <<< "$changed"; then
|
||||
echo "mods=$all" >> "$GITHUB_OUTPUT"; exit 0
|
||||
fi
|
||||
mods=$(grep -oE '^native/[^/]+/' <<< "$changed" | cut -d/ -f2 | sort -u \
|
||||
| while read -r m; do [ -f "native/$m/compatibility.json" ] && echo "$m"; done | jq -R . | jq -sc .)
|
||||
echo "mods=$mods" >> "$GITHUB_OUTPUT"
|
||||
mod:
|
||||
needs: changes
|
||||
if: needs.changes.outputs.mods != '[]'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
mod: ${{ fromJson(needs.changes.outputs.mods) }}
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: native/${{ matrix.mod }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: rustup toolchain install $RUST --profile minimal --component rustfmt --component clippy && rustup default $RUST
|
||||
- run: cargo fmt --check
|
||||
- run: cargo clippy --all-targets --locked -- -D warnings
|
||||
- run: cargo test --locked
|
||||
- name: Package the .hnmod and check what it holds
|
||||
working-directory: .
|
||||
run: |
|
||||
python3 native/tools/native.py package ${{ matrix.mod }}
|
||||
python3 - <<'PY'
|
||||
import json, zipfile, glob
|
||||
path, = glob.glob("native/target/*.hnmod")
|
||||
names = zipfile.ZipFile(path).namelist()
|
||||
manifest = json.loads(zipfile.ZipFile(path).read("horizon-native.json"))
|
||||
assert manifest["hooks"] >= 1, manifest
|
||||
bundle = f"mixins/{manifest['id']}/"
|
||||
assert all(n == "horizon-native.json" or n == bundle + "LICENSE" or (n.startswith(bundle) and n.endswith((".rs", ".toml"))) for n in names), names
|
||||
assert bundle + "LICENSE" in names, "the mod's LICENSE travels in its package"
|
||||
print(path, manifest)
|
||||
PY
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: hnmod-${{ matrix.mod }}
|
||||
path: native/target/*.hnmod
|
||||
horizon:
|
||||
needs: changes
|
||||
if: needs.changes.outputs.mods != '[]'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
mod: ${{ fromJson(needs.changes.outputs.mods) }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: rustup toolchain install $RUST --profile minimal && rustup default $RUST
|
||||
- run: sudo apt-get update && sudo apt-get install -y libasound2-dev libudev-dev pkg-config
|
||||
- name: Apply on Horizon and check the game
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
HORIZON: ${{ github.event_name == 'schedule' && 'latest' || inputs.horizon || '' }}
|
||||
run: python3 native/tools/native.py check ${{ matrix.mod }} ${HORIZON:+--horizon "$HORIZON"}
|
||||
together:
|
||||
needs: changes
|
||||
if: needs.changes.outputs.mods != '[]'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: rustup toolchain install $RUST --profile minimal && rustup default $RUST
|
||||
- run: sudo apt-get update && sudo apt-get install -y libasound2-dev libudev-dev pkg-config
|
||||
- name: Every native mod applied together, on Horizon
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
HORIZON: ${{ github.event_name == 'schedule' && 'latest' || inputs.horizon || '' }}
|
||||
run: python3 native/tools/native.py check all ${HORIZON:+--horizon "$HORIZON"}
|
||||
@@ -1,11 +1,15 @@
|
||||
# Merging publishes: each new version's file becomes a release asset
|
||||
# (<id>-<version>/<file>), then index.json and the icons go to GitHub Pages
|
||||
# (https://ludovic111.github.io/horizon-mods/index.json), which the launcher and
|
||||
# (<id>-<version>/<file>), then index.json, the icons and the screenshots go to GitHub
|
||||
# Pages (https://horizon-rust.github.io/horizon-mods/index.json), which the launcher and
|
||||
# playhorizon.dev/mods read. Nothing is published if any entry fails its checks.
|
||||
# Every hour it runs again to refresh each project's `downloads` (GitHub's download
|
||||
# counts of its release assets) and `stars` (its source repository's GitHub stars).
|
||||
name: publish
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
schedule:
|
||||
- cron: "23 * * * *"
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -26,19 +30,21 @@ jobs:
|
||||
GH_REPO: ${{ github.repository }}
|
||||
HORIZON_REV: ${{ vars.HORIZON_REV || 'main' }}
|
||||
steps:
|
||||
# No actions/cache in this job, ever: it holds a write token (releases, Pages), and any run on main can
|
||||
# write cache entries, including native-build.yml's and native-compat.yml's builds of other people's
|
||||
# code. Restoring an entry unpacks a tar anywhere on the runner, so the tool is built fresh (about two
|
||||
# minutes) and the files come from the registry-files release, each checked against its SHA-256.
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin
|
||||
~/.cargo/horizon-rev
|
||||
key: cargo-horizon-mod-${{ env.HORIZON_REV }}
|
||||
persist-credentials: false
|
||||
- run: scripts/install-tool.sh
|
||||
- run: scripts/fetch.sh .
|
||||
# FILES_MIRROR_UPLOAD: what isn't in registry-files yet is fetched from its own release once, then kept
|
||||
# there, so these hourly runs don't count as players' downloads.
|
||||
- run: FILES_MIRROR_UPLOAD=1 scripts/fetch.sh .
|
||||
- name: Build the index
|
||||
run: |
|
||||
mkdir -p site
|
||||
cargo horizon-mod registry build . --files files --out site/index.json --generated "$(date -u +%FT%TZ)"
|
||||
cargo horizon-mod registry build . --files files --out site/index.json --site https://horizon-rust.github.io/horizon-mods/ --generated "$(date -u +%FT%TZ)"
|
||||
- name: Publish new files as releases
|
||||
run: |
|
||||
cargo horizon-mod registry list . | while read -r sha id version file; do
|
||||
@@ -49,9 +55,61 @@ jobs:
|
||||
gh release create "$tag" "$dir/$file" --title "$id $version" --latest=false \
|
||||
--notes "Reviewed in the registry: mods/$id.toml. SHA-256 $sha."
|
||||
done
|
||||
- name: Download counts
|
||||
run: |
|
||||
# Each version's file is the asset at its URL: a project's downloads
|
||||
# are its files' counts, every version together.
|
||||
gh api --paginate "repos/$GH_REPO/releases?per_page=100" \
|
||||
--jq '.[].assets[] | {key: .browser_download_url, value: .download_count}' \
|
||||
| jq -s from_entries > counts.json
|
||||
jq --slurpfile counts counts.json '
|
||||
.projects |= map(
|
||||
([.versions[].file.url | $counts[0][.] // 0] | add) as $n
|
||||
| if $n > 0 then .downloads = $n else . end)' site/index.json > index.counted.json
|
||||
mv index.counted.json site/index.json
|
||||
jq -r '.projects[] | "\(.id) \(.downloads // 0)"' site/index.json
|
||||
- name: GitHub stars
|
||||
run: |
|
||||
# A project's stars are its source repository's (owner/repo of a
|
||||
# https://github.com/ source, a folder of it or .git included), asked
|
||||
# once per repository. One that can't be read is left out, not zero.
|
||||
jq -r '.projects[].source // empty' site/index.json \
|
||||
| sed -nE 's#^https://github\.com/([A-Za-z0-9-]+)/([A-Za-z0-9._-]+).*#\1/\2#p' \
|
||||
| sed -E 's/\.git$//' | tr 'A-Z' 'a-z' | sort -u \
|
||||
| while read -r repo; do
|
||||
n="$(gh api "repos/$repo" --jq .stargazers_count 2>/dev/null)" || continue
|
||||
echo "{\"key\": \"$repo\", \"value\": $n}"
|
||||
done | jq -s from_entries > stars.json
|
||||
jq --slurpfile stars stars.json '
|
||||
.projects |= map(
|
||||
(first((.source // "") | ascii_downcase
|
||||
| capture("^https://github[.]com/(?<r>[a-z0-9-]+/[a-z0-9._-]+)").r
|
||||
| sub("[.]git$"; "")) // "") as $r
|
||||
| ($stars[0][$r] // 0) as $n
|
||||
| if $n > 0 then .stars = $n else . end)' site/index.json > index.starred.json
|
||||
mv index.starred.json site/index.json
|
||||
jq -r '.projects[] | "\(.id) \(.stars // 0)"' site/index.json
|
||||
- name: Native mods that don't apply on the newest Nightly
|
||||
run: |
|
||||
# native-compat.yml's daily health: the version each names gets `broken_on`, the Horizon
|
||||
# commit horizon-mixins refused it on and its code, so the launcher warns before an install.
|
||||
if gh release download native-health --pattern native-health.json --dir health 2>/dev/null; then
|
||||
jq --slurpfile h health/native-health.json '
|
||||
$h[0] as $health
|
||||
| .projects |= map(. as $p | .versions |= map(
|
||||
($health.broken[$p.id] // null) as $b
|
||||
| if $b != null and $b.version == .version
|
||||
then .broken_on = [{horizon: $health.horizon, code: $b.code}] else . end))' \
|
||||
site/index.json > index.health.json
|
||||
mv index.health.json site/index.json
|
||||
jq -r '.projects[] | select(.versions[0].broken_on) | "\(.id) \(.versions[0].version): \(.versions[0].broken_on[0].code)"' site/index.json
|
||||
fi
|
||||
- name: The site
|
||||
run: |
|
||||
cp -R icons site/icons
|
||||
# Screenshots are served beside the icons: the index lists them at
|
||||
# <site>screenshots/<id>/<name>.
|
||||
if [ -d screenshots ]; then cp -R screenshots site/screenshots; fi
|
||||
cat > site/index.html <<'HTML'
|
||||
<!doctype html><meta charset="utf-8"><title>Horizon mods</title>
|
||||
<meta http-equiv="refresh" content="0; url=https://playhorizon.dev/mods">
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/files/
|
||||
/site/
|
||||
/bootstrap/
|
||||
/native/target/
|
||||
/native/*/target/
|
||||
@@ -0,0 +1,70 @@
|
||||
# Mod policy
|
||||
|
||||
This registry lists mods, modpacks and packs for [Horizon](https://github.com/horizon-rust/horizon). This page says
|
||||
who makes them, who is responsible for them, and how to have one removed. By submitting to the registry you accept it.
|
||||
|
||||
## Who makes the mods
|
||||
|
||||
- **Mods made by the Horizon project** are marked **Official**: their author is "Horizon" and their source is in a
|
||||
[horizon-rust](https://github.com/horizon-rust) repository. They are the only mods the Horizon
|
||||
project makes and is responsible for.
|
||||
- **Every other mod is made, owned and maintained by its own authors**, named on its page. The Horizon project and
|
||||
its maintainers are not their authors. They don't own these mods, sell them, or license them to you; each mod's
|
||||
licence is the one its authors chose.
|
||||
- **A listing is not an endorsement.** Reviewers check that an entry is complete, that its files match their pinned
|
||||
hashes, that it builds or loads against the Horizon version it names, and that nothing obviously harmful is in it.
|
||||
That review is not a legal review, a security audit or a guarantee. Mods are provided as they are, without
|
||||
warranty, by their authors.
|
||||
- **Native mods** (built into the game with [horizon-mixins](https://github.com/horizon-rust/horizon-mixins)) run
|
||||
with the same access to your computer as the game. Install them only from authors you trust.
|
||||
|
||||
## What authors promise when they submit
|
||||
|
||||
Submitting a project, through the form or a pull request, means you confirm that:
|
||||
|
||||
1. You made it, or you have the right to publish it, under the licence its entry names.
|
||||
2. **If it is a port or a translation of another mod** (for example from Java Edition), the original's licence allows
|
||||
that, or its authors gave you permission. You keep the original's copyright and licence notices, and you credit
|
||||
its authors and link the original on your page. A port of a mod whose licence reserves all rights needs its
|
||||
authors' written permission.
|
||||
3. It contains no code, assets or data from Minecraft, Mojang or Microsoft beyond what their
|
||||
[usage guidelines](https://www.minecraft.net/usage-guidelines) and EULA allow. Horizon players bring their own
|
||||
copy of the game.
|
||||
4. It contains nothing that harms players: no malware, no stealing of accounts or data, no hidden network access.
|
||||
5. The `source` link has the code the published files were built from.
|
||||
|
||||
You alone are responsible for what you publish and for any claim about it. The Horizon project may refuse, hide or
|
||||
remove any project, at any time, without having to give a reason.
|
||||
|
||||
## Licences reviewers can read
|
||||
|
||||
Reviewers read a project's source before each version is listed; that is how they check it holds nothing harmful.
|
||||
They may only read source under a licence that lets them, so a new project, and every new version of a listed one,
|
||||
needs source under one of:
|
||||
|
||||
- MIT, MIT-0, BSD-2-Clause, BSD-3-Clause, 0BSD, Apache-2.0, ISC, zlib, Unlicense or CC0-1.0;
|
||||
- or any licence, with a written grant from its authors that lets the Horizon reviewers read the source to review it
|
||||
(say so in the pull request, with a link to the grant).
|
||||
|
||||
Source under GPL, LGPL, MPL or all rights reserved, without such a grant, can't be reviewed, so it can't be listed or
|
||||
updated. The projects already listed under other licences when this rule was written (classic_zoom,
|
||||
distant_horizons, fly_speed, serverify) stay listed as they are; their next version needs a licence above or a
|
||||
grant. A port also has to follow promise 2 above.
|
||||
|
||||
## Copyright and takedowns
|
||||
|
||||
If a project in this registry uses your work without your permission:
|
||||
|
||||
- [Open a takedown request](../../issues/new?template=takedown.yml) (public), or
|
||||
- write privately to the maintainer through [lu4ovic.com](https://lu4ovic.com/#contact).
|
||||
|
||||
Say which project, which of your work it uses, and how to reach you. A claim that holds gets the project removed and
|
||||
listed in [`revoked.toml`](revoked.toml): the registry stops listing it and the launcher stops offering it and turns
|
||||
installed copies off. Clear claims are acted on quickly. The project's author is told, and can answer the claim in the
|
||||
same issue.
|
||||
|
||||
Authors whose project was removed by mistake can ask for it back in that issue.
|
||||
|
||||
## Changes
|
||||
|
||||
This policy can change. The version in this repository's `main` branch is the one that applies.
|
||||
@@ -1,12 +1,26 @@
|
||||
# Horizon mods
|
||||
|
||||
The registry of what is made for [Horizon](https://github.com/ludovic111/horizon): mods (`.hmod`), modpacks (`.hpack`)
|
||||
The registry of what is made for [Horizon](https://github.com/horizon-rust/horizon): mods (`.hmod`), modpacks (`.hpack`)
|
||||
and resource packs, each one reviewed before it is published. Browse them at
|
||||
[playhorizon.dev/mods](https://playhorizon.dev/mods) or in the launcher (Mods, then Horizon beside Modrinth).
|
||||
|
||||
Players install from here through the launcher, which checks every file's SHA-256 and, when the author signs, its
|
||||
signature before the game sees it. Mods run in Horizon's sandbox, with no file, network or clock access, and can only
|
||||
do what their page lists.
|
||||
signature before the game sees it. Sandboxed `.hmod` mods run in Horizon's sandbox,
|
||||
with no file, network or clock access, and can only do what their page lists.
|
||||
|
||||
Native source mods (`.hnmod`, kind `native-mod`) are built into an isolated game by
|
||||
a launcher with native-mod support. They run with the player’s account permissions.
|
||||
Their bundles pin the exact Horizon and Horizon Mixins revisions used for the build.
|
||||
Other revisions have not been validated. Registry checks inspect the source bundle and compile it
|
||||
on the newest Nightly with the most downloaded native mods (in a job whose token
|
||||
can only read) without running it; reviewers must also examine the source and
|
||||
pinned-build validation. Publishing these requires the native-mod changes to `cargo-horizon-mod`
|
||||
and the launcher to have shipped first.
|
||||
|
||||
**Who is responsible.** Except the projects marked Official (made by the Horizon project), everything here is made and
|
||||
owned by its own authors, not by the Horizon project or its maintainers. A listing is not an endorsement. See the
|
||||
[mod policy](MOD-POLICY.md): what authors promise when they submit (ports of other mods included), the licences
|
||||
whose source reviewers can read, and how to have a project taken down.
|
||||
|
||||
## Submit
|
||||
|
||||
@@ -24,19 +38,21 @@ What the registry takes now: mods, modpacks of registry mods, and resource packs
|
||||
shader pack format is out (Iris and OptiFine packs are on Modrinth). Before you submit a mod, run what the checks run:
|
||||
|
||||
```sh
|
||||
cargo install --git https://github.com/ludovic111/horizon cargo-horizon-mod
|
||||
cargo install --git https://github.com/horizon-rust/horizon cargo-horizon-mod
|
||||
cargo horizon-mod check target/horizon-mod/my_mod.hmod
|
||||
```
|
||||
|
||||
**Updates** come the same way: the form adds a version to your entry. Only an entry's `owners` publish its updates
|
||||
without a reviewer's extra look. A published file never changes: publish a new version.
|
||||
**Updates** come the same way: the form adds a version to your entry, and a reviewer reads it before it is
|
||||
published. Only an entry's `owners` can add versions, and every version of a native mod is reviewed, owner or
|
||||
not: its code runs with the player's account permissions. A published file never changes: publish a new version.
|
||||
|
||||
**Signing** is optional and recommended: put your [minisign](https://jedisct1.github.io/minisign/) public key in your
|
||||
entry, and every later version must be signed with it, so nobody else can publish an update in your name.
|
||||
|
||||
## Report
|
||||
|
||||
Something wrong with a project? [Report it](../../issues/new?template=report.yml). A project that does harm is taken
|
||||
Something wrong with a project? [Report it](../../issues/new?template=report.yml). Your work published without your
|
||||
permission? [Ask for a takedown](../../issues/new?template=takedown.yml) ([mod policy](MOD-POLICY.md)). A project that does harm is taken
|
||||
down in [`revoked.toml`](revoked.toml): the launcher then refuses it and turns installed copies off, telling players
|
||||
why.
|
||||
|
||||
@@ -50,11 +66,33 @@ why.
|
||||
| `.github/workflows/check.yml` | A pull request's checks |
|
||||
| `.github/workflows/publish.yml` | On merge: releases, then `index.json` on Pages |
|
||||
| `.github/workflows/cleanup.yml` | Weekly: staging files nobody needs any more |
|
||||
| `.github/workflows/native-compat.yml` | Native mods on the newest Nightly, as the launcher builds them: a pull request's new version applied and compiled with the most downloaded native mods, its module names against every mod's; daily, every mod's newest version, the ones that don't apply marked `broken_on` in the index (`scripts/native_compat.py`) |
|
||||
| `.github/workflows/native.yml` | Horizon's native mods: each changed one's tests, `.hnmod` and game check, all of them together, and weekly on the newest Nightly |
|
||||
| `native/<name>/` | Horizon's own native mods (Zoom, FPS Counter, Keystrokes, Minimap, Recipes, Coordinates HUD): the source of their `.hnmod` files |
|
||||
| `native/tools/` | Shared by them: apply on Horizon, check, build, package, prove (`native/tools/native.py`) |
|
||||
| `scripts/` | What the workflows share |
|
||||
|
||||
The format of the entries and of `index.json` is
|
||||
[docs/api/mod-index.md](https://github.com/ludovic111/horizon/blob/main/docs/api/mod-index.md) in Horizon's repository;
|
||||
[docs/api/mod-index.md](https://github.com/horizon-rust/horizon/blob/main/docs/api/mod-index.md) in Horizon's repository;
|
||||
the checks are `cargo horizon-mod check` and `cargo horizon-mod registry`, from Horizon at the commit in the
|
||||
repository variable `HORIZON_REV` (else `main`).
|
||||
|
||||
The index: `https://ludovic111.github.io/horizon-mods/index.json`.
|
||||
The index: `https://horizon-rust.github.io/horizon-mods/index.json`.
|
||||
|
||||
## Horizon's native mods
|
||||
|
||||
`native/<name>/` holds the source of Horizon's official mods, as
|
||||
[horizon-mixins](https://github.com/horizon-rust/horizon-mixins) bundles on Horizon's native hook points
|
||||
([docs/native-mods.md](https://github.com/horizon-rust/horizon/blob/main/docs/native-mods.md)). Each folder keeps the
|
||||
history of the repository it came from. A mod builds on any Horizon whose hook points are at least the version its
|
||||
`compatibility.json` names; `horizon_revision` is the commit it was last tested on. `smp` and `voicechat-server`
|
||||
are server mods, built into the official server's engine (see their READMEs).
|
||||
|
||||
```sh
|
||||
python3 native/tools/native.py check zoom # apply on the tested Horizon and cargo check the game
|
||||
python3 native/tools/native.py check all # every native mod together
|
||||
python3 native/tools/native.py package all # native/target/<id>-<version>.hnmod, for a registry entry
|
||||
python3 native/tools/native.py proof zoom --resources /path/to/.local/horizon-client/26.3
|
||||
```
|
||||
|
||||
To make your own, start from [horizon-native-mod-template](https://github.com/horizon-rust/horizon-native-mod-template).
|
||||
|
After Width: | Height: | Size: 782 B |
|
After Width: | Height: | Size: 208 KiB |
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
|
After Width: | Height: | Size: 193 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 805 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 543 B |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 975 B |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 687 B |
|
After Width: | Height: | Size: 150 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 198 KiB |
|
After Width: | Height: | Size: 85 KiB |
|
After Width: | Height: | Size: 245 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 100 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 855 B |
|
After Width: | Height: | Size: 211 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 8.8 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 107 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 660 B |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 195 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 212 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 9.5 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 644 B |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 221 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.2 KiB |