Skip to content

Commit 13528fa

Browse files
committed
ci: add release workflows
1 parent 7116e00 commit 13528fa

13 files changed

Lines changed: 712 additions & 10 deletions

.github/workflows/ci.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ jobs:
3535
uses: gradle/actions/setup-gradle@v5
3636

3737
- name: Decode google-services.json
38-
run: echo "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/google-services.json
38+
run: |
39+
mkdir -p app/src/debug
40+
echo "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/src/debug/google-services.json
3941
env:
4042
GOOGLE_SERVICES_JSON_BASE64: ${{ secrets.GOOGLE_SERVICES_JSON_BASE64 }}
4143

.github/workflows/e2e.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ jobs:
5757
uses: gradle/actions/setup-gradle@v5
5858

5959
- name: Decode google-services.json
60-
run: echo "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/google-services.json
60+
run: |
61+
mkdir -p app/src/debug
62+
echo "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/src/debug/google-services.json
6163
env:
6264
GOOGLE_SERVICES_JSON_BASE64: ${{ secrets.GOOGLE_SERVICES_JSON_BASE64 }}
6365

@@ -100,7 +102,9 @@ jobs:
100102
uses: gradle/actions/setup-gradle@v5
101103

102104
- name: Decode google-services.json
103-
run: echo "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/google-services.json
105+
run: |
106+
mkdir -p app/src/debug
107+
echo "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/src/debug/google-services.json
104108
env:
105109
GOOGLE_SERVICES_JSON_BASE64: ${{ secrets.GOOGLE_SERVICES_JSON_BASE64 }}
106110

.github/workflows/e2e_migration.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ jobs:
3737
uses: gradle/actions/setup-gradle@v5
3838

3939
- name: Decode google-services.json
40-
run: echo "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/google-services.json
40+
run: |
41+
mkdir -p app/src/debug
42+
echo "$GOOGLE_SERVICES_JSON_BASE64" | base64 -d > app/src/debug/google-services.json
4143
env:
4244
GOOGLE_SERVICES_JSON_BASE64: ${{ secrets.GOOGLE_SERVICES_JSON_BASE64 }}
4345

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
name: Release Internal
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
tags:
7+
- 'v*'
8+
9+
concurrency:
10+
group: ${{ github.workflow }}-${{ github.ref }}
11+
cancel-in-progress: false
12+
13+
env:
14+
TERM: xterm-256color
15+
FORCE_COLOR: 1
16+
17+
jobs:
18+
build-internal:
19+
if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/v')
20+
runs-on: ubuntu-latest
21+
timeout-minutes: 45
22+
environment: release-internal
23+
24+
permissions:
25+
contents: read
26+
packages: read
27+
28+
steps:
29+
- name: Checkout
30+
uses: actions/checkout@v6
31+
32+
- name: Setup Java
33+
uses: actions/setup-java@v5
34+
with:
35+
java-version: '17'
36+
distribution: 'adopt'
37+
38+
- name: Setup Gradle
39+
uses: gradle/actions/setup-gradle@v5
40+
41+
- name: Decode mainnet release google-services.json
42+
env:
43+
MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64: ${{ secrets.MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64 }}
44+
run: |
45+
set -euo pipefail
46+
test -n "$MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64"
47+
mkdir -p app/src/mainnetRelease
48+
printf '%s' "$MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64" | base64 --decode > app/src/mainnetRelease/google-services.json
49+
50+
- name: Decode internal keystore
51+
env:
52+
INTERNAL_KEYSTORE_BASE64: ${{ secrets.INTERNAL_KEYSTORE_BASE64 }}
53+
run: |
54+
set -euo pipefail
55+
test -n "$INTERNAL_KEYSTORE_BASE64"
56+
umask 077
57+
keystore_path="$RUNNER_TEMP/internal.keystore"
58+
printf '%s' "$INTERNAL_KEYSTORE_BASE64" | base64 --decode > "$keystore_path"
59+
echo "KEYSTORE_FILE=$keystore_path" >> "$GITHUB_ENV"
60+
61+
- name: Build internal release APK
62+
env:
63+
GPR_USER: ${{ secrets.GPR_USER || github.actor }}
64+
GPR_TOKEN: ${{ secrets.GPR_TOKEN || github.token }}
65+
GITHUB_TOKEN: ${{ secrets.GPR_TOKEN || github.token }}
66+
KEYSTORE_PASSWORD: ${{ secrets.INTERNAL_KEYSTORE_PASSWORD }}
67+
KEY_ALIAS: ${{ secrets.INTERNAL_KEY_ALIAS }}
68+
KEY_PASSWORD: ${{ secrets.INTERNAL_KEY_PASSWORD }}
69+
run: ./gradlew assembleMainnetRelease --no-daemon --stacktrace
70+
71+
- name: Verify internal release signature
72+
run: |
73+
set -euo pipefail
74+
android_sdk_root="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}"
75+
test -n "$android_sdk_root"
76+
apksigner_path="$(find "$android_sdk_root/build-tools" -name apksigner -type f | sort -V | tail -n 1)"
77+
test -n "$apksigner_path"
78+
79+
apk_count=0
80+
while IFS= read -r -d '' apk_path; do
81+
apk_count=$((apk_count + 1))
82+
"$apksigner_path" verify --verbose --print-certs "$apk_path"
83+
done < <(find app/build/outputs/apk/mainnet/release -name 'bitkit-mainnet-release-*.apk' -print0)
84+
test "$apk_count" -gt 0
85+
86+
- name: Collect internal artifacts
87+
id: artifacts
88+
run: |
89+
set -euo pipefail
90+
artifact_dir="$RUNNER_TEMP/internal-release"
91+
mkdir -p "$artifact_dir"
92+
find app/build/outputs/apk/mainnet/release -name 'bitkit-mainnet-release-*.apk' -print0 |
93+
xargs -0 -I {} cp {} "$artifact_dir/"
94+
(cd "$artifact_dir" && sha256sum *.apk > SHA256SUMS.txt)
95+
echo "artifact_dir=$artifact_dir" >> "$GITHUB_OUTPUT"
96+
97+
- name: Upload internal artifacts
98+
uses: actions/upload-artifact@v6
99+
with:
100+
name: bitkit-internal-release-${{ github.run_number }}
101+
path: ${{ steps.artifacts.outputs.artifact_dir }}
102+
retention-days: 30

.github/workflows/release.yml

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
name: Release
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
tags:
7+
- 'v*'
8+
9+
concurrency:
10+
group: ${{ github.workflow }}-${{ github.ref }}
11+
cancel-in-progress: false
12+
13+
env:
14+
TERM: xterm-256color
15+
FORCE_COLOR: 1
16+
17+
jobs:
18+
build-release:
19+
if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/v')
20+
runs-on: ubuntu-latest
21+
timeout-minutes: 45
22+
environment: release
23+
24+
permissions:
25+
contents: read
26+
packages: read
27+
28+
steps:
29+
- name: Checkout
30+
uses: actions/checkout@v6
31+
32+
- name: Setup Java
33+
uses: actions/setup-java@v5
34+
with:
35+
java-version: '17'
36+
distribution: 'adopt'
37+
38+
- name: Setup Gradle
39+
uses: gradle/actions/setup-gradle@v5
40+
41+
- name: Decode mainnet release google-services.json
42+
env:
43+
MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64: ${{ secrets.MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64 }}
44+
run: |
45+
set -euo pipefail
46+
test -n "$MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64"
47+
mkdir -p app/src/mainnetRelease
48+
printf '%s' "$MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64" | base64 --decode > app/src/mainnetRelease/google-services.json
49+
50+
- name: Decode release keystore
51+
env:
52+
BITKIT_KEYSTORE_BASE64: ${{ secrets.BITKIT_KEYSTORE_BASE64 }}
53+
run: |
54+
set -euo pipefail
55+
test -n "$BITKIT_KEYSTORE_BASE64"
56+
umask 077
57+
keystore_path="$RUNNER_TEMP/bitkit.keystore"
58+
printf '%s' "$BITKIT_KEYSTORE_BASE64" | base64 --decode > "$keystore_path"
59+
echo "KEYSTORE_FILE=$keystore_path" >> "$GITHUB_ENV"
60+
61+
- name: Build release artifacts
62+
env:
63+
GPR_USER: ${{ secrets.GPR_USER || github.actor }}
64+
GPR_TOKEN: ${{ secrets.GPR_TOKEN || github.token }}
65+
GITHUB_TOKEN: ${{ secrets.GPR_TOKEN || github.token }}
66+
KEYSTORE_PASSWORD: ${{ secrets.BITKIT_KEYSTORE_PASSWORD }}
67+
KEY_ALIAS: ${{ secrets.BITKIT_KEY_ALIAS }}
68+
KEY_PASSWORD: ${{ secrets.BITKIT_KEY_PASSWORD }}
69+
run: ./gradlew assembleMainnetRelease bundleMainnetRelease --no-daemon --stacktrace
70+
71+
- name: Verify release signatures
72+
run: |
73+
set -euo pipefail
74+
android_sdk_root="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}"
75+
test -n "$android_sdk_root"
76+
apksigner_path="$(find "$android_sdk_root/build-tools" -name apksigner -type f | sort -V | tail -n 1)"
77+
test -n "$apksigner_path"
78+
79+
apk_count=0
80+
while IFS= read -r -d '' apk_path; do
81+
apk_count=$((apk_count + 1))
82+
"$apksigner_path" verify --verbose --print-certs "$apk_path"
83+
done < <(find app/build/outputs/apk/mainnet/release -name 'bitkit-mainnet-release-*.apk' -print0)
84+
test "$apk_count" -gt 0
85+
86+
bundle_count=0
87+
while IFS= read -r -d '' bundle_path; do
88+
bundle_count=$((bundle_count + 1))
89+
verify_output="$(mktemp)"
90+
if ! jarsigner -verify -verbose -certs "$bundle_path" 2>&1 | tee "$verify_output"; then
91+
rm -f "$verify_output"
92+
exit 1
93+
fi
94+
if grep -qi "jar is unsigned" "$verify_output"; then
95+
echo "Unsigned bundle: $bundle_path"
96+
rm -f "$verify_output"
97+
exit 1
98+
fi
99+
if ! grep -qi "jar verified" "$verify_output"; then
100+
echo "Bundle signature verification did not report success: $bundle_path"
101+
rm -f "$verify_output"
102+
exit 1
103+
fi
104+
rm -f "$verify_output"
105+
done < <(find app/build/outputs/bundle/mainnetRelease -name 'bitkit-mainnet-release-*.aab' -print0)
106+
test "$bundle_count" -gt 0
107+
108+
- name: Collect release artifacts
109+
id: artifacts
110+
run: |
111+
set -euo pipefail
112+
artifact_dir="$RUNNER_TEMP/release"
113+
mkdir -p "$artifact_dir"
114+
find app/build/outputs/bundle/mainnetRelease -name 'bitkit-mainnet-release-*.aab' -print0 |
115+
xargs -0 -I {} cp {} "$artifact_dir/"
116+
find app/build/outputs/apk/mainnet/release -name 'bitkit-mainnet-release-*.apk' -print0 |
117+
xargs -0 -I {} cp {} "$artifact_dir/"
118+
(cd "$artifact_dir" && sha256sum *.aab *.apk > SHA256SUMS.txt)
119+
echo "artifact_dir=$artifact_dir" >> "$GITHUB_OUTPUT"
120+
121+
- name: Upload release artifacts
122+
uses: actions/upload-artifact@v6
123+
with:
124+
name: bitkit-release-${{ github.run_number }}
125+
path: ${{ steps.artifacts.outputs.artifact_dir }}
126+
retention-days: 30
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
name: Reproducible Release
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
comparison_artifact_name:
7+
description: Optional artifact name to compare against with diffoscope
8+
required: false
9+
default: ''
10+
comparison_run_id:
11+
description: Workflow run id that produced the comparison artifact
12+
required: false
13+
default: ''
14+
push:
15+
tags:
16+
- 'v*'
17+
18+
concurrency:
19+
group: ${{ github.workflow }}-${{ github.ref }}
20+
cancel-in-progress: false
21+
22+
env:
23+
TERM: xterm-256color
24+
FORCE_COLOR: 1
25+
26+
jobs:
27+
reproduce-mainnet:
28+
if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/v')
29+
runs-on: ubuntu-latest
30+
timeout-minutes: 60
31+
environment: release
32+
33+
permissions:
34+
actions: read
35+
contents: read
36+
packages: read
37+
38+
steps:
39+
- name: Checkout
40+
uses: actions/checkout@v6
41+
42+
- name: Setup Java
43+
uses: actions/setup-java@v5
44+
with:
45+
java-version: '17'
46+
distribution: 'adopt'
47+
48+
- name: Setup Gradle
49+
uses: gradle/actions/setup-gradle@v5
50+
51+
- name: Decode mainnet release google-services.json
52+
env:
53+
MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64: ${{ secrets.MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64 }}
54+
run: |
55+
set -euo pipefail
56+
test -n "$MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64"
57+
mkdir -p app/src/mainnetRelease
58+
printf '%s' "$MAINNET_RELEASE_GOOGLE_SERVICES_JSON_BASE64" | base64 --decode > app/src/mainnetRelease/google-services.json
59+
60+
- name: Decode release keystore
61+
env:
62+
BITKIT_KEYSTORE_BASE64: ${{ secrets.BITKIT_KEYSTORE_BASE64 }}
63+
run: |
64+
set -euo pipefail
65+
test -n "$BITKIT_KEYSTORE_BASE64"
66+
umask 077
67+
keystore_path="$RUNNER_TEMP/bitkit.keystore"
68+
printf '%s' "$BITKIT_KEYSTORE_BASE64" | base64 --decode > "$keystore_path"
69+
echo "KEYSTORE_FILE=$keystore_path" >> "$GITHUB_ENV"
70+
71+
- name: Validate comparison inputs
72+
if: ${{ github.event_name == 'workflow_dispatch' && inputs.comparison_artifact_name != '' }}
73+
env:
74+
COMPARISON_RUN_ID: ${{ inputs.comparison_run_id }}
75+
run: |
76+
set -euo pipefail
77+
test -n "$COMPARISON_RUN_ID"
78+
79+
- name: Download comparison artifact
80+
if: ${{ github.event_name == 'workflow_dispatch' && inputs.comparison_artifact_name != '' }}
81+
uses: actions/download-artifact@v6
82+
with:
83+
name: ${{ inputs.comparison_artifact_name }}
84+
path: ${{ runner.temp }}/comparison
85+
github-token: ${{ github.token }}
86+
repository: ${{ github.repository }}
87+
run-id: ${{ inputs.comparison_run_id }}
88+
89+
- name: Install diffoscope
90+
if: ${{ github.event_name == 'workflow_dispatch' && inputs.comparison_artifact_name != '' }}
91+
run: |
92+
set -euo pipefail
93+
sudo apt-get update
94+
sudo apt-get install -y diffoscope
95+
96+
- name: Build reproducibility artifacts
97+
env:
98+
GPR_USER: ${{ secrets.GPR_USER || github.actor }}
99+
GPR_TOKEN: ${{ secrets.GPR_TOKEN || github.token }}
100+
GITHUB_TOKEN: ${{ secrets.GPR_TOKEN || github.token }}
101+
KEYSTORE_PASSWORD: ${{ secrets.BITKIT_KEYSTORE_PASSWORD }}
102+
KEY_ALIAS: ${{ secrets.BITKIT_KEY_ALIAS }}
103+
KEY_PASSWORD: ${{ secrets.BITKIT_KEY_PASSWORD }}
104+
OUTPUT_DIR: ${{ runner.temp }}/reproducible-release
105+
run: |
106+
set -euo pipefail
107+
if [ -d "$RUNNER_TEMP/comparison/extracted-apks" ]; then
108+
export DIFFOSCOPE_COMPARE_DIR="$RUNNER_TEMP/comparison/extracted-apks"
109+
elif [ -d "$RUNNER_TEMP/comparison" ]; then
110+
export DIFFOSCOPE_COMPARE_DIR="$RUNNER_TEMP/comparison"
111+
fi
112+
scripts/reproduce-release.sh
113+
114+
- name: Upload reproducibility artifacts
115+
uses: actions/upload-artifact@v6
116+
with:
117+
name: bitkit-reproducible-release-${{ github.run_number }}
118+
path: ${{ runner.temp }}/reproducible-release
119+
retention-days: 30

0 commit comments

Comments
 (0)