mirror of
https://github.com/Darkrock-Studios/hammer-editor.git
synced 2026-10-19 09:34:23 +00:00
Stop submitting the GitHub changelog link to the Apple stores (#928)
prepareForRelease has appended a "Full changelog:" footer pointing at the GitHub release page to every store's notes since v3.7.0. App Store review rejects release notes carrying that link — it offers the app outside the App Store — and iOS and macOS submissions from v3.7.0 on have come back as invalid metadata. v3.6.1, the last release to ship, predates the footer. formatStoreNotes and storeNotesLength now take a nullable footer URL, and the Apple path passes null. Google Play keeps the link: its 500 character limit means the notes really are truncated there, so the footer is the only way to reach the full text. Also strips the stale v3.9.1 footer out of the two committed release_notes.txt files, so a resubmission of the current version does not ship it while waiting on the next prepareForRelease.
This commit is contained in:
parent
9ead030ab9
commit
bec73ea070
5 changed files with 73 additions and 23 deletions
|
|
@ -175,14 +175,15 @@ tasks.register("prepareForRelease") {
|
|||
val versionsFile = project.rootDir.resolve(versionsPath)
|
||||
writeSemvar(libs.versions.app.get(), releaseInfo.semVar, versionsFile)
|
||||
|
||||
// Store listings carry the app-only notes plus a link to the GitHub release,
|
||||
// which holds the full text including the web and server changes. A release
|
||||
// that reaches no store has no store notes; leave the existing metadata alone
|
||||
// rather than blanking the notes the last client release published.
|
||||
// Store listings carry the app-only notes. Google Play also gets a link to the
|
||||
// GitHub release, which holds the full text including the web and server changes;
|
||||
// the Apple stores must not (see releaseNotesUrl) and get the notes alone. A
|
||||
// release that reaches no store has no store notes; leave the existing metadata
|
||||
// alone rather than blanking the notes the last client release published.
|
||||
val fullNotesUrl = releaseNotesUrl(releaseInfo.tag)
|
||||
val storeNotes = releaseInfo.storeChangeLog
|
||||
val writeStoreNotes = storeNotes.isNotBlank()
|
||||
val notesLength = storeNotesLength(storeNotes, fullNotesUrl)
|
||||
val playNotesLength = storeNotesLength(storeNotes, fullNotesUrl)
|
||||
val playChangelog = formatStoreNotes(storeNotes, PLAY_STORE_LIMIT, fullNotesUrl)
|
||||
|
||||
// Write the Fastlane changelog file
|
||||
|
|
@ -194,15 +195,15 @@ tasks.register("prepareForRelease") {
|
|||
if (writeStoreNotes) {
|
||||
changeLogFile.writeText(playChangelog)
|
||||
println("Changelog for version ${releaseInfo.semVar} written to $changelogsPath/$versionCode.txt")
|
||||
if (notesLength > PLAY_STORE_LIMIT) {
|
||||
if (playNotesLength > PLAY_STORE_LIMIT) {
|
||||
println(" Google Play notes truncated to $PLAY_STORE_LIMIT characters; full text at $fullNotesUrl")
|
||||
}
|
||||
} else {
|
||||
println("No store notes for this release; store metadata left untouched")
|
||||
}
|
||||
|
||||
val appleChangelog = formatStoreNotes(storeNotes, APPLE_STORE_LIMIT, fullNotesUrl)
|
||||
if (writeStoreNotes && notesLength > APPLE_STORE_LIMIT) {
|
||||
val appleChangelog = formatStoreNotes(storeNotes, APPLE_STORE_LIMIT, null)
|
||||
if (writeStoreNotes && storeNotesLength(storeNotes, null) > APPLE_STORE_LIMIT) {
|
||||
println("App Store notes truncated to $APPLE_STORE_LIMIT characters; full text at $fullNotesUrl")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,14 @@ private val SECTION_HEADER = Regex("""^\[[^]]+]$""")
|
|||
/** The longest `Label:` prefix treated as an audience label rather than prose. */
|
||||
private const val MAX_LABEL_LENGTH = 20
|
||||
|
||||
/** The GitHub release page for a release tag, where the untruncated notes live. */
|
||||
/**
|
||||
* The GitHub release page for a release tag, where the untruncated notes live.
|
||||
*
|
||||
* Only Google Play is given this link. App Store review rejects release notes that
|
||||
* link to the project's GitHub page — it offers the app outside the App Store — so
|
||||
* the Apple stores pass `null` as the footer URL. The link shipped to iOS and macOS
|
||||
* from v3.7.0 onward and drew "invalid metadata" rejections until it was removed.
|
||||
*/
|
||||
fun releaseNotesUrl(tag: String): String = "$GITHUB_REPO/releases/tag/$tag"
|
||||
|
||||
/**
|
||||
|
|
@ -156,8 +163,9 @@ fun deriveStoreNotes(fullChangelog: String): StoreNotesDerivation {
|
|||
}
|
||||
|
||||
/**
|
||||
* Fits [changelog] into a store's character limit, always leaving room for a footer
|
||||
* pointing at the full notes. Whole entries are kept so the text does not end
|
||||
* Fits [changelog] into a store's character limit, leaving room for a footer pointing
|
||||
* at the full notes when [fullNotesUrl] is given — see [releaseNotesUrl] for why the
|
||||
* Apple stores pass null. Whole entries are kept so the text does not end
|
||||
* mid-bullet, and an entry too long for the remaining budget is skipped rather than
|
||||
* ending the notes. The `…` mark is appended only when an entry was actually left
|
||||
* out; text that fits once its blank lines are collapsed is not marked truncated.
|
||||
|
|
@ -169,7 +177,7 @@ fun deriveStoreNotes(fullChangelog: String): StoreNotesDerivation {
|
|||
* A blank changelog yields an empty string: store metadata that is only a link
|
||||
* describes nothing, and App Store review rejects it.
|
||||
*/
|
||||
fun formatStoreNotes(changelog: String, limit: Int, fullNotesUrl: String): String {
|
||||
fun formatStoreNotes(changelog: String, limit: Int, fullNotesUrl: String?): String {
|
||||
val body = normalizeNotes(changelog)
|
||||
if (body.isEmpty()) return ""
|
||||
|
||||
|
|
@ -202,13 +210,19 @@ fun formatStoreNotes(changelog: String, limit: Int, fullNotesUrl: String): Strin
|
|||
return fitted + mark + footer
|
||||
}
|
||||
|
||||
/** How many characters [changelog] needs at a store, footer included. */
|
||||
fun storeNotesLength(changelog: String, fullNotesUrl: String): Int {
|
||||
/**
|
||||
* How many characters [changelog] needs at a store, including the footer when
|
||||
* [fullNotesUrl] is given. Pass the same URL the store's [formatStoreNotes] call
|
||||
* gets — null for the Apple stores — or the "will it truncate?" comparison is off
|
||||
* by the footer's length.
|
||||
*/
|
||||
fun storeNotesLength(changelog: String, fullNotesUrl: String?): Int {
|
||||
val body = normalizeNotes(changelog)
|
||||
return if (body.isEmpty()) 0 else body.length + footerFor(fullNotesUrl).length
|
||||
}
|
||||
|
||||
private fun footerFor(fullNotesUrl: String) = "\n\n$FOOTER_LABEL\n$fullNotesUrl"
|
||||
private fun footerFor(fullNotesUrl: String?) =
|
||||
if (fullNotesUrl == null) "" else "\n\n$FOOTER_LABEL\n$fullNotesUrl"
|
||||
|
||||
/**
|
||||
* CHANGELOG.md is checked out with CRLF on Windows and seeds the release dialog, so
|
||||
|
|
|
|||
|
|
@ -213,6 +213,47 @@ class StoreNotesTest {
|
|||
assertEquals(shortNotes.length, storeNotesLength(short, url))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `A null url publishes the notes with no footer and no link`() {
|
||||
val changelog = "- [Fix] A bug\n- [New] A feature"
|
||||
|
||||
val notes = formatStoreNotes(changelog, APPLE_STORE_LIMIT, null)
|
||||
|
||||
assertEquals(changelog, notes)
|
||||
assertEquals(changelog.length, storeNotesLength(changelog, null))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Apple notes never carry a link that App Store review would reject`() {
|
||||
// The regression that broke every iOS and macOS submission from v3.7.0: the
|
||||
// footer was appended for Apple too, so the notes ended in a github.com URL.
|
||||
val changelog = (1..400).joinToString("\n") { "- [New] Feature number $it in this release" }
|
||||
|
||||
listOf(
|
||||
formatStoreNotes("- [Fix] A bug", APPLE_STORE_LIMIT, null),
|
||||
formatStoreNotes(changelog, APPLE_STORE_LIMIT, null),
|
||||
formatStoreNotes("word ".repeat(2000).trim(), APPLE_STORE_LIMIT, null),
|
||||
).forEach { notes ->
|
||||
assertTrue(!notes.contains("github.com"), "Apple notes carry a GitHub link:\n$notes")
|
||||
assertTrue(!notes.contains("Full changelog:"), "Apple notes carry the footer:\n$notes")
|
||||
assertTrue(notes.length <= APPLE_STORE_LIMIT, "Notes were ${notes.length} characters")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Dropping the footer gives the body more of the budget`() {
|
||||
val changelog = (1..40).joinToString("\n") { "- [New] Feature number $it in this release" }
|
||||
|
||||
val withFooter = formatStoreNotes(changelog, PLAY_STORE_LIMIT, url)
|
||||
val without = formatStoreNotes(changelog, PLAY_STORE_LIMIT, null)
|
||||
|
||||
assertTrue(
|
||||
publishedBody(without).length > publishedBody(withFooter).length,
|
||||
"The footer's characters were not returned to the body",
|
||||
)
|
||||
assertTrue(without.length <= PLAY_STORE_LIMIT, "Notes were ${without.length} characters")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Release url points at the tag`() {
|
||||
assertEquals(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,2 @@
|
|||
- Fix sharing scene order
|
||||
- Fix PDF export crash
|
||||
|
||||
Full changelog:
|
||||
https://github.com/Darkrock-Studios/hammer-editor/releases/tag/v3.9.1
|
||||
- Fix PDF export crash
|
||||
|
|
@ -1,5 +1,2 @@
|
|||
- Fix sharing scene order
|
||||
- Fix PDF export crash
|
||||
|
||||
Full changelog:
|
||||
https://github.com/Darkrock-Studios/hammer-editor/releases/tag/v3.9.1
|
||||
- Fix PDF export crash
|
||||
Loading…
Add table
Add a link
Reference in a new issue