Skip to content

Build & Release Android #3

Build & Release Android

Build & Release Android #3

Workflow file for this run

# MasterDnsVPN — Manual Android Build & Release
#
# Trigger: Actions → Run workflow
# Required secrets: KEYSTORE_BASE64, KEYSTORE_PASSWORD, KEY_ALIAS, KEY_PASSWORD
name: Build & Release Android
on:
workflow_dispatch:
inputs:
version_name:
description: "Version name (e.g. 1.3.0) — empty = auto date-based"
required: false
default: ""
upstream_version:
description: "Upstream MasterDnsVPN version built into this release (e.g. v2026.04.01.080348-951927f)"
required: false
default: ""
prerelease:
description: "Mark as pre-release?"
type: boolean
default: false
permissions:
contents: write
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
JAVA_VERSION: "17"
NDK_VERSION: "27.3.13750724"
ANDROID_API: "26"
jobs:
build:
name: "Build & Release"
runs-on: ubuntu-latest
steps:
# ── Checkout ─────────────────────────────────────────────────────────
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
# ── Go ────────────────────────────────────────────────────────────────
- name: Set up Go (version from go.mod)
uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
cache-dependency-path: go.sum
# ── Java ─────────────────────────────────────────────────────────────
- name: Set up Java ${{ env.JAVA_VERSION }}
uses: actions/setup-java@v4
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: temurin
cache: gradle
# ── Android NDK ──────────────────────────────────────────────────────
- name: Install Android NDK ${{ env.NDK_VERSION }}
run: |
yes | "$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager" \
--licenses > /dev/null 2>&1 || true
"$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager" \
"ndk;${{ env.NDK_VERSION }}" \
"build-tools;35.0.0" \
"platforms;android-35"
echo "ANDROID_NDK_HOME=$ANDROID_SDK_ROOT/ndk/${{ env.NDK_VERSION }}" >> "$GITHUB_ENV"
# ── Override local.properties (CI uses Linux paths) ──────────────────
- name: Write android/local.properties for CI
run: |
printf 'sdk.dir=%s\nndk.dir=%s/ndk/%s\n' \
"$ANDROID_SDK_ROOT" \
"$ANDROID_SDK_ROOT" \
"${{ env.NDK_VERSION }}" \
> android/local.properties
echo "local.properties:"
cat android/local.properties
# ── Version ──────────────────────────────────────────────────────────
- name: Determine version
id: version
run: |
if [[ -n "${{ inputs.version_name }}" ]]; then
VER="${{ inputs.version_name }}"
PRERELEASE="${{ inputs.prerelease || 'false' }}"
else
VER="$(date -u +%Y.%m.%d)-${{ github.run_number }}"
PRERELEASE=true
fi
echo "version_name=$VER" >> "$GITHUB_OUTPUT"
echo "version_code=${{ github.run_number }}" >> "$GITHUB_OUTPUT"
echo "is_prerelease=$PRERELEASE" >> "$GITHUB_OUTPUT"
echo "release_tag=v${VER}" >> "$GITHUB_OUTPUT"
echo "Version: $VER code: ${{ github.run_number }} pre: $PRERELEASE"
# ── gomobile ─────────────────────────────────────────────────────────
- name: Install gomobile
run: |
go install golang.org/x/mobile/cmd/gomobile@latest
go install golang.org/x/mobile/cmd/gobind@latest
echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH"
- name: gomobile init
run: GOFLAGS="-mod=mod" gomobile init
- name: Build gomobile .aar
run: |
mkdir -p android/app/libs
GOFLAGS="-mod=mod" gomobile bind \
-v \
-target android \
-androidapi "${{ env.ANDROID_API }}" \
-javapkg com.masterdnsvpn.gomobile \
-o android/app/libs/masterdnsvpn.aar \
masterdnsvpn-go/cmd/android
echo "--- .aar output ---"
ls -lh android/app/libs/
# ── Signing keystore ──────────────────────────────────────────────────
- name: Restore signing keystore
env:
KEYSTORE_B64: ${{ secrets.KEYSTORE_BASE64 }}
run: |
if [ -n "$KEYSTORE_B64" ]; then
echo "$KEYSTORE_B64" | base64 --decode > /tmp/masterdnsvpn-release.jks
echo "KEYSTORE_PATH=/tmp/masterdnsvpn-release.jks" >> "$GITHUB_ENV"
echo "KEYSTORE_PASSWORD=${{ secrets.KEYSTORE_PASSWORD }}" >> "$GITHUB_ENV"
echo "KEY_ALIAS=${{ secrets.KEY_ALIAS }}" >> "$GITHUB_ENV"
echo "KEY_PASSWORD=${{ secrets.KEY_PASSWORD }}" >> "$GITHUB_ENV"
echo "Keystore: from secret KEYSTORE_BASE64"
else
echo "KEYSTORE_PATH=$GITHUB_WORKSPACE/android/masterdnsvpn-release.jks" >> "$GITHUB_ENV"
echo "KEYSTORE_PASSWORD=MasterDnsVPN2024!" >> "$GITHUB_ENV"
echo "KEY_ALIAS=masterdnsvpn" >> "$GITHUB_ENV"
echo "KEY_PASSWORD=MasterDnsVPN2024!" >> "$GITHUB_ENV"
echo "WARNING: KEYSTORE_BASE64 not set - using bundled keystore."
fi
# ── Gradle APKs ──────────────────────────────────────────────────────
- name: Make gradlew executable
run: |
# Download official Gradle wrapper script from Gradle GitHub
GRADLE_VER=$(grep distributionUrl android/gradle/wrapper/gradle-wrapper.properties \
| grep -oP 'gradle-\K[0-9.]+(?=-)')
echo "Gradle version from wrapper: $GRADLE_VER"
curl -fsSL \
"https://raw.githubusercontent.com/gradle/gradle/v${GRADLE_VER}.0/gradlew" \
-o android/gradlew 2>/dev/null \
|| curl -fsSL \
"https://raw.githubusercontent.com/gradle/gradle/master/gradlew" \
-o android/gradlew
chmod +x android/gradlew
ls -la android/gradlew
- name: Build release APKs
working-directory: android
run: |
./gradlew assembleRelease \
-PversionCode=${{ steps.version.outputs.version_code }} \
-PversionName=${{ steps.version.outputs.version_name }} \
--no-daemon \
--stacktrace
echo "--- Output APKs ---"
ls -lh app/build/outputs/apk/release/
# ── Upload artifact ───────────────────────────────────────────────────
- name: Upload APKs as workflow artifact
uses: actions/upload-artifact@v4
with:
name: apks-${{ steps.version.outputs.version_name }}
path: android/app/build/outputs/apk/release/*.apk
retention-days: 30
# ── GitHub Release ────────────────────────────────────────────────────
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.version.outputs.release_tag }}
name: "MasterDnsVPN ${{ steps.version.outputs.version_name }}"
prerelease: ${{ steps.version.outputs.is_prerelease }}
generate_release_notes: true
fail_on_unmatched_files: false
files: android/app/build/outputs/apk/release/*.apk
body: |
## MasterDnsVPN ${{ steps.version.outputs.version_name }}
| | |
|---|---|
| **Version Code** | `${{ steps.version.outputs.version_code }}` |
| **Upstream Engine** | `${{ inputs.upstream_version || 'N/A' }}` |
| **Commit** | [`${{ github.sha }}`](${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}) |
| **Build** | #${{ github.run_number }} |
| **Triggered by** | `${{ github.event_name }}` |
## APK Downloads
| File | Architecture | Use |
|------|-------------|-----|
| `...-arm64-v8a.apk` | ARM 64-bit | Most modern phones |
| `...-armeabi-v7a.apk` | ARM 32-bit | Older phones |
| `...-x86_64.apk` | x86 64-bit | Chromebook / emulator |
| `...-universal.apk` | Universal | When not sure |