diff --git a/.gitea/workflows/release-offline.yml b/.gitea/workflows/release-offline.yml index 59626f53..5ff4229a 100644 --- a/.gitea/workflows/release-offline.yml +++ b/.gitea/workflows/release-offline.yml @@ -4,155 +4,37 @@ on: workflow_dispatch: jobs: - build-upload: - runs-on: local-build + package-and-deploy: + runs-on: build-host steps: - name: Prepare local worktree env: - MAIBOT_REPO_URL: https://git.lecspace.com/${{ gitea.repository }}.git - MAIBOT_GIT_REPO_URL: ${{ secrets.MAIBOT_GIT_REPO_URL }} + MAIBOT_REPO_URL: http://127.0.0.1:3000/${{ gitea.repository }}.git MAIBOT_REPO_SHA: ${{ gitea.sha }} - MAIBOT_GITEA_USER: ${{ secrets.MAIBOT_GITEA_USER }} - MAIBOT_GITEA_TOKEN: ${{ secrets.MAIBOT_GITEA_TOKEN }} - shell: powershell - run: | - $ErrorActionPreference = "Stop" - Set-StrictMode -Version Latest - - function Add-GitHubEnv { - param([string]$Line) - $utf8NoBom = [System.Text.UTF8Encoding]::new($false) - [System.IO.File]::AppendAllText($env:GITHUB_ENV, $Line + [Environment]::NewLine, $utf8NoBom) - } - - $worktreeRoot = Join-Path ([System.IO.Path]::GetTempPath()) "maibot-actions" - $worktree = Join-Path $worktreeRoot $env:MAIBOT_REPO_SHA - $repoUrl = if ([string]::IsNullOrWhiteSpace($env:MAIBOT_GIT_REPO_URL)) { $env:MAIBOT_REPO_URL } else { $env:MAIBOT_GIT_REPO_URL } - - if (Test-Path $worktree) { - Remove-Item -LiteralPath $worktree -Recurse -Force - } - New-Item -ItemType Directory -Force -Path $worktreeRoot | Out-Null - - $gitArgs = @() - if (-not [string]::IsNullOrWhiteSpace($env:MAIBOT_GITEA_TOKEN)) { - $giteaUser = $env:MAIBOT_GITEA_USER - if ([string]::IsNullOrWhiteSpace($giteaUser)) { $giteaUser = "Losita" } - $basicToken = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $giteaUser, $env:MAIBOT_GITEA_TOKEN))) - $gitArgs += @("-c", ("http.extraHeader=Authorization: Basic {0}" -f $basicToken)) - } - - & git @gitArgs clone --no-checkout $repoUrl $worktree - if ($LASTEXITCODE -ne 0) { throw "source clone failed." } - - & git -C $worktree checkout --force $env:MAIBOT_REPO_SHA - if ($LASTEXITCODE -ne 0) { throw "source checkout failed." } - - & git -C $worktree clean -dffx - if ($LASTEXITCODE -ne 0) { throw "source cleanup failed." } - - $appTag = (& git -C $worktree rev-parse --short=12 HEAD).Trim() - Add-GitHubEnv "APP_TAG=$appTag" - Add-GitHubEnv "MAIBOT_WORKTREE=$worktree" - - - name: Build release archive - shell: powershell - run: | - $ErrorActionPreference = "Stop" - Set-StrictMode -Version Latest - - function Add-GitHubEnv { - param([string]$Line) - $utf8NoBom = [System.Text.UTF8Encoding]::new($false) - [System.IO.File]::AppendAllText($env:GITHUB_ENV, $Line + [Environment]::NewLine, $utf8NoBom) - } - - Set-Location $env:MAIBOT_WORKTREE - $archiveDir = Join-Path ([System.IO.Path]::GetTempPath()) "maibot-release" - New-Item -ItemType Directory -Force -Path $archiveDir | Out-Null - $archivePath = Join-Path $archiveDir ("mai-bot-{0}.tgz" -f $env:APP_TAG) - if (Test-Path $archivePath) { - Remove-Item -LiteralPath $archivePath -Force - } - - & git archive --format=tar.gz --output=$archivePath HEAD - if ($LASTEXITCODE -ne 0) { throw "release archive failed." } - - Add-GitHubEnv "RELEASE_ARCHIVE=$archivePath" - - - name: Upload release to server - env: - MAIBOT_RELEASE_HOST: ${{ secrets.MAIBOT_RELEASE_HOST }} - MAIBOT_RELEASE_USER: ${{ secrets.MAIBOT_RELEASE_USER }} - MAIBOT_RELEASE_PORT: ${{ secrets.MAIBOT_RELEASE_PORT }} - MAIBOT_RELEASE_ROOT: ${{ secrets.MAIBOT_RELEASE_ROOT }} - MAIBOT_SSH_KEY: ${{ secrets.MAIBOT_SSH_KEY }} - shell: powershell - run: | - $ErrorActionPreference = "Stop" - Set-StrictMode -Version Latest - - $hostName = $env:MAIBOT_RELEASE_HOST - if ([string]::IsNullOrWhiteSpace($hostName)) { $hostName = "192.140.166.210" } - $userName = $env:MAIBOT_RELEASE_USER - if ([string]::IsNullOrWhiteSpace($userName)) { $userName = "root" } - $port = $env:MAIBOT_RELEASE_PORT - if ([string]::IsNullOrWhiteSpace($port)) { $port = "22" } - $releaseRoot = $env:MAIBOT_RELEASE_ROOT - if ([string]::IsNullOrWhiteSpace($releaseRoot)) { $releaseRoot = "/srv/maibot/releases" } - if ($releaseRoot -notmatch '^/srv/maibot/releases(/.*)?$') { throw "release root must stay under /srv/maibot/releases." } - - $remote = "{0}@{1}" -f $userName, $hostName - $remoteArchive = ("{0}/{1}.tgz" -f $releaseRoot.TrimEnd('/'), $env:APP_TAG) - $sshArgs = @("-o", "BatchMode=yes", "-o", "StrictHostKeyChecking=no", "-o", "ConnectTimeout=30", "-p", $port) - $scpArgs = @("-o", "BatchMode=yes", "-o", "StrictHostKeyChecking=no", "-o", "ConnectTimeout=30", "-P", $port) - - if (-not [string]::IsNullOrWhiteSpace($env:MAIBOT_SSH_KEY)) { - $keyPath = Join-Path ([System.IO.Path]::GetTempPath()) ("maibot-release-{0}.key" -f $env:APP_TAG) - $env:MAIBOT_SSH_KEY.Replace("`r`n", "`n") | Out-File -FilePath $keyPath -Encoding ascii -NoNewline - if ([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([System.Runtime.InteropServices.OSPlatform]::Windows)) { - & icacls $keyPath /inheritance:r /grant:r "$($env:USERNAME):(R)" | Out-Null - } else { - & chmod 600 $keyPath - } - $sshArgs += @("-i", $keyPath) - $scpArgs += @("-i", $keyPath) - } - - & ssh @sshArgs $remote "mkdir -p '$releaseRoot'" - if ($LASTEXITCODE -ne 0) { throw "remote release root prepare failed." } - - & scp @scpArgs $env:RELEASE_ARCHIVE ("{0}:{1}" -f $remote, $remoteArchive) - if ($LASTEXITCODE -ne 0) { throw "release upload failed." } - - - name: Cleanup worktree - if: ${{ always() }} - shell: powershell - run: | - $ErrorActionPreference = "Stop" - $worktreeRoot = Join-Path ([System.IO.Path]::GetTempPath()) "maibot-actions" - $expectedPrefix = $worktreeRoot.TrimEnd([System.IO.Path]::DirectorySeparatorChar, [System.IO.Path]::AltDirectorySeparatorChar) + [System.IO.Path]::DirectorySeparatorChar - if (-not [string]::IsNullOrWhiteSpace($env:MAIBOT_WORKTREE) -and $env:MAIBOT_WORKTREE.StartsWith($expectedPrefix, [System.StringComparison]::OrdinalIgnoreCase)) { - Remove-Item -LiteralPath $env:MAIBOT_WORKTREE -Recurse -Force -ErrorAction SilentlyContinue - } - - deploy: - runs-on: build-host - needs: build-upload - steps: - - name: Deploy release - env: - MAIBOT_REPO_SHA: ${{ gitea.sha }} - MAIBOT_RELEASE_ROOT: ${{ secrets.MAIBOT_RELEASE_ROOT }} - MAIBOT_RUNTIME_ROOT: ${{ secrets.MAIBOT_RUNTIME_ROOT }} shell: bash run: | set -euo pipefail - app_tag="${MAIBOT_REPO_SHA:0:12}" - release_root="${MAIBOT_RELEASE_ROOT:-/srv/maibot/releases}" - runtime_root="${MAIBOT_RUNTIME_ROOT:-/root/maibot-offline}" + worktree_root=/tmp/maibot-actions + worktree="${worktree_root}/${MAIBOT_REPO_SHA}" + rm -rf "$worktree" + mkdir -p "$worktree_root" + + git clone --no-checkout "$MAIBOT_REPO_URL" "$worktree" + git -C "$worktree" checkout --force "$MAIBOT_REPO_SHA" + git -C "$worktree" clean -dffx + + app_tag="$(git -C "$worktree" rev-parse --short=12 HEAD)" + printf 'APP_TAG=%s\n' "$app_tag" >> "$GITHUB_ENV" + printf 'MAIBOT_WORKTREE=%s\n' "$worktree" >> "$GITHUB_ENV" + + - name: Stage release directory + shell: bash + run: | + set -euo pipefail + + release_root="${MAIBOT_RELEASE_ROOT:-/srv/maibot/releases}" case "$release_root" in /srv/maibot/releases|/srv/maibot/releases/*) ;; *) @@ -161,21 +43,31 @@ jobs: ;; esac - case "$runtime_root" in - /root/maibot-offline|/root/maibot-offline/*) ;; - *) - echo "runtime root must stay under /root/maibot-offline" >&2 - exit 1 - ;; - esac - - archive="${release_root}/${app_tag}.tgz" - release_dir="${release_root}/${app_tag}" - - test -f "$archive" + release_dir="${release_root}/${APP_TAG}" rm -rf "$release_dir" mkdir -p "$release_dir" - tar -xzf "$archive" -C "$release_dir" - chmod +x "$release_dir/deploy/server-maibot/activate-release.sh" - MAIBOT_RUNTIME_ROOT="$runtime_root" "$release_dir/deploy/server-maibot/activate-release.sh" "$release_dir" - rm -f "$archive" + git -C "$MAIBOT_WORKTREE" archive HEAD | tar -x -C "$release_dir" + + printf 'RELEASE_DIR=%s\n' "$release_dir" >> "$GITHUB_ENV" + + - name: Deploy release + shell: bash + run: | + set -euo pipefail + + runtime_root="${MAIBOT_RUNTIME_ROOT:-/root/maibot-offline}" + chmod +x "$RELEASE_DIR/deploy/server-maibot/activate-release.sh" + MAIBOT_RUNTIME_ROOT="$runtime_root" "$RELEASE_DIR/deploy/server-maibot/activate-release.sh" "$RELEASE_DIR" + + - name: Cleanup worktree + if: ${{ always() }} + shell: bash + run: | + set -euo pipefail + + worktree_root=/tmp/maibot-actions/ + case "${MAIBOT_WORKTREE:-}" in + ${worktree_root}*) + rm -rf "$MAIBOT_WORKTREE" + ;; + esac diff --git a/deploy/server-maibot/README_DEPLOY_STEPS.txt b/deploy/server-maibot/README_DEPLOY_STEPS.txt index 2a283309..08176e36 100644 --- a/deploy/server-maibot/README_DEPLOY_STEPS.txt +++ b/deploy/server-maibot/README_DEPLOY_STEPS.txt @@ -19,13 +19,14 @@ Persistent files that stay on the server and do not go into Git: Gitea workflow: - .gitea/workflows/release-offline.yml -Suggested Gitea secrets: -- MAIBOT_RELEASE_HOST -- MAIBOT_RELEASE_USER -- MAIBOT_RELEASE_PORT -- MAIBOT_RELEASE_ROOT -- MAIBOT_RUNTIME_ROOT -- MAIBOT_SSH_KEY -- MAIBOT_GIT_REPO_URL (optional) -- MAIBOT_GITEA_USER (optional) -- MAIBOT_GITEA_TOKEN (optional) +Current pipeline mode: +- single-host release on the repo-level `build-host` runner +- clones from local Gitea HTTP on `127.0.0.1:3000` +- stages source into `/srv/maibot/releases/` +- activates it into `/root/maibot-offline` + +Optional environment overrides for the workflow runtime: +- `MAIBOT_RELEASE_ROOT` +- `MAIBOT_RUNTIME_ROOT` + +No repository secrets are required for the default same-host pipeline.