Skip to content
Draft
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
253 changes: 253 additions & 0 deletions .github/workflows/build-rom.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
name: Reusable ROM build

on:
workflow_call:
inputs:
rom-family:
required: true
type: string
device-id:
required: true
type: string
root:
required: true
type: boolean
magisk-preinit-device:
required: false
type: string
default: ""
update-channel:
required: true
type: string
compatible-sepolicy-patching:
required: true
type: boolean
allow-unauthorized-adb:
required: true
type: boolean
release-type:
required: true
type: string
publish:
required: true
type: boolean
secrets:
AVB_KEY:
required: true
CERT_OTA:
required: true
OTA_KEY:
required: true
PASSPHRASE_AVB:
required: true
PASSPHRASE_OTA:
required: true
GH_TOKEN:
required: false
EMAIL:
required: false

env:
CARGO_INCREMENTAL: 1
DEVICE_NAME: ${{ inputs.device-id }}
INTERACTIVE_MODE: false
ROM_FAMILY: ${{ inputs.rom-family }}
GRAPHENEOS_UPDATE_CHANNEL: ${{ inputs.update-channel }}
OUTPUT_SCOPE: ${{ inputs.publish && inputs.release-type != 'build-only' && 'published' || 'local-unpublished' }}
RUST_BACKTRACE: short
RUSTUP_MAX_RETRIES: 10
GH_TOKEN: ${{ secrets.GH_TOKEN }}
RELEASE_TYPE: ${{ inputs.release-type }}

jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- name: Validate build request
shell: bash
env:
ADDITIONALS_DEBUG: ${{ inputs.allow-unauthorized-adb }}
ADDITIONALS_MAS_COMPATIBLE_SEPOLICY: ${{ inputs.compatible-sepolicy-patching }}
ADDITIONALS_ROOT: ${{ inputs.root }}
MAGISK_PREINIT_REQUEST: ${{ inputs.magisk-preinit-device }}
run: |
if [[ "${ADDITIONALS_ROOT}" == "true" && -z "${MAGISK_PREINIT_REQUEST}" ]]; then
echo "::error::magisk-preinit-device is required for rooted builds"
exit 1
fi
case "${RELEASE_TYPE}" in
default|build-only|force-publish) ;;
*) echo "::error::Unknown release type"; exit 1 ;;
esac

- name: Checkout shared implementation
uses: actions/checkout@v7
with:
fetch-depth: 0

- name: Enforce profile output policy
shell: bash
env:
ADDITIONALS_DEBUG: ${{ inputs.allow-unauthorized-adb }}
ADDITIONALS_MAS_COMPATIBLE_SEPOLICY: ${{ inputs.compatible-sepolicy-patching }}
ADDITIONALS_ROOT: ${{ inputs.root }}
run: |
source src/declarations.sh
source src/rom_profiles.sh
resolve_rom_profile
enforce_output_policy "${OUTPUT_SCOPE}"

- name: Install Rust stable
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable 2 weeks ago

- name: Cache Rust dependencies
uses: Swatinem/rust-cache@v2.9.1

- name: Install Python
uses: actions/setup-python@v6
with:
python-version: "3.12-dev"

- name: Setup signing environment
shell: bash
run: |
echo "KEYS_AVB_BASE64<<EOF" >> "${GITHUB_ENV}"
echo "${{ secrets.AVB_KEY }}" >> "${GITHUB_ENV}"
echo "EOF" >> "${GITHUB_ENV}"
echo "KEYS_CERT_OTA_BASE64<<EOF" >> "${GITHUB_ENV}"
echo "${{ secrets.CERT_OTA }}" >> "${GITHUB_ENV}"
echo "EOF" >> "${GITHUB_ENV}"
echo "KEYS_OTA_BASE64<<EOF" >> "${GITHUB_ENV}"
echo "${{ secrets.OTA_KEY }}" >> "${GITHUB_ENV}"
echo "EOF" >> "${GITHUB_ENV}"

- name: Patch OTA
shell: bash
env:
ADDITIONALS_DEBUG: ${{ inputs.allow-unauthorized-adb }}
ADDITIONALS_MAS_COMPATIBLE_SEPOLICY: ${{ inputs.compatible-sepolicy-patching }}
ADDITIONALS_ROOT: ${{ inputs.root }}
CLEANUP: true
MAGISK_PREINIT: ${{ inputs.magisk-preinit-device }}
PASSPHRASE_AVB: ${{ secrets.PASSPHRASE_AVB }}
PASSPHRASE_OTA: ${{ secrets.PASSPHRASE_OTA }}
run: |
source src/main.sh
{
echo "GRAPHENEOS_OTA_TARGET=${GRAPHENEOS[OTA_TARGET]}"
echo "GRAPHENEOS_VERSION=${VERSION[GRAPHENEOS]}"
echo "MODULE_SELECTION_FINGERPRINT=${MODULE_SELECTION_FINGERPRINT}"
echo "OUTPUTS_PATCHED_OTA=${OUTPUTS[PATCHED_OTA]}"
echo "WORKDIR=${WORKDIR}"
} >> "${GITHUB_ENV}"

- name: Record build metadata
shell: bash
run: |
selection_metadata="${OUTPUTS_PATCHED_OTA}.selection.json"
python3 - \
"${selection_metadata}" \
"${ROM_FAMILY}" \
"${DEVICE_NAME}" \
"${MODULE_SELECTION_FINGERPRINT}" \
"${OUTPUT_SCOPE}" <<'PY'
import json
import pathlib
import sys

path, rom_family, device, fingerprint, output_scope = sys.argv[1:]
data = {
"device": device,
"module_selection_fingerprint": fingerprint,
"output_scope": output_scope,
"rom_family": rom_family,
"schema_version": 1,
}
pathlib.Path(path).write_text(
json.dumps(data, indent=2, sort_keys=True) + "\n",
encoding="utf-8",
)
PY
echo "SELECTION_METADATA=${selection_metadata}" >> "${GITHUB_ENV}"
{
echo "ROM family: ${ROM_FAMILY}"
echo "Device: ${DEVICE_NAME}"
echo "Selection fingerprint: ${MODULE_SELECTION_FINGERPRINT}"
echo "Output scope: ${OUTPUT_SCOPE}"
} >> "${GITHUB_STEP_SUMMARY}"

- name: Re-enforce publication policy
if: inputs.publish && inputs.release-type != 'build-only'
shell: bash
env:
ADDITIONALS_DEBUG: ${{ inputs.allow-unauthorized-adb }}
ADDITIONALS_MAS_COMPATIBLE_SEPOLICY: ${{ inputs.compatible-sepolicy-patching }}
ADDITIONALS_ROOT: ${{ inputs.root }}
run: |
source src/declarations.sh
source src/rom_profiles.sh
resolve_rom_profile
enforce_publication_evidence "${OUTPUT_SCOPE}"
[[ "${MODULE_SELECTION_FINGERPRINT}" =~ ^[0-9a-f]{64}$ ]]

- name: Generate changelog
if: inputs.publish && inputs.release-type != 'build-only'
shell: bash
run: |
{
echo "ROM family: ${ROM_FAMILY}"
echo "Device: ${DEVICE_NAME}"
echo "Module-selection fingerprint: ${MODULE_SELECTION_FINGERPRINT}"
} > "${GITHUB_WORKSPACE}-CHANGELOG.txt"

- name: Publish GitHub release
if: inputs.publish && inputs.release-type != 'build-only'
uses: softprops/action-gh-release@v3
with:
body_path: ${{ github.workspace }}-CHANGELOG.txt
files: |
${{ env.OUTPUTS_PATCHED_OTA }}
${{ env.OUTPUTS_PATCHED_OTA }}.csig
${{ env.SELECTION_METADATA }}
name: ${{ env.GRAPHENEOS_VERSION }}
tag_name: ${{ env.GRAPHENEOS_VERSION }}

- name: Publish OTA metadata
if: inputs.publish && inputs.release-type != 'build-only'
shell: bash
run: |
git config user.email "${{ secrets.EMAIL }}"
git config user.name "${{ github.repository_owner }}"
current_commit="$(git rev-parse --short HEAD)"
if [[ "${{ inputs.root }}" == 'true' ]]; then
flavor='magisk'
else
flavor='rootless'
fi

git checkout gh-pages
target_file="${flavor}/${DEVICE_NAME}.json"
variant_file="variants/${ROM_FAMILY}/${flavor}/${DEVICE_NAME}-${MODULE_SELECTION_FINGERPRINT}.json"
mkdir -p -- "${flavor}" "$(dirname -- "${variant_file}")"
[[ -f "${DEVICE_NAME}.json" ]] || {
echo "::error::Missing generated OTA metadata for ${DEVICE_NAME}"
exit 1
}
cp -- "${DEVICE_NAME}.json" "${target_file}"
cp -- "${DEVICE_NAME}.json" "${variant_file}"
git add -- "${target_file}" "${variant_file}"

if [[ "${RELEASE_TYPE}" == 'force-publish' ]]; then
printf 'force-publish run %s (attempt %s)\n' \
"${GITHUB_RUN_ID}" "${GITHUB_RUN_ATTEMPT}" > .tmp
git add -- .tmp
fi
if ! git diff-index --quiet HEAD; then
git commit -m "release(${current_commit}): publish ${ROM_FAMILY} ${GRAPHENEOS_VERSION} ${MODULE_SELECTION_FINGERPRINT}"
git push origin gh-pages
fi
53 changes: 53 additions & 0 deletions .github/workflows/phase3-build-only.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: Phase 3 build-only acceptance

on:
workflow_dispatch:
inputs:
rom-family:
description: ROM family to exercise
required: true
type: choice
options:
- grapheneos
- lineageos
default: grapheneos
device-id:
description: Device code name
required: true
default: shiba
root:
description: Add root to the build
required: false
type: boolean
default: false
magisk-preinit-device:
description: Magisk preinit device for rooted builds
required: false
default: sda10
update-channel:
description: Provider update channel
required: true
default: stable
compatible-sepolicy-patching:
description: Enable compatible SELinux patching
required: false
type: boolean
default: false

permissions:
contents: read

jobs:
build-only:
uses: ./.github/workflows/build-rom.yml
with:
rom-family: ${{ inputs.rom-family }}
device-id: ${{ inputs.device-id }}
root: ${{ inputs.root }}
magisk-preinit-device: ${{ inputs.magisk-preinit-device }}
update-channel: ${{ inputs.update-channel }}
compatible-sepolicy-patching: ${{ inputs.compatible-sepolicy-patching }}
allow-unauthorized-adb: false
release-type: build-only
publish: false
secrets: inherit
Loading
Loading