Skip to content

Commit df59a6d

Browse files
committed
chore: github action release
1 parent 9001837 commit df59a6d

1 file changed

Lines changed: 361 additions & 0 deletions

File tree

.github/workflows/release.yml

Lines changed: 361 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,361 @@
1+
name: Create GitHub Release
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
branches:
7+
- main
8+
9+
permissions:
10+
contents: write
11+
12+
concurrency:
13+
group: github-release-${{ github.ref }}
14+
cancel-in-progress: false
15+
16+
jobs:
17+
release:
18+
name: Build APKs And Create Release
19+
runs-on: ubuntu-latest
20+
timeout-minutes: 60
21+
env:
22+
RELEASE_KEYSTORE_BASE64: ${{ secrets.RELEASE_KEYSTORE_BASE64 }}
23+
RELEASE_KEYSTORE_PASSWORD: ${{ secrets.RELEASE_KEYSTORE_PASSWORD }}
24+
RELEASE_KEY_ALIAS: ${{ secrets.RELEASE_KEY_ALIAS }}
25+
RELEASE_KEY_PASSWORD: ${{ secrets.RELEASE_KEY_PASSWORD }}
26+
27+
steps:
28+
- name: Checkout
29+
uses: actions/checkout@v4
30+
with:
31+
fetch-depth: 0
32+
33+
- name: Read ProjectConfig version
34+
id: version
35+
shell: bash
36+
run: |
37+
set -euo pipefail
38+
39+
version_name="$(sed -n 's/^[[:space:]]*const val VERSION_NAME = "\([^"]*\)".*/\1/p' buildSrc/src/main/kotlin/BuildConfig.kt | head -n 1)"
40+
if [ -z "$version_name" ]; then
41+
echo "::error::Unable to read ProjectConfig.VERSION_NAME from buildSrc/src/main/kotlin/BuildConfig.kt."
42+
exit 1
43+
fi
44+
45+
echo "version_name=$version_name" >> "$GITHUB_OUTPUT"
46+
echo "tag_name=v$version_name" >> "$GITHUB_OUTPUT"
47+
48+
if [[ "$version_name" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
49+
echo "is_release=true" >> "$GITHUB_OUTPUT"
50+
echo "Detected formal release version: $version_name"
51+
else
52+
echo "is_release=false" >> "$GITHUB_OUTPUT"
53+
echo "::notice::ProjectConfig.VERSION_NAME is '$version_name', not strict x.y.z. Skipping tag and release creation."
54+
fi
55+
56+
- name: Skip non-release version
57+
if: steps.version.outputs.is_release != 'true'
58+
run: echo "No GitHub Release will be created for version '${{ steps.version.outputs.version_name }}'."
59+
60+
- name: Make Gradle wrapper executable
61+
if: steps.version.outputs.is_release == 'true'
62+
run: chmod +x ./gradlew
63+
64+
- name: Set up JDK
65+
if: steps.version.outputs.is_release == 'true'
66+
uses: actions/setup-java@v4
67+
with:
68+
distribution: temurin
69+
java-version: "25"
70+
71+
- name: Set up Gradle
72+
if: steps.version.outputs.is_release == 'true'
73+
uses: gradle/actions/setup-gradle@v4
74+
75+
- name: Set up Android SDK
76+
if: steps.version.outputs.is_release == 'true'
77+
uses: android-actions/setup-android@v3
78+
79+
- name: Install Android SDK packages
80+
if: steps.version.outputs.is_release == 'true'
81+
shell: bash
82+
run: |
83+
set -euo pipefail
84+
85+
set +o pipefail
86+
yes | sdkmanager --licenses >/dev/null
87+
licenses_status="${PIPESTATUS[1]}"
88+
set -o pipefail
89+
if [ "$licenses_status" -ne 0 ]; then
90+
echo "::error::Failed to accept Android SDK licenses."
91+
exit "$licenses_status"
92+
fi
93+
94+
target_sdk="$(sed -n 's/^[[:space:]]*const val TARGET_SDK = \([0-9]\+\).*/\1/p' buildSrc/src/main/kotlin/BuildConfig.kt | head -n 1)"
95+
if [ -z "$target_sdk" ]; then
96+
echo "::error::Unable to read ProjectConfig.TARGET_SDK from buildSrc/src/main/kotlin/BuildConfig.kt."
97+
exit 1
98+
fi
99+
100+
available_packages="$(sdkmanager --list | awk '{ print $1 }')"
101+
platform_package="platforms;android-${target_sdk}"
102+
if ! grep -Fxq "$platform_package" <<< "$available_packages"; then
103+
if grep -Fxq "platforms;android-${target_sdk}.0" <<< "$available_packages"; then
104+
platform_package="platforms;android-${target_sdk}.0"
105+
else
106+
echo "::error::No Android SDK platform package is available for TARGET_SDK=$target_sdk."
107+
exit 1
108+
fi
109+
fi
110+
111+
build_tools_package="$(printf '%s\n' "$available_packages" | awk '$1 ~ /^build-tools;[0-9]/ { print $1 }' | sort -V | tail -n 1)"
112+
ndk_package="$(printf '%s\n' "$available_packages" | awk '$1 ~ /^ndk;[0-9]/ { print $1 }' | sort -V | tail -n 1)"
113+
114+
if [ -z "$build_tools_package" ]; then
115+
echo "::error::No Android build-tools package is available from sdkmanager."
116+
exit 1
117+
fi
118+
if [ -z "$ndk_package" ]; then
119+
echo "::error::No Android NDK package is available from sdkmanager."
120+
exit 1
121+
fi
122+
123+
sdkmanager --install "$platform_package" "$build_tools_package" "$ndk_package"
124+
125+
ndk_version="${ndk_package#ndk;}"
126+
echo "ANDROID_NDK_HOME=$ANDROID_HOME/ndk/$ndk_version" >> "$GITHUB_ENV"
127+
echo "ANDROID_NDK_ROOT=$ANDROID_HOME/ndk/$ndk_version" >> "$GITHUB_ENV"
128+
129+
- name: Validate signing secrets
130+
if: steps.version.outputs.is_release == 'true'
131+
shell: bash
132+
run: |
133+
set -euo pipefail
134+
if [ -z "${RELEASE_KEYSTORE_BASE64:-}" ]; then
135+
echo "::error::Missing repository secret RELEASE_KEYSTORE_BASE64."
136+
exit 1
137+
fi
138+
if [ -z "${RELEASE_KEYSTORE_PASSWORD:-}" ]; then
139+
echo "::error::Missing repository secret RELEASE_KEYSTORE_PASSWORD."
140+
exit 1
141+
fi
142+
if [ -z "${RELEASE_KEY_ALIAS:-}" ]; then
143+
echo "::error::Missing repository secret RELEASE_KEY_ALIAS."
144+
exit 1
145+
fi
146+
147+
- name: Build release APKs
148+
if: steps.version.outputs.is_release == 'true'
149+
run: ./gradlew --no-daemon :app:assembleRelease
150+
151+
- name: Sign release APKs
152+
if: steps.version.outputs.is_release == 'true'
153+
shell: bash
154+
run: |
155+
set -euo pipefail
156+
157+
keystore_path="$RUNNER_TEMP/release.keystore"
158+
printf '%s' "$RELEASE_KEYSTORE_BASE64" | base64 --decode > "$keystore_path"
159+
160+
key_password="${RELEASE_KEY_PASSWORD:-$RELEASE_KEYSTORE_PASSWORD}"
161+
export RELEASE_KEYSTORE_PASSWORD
162+
export RELEASE_KEY_PASSWORD="$key_password"
163+
164+
zipalign="$(find "$ANDROID_HOME/build-tools" -type f -name zipalign | sort -V | tail -n 1)"
165+
apksigner="$(find "$ANDROID_HOME/build-tools" -type f -name apksigner | sort -V | tail -n 1)"
166+
167+
if [ -z "$zipalign" ]; then
168+
echo "::error::zipalign was not found in Android build-tools."
169+
exit 1
170+
fi
171+
if [ -z "$apksigner" ]; then
172+
echo "::error::apksigner was not found in Android build-tools."
173+
exit 1
174+
fi
175+
176+
mkdir -p signed-apks
177+
shopt -s nullglob
178+
for apk in app/build/outputs/apk/release/*.apk; do
179+
base_name="$(basename "$apk" .apk)"
180+
aligned_apk="$RUNNER_TEMP/${base_name}-aligned.apk"
181+
signed_apk="signed-apks/${base_name}-signed.apk"
182+
183+
"$zipalign" -p -f 4 "$apk" "$aligned_apk"
184+
"$apksigner" sign \
185+
--ks "$keystore_path" \
186+
--ks-pass env:RELEASE_KEYSTORE_PASSWORD \
187+
--ks-key-alias "$RELEASE_KEY_ALIAS" \
188+
--key-pass env:RELEASE_KEY_PASSWORD \
189+
--out "$signed_apk" \
190+
"$aligned_apk"
191+
"$apksigner" verify --verbose "$signed_apk"
192+
done
193+
194+
if ! find signed-apks -type f -name "*.apk" | grep -q .; then
195+
echo "::error::No signed release APKs were produced."
196+
exit 1
197+
fi
198+
199+
- name: Collect release APKs
200+
if: steps.version.outputs.is_release == 'true'
201+
id: collect
202+
shell: bash
203+
env:
204+
VERSION_NAME: ${{ steps.version.outputs.version_name }}
205+
run: |
206+
set -euo pipefail
207+
208+
version_code="$(git rev-list --count HEAD)"
209+
mkdir -p release-artifacts
210+
211+
shopt -s nullglob
212+
for apk in signed-apks/*.apk; do
213+
file_name="$(basename "$apk")"
214+
abi="universal"
215+
case "$file_name" in
216+
*arm64-v8a*) abi="arm64-v8a" ;;
217+
*armeabi-v7a*) abi="armeabi-v7a" ;;
218+
*x86_64*) abi="x86_64" ;;
219+
*x86*) abi="x86" ;;
220+
*universal*) abi="universal" ;;
221+
esac
222+
223+
cp "$apk" "release-artifacts/AsteriskNG_${VERSION_NAME}-${version_code}-${abi}.apk"
224+
done
225+
226+
if ! find release-artifacts -type f -name "*.apk" | grep -q .; then
227+
echo "::error::No release APKs were found under signed-apks."
228+
exit 1
229+
fi
230+
231+
if ! find release-artifacts -type f -name "*-universal.apk" | grep -q .; then
232+
echo "::error::The universal APK was not produced."
233+
find release-artifacts -maxdepth 1 -type f -name "*.apk" -print
234+
exit 1
235+
fi
236+
237+
echo "version_code=$version_code" >> "$GITHUB_OUTPUT"
238+
ls -lh release-artifacts
239+
240+
- name: Upload GitHub artifact
241+
if: steps.version.outputs.is_release == 'true'
242+
uses: actions/upload-artifact@v4
243+
with:
244+
name: AsteriskNG_${{ steps.version.outputs.version_name }}-${{ steps.collect.outputs.version_code }}
245+
path: release-artifacts/*.apk
246+
if-no-files-found: error
247+
248+
- name: Create tag and release notes
249+
if: steps.version.outputs.is_release == 'true'
250+
id: release_info
251+
shell: bash
252+
env:
253+
TAG_NAME: ${{ steps.version.outputs.tag_name }}
254+
VERSION_NAME: ${{ steps.version.outputs.version_name }}
255+
VERSION_CODE: ${{ steps.collect.outputs.version_code }}
256+
run: |
257+
set -euo pipefail
258+
259+
current_sha="$(git rev-parse HEAD)"
260+
git fetch --tags --force
261+
262+
if git rev-parse -q --verify "refs/tags/$TAG_NAME" >/dev/null; then
263+
tag_sha="$(git rev-list -n 1 "$TAG_NAME")"
264+
if [ "$tag_sha" != "$current_sha" ]; then
265+
echo "::error::Tag $TAG_NAME already exists at $tag_sha, but this workflow is running at $current_sha."
266+
exit 1
267+
fi
268+
echo "Tag $TAG_NAME already exists on the current commit."
269+
else
270+
git config user.name "github-actions[bot]"
271+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
272+
git tag -a "$TAG_NAME" -m "Release $TAG_NAME" "$current_sha"
273+
git push origin "refs/tags/$TAG_NAME"
274+
fi
275+
276+
mapfile -t release_tags < <(
277+
git for-each-ref --format='%(refname:short)' refs/tags |
278+
grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' || true
279+
)
280+
281+
previous_candidates="$(mktemp)"
282+
for candidate in "${release_tags[@]}"; do
283+
if [ "$candidate" = "$TAG_NAME" ]; then
284+
continue
285+
fi
286+
if git merge-base --is-ancestor "$candidate" "$current_sha"; then
287+
count="$(git rev-list --count "${candidate}..${current_sha}")"
288+
printf '%s\t%s\n' "$count" "$candidate" >> "$previous_candidates"
289+
fi
290+
done
291+
292+
previous_tag=""
293+
if [ -s "$previous_candidates" ]; then
294+
previous_tag="$(sort -n "$previous_candidates" | head -n 1 | cut -f2-)"
295+
fi
296+
297+
range="$current_sha"
298+
if [ -n "$previous_tag" ]; then
299+
range="${previous_tag}..${current_sha}"
300+
fi
301+
302+
tmp_changes="$(mktemp)"
303+
git log --reverse --no-merges --pretty=format:"- %s ([%h](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/commit/%H))" "$range" > "$tmp_changes"
304+
if [ ! -s "$tmp_changes" ]; then
305+
git log --reverse --pretty=format:"- %s ([%h](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/commit/%H))" "$range" > "$tmp_changes"
306+
fi
307+
308+
{
309+
echo "## AsteriskNG $TAG_NAME"
310+
echo
311+
echo "- Version name: $VERSION_NAME"
312+
echo "- Version code: $VERSION_CODE"
313+
echo
314+
315+
if [ -n "$previous_tag" ]; then
316+
echo "### Changes since $previous_tag"
317+
echo
318+
echo "[Compare $previous_tag...$TAG_NAME](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/compare/${previous_tag}...${TAG_NAME})"
319+
else
320+
echo "### Changes"
321+
fi
322+
echo
323+
324+
if [ -s "$tmp_changes" ]; then
325+
cat "$tmp_changes"
326+
echo
327+
else
328+
echo "- No commit changes found."
329+
fi
330+
331+
echo
332+
echo "### APK artifacts"
333+
echo
334+
find release-artifacts -maxdepth 1 -type f -name "*.apk" -printf "- %f\n" | sort
335+
} > release-notes.md
336+
337+
echo "previous_tag=$previous_tag" >> "$GITHUB_OUTPUT"
338+
cat release-notes.md
339+
340+
- name: Create GitHub Release
341+
if: steps.version.outputs.is_release == 'true'
342+
shell: bash
343+
env:
344+
GH_TOKEN: ${{ github.token }}
345+
TAG_NAME: ${{ steps.version.outputs.tag_name }}
346+
VERSION_NAME: ${{ steps.version.outputs.version_name }}
347+
run: |
348+
set -euo pipefail
349+
350+
if gh release view "$TAG_NAME" >/dev/null 2>&1; then
351+
gh release upload "$TAG_NAME" release-artifacts/*.apk --clobber
352+
gh release edit "$TAG_NAME" \
353+
--title "AsteriskNG $TAG_NAME" \
354+
--notes-file release-notes.md \
355+
--latest
356+
else
357+
gh release create "$TAG_NAME" release-artifacts/*.apk \
358+
--title "AsteriskNG $TAG_NAME" \
359+
--notes-file release-notes.md \
360+
--latest
361+
fi

0 commit comments

Comments
 (0)