summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ci/scripts/android/eabuild.sh21
-rw-r--r--.ci/scripts/android/mainlinebuild.sh21
-rw-r--r--.github/workflows/android-ea-play-release.yml66
-rw-r--r--.github/workflows/android-mainline-play-release.yml59
-rw-r--r--.github/workflows/android-merge.js129
-rw-r--r--.github/workflows/android-publish.yml4
-rw-r--r--src/android/app/build.gradle.kts31
7 files changed, 288 insertions, 43 deletions
diff --git a/.ci/scripts/android/eabuild.sh b/.ci/scripts/android/eabuild.sh
new file mode 100644
index 000000000..1672f2948
--- /dev/null
+++ b/.ci/scripts/android/eabuild.sh
@@ -0,0 +1,21 @@
1#!/bin/bash -ex
2
3# SPDX-FileCopyrightText: 2024 yuzu Emulator Project
4# SPDX-License-Identifier: GPL-3.0-or-later
5
6export NDK_CCACHE="$(which ccache)"
7ccache -s
8
9export ANDROID_KEYSTORE_FILE="${GITHUB_WORKSPACE}/ks.jks"
10base64 --decode <<< "${EA_PLAY_ANDROID_KEYSTORE_B64}" > "${ANDROID_KEYSTORE_FILE}"
11export ANDROID_KEY_ALIAS="${PLAY_ANDROID_KEY_ALIAS}"
12export ANDROID_KEYSTORE_PASS="${PLAY_ANDROID_KEYSTORE_PASS}"
13export SERVICE_ACCOUNT_KEY_PATH="${GITHUB_WORKSPACE}/sa.json"
14base64 --decode <<< "${EA_SERVICE_ACCOUNT_KEY_B64}" > "${SERVICE_ACCOUNT_KEY_PATH}"
15./gradlew "publishEaReleaseBundle"
16
17ccache -s
18
19if [ ! -z "${ANDROID_KEYSTORE_B64}" ]; then
20 rm "${ANDROID_KEYSTORE_FILE}"
21fi
diff --git a/.ci/scripts/android/mainlinebuild.sh b/.ci/scripts/android/mainlinebuild.sh
new file mode 100644
index 000000000..f3b89ed1c
--- /dev/null
+++ b/.ci/scripts/android/mainlinebuild.sh
@@ -0,0 +1,21 @@
1#!/bin/bash -ex
2
3# SPDX-FileCopyrightText: 2024 yuzu Emulator Project
4# SPDX-License-Identifier: GPL-3.0-or-later
5
6export NDK_CCACHE="$(which ccache)"
7ccache -s
8
9export ANDROID_KEYSTORE_FILE="${GITHUB_WORKSPACE}/ks.jks"
10base64 --decode <<< "${MAINLINE_PLAY_ANDROID_KEYSTORE_B64}" > "${ANDROID_KEYSTORE_FILE}"
11export ANDROID_KEY_ALIAS="${PLAY_ANDROID_KEY_ALIAS}"
12export ANDROID_KEYSTORE_PASS="${PLAY_ANDROID_KEYSTORE_PASS}"
13export SERVICE_ACCOUNT_KEY_PATH="${GITHUB_WORKSPACE}/sa.json"
14base64 --decode <<< "${MAINLINE_SERVICE_ACCOUNT_KEY_B64}" > "${SERVICE_ACCOUNT_KEY_PATH}"
15./gradlew "publishMainlineReleaseBundle"
16
17ccache -s
18
19if [ ! -z "${ANDROID_KEYSTORE_B64}" ]; then
20 rm "${ANDROID_KEYSTORE_FILE}"
21fi
diff --git a/.github/workflows/android-ea-play-release.yml b/.github/workflows/android-ea-play-release.yml
new file mode 100644
index 000000000..0cf78279c
--- /dev/null
+++ b/.github/workflows/android-ea-play-release.yml
@@ -0,0 +1,66 @@
1# SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2# SPDX-License-Identifier: GPL-2.0-or-later
3
4name: yuzu-android-ea-play-release
5
6on:
7 workflow_dispatch:
8 inputs:
9 release-track:
10 description: 'Play store release track (internal/alpha/beta/production)'
11 required: true
12 default: 'alpha'
13
14jobs:
15 android:
16 runs-on: ubuntu-latest
17 if: ${{ github.repository == 'yuzu-emu/yuzu' }}
18 steps:
19 - uses: actions/checkout@v3
20 name: Checkout
21 with:
22 fetch-depth: 0
23 submodules: true
24 token: ${{ secrets.ALT_GITHUB_TOKEN }}
25 - run: npm install execa@5
26 - uses: actions/github-script@v5
27 name: 'Merge and publish Android EA changes'
28 env:
29 ALT_GITHUB_TOKEN: ${{ secrets.ALT_GITHUB_TOKEN }}
30 BUILD_EA: true
31 with:
32 script: |
33 const execa = require("execa");
34 const mergebot = require('./.github/workflows/android-merge.js').mergebot;
35 process.chdir('${{ github.workspace }}');
36 mergebot(github, context, execa);
37 - name: Get tag name
38 run: echo "GIT_TAG_NAME=$(cat tag-name.txt)" >> $GITHUB_ENV
39 - name: Set up JDK 17
40 uses: actions/setup-java@v3
41 with:
42 java-version: '17'
43 distribution: 'temurin'
44 - name: Install dependencies
45 run: |
46 sudo apt-get update
47 sudo apt-get install -y ccache apksigner glslang-dev glslang-tools
48 - name: Build
49 run: ./.ci/scripts/android/eabuild.sh
50 env:
51 EA_PLAY_ANDROID_KEYSTORE_B64: ${{ secrets.PLAY_ANDROID_KEYSTORE_B64 }}
52 PLAY_ANDROID_KEY_ALIAS: ${{ secrets.PLAY_ANDROID_KEY_ALIAS }}
53 PLAY_ANDROID_KEYSTORE_PASS: ${{ secrets.PLAY_ANDROID_KEYSTORE_PASS }}
54 EA_SERVICE_ACCOUNT_KEY_B64: ${{ secrets.EA_SERVICE_ACCOUNT_KEY_B64 }}
55 STORE_TRACK: ${{ github.event.inputs.release-track }}
56 AUTO_VERSIONED: true
57 BUILD_EA: true
58 - name: Create release
59 uses: softprops/action-gh-release@v1
60 with:
61 tag_name: ${{ env.EA_TAG_NAME }}
62 name: ${{ env.EA_TAG_NAME }}
63 draft: false
64 prerelease: false
65 repository: yuzu/yuzu-android
66 token: ${{ secrets.ALT_GITHUB_TOKEN }}
diff --git a/.github/workflows/android-mainline-play-release.yml b/.github/workflows/android-mainline-play-release.yml
new file mode 100644
index 000000000..8255e0a40
--- /dev/null
+++ b/.github/workflows/android-mainline-play-release.yml
@@ -0,0 +1,59 @@
1# SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2# SPDX-License-Identifier: GPL-2.0-or-later
3
4name: yuzu-android-mainline-play-release
5
6on:
7 workflow_dispatch:
8 inputs:
9 release-tag:
10 description: 'Tag # from yuzu-android that you want to build and publish'
11 required: true
12 default: '200'
13 release-track:
14 description: 'Play store release track (internal/alpha/beta/production)'
15 required: true
16 default: 'alpha'
17
18jobs:
19 android:
20 runs-on: ubuntu-latest
21 if: ${{ github.repository == 'yuzu-emu/yuzu' }}
22 steps:
23 - uses: actions/checkout@v3
24 name: Checkout
25 with:
26 fetch-depth: 0
27 submodules: true
28 token: ${{ secrets.ALT_GITHUB_TOKEN }}
29 - run: npm install execa@5
30 - uses: actions/github-script@v5
31 name: 'Pull mainline tag'
32 env:
33 MAINLINE_TAG: ${{ github.event.inputs.release-tag }}
34 with:
35 script: |
36 const execa = require("execa");
37 const mergebot = require('./.github/workflows/android-merge.js').getMainlineTag;
38 process.chdir('${{ github.workspace }}');
39 mergebot(execa);
40 - name: Set up JDK 17
41 uses: actions/setup-java@v3
42 with:
43 java-version: '17'
44 distribution: 'temurin'
45 - name: Install dependencies
46 run: |
47 sudo apt-get update
48 sudo apt-get install -y ccache apksigner glslang-dev glslang-tools
49 - name: Build
50 run: |
51 echo "GIT_TAG_NAME=android-${{ github.event.inputs.releast-tag }}" >> $GITHUB_ENV
52 ./.ci/scripts/android/mainlinebuild.sh
53 env:
54 MAINLINE_PLAY_ANDROID_KEYSTORE_B64: ${{ secrets.PLAY_ANDROID_KEYSTORE_B64 }}
55 PLAY_ANDROID_KEY_ALIAS: ${{ secrets.PLAY_ANDROID_KEY_ALIAS }}
56 PLAY_ANDROID_KEYSTORE_PASS: ${{ secrets.PLAY_ANDROID_KEYSTORE_PASS }}
57 SERVICE_ACCOUNT_KEY_B64: ${{ secrets.MAINLINE_SERVICE_ACCOUNT_KEY_B64 }}
58 STORE_TRACK: ${{ github.event.inputs.release-track }}
59 AUTO_VERSIONED: true
diff --git a/.github/workflows/android-merge.js b/.github/workflows/android-merge.js
index 44ab56e44..315f81ba0 100644
--- a/.github/workflows/android-merge.js
+++ b/.github/workflows/android-merge.js
@@ -6,9 +6,12 @@
6 6
7const fs = require("fs"); 7const fs = require("fs");
8// which label to check for changes 8// which label to check for changes
9const CHANGE_LABEL = 'android-merge'; 9const CHANGE_LABEL_MAINLINE = 'android-merge';
10const CHANGE_LABEL_EA = 'android-ea-merge';
10// how far back in time should we consider the changes are "recent"? (default: 24 hours) 11// how far back in time should we consider the changes are "recent"? (default: 24 hours)
11const DETECTION_TIME_FRAME = (parseInt(process.env.DETECTION_TIME_FRAME)) || (24 * 3600 * 1000); 12const DETECTION_TIME_FRAME = (parseInt(process.env.DETECTION_TIME_FRAME)) || (24 * 3600 * 1000);
13const BUILD_EA = process.env.BUILD_EA == 'true';
14const MAINLINE_TAG = process.env.MAINLINE_TAG;
12 15
13async function checkBaseChanges(github) { 16async function checkBaseChanges(github) {
14 // query the commit date of the latest commit on this branch 17 // query the commit date of the latest commit on this branch
@@ -40,20 +43,7 @@ async function checkBaseChanges(github) {
40 43
41async function checkAndroidChanges(github) { 44async function checkAndroidChanges(github) {
42 if (checkBaseChanges(github)) return true; 45 if (checkBaseChanges(github)) return true;
43 const query = `query($owner:String!, $name:String!, $label:String!) { 46 const pulls = getPulls(github, false);
44 repository(name:$name, owner:$owner) {
45 pullRequests(labels: [$label], states: OPEN, first: 100) {
46 nodes { number headRepository { pushedAt } }
47 }
48 }
49 }`;
50 const variables = {
51 owner: 'yuzu-emu',
52 name: 'yuzu',
53 label: CHANGE_LABEL,
54 };
55 const result = await github.graphql(query, variables);
56 const pulls = result.repository.pullRequests.nodes;
57 for (let i = 0; i < pulls.length; i++) { 47 for (let i = 0; i < pulls.length; i++) {
58 let pull = pulls[i]; 48 let pull = pulls[i];
59 if (new Date() - new Date(pull.headRepository.pushedAt) <= DETECTION_TIME_FRAME) { 49 if (new Date() - new Date(pull.headRepository.pushedAt) <= DETECTION_TIME_FRAME) {
@@ -83,7 +73,13 @@ async function tagAndPush(github, owner, repo, execa, commit=false) {
83 }; 73 };
84 const tags = await github.graphql(query, variables); 74 const tags = await github.graphql(query, variables);
85 const tagList = tags.repository.refs.nodes; 75 const tagList = tags.repository.refs.nodes;
86 const lastTag = tagList[0] ? tagList[0].name : 'dummy-0'; 76 let lastTag = 'android-1';
77 for (let i = 0; i < tagList.length; ++i) {
78 if (tagList[i].name.includes('android-')) {
79 lastTag = tagList[i].name;
80 break;
81 }
82 }
87 const tagNumber = /\w+-(\d+)/.exec(lastTag)[1] | 0; 83 const tagNumber = /\w+-(\d+)/.exec(lastTag)[1] | 0;
88 const channel = repo.split('-')[1]; 84 const channel = repo.split('-')[1];
89 const newTag = `${channel}-${tagNumber + 1}`; 85 const newTag = `${channel}-${tagNumber + 1}`;
@@ -101,6 +97,48 @@ async function tagAndPush(github, owner, repo, execa, commit=false) {
101 console.info('Successfully pushed new changes.'); 97 console.info('Successfully pushed new changes.');
102} 98}
103 99
100async function tagAndPushEA(github, owner, repo, execa) {
101 let altToken = process.env.ALT_GITHUB_TOKEN;
102 if (!altToken) {
103 throw `Please set ALT_GITHUB_TOKEN environment variable. This token should have write access to ${owner}/${repo}.`;
104 }
105 const query = `query ($owner:String!, $name:String!) {
106 repository(name:$name, owner:$owner) {
107 refs(refPrefix: "refs/tags/", orderBy: {field: TAG_COMMIT_DATE, direction: DESC}, first: 10) {
108 nodes { name }
109 }
110 }
111 }`;
112 const variables = {
113 owner: owner,
114 name: repo,
115 };
116 const tags = await github.graphql(query, variables);
117 const tagList = tags.repository.refs.nodes;
118 let lastTag = 'ea-1';
119 for (let i = 0; i < tagList.length; ++i) {
120 if (tagList[i].name.includes('ea-')) {
121 lastTag = tagList[i].name;
122 break;
123 }
124 }
125 const tagNumber = /\w+-(\d+)/.exec(lastTag)[1] | 0;
126 const newTag = `ea-${tagNumber + 1}`;
127 console.log(`New tag: ${newTag}`);
128 console.info('Pushing tags to GitHub ...');
129 await execa("git", ["remote", "add", "android", "https://github.com/yuzu-emu/yuzu-android.git"]);
130 await execa("git", ["fetch", "android"]);
131
132 await execa("git", ['tag', newTag]);
133 await execa("git", ['push', 'android', `${newTag}`]);
134
135 fs.writeFile('tag-name.txt', newTag, (err) => {
136 if (err) throw 'Could not write tag name to file!'
137 })
138
139 console.info('Successfully pushed new changes.');
140}
141
104async function generateReadme(pulls, context, mergeResults, execa) { 142async function generateReadme(pulls, context, mergeResults, execa) {
105 let baseUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/`; 143 let baseUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/`;
106 let output = 144 let output =
@@ -202,10 +240,7 @@ async function resetBranch(execa) {
202 } 240 }
203} 241}
204 242
205async function mergebot(github, context, execa) { 243async function getPulls(github) {
206 // Reset our local copy of master to what appears on yuzu-emu/yuzu - master
207 await resetBranch(execa);
208
209 const query = `query ($owner:String!, $name:String!, $label:String!) { 244 const query = `query ($owner:String!, $name:String!, $label:String!) {
210 repository(name:$name, owner:$owner) { 245 repository(name:$name, owner:$owner) {
211 pullRequests(labels: [$label], states: OPEN, first: 100) { 246 pullRequests(labels: [$label], states: OPEN, first: 100) {
@@ -215,13 +250,49 @@ async function mergebot(github, context, execa) {
215 } 250 }
216 } 251 }
217 }`; 252 }`;
218 const variables = { 253 const mainlineVariables = {
219 owner: 'yuzu-emu', 254 owner: 'yuzu-emu',
220 name: 'yuzu', 255 name: 'yuzu',
221 label: CHANGE_LABEL, 256 label: CHANGE_LABEL_MAINLINE,
222 }; 257 };
223 const result = await github.graphql(query, variables); 258 const mainlineResult = await github.graphql(query, mainlineVariables);
224 const pulls = result.repository.pullRequests.nodes; 259 const pulls = mainlineResult.repository.pullRequests.nodes;
260 if (BUILD_EA) {
261 const eaVariables = {
262 owner: 'yuzu-emu',
263 name: 'yuzu',
264 label: CHANGE_LABEL_EA,
265 };
266 const eaResult = await github.graphql(query, eaVariables);
267 const eaPulls = eaResult.repository.pullRequests.nodes;
268 return pulls.concat(eaPulls);
269 }
270 return pulls;
271}
272
273async function getMainlineTag(execa) {
274 console.log(`::group::Getting mainline tag android-${MAINLINE_TAG}`);
275 let hasFailed = false;
276 try {
277 await execa("git", ["remote", "add", "mainline", "https://github.com/yuzu-emu/yuzu-android.git"]);
278 await execa("git", ["fetch", "mainline", "--tags"]);
279 await execa("git", ["checkout", `tags/android-${MAINLINE_TAG}`]);
280 await execa("git", ["submodule", "update", "--init", "--recursive"]);
281 } catch (err) {
282 console.log('::error title=Failed pull tag');
283 hasFailed = true;
284 }
285 console.log("::endgroup::");
286 if (hasFailed) {
287 throw 'Failed pull mainline tag. Aborting!';
288 }
289}
290
291async function mergebot(github, context, execa) {
292 // Reset our local copy of master to what appears on yuzu-emu/yuzu - master
293 await resetBranch(execa);
294
295 const pulls = await getPulls(github);
225 let displayList = []; 296 let displayList = [];
226 for (let i = 0; i < pulls.length; i++) { 297 for (let i = 0; i < pulls.length; i++) {
227 let pull = pulls[i]; 298 let pull = pulls[i];
@@ -231,11 +302,17 @@ async function mergebot(github, context, execa) {
231 console.table(displayList); 302 console.table(displayList);
232 await fetchPullRequests(pulls, "https://github.com/yuzu-emu/yuzu", execa); 303 await fetchPullRequests(pulls, "https://github.com/yuzu-emu/yuzu", execa);
233 const mergeResults = await mergePullRequests(pulls, execa); 304 const mergeResults = await mergePullRequests(pulls, execa);
234 await generateReadme(pulls, context, mergeResults, execa); 305
235 await tagAndPush(github, 'yuzu-emu', `yuzu-android`, execa, true); 306 if (BUILD_EA) {
307 await tagAndPushEA(github, 'yuzu-emu', `yuzu-android`, execa);
308 } else {
309 await generateReadme(pulls, context, mergeResults, execa);
310 await tagAndPush(github, 'yuzu-emu', `yuzu-android`, execa, true);
311 }
236} 312}
237 313
238module.exports.mergebot = mergebot; 314module.exports.mergebot = mergebot;
239module.exports.checkAndroidChanges = checkAndroidChanges; 315module.exports.checkAndroidChanges = checkAndroidChanges;
240module.exports.tagAndPush = tagAndPush; 316module.exports.tagAndPush = tagAndPush;
241module.exports.checkBaseChanges = checkBaseChanges; 317module.exports.checkBaseChanges = checkBaseChanges;
318module.exports.getMainlineTag = getMainlineTag;
diff --git a/.github/workflows/android-publish.yml b/.github/workflows/android-publish.yml
index 68e21c2f2..61f739e96 100644
--- a/.github/workflows/android-publish.yml
+++ b/.github/workflows/android-publish.yml
@@ -1,4 +1,4 @@
1# SPDX-FileCopyrightText: 2023 yuzu Emulator Project 1# SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2# SPDX-License-Identifier: GPL-2.0-or-later 2# SPDX-License-Identifier: GPL-2.0-or-later
3 3
4name: yuzu-android-publish 4name: yuzu-android-publish
@@ -16,7 +16,7 @@ on:
16jobs: 16jobs:
17 android: 17 android:
18 runs-on: ubuntu-latest 18 runs-on: ubuntu-latest
19 if: ${{ github.event.inputs.android != 'false' && github.repository == 'yuzu-emu/yuzu-android' }} 19 if: ${{ github.event.inputs.android != 'false' && github.repository == 'yuzu-emu/yuzu' }}
20 steps: 20 steps:
21 # this checkout is required to make sure the GitHub Actions scripts are available 21 # this checkout is required to make sure the GitHub Actions scripts are available
22 - uses: actions/checkout@v3 22 - uses: actions/checkout@v3
diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts
index 188ef9469..d44bb4c74 100644
--- a/src/android/app/build.gradle.kts
+++ b/src/android/app/build.gradle.kts
@@ -3,8 +3,8 @@
3 3
4import android.annotation.SuppressLint 4import android.annotation.SuppressLint
5import kotlin.collections.setOf 5import kotlin.collections.setOf
6import org.jetbrains.kotlin.konan.properties.Properties
7import org.jlleitschuh.gradle.ktlint.reporter.ReporterType 6import org.jlleitschuh.gradle.ktlint.reporter.ReporterType
7import com.github.triplet.gradle.androidpublisher.ReleaseStatus
8 8
9plugins { 9plugins {
10 id("com.android.application") 10 id("com.android.application")
@@ -13,6 +13,7 @@ plugins {
13 kotlin("plugin.serialization") version "1.9.20" 13 kotlin("plugin.serialization") version "1.9.20"
14 id("androidx.navigation.safeargs.kotlin") 14 id("androidx.navigation.safeargs.kotlin")
15 id("org.jlleitschuh.gradle.ktlint") version "11.4.0" 15 id("org.jlleitschuh.gradle.ktlint") version "11.4.0"
16 id("com.github.triplet.play") version "3.8.6"
16} 17}
17 18
18/** 19/**
@@ -58,15 +59,7 @@ android {
58 targetSdk = 34 59 targetSdk = 34
59 versionName = getGitVersion() 60 versionName = getGitVersion()
60 61
61 // If you want to use autoVersion for the versionCode, create a property in local.properties 62 versionCode = if (System.getenv("AUTO_VERSIONED") == "true") {
62 // named "autoVersioned" and set it to "true"
63 val properties = Properties()
64 val versionProperty = try {
65 properties.load(project.rootProject.file("local.properties").inputStream())
66 properties.getProperty("autoVersioned") ?: ""
67 } catch (e: Exception) { "" }
68
69 versionCode = if (versionProperty == "true") {
70 autoVersion 63 autoVersion
71 } else { 64 } else {
72 1 65 1
@@ -221,6 +214,15 @@ ktlint {
221 } 214 }
222} 215}
223 216
217play {
218 val keyPath = System.getenv("SERVICE_ACCOUNT_KEY_PATH")
219 if (keyPath != null) {
220 serviceAccountCredentials.set(File(keyPath))
221 }
222 track.set(System.getenv("STORE_TRACK") ?: "internal")
223 releaseStatus.set(ReleaseStatus.COMPLETED)
224}
225
224dependencies { 226dependencies {
225 implementation("androidx.core:core-ktx:1.12.0") 227 implementation("androidx.core:core-ktx:1.12.0")
226 implementation("androidx.appcompat:appcompat:1.6.1") 228 implementation("androidx.appcompat:appcompat:1.6.1")
@@ -257,14 +259,13 @@ fun runGitCommand(command: List<String>): String {
257} 259}
258 260
259fun getGitVersion(): String { 261fun getGitVersion(): String {
262 val gitVersion = runGitCommand(listOf("git", "describe", "--always", "--long"))
260 val versionName = if (System.getenv("GITHUB_ACTIONS") != null) { 263 val versionName = if (System.getenv("GITHUB_ACTIONS") != null) {
261 val gitTag = System.getenv("GIT_TAG_NAME") ?: "" 264 System.getenv("GIT_TAG_NAME") ?: gitVersion
262 gitTag
263 } else { 265 } else {
264 runGitCommand(listOf("git", "describe", "--always", "--long")) 266 gitVersion
265 .replace(Regex("(-0)?-[^-]+$"), "")
266 } 267 }
267 return versionName.ifEmpty { "0.0" } 268 return versionName.replace(Regex("(-0)?-[^-]+$"), "").ifEmpty { "0.0" }
268} 269}
269 270
270fun getGitHash(): String = 271fun getGitHash(): String =