1010 description : " Repository path containing the small CurseForge manifest.json (overrides optional)"
1111 required : true
1212 default : " modpack/Mars"
13+ source_release :
14+ description : " Optional existing release tag containing an already signed manifest pair"
15+ required : false
16+ default : " "
17+ payload_asset :
18+ description : " Optional JAR asset in that release, installed under mods after hash verification"
19+ required : false
20+ default : " "
1321 prerelease :
1422 description : " Publish as a pre-release (not served to users)"
1523 type : boolean
3038 steps :
3139 - uses : actions/checkout@v4
3240
41+ - name : Test staged payload deployment
42+ run : python3 -m unittest discover -s scripts -p test_deploy_pack_release.py
43+
44+ - name : Download existing signed pack
45+ if : ${{ inputs.source_release != '' }}
46+ env :
47+ GH_TOKEN : ${{ github.token }}
48+ SOURCE_RELEASE : ${{ inputs.source_release }}
49+ PACK_VERSION : ${{ inputs.pack_version }}
50+ PAYLOAD_ASSET : ${{ inputs.payload_asset }}
51+ shell : bash
52+ run : |
53+ set -euo pipefail
54+ [[ "$SOURCE_RELEASE" =~ ^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$ ]]
55+ [[ "$PACK_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]
56+ mkdir -p manifest-dist
57+ gh release download "$SOURCE_RELEASE" --repo "$GITHUB_REPOSITORY" \
58+ --pattern manifest.json --pattern manifest.json.sig --dir manifest-dist
59+ python3 - <<'PY'
60+ import json, os
61+ from pathlib import Path
62+ path = Path("manifest-dist/manifest.json")
63+ assert path.stat().st_size <= 8 * 1024 * 1024, "Manifest exceeds 8 MiB"
64+ manifest = json.loads(path.read_text(encoding="utf-8"))
65+ assert manifest["packVersion"] == os.environ["PACK_VERSION"], "Pack version differs"
66+ assert manifest["minecraftVersion"] == "1.21.1", "Minecraft version differs"
67+ assert manifest["loader"] == "neoforge" and manifest["loaderVersion"] == "21.1.250", "Loader differs"
68+ PY
69+ if [[ -n "$PAYLOAD_ASSET" ]]; then
70+ [[ "$PAYLOAD_ASSET" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*\.jar$ ]]
71+ mkdir -p release-payload
72+ gh release download "$SOURCE_RELEASE" --repo "$GITHUB_REPOSITORY" \
73+ --pattern "$PAYLOAD_ASSET" --dir release-payload
74+ python3 - <<'PY'
75+ import hashlib, json, os
76+ from pathlib import Path
77+ name = os.environ["PAYLOAD_ASSET"]
78+ entries = json.loads(Path("manifest-dist/manifest.json").read_text(encoding="utf-8"))["files"]
79+ matches = [entry for entry in entries if entry["path"] == "mods/" + name]
80+ assert len(matches) == 1, "Payload must be declared exactly once"
81+ entry = matches[0]
82+ path = Path("release-payload") / name
83+ assert path.stat().st_size <= 64 * 1024 * 1024, "Payload exceeds 64 MiB"
84+ assert path.stat().st_size == entry["size"], "Payload size differs"
85+ assert hashlib.sha256(path.read_bytes()).hexdigest() == entry["sha256"], "Payload hash differs"
86+ PY
87+ fi
88+
3389 - uses : dtolnay/rust-toolchain@stable
3490
3591 - name : Install Tauri Linux dependencies
53109 workspaces : src-tauri
54110
55111 - name : Build manifest from CurseForge export
112+ if : ${{ inputs.source_release == '' }}
56113 working-directory : src-tauri
57114 env :
58115 CURSEFORGE_API_KEY : ${{ secrets.CURSEFORGE_API_KEY }}
72129 ../manifest-dist/manifest.json
73130
74131 - name : Sign manifest
132+ if : ${{ inputs.source_release == '' }}
75133 working-directory : src-tauri
76134 env :
77135 MARS_SIGNING_KEY : ${{ secrets.MARS_SIGNING_KEY }}
90148 run : cargo run --release --example manifest_tool -- verify ../manifest-dist/manifest.json
91149
92150 - name : Publish release
151+ if : ${{ inputs.source_release == '' }}
93152 uses : softprops/action-gh-release@v2
94153 with :
95154 tag_name : pack-v${{ inputs.pack_version }}-manifest-${{ github.run_number }}
@@ -107,6 +166,7 @@ jobs:
107166 MARS_DEPLOY_USER : ${{ secrets.MARS_DEPLOY_USER }}
108167 MARS_DEPLOY_SSH_KEY : ${{ secrets.MARS_DEPLOY_SSH_KEY }}
109168 MARS_DEPLOY_KNOWN_HOSTS : ${{ secrets.MARS_DEPLOY_KNOWN_HOSTS }}
169+ PAYLOAD_ASSET : ${{ inputs.payload_asset }}
110170 run : |
111171 set -euo pipefail
112172 : "${MARS_DEPLOY_HOST:?MARS_DEPLOY_HOST secret is not set}"
@@ -155,11 +215,19 @@ jobs:
155215 manifest-dist/manifest.json.sig \
156216 "${REMOTE}:${STAGING_DIR}/"
157217
218+ if [[ -n "$PAYLOAD_ASSET" ]]; then
219+ [[ "$PAYLOAD_ASSET" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*\.jar$ ]]
220+ ssh "${SSH_ARGS[@]}" "$REMOTE" "mkdir -p '$STAGING_DIR/mods'"
221+ scp -i "$KEY_FILE" -P 22 \
222+ -o IdentitiesOnly=yes \
223+ -o BatchMode=yes \
224+ -o StrictHostKeyChecking=yes \
225+ -o "UserKnownHostsFile=$KNOWN_HOSTS_FILE" \
226+ "release-payload/$PAYLOAD_ASSET" "${REMOTE}:${STAGING_DIR}/mods/"
227+ fi
228+
158229 ssh "${SSH_ARGS[@]}" "$REMOTE" \
159- "install -m 0644 '$STAGING_DIR/manifest.json.sig' '$RELEASE_DIR/.manifest.json.sig.next-${RUN_SUFFIX}' && \
160- install -m 0644 '$STAGING_DIR/manifest.json' '$RELEASE_DIR/.manifest.json.next-${RUN_SUFFIX}' && \
161- mv -f '$RELEASE_DIR/.manifest.json.sig.next-${RUN_SUFFIX}' '$RELEASE_DIR/manifest.json.sig' && \
162- mv -f '$RELEASE_DIR/.manifest.json.next-${RUN_SUFFIX}' '$RELEASE_DIR/manifest.json'"
230+ "python3 - '$RELEASE_DIR' '$STAGING_DIR' '$RUN_SUFFIX'" < scripts/deploy-pack-release.py
163231 ssh "${SSH_ARGS[@]}" "$REMOTE" \
164232 "sudo -n systemctl restart mars-package-api"
165233
0 commit comments