Crave Builder(self-hosted) #125
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# Copyright (C) 2024 Antonino Scordino | |
# Copyright (C) 2024 Souhrud Reddy | |
# | |
# SPDX-License-Identifier: Apache-2.0 | |
# | |
name: Crave Builder(self-hosted) | |
on: | |
workflow_dispatch: | |
# Various inputs to simplify usage of workflow. | |
inputs: | |
BASE_PROJECT: | |
description: 'Choose a base project:' | |
required: true | |
default: 'LineageOS 20.0' | |
type: choice | |
options: | |
- 'LineageOS 20.0' | |
- 'LineageOS 21.0' | |
- 'ArrowOS 13.1' | |
- 'DerpFest 13.0' | |
- 'CipherOS 14' | |
- 'PixelOS 14' | |
- 'RisingOS 14' | |
- 'LineageOS 18.1' | |
- 'LineageOS 16.0' | |
- 'CyanogenMOD 14.1' | |
BUILD_DIFFERENT_ROM: | |
description: "Command to initialize a different 'repo' project:" | |
required: false | |
default: "echo 'Build Starting!'" | |
REMOVALS: | |
description: "Folders to be removed before syncing:" | |
required: false | |
LOCAL_MANIFEST: | |
description: "Personal local manifest [repository or raw]:" | |
required: true | |
default: 'https://github.com/sounddrill31/local_manifests' | |
LOCAL_MANIFEST_BRANCH: | |
description: "Personal local manifest's branch:" | |
required: false | |
default: 'lineage-oxygen' | |
DEVICE_NAME: | |
description: "Device's codename:" | |
required: true | |
default: "oxygen" | |
PRODUCT_NAME: | |
description: "Product to build:" | |
required: true | |
default: "lineage_oxygen" | |
BUILD_COMMAND: | |
description: 'Command to be used for compiling:' | |
required: true | |
default: 'mka bacon' | |
BUILD_TYPE: | |
description: 'Type of build:' | |
required: true | |
default: 'userdebug' | |
type: choice | |
options: | |
- 'eng' | |
- 'userdebug' | |
- 'user' | |
CLEAN_BUILD: | |
description: 'Build with a clean workspace?' | |
required: true | |
default: 'no' | |
type: choice | |
options: | |
- 'yes' | |
- 'no' | |
jobs: | |
Start-Runner: | |
runs-on: ubuntu-latest | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@v2 | |
- name: Set up environment | |
run: | | |
sudo apt-get update | |
sudo apt-get install -y tmux | |
# Download and configure 'crave'. | |
- name: Configure the 'crave' environment | |
run: | | |
if [ "${DCDEVSPACE}" == "1" ]; then | |
echo 'No need to set up crave, we are already running in devspace!' | |
else | |
mkdir ${HOME}/bin/ | |
curl -s https://raw.githubusercontent.com/accupara/crave/master/get_crave.sh | bash -s -- | |
mv ${PWD}/crave ${HOME}/bin/ | |
sudo ln -sf /home/${USER}/bin/crave /usr/bin/crave | |
envsubst < ${PWD}/crave.conf.sample >> ${PWD}/crave.conf | |
rm -rf ${PWD}/crave.conf.sample | |
fi | |
env: | |
CRAVE_USERNAME: ${{ secrets.CRAVE_USERNAME }} | |
CRAVE_TOKEN: ${{ secrets.CRAVE_TOKEN }} | |
- name: Run crave devspace | |
run: | | |
crave ${{ secrets.CRAVE_FLAGS }} devspace -- "if tmux has-session -t ghactions; then | |
echo "Runner is already Running" | |
else | |
tmux kill-session -t ghactions; | |
tmux new-session -d -s ghactions | |
tmux send-keys -t ghactions './actions-runner/run.sh' Enter | |
echo "Runner Started" | |
fi " | |
prepare: | |
name: Prepare for Building | |
runs-on: ubuntu-latest | |
steps: | |
- name: Set Project variables | |
id: proj-variables | |
run: | | |
case "${{ github.event.inputs.BASE_PROJECT }}" in | |
"ArrowOS 13.1") | |
export PROJECTFOLDER="/crave-devspaces/Arrow13" | |
export PROJECTID="73" | |
export REPO_INIT="repo init -u https://github.com/ArrowOS/android_manifest.git -b arrow-13.1 --depth=1" | |
;; | |
"DerpFest 13.0") | |
export PROJECTFOLDER="/crave-devspaces/DerpFest13" | |
export PROJECTID="64" | |
export REPO_INIT="repo init -u https://github.com/DerpFest-AOSP/manifest.git -b 13 --depth=1" | |
;; | |
"LineageOS 21.0") | |
export PROJECTFOLDER="/crave-devspaces/Lineage21" | |
export PROJECTID="72" | |
export REPO_INIT="repo init -u https://github.com/LineageOS/android.git -b lineage-21.0 --git-lfs --depth=1" | |
;; | |
"LineageOS 20.0") | |
export PROJECTFOLDER="/crave-devspaces/Lineage20" | |
export PROJECTID="36" | |
export REPO_INIT="repo init -u https://github.com/accupara/los20.git -b lineage-20.0 --git-lfs --depth=1" | |
;; | |
"CipherOS 14") | |
export PROJECTFOLDER="/crave-devspaces/Cipher14" | |
export PROJECTID="79" | |
export REPO_INIT="repo init -u https://github.com/CipherOS/android_manifest.git -b fourteen --git-lfs --depth=1" | |
;; | |
"PixelOS 14") | |
export PROJECTFOLDER="/crave-devspaces/Pixel14" | |
export PROJECTID="82" | |
export REPO_INIT="repo init -u https://github.com/PixelOS-AOSP/manifest.git -b fourteen --git-lfs --depth=1" | |
;; | |
"RisingOS 14") | |
export PROJECTFOLDER="/crave-devspaces/Rising14" | |
export PROJECTID="86" | |
export REPO_INIT="repo init -u https://github.com/RisingTechOSS/android.git -b fourteen --git-lfs --depth=1" | |
;; | |
"LineageOS 18.1") | |
export PROJECTFOLDER="/crave-devspaces/Lineage18" | |
export PROJECTID="85" | |
export REPO_INIT="repo init -u https://github.com/accupara/los18.1.git -b lineage-18.1 --git-lfs --depth=1" | |
;; | |
"LineageOS 16.0") | |
export PROJECTFOLDER="/crave-devspaces/Lineage16" | |
export PROJECTID="81" | |
export REPO_INIT="repo init -u https://github.com/accupara/los16.git -b lineage-16.0 --git-lfs --depth=1" | |
;; | |
"CyanogenMOD 14.1") | |
export PROJECTFOLDER="/crave-devspaces/Cyanogen14" | |
export PROJECTID="83" | |
export REPO_INIT="repo init -u https://github.com/accupara/los-cm14.1.git -b cm-14.1 --git-lfs --depth=1" | |
;; | |
esac | |
if [[ ${{ github.event.inputs.PRODUCT_NAME }} == *_* &&! (${{ github.event.inputs.PRODUCT_NAME }} == gsi_* || ${{ github.event.inputs.PRODUCT_NAME }} == sdk_*) ]]; then | |
LUNCH="lunch ${{ github.event.inputs.PRODUCT_NAME }}-${{ github.event.inputs.BUILD_TYPE }}" | |
else | |
LUNCH="breakfast ${{ github.event.inputs.PRODUCT_NAME }} ${{ github.event.inputs.BUILD_TYPE }}" | |
fi | |
echo "Building on ${{ github.event.inputs.BASE_PROJECT }} project" | |
echo "PROJECTFOLDER=$PROJECTFOLDER" >> "$GITHUB_OUTPUT" | |
echo "PROJECTID=$PROJECTID" >> "$GITHUB_OUTPUT" | |
echo "REPO_INIT=$REPO_INIT" >> "$GITHUB_OUTPUT" | |
echo "LUNCH=$LUNCH" >> "$GITHUB_ENV" | |
echo "LUNCH=$LUNCH" >> "$GITHUB_OUTPUT" | |
- name: Display Run Parameters # Credit to azwhikaru for this part | |
run: | | |
echo "::group::User Environment Variables" | |
echo "Base Project: ${{ github.event.inputs.BASE_PROJECT }}" | |
echo "Extra Repo Init: ${{ github.event.inputs.BUILD_DIFFERENT_ROM }}" | |
echo "Removals: ${{ github.event.inputs.REMOVALS }}" | |
echo "Local Manifest: ${{ github.event.inputs.LOCAL_MANIFEST }}" | |
echo "Local Manifest Branch: ${{ github.event.inputs.LOCAL_MANIFEST_BRANCH }}" | |
echo "Device Name: ${{ github.event.inputs.DEVICE_NAME }}" | |
echo "Breakfast/Lunch Target: ${{ env.LUNCH }}" | |
echo "Build Command: ${{ github.event.inputs.BUILD_COMMAND }}" | |
echo "Clean Build: ${{ github.event.inputs.CLEAN_BUILD }}" | |
echo "::endgroup::" | |
echo "Displaying Local Manifests" | |
if [[ -z "${{ secrets.DISPLAY_FALSE }}" ]]; then | |
git clone ${{ github.event.inputs.LOCAL_MANIFEST }} --depth 1 -b ${{ github.event.inputs.LOCAL_MANIFEST_BRANCH }} local_manifests | |
cat local_manifests/*.xml | |
else | |
echo "Displaying is disabled through secrets." | |
fi | |
outputs: | |
PROJECTFOLDER: ${{ steps.proj-variables.outputs.PROJECTFOLDER }} | |
PROJECTID: ${{ steps.proj-variables.outputs.PROJECTID }} | |
REPO_INIT: ${{ steps.proj-variables.outputs.REPO_INIT }} | |
LUNCH: ${{ steps.proj-variables.outputs.LUNCH }} | |
test: | |
name: Test Local Manifests | |
needs: prepare | |
runs-on: ubuntu-latest | |
steps: | |
# Accept Project Variables | |
- name: Set Repo Project | |
run: | | |
PROJECTFOLDER="${{ needs.prepare.outputs.PROJECTFOLDER }}" | |
PROJECTID="${{ needs.prepare.outputs.PROJECTID }}" | |
REPO_INIT="${{ needs.prepare.outputs.REPO_INIT }}" | |
LUNCH="${{ needs.prepare.outputs.LUNCH }}" | |
echo "PROJECTFOLDER=$PROJECTFOLDER" >> "$GITHUB_ENV" | |
echo "PROJECTID=$PROJECTID" >> "$GITHUB_ENV" | |
echo "REPO_INIT=$REPO_INIT" >> "$GITHUB_ENV" | |
echo "LUNCH=$LUNCH" >> "$GITHUB_ENV" | |
# Download and configure 'repo'. | |
- name: Configure the 'repo' environment | |
run: | | |
# Check if repo is already installed | |
if ! command -v repo >/dev/null 2>&1; then | |
echo "Repo not found. Installing now..." | |
# Create bin directory if it doesn't exist | |
mkdir -p ~/bin | |
# Download repo script | |
curl https://storage.googleapis.com/git-repo-downloads/repo >> ~/bin/repo | |
# Make repo script executable | |
chmod a+x ~/bin/repo | |
# Create symbolic link to /usr/bin/repo | |
sudo ln -sf "/home/$(whoami)/bin/repo" "/usr/bin/repo" | |
echo "Repo installation complete." | |
else | |
echo "Repo already installed." | |
fi | |
continue-on-error: true | |
# Generate 'git' credential in base of the workflow's author. | |
- name: Set-up 'git' credential(s) | |
run: | | |
git config --global user.name "${{ github.actor }}" | |
git config --global user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com" | |
# Test Your Personal 'Local Manifests' against The ROM | |
- name: Test Local Manifests | |
if: ${{ github.event.inputs.BUILD_DIFFERENT_ROM != 'skip' }} | |
run: | | |
rm -rf tester | |
mkdir tester | |
cd tester | |
if [ "${{ github.event.inputs.BUILD_DIFFERENT_ROM }}" == "echo 'Build Starting!'" ]; then | |
$REPO_INIT | |
else | |
${{ github.event.inputs.BUILD_DIFFERENT_ROM }} | |
fi | |
git clone ${{ github.event.inputs.LOCAL_MANIFEST }} --depth 1 -b ${{ github.event.inputs.LOCAL_MANIFEST_BRANCH }} .repo/local_manifests && \ | |
if [ ! $? == 0 ]; then \ | |
curl -o .repo/local_manifests ${{ github.event.inputs.LOCAL_MANIFEST }}; \ | |
echo "Git clone failed, downloading through curl instead..."; \ | |
fi \ | |
timeout 1m repo sync --force-sync || { exit_code=$?; [ $exit_code -eq 124 ] || (echo "Error: Process failed with exit code $exit_code"; exit $exit_code); } | |
du -csh . # Output Size when done | |
rm -rf .repo | |
timeout-minutes: 10 | |
outputs: | |
PROJECTFOLDER: ${{ needs.prepare.outputs.PROJECTFOLDER }} | |
PROJECTID: ${{ needs.prepare.outputs.PROJECTID }} | |
REPO_INIT: ${{ needs.prepare.outputs.REPO_INIT }} | |
LUNCH: ${{ needs.prepare.outputs.LUNCH }} | |
build: | |
timeout-minutes: 720 | |
name: Build using foss.crave.io | |
needs: test | |
runs-on: self-hosted | |
concurrency: | |
group: ${{ github.actor }} | |
steps: | |
# Accept Project Variables | |
- name: Set Repo Project | |
run: | | |
PROJECTFOLDER="${{ needs.test.outputs.PROJECTFOLDER }}" | |
PROJECTID="${{ needs.test.outputs.PROJECTID }}" | |
REPO_INIT="${{ needs.test.outputs.REPO_INIT }}" | |
LUNCH="${{ needs.test.outputs.LUNCH }}" | |
echo "PROJECTFOLDER=$PROJECTFOLDER" >> "$GITHUB_ENV" | |
echo "PROJECTID=$PROJECTID" >> "$GITHUB_ENV" | |
echo "REPO_INIT=$REPO_INIT" >> "$GITHUB_ENV" | |
echo "LUNCH=$LUNCH" >> "$GITHUB_ENV" | |
# Send Build 'start' notification | |
- name: Telegram Notification | |
continue-on-error: true | |
run: | | |
send_telegram_message() { | |
local message="$1" | |
curl -s -X POST \ | |
https://api.telegram.org/bot$botToken/sendMessage \ | |
-d chat_id=$chatId \ | |
-d text="$message" \ | |
-d parse_mode="Markdown" | |
} | |
send_telegram_message "Crave ProjectID: $PROJECTID %0ABuild for ${{ github.event.inputs.DEVICE_NAME }} has been queued %0ACheck Progress at: %0Ahttps://github.com/$(echo "${{ github.repository }}" | sed 's@_@\\_@g')/actions/runs/${{ github.run_id }}" | |
env: | |
chatId: ${{ secrets.TELEGRAM_TO }} | |
botToken: ${{ secrets.TELEGRAM_TOKEN }} | |
jobStatus: "Build Started" | |
skipSuccess: false | |
- name: Cleanup | |
run: rm -rf * # This cleans github actions workspace | |
# Create a project folder | |
- name: Create Project Folders | |
run: | | |
if [ "${DCDEVSPACE}" != "1" ]; then | |
echo "Symlinking devspace folder" | |
mkdir -p devspace | |
sudo mkdir -p /crave-devspaces | |
sudo ln -sf ${pwd}/devspace /crave-devspaces | |
sudo chmod 777 /crave-devspaces | |
else | |
echo "We are already running in devspace... Skipping Symlinks" | |
fi | |
if grep -q "$PROJECTFOLDER" <(crave clone list --json | jq -r '.clones[]."Cloned At"') && [ "${DCDEVSPACE}" == "1" ]; then | |
crave clone destroy -y $PROJECTFOLDER || echo "Error removing $PROJECTFOLDER" | |
else | |
rm -rf $PROJECTFOLDER || true | |
fi | |
if [ "${DCDEVSPACE}" == "1" ]; then | |
crave clone create --projectID $PROJECTID $PROJECTFOLDER || echo "Crave clone create failed!" | |
else | |
mkdir $PROJECTFOLDER | |
fi | |
# Check-out in order to access the repository's files. | |
- name: Check-out to repository | |
uses: actions/checkout@v4 | |
# Set-up a spearate directory for the device. | |
- name: Set-up workspace environment | |
run: | | |
cd $PROJECTFOLDER | |
mkdir ${{ github.event.inputs.DEVICE_NAME }} | |
cd ${{ github.event.inputs.DEVICE_NAME }} | |
continue-on-error: true | |
# Generate 'git' credential in base of the workflow's author. | |
- name: Set-up 'git' credential(s) | |
run: | | |
git config --global user.name "${{ github.actor }}" | |
git config --global user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com" | |
# Download and configure 'crave'. | |
- name: Configure the 'crave' environment | |
run: | | |
mkdir -p $PROJECTFOLDER/.repo/manifests | |
if [ "${DCDEVSPACE}" == "1" ]; then | |
echo 'No need to set up crave, we are already running in devspace!' | |
else | |
curl -s https://raw.githubusercontent.com/accupara/crave/master/get_crave.sh | bash -s -- | |
mv ${PWD}/crave ${HOME}/bin/ | |
sudo ln -sf /home/${USER}/bin/crave /usr/bin/crave | |
envsubst < ${PWD}/crave.conf.sample >> ${PWD}/crave.conf | |
rm -rf ${PWD}/crave.conf.sample | |
cp crave.conf $PROJECTFOLDER | |
fi | |
if [[ -z "${{ secrets.CUSTOM_YAML }}" ]]; then | |
cp configs/crave/crave.yaml.aosp $PROJECTFOLDER/.repo/manifests/crave.yaml | |
echo "No Custom Configuration Found, Using Template!" | |
else | |
touch $PROJECTFOLDER/.repo/manifests/crave.yaml || true | |
echo "${{ secrets.CUSTOM_YAML }}" > $PROJECTFOLDER/.repo/manifests/crave.yaml | |
echo "Custom Configuration Found!" | |
fi | |
env: | |
CRAVE_USERNAME: ${{ secrets.CRAVE_USERNAME }} | |
CRAVE_TOKEN: ${{ secrets.CRAVE_TOKEN }} | |
# Initialize the previously choosen 'repo' project. | |
- name: Set the 'crave' project | |
run: | | |
cd $PROJECTFOLDER | |
if grep -q "$PROJECTFOLDER" <(crave clone list --json | jq -r '.clones[]."Cloned At"') && [ "${DCDEVSPACE}" == "1" ]; then | |
echo "Using Crave Clone... skipping" | |
else | |
echo "Running $REPO_INIT" | |
$REPO_INIT | |
fi | |
if [ "${{ github.event.inputs.BUILD_DIFFERENT_ROM }}" == "echo 'Build Starting!'" ]; then | |
export BUILD_DIFFERENT_ROM="$REPO_INIT" | |
echo "Building $BUILD_DIFFERENT_ROM" | |
else | |
export BUILD_DIFFERENT_ROM="${{ github.event.inputs.BUILD_DIFFERENT_ROM }}" | |
echo "Building $BUILD_DIFFERENT_ROM" | |
fi | |
echo "BUILD_DIFFERENT_ROM=$BUILD_DIFFERENT_ROM" >> "$GITHUB_ENV" | |
echo "We will be building with $LUNCH" | |
# Create a 'crave' job for building. | |
- name: Start compilation through 'crave' | |
if: ${{ github.event.inputs.BUILD_DIFFERENT_ROM != 'skip' }} | |
run: | | |
cd $PROJECTFOLDER | |
jq '.projects = []' ~/crave.conf > tmp && mv tmp ~/crave.conf | |
if [ "${{ github.event.inputs.CLEAN_BUILD }}" == "yes" ]; then | |
export CLEAN="clean" | |
fi | |
crave run --no-patch --${CLEAN} "rm -rf .repo/local_manifests/ ${{ github.event.inputs.REMOVALS }} && \ | |
# Clone local_manifests repository | |
$BUILD_DIFFERENT_ROM ; \ | |
git clone ${{ github.event.inputs.LOCAL_MANIFEST }} --depth 1 -b ${{ github.event.inputs.LOCAL_MANIFEST_BRANCH }} .repo/local_manifests && \ | |
if [ ! \$? == 0 ]; then \ | |
curl -o .repo/local_manifests ${{ github.event.inputs.LOCAL_MANIFEST }}; \ | |
echo "Git clone failed, downloading through curl instead..."; \ | |
fi \ | |
# Sync the repositories | |
/opt/crave/resync.sh && \ | |
# Set up build environment | |
export BUILD_USERNAME=${{ github.actor }} ; \ | |
export BUILD_HOSTNAME=crave ; \ | |
source build/envsetup.sh && \ | |
echo "Repository: ${{ github.repository }}"; \ | |
echo "Run ID: ${{ github.run_id }}"; \ | |
# Build the ROM | |
$LUNCH && \ | |
make installclean && \ | |
${{ github.event.inputs.BUILD_COMMAND }}" | |
timeout-minutes: 720 | |
# Send Build 'status' notification | |
- name: Telegram Notification | |
if: ${{ success() || cancelled() || failure() }} | |
continue-on-error: true | |
run: | | |
send_telegram_message() { | |
local message="$1" | |
curl -s -X POST \ | |
https://api.telegram.org/bot$botToken/sendMessage \ | |
-d chat_id=$chatId \ | |
-d text="$message" \ | |
-d parse_mode="Markdown" | |
} | |
send_telegram_message "Crave ProjectID: $PROJECTID %0ABuild for ${{ github.event.inputs.DEVICE_NAME }} has status: $jobStatus %0ACheck Progress at: %0Ahttps://github.com/$(echo "${{ github.repository }}" | sed 's@_@\\_@g')/actions/runs/${{ github.run_id }}" | |
# Todo: error.log link | |
env: | |
chatId: ${{ secrets.TELEGRAM_TO }} | |
botToken: ${{ secrets.TELEGRAM_TOKEN }} | |
jobStatus: ${{ job.status }} | |
skipSuccess: false | |
# Only reach this wheter the user killed the workflow. | |
- name: Execute if the job is cancelled | |
if: ${{ cancelled() }} | |
run: | | |
cd $PROJECTFOLDER | |
crave stop --all | |
# Upload '.zip's and '.img's directly from 'crave' ssh. | |
- name: Upload build artifact(s) | |
continue-on-error: true | |
run: | | |
cd $PROJECTFOLDER | |
echo "${{ secrets.GITHUB_TOKEN }}" > token.txt | |
crave push token.txt -d $(crave ssh -- pwd | grep -v Select | sed -s 's/\r//g')/ | |
crave ssh -- "export GH_UPLOAD_LIMIT="${{ secrets.GH_UPLOAD_LIMIT }}"; bash /opt/crave/github-actions/upload.sh '${{ github.run_id }}' '${{ github.event.inputs.DEVICE_NAME }}' '${{ github.repository }}' '${{ github.event.inputs.PRODUCT_NAME }}-${{ github.run_id }}' '${{ secrets.EXTRA_FILES }}'" | |
# Pull Errors and Display them | |
- name: Display error.log | |
if: ${{ failure() }} | |
id: errorlog | |
run: | | |
cd $PROJECTFOLDER | |
crave ssh -- sleep 1 | |
crave pull out/error.log | |
echo "Displaying out/error.log" | |
cat out/error.log | |
# Upload to Telegram | |
- name: Upload Using telegram-upload | |
run: | | |
cd $PROJECTFOLDER | |
crave ssh -- "export TG_UPLOAD_LIMIT="${{ secrets.TG_UPLOAD_LIMIT }}"; bash /opt/crave/telegram/upload.sh '${{ github.event.inputs.DEVICE_NAME }}' '${{ secrets.EXTRA_FILES }}'" | |
continue-on-error: true | |
# Remove 'crave' clone | |
- name: Destroy Crave Clone | |
if: ${{ always() }} | |
run: | | |
if grep -q "$PROJECTFOLDER" <(crave clone list --json | jq -r '.clones[]."Cloned At"') && [ "${DCDEVSPACE}" == "1" ]; then | |
crave clone destroy -y $PROJECTFOLDER || echo "Error removing $PROJECTFOLDER" | |
else | |
rm -rf $PROJECTFOLDER || true | |
fi |