forked from klausw/hackerskeyboard
-
Notifications
You must be signed in to change notification settings - Fork 0
199 lines (174 loc) · 7.71 KB
/
Copy pathrelease.yml
File metadata and controls
199 lines (174 loc) · 7.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# SPDX-License-Identifier: Apache-2.0
name: Signed v2 release
on:
workflow_dispatch:
permissions:
contents: read
concurrency:
group: signed-v2-release
cancel-in-progress: false
jobs:
build-release:
name: Build unsigned release
if: github.ref == 'refs/heads/v2'
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Check out v2
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
- name: Set up JDK 21
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961
with:
distribution: temurin
java-version-file: .java-version
- name: Set up Android SDK
uses: android-actions/setup-android@40fd30fb8d7440372e1316f5d1809ec01dcd3699
with:
packages: >-
platform-tools
platforms;android-37.0
build-tools;37.0.0
ndk;29.0.14206865
cmake;3.22.1
- name: Set up Gradle
uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb
- name: Build unsigned release APK
run: ./gradlew :app:assembleRelease --no-daemon --stacktrace
- name: Verify unsigned release APK
run: |
scripts/verify-apk-native-libs.sh app/build/outputs/apk/release/app-release-unsigned.apk
scripts/verify-apk-identity.sh app/build/outputs/apk/release/app-release-unsigned.apk
scripts/verify-apk-size.sh app/build/outputs/apk/release/app-release-unsigned.apk 3400000
- name: Upload unsigned release APK
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: unsigned-v2-release-${{ github.sha }}
path: app/build/outputs/apk/release/app-release-unsigned.apk
if-no-files-found: error
retention-days: 1
compression-level: 0
sign-release:
name: Sign and verify release
needs: build-release
runs-on: ubuntu-latest
timeout-minutes: 10
environment: release
steps:
- name: Set up JDK 21 for apksigner
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961
with:
distribution: temurin
java-version: '21'
- name: Set up Android build tools
uses: android-actions/setup-android@40fd30fb8d7440372e1316f5d1809ec01dcd3699
with:
packages: >-
platform-tools
build-tools;37.0.0
- name: Download unsigned release APK
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
name: unsigned-v2-release-${{ github.sha }}
path: unsigned
- name: Sign APK and verify release identity
id: sign
shell: bash
env:
KEYSTORE_BASE64: ${{ secrets.V2_RELEASE_KEYSTORE_BASE64 }}
KEY_ALIAS: ${{ secrets.V2_RELEASE_KEY_ALIAS }}
STORE_PASSWORD: ${{ secrets.V2_RELEASE_STORE_PASSWORD }}
STORE_TYPE: ${{ vars.V2_RELEASE_STORE_TYPE }}
EXPECTED_CERT_SHA256: ${{ vars.V2_RELEASE_CERT_SHA256 }}
run: |
set -euo pipefail
readonly unsigned_apk="unsigned/app-release-unsigned.apk"
readonly keystore="${RUNNER_TEMP}/hackers-keyboard-v2-release.p12"
readonly apksigner="${ANDROID_SDK_ROOT}/build-tools/37.0.0/apksigner"
readonly apkanalyzer="${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/apkanalyzer"
mkdir -p signed
trap 'rm -f "${keystore}"' EXIT
if [[ -z "${KEYSTORE_BASE64}" || -z "${KEY_ALIAS}" || -z "${STORE_PASSWORD}" ]]; then
echo "Required release signing secret is missing" >&2
exit 1
fi
application_id="$("${apkanalyzer}" manifest application-id "${unsigned_apk}" | tr -d '\r')"
version_code="$("${apkanalyzer}" manifest version-code "${unsigned_apk}" | tr -d '\r')"
version_name="$("${apkanalyzer}" manifest version-name "${unsigned_apk}" | tr -d '\r')"
debuggable="$("${apkanalyzer}" manifest debuggable "${unsigned_apk}" | tr -d '\r')"
if [[ "${application_id}" != "com.baodeep.hackerskeyboard" ]]; then
echo "Unexpected release application ID: ${application_id}" >&2
exit 1
fi
if [[ ! "${version_code}" =~ ^[0-9]+$ ]] || (( version_code <= 2000001 )); then
echo "Invalid v2 release versionCode: ${version_code}" >&2
exit 1
fi
if [[ ! "${version_name}" =~ ^2\.[0-9]+\.[0-9]+(-(alpha|beta|rc)[0-9]+)?$ ]]; then
echo "Invalid v2 release versionName: ${version_name}" >&2
exit 1
fi
if [[ "${debuggable}" != "false" ]]; then
echo "Release APK must not be debuggable" >&2
exit 1
fi
readonly signed_apk="signed/hackers-keyboard-v2-${version_name}.apk"
printf '%s' "${KEYSTORE_BASE64}" | base64 --decode > "${keystore}"
chmod 600 "${keystore}"
"${apksigner}" sign \
--ks "${keystore}" \
--ks-type "${STORE_TYPE:-PKCS12}" \
--ks-key-alias "${KEY_ALIAS}" \
--ks-pass env:STORE_PASSWORD \
--key-pass env:STORE_PASSWORD \
--out "${signed_apk}" \
"${unsigned_apk}"
"${apksigner}" verify --verbose --print-certs "${signed_apk}"
actual_cert="$("${apksigner}" verify --print-certs "${signed_apk}" \
| awk '/certificate SHA-256 digest:/ {print $NF; exit}' \
| tr -d '[:space:]:' \
| tr '[:lower:]' '[:upper:]')"
expected_cert="$(printf '%s' "${EXPECTED_CERT_SHA256}" \
| tr -d '[:space:]:' \
| tr '[:lower:]' '[:upper:]')"
if [[ ! "${expected_cert}" =~ ^[0-9A-F]{64}$ ]]; then
echo "V2_RELEASE_CERT_SHA256 is not a SHA-256 digest" >&2
exit 1
fi
if [[ ! "${actual_cert}" =~ ^[0-9A-F]{64}$ ]]; then
echo "Unable to parse the signed APK certificate SHA-256 digest" >&2
exit 1
fi
if [[ "${actual_cert}" != "${expected_cert}" ]]; then
echo "Signed APK certificate does not match V2_RELEASE_CERT_SHA256" >&2
exit 1
fi
signed_application_id="$("${apkanalyzer}" manifest application-id "${signed_apk}" | tr -d '\r')"
signed_version_code="$("${apkanalyzer}" manifest version-code "${signed_apk}" | tr -d '\r')"
signed_version_name="$("${apkanalyzer}" manifest version-name "${signed_apk}" | tr -d '\r')"
signed_debuggable="$("${apkanalyzer}" manifest debuggable "${signed_apk}" | tr -d '\r')"
[[ "${signed_application_id}" == "${application_id}" ]]
[[ "${signed_version_code}" == "${version_code}" ]]
[[ "${signed_version_name}" == "${version_name}" ]]
[[ "${signed_debuggable}" == "${debuggable}" ]]
apk_sha256="$(sha256sum "${signed_apk}" | awk '{print $1}')"
{
echo "commit=${GITHUB_SHA}"
echo "application_id=${application_id}"
echo "version_code=${version_code}"
echo "version_name=${version_name}"
echo "debuggable=${debuggable}"
echo "certificate_sha256=${actual_cert}"
echo "apk_sha256=${apk_sha256}"
} > signed/release-evidence.txt
{
echo "version_code=${version_code}"
echo "version_name=${version_name}"
} >> "${GITHUB_OUTPUT}"
- name: Upload signed release APK
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: hackers-keyboard-v2-${{ steps.sign.outputs.version_name }}-${{ github.sha }}
path: signed/
if-no-files-found: error
retention-days: 30
compression-level: 0