mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-05 10:29:58 +02:00
Compare commits
6 Commits
6d908d8dc3
...
v0.2.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7236933b76 | ||
| fc732e1b77 | |||
|
|
d097c82f6a | ||
| caaa9a472d | |||
|
|
4ce124da7c | ||
| 94a8b3481b |
109
.github/workflows/release.yml
vendored
109
.github/workflows/release.yml
vendored
@@ -246,6 +246,114 @@ jobs:
|
|||||||
retention-days: 90
|
retention-days: 90
|
||||||
compression-level: 0
|
compression-level: 0
|
||||||
|
|
||||||
|
publish-microsoft-store:
|
||||||
|
name: Submit Microsoft Store update
|
||||||
|
needs:
|
||||||
|
- preflight
|
||||||
|
- linux
|
||||||
|
- windows
|
||||||
|
- macos
|
||||||
|
- play-closed-testing
|
||||||
|
runs-on: windows-2025
|
||||||
|
timeout-minutes: 30
|
||||||
|
environment: microsoft-store
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Download Windows Store package
|
||||||
|
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
|
||||||
|
with:
|
||||||
|
name: vnidrop-${{ needs.preflight.outputs.version }}-windows-store-x64
|
||||||
|
path: build/release/windows
|
||||||
|
|
||||||
|
- name: Validate Microsoft Store configuration
|
||||||
|
id: store-package
|
||||||
|
shell: pwsh
|
||||||
|
env:
|
||||||
|
AZURE_AD_TENANT_ID: ${{ secrets.AZURE_AD_TENANT_ID }}
|
||||||
|
AZURE_AD_APPLICATION_CLIENT_ID: ${{ secrets.AZURE_AD_APPLICATION_CLIENT_ID }}
|
||||||
|
AZURE_AD_APPLICATION_SECRET: ${{ secrets.AZURE_AD_APPLICATION_SECRET }}
|
||||||
|
SELLER_ID: ${{ secrets.SELLER_ID }}
|
||||||
|
MICROSOFT_STORE_PRODUCT_ID: ${{ vars.MICROSOFT_STORE_PRODUCT_ID }}
|
||||||
|
run: |
|
||||||
|
$configuration = @{
|
||||||
|
AZURE_AD_TENANT_ID = $env:AZURE_AD_TENANT_ID
|
||||||
|
AZURE_AD_APPLICATION_CLIENT_ID = $env:AZURE_AD_APPLICATION_CLIENT_ID
|
||||||
|
AZURE_AD_APPLICATION_SECRET = $env:AZURE_AD_APPLICATION_SECRET
|
||||||
|
SELLER_ID = $env:SELLER_ID
|
||||||
|
MICROSOFT_STORE_PRODUCT_ID = $env:MICROSOFT_STORE_PRODUCT_ID
|
||||||
|
}
|
||||||
|
foreach ($entry in $configuration.GetEnumerator()) {
|
||||||
|
if ([string]::IsNullOrWhiteSpace($entry.Value) -or $entry.Value -eq "REPLACE_ME") {
|
||||||
|
throw "Missing Microsoft Store configuration: $($entry.Key)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($env:MICROSOFT_STORE_PRODUCT_ID -ne "9NJ5Q0FG7TGL") {
|
||||||
|
throw "Unexpected Microsoft Store product ID: $env:MICROSOFT_STORE_PRODUCT_ID"
|
||||||
|
}
|
||||||
|
$packages = @(
|
||||||
|
Get-ChildItem build/release/windows -File -Filter *.msixupload -Recurse
|
||||||
|
)
|
||||||
|
if ($packages.Count -ne 1) {
|
||||||
|
throw "Expected exactly one msixupload package, found $($packages.Count)"
|
||||||
|
}
|
||||||
|
"path=$($packages[0].FullName)" >> $env:GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Set up Microsoft Store Developer CLI
|
||||||
|
uses: microsoft/microsoft-store-apppublisher@15abd1c50fcc164b19cb240fb04ef3c49bf715a2 # v1.1
|
||||||
|
with:
|
||||||
|
version: v0.3.9
|
||||||
|
|
||||||
|
- name: Authenticate and verify Store access
|
||||||
|
shell: pwsh
|
||||||
|
env:
|
||||||
|
AZURE_AD_TENANT_ID: ${{ secrets.AZURE_AD_TENANT_ID }}
|
||||||
|
AZURE_AD_APPLICATION_CLIENT_ID: ${{ secrets.AZURE_AD_APPLICATION_CLIENT_ID }}
|
||||||
|
AZURE_AD_APPLICATION_SECRET: ${{ secrets.AZURE_AD_APPLICATION_SECRET }}
|
||||||
|
SELLER_ID: ${{ secrets.SELLER_ID }}
|
||||||
|
MICROSOFT_STORE_PRODUCT_ID: ${{ vars.MICROSOFT_STORE_PRODUCT_ID }}
|
||||||
|
run: |
|
||||||
|
msstore settings --enableTelemetry false
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "Failed to disable Microsoft Store CLI telemetry"
|
||||||
|
}
|
||||||
|
msstore reconfigure `
|
||||||
|
--tenantId "$env:AZURE_AD_TENANT_ID" `
|
||||||
|
--sellerId "$env:SELLER_ID" `
|
||||||
|
--clientId "$env:AZURE_AD_APPLICATION_CLIENT_ID" `
|
||||||
|
--clientSecret "$env:AZURE_AD_APPLICATION_SECRET"
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "Microsoft Store authentication failed"
|
||||||
|
}
|
||||||
|
msstore apps get "$env:MICROSOFT_STORE_PRODUCT_ID"
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "The Microsoft Store application is not accessible"
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Publish package to Microsoft Store
|
||||||
|
shell: pwsh
|
||||||
|
env:
|
||||||
|
MICROSOFT_STORE_PRODUCT_ID: ${{ vars.MICROSOFT_STORE_PRODUCT_ID }}
|
||||||
|
STORE_PACKAGE: ${{ steps.store-package.outputs.path }}
|
||||||
|
run: |
|
||||||
|
msstore publish "$env:STORE_PACKAGE" `
|
||||||
|
--appId "$env:MICROSOFT_STORE_PRODUCT_ID"
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "Microsoft Store package publication failed"
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Summarize Store submission
|
||||||
|
shell: pwsh
|
||||||
|
env:
|
||||||
|
VERSION: ${{ needs.preflight.outputs.version }}
|
||||||
|
MICROSOFT_STORE_PRODUCT_ID: ${{ vars.MICROSOFT_STORE_PRODUCT_ID }}
|
||||||
|
run: |
|
||||||
|
"### Microsoft Store submission" >> $env:GITHUB_STEP_SUMMARY
|
||||||
|
"- App version: $env:VERSION" >> $env:GITHUB_STEP_SUMMARY
|
||||||
|
"- Product ID: $env:MICROSOFT_STORE_PRODUCT_ID" >> $env:GITHUB_STEP_SUMMARY
|
||||||
|
"- Package submitted for certification" >> $env:GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
publish-github:
|
publish-github:
|
||||||
name: Publish coordinated GitHub Release
|
name: Publish coordinated GitHub Release
|
||||||
needs:
|
needs:
|
||||||
@@ -254,6 +362,7 @@ jobs:
|
|||||||
- windows
|
- windows
|
||||||
- macos
|
- macos
|
||||||
- play-closed-testing
|
- play-closed-testing
|
||||||
|
- publish-microsoft-store
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
timeout-minutes: 20
|
timeout-minutes: 20
|
||||||
permissions:
|
permissions:
|
||||||
|
|||||||
6
Makefile
6
Makefile
@@ -13,7 +13,7 @@ include $(ROOT)/make/release.mk
|
|||||||
.PHONY: test-rust-transfer test-rust-approval test-rust-lifecycle test-rust-output-sink
|
.PHONY: test-rust-transfer test-rust-approval test-rust-lifecycle test-rust-output-sink
|
||||||
.PHONY: check-shared test-shared test-android-host check-android verify-android-libs build-android run-desktop
|
.PHONY: check-shared test-shared test-android-host check-android verify-android-libs build-android run-desktop
|
||||||
.PHONY: apple-core apple-version-config apple-project open-apple-project open-apple build-apple-macos build-apple-ios check-apple
|
.PHONY: apple-core apple-version-config apple-project open-apple-project open-apple build-apple-macos build-apple-ios check-apple
|
||||||
.PHONY: check-version check-release check-localization localization localization-migrate
|
.PHONY: prepare-release check-version check-release check-localization localization localization-migrate
|
||||||
.PHONY: check-docs run-docs check-diagnostics run-diagnostics diagnostics-db-local diagnostics-db-remote diagnostics-typegen deploy-diagnostics
|
.PHONY: check-docs run-docs check-diagnostics run-diagnostics diagnostics-db-local diagnostics-db-remote diagnostics-typegen deploy-diagnostics
|
||||||
|
|
||||||
help: ## Show available commands and common configuration variables.
|
help: ## Show available commands and common configuration variables.
|
||||||
@@ -61,6 +61,10 @@ test: test-rust test-shared ## Run the main Rust and shared JVM test suites.
|
|||||||
|
|
||||||
check: check-version check-rust check-shared check-localization check-docs check-diagnostics ## Run portable pre-PR verification.
|
check: check-version check-rust check-shared check-localization check-docs check-diagnostics ## Run portable pre-PR verification.
|
||||||
|
|
||||||
|
prepare-release: ## Update PRODUCT_VERSION and show its derived store versions (RELEASE_VERSION=x.y.z).
|
||||||
|
@test -n "$(RELEASE_VERSION)" || { printf 'Usage: make prepare-release RELEASE_VERSION=x.y.z\n' >&2; exit 1; }
|
||||||
|
cd $(ROOT) && packaging/version/prepare-release.sh "$(RELEASE_VERSION)"
|
||||||
|
|
||||||
check-version: ## Validate the canonical version and its platform mappings.
|
check-version: ## Validate the canonical version and its platform mappings.
|
||||||
cd $(ROOT) && packaging/version/test-version.sh
|
cd $(ROOT) && packaging/version/test-version.sh
|
||||||
cd $(ROOT) && packaging/version/resolve-version.sh verify
|
cd $(ROOT) && packaging/version/resolve-version.sh verify
|
||||||
|
|||||||
@@ -41,19 +41,18 @@ val productVersionMatch = Regex("(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-
|
|||||||
.matchEntire(productVersion)
|
.matchEntire(productVersion)
|
||||||
?: error("PRODUCT_VERSION must use canonical MAJOR.MINOR.PATCH integers")
|
?: error("PRODUCT_VERSION must use canonical MAJOR.MINOR.PATCH integers")
|
||||||
val productVersionParts = productVersionMatch.groupValues.drop(1).map(String::toLong)
|
val productVersionParts = productVersionMatch.groupValues.drop(1).map(String::toLong)
|
||||||
require(productVersionParts[0] <= 65534 && productVersionParts.drop(1).all { it <= 65535 }) {
|
require(productVersionParts[0] <= 2099 && productVersionParts.drop(1).all { it <= 999 }) {
|
||||||
"PRODUCT_VERSION components exceed the supported store ranges"
|
"PRODUCT_VERSION must use a major no greater than 2099 and minor/patch no greater than 999"
|
||||||
}
|
}
|
||||||
|
|
||||||
val releaseChannel = requiredVersionProperty("RELEASE_CHANNEL")
|
val releaseChannel = requiredVersionProperty("RELEASE_CHANNEL")
|
||||||
require(releaseChannel.matches(Regex("[a-z][a-z0-9-]*"))) {
|
require(releaseChannel.matches(Regex("[a-z][a-z0-9-]*"))) {
|
||||||
"RELEASE_CHANNEL contains unsupported characters"
|
"RELEASE_CHANNEL contains unsupported characters"
|
||||||
}
|
}
|
||||||
val androidVersionCode = canonicalInteger(
|
val androidVersionCode =
|
||||||
"ANDROID_VERSION_CODE",
|
(productVersionParts[0] * 1_000_000L + productVersionParts[1] * 1_000L + productVersionParts[2])
|
||||||
requiredVersionProperty("ANDROID_VERSION_CODE"),
|
.also { require(it in 1L..2_100_000_000L) { "Derived Android version code is out of range" } }
|
||||||
1L..2_100_000_000L,
|
.toInt()
|
||||||
).toInt()
|
|
||||||
val windowsVersionEpoch = canonicalInteger(
|
val windowsVersionEpoch = canonicalInteger(
|
||||||
"WINDOWS_VERSION_EPOCH",
|
"WINDOWS_VERSION_EPOCH",
|
||||||
requiredVersionProperty("WINDOWS_VERSION_EPOCH"),
|
requiredVersionProperty("WINDOWS_VERSION_EPOCH"),
|
||||||
|
|||||||
@@ -13,27 +13,37 @@ Platform workflows upload private workflow artifacts. After every native build
|
|||||||
passes, the release pipeline:
|
passes, the release pipeline:
|
||||||
|
|
||||||
1. stages the signed AAB as a draft on the configured Play closed-test track;
|
1. stages the signed AAB as a draft on the configured Play closed-test track;
|
||||||
2. downloads the universal APK signed by Play;
|
2. submits the unsigned `.msixupload` package to Microsoft Store certification;
|
||||||
3. verifies and assembles the public artifacts;
|
3. downloads the universal APK signed by Play;
|
||||||
4. generates checksums and GitHub build-provenance attestations;
|
4. verifies and assembles the public artifacts;
|
||||||
5. creates exactly one GitHub Release;
|
5. generates checksums and GitHub build-provenance attestations;
|
||||||
6. updates the Homebrew cask.
|
6. creates exactly one GitHub Release;
|
||||||
|
7. updates the Homebrew cask.
|
||||||
|
|
||||||
Public GitHub Release assets are the DEB, RPM, notarized DMG, Sparkle appcast,
|
Public GitHub Release assets are the DEB, RPM, notarized DMG, Sparkle appcast,
|
||||||
Play-signed universal APK, checksum file, and release manifest.
|
Play-signed universal APK, checksum file, and release manifest.
|
||||||
|
|
||||||
The unsigned Microsoft `.msixupload` and upload-signed Android AAB remain
|
The unsigned Microsoft `.msixupload` and upload-signed Android AAB remain
|
||||||
private workflow artifacts. Partner Center submission stays manual until the
|
private workflow artifacts. The protected `microsoft-store` GitHub Environment
|
||||||
first Microsoft Store release is certified. The Play release remains a draft
|
supplies the Partner Center credentials and Store product ID used to submit the
|
||||||
on a closed-testing track; this pipeline cannot publish to production.
|
Windows package. Microsoft publishes the update after certification; the job
|
||||||
|
does not change Store listings, pricing, or availability. The Play release
|
||||||
|
remains a draft on a closed-testing track; this pipeline cannot publish it to
|
||||||
|
production.
|
||||||
|
|
||||||
To release, first update and merge `version.properties`, including a monotonic
|
To release, prepare and merge the new product version. Android, Microsoft Store,
|
||||||
Android version code. Apple Store and Direct build numbers are derived
|
and Apple build/package versions are derived automatically:
|
||||||
independently at build time. Then create and push the matching tag:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git tag -s v0.2.0 -m "VniDrop 0.2.0"
|
make prepare-release RELEASE_VERSION=0.2.1
|
||||||
git push origin v0.2.0
|
make check-version
|
||||||
|
```
|
||||||
|
|
||||||
|
Then create and push the matching tag:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git tag -s v0.2.1 -m "VniDrop 0.2.1"
|
||||||
|
git push origin v0.2.1
|
||||||
```
|
```
|
||||||
|
|
||||||
The tag must point at the current `origin/master` commit. A failed run creates
|
The tag must point at the current `origin/master` commit. A failed run creates
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
# Application versioning
|
# Application versioning
|
||||||
|
|
||||||
`version.properties` at the repository root is the single source of truth for
|
`version.properties` at the repository root is the single source of truth for
|
||||||
the VniDrop product version and persistent store counters. Platform projects
|
the VniDrop product version and the permanent Windows version epoch. Platform
|
||||||
and release workflows use the version resolver rather than accepting
|
projects and release workflows derive their versions from it rather than
|
||||||
independent version overrides.
|
accepting independent release counters.
|
||||||
|
|
||||||
Keep it as plain `KEY=VALUE` assignments: the same file is parsed by shell,
|
Keep it as plain `KEY=VALUE` assignments: the same file is parsed by shell,
|
||||||
PowerShell, Gradle, and Rust. Xcode receives resolver-generated xcconfig files.
|
PowerShell, Gradle, and Rust. Xcode receives resolver-generated xcconfig files.
|
||||||
@@ -15,7 +15,7 @@ the patch component (`0.2.1`). Release channels belong in
|
|||||||
|
|
||||||
| Platform | Product version | Platform build/package version |
|
| Platform | Product version | Platform build/package version |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| Android | `PRODUCT_VERSION` | `ANDROID_VERSION_CODE` |
|
| Android | `PRODUCT_VERSION` | Derived monotonic integer |
|
||||||
| Apple Store | `PRODUCT_VERSION` | Derived UTC `YYYYMMDD.HHMM.SS` |
|
| Apple Store | `PRODUCT_VERSION` | Derived UTC `YYYYMMDD.HHMM.SS` |
|
||||||
| Direct macOS | `PRODUCT_VERSION` | Independently derived UTC `YYYYMMDD.HHMM.SS` |
|
| Direct macOS | `PRODUCT_VERSION` | Independently derived UTC `YYYYMMDD.HHMM.SS` |
|
||||||
| Linux | `PRODUCT_VERSION` | Native package revision |
|
| Linux | `PRODUCT_VERSION` | Native package revision |
|
||||||
@@ -32,12 +32,23 @@ the Store. Its version is:
|
|||||||
With epoch `1`, product `0.2.0` maps to MSIX `1.2.0.0`, while product `1.0.0`
|
With epoch `1`, product `0.2.0` maps to MSIX `1.2.0.0`, while product `1.0.0`
|
||||||
maps to `2.0.0.0`. Do not change the epoch after publishing.
|
maps to `2.0.0.0`. Do not change the epoch after publishing.
|
||||||
|
|
||||||
Every Android upload must increment `ANDROID_VERSION_CODE`. Apple build numbers
|
Android derives its version code as:
|
||||||
are derived at build time by `apple-store-build` and `apple-direct-build`; they
|
|
||||||
are kept as separate resolver outputs so App Store and Sparkle releases do not
|
```text
|
||||||
consume each other's cadence. Every changed Windows Store package must
|
product major * 1,000,000 + product minor * 1,000 + product patch
|
||||||
increment the product version because the Store-reserved fourth component
|
```
|
||||||
cannot carry a rebuild number.
|
|
||||||
|
For example, `0.2.0` maps to Android code `2000`, `0.2.1` to `2001`, and
|
||||||
|
`1.0.0` to `1000000`. To keep that mapping unique and within store limits,
|
||||||
|
the product major may not exceed `2099`, and minor and patch may not exceed
|
||||||
|
`999`. A rejected store build must use a new patch version rather than
|
||||||
|
rebuilding a previously uploaded product version.
|
||||||
|
|
||||||
|
Apple build numbers are derived at build time by `apple-store-build` and
|
||||||
|
`apple-direct-build`; they are kept as separate resolver outputs so App Store
|
||||||
|
and Sparkle releases do not consume each other's cadence. Every changed Windows
|
||||||
|
Store package must increment the product version because the Store-reserved
|
||||||
|
fourth component cannot carry a rebuild number.
|
||||||
|
|
||||||
Apple projects read generated build settings rather than `version.properties`
|
Apple projects read generated build settings rather than `version.properties`
|
||||||
directly:
|
directly:
|
||||||
@@ -50,7 +61,14 @@ The generated files under `apple/Generated/` are intentionally ignored.
|
|||||||
`VNIDROP_BUILD_TIME_UTC=YYYYMMDDHHMMSS` provides a deterministic clock for
|
`VNIDROP_BUILD_TIME_UTC=YYYYMMDDHHMMSS` provides a deterministic clock for
|
||||||
tests; distribution builds normally use the current UTC time.
|
tests; distribution builds normally use the current UTC time.
|
||||||
|
|
||||||
Before releasing:
|
Prepare the next release by changing only the product version:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make prepare-release RELEASE_VERSION=0.2.1
|
||||||
|
```
|
||||||
|
|
||||||
|
The command refuses non-increasing versions, updates `PRODUCT_VERSION`, and
|
||||||
|
prints the derived Android and Microsoft Store versions. Then verify:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make check-version
|
make check-version
|
||||||
|
|||||||
53
packaging/version/prepare-release.sh
Executable file
53
packaging/version/prepare-release.sh
Executable file
@@ -0,0 +1,53 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
repo_root="$(cd "$script_dir/../.." && pwd)"
|
||||||
|
resolver="$script_dir/resolve-version.sh"
|
||||||
|
version_file="${VNIDROP_VERSION_FILE:-$repo_root/version.properties}"
|
||||||
|
next_version="${1:-}"
|
||||||
|
|
||||||
|
fail() {
|
||||||
|
printf '%s\n' "$*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
[[ -n $next_version ]] ||
|
||||||
|
fail "Usage: $0 MAJOR.MINOR.PATCH"
|
||||||
|
[[ -f $version_file ]] ||
|
||||||
|
fail "Version file not found: $version_file"
|
||||||
|
[[ $(grep -c '^PRODUCT_VERSION=' "$version_file") == 1 ]] ||
|
||||||
|
fail "Expected exactly one PRODUCT_VERSION entry in $version_file"
|
||||||
|
|
||||||
|
current_version="$(
|
||||||
|
VNIDROP_VERSION_FILE="$version_file" "$resolver" product
|
||||||
|
)"
|
||||||
|
current_android_code="$(
|
||||||
|
VNIDROP_VERSION_FILE="$version_file" "$resolver" android-code
|
||||||
|
)"
|
||||||
|
temporary="$(mktemp "$(dirname "$version_file")/.version.properties.XXXXXX")"
|
||||||
|
trap 'rm -f "$temporary"' EXIT
|
||||||
|
|
||||||
|
sed "s/^PRODUCT_VERSION=.*/PRODUCT_VERSION=$next_version/" \
|
||||||
|
"$version_file" > "$temporary"
|
||||||
|
|
||||||
|
next_android_code="$(
|
||||||
|
VNIDROP_VERSION_FILE="$temporary" "$resolver" android-code
|
||||||
|
)"
|
||||||
|
next_windows_package="$(
|
||||||
|
VNIDROP_VERSION_FILE="$temporary" "$resolver" windows-package
|
||||||
|
)"
|
||||||
|
VNIDROP_VERSION_FILE="$temporary" "$resolver" verify >/dev/null
|
||||||
|
|
||||||
|
(( next_android_code > current_android_code )) ||
|
||||||
|
fail "New version must be greater than $current_version"
|
||||||
|
|
||||||
|
chmod 644 "$temporary"
|
||||||
|
mv "$temporary" "$version_file"
|
||||||
|
trap - EXIT
|
||||||
|
|
||||||
|
printf 'Prepared VniDrop %s\n' "$next_version"
|
||||||
|
printf ' Android version code: %s\n' "$next_android_code"
|
||||||
|
printf ' Microsoft Store package: %s\n' "$next_windows_package"
|
||||||
|
printf 'Next: make check-version\n'
|
||||||
@@ -47,7 +47,6 @@ function Convert-CanonicalInteger {
|
|||||||
|
|
||||||
$productVersion = Read-VersionProperty "PRODUCT_VERSION"
|
$productVersion = Read-VersionProperty "PRODUCT_VERSION"
|
||||||
$releaseChannel = Read-VersionProperty "RELEASE_CHANNEL"
|
$releaseChannel = Read-VersionProperty "RELEASE_CHANNEL"
|
||||||
$androidVersionCodeText = Read-VersionProperty "ANDROID_VERSION_CODE"
|
|
||||||
$windowsVersionEpochText = Read-VersionProperty "WINDOWS_VERSION_EPOCH"
|
$windowsVersionEpochText = Read-VersionProperty "WINDOWS_VERSION_EPOCH"
|
||||||
$buildTimeUtc = $env:VNIDROP_BUILD_TIME_UTC
|
$buildTimeUtc = $env:VNIDROP_BUILD_TIME_UTC
|
||||||
if ([string]::IsNullOrWhiteSpace($buildTimeUtc)) {
|
if ([string]::IsNullOrWhiteSpace($buildTimeUtc)) {
|
||||||
@@ -78,13 +77,16 @@ if ($productVersion -notmatch "^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*
|
|||||||
throw "PRODUCT_VERSION must use canonical MAJOR.MINOR.PATCH integers"
|
throw "PRODUCT_VERSION must use canonical MAJOR.MINOR.PATCH integers"
|
||||||
}
|
}
|
||||||
$productParts = $productVersion.Split(".")
|
$productParts = $productVersion.Split(".")
|
||||||
$productMajor = Convert-CanonicalInteger "PRODUCT_VERSION major" $productParts[0] 0 65534
|
$productMajor = Convert-CanonicalInteger "PRODUCT_VERSION major" $productParts[0] 0 2099
|
||||||
$null = Convert-CanonicalInteger "PRODUCT_VERSION minor" $productParts[1] 0 65535
|
$productMinor = Convert-CanonicalInteger "PRODUCT_VERSION minor" $productParts[1] 0 999
|
||||||
$null = Convert-CanonicalInteger "PRODUCT_VERSION patch" $productParts[2] 0 65535
|
$productPatch = Convert-CanonicalInteger "PRODUCT_VERSION patch" $productParts[2] 0 999
|
||||||
if ($releaseChannel -notmatch "^[a-z][a-z0-9-]*$") {
|
if ($releaseChannel -notmatch "^[a-z][a-z0-9-]*$") {
|
||||||
throw "RELEASE_CHANNEL contains unsupported characters"
|
throw "RELEASE_CHANNEL contains unsupported characters"
|
||||||
}
|
}
|
||||||
$androidVersionCode = Convert-CanonicalInteger "ANDROID_VERSION_CODE" $androidVersionCodeText 1 2100000000
|
$androidVersionCode = $productMajor * 1000000L + $productMinor * 1000L + $productPatch
|
||||||
|
if ($androidVersionCode -lt 1 -or $androidVersionCode -gt 2100000000L) {
|
||||||
|
throw "Derived Android version code must be between 1 and 2100000000"
|
||||||
|
}
|
||||||
$windowsVersionEpoch = Convert-CanonicalInteger "WINDOWS_VERSION_EPOCH" $windowsVersionEpochText 1 65535
|
$windowsVersionEpoch = Convert-CanonicalInteger "WINDOWS_VERSION_EPOCH" $windowsVersionEpochText 1 65535
|
||||||
$windowsMajor = $productMajor + $windowsVersionEpoch
|
$windowsMajor = $productMajor + $windowsVersionEpoch
|
||||||
if ($windowsMajor -gt 65535) {
|
if ($windowsMajor -gt 65535) {
|
||||||
|
|||||||
@@ -34,23 +34,29 @@ validate_canonical_integer() {
|
|||||||
|
|
||||||
product_version="$(read_property PRODUCT_VERSION)"
|
product_version="$(read_property PRODUCT_VERSION)"
|
||||||
release_channel="$(read_property RELEASE_CHANNEL)"
|
release_channel="$(read_property RELEASE_CHANNEL)"
|
||||||
android_version_code="$(read_property ANDROID_VERSION_CODE)"
|
|
||||||
windows_version_epoch="$(read_property WINDOWS_VERSION_EPOCH)"
|
windows_version_epoch="$(read_property WINDOWS_VERSION_EPOCH)"
|
||||||
build_time_utc="${VNIDROP_BUILD_TIME_UTC:-$(date -u +%Y%m%d%H%M%S)}"
|
build_time_utc="${VNIDROP_BUILD_TIME_UTC:-$(date -u +%Y%m%d%H%M%S)}"
|
||||||
|
|
||||||
[[ $product_version =~ ^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]] ||
|
[[ $product_version =~ ^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]] ||
|
||||||
fail "PRODUCT_VERSION must use canonical MAJOR.MINOR.PATCH integers"
|
fail "PRODUCT_VERSION must use canonical MAJOR.MINOR.PATCH integers"
|
||||||
IFS=. read -r product_major product_minor product_patch <<< "$product_version"
|
IFS=. read -r product_major product_minor product_patch <<< "$product_version"
|
||||||
validate_canonical_integer "PRODUCT_VERSION major" "$product_major" 0 65534
|
validate_canonical_integer "PRODUCT_VERSION major" "$product_major" 0 2099
|
||||||
validate_canonical_integer "PRODUCT_VERSION minor" "$product_minor" 0 65535
|
validate_canonical_integer "PRODUCT_VERSION minor" "$product_minor" 0 999
|
||||||
validate_canonical_integer "PRODUCT_VERSION patch" "$product_patch" 0 65535
|
validate_canonical_integer "PRODUCT_VERSION patch" "$product_patch" 0 999
|
||||||
[[ $release_channel =~ ^[a-z][a-z0-9-]*$ ]] ||
|
[[ $release_channel =~ ^[a-z][a-z0-9-]*$ ]] ||
|
||||||
fail "RELEASE_CHANNEL must start with a lowercase letter and contain only lowercase letters, digits, and hyphens"
|
fail "RELEASE_CHANNEL must start with a lowercase letter and contain only lowercase letters, digits, and hyphens"
|
||||||
validate_canonical_integer "ANDROID_VERSION_CODE" "$android_version_code" 1 2100000000
|
|
||||||
validate_canonical_integer "WINDOWS_VERSION_EPOCH" "$windows_version_epoch" 1 65535
|
validate_canonical_integer "WINDOWS_VERSION_EPOCH" "$windows_version_epoch" 1 65535
|
||||||
[[ $build_time_utc =~ ^[0-9]{14}$ ]] ||
|
[[ $build_time_utc =~ ^[0-9]{14}$ ]] ||
|
||||||
fail "VNIDROP_BUILD_TIME_UTC must use YYYYMMDDHHMMSS"
|
fail "VNIDROP_BUILD_TIME_UTC must use YYYYMMDDHHMMSS"
|
||||||
|
|
||||||
|
android_version_code=$((
|
||||||
|
(10#$product_major * 1000000) +
|
||||||
|
(10#$product_minor * 1000) +
|
||||||
|
10#$product_patch
|
||||||
|
))
|
||||||
|
(( android_version_code >= 1 && android_version_code <= 2100000000 )) ||
|
||||||
|
fail "Derived Android version code must be between 1 and 2100000000"
|
||||||
|
|
||||||
build_month="${build_time_utc:4:2}"
|
build_month="${build_time_utc:4:2}"
|
||||||
build_day="${build_time_utc:6:2}"
|
build_day="${build_time_utc:6:2}"
|
||||||
build_hour="${build_time_utc:8:2}"
|
build_hour="${build_time_utc:8:2}"
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ set -euo pipefail
|
|||||||
|
|
||||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
resolver="$script_dir/resolve-version.sh"
|
resolver="$script_dir/resolve-version.sh"
|
||||||
|
prepare_release="$script_dir/prepare-release.sh"
|
||||||
scratch="$(mktemp -d)"
|
scratch="$(mktemp -d)"
|
||||||
trap 'rm -rf "$scratch"' EXIT
|
trap 'rm -rf "$scratch"' EXIT
|
||||||
|
|
||||||
@@ -11,8 +12,7 @@ write_version() {
|
|||||||
printf '%s\n' \
|
printf '%s\n' \
|
||||||
"PRODUCT_VERSION=$1" \
|
"PRODUCT_VERSION=$1" \
|
||||||
"RELEASE_CHANNEL=$2" \
|
"RELEASE_CHANNEL=$2" \
|
||||||
"ANDROID_VERSION_CODE=$3" \
|
"WINDOWS_VERSION_EPOCH=$3" \
|
||||||
"WINDOWS_VERSION_EPOCH=$4" \
|
|
||||||
> "$scratch/version.properties"
|
> "$scratch/version.properties"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,34 +29,45 @@ expect_failure() {
|
|||||||
|
|
||||||
export VNIDROP_BUILD_TIME_UTC=20260728143217
|
export VNIDROP_BUILD_TIME_UTC=20260728143217
|
||||||
|
|
||||||
write_version 0.2.0 beta 2 1
|
write_version 0.2.0 beta 1
|
||||||
[[ $(resolve product) == 0.2.0 ]]
|
[[ $(resolve product) == 0.2.0 ]]
|
||||||
[[ $(resolve android-code) == 2 ]]
|
[[ $(resolve android-code) == 2000 ]]
|
||||||
[[ $(resolve apple-store-build) == 20260728.1432.17 ]]
|
[[ $(resolve apple-store-build) == 20260728.1432.17 ]]
|
||||||
[[ $(resolve apple-direct-build) == 20260728.1432.17 ]]
|
[[ $(resolve apple-direct-build) == 20260728.1432.17 ]]
|
||||||
[[ $(resolve windows-package) == 1.2.0.0 ]]
|
[[ $(resolve windows-package) == 1.2.0.0 ]]
|
||||||
resolve verify-tag v0.2.0
|
resolve verify-tag v0.2.0
|
||||||
expect_failure resolve verify-tag v1.0.0
|
expect_failure resolve verify-tag v1.0.0
|
||||||
|
|
||||||
write_version 1.0.0 stable 42 1
|
write_version 1.0.0 stable 1
|
||||||
|
[[ $(resolve android-code) == 1000000 ]]
|
||||||
[[ $(resolve windows-package) == 2.0.0.0 ]]
|
[[ $(resolve windows-package) == 2.0.0.0 ]]
|
||||||
|
|
||||||
write_version 01.0.0 beta 2 1
|
write_version 2099.999.999 stable 1
|
||||||
|
[[ $(resolve android-code) == 2099999999 ]]
|
||||||
|
|
||||||
|
write_version 01.0.0 beta 1
|
||||||
expect_failure resolve verify
|
expect_failure resolve verify
|
||||||
|
|
||||||
write_version 0.2.0 beta 0 1
|
write_version 0.0.0 beta 1
|
||||||
expect_failure resolve verify
|
expect_failure resolve verify
|
||||||
|
|
||||||
write_version 65535.0.0 stable 2 1
|
write_version 2100.0.0 stable 1
|
||||||
|
expect_failure resolve verify
|
||||||
|
|
||||||
|
write_version 0.1000.0 stable 1
|
||||||
|
expect_failure resolve verify
|
||||||
|
|
||||||
|
write_version 0.0.1000 stable 1
|
||||||
expect_failure resolve verify
|
expect_failure resolve verify
|
||||||
|
|
||||||
VNIDROP_BUILD_TIME_UTC=20260728146000 expect_failure resolve verify
|
VNIDROP_BUILD_TIME_UTC=20260728146000 expect_failure resolve verify
|
||||||
VNIDROP_BUILD_TIME_UTC=2026-07-28 expect_failure resolve verify
|
VNIDROP_BUILD_TIME_UTC=2026-07-28 expect_failure resolve verify
|
||||||
VNIDROP_BUILD_TIME_UTC=20260229080000 expect_failure resolve verify
|
VNIDROP_BUILD_TIME_UTC=20260229080000 expect_failure resolve verify
|
||||||
|
|
||||||
write_version 0.2.0 beta 2 1
|
write_version 0.2.0 beta 1
|
||||||
config_dir="$scratch/xcconfig"
|
config_dir="$scratch/xcconfig"
|
||||||
VNIDROP_APPLE_XCCONFIG_DIR="$config_dir" \
|
VNIDROP_VERSION_FILE="$scratch/version.properties" \
|
||||||
|
VNIDROP_APPLE_XCCONFIG_DIR="$config_dir" \
|
||||||
"$script_dir/generate-apple-xcconfig.sh" all
|
"$script_dir/generate-apple-xcconfig.sh" all
|
||||||
grep -Fx "PRODUCT_VERSION = 0.2.0" "$config_dir/StoreVersion.xcconfig" >/dev/null
|
grep -Fx "PRODUCT_VERSION = 0.2.0" "$config_dir/StoreVersion.xcconfig" >/dev/null
|
||||||
grep -Fx "CURRENT_PROJECT_VERSION = 20260728.1432.17" \
|
grep -Fx "CURRENT_PROJECT_VERSION = 20260728.1432.17" \
|
||||||
@@ -64,4 +75,14 @@ grep -Fx "CURRENT_PROJECT_VERSION = 20260728.1432.17" \
|
|||||||
grep -Fx "CURRENT_PROJECT_VERSION = 20260728.1432.17" \
|
grep -Fx "CURRENT_PROJECT_VERSION = 20260728.1432.17" \
|
||||||
"$config_dir/DirectVersion.xcconfig" >/dev/null
|
"$config_dir/DirectVersion.xcconfig" >/dev/null
|
||||||
|
|
||||||
|
VNIDROP_VERSION_FILE="$scratch/version.properties" \
|
||||||
|
"$prepare_release" 0.2.1 >/dev/null
|
||||||
|
[[ $(resolve product) == 0.2.1 ]]
|
||||||
|
[[ $(resolve android-code) == 2001 ]]
|
||||||
|
[[ $(resolve windows-package) == 1.2.1.0 ]]
|
||||||
|
expect_failure env VNIDROP_VERSION_FILE="$scratch/version.properties" \
|
||||||
|
"$prepare_release" 0.2.1
|
||||||
|
expect_failure env VNIDROP_VERSION_FILE="$scratch/version.properties" \
|
||||||
|
"$prepare_release" 0.1.999
|
||||||
|
|
||||||
printf 'Version resolver tests passed.\n'
|
printf 'Version resolver tests passed.\n'
|
||||||
|
|||||||
@@ -74,16 +74,21 @@ Use this restricted-capability justification in Submission options:
|
|||||||
> Rust and JVM libraries and needs normal user-level filesystem and network
|
> Rust and JVM libraries and needs normal user-level filesystem and network
|
||||||
> access to transfer user-selected files directly between devices.
|
> access to transfer user-selected files directly between devices.
|
||||||
|
|
||||||
After the first release is certified and live, Store publication can be added
|
After the first release is certified and live, the coordinated release
|
||||||
as a separate protected job. Keep its Partner Center credentials in a GitHub
|
workflow submits the generated `.msixupload` from a separate protected job.
|
||||||
Environment, not in this build job:
|
Keep its Partner Center credentials in the `microsoft-store` GitHub
|
||||||
|
Environment, not in the build job:
|
||||||
|
|
||||||
- AZURE_AD_TENANT_ID
|
- AZURE_AD_TENANT_ID
|
||||||
- AZURE_AD_APPLICATION_CLIENT_ID
|
- AZURE_AD_APPLICATION_CLIENT_ID
|
||||||
- AZURE_AD_APPLICATION_SECRET
|
- AZURE_AD_APPLICATION_SECRET
|
||||||
- SELLER_ID
|
- SELLER_ID
|
||||||
|
|
||||||
The Store ID is a non-secret variable.
|
Set `MICROSOFT_STORE_PRODUCT_ID` to `9NJ5Q0FG7TGL` as a non-secret variable in
|
||||||
|
the same environment. The publishing job validates the product ID,
|
||||||
|
authenticates with the pinned Microsoft Store Developer CLI, verifies access to
|
||||||
|
the product, and submits only the package for certification. Existing listings,
|
||||||
|
pricing, and availability are preserved.
|
||||||
|
|
||||||
## Manual build on Windows
|
## Manual build on Windows
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
PRODUCT_VERSION=0.2.0
|
PRODUCT_VERSION=0.2.1
|
||||||
RELEASE_CHANNEL=beta
|
RELEASE_CHANNEL=beta
|
||||||
ANDROID_VERSION_CODE=2
|
|
||||||
WINDOWS_VERSION_EPOCH=1
|
WINDOWS_VERSION_EPOCH=1
|
||||||
|
|||||||
Reference in New Issue
Block a user