Patch and Release OTA #536
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
name: Patch and Release OTA | |
on: | |
schedule: | |
- cron: "0 */6 * * *" # Check for update every 6 hours (UTC). GrapheneOS checks every 6 hours. | |
# Allows you to run this workflow manually from the Actions tab | |
workflow_dispatch: | |
inputs: | |
device-id: | |
description: Device code name | |
required: true | |
root: | |
description: Add root to the build | |
required: false | |
type: boolean | |
default: false | |
magisk-preinit-device: | |
description: Magisk preinit device. For example, "sda8", "sda15" etc., | |
required: false | |
update-channel: | |
description: GrapheneOS update channel. Supports `alpha`, `beta` and `stable`. Defaults to `stable` | |
required: false | |
env: | |
CARGO_INCREMENTAL: 1 | |
DEVICE_NAME: ${{ github.event.inputs.device-id }} | |
INTERACTIVE_MODE: false | |
GRAPHENEOS_UPDATE_CHANNEL: ${{ github.event.inputs.update-channel }} | |
RUST_BACKTRACE: short | |
RUSTUP_MAX_RETRIES: 10 | |
jobs: | |
build: | |
runs-on: ubuntu-latest | |
# Required by publisher step | |
permissions: write-all | |
steps: | |
- name: Check if `magisk-preinit-device` is set when `root` is true | |
run: | | |
# Convert inputs to proper boolean values | |
root=${{ github.event.inputs.root }} | |
magisk_preinit_device=${{ github.event.inputs.magisk-preinit-device }} | |
# Ensure that the boolean comparison is correctly handled | |
if [ "$root" == "true" ] && [ -z "$magisk_preinit_device" ]; then | |
echo -e "::error:: magisk-preinit-device is required when root is true." | |
exit 1 | |
fi | |
- name: Checkout code | |
uses: actions/checkout@v4 | |
with: | |
# Allow for switching to github-pages branch | |
fetch-depth: 0 | |
- name: Read from `env.toml` if exist | |
if: ${{ github.event_name == 'schedule' }} | |
run: | | |
# Check if the file exists | |
source src/util_functions.sh && check_toml_env | |
echo "DEVICE_NAME=${DEVICE_NAME}" >> $GITHUB_ENV | |
echo "GRAPHENEOS_UPDATE_CHANNEL=${GRAPHENEOS[UPDATE_CHANNEL]}" >> $GITHUB_ENV | |
- name: Check if a build exists already and verify assets | |
shell: bash | |
run: | | |
# Device name is a required parameter | |
if [[ -z "${DEVICE_NAME}" ]]; then | |
echo -e "::error::Missing required param \`DEVICE_NAME\`" | |
exit 1 | |
fi | |
# Fetch the latest GrapheneOS version and set up the environment | |
source src/fetcher.sh && get_latest_version | |
echo "GRAPHENEOS_VERSION=${VERSION[GRAPHENEOS]}" >> $GITHUB_ENV | |
# Determine if the build is based on magisk or rootless (build flavor) | |
build_flavor=$([[ ${{ github.event.inputs.root == 'true' }} == 'true' ]] && echo 'magisk' || echo 'rootless') | |
# Check if the tag exists | |
if git show-ref --tags ${VERSION[GRAPHENEOS]} --quiet; then | |
echo -e "Tag with GrapheneOS version ${VERSION[GRAPHENEOS]} already exists. Looking for assets..." | |
# Fetch the release information for the tag | |
repo_url="https://api.github.com/repos/${{ github.repository }}/releases/tags/${VERSION[GRAPHENEOS]}" | |
release_info=$(curl -sL "$repo_url") | |
# Define required assets | |
required_assets=( | |
"${{ env.DEVICE_NAME }}-${VERSION[GRAPHENEOS]}-magisk-*.zip" | |
"${{ env.DEVICE_NAME }}-${VERSION[GRAPHENEOS]}-magisk-*.zip.csig" | |
"${{ env.DEVICE_NAME }}-${VERSION[GRAPHENEOS]}-rootless-*.zip" | |
"${{ env.DEVICE_NAME }}-${VERSION[GRAPHENEOS]}-rootless-*.zip.csig" | |
) | |
existing_assets=$(echo "$release_info" | jq -r '.assets[].name') | |
missing_assets=() | |
for required_asset in "${required_assets[@]}"; do | |
# Convert wildcard pattern to regex | |
regex="${required_asset//\*/.*}" | |
for asset in "${existing_assets[@]}"; do | |
# if existing asset matches the required asset, break the loop | |
if ! [[ $asset =~ $regex ]]; then | |
missing_assets+=("$required_asset") | |
break | |
fi | |
done | |
done | |
if [ ${#missing_assets[@]} -eq 0 ]; then | |
echo -e "::error::All required assets are present. Exiting..." | |
exit 1 | |
else | |
echo -e "Missing assets:" | |
for missing_asset in "${missing_assets[@]}"; do | |
echo -e " - $missing_asset" | |
done | |
# Grep always throws an error stating it cannot find the file or directory | |
valid_build="" | |
for missing_asset in "${missing_assets[@]}"; do | |
if [[ $missing_asset == *"$build_flavor"* ]]; then | |
valid_build="$build_flavor" | |
break | |
fi | |
done | |
# Check if valid_build is either "magisk" or "rootless" | |
if [[ "$valid_build" == "magisk" ]] || [[ "$valid_build" == "rootless" ]]; then | |
echo -e "Proceeding with build to create missing assets..." | |
else | |
echo -e "::error::Asset with \`$build_flavor\` flavor already exists!" | |
exit 1 | |
fi | |
fi | |
else | |
echo -e "Tag with GrapheneOS version ${VERSION[GRAPHENEOS]} does not exist. Creating one..." | |
fi | |
- name: Setup Git | |
run: | | |
# Configure git for pushing changes | |
git config --global user.email ${{ secrets.EMAIL }} && git config --global user.name PiX | |
- name: Install Rust stable | |
uses: dtolnay/rust-toolchain@master | |
with: | |
toolchain: stable 2 weeks ago | |
- name: Build and Cache Rust Dependencies | |
uses: Swatinem/[email protected] | |
- name: Install Python | |
uses: actions/setup-python@v5 | |
with: | |
python-version: "3.12-dev" | |
- name: Setup Environment variables | |
run: | | |
echo "KEYS_AVB_BASE64=${{ secrets.AVB_KEY }}" >> $GITHUB_ENV | |
echo "KEYS_CERT_OTA_BASE64=${{ secrets.CERT_OTA }}" >> $GITHUB_ENV | |
echo "KEYS_OTA_BASE64=${{ secrets.OTA_KEY }}" >> $GITHUB_ENV | |
- name: Patch OTA | |
shell: bash | |
env: | |
ADDITIONALS_ROOT: ${{ github.event.inputs.root }} | |
CLEANUP: true | |
MAGISK_PREINIT: ${{ github.event.inputs.magisk-preinit-device }} | |
PASSPHRASE_AVB: ${{ secrets.PASSPHRASE_AVB }} | |
PASSPHRASE_OTA: ${{ secrets.PASSPHRASE_OTA }} | |
run: | | |
echo -e "Running release script.." | |
# Instead of running the script directly, | |
# we source it to get the variables in the current shell and use them in the next steps by exporting them | |
. src/main.sh | |
# Export the variables for the next steps | |
echo "GRAPHENEOS_OTA_TARGET=${GRAPHENEOS[OTA_TARGET]}" >> $GITHUB_ENV | |
echo "OUTPUTS_PATCHED_OTA=${OUTPUTS[PATCHED_OTA]}" >> $GITHUB_ENV | |
echo "WORKDIR=${WORKDIR}" >> $GITHUB_ENV | |
- name: Generate Changelog | |
run: | | |
# Generate a changelog for the release taking the latest GrapheneOS release | |
echo -e "See [Changelog](https://grapheneos.org/releases#${{ env.GRAPHENEOS_VERSION }})." > ${{ github.workspace }}-CHANGELOG.txt | |
- name: Make Release | |
uses: softprops/action-gh-release@v2 | |
with: | |
body_path: ${{ github.workspace }}-CHANGELOG.txt | |
files: | | |
${{ env.OUTPUTS_PATCHED_OTA }} | |
${{ env.OUTPUTS_PATCHED_OTA }}.csig | |
name: "${{ env.GRAPHENEOS_VERSION }}" | |
tag_name: "${{ env.GRAPHENEOS_VERSION }}" | |
- name: Publish OTA to server | |
shell: bash | |
run: | | |
CURRENT_COMMIT=$(git rev-parse --short HEAD) | |
FLAVOR=("magisk" "rootless") | |
root=${{ github.event.inputs.root }} | |
# Create `magisk` and `rootless` directories if they don't exist | |
mkdir -p "${FLAVOR}" | |
# Switch to gh-pages branch | |
git checkout gh-pages | |
echo -e "Updating Configs for the new release..." | |
# If root is true or the the release has `magisk` in <device_name>.json, use magisk flavor | |
if [ "${root}" = "true" ] || grep -q magisk "${{ env.DEVICE_NAME }}.json"; then | |
TARGET_FILE="${FLAVOR[0]}/${{ env.DEVICE_NAME }}.json" | |
else | |
TARGET_FILE="${FLAVOR[1]}/${{ env.DEVICE_NAME }}.json" | |
fi | |
echo -e "Updating Configs for the new release..." | |
# Check if the target file exists, if not create an empty one to avoid any issues | |
if [ ! -f "${TARGET_FILE}" ]; then | |
echo -e "touching ${TARGET_FILE}..." | |
touch "${TARGET_FILE}" | |
fi | |
# Copy from `./` to `./<flavor>/` if same tag doesn't exist in the target file | |
if ! grep -q "${{ env.GRAPHENEOS_VERSION }}" "${TARGET_FILE}"; then | |
echo -e "Copying ${{ env.DEVICE_NAME }}.json to ${TARGET_FILE}..." | |
cp "${{ env.DEVICE_NAME }}.json" "${TARGET_FILE}" | |
git add "${TARGET_FILE}" | |
else | |
echo -e "Deployed version (${{ env.GRAPHENEOS_VERSION }}) is same as current GrapheneOS release (${{ env.GRAPHENEOS_VERSION }}).\nUpdate skipped." | |
fi | |
# Commit and push the changes if the working directory is clean | |
if ! git diff-index --quiet HEAD; then | |
git commit -m "release("${CURRENT_COMMIT}"): bump GrapheneOS version to ${{ env.GRAPHENEOS_VERSION }}" | |
git push origin gh-pages | |
fi | |
# Switch back to main branch | |
git checkout main |