summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/android-build.yml79
-rw-r--r--.github/workflows/android-merge.js218
-rw-r--r--.github/workflows/android-publish.yml57
-rw-r--r--.github/workflows/verify.yml4
-rw-r--r--src/yuzu/main.cpp2
5 files changed, 357 insertions, 3 deletions
diff --git a/.github/workflows/android-build.yml b/.github/workflows/android-build.yml
new file mode 100644
index 000000000..e639e965a
--- /dev/null
+++ b/.github/workflows/android-build.yml
@@ -0,0 +1,79 @@
1# SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2# SPDX-License-Identifier: GPL-3.0-or-later
3
4name: 'yuzu-android-build'
5
6on:
7 push:
8 tags: [ "*" ]
9
10jobs:
11 android:
12 runs-on: ubuntu-latest
13 steps:
14 - uses: actions/checkout@v3
15 with:
16 submodules: recursive
17 fetch-depth: 0
18 - name: Set up JDK 17
19 uses: actions/setup-java@v3
20 with:
21 java-version: '17'
22 distribution: 'temurin'
23 - name: Set up cache
24 uses: actions/cache@v3
25 with:
26 path: |
27 ~/.gradle/caches
28 ~/.gradle/wrapper
29 ~/.ccache
30 key: ${{ runner.os }}-android-${{ github.sha }}
31 restore-keys: |
32 ${{ runner.os }}-android-
33 - name: Query tag name
34 uses: olegtarasov/get-tag@v2.1.2
35 id: tagName
36 - name: Install dependencies
37 run: |
38 sudo apt-get update
39 sudo apt-get install -y ccache apksigner glslang-dev glslang-tools
40 - name: Build
41 run: ./.ci/scripts/android/build.sh
42 - name: Copy and sign artifacts
43 env:
44 ANDROID_KEYSTORE_B64: ${{ secrets.ANDROID_KEYSTORE_B64 }}
45 ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
46 ANDROID_KEYSTORE_PASS: ${{ secrets.ANDROID_KEYSTORE_PASS }}
47 run: ./.ci/scripts/android/upload.sh
48 - name: Upload
49 uses: actions/upload-artifact@v3
50 with:
51 name: android
52 path: artifacts/
53 # release steps
54 release-android:
55 runs-on: ubuntu-latest
56 needs: [android]
57 if: ${{ startsWith(github.ref, 'refs/tags/') }}
58 permissions:
59 contents: write
60 steps:
61 - uses: actions/download-artifact@v3
62 - name: Query tag name
63 uses: olegtarasov/get-tag@v2.1.2
64 id: tagName
65 - name: Create release
66 uses: actions/create-release@v1
67 env:
68 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
69 with:
70 tag_name: ${{ steps.tagName.outputs.tag }}
71 release_name: ${{ steps.tagName.outputs.tag }}
72 draft: false
73 prerelease: false
74 - name: Upload artifacts
75 uses: alexellis/upload-assets@0.2.3
76 env:
77 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
78 with:
79 asset_paths: '["./**/*.apk","./**/*.aab"]'
diff --git a/.github/workflows/android-merge.js b/.github/workflows/android-merge.js
new file mode 100644
index 000000000..7e02dc9e5
--- /dev/null
+++ b/.github/workflows/android-merge.js
@@ -0,0 +1,218 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// Note: This is a GitHub Actions script
5// It is not meant to be executed directly on your machine without modifications
6
7const fs = require("fs");
8// which label to check for changes
9const CHANGE_LABEL = 'android-merge';
10// 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);
12
13async function checkBaseChanges(github, context) {
14 // query the commit date of the latest commit on this branch
15 const query = `query($owner:String!, $name:String!, $ref:String!) {
16 repository(name:$name, owner:$owner) {
17 ref(qualifiedName:$ref) {
18 target {
19 ... on Commit { id pushedDate oid }
20 }
21 }
22 }
23 }`;
24 const variables = {
25 owner: context.repo.owner,
26 name: context.repo.repo,
27 ref: 'refs/heads/master',
28 };
29 const result = await github.graphql(query, variables);
30 const pushedAt = result.repository.ref.target.pushedDate;
31 console.log(`Last commit pushed at ${pushedAt}.`);
32 const delta = new Date() - new Date(pushedAt);
33 if (delta <= DETECTION_TIME_FRAME) {
34 console.info('New changes detected, triggering a new build.');
35 return true;
36 }
37 console.info('No new changes detected.');
38 return false;
39}
40
41async function checkAndroidChanges(github, context) {
42 if (checkBaseChanges(github, context)) return true;
43 const query = `query($owner:String!, $name:String!, $label:String!) {
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: context.repo.owner,
52 name: context.repo.repo,
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++) {
58 let pull = pulls[i];
59 if (new Date() - new Date(pull.headRepository.pushedAt) <= DETECTION_TIME_FRAME) {
60 console.info(`${pull.number} updated at ${pull.headRepository.pushedAt}`);
61 return true;
62 }
63 }
64 console.info("No changes detected in any tagged pull requests.");
65 return false;
66}
67
68async function tagAndPush(github, owner, repo, execa, commit=false) {
69 let altToken = process.env.ALT_GITHUB_TOKEN;
70 if (!altToken) {
71 throw `Please set ALT_GITHUB_TOKEN environment variable. This token should have write access to ${owner}/${repo}.`;
72 }
73 const query = `query ($owner:String!, $name:String!) {
74 repository(name:$name, owner:$owner) {
75 refs(refPrefix: "refs/tags/", orderBy: {field: TAG_COMMIT_DATE, direction: DESC}, first: 10) {
76 nodes { name }
77 }
78 }
79 }`;
80 const variables = {
81 owner: owner,
82 name: repo,
83 };
84 const tags = await github.graphql(query, variables);
85 const tagList = tags.repository.refs.nodes;
86 const lastTag = tagList[0] ? tagList[0].name : 'dummy-0';
87 const tagNumber = /\w+-(\d+)/.exec(lastTag)[1] | 0;
88 const channel = repo.split('-')[1];
89 const newTag = `${channel}-${tagNumber + 1}`;
90 console.log(`New tag: ${newTag}`);
91 if (commit) {
92 let channelName = channel[0].toUpperCase() + channel.slice(1);
93 console.info(`Committing pending commit as ${channelName} #${tagNumber + 1}`);
94 await execa("git", ['commit', '-m', `${channelName} #${tagNumber + 1}`]);
95 }
96 console.info('Pushing tags to GitHub ...');
97 await execa("git", ['tag', newTag]);
98 await execa("git", ['remote', 'add', 'target', `https://${altToken}@github.com/${owner}/${repo}.git`]);
99 await execa("git", ['push', 'target', 'master', '-f']);
100 await execa("git", ['push', 'target', 'master', '--tags']);
101 console.info('Successfully pushed new changes.');
102}
103
104async function generateReadme(pulls, context, mergeResults, execa) {
105 let baseUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/`;
106 let output =
107 "| Pull Request | Commit | Title | Author | Merged? |\n|----|----|----|----|----|\n";
108 for (let pull of pulls) {
109 let pr = pull.number;
110 let result = mergeResults[pr];
111 output += `| [${pr}](${baseUrl}/pull/${pr}) | [\`${result.rev || "N/A"}\`](${baseUrl}/pull/${pr}/files) | ${pull.title} | [${pull.author.login}](https://github.com/${pull.author.login}/) | ${result.success ? "Yes" : "No"} |\n`;
112 }
113 output +=
114 "\n\nEnd of merge log. You can find the original README.md below the break.\n\n-----\n\n";
115 output += fs.readFileSync("./README.md");
116 fs.writeFileSync("./README.md", output);
117 await execa("git", ["add", "README.md"]);
118}
119
120async function fetchPullRequests(pulls, repoUrl, execa) {
121 console.log("::group::Fetch pull requests");
122 for (let pull of pulls) {
123 let pr = pull.number;
124 console.info(`Fetching PR ${pr} ...`);
125 await execa("git", [
126 "fetch",
127 "-f",
128 "--no-recurse-submodules",
129 repoUrl,
130 `pull/${pr}/head:pr-${pr}`,
131 ]);
132 }
133 console.log("::endgroup::");
134}
135
136async function mergePullRequests(pulls, execa) {
137 let mergeResults = {};
138 console.log("::group::Merge pull requests");
139 await execa("git", ["config", "--global", "user.name", "yuzubot"]);
140 await execa("git", [
141 "config",
142 "--global",
143 "user.email",
144 "yuzu\x40yuzu-emu\x2eorg", // prevent email harvesters from scraping the address
145 ]);
146 let hasFailed = false;
147 for (let pull of pulls) {
148 let pr = pull.number;
149 console.info(`Merging PR ${pr} ...`);
150 try {
151 const process1 = execa("git", [
152 "merge",
153 "--squash",
154 "--no-edit",
155 `pr-${pr}`,
156 ]);
157 process1.stdout.pipe(process.stdout);
158 await process1;
159
160 const process2 = execa("git", ["commit", "-m", `Merge PR ${pr}`]);
161 process2.stdout.pipe(process.stdout);
162 await process2;
163
164 const process3 = await execa("git", ["rev-parse", "--short", `pr-${pr}`]);
165 mergeResults[pr] = {
166 success: true,
167 rev: process3.stdout,
168 };
169 } catch (err) {
170 console.log(
171 `::error title=#${pr} not merged::Failed to merge pull request: ${pr}: ${err}`
172 );
173 mergeResults[pr] = { success: false };
174 hasFailed = true;
175 await execa("git", ["reset", "--hard"]);
176 }
177 }
178 console.log("::endgroup::");
179 if (hasFailed) {
180 throw 'There are merge failures. Aborting!';
181 }
182 return mergeResults;
183}
184
185async function mergebot(github, context, execa) {
186 const query = `query ($owner:String!, $name:String!, $label:String!) {
187 repository(name:$name, owner:$owner) {
188 pullRequests(labels: [$label], states: OPEN, first: 100) {
189 nodes {
190 number title author { login }
191 }
192 }
193 }
194 }`;
195 const variables = {
196 owner: context.repo.owner,
197 name: context.repo.repo,
198 label: CHANGE_LABEL,
199 };
200 const result = await github.graphql(query, variables);
201 const pulls = result.repository.pullRequests.nodes;
202 let displayList = [];
203 for (let i = 0; i < pulls.length; i++) {
204 let pull = pulls[i];
205 displayList.push({ PR: pull.number, Title: pull.title });
206 }
207 console.info("The following pull requests will be merged:");
208 console.table(displayList);
209 await fetchPullRequests(pulls, "https://github.com/yuzu-emu/yuzu", execa);
210 const mergeResults = await mergePullRequests(pulls, execa);
211 await generateReadme(pulls, context, mergeResults, execa);
212 await tagAndPush(github, context.repo.owner, `${context.repo.repo}-android`, execa, true);
213}
214
215module.exports.mergebot = mergebot;
216module.exports.checkAndroidChanges = checkAndroidChanges;
217module.exports.tagAndPush = tagAndPush;
218module.exports.checkBaseChanges = checkBaseChanges;
diff --git a/.github/workflows/android-publish.yml b/.github/workflows/android-publish.yml
new file mode 100644
index 000000000..8f46fcf74
--- /dev/null
+++ b/.github/workflows/android-publish.yml
@@ -0,0 +1,57 @@
1# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2# SPDX-License-Identifier: GPL-2.0-or-later
3
4name: yuzu-android-publish
5
6on:
7 schedule:
8 - cron: '37 0 * * *'
9 workflow_dispatch:
10 inputs:
11 android:
12 description: 'Whether to trigger an Android build (true/false/auto)'
13 required: false
14 default: 'true'
15
16jobs:
17 android:
18 runs-on: ubuntu-latest
19 if: ${{ github.event.inputs.android != 'false' && github.repository == 'yuzu-emu/yuzu' }}
20 steps:
21 # this checkout is required to make sure the GitHub Actions scripts are available
22 - uses: actions/checkout@v3
23 name: Pre-checkout
24 with:
25 submodules: false
26 - uses: actions/github-script@v6
27 id: check-changes
28 name: 'Check for new changes'
29 env:
30 # 24 hours
31 DETECTION_TIME_FRAME: 86400000
32 with:
33 script: |
34 if (context.payload.inputs && context.payload.inputs.android === 'true') return true;
35 const checkAndroidChanges = require('./.github/workflows/android-merge.js').checkAndroidChanges;
36 return checkAndroidChanges(github, context);
37 - run: npm install execa@5
38 if: ${{ steps.check-changes.outputs.result == 'true' }}
39 - uses: actions/checkout@v3
40 name: Checkout
41 if: ${{ steps.check-changes.outputs.result == 'true' }}
42 with:
43 path: 'yuzu-merge'
44 fetch-depth: 0
45 submodules: true
46 token: ${{ secrets.ALT_GITHUB_TOKEN }}
47 - uses: actions/github-script@v5
48 name: 'Check and merge Android changes'
49 if: ${{ steps.check-changes.outputs.result == 'true' }}
50 env:
51 ALT_GITHUB_TOKEN: ${{ secrets.ALT_GITHUB_TOKEN }}
52 with:
53 script: |
54 const execa = require("execa");
55 const mergebot = require('./.github/workflows/android-merge.js').mergebot;
56 process.chdir('${{ github.workspace }}/yuzu-merge');
57 mergebot(github, context, execa);
diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml
index bd4141f56..b5d338199 100644
--- a/.github/workflows/verify.yml
+++ b/.github/workflows/verify.yml
@@ -129,11 +129,12 @@ jobs:
129 - uses: actions/checkout@v3 129 - uses: actions/checkout@v3
130 with: 130 with:
131 submodules: recursive 131 submodules: recursive
132 fetch-depth: 0
132 - name: set up JDK 17 133 - name: set up JDK 17
133 uses: actions/setup-java@v3 134 uses: actions/setup-java@v3
134 with: 135 with:
135 java-version: '17' 136 java-version: '17'
136 distribution: 'adopt' 137 distribution: 'temurin'
137 - name: Set up cache 138 - name: Set up cache
138 uses: actions/cache@v3 139 uses: actions/cache@v3
139 with: 140 with:
@@ -151,7 +152,6 @@ jobs:
151 run: | 152 run: |
152 sudo apt-get update 153 sudo apt-get update
153 sudo apt-get install -y ccache apksigner glslang-dev glslang-tools 154 sudo apt-get install -y ccache apksigner glslang-dev glslang-tools
154 git -C ./externals/vcpkg/ fetch --all --unshallow
155 - name: Build 155 - name: Build
156 run: ./.ci/scripts/android/build.sh 156 run: ./.ci/scripts/android/build.sh
157 - name: Copy and sign artifacts 157 - name: Copy and sign artifacts
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index fea5eb614..20532416c 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -454,7 +454,7 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan
454 // the user through their desktop environment. 454 // the user through their desktop environment.
455 //: TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the 455 //: TRANSLATORS: This string is shown to the user to explain why yuzu needs to prevent the
456 //: computer from sleeping 456 //: computer from sleeping
457 QByteArray wakelock_reason = tr("Running a game").toLatin1(); 457 QByteArray wakelock_reason = tr("Running a game").toUtf8();
458 SDL_SetHint(SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME, wakelock_reason.data()); 458 SDL_SetHint(SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME, wakelock_reason.data());
459 459
460 // SDL disables the screen saver by default, and setting the hint 460 // SDL disables the screen saver by default, and setting the hint