param(
[string]$Version = "",
[string]$Arch = "x64",
[string]$BuildDir = "build\winget-$Arch",
[string]$OutputDir = "dist\winget-$Arch",
[string]$GitHubRepository = "BYVoid/OpenCC",
[string]$PackageIdentifier = "BYVoid.OpenCC",
[string]$Publisher = "BYVoid",
[switch]$SkipTests
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
function Get-ProjectVersion {
$cmakeFile = Join-Path $PSScriptRoot "..\CMakeLists.txt"
$content = Get-Content -Path $cmakeFile -Raw
$major = [regex]::Match($content, 'OPENCC_VERSION_MAJOR\s+(\d+)').Groups[1].Value
$minor = [regex]::Match($content, 'OPENCC_VERSION_MINOR\s+(\d+)').Groups[1].Value
$revision = [regex]::Match($content, 'OPENCC_VERSION_REVISION\s+(\d+)').Groups[1].Value
if (-not $major -or -not $minor -or -not $revision) {
throw "Failed to parse version from CMakeLists.txt."
}
return "$major.$minor.$revision"
}
function Write-Utf8File {
param(
[Parameter(Mandatory = $true)][string]$Path,
[Parameter(Mandatory = $true)][string]$Content
)
$directory = Split-Path -Parent $Path
if ($directory) {
New-Item -ItemType Directory -Force -Path $directory | Out-Null
}
$utf8NoBom = New-Object System.Text.UTF8Encoding($false)
[System.IO.File]::WriteAllText($Path, $Content, $utf8NoBom)
}
function Normalize-ReleaseVersion {
param(
[Parameter(Mandatory = $true)][string]$RawVersion
)
$value = $RawVersion.Trim()
if ($value.StartsWith("ver.")) {
$value = $value.Substring(4)
} elseif ($value.StartsWith("v")) {
$value = $value.Substring(1)
}
if ($value -notmatch '^\d+\.\d+\.\d+(-(alpha|rc)\d+)?$') {
throw "Invalid version '$RawVersion'. Expected x.y.z, x.y.z-alphaN, or x.y.z-rcN."
}
return $value
}
function Normalize-GitHubRepository {
param(
[Parameter(Mandatory = $true)][string]$RawRepository
)
$value = $RawRepository.Trim()
if ($value -notmatch '^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$') {
throw "Invalid GitHub repository '$RawRepository'. Expected owner/name."
}
return $value
}
function Normalize-BaseUrl {
param(
[Parameter(Mandatory = $true)][string]$RawUrl
)
$value = $RawUrl.Trim().TrimEnd('/')
if ($value -notmatch '^https://') {
throw "Invalid public base URL '$RawUrl'. Expected an https URL."
}
return $value
}
function New-WinGetManifests {
param(
[Parameter(Mandatory = $true)][string]$PackageVersion,
[Parameter(Mandatory = $true)][string]$PackageUrl,
[Parameter(Mandatory = $true)][string]$InstallerSha256,
[Parameter(Mandatory = $true)][string]$ManifestRoot,
[Parameter(Mandatory = $true)][string]$PackageIdentifier,
[Parameter(Mandatory = $true)][string]$Publisher,
[Parameter(Mandatory = $true)][string]$PublisherUrl,
[Parameter(Mandatory = $true)][string]$PublisherSupportUrl,
[Parameter(Mandatory = $true)][string]$PackagePageUrl,
[Parameter(Mandatory = $true)][string]$LicenseUrl
)
$releaseDate = Get-Date -Format "yyyy-MM-dd"
$versionManifest = @"
# yaml-language-server: `$schema=https://aka.ms/winget-manifest.version.1.9.0.schema.json
PackageIdentifier: $packageIdentifier
PackageVersion: $PackageVersion
DefaultLocale: en-US
ManifestType: version
ManifestVersion: 1.9.0
"@
$installerManifest = @"
# yaml-language-server: `$schema=https://aka.ms/winget-manifest.installer.1.9.0.schema.json
PackageIdentifier: $packageIdentifier
PackageVersion: $PackageVersion
InstallerType: zip
NestedInstallerType: portable
ReleaseDate: $releaseDate
Installers:
- Architecture: x64
InstallerUrl: $PackageUrl
InstallerSha256: $InstallerSha256
NestedInstallerFiles:
- RelativeFilePath: bin/opencc.exe
PortableCommandAlias: opencc
- RelativeFilePath: bin/opencc_dict.exe
PortableCommandAlias: opencc_dict
- RelativeFilePath: bin/opencc_phrase_extract.exe
PortableCommandAlias: opencc_phrase_extract
ManifestType: installer
ManifestVersion: 1.9.0
"@
$localeManifest = @"
# yaml-language-server: `$schema=https://aka.ms/winget-manifest.defaultLocale.1.9.0.schema.json
PackageIdentifier: $packageIdentifier
PackageVersion: $PackageVersion
PackageLocale: en-US
Publisher: $Publisher
PublisherUrl: $PublisherUrl
PublisherSupportUrl: $PublisherSupportUrl
Author: Carbo Kuo and OpenCC contributors
PackageName: OpenCC
PackageUrl: $PackagePageUrl
License: Apache-2.0
LicenseUrl: $LicenseUrl
ShortDescription: Open Chinese Convert
Description: Command-line tools and libraries for Simplified Chinese, Traditional Chinese, regional variant, and Japanese Kanji conversion.
Moniker: opencc
Tags:
- chinese
- conversion
- opencc
- simplified-chinese
- traditional-chinese
ManifestType: defaultLocale
ManifestVersion: 1.9.0
"@
Write-Utf8File -Path (Join-Path $ManifestRoot "BYVoid.OpenCC.yaml") -Content $versionManifest
Write-Utf8File -Path (Join-Path $ManifestRoot "BYVoid.OpenCC.installer.yaml") -Content $installerManifest
Write-Utf8File -Path (Join-Path $ManifestRoot "BYVoid.OpenCC.locale.en-US.yaml") -Content $localeManifest
}
$repoRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot ".."))
Push-Location $repoRoot
try {
$publicBaseUrl = "https://opencc.byvoid.com/opencc-winget-release"
if (-not $Version) {
$Version = Get-ProjectVersion
}
$Version = Normalize-ReleaseVersion -RawVersion $Version
$GitHubRepository = Normalize-GitHubRepository -RawRepository $GitHubRepository
if ($Arch -ne "x64") {
throw "This script currently supports only -Arch x64 for WinGet releases."
}
$packageRoot = "https://github.com/$GitHubRepository"
$releaseUrlBase = Normalize-BaseUrl -RawUrl $publicBaseUrl
$licenseUrl = "$packageRoot/blob/master/LICENSE"
$publisherSupportUrl = "$packageRoot/issues"
$resolvedBuildDir = [System.IO.Path]::GetFullPath((Join-Path $repoRoot $BuildDir))
$resolvedOutputDir = [System.IO.Path]::GetFullPath((Join-Path $repoRoot $OutputDir))
$installRoot = Join-Path $resolvedOutputDir "install"
$stagingRoot = Join-Path $resolvedOutputDir "staging"
$assetName = "OpenCC-$Version-windows-$Arch-portable.zip"
$assetPath = Join-Path $resolvedOutputDir $assetName
$checksumPath = "$assetPath.sha256"
$wingetPathIdentifier = $PackageIdentifier.Replace('.', '\')
$wingetRoot = Join-Path $resolvedOutputDir "winget-manifests\$wingetPathIdentifier\$Version"
$releaseUrl = "$releaseUrlBase/$assetName"
Remove-Item -Recurse -Force $resolvedBuildDir -ErrorAction SilentlyContinue
Remove-Item -Recurse -Force $resolvedOutputDir -ErrorAction SilentlyContinue
New-Item -ItemType Directory -Force -Path $resolvedBuildDir | Out-Null
New-Item -ItemType Directory -Force -Path $installRoot | Out-Null
New-Item -ItemType Directory -Force -Path $stagingRoot | Out-Null
cmake -S . -B $resolvedBuildDir -A x64 `
-DCMAKE_BUILD_TYPE=Release `
-DBUILD_SHARED_LIBS:BOOL=OFF `
-DBUILD_OPENCC_JIEBA_PLUGIN:BOOL=ON `
-DCMAKE_INSTALL_PREFIX:PATH=$installRoot `
-DENABLE_GTEST:BOOL=OFF `
-DENABLE_BENCHMARK:BOOL=OFF
cmake --build $resolvedBuildDir --config Release --target install
if (-not $SkipTests) {
ctest --test-dir $resolvedBuildDir --build-config Release --output-on-failure
}
New-Item -ItemType Directory -Force -Path (Join-Path $stagingRoot "bin") | Out-Null
New-Item -ItemType Directory -Force -Path (Join-Path $stagingRoot "bin\plugins") | Out-Null
New-Item -ItemType Directory -Force -Path (Join-Path $stagingRoot "share\opencc") | Out-Null
Copy-Item -Path (Join-Path $installRoot "bin\opencc.exe") -Destination (Join-Path $stagingRoot "bin\opencc.exe")
Copy-Item -Path (Join-Path $installRoot "bin\opencc_dict.exe") -Destination (Join-Path $stagingRoot "bin\opencc_dict.exe")
Copy-Item -Path (Join-Path $installRoot "bin\opencc_phrase_extract.exe") -Destination (Join-Path $stagingRoot "bin\opencc_phrase_extract.exe")
Copy-Item -Path (Join-Path $installRoot "bin\plugins\*.dll") -Destination (Join-Path $stagingRoot "bin\plugins")
Copy-Item -Path (Join-Path $installRoot "share\opencc\*") -Destination (Join-Path $stagingRoot "share\opencc") -Recurse
Copy-Item -Path LICENSE -Destination (Join-Path $stagingRoot "LICENSE.txt")
Copy-Item -Path README.md -Destination (Join-Path $stagingRoot "README.md")
Compress-Archive -Path (Join-Path $stagingRoot '*') -DestinationPath $assetPath -CompressionLevel Optimal
$hash = (Get-FileHash -Path $assetPath -Algorithm SHA256).Hash.ToUpperInvariant()
Write-Utf8File -Path $checksumPath -Content "$hash *$assetName`n"
New-WinGetManifests `
-PackageVersion $Version `
-PackageUrl $releaseUrl `
-InstallerSha256 $hash `
-ManifestRoot $wingetRoot `
-PackageIdentifier $PackageIdentifier `
-Publisher $Publisher `
-PublisherUrl $packageRoot `
-PublisherSupportUrl $publisherSupportUrl `
-PackagePageUrl $packageRoot `
-LicenseUrl $licenseUrl
Write-Host "Release archive: $assetPath"
Write-Host "SHA256: $hash"
Write-Host "WinGet manifests: $wingetRoot"
}
finally {
Pop-Location
}