Skip to content

feat: implement unified build system [WPB-8645] #4059

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions .github/actions/setup-keystore/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
name: 'Setup Keystore'
description: 'Decode and setup keystore for signing Android builds'

inputs:
keystore-type:
description: 'Type of keystore to setup (debug|prerelease|internal|public)'
required: true
keystore-path:
description: 'Path where keystore should be placed'
required: false
default: '${{ runner.temp }}/keystore'

outputs:
keystore-file-path:
description: 'Full path to the decoded keystore file'
value: ${{ steps.setup.outputs.keystore-file-path }}

runs:
using: 'composite'
steps:
- name: Validate keystore type
shell: bash
run: |
case "${{ inputs.keystore-type }}" in
debug|prerelease|internal|public)
echo "Valid keystore type: ${{ inputs.keystore-type }}"
;;
*)
echo "Error: Invalid keystore type '${{ inputs.keystore-type }}'. Must be one of: debug, prerelease, internal, public"
exit 1
;;
esac

- name: Setup keystore directory
shell: bash
run: |
mkdir -p "${{ inputs.keystore-path }}"

- name: Decode keystore
id: setup
shell: bash
env:
KEYSTORE_TYPE: ${{ inputs.keystore-type }}
KEYSTORE_PATH: ${{ inputs.keystore-path }}
run: |
# Map keystore type to secret name
case "${KEYSTORE_TYPE}" in
debug)
SECRET_NAME="ENCODED_KEYSTORE_DEBUG"
;;
prerelease)
SECRET_NAME="ENCODED_KEYSTORE_PRE_RELEASE"
;;
internal)
SECRET_NAME="ENCODED_KEYSTORE_INTERNAL_RELEASE"
;;
public)
SECRET_NAME="ENCODED_KEYSTORE_PUBLIC_RELEASE"
;;
esac

# Get the encoded keystore from environment
ENCODED_KEYSTORE=$(printenv "${SECRET_NAME}" || echo "")

if [ -z "${ENCODED_KEYSTORE}" ]; then
echo "Error: Secret ${SECRET_NAME} not found or empty"
exit 1
fi

# Decode keystore to file
KEYSTORE_FILE="${KEYSTORE_PATH}/the.keystore"
echo "${ENCODED_KEYSTORE}" | base64 -d > "${KEYSTORE_FILE}"

# Verify file was created
if [ ! -f "${KEYSTORE_FILE}" ]; then
echo "Error: Failed to create keystore file"
exit 1
fi

echo "Keystore decoded successfully to: ${KEYSTORE_FILE}"
echo "keystore-file-path=${KEYSTORE_FILE}" >> $GITHUB_OUTPUT

- name: Set keystore environment variables
shell: bash
env:
KEYSTORE_TYPE: ${{ inputs.keystore-type }}
KEYSTORE_FILE_PATH: ${{ steps.setup.outputs.keystore-file-path }}
run: |
# Set common environment variables
echo "KEYSTORE_FILE_PATH_DEBUG=${KEYSTORE_FILE_PATH}" >> $GITHUB_ENV
echo "KEYSTORE_FILE_PATH_RELEASE=${KEYSTORE_FILE_PATH}" >> $GITHUB_ENV
echo "KEYSTORE_FILE_PATH_COMPAT=${KEYSTORE_FILE_PATH}" >> $GITHUB_ENV
echo "KEYSTORE_FILE_PATH_COMPAT_RELEASE=${KEYSTORE_FILE_PATH}" >> $GITHUB_ENV

# Set type-specific alias and password environment variables
case "${KEYSTORE_TYPE}" in
debug)
echo "Using debug keystore configuration"
# Debug keystore env vars will be set by secrets in calling workflow
;;
prerelease)
echo "Using pre-release keystore configuration"
# Pre-release keystore env vars will be set by secrets in calling workflow
;;
internal)
echo "Using internal release keystore configuration"
# Internal keystore env vars will be set by secrets in calling workflow
;;
public)
echo "Using public release keystore configuration"
# Public keystore env vars will be set by secrets in calling workflow
;;
esac
72 changes: 72 additions & 0 deletions .github/workflows/build-develop-unified.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
name: "Develop build (Unified)"

on:
push:
branches:
- develop
merge_group:
types: [ checks_requested ]
branches: [ develop ]
pull_request:
branches:
- develop
types: [ opened, synchronize ]

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.event.merge_group.head_sha }}
cancel-in-progress: true

jobs:
# Build for PR and merge group validation (no store deployment)
build-validation:
if: github.event_name == 'pull_request' || github.event_name == 'merge_group'
uses: ./.github/workflows/build-unified.yml
with:
build-config: |
[
{
"flavor": "Dev",
"variant": "Debug",
"keystore-type": "debug",
"build-type": "apk",
"generate-version-file": false,
"deployment-targets": "[{\"type\": \"s3\"}]"
},
{
"flavor": "Internal",
"variant": "Compat",
"keystore-type": "internal",
"build-type": "bundle",
"generate-version-file": false,
"deployment-targets": "[]"
}
]
enable-deployment: true
secrets: inherit

# Build for direct pushes to develop (includes internal store deployment)
build-develop:
if: github.event_name == 'push' && github.ref_name == 'develop'
uses: ./.github/workflows/build-unified.yml
with:
build-config: |
[
{
"flavor": "Dev",
"variant": "Debug",
"keystore-type": "debug",
"build-type": "apk",
"generate-version-file": false,
"deployment-targets": "[{\"type\": \"s3\"}]"
},
{
"flavor": "Internal",
"variant": "Compat",
"keystore-type": "internal",
"build-type": "bundle",
"generate-version-file": false,
"deployment-targets": "[{\"type\": \"s3\"}, {\"type\": \"google-play\", \"package-name\": \"com.wire.internal\", \"track\": \"internal\", \"status\": \"completed\"}]"
}
]
enable-deployment: true
secrets: inherit
53 changes: 53 additions & 0 deletions .github/workflows/build-main-unified.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: "Main build (Unified)"

on:
push:
branches:
- main
pull_request:
branches:
- main
workflow_call:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
validate-trigger:
runs-on: ubuntu-latest
outputs:
should-build: ${{ steps.check.outputs.should-build }}
steps:
- name: Check if should build
id: check
run: |
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
echo "Pull request detected - skipping build"
echo "should-build=false" >> $GITHUB_OUTPUT
elif [[ "${{ github.ref }}" != "refs/heads/main" ]]; then
echo "Not main branch - skipping build"
echo "should-build=false" >> $GITHUB_OUTPUT
else
echo "Push to main branch - proceeding with build"
echo "should-build=true" >> $GITHUB_OUTPUT
fi

build-beta:
needs: validate-trigger
if: needs.validate-trigger.outputs.should-build == 'true'
uses: ./.github/workflows/build-unified.yml
with:
build-config: |
[
{
"flavor": "Beta",
"variant": "Release",
"keystore-type": "prerelease",
"build-type": "both",
"generate-version-file": false,
"deployment-targets": "[{\"type\": \"s3\"}, {\"type\": \"google-play\", \"package-name\": \"com.wire.android.internal\", \"track\": \"internal\"}]"
}
]
enable-deployment: true
secrets: inherit
69 changes: 69 additions & 0 deletions .github/workflows/build-production-unified.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: "Production build (Unified)"

on:
release:
types: [ published ]

concurrency:
group: ${{ github.workflow }}-${{ github.event.release.tag_name }}
cancel-in-progress: true

jobs:
validate-release:
runs-on: ubuntu-latest
steps:
- name: Get latest release tag
id: get_latest_release
run: |
latest_tag=$(curl -s https://api.github.com/repos/${{ github.repository }}/releases/latest | jq -r '.tag_name')
echo "latest_tag=$latest_tag" >> $GITHUB_OUTPUT

- name: Compare versions
run: |
current_tag="${{ github.event.release.tag_name }}"
latest_tag="${{ steps.get_latest_release.outputs.latest_tag }}"
cur_ver="${current_tag#v}"
lat_ver="${latest_tag#v}"
highest="$(printf '%s\n%s' "$cur_ver" "$lat_ver" | sort -V | tail -n1)"
if [[ "$cur_ver" != "$lat_ver" && "$highest" != "$cur_ver" ]]; then
echo "Current tag ($current_tag) is lower than latest tag ($latest_tag). Failing the workflow."
exit 1
else
echo "Current tag ($current_tag) is equal or higher than $latest_tag. Continuing the workflow."
fi

build-production:
needs: validate-release
uses: ./.github/workflows/build-unified.yml
with:
build-config: |
[
{
"flavor": "Prod",
"variant": "Compatrelease",
"keystore-type": "public",
"build-type": "both",
"generate-version-file": true,
"deployment-targets": "[{\"type\": \"s3\"}, {\"type\": \"google-play\", \"package-name\": \"com.wire\", \"track\": \"alpha\"}, {\"type\": \"github-release\", \"additional-files\": \"app/version.txt\"}]"
}
]
enable-deployment: true
secrets: inherit

build-fdroid:
needs: validate-release
uses: ./.github/workflows/build-unified.yml
with:
build-config: |
[
{
"flavor": "Fdroid",
"variant": "Compatrelease",
"keystore-type": "public",
"build-type": "apk",
"generate-version-file": false,
"deployment-targets": "[{\"type\": \"s3\"}, {\"type\": \"github-release\"}]"
}
]
enable-deployment: true
secrets: inherit
64 changes: 64 additions & 0 deletions .github/workflows/build-release-candidate-unified.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: "Release Candidate build (Unified)"

on:
push:
branches:
- release/candidate
merge_group:
types: [ checks_requested ]
branches: [ release/candidate ]
pull_request:
branches:
- release/candidate
types: [ opened, synchronize ]

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.event.merge_group.head_sha }}
cancel-in-progress: true

jobs:
# Build for PR and merge group validation
build-validation:
if: github.event_name == 'merge_group' || (github.event.pull_request.head.repo.full_name == github.repository && github.event_name == 'pull_request')
uses: ./.github/workflows/build-unified.yml
with:
build-config: |
[
{
"flavor": "Staging",
"variant": "Compat",
"keystore-type": "internal",
"build-type": "apk",
"generate-version-file": false,
"deployment-targets": "[{\"type\": \"s3\"}]"
}
]
enable-deployment: true
secrets: inherit

# Build for direct pushes to release/candidate
build-release-candidate:
if: github.event_name == 'push' && github.ref_name == 'release/candidate'
uses: ./.github/workflows/build-unified.yml
with:
build-config: |
[
{
"flavor": "Staging",
"variant": "Compat",
"keystore-type": "internal",
"build-type": "apk",
"generate-version-file": false,
"deployment-targets": "[{\"type\": \"s3\"}]"
},
{
"flavor": "Internal",
"variant": "Compat",
"keystore-type": "internal",
"build-type": "both",
"generate-version-file": false,
"deployment-targets": "[{\"type\": \"s3\"}, {\"type\": \"google-play\", \"package-name\": \"com.wire.internal\", \"track\": \"production\", \"status\": \"completed\"}]"
}
]
enable-deployment: true
secrets: inherit
Loading
Loading