name: Build Mod Package on: push: branches: - main - master tags: - "v*" workflow_dispatch: permissions: contents: write jobs: build: runs-on: - win11 defaults: run: shell: powershell steps: - name: Prepare workspace run: | $ErrorActionPreference = "Stop" $workspace = "${{ gitea.workspace }}" $repoUrl = "${{ gitea.server_url }}/${{ gitea.repository }}.git" $repoRef = "${{ gitea.ref }}" $authPair = "${{ gitea.actor }}:${{ secrets.GITEA_TOKEN }}" $authBytes = [System.Text.Encoding]::ASCII.GetBytes($authPair) $authBasic = [Convert]::ToBase64String($authBytes) New-Item -ItemType Directory -Path $workspace -Force | Out-Null Set-Location $workspace if (-not (Test-Path -LiteralPath ".git")) { git init | Out-Null git remote add origin $repoUrl } else { git remote set-url origin $repoUrl } git -c "http.extraHeader=Authorization: Basic $authBasic" fetch --depth 1 origin $repoRef git checkout --force FETCH_HEAD if (-not (Test-Path -LiteralPath "Mods\\DnD 5.5e AIO Russian\\Localization\\Russian\\russian.xml")) { throw "Repository sources are not available in the runner workspace." } New-Item -ItemType Directory -Path ".tools\\lslib" -Force | Out-Null New-Item -ItemType Directory -Path "build" -Force | Out-Null - name: Download latest LSLib release run: | $ErrorActionPreference = "Stop" $release = Invoke-RestMethod -Uri "https://api.github.com/repos/Norbyte/lslib/releases/latest" $asset = $release.assets | Where-Object { $_.name -match '\.zip$' } | Select-Object -First 1 if (-not $asset) { throw "Could not find a downloadable LSLib zip asset in the latest release." } Invoke-WebRequest -Uri $asset.browser_download_url -OutFile ".tools/lslib/lslib.zip" Expand-Archive -LiteralPath ".tools/lslib/lslib.zip" -DestinationPath ".tools/lslib" -Force - name: Build .pak run: | $ErrorActionPreference = "Stop" $workspace = "${{ gitea.workspace }}" $refName = "${{ gitea.ref_name }}" $archiveBaseName = "DnD 5.5e AIO Russian" $modName = "DnD 5.5e All-in-One BEYOND Russian Localization" $modFolder = "DnD 5.5e AIO Russian" $modUuid = "6401e84d-daf2-416d-adeb-99c03a2487a6" $modDescription = "Russian Localization" $modAuthor = "MikhailRaw" $modVersion64 = "36028797018963968" $modGroup = "6401e84d-daf2-416d-adeb-99c03a2487a6" if ($refName -and $refName -like "v*") { $archiveBaseName = "DnD 5.5e AIO Russian $refName" } $stagingPath = [System.IO.Path]::GetFullPath((Join-Path $workspace ".build-stage")) $packagePath = [System.IO.Path]::GetFullPath((Join-Path $workspace "build/DnD 5.5e AIO Russian.pak")) $tempPackagePath = [System.IO.Path]::GetFullPath((Join-Path $env:TEMP "DnD 5.5e AIO Russian.pak")) $zipPath = [System.IO.Path]::GetFullPath((Join-Path $workspace "build/$archiveBaseName.zip")) $infoJsonPath = [System.IO.Path]::GetFullPath((Join-Path $workspace "build/info.json")) $divine = Get-ChildItem -Path ".tools/lslib" -Recurse -File | Where-Object { $_.Name -ieq "Divine.exe" } | Select-Object -First 1 if (-not $divine) { throw "Divine.exe was not found in the downloaded LSLib release." } if (Test-Path -LiteralPath $stagingPath) { Remove-Item -LiteralPath $stagingPath -Recurse -Force } if (Test-Path -LiteralPath $tempPackagePath) { Remove-Item -LiteralPath $tempPackagePath -Force } if (Test-Path -LiteralPath $packagePath) { Remove-Item -LiteralPath $packagePath -Force } New-Item -ItemType Directory -Path $stagingPath | Out-Null Copy-Item -LiteralPath (Join-Path $workspace "Mods") -Destination $stagingPath -Recurse & $divine.FullName -a create-package -g bg3 -s $stagingPath -d $tempPackagePath if (-not (Test-Path -LiteralPath $tempPackagePath)) { throw "Temporary package was not created." } Move-Item -LiteralPath $tempPackagePath -Destination $packagePath if (-not (Test-Path -LiteralPath "build/DnD 5.5e AIO Russian.pak")) { throw "Package was not created." } $packageFile = Get-Item -LiteralPath $packagePath if ($packageFile.Length -lt 1024) { throw "Package looks invalid: '$packagePath' is only $($packageFile.Length) bytes." } if (Test-Path -LiteralPath $zipPath) { Remove-Item -LiteralPath $zipPath -Force } $packageMd5 = (Get-FileHash -LiteralPath $packagePath -Algorithm MD5).Hash.ToLowerInvariant() $createdAt = (Get-Date).ToString("o") $infoJson = [ordered]@{ Mods = @( [ordered]@{ Author = $modAuthor Name = $modName Folder = $modFolder Version = $modVersion64 Description = $modDescription UUID = $modUuid Created = $createdAt Dependencies = @() Group = $modGroup } ) MD5 = $packageMd5 } | ConvertTo-Json -Depth 4 Set-Content -LiteralPath $infoJsonPath -Value $infoJson -Encoding utf8 Compress-Archive -LiteralPath @($packagePath, $infoJsonPath) -DestinationPath $zipPath -CompressionLevel Optimal if (-not (Test-Path -LiteralPath $zipPath)) { throw "ZIP archive was not created." } - name: Show build result run: | $ErrorActionPreference = "Stop" $workspace = "${{ gitea.workspace }}" $refName = "${{ gitea.ref_name }}" $archiveBaseName = "DnD 5.5e AIO Russian" if ($refName -and $refName -like "v*") { $archiveBaseName = "DnD 5.5e AIO Russian $refName" } $zipPath = [System.IO.Path]::GetFullPath((Join-Path $workspace "build/$archiveBaseName.zip")) Get-ChildItem "build/DnD 5.5e AIO Russian.pak", "build/info.json", $zipPath | Select-Object FullName, Length, LastWriteTime - name: Publish archive to release if: startsWith(gitea.ref, 'refs/tags/v') run: | $ErrorActionPreference = "Stop" $workspace = "${{ gitea.workspace }}" $serverUrl = "${{ gitea.server_url }}" $repository = "${{ gitea.repository }}" $tagName = "${{ gitea.ref_name }}" $zipPath = [System.IO.Path]::GetFullPath((Join-Path $workspace "build/DnD 5.5e AIO Russian $tagName.zip")) $assetName = [System.IO.Path]::GetFileName($zipPath) $repoParts = $repository.Split("/", 2) if ($repoParts.Length -ne 2) { throw "Could not parse repository owner and name from '$repository'." } if (-not (Test-Path -LiteralPath $zipPath)) { throw "Release archive was not found at '$zipPath'." } $owner = $repoParts[0] $repo = $repoParts[1] $apiBase = "$serverUrl/api/v1/repos/$owner/$repo" $headers = @{ Authorization = "token ${{ secrets.GITEA_TOKEN }}" Accept = "application/json" } $release = $null try { $release = Invoke-RestMethod -Method Get -Uri "$apiBase/releases/tags/$tagName" -Headers $headers } catch { $statusCode = $null if ($_.Exception.Response) { $statusCode = [int]$_.Exception.Response.StatusCode } if ($statusCode -ne 404) { throw } } if (-not $release) { $releaseBody = @{ tag_name = $tagName name = $tagName target_commitish = "${{ gitea.sha }}" draft = $false prerelease = $false } | ConvertTo-Json $release = Invoke-RestMethod -Method Post -Uri "$apiBase/releases" -Headers $headers -ContentType "application/json" -Body $releaseBody } $existingAsset = $null if ($release.assets) { $existingAsset = $release.assets | Where-Object { $_.name -eq $assetName } | Select-Object -First 1 } if ($existingAsset) { Invoke-RestMethod -Method Delete -Uri "$apiBase/releases/$($release.id)/assets/$($existingAsset.id)" -Headers $headers } $uploadHeaders = @{ Authorization = "token ${{ secrets.GITEA_TOKEN }}" Accept = "application/json" } Invoke-WebRequest -Method Post -Uri "$apiBase/releases/$($release.id)/assets?name=$([uri]::EscapeDataString($assetName))" -Headers $uploadHeaders -ContentType "application/octet-stream" -InFile $zipPath