fix(mobile): keep Android release versionCode committed (#7271)
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
parent
f4790e9fac
commit
25896f2cdb
|
|
@ -9,22 +9,8 @@ on:
|
|||
- 'mobile-android-v*'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
bump_patch_version:
|
||||
description: 'Bump the mobile marketing version patch number before release'
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
release_version:
|
||||
description: 'Optional exact mobile marketing version override, e.g. 0.0.15'
|
||||
required: false
|
||||
type: string
|
||||
bump_android_version_code:
|
||||
description: 'Increment the Android versionCode before release'
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
android_version_code:
|
||||
description: 'Optional exact Android versionCode override'
|
||||
description: 'Optional exact mobile marketing version assertion, e.g. 0.0.22'
|
||||
required: false
|
||||
type: string
|
||||
publish_github_release:
|
||||
|
|
@ -69,9 +55,6 @@ jobs:
|
|||
id: release
|
||||
env:
|
||||
MOBILE_ANDROID_RELEASE_VERSION: ${{ github.event.inputs.release_version }}
|
||||
MOBILE_ANDROID_BUMP_PATCH_VERSION: ${{ github.event.inputs.bump_patch_version }}
|
||||
MOBILE_ANDROID_VERSION_CODE: ${{ github.event.inputs.android_version_code }}
|
||||
MOBILE_ANDROID_BUMP_VERSION_CODE: ${{ github.event.inputs.bump_android_version_code }}
|
||||
MOBILE_ANDROID_PUBLISH_RELEASE: ${{ github.event.inputs.publish_github_release }}
|
||||
run: node scripts/prepare-android-release.mjs
|
||||
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@
|
|||
"allowBackup": false,
|
||||
"permissions": ["RECORD_AUDIO", "MODIFY_AUDIO_SETTINGS"],
|
||||
"package": "com.stably.orca.mobile",
|
||||
"versionCode": 2
|
||||
"versionCode": 4
|
||||
},
|
||||
"plugins": [
|
||||
"expo-router",
|
||||
|
|
|
|||
|
|
@ -22,31 +22,12 @@ function fail(message) {
|
|||
process.exit(1)
|
||||
}
|
||||
|
||||
function parsePositiveInteger(value, name) {
|
||||
if (!/^\d+$/.test(value)) {
|
||||
fail(`${name} must be a positive integer`)
|
||||
}
|
||||
|
||||
const parsed = Number(value)
|
||||
if (!Number.isSafeInteger(parsed) || parsed <= 0) {
|
||||
fail(`${name} must be a positive integer`)
|
||||
}
|
||||
|
||||
return parsed
|
||||
}
|
||||
|
||||
function validateSemver(version, name) {
|
||||
if (!semverPattern.test(version)) {
|
||||
fail(`${name} must use x.y.z format`)
|
||||
}
|
||||
}
|
||||
|
||||
function bumpPatchVersion(version) {
|
||||
validateSemver(version, 'Current mobile version')
|
||||
const [major, minor, patch] = version.split('.')
|
||||
return `${major}.${minor}.${Number(patch) + 1}`
|
||||
}
|
||||
|
||||
function writeOutput(name, value) {
|
||||
const outputPath = process.env.GITHUB_OUTPUT
|
||||
if (!outputPath) {
|
||||
|
|
@ -74,10 +55,6 @@ const tagVersion = githubRef.startsWith(androidTagRefPrefix)
|
|||
const requestedVersion = input('MOBILE_ANDROID_RELEASE_VERSION')
|
||||
const bumpPatch = truthy(input('MOBILE_ANDROID_BUMP_PATCH_VERSION'))
|
||||
|
||||
if (requestedVersion && bumpPatch) {
|
||||
fail('Use either MOBILE_ANDROID_RELEASE_VERSION or MOBILE_ANDROID_BUMP_PATCH_VERSION, not both')
|
||||
}
|
||||
|
||||
if (tagVersion) {
|
||||
validateSemver(tagVersion, 'Android release tag version')
|
||||
}
|
||||
|
|
@ -86,43 +63,36 @@ if (requestedVersion) {
|
|||
validateSemver(requestedVersion, 'MOBILE_ANDROID_RELEASE_VERSION')
|
||||
}
|
||||
|
||||
if (tagVersion && requestedVersion && requestedVersion !== tagVersion) {
|
||||
fail('MOBILE_ANDROID_RELEASE_VERSION must match the mobile-android-v tag')
|
||||
if (bumpPatch) {
|
||||
fail('MOBILE_ANDROID_BUMP_PATCH_VERSION is no longer supported; commit mobile/app.json first')
|
||||
}
|
||||
|
||||
if (tagVersion && bumpPatch) {
|
||||
fail('MOBILE_ANDROID_BUMP_PATCH_VERSION is only supported for manual branch runs')
|
||||
if (tagVersion && tagVersion !== currentVersion) {
|
||||
fail('Android release tag version must match the committed mobile app version')
|
||||
}
|
||||
|
||||
const version =
|
||||
requestedVersion || tagVersion || (bumpPatch ? bumpPatchVersion(currentVersion) : currentVersion)
|
||||
if (requestedVersion && requestedVersion !== currentVersion) {
|
||||
fail('MOBILE_ANDROID_RELEASE_VERSION must match the committed mobile app version')
|
||||
}
|
||||
|
||||
const requestedVersionCode = input('MOBILE_ANDROID_VERSION_CODE')
|
||||
const bumpVersionCode = truthy(input('MOBILE_ANDROID_BUMP_VERSION_CODE'))
|
||||
|
||||
if (requestedVersionCode && bumpVersionCode) {
|
||||
fail('Use either MOBILE_ANDROID_VERSION_CODE or MOBILE_ANDROID_BUMP_VERSION_CODE, not both')
|
||||
if (requestedVersionCode || bumpVersionCode) {
|
||||
// Why: Android rejects lower versionCode installs; release-only bumps leave
|
||||
// committed dev builds behind shipped APKs and break local testing.
|
||||
fail('Android versionCode changes must be committed in mobile/app.json before release')
|
||||
}
|
||||
|
||||
const versionCode = requestedVersionCode
|
||||
? parsePositiveInteger(requestedVersionCode, 'MOBILE_ANDROID_VERSION_CODE')
|
||||
: bumpVersionCode || version !== currentVersion
|
||||
? currentVersionCode + 1
|
||||
: currentVersionCode
|
||||
|
||||
expo.version = version
|
||||
android.versionCode = versionCode
|
||||
fs.writeFileSync(appConfigPath, `${JSON.stringify(config, null, 2)}\n`)
|
||||
|
||||
const tag = `mobile-android-v${version}`
|
||||
const tag = `mobile-android-v${currentVersion}`
|
||||
const publishRelease =
|
||||
githubRef.startsWith(androidTagRefPrefix) || truthy(input('MOBILE_ANDROID_PUBLISH_RELEASE'))
|
||||
|
||||
writeOutput('version', version)
|
||||
writeOutput('android_version_code', String(versionCode))
|
||||
writeOutput('version', currentVersion)
|
||||
writeOutput('android_version_code', String(currentVersionCode))
|
||||
writeOutput('tag', tag)
|
||||
writeOutput('publish_release', publishRelease ? 'true' : 'false')
|
||||
|
||||
console.log(`Prepared Orca Mobile Android ${version} (${versionCode})`)
|
||||
console.log(`Prepared Orca Mobile Android ${currentVersion} (${currentVersionCode})`)
|
||||
console.log(`Release tag: ${tag}`)
|
||||
console.log(`Publish GitHub Release: ${publishRelease ? 'yes' : 'no'}`)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
import { execFileSync, spawnSync } from 'node:child_process'
|
||||
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
|
||||
const scriptPath = fileURLToPath(
|
||||
new URL('../../scripts/prepare-android-release.mjs', import.meta.url)
|
||||
)
|
||||
|
||||
const appConfig = {
|
||||
expo: {
|
||||
version: '0.0.22',
|
||||
android: {
|
||||
versionCode: 4
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let tempDirs: string[] = []
|
||||
|
||||
function createAppConfig() {
|
||||
const dir = mkdtempSync(join(tmpdir(), 'orca-android-release-'))
|
||||
tempDirs.push(dir)
|
||||
const configPath = join(dir, 'app.json')
|
||||
const contents = `${JSON.stringify(appConfig, null, 2)}\n`
|
||||
writeFileSync(configPath, contents)
|
||||
return { configPath, contents }
|
||||
}
|
||||
|
||||
describe('prepare Android release script', () => {
|
||||
afterEach(() => {
|
||||
for (const dir of tempDirs) {
|
||||
rmSync(dir, { force: true, recursive: true })
|
||||
}
|
||||
tempDirs = []
|
||||
})
|
||||
|
||||
it('uses committed Android release identity without mutating app config', () => {
|
||||
const { configPath, contents } = createAppConfig()
|
||||
|
||||
const output = execFileSync(process.execPath, [scriptPath], {
|
||||
encoding: 'utf8',
|
||||
env: {
|
||||
...process.env,
|
||||
MOBILE_APP_CONFIG_PATH: configPath,
|
||||
MOBILE_ANDROID_PUBLISH_RELEASE: 'true'
|
||||
}
|
||||
})
|
||||
|
||||
expect(output).toContain('Prepared Orca Mobile Android 0.0.22 (4)')
|
||||
expect(output).toContain('Release tag: mobile-android-v0.0.22')
|
||||
expect(readFileSync(configPath, 'utf8')).toBe(contents)
|
||||
})
|
||||
|
||||
it('rejects release-only Android versionCode bumps', () => {
|
||||
const { configPath } = createAppConfig()
|
||||
|
||||
const result = spawnSync(process.execPath, [scriptPath], {
|
||||
encoding: 'utf8',
|
||||
env: {
|
||||
...process.env,
|
||||
MOBILE_APP_CONFIG_PATH: configPath,
|
||||
MOBILE_ANDROID_BUMP_VERSION_CODE: 'true'
|
||||
}
|
||||
})
|
||||
|
||||
expect(result.status).toBe(1)
|
||||
expect(result.stderr).toContain(
|
||||
'Android versionCode changes must be committed in mobile/app.json before release'
|
||||
)
|
||||
})
|
||||
|
||||
it('rejects release versions that do not match committed app config', () => {
|
||||
const { configPath } = createAppConfig()
|
||||
|
||||
const result = spawnSync(process.execPath, [scriptPath], {
|
||||
encoding: 'utf8',
|
||||
env: {
|
||||
...process.env,
|
||||
MOBILE_APP_CONFIG_PATH: configPath,
|
||||
MOBILE_ANDROID_RELEASE_VERSION: '0.0.23'
|
||||
}
|
||||
})
|
||||
|
||||
expect(result.status).toBe(1)
|
||||
expect(result.stderr).toContain(
|
||||
'MOBILE_ANDROID_RELEASE_VERSION must match the committed mobile app version'
|
||||
)
|
||||
})
|
||||
})
|
||||
Loading…
Reference in New Issue