Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,12 @@ jobs:
run: |
set -euo pipefail
gradle_tasks=()
gradle_options=()
if [[ "${RUN_CONTRACT}" == "true" ]]; then
gradle_tasks+=(":contractAcquisition:test")
fi
if [[ "${RUN_DESKTOP}" == "true" ]]; then
gradle_options+=("-PncDirectDesktopPackageUpdates=true")
gradle_tasks+=(":ui:desktopTest" ":ui:createDistributable")
fi
if [[ "${RUN_ANDROID}" == "true" ]]; then
Expand All @@ -228,7 +230,7 @@ jobs:
)
fi
printf 'Selected Gradle tasks: %s\n' "${gradle_tasks[*]}"
./gradlew --no-daemon "${gradle_tasks[@]}"
./gradlew --no-daemon "${gradle_options[@]}" "${gradle_tasks[@]}"

- name: Upload Linux desktop app
if: >-
Expand Down Expand Up @@ -335,7 +337,9 @@ jobs:
steps.changes.outputs.windows == 'true'
shell: pwsh
run: |
.\gradlew.bat --no-daemon :ui:desktopTest :ui:packageMsi
.\gradlew.bat --no-daemon `
-PncDirectDesktopPackageUpdates=true `
:ui:desktopTest :ui:packageMsi

- name: Verify Windows MSI metadata
if: >-
Expand Down
7 changes: 7 additions & 0 deletions changes/unreleased/development-build-release-updates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
category: fix
issue: 327
pull: 328
platforms: desktop
user-facing: yes

Development desktop builds can now update to a newer verified release from their selected remote update channel.
2 changes: 2 additions & 0 deletions tools/test-nightly-release-workflow.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ set -euo pipefail
project_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
nightly="$project_root/.github/workflows/nightly.yml"
prerelease="$project_root/.github/workflows/prerelease.yml"
ci="$project_root/.github/workflows/ci.yml"
nightly_notes="$project_root/tools/nightly-release-notes.mjs"
promotion="$project_root/tools/promote-app-update-channel.sh"
msi_repackager="$project_root/tools/repackage-msi-with-uninstall-cleanup.ps1"
Expand Down Expand Up @@ -76,6 +77,7 @@ require_text "$nightly" '-PncMacosPackageVersion="${NIGHTLY_DESKTOP_VERSION}"'
require_text "$nightly" '-PncDesktopReleaseBuild=true'
require_text "$nightly" '-PncDirectDesktopPackageUpdates="${{ matrix.direct_updates }}"'
require_text "$nightly" 'direct_updates: "true"'
require_count "$ci" '-PncDirectDesktopPackageUpdates=true' 2
require_text "$nightly" 'name: nextcloud-native-${{ matrix.platform }}'
require_text "$nightly" 'name: nextcloud-native-android'
require_text "$nightly" 'tools/stage-nightly-assets.sh artifacts dist'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ internal data class DesktopUpdateTarget(
val architecture: String,
)

internal fun canUseDirectDesktopUpdates(
buildIdentity: DesktopUpdateBuildIdentity,
target: DesktopUpdateTarget?,
): Boolean = target != null &&
buildIdentity.versionCode > 0 &&
buildIdentity.directPackageUpdates
Comment thread
veryCrunchy marked this conversation as resolved.

internal enum class DesktopPackageInstallerOutcome {
InstallerHandoffStarted,
InstallationCompleted,
Expand Down Expand Up @@ -157,10 +164,7 @@ internal class DesktopAppUpdater(
@Volatile private var cancellationRequested = false

fun support(): AppUpdateSupport {
val canUpdate = buildIdentity.releaseBuild &&
buildIdentity.directPackageUpdates &&
target != null &&
buildIdentity.versionCode > 0
val canUpdate = canUseDirectDesktopUpdates(buildIdentity, target)
return AppUpdateSupport(
channel = if (canUpdate) {
AppDistributionChannel.DirectDesktopPackage
Expand All @@ -170,12 +174,19 @@ internal class DesktopAppUpdater(
currentVersionName = buildIdentity.versionName,
currentVersionCode = buildIdentity.versionCode,
canCheckDirectUpdates = canUpdate,
explanation = if (canUpdate) {
"This native package checks the selected release channel, matches downloads to its advertised " +
"checksum, and uses your system installer."
} else {
"Development, distribution-managed, and unsupported desktop packages are updated through " +
"their distribution workflow."
explanation = when {
canUpdate && !buildIdentity.releaseBuild ->
"This development build can update to a newer release from the selected channel. Downloads " +
"are matched to their advertised checksum before using your system installer."
canUpdate ->
"This native package checks the selected release channel, matches downloads to its advertised " +
"checksum, and uses your system installer."
!buildIdentity.releaseBuild ->
"This development build cannot check for updates directly. Install a newer development build " +
"or release through the same download or package workflow that provided this build."
else ->
"Distribution-managed and unsupported desktop packages are updated through their distribution " +
"workflow."
},
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ class DesktopAppUpdatesTest {
}

@Test
fun onlyPackagedReleaseBuildsOfferDirectNativePackageUpdates() {
fun developmentAndEligibleReleaseBuildsOfferDirectNativePackageUpdates() {
val node = Preferences.userRoot().node("desktop-update-test-${UUID.randomUUID()}")
val directory = Files.createTempDirectory("desktop-update-support-test").toFile()
try {
Expand All @@ -310,7 +310,27 @@ class DesktopAppUpdatesTest {
)
val development = DesktopAppUpdater(
preferences = node,
buildIdentity = DesktopUpdateBuildIdentity("development", 0, "0.1.0", false, false),
buildIdentity = DesktopUpdateBuildIdentity(
"development",
10_000_001,
"0.1.0",
releaseBuild = false,
directPackageUpdates = true,
),
target = DesktopUpdateTarget("linux", "rpm", "x86_64"),
updateDirectory = directory,
openInstaller = { DesktopPackageInstallerOutcome.InstallerHandoffStarted },
)
val optedOutDevelopment = DesktopAppUpdater(
preferences = node,
buildIdentity = DesktopUpdateBuildIdentity("development", 10_000_001, "0.1.0", false, false),
target = DesktopUpdateTarget("linux", "rpm", "x86_64"),
updateDirectory = directory,
openInstaller = { DesktopPackageInstallerOutcome.InstallerHandoffStarted },
)
val unversionedDevelopment = DesktopAppUpdater(
preferences = node,
buildIdentity = DesktopUpdateBuildIdentity("development", 0, "0.1.0", false, true),
target = DesktopUpdateTarget("linux", "rpm", "x86_64"),
updateDirectory = directory,
openInstaller = { DesktopPackageInstallerOutcome.InstallerHandoffStarted },
Expand All @@ -320,8 +340,16 @@ class DesktopAppUpdatesTest {
assertTrue(release.support().canCheckDirectUpdates)
assertTrue(release.support().explanation.contains("checksum"))
assertFalse(release.support().explanation.contains("signed", ignoreCase = true))
assertEquals(AppDistributionChannel.Development, development.support().channel)
assertFalse(development.support().canCheckDirectUpdates)
assertEquals(AppDistributionChannel.DirectDesktopPackage, development.support().channel)
assertTrue(development.support().canCheckDirectUpdates)
assertTrue(development.support().explanation.contains("development build"))
assertEquals(AppDistributionChannel.Development, optedOutDevelopment.support().channel)
assertFalse(optedOutDevelopment.support().canCheckDirectUpdates)
assertTrue(optedOutDevelopment.support().explanation.contains("development build"))
assertTrue(optedOutDevelopment.support().explanation.contains("cannot check for updates directly"))
assertEquals(AppDistributionChannel.Development, unversionedDevelopment.support().channel)
assertFalse(unversionedDevelopment.support().canCheckDirectUpdates)
assertTrue(unversionedDevelopment.support().explanation.contains("development build"))
val distributionManaged = DesktopAppUpdater(
preferences = node,
buildIdentity = DesktopUpdateBuildIdentity(
Expand All @@ -337,7 +365,9 @@ class DesktopAppUpdatesTest {
)
assertEquals(AppDistributionChannel.Development, distributionManaged.support().channel)
assertFalse(distributionManaged.support().canCheckDirectUpdates)
assertTrue(distributionManaged.support().explanation.contains("distribution-managed"))
assertTrue(
distributionManaged.support().explanation.contains("distribution-managed", ignoreCase = true),
)
assertEquals(6L * 60L * 60L * 1_000L, DESKTOP_APP_UPDATE_CHECK_INTERVAL_MILLIS)
val windowsRelease = DesktopAppUpdater(
preferences = node,
Expand Down
Loading