android: uploadShx sends the build commit to the SHX VPN panel: upload-apk.mjs takes it from --commit, SHX_COMMIT or git rev-parse HEAD of the repository the APK lies in, and warns when the working copy has uncommitted changes; the panel shows it in the version card and gives it to a connected model through MCP, so a report on a version leads to the code that version was built from
Android / pre-check (push) Failing after 5s
Android / android (push) Skipped
CIFuzz / pre-check (push) Failing after 3s
CIFuzz / fuzzing (address) (push) Skipped
CIFuzz / fuzzing (undefined) (push) Skipped
CodeQL / pre-check (push) Failing after 3s
CodeQL / analyze (python) (push) Skipped
CodeQL / analyze (ruby) (push) Skipped
CodeQL / analyze-cpp (push) Skipped
FreeBSD / pre-check (push) Failing after 4s
FreeBSD / freebsd (14.4) (push) Skipped
FreeBSD / freebsd (15.0) (push) Skipped
Linux / pre-check (push) Failing after 4s
Linux / latest (apidoc) (push) Skipped
Linux / latest (clang, no, all) (push) Skipped
Linux / latest (clang, no, default) (push) Skipped
Linux / latest (clang, no, printf-builtin) (push) Skipped
Linux / latest (clang, no-dbg) (push) Skipped
Linux / latest (clang, no-testable-ke) (push) Skipped
Linux / latest (clang, yes, all) (push) Skipped
Linux / latest (clang, yes, default) (push) Skipped
Linux / latest (clang, yes, fuzzing) (push) Skipped
Linux / latest (coverage) (push) Skipped
Linux / latest (dist) (push) Skipped
Linux / latest (gcc, no, all) (push) Skipped
Linux / latest (gcc, no, default) (push) Skipped
Linux / latest (gcc, no, printf-builtin) (push) Skipped
Linux / latest (gcc, yes, all) (push) Skipped
Linux / latest (gcc, yes, default) (push) Skipped
Linux / latest (nm) (push) Skipped
Linux / latest (no-dbg) (push) Skipped
Linux / latest (no-testable-ke) (push) Skipped
Linux / latest (yes, ld) (push) Skipped
Linux / crypto (ubuntu-22.04, openssl-sys) (push) Skipped
Linux / crypto (ubuntu-latest, botan) (push) Skipped
Linux / crypto (ubuntu-latest, openssl-3) (push) Skipped
Linux / crypto (ubuntu-latest, openssl-4) (push) Skipped
Linux / crypto (ubuntu-latest, openssl-awslc) (push) Skipped
Linux / crypto (ubuntu-latest, openssl-sys) (push) Skipped
Linux / crypto (ubuntu-latest, wolfssl) (push) Skipped
Linux / older (clang, ubuntu-22.04, all) (push) Skipped
Linux / older (gcc, ubuntu-22.04, all) (push) Skipped
Linux / older (gcc, ubuntu-22.04, nm) (push) Skipped
Linux / alpine (push) Skipped
macOS / pre-check (push) Failing after 3s
macOS / macos (macos-14) (push) Skipped
macOS / macos (macos-latest) (push) Skipped
SonarCloud / pre-check (push) Failing after 4s
SonarCloud / sonarcloud (push) Skipped
TKM / pre-check (push) Failing after 3s
TKM / tkm (push) Skipped
Windows / pre-check (push) Failing after 3s
Windows / cross-compile (win32) (push) Skipped
Windows / cross-compile (win64) (push) Skipped
Windows / native (i686, mingw32, win32) (push) Skipped
Windows / native (x86_64, mingw64, win64) (push) Skipped

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
DenozordecandClaude Fable 5.2 committed 2026-11-14 09:14:38 +07:00
1 parent fdef56fa29
commit 20f9040e48
1 file changed
+15 -2
+15 -2
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env node #!/usr/bin/env node
// Загрузка собранного APK в панель SHX VPN с компьютера сборки. Нужен только Node 18+, без зависимостей. // Загрузка собранного APK в панель SHX VPN с компьютера сборки. Нужен только Node 18+, без зависимостей.
// //
// node upload-apk.mjs app-release.apk --type phone --channel beta [--publish] [--rollout 100] [--notes "Что нового"] // node upload-apk.mjs app-release.apk --type phone --channel beta [--publish] [--rollout 100] [--notes "Что нового"] [--commit <хэш>]
// //
// Окружение: // Окружение:
// SHX_URL адрес консоли, например https://apk.shnt.top // SHX_URL адрес консоли, например https://apk.shnt.top
@@ -9,6 +9,8 @@
// //
// versionName и versionCode берутся из output-metadata.json, который Gradle кладёт рядом с APK, // versionName и versionCode берутся из output-metadata.json, который Gradle кладёт рядом с APK,
// либо из --version-name / --version-code. // либо из --version-name / --version-code.
// Коммит сборки (по нему нейросеть через MCP открывает код нужной версии) берётся из --commit, SHX_COMMIT
// или из git-репозитория, в котором лежит APK.
// //
// Шаги: билет на загрузку → файл напрямую в S3 по presigned PUT → сервер проверяет SHA-256 и оформляет версию. // Шаги: билет на загрузку → файл напрямую в S3 по presigned PUT → сервер проверяет SHA-256 и оформляет версию.
import { execFileSync } from "node:child_process"; import { execFileSync } from "node:child_process";
@@ -28,6 +30,7 @@ const { values: o, positionals } = parseArgs({
"notes-en": { type: "string", default: "" }, "notes-en": { type: "string", default: "" },
publish: { type: "boolean", default: false }, publish: { type: "boolean", default: false },
rollout: { type: "string", default: "100" }, rollout: { type: "string", default: "100" },
commit: { type: "string" },
help: { type: "boolean", short: "h", default: false }, help: { type: "boolean", short: "h", default: false },
}, },
}); });
@@ -72,6 +75,15 @@ if ((!versionName || !versionCode) && existsSync(meta)) {
} }
if (!versionName || !Number.isInteger(versionCode)) die("Не удалось определить версию: передайте --version-name и --version-code"); if (!versionName || !Number.isInteger(versionCode)) die("Не удалось определить версию: передайте --version-name и --version-code");
const git = (...args) => {
try { return execFileSync("git", args, { cwd: dirname(file), encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim(); } catch { return ""; }
};
const commit = (o.commit ?? process.env.SHX_COMMIT ?? git("rev-parse", "HEAD")).toLowerCase();
if (commit && !/^[0-9a-f]{7,40}$/.test(commit)) die("--commit: ожидается хэш git (7–40 шестнадцатеричных символов)");
if (!commit) console.warn("! коммит сборки не определён (APK вне git-репозитория): версия загрузится без него");
// сборка с правками поверх коммита: код по хэшу будет не тем, что в APK
else if (!o.commit && !process.env.SHX_COMMIT && git("status", "--porcelain", "--untracked-files=no")) console.warn("! в рабочей копии есть незакоммиченные изменения: коммит " + commit.slice(0, 12) + " не точно соответствует сборке");
const bytes = readFileSync(file); const bytes = readFileSync(file);
const sha256 = createHash("sha256").update(bytes).digest("hex"); const sha256 = createHash("sha256").update(bytes).digest("hex");
const mb = (statSync(file).size / 1048576).toFixed(1); const mb = (statSync(file).size / 1048576).toFixed(1);
@@ -87,7 +99,7 @@ async function api(path, body) {
return data; return data;
} }
console.log(`→ ${o.type} / ${o.channel} / ${versionName} (${versionCode}), ${mb} МБ, sha256 ${sha256.slice(0, 12)}…`); console.log(`→ ${o.type} / ${o.channel} / ${versionName} (${versionCode}), ${mb} МБ, sha256 ${sha256.slice(0, 12)}…${commit ? `, коммит ${commit.slice(0, 12)}` : ""}`);
const ticket = await api("/api/admin/versions/uploads", { typeId: o.type, channel: o.channel, versionName, versionCode }); const ticket = await api("/api/admin/versions/uploads", { typeId: o.type, channel: o.channel, versionName, versionCode });
if (bytes.length > ticket.maxBytes) die("Файл больше допустимого размера"); if (bytes.length > ticket.maxBytes) die("Файл больше допустимого размера");
@@ -102,6 +114,7 @@ const v = await api("/api/admin/versions", {
notes: { ru: o.notes, en: o["notes-en"] }, notes: { ru: o.notes, en: o["notes-en"] },
publish: o.publish, publish: o.publish,
rolloutPct, rolloutPct,
...(commit ? { commit } : {}),
}); });
console.log(`✓ версия оформлена: ${v.versionName}, статус ${v.status}${o.publish ? `, раскатка ${rolloutPct}%` : " (черновик: опубликуйте в консоли)"}`); console.log(`✓ версия оформлена: ${v.versionName}, статус ${v.status}${o.publish ? `, раскатка ${rolloutPct}%` : " (черновик: опубликуйте в консоли)"}`);
console.log(` ${base}/versions/${v.id}`); console.log(` ${base}/versions/${v.id}`);