mirror of
git://f0xx.org/android_cast
synced 2026-07-29 02:18:40 +03:00
sync BE
This commit is contained in:
@@ -36,6 +36,14 @@ try {
|
||||
$build = BuildRunner::rerunBuild($buildId, $override, $userId);
|
||||
json_out(['ok' => true, 'build' => $build]);
|
||||
}
|
||||
if ($action === 'rerun_from_failed') {
|
||||
$build = BuildRunner::rerunFromFailed($buildId, $userId, false);
|
||||
json_out(['ok' => true, 'build' => $build, 'resume_from' => BuildRunner::detectFailedStep($buildId)]);
|
||||
}
|
||||
if ($action === 'rerun_with_ssh') {
|
||||
$build = BuildRunner::rerunFromFailed($buildId, $userId, true);
|
||||
json_out(['ok' => true, 'build' => $build, 'resume_from' => BuildRunner::detectFailedStep($buildId)]);
|
||||
}
|
||||
json_out(['ok' => false, 'error' => 'unknown_action'], 400);
|
||||
} catch (Throwable $e) {
|
||||
error_log('build_actions: ' . $e->getMessage());
|
||||
|
||||
@@ -79,29 +79,43 @@
|
||||
}
|
||||
|
||||
var rerunBtn = document.getElementById('build-rerun-btn');
|
||||
var rerunFailedBtn = document.getElementById('build-rerun-failed-btn');
|
||||
var rerunSshBtn = document.getElementById('build-rerun-ssh-btn');
|
||||
var stopBtn = document.getElementById('build-stop-btn');
|
||||
var rerunAsToggle = document.getElementById('build-rerun-as-toggle');
|
||||
var rerunAsForm = document.getElementById('build-rerun-as-form');
|
||||
var actionStatus = document.getElementById('build-action-status');
|
||||
|
||||
function goToBuild(j) {
|
||||
if (j.build && j.build.id) {
|
||||
window.location.href = base + '/?view=build&id=' + j.build.id;
|
||||
}
|
||||
}
|
||||
|
||||
function bindRerunAction(btn, action, confirmMsg) {
|
||||
if (!btn) return;
|
||||
btn.addEventListener('click', function () {
|
||||
var id = Number(btn.getAttribute('data-build-id'));
|
||||
if (!id) return;
|
||||
if (confirmMsg && !confirm(confirmMsg)) return;
|
||||
if (actionStatus) actionStatus.textContent = 'Starting…';
|
||||
postBuildAction({ action: action, build_id: id }).then(goToBuild).catch(function (err) {
|
||||
if (actionStatus) actionStatus.textContent = err.message || 'Failed';
|
||||
});
|
||||
});
|
||||
}
|
||||
if (rerunAsToggle && rerunAsForm) {
|
||||
rerunAsToggle.addEventListener('click', function () {
|
||||
rerunAsForm.hidden = !rerunAsForm.hidden;
|
||||
});
|
||||
}
|
||||
if (rerunBtn) {
|
||||
rerunBtn.addEventListener('click', function () {
|
||||
var id = Number(rerunBtn.getAttribute('data-build-id'));
|
||||
if (!id) return;
|
||||
if (actionStatus) actionStatus.textContent = 'Re-running…';
|
||||
postBuildAction({ action: 'rerun', build_id: id }).then(function (j) {
|
||||
if (j.build && j.build.id) {
|
||||
window.location.href = base + '/?view=build&id=' + j.build.id;
|
||||
}
|
||||
}).catch(function (err) {
|
||||
if (actionStatus) actionStatus.textContent = err.message || 'Failed';
|
||||
});
|
||||
});
|
||||
}
|
||||
bindRerunAction(rerunBtn, 'rerun', null);
|
||||
bindRerunAction(rerunFailedBtn, 'rerun_from_failed', null);
|
||||
bindRerunAction(
|
||||
rerunSshBtn,
|
||||
'rerun_with_ssh',
|
||||
'Start an SSH debug session on the builder? You will need shell access to the builder host.'
|
||||
);
|
||||
if (stopBtn) {
|
||||
stopBtn.addEventListener('click', function () {
|
||||
var id = Number(stopBtn.getAttribute('data-build-id'));
|
||||
@@ -170,6 +184,8 @@
|
||||
var eof = j.log && j.log.eof;
|
||||
if (running || !eof) {
|
||||
setTimeout(poll, running ? 1000 : 250);
|
||||
} else if (j.build && j.build.phase === 'ssh_debug' && !document.getElementById('build-ssh-panel')) {
|
||||
window.location.reload();
|
||||
}
|
||||
}).catch(function () { setTimeout(poll, 2000); });
|
||||
}
|
||||
|
||||
@@ -130,9 +130,10 @@ YAML;
|
||||
return BuildRepository::getById($id) ?? ['id' => $id, 'build_code' => $buildCode, 'status' => 'failed'];
|
||||
}
|
||||
|
||||
$phase = !empty($params['ssh_debug']) ? 'ssh_debug' : 'docker';
|
||||
BuildRepository::update($id, [
|
||||
'status' => 'running',
|
||||
'phase' => 'docker',
|
||||
'phase' => $phase,
|
||||
'started_at' => gmdate('Y-m-d H:i:s'),
|
||||
]);
|
||||
|
||||
@@ -163,6 +164,16 @@ YAML;
|
||||
'OTA_CHANNEL=' . escapeshellarg((string) ($params['ota_channel'] ?? 'staging')),
|
||||
'AUTO_OTA=' . (!empty($params['auto_ota']) ? '1' : '0'),
|
||||
];
|
||||
if (!empty($params['resume_from'])) {
|
||||
$env[] = 'BUILD_RESUME_FROM=' . escapeshellarg((string) $params['resume_from']);
|
||||
}
|
||||
if (!empty($params['reuse_build_id'])) {
|
||||
$reuseSrc = self::artifactDir((int) $params['reuse_build_id']) . '/src';
|
||||
$env[] = 'BUILD_REUSE_SRC_DIR=' . escapeshellarg($reuseSrc);
|
||||
}
|
||||
if (!empty($params['ssh_debug'])) {
|
||||
$env[] = 'BUILD_SSH_DEBUG=1';
|
||||
}
|
||||
$cmd = implode(' ', $env) . ' nohup ' . escapeshellarg($runner) . ' >> ' . escapeshellarg($logPath) . ' 2>&1 & echo $!';
|
||||
$pid = trim((string) shell_exec($cmd));
|
||||
file_put_contents($dir . '/runner.pid', $pid);
|
||||
@@ -193,6 +204,7 @@ YAML;
|
||||
if ($pid !== '' && ctype_digit($pid)) {
|
||||
shell_exec('kill ' . escapeshellarg($pid) . ' 2>/dev/null');
|
||||
}
|
||||
self::stopDebugContainer($id);
|
||||
$logPath = $build['log_path'] ?? ($dir . '/build.log');
|
||||
if ($logPath) {
|
||||
file_put_contents($logPath, "\n[builder] Stopped by operator\n", FILE_APPEND);
|
||||
@@ -222,10 +234,78 @@ YAML;
|
||||
if (empty($params['git_ref'])) {
|
||||
$params['git_ref'] = $build['git_ref'] ?? $build['branch'] ?? 'next';
|
||||
}
|
||||
$params['rerun_of'] = $id;
|
||||
if (!isset($params['rerun_of'])) {
|
||||
$params['rerun_of'] = $id;
|
||||
}
|
||||
return self::enqueue($params, $userId);
|
||||
}
|
||||
|
||||
/** CircleCI-style: resume from the step that failed (git → docker-build → docker-run). */
|
||||
public static function detectFailedStep(int $buildId): string {
|
||||
$dir = self::artifactDir($buildId);
|
||||
$logTail = '';
|
||||
$logPath = $dir . '/build.log';
|
||||
if (is_file($logPath)) {
|
||||
$size = filesize($logPath) ?: 0;
|
||||
$logTail = (string) file_get_contents($logPath, false, null, max(0, $size - 65536));
|
||||
}
|
||||
$hasRun = is_file($dir . '/docker-run.log') && (filesize($dir . '/docker-run.log') ?: 0) > 0;
|
||||
$hasBuild = is_file($dir . '/docker-build.log') && (filesize($dir . '/docker-build.log') ?: 0) > 0;
|
||||
|
||||
if ($hasRun && !is_file($dir . '/android_cast-latest.apk')) {
|
||||
return 'docker-run';
|
||||
}
|
||||
if ($hasBuild || str_contains($logTail, '[runner] step docker-build')) {
|
||||
if (!str_contains($logTail, '[runner] step docker-run') && !str_contains($logTail, '[runner] finished')) {
|
||||
return 'docker-build';
|
||||
}
|
||||
$buildStep = $hasBuild ? (string) file_get_contents($dir . '/docker-build.log') : '';
|
||||
if (str_contains($logTail, 'ERROR: failed to build') || str_contains($buildStep, 'ERROR: failed to build')) {
|
||||
return 'docker-build';
|
||||
}
|
||||
}
|
||||
if (str_contains($logTail, 'isolate:') || str_contains($logTail, 'syncing shared checkout')
|
||||
|| str_contains($logTail, 'dubious ownership') || is_file($dir . '/git-clone.log')) {
|
||||
return 'git';
|
||||
}
|
||||
return $hasRun ? 'docker-run' : ($hasBuild ? 'docker-build' : 'git');
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
public static function rerunFromFailed(int $id, ?int $userId = null, bool $sshDebug = false): array {
|
||||
$build = BuildRepository::getById($id);
|
||||
if (!$build) {
|
||||
throw new InvalidArgumentException('build_not_found');
|
||||
}
|
||||
$status = (string) ($build['status'] ?? '');
|
||||
if (!in_array($status, ['failed', 'cancelled'], true)) {
|
||||
throw new InvalidArgumentException('rerun_from_failed_requires_failed_build');
|
||||
}
|
||||
$step = self::detectFailedStep($id);
|
||||
return self::rerunBuild($id, [
|
||||
'resume_from' => $step,
|
||||
'reuse_build_id' => $id,
|
||||
'ssh_debug' => $sshDebug,
|
||||
'rerun_of' => $id,
|
||||
], $userId);
|
||||
}
|
||||
|
||||
public static function debugContainerName(int $buildId): string {
|
||||
return 'androidcast-bld-' . $buildId;
|
||||
}
|
||||
|
||||
public static function debugContainerAlive(int $buildId): bool {
|
||||
$bin = escapeshellarg(self::dockerBinary());
|
||||
$name = escapeshellarg(self::debugContainerName($buildId));
|
||||
return trim((string) shell_exec("{$bin} inspect -f '{{.State.Running}}' {$name} 2>/dev/null")) === 'true';
|
||||
}
|
||||
|
||||
public static function stopDebugContainer(int $buildId): void {
|
||||
$bin = escapeshellarg(self::dockerBinary());
|
||||
$name = escapeshellarg(self::debugContainerName($buildId));
|
||||
shell_exec("{$bin} rm -f {$name} 2>/dev/null");
|
||||
}
|
||||
|
||||
public static function runnerAlive(int $buildId): bool {
|
||||
$pidFile = self::artifactDir($buildId) . '/runner.pid';
|
||||
$pid = is_file($pidFile) ? trim((string) file_get_contents($pidFile)) : '';
|
||||
@@ -258,6 +338,15 @@ YAML;
|
||||
$size = filesize($logPath) ?: 0;
|
||||
$tail = (string) file_get_contents($logPath, false, null, max(0, $size - 8192));
|
||||
}
|
||||
$modeFile = $dir . '/runner.mode';
|
||||
if (is_file($modeFile) && trim((string) file_get_contents($modeFile)) === 'ssh_debug') {
|
||||
if (self::debugContainerAlive($id)) {
|
||||
BuildRepository::update($id, ['status' => 'running', 'phase' => 'ssh_debug']);
|
||||
continue;
|
||||
}
|
||||
self::failBuild($id, 'SSH debug container ended', $logPath);
|
||||
continue;
|
||||
}
|
||||
if (self::runnerAlive($id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -2,14 +2,25 @@
|
||||
$artifacts = json_decode($build['artifacts_json'] ?? '{}', true) ?: [];
|
||||
$params = json_decode($build['params_json'] ?? '{}', true) ?: [];
|
||||
$status = (string) ($build['status'] ?? '');
|
||||
$phase = (string) ($build['phase'] ?? '');
|
||||
$isRunning = $status === 'running';
|
||||
$canRerunFromFailed = in_array($status, ['failed', 'cancelled'], true);
|
||||
$failedStep = $canRerunFromFailed ? BuildRunner::detectFailedStep((int) ($build['id'] ?? 0)) : '';
|
||||
$isSshDebug = $phase === 'ssh_debug' || ($isRunning && !empty($params['ssh_debug']));
|
||||
$sshContainer = BuildRunner::debugContainerName((int) ($build['id'] ?? 0));
|
||||
$builderHost = (string) ($build['builder_id'] ?? gethostname() ?: 'builder');
|
||||
$pipelineYaml = trim((string) ($build['pipeline_yaml'] ?? ''));
|
||||
if ($pipelineYaml === '') {
|
||||
$pipelineYaml = '(none — will be generated on next trigger from build.config.yml or params)';
|
||||
}
|
||||
$stepLabels = [
|
||||
'git' => 'Git checkout / shallow clone',
|
||||
'docker-build' => 'Docker image build',
|
||||
'docker-run' => 'Gradle pipeline (docker run)',
|
||||
];
|
||||
?>
|
||||
<h1>Build <?= h($build['build_code'] ?? '') ?></h1>
|
||||
<p class="muted">Status: <strong><?= h($status) ?></strong> · Phase: <?= h($build['phase'] ?? '') ?>
|
||||
<p class="muted">Status: <strong><?= h($status) ?></strong> · Phase: <?= h($phase) ?>
|
||||
<?php if (!empty($build['error_message'])): ?>
|
||||
· <span class="error-text"><?= h($build['error_message']) ?></span>
|
||||
<?php endif; ?>
|
||||
@@ -21,17 +32,34 @@ if ($status === 'failed' && $dockerFail && $healthDocker):
|
||||
?>
|
||||
<p class="muted graphs-footnote">Ecosystem health reports <strong>Docker: ok</strong> now. The log below is from this failed run — use <strong>Re-run</strong> (do not only refresh).</p>
|
||||
<?php endif; ?>
|
||||
<?php if ($canRerunFromFailed && $failedStep !== ''): ?>
|
||||
<p class="muted graphs-footnote">Failed at step: <strong><?= h($stepLabels[$failedStep] ?? $failedStep) ?></strong> — <em>Re-run from failed</em> skips earlier steps when artifacts allow (like CircleCI).</p>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="build-actions card card--lift" style="margin-bottom:12px">
|
||||
<h2>Actions</h2>
|
||||
<div class="toolbar-actions" style="flex-wrap:wrap;gap:8px">
|
||||
<a class="btn" href="<?= h(Auth::basePath()) ?>/">← Back</a>
|
||||
<button type="button" class="btn" id="build-rerun-btn" data-build-id="<?= (int) $build['id'] ?>">Re-run</button>
|
||||
<?php if ($canRerunFromFailed): ?>
|
||||
<button type="button" class="btn" id="build-rerun-failed-btn" data-build-id="<?= (int) $build['id'] ?>" title="Resume from <?= h($failedStep) ?>">Re-run from failed</button>
|
||||
<button type="button" class="btn" id="build-rerun-ssh-btn" data-build-id="<?= (int) $build['id'] ?>" title="Resume from failed step, then open an interactive container (CircleCI-style SSH debug)">Re-run with SSH</button>
|
||||
<?php endif; ?>
|
||||
<button type="button" class="btn" id="build-rerun-as-toggle">Re-run as…</button>
|
||||
<?php if ($isRunning): ?>
|
||||
<button type="button" class="btn btn-danger" id="build-stop-btn" data-build-id="<?= (int) $build['id'] ?>">Stop</button>
|
||||
<button type="button" class="btn btn-danger" id="build-stop-btn" data-build-id="<?= (int) $build['id'] ?>"><?= $isSshDebug ? 'End SSH session' : 'Stop' ?></button>
|
||||
<?php endif; ?>
|
||||
<span id="build-action-status" class="muted"></span>
|
||||
</div>
|
||||
<?php if ($isSshDebug): ?>
|
||||
<div class="build-ssh-panel" id="build-ssh-panel">
|
||||
<strong>SSH debug session</strong>
|
||||
<p class="muted graphs-footnote" style="margin:6px 0 0">Shell into the CI container on the builder host (same idea as CircleCI “Rerun with SSH”). Stop ends the session.</p>
|
||||
<pre id="build-ssh-command">ssh <user>@<?= h($builderHost) ?>
|
||||
|
||||
docker exec -it <?= h($sshContainer) ?> bash</pre>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<form id="build-rerun-as-form" class="build-form" hidden style="margin-top:12px">
|
||||
<div class="build-form-grid">
|
||||
<label>Git ref<input name="git_ref" value="<?= h($params['git_ref'] ?? $build['git_ref'] ?? 'next') ?>"></label>
|
||||
|
||||
@@ -499,12 +499,26 @@ a:hover { text-decoration: underline; }
|
||||
}
|
||||
.login-card button {
|
||||
padding: 12px;
|
||||
border: none;
|
||||
border: 1px solid var(--accent);
|
||||
border-radius: 8px;
|
||||
background: var(--accent);
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(61, 139, 253, .25),
|
||||
0 4px 14px rgba(0, 0, 0, .3),
|
||||
0 0 20px rgba(61, 139, 253, .35);
|
||||
transition: box-shadow .18s ease, filter .18s ease, transform .12s ease;
|
||||
}
|
||||
.login-card button:hover,
|
||||
.login-card button:focus-visible {
|
||||
filter: brightness(1.08);
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(61, 139, 253, .55),
|
||||
0 6px 18px rgba(0, 0, 0, .34),
|
||||
0 0 28px rgba(61, 139, 253, .48);
|
||||
outline: none;
|
||||
}
|
||||
.toolbar { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 12px; }
|
||||
.btn {
|
||||
@@ -516,8 +530,53 @@ a:hover { text-decoration: underline; }
|
||||
cursor: pointer;
|
||||
background: var(--surface);
|
||||
color: var(--text);
|
||||
text-decoration: none;
|
||||
font: inherit;
|
||||
line-height: 1.25;
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(61, 139, 253, .12),
|
||||
0 2px 10px rgba(0, 0, 0, .28),
|
||||
0 0 18px rgba(61, 139, 253, .08);
|
||||
transition:
|
||||
border-color .18s ease,
|
||||
box-shadow .18s ease,
|
||||
transform .12s ease,
|
||||
filter .18s ease,
|
||||
background .18s ease;
|
||||
}
|
||||
[data-theme="light"] .btn {
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(37, 99, 235, .14),
|
||||
0 2px 8px rgba(15, 23, 42, .12),
|
||||
0 0 16px rgba(37, 99, 235, .1);
|
||||
}
|
||||
.btn:hover,
|
||||
.btn:focus-visible {
|
||||
border-color: var(--accent);
|
||||
text-decoration: none;
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(61, 139, 253, .45),
|
||||
0 4px 16px rgba(0, 0, 0, .32),
|
||||
0 0 22px rgba(61, 139, 253, .38);
|
||||
outline: none;
|
||||
}
|
||||
[data-theme="light"] .btn:hover,
|
||||
[data-theme="light"] .btn:focus-visible {
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(37, 99, 235, .5),
|
||||
0 4px 14px rgba(15, 23, 42, .16),
|
||||
0 0 20px rgba(37, 99, 235, .28);
|
||||
}
|
||||
.btn:active {
|
||||
transform: translateY(1px);
|
||||
}
|
||||
.btn.active { background: var(--accent); border-color: var(--accent); color: #fff; }
|
||||
.btn:disabled,
|
||||
.btn[aria-disabled="true"] {
|
||||
opacity: .55;
|
||||
cursor: not-allowed;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, .15);
|
||||
}
|
||||
.data-table { width: 100%; border-collapse: collapse; margin-top: 16px; font-size: 14px; }
|
||||
.data-table th, .data-table td { border-bottom: 1px solid var(--border); padding: 10px 8px; text-align: left; }
|
||||
.reports-tree .report-tree-head { width: 2.5rem; padding: 0; }
|
||||
@@ -1087,8 +1146,65 @@ button.report-tag--filter:hover {
|
||||
background: var(--accent);
|
||||
border-color: var(--accent);
|
||||
color: #fff;
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(61, 139, 253, .35),
|
||||
0 3px 12px rgba(0, 0, 0, .3),
|
||||
0 0 22px rgba(61, 139, 253, .32);
|
||||
}
|
||||
.btn-primary:hover,
|
||||
.btn-primary:focus-visible {
|
||||
filter: brightness(1.08);
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(61, 139, 253, .6),
|
||||
0 5px 18px rgba(0, 0, 0, .34),
|
||||
0 0 30px rgba(61, 139, 253, .5);
|
||||
}
|
||||
.btn-danger {
|
||||
background: rgba(248, 113, 113, .14);
|
||||
border-color: var(--danger);
|
||||
color: var(--danger);
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(248, 113, 113, .28),
|
||||
0 3px 12px rgba(0, 0, 0, .28),
|
||||
0 0 18px rgba(248, 113, 113, .22);
|
||||
}
|
||||
[data-theme="light"] .btn-danger {
|
||||
background: rgba(220, 38, 38, .08);
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(220, 38, 38, .3),
|
||||
0 3px 10px rgba(15, 23, 42, .12),
|
||||
0 0 16px rgba(220, 38, 38, .18);
|
||||
}
|
||||
.btn-danger:hover,
|
||||
.btn-danger:focus-visible {
|
||||
background: rgba(248, 113, 113, .22);
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(248, 113, 113, .55),
|
||||
0 5px 16px rgba(0, 0, 0, .32),
|
||||
0 0 26px rgba(248, 113, 113, .42);
|
||||
}
|
||||
.build-actions .toolbar-actions .btn,
|
||||
.build-actions .toolbar-actions a.btn,
|
||||
.build-log-actions .btn,
|
||||
.build-log-actions a.btn {
|
||||
margin-left: 0;
|
||||
}
|
||||
.build-ssh-panel {
|
||||
margin-top: 12px;
|
||||
padding: 12px 14px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--accent);
|
||||
background: rgba(61, 139, 253, .08);
|
||||
box-shadow: 0 0 18px rgba(61, 139, 253, .15);
|
||||
}
|
||||
.build-ssh-panel pre {
|
||||
margin: 8px 0 0;
|
||||
padding: 10px 12px;
|
||||
background: var(--surface2);
|
||||
border-radius: 6px;
|
||||
overflow-x: auto;
|
||||
font-size: 13px;
|
||||
}
|
||||
.btn-primary:hover { filter: brightness(1.08); }
|
||||
|
||||
.tag-edit-btn {
|
||||
flex-shrink: 0;
|
||||
|
||||
@@ -25,6 +25,10 @@ CLONE_DEPTH="${BUILD_GIT_DEPTH:-1}"
|
||||
SOURCE_DIR="${OUT_DIR}/src"
|
||||
DOCKER_WORKSPACE="${WORK_DIR}"
|
||||
CONTAINER_NAME="androidcast-bld-${BUILD_ID}"
|
||||
RESUME_FROM="${BUILD_RESUME_FROM:-}"
|
||||
REUSE_SRC_DIR="${BUILD_REUSE_SRC_DIR:-}"
|
||||
SSH_DEBUG="${BUILD_SSH_DEBUG:-0}"
|
||||
SSH_DEBUG_SECONDS="${BUILD_SSH_DEBUG_SECONDS:-7200}"
|
||||
|
||||
mkdir -p "$(dirname "$LOG_FILE")" "$OUT_DIR"
|
||||
|
||||
@@ -136,22 +140,89 @@ sync_shared_checkout() {
|
||||
run_logged git_safe -C "${WORK_DIR}" rev-parse HEAD
|
||||
}
|
||||
|
||||
log "[runner] image=${IMAGE} ci_version=${CI_VERSION} build_id=${BUILD_ID} container=${CONTAINER_NAME}"
|
||||
log "[runner] workdir=${WORK_DIR} out=${OUT_DIR} isolate=${ISOLATE_SOURCE}"
|
||||
should_skip_git() {
|
||||
[[ "$RESUME_FROM" == "docker-build" || "$RESUME_FROM" == "docker-run" ]]
|
||||
}
|
||||
|
||||
if [[ "$ISOLATE_SOURCE" == "1" ]]; then
|
||||
prepare_isolated_source
|
||||
should_skip_docker_build() {
|
||||
[[ "$RESUME_FROM" == "docker-run" ]] && docker image inspect "$IMAGE" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
reuse_source_tree() {
|
||||
if [[ -n "$REUSE_SRC_DIR" && -d "${REUSE_SRC_DIR}/.git" ]]; then
|
||||
log "[runner] reuse: source tree from ${REUSE_SRC_DIR}"
|
||||
rm -rf "${SOURCE_DIR}"
|
||||
mkdir -p "${SOURCE_DIR}"
|
||||
cp -a "${REUSE_SRC_DIR}/." "${SOURCE_DIR}/"
|
||||
DOCKER_WORKSPACE="${SOURCE_DIR}"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
run_git_step() {
|
||||
if [[ "$ISOLATE_SOURCE" == "1" ]]; then
|
||||
prepare_isolated_source
|
||||
else
|
||||
sync_shared_checkout
|
||||
DOCKER_WORKSPACE="${WORK_DIR}"
|
||||
fi
|
||||
}
|
||||
|
||||
start_ssh_debug_container() {
|
||||
log "[runner] SSH debug: starting container ${CONTAINER_NAME} (${SSH_DEBUG_SECONDS}s)"
|
||||
docker rm -f "${CONTAINER_NAME}" 2>/dev/null || true
|
||||
run_step docker-run-ssh docker run -d --name "${CONTAINER_NAME}" \
|
||||
-v "${DOCKER_WORKSPACE}:/workspace" \
|
||||
-v "${ROOT}/out:/workspace/out" \
|
||||
-w /workspace \
|
||||
-e BUILD_ID="${BUILD_ID}" \
|
||||
-e OUT_DIR="/workspace/out/builds/${BUILD_ID}" \
|
||||
"$IMAGE" \
|
||||
sleep "${SSH_DEBUG_SECONDS}"
|
||||
printf '%s\n' 'ssh_debug' > "${OUT_DIR}/runner.mode"
|
||||
local host
|
||||
host="$(hostname -f 2>/dev/null || hostname 2>/dev/null || echo builder)"
|
||||
log "[runner] SSH debug: session open on ${host}"
|
||||
log "[runner] SSH debug: ssh <you>@${host}"
|
||||
log "[runner] SSH debug: docker exec -it ${CONTAINER_NAME} bash"
|
||||
log "[runner] SSH debug: workspace is /workspace inside the container"
|
||||
log "[runner] finished build_id=${BUILD_ID} (ssh debug — stop build or wait ${SSH_DEBUG_SECONDS}s)"
|
||||
}
|
||||
|
||||
log "[runner] image=${IMAGE} ci_version=${CI_VERSION} build_id=${BUILD_ID} container=${CONTAINER_NAME}"
|
||||
log "[runner] workdir=${WORK_DIR} out=${OUT_DIR} isolate=${ISOLATE_SOURCE} resume=${RESUME_FROM:-full}"
|
||||
|
||||
if should_skip_git; then
|
||||
if reuse_source_tree; then
|
||||
log "[runner] resume: skipped git (reused source)"
|
||||
elif [[ "$ISOLATE_SOURCE" != "1" && -d "${WORK_DIR}/.git" ]]; then
|
||||
DOCKER_WORKSPACE="${WORK_DIR}"
|
||||
log "[runner] resume: skipped git (shared workdir ${WORK_DIR})"
|
||||
else
|
||||
log "[runner] resume: no reusable source; running git step"
|
||||
run_git_step
|
||||
fi
|
||||
else
|
||||
sync_shared_checkout
|
||||
run_git_step
|
||||
fi
|
||||
|
||||
log "[runner] docker workspace=${DOCKER_WORKSPACE}"
|
||||
|
||||
run_step docker-build docker build \
|
||||
--build-arg ANDROIDCAST_CI_VERSION="${CI_VERSION}" \
|
||||
-t "$IMAGE" \
|
||||
-f "${ROOT}/Dockerfile" \
|
||||
"${ROOT}"
|
||||
if should_skip_docker_build; then
|
||||
log "[runner] resume: skipped docker-build (image ${IMAGE} exists)"
|
||||
else
|
||||
run_step docker-build docker build \
|
||||
--build-arg ANDROIDCAST_CI_VERSION="${CI_VERSION}" \
|
||||
-t "$IMAGE" \
|
||||
-f "${ROOT}/Dockerfile" \
|
||||
"${ROOT}"
|
||||
fi
|
||||
|
||||
if [[ "$SSH_DEBUG" == "1" ]]; then
|
||||
start_ssh_debug_container
|
||||
exit 0
|
||||
fi
|
||||
|
||||
run_step docker-run docker run --rm --name "${CONTAINER_NAME}" \
|
||||
-v "${DOCKER_WORKSPACE}:/workspace" \
|
||||
|
||||
Reference in New Issue
Block a user