name: offline-release on: workflow_dispatch: jobs: build-upload: runs-on: local-build 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_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}" case "$release_root" in /srv/maibot/releases|/srv/maibot/releases/*) ;; *) echo "release root must stay under /srv/maibot/releases" >&2 exit 1 ;; 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" 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"