Rooting, Flashing, & Bootloader Exploits

Automating Magisk Module Builds: From Shell Scripts to CI/CD for Seamless Development

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction: Streamlining Magisk Module Development

Magisk modules have revolutionized the way Android users customize and enhance their devices, offering systemless modifications that preserve the integrity of the /system partition. For developers, creating and maintaining these modules often involves repetitive tasks: packaging files, versioning, and distributing updates. As your module grows in complexity or you manage multiple projects, manually performing these steps becomes cumbersome and error-prone. This article will guide you through automating your Magisk module build process, starting with fundamental shell scripting and culminating in a robust CI/CD pipeline using GitHub Actions, ensuring consistent, efficient, and reliable module releases.

The Manual Build Headache: Why Automation is Crucial

A typical Magisk module package is a simple ZIP file containing specific directories and files like module.prop, customize.sh, and the actual modification files. The manual process often looks like this:

  1. Make changes to module files.
  2. Update module.prop (especially the version code).
  3. Select all relevant files and folders.
  4. Compress them into a .zip file, ensuring the root directory contains the module structure directly.
  5. Rename the .zip file to reflect the version.
  6. Manually upload or distribute the file.

This iterative process, especially during rapid development cycles, introduces opportunities for mistakes – forgetting to update the version, including unnecessary files, or incorrect ZIP compression, leading to broken modules or frustrated users. Automation eliminates these human errors.

Enter Shell Scripts: The First Step to Automation

The simplest form of automation involves a shell script to handle the packaging. Let’s create a basic build.sh script.

Basic Packaging Script

First, ensure your module’s structure is correct. Typically, it looks like this:

my-magisk-module/├── module.prop├── customize.sh├── post-fs-data.sh (optional)├── system/ (optional)│   └── ...└── service.sh (optional)

Now, create build.sh in the root of your module directory:

#!/bin/bashMODULE_ID=$(grep -E '^id=' module.prop | cut -d'=' -f2)MODULE_VERSION=$(grep -E '^versionCode=' module.prop | cut -d'=' -f2)OUTPUT_DIR="./build"MODULE_ZIP="${MODULE_ID}-${MODULE_VERSION}.zip"# Clean up previous buildsrm -rf ${OUTPUT_DIR}mkdir -p ${OUTPUT_DIR}# Create the zip packageecho "Building ${MODULE_ZIP}..."zip -r ${OUTPUT_DIR}/${MODULE_ZIP} . -x "*.git*" "*build.sh" "*README.md" "*LICENSE" ".gitignore" "*.DS_Store"echo "Module built successfully: ${OUTPUT_DIR}/${MODULE_ZIP}"

To run this, make it executable: chmod +x build.sh, then execute: ./build.sh. This script reads the module ID and version from module.prop, creates a unique ZIP filename, and excludes common development files from the package.

Understanding module.prop and customize.sh

  • module.prop: Contains metadata like id, name, version, versionCode, author, and description. The id and versionCode are crucial for automation.
  • customize.sh: The core script that Magisk executes during module installation. It handles system modifications, symlinking, and other setup tasks. Your build script only packages this; customize.sh itself dictates the installation logic.

Elevating Automation with Advanced Shell Scripting

Beyond basic packaging, you can enhance your build.sh to include more sophisticated checks and operations:

  • Version Incrementing: Automatically increment versionCode.
  • Linting/Validation: Check module.prop for required fields or customize.sh for common errors.
  • Dependency Checks: Ensure necessary tools (like zip) are available.
  • Cleanup: Remove any temporary files created during the build process.

Here’s an advanced snippet for automatic version increment (you might integrate this into your build script or run it as a separate pre-build step):

# Inside build.sh or a pre-build script# Get current version codeCURRENT_VERSION_CODE=$(grep -E '^versionCode=' module.prop | cut -d'=' -f2)NEW_VERSION_CODE=$((CURRENT_VERSION_CODE + 1))# Update version code in module.prop (requires sed or similar)sed -i "s/^versionCode=${CURRENT_VERSION_CODE}/versionCode=${NEW_VERSION_CODE}/" module.prop# Optionally update version name based on date or git commit hashVERSION_NAME_DATE=$(date +"%Y.%m.%d")sed -i "s/^version=.*/version=${VERSION_NAME_DATE}-release/" module.prop

This makes sure that every build has a unique and incremental version code, a critical aspect for proper module updates.

From Local Scripts to Cloud Power: CI/CD for Magisk Modules

While local shell scripts are great, they still require manual execution. Continuous Integration/Continuous Delivery (CI/CD) takes automation to the next level by automatically building, testing, and even releasing your module whenever changes are pushed to your repository. This offers:

  • Consistency: Every build uses the same environment and steps.
  • Collaboration: Multiple developers can contribute without worrying about local environment differences.
  • Automation of Releases: New versions can be automatically published upon specific events (e.g., a new Git tag).
  • Early Error Detection: Build failures are caught immediately.

For open-source projects, GitHub Actions is an excellent choice for CI/CD, being tightly integrated with GitHub repositories.

Implementing CI/CD with GitHub Actions

Let’s set up a GitHub Actions workflow that triggers on every push to the main branch, builds the module, and creates a GitHub Release when a new tag is pushed.

GitHub Actions Workflow (`.github/workflows/build.yml`)

Create a file named build.yml inside the .github/workflows/ directory in your repository.

name: Build Magisk Moduleon:  push:    branches:      - main    tags:      - 'v*' # Trigger on tags like v1.0, v2.1.0jobs:  build:    runs-on: ubuntu-latest    steps:      - name: Checkout code        uses: actions/checkout@v4      - name: Set up build environment        run: |          sudo apt-get update          sudo apt-get install -y zip # Ensure zip is available      - name: Make build script executable        run: chmod +x build.sh      - name: Build Magisk Module        id: build_module        run: ./build.sh      - name: Get module artifact path and name        id: get_artifact        run: |          MODULE_ID=$(grep -E '^id=' module.prop | cut -d'=' -f2)          MODULE_VERSION=$(grep -E '^versionCode=' module.prop | cut -d'=' -f2)          MODULE_ZIP_PATH="./build/${MODULE_ID}-${MODULE_VERSION}.zip"          echo "artifact_path=${MODULE_ZIP_PATH}" >> "$GITHUB_OUTPUT"          echo "artifact_name=${MODULE_ID}-${MODULE_VERSION}.zip" >> "$GITHUB_OUTPUT"      - name: Upload module as artifact        uses: actions/upload-artifact@v4        with:          name: ${{ steps.get_artifact.outputs.artifact_name }}          path: ${{ steps.get_artifact.outputs.artifact_path }}      - name: Create GitHub Release        if: startsWith(github.ref, 'refs/tags/') # Only run if a tag triggered the workflow        uses: softprops/action-gh-release@v1        with:          files: ${{ steps.get_artifact.outputs.artifact_path }}          prerelease: ${{ contains(github.ref, '-beta') || contains(github.ref, '-rc') }} # Example: v1.0-beta1 becomes prerelease          body: |            ## New Release: ${{ github.ref_name }}            This release contains the latest changes and bug fixes.            Please report any issues!

This workflow does the following:

  1. Triggers: Runs on every push to main or when a new Git tag (e.g., v1.0) is pushed.
  2. Checkout Code: Retrieves your repository’s code.
  3. Set up Environment: Installs zip, which our build.sh relies on.
  4. Build Module: Executes your build.sh script.
  5. Get Artifact Path: A small step to dynamically get the path and name of the generated ZIP file.
  6. Upload Artifact: Stores the built .zip file as a workflow artifact, accessible from the GitHub Actions run summary.
  7. Create GitHub Release: If the workflow was triggered by a tag, it uses softprops/action-gh-release to create a new GitHub Release, attaching the built Magisk module ZIP as an asset. It also intelligently marks prereleases based on tag naming conventions.

To trigger a release, you would typically make your changes, commit them, push to main, and then create and push a tag:

git add .git commit -m "feat: new awesome feature"git push origin maingit tag -a v1.0.0 -m "Version 1.0.0 Release"git push origin v1.0.0

Testing and Distribution Considerations

While the CI/CD pipeline automates the build and release, a critical next step for full automation is integrating automated testing. This could involve:

  • Emulator Testing: Using Android emulators (e.g., via AVDs or Genymotion) to install the module and verify its functionality.
  • Device Farms: Utilizing services like Firebase Test Lab to test on a wider range of real devices.
  • Unit/Integration Tests: If applicable, writing tests for individual components of your module’s scripts (e.g., using Bash automated testing frameworks).

For distribution, the GitHub Release is a solid starting point. Users can download modules directly from your repository’s Releases page. You might also consider integrating with a custom update server or Magisk’s module repository if your module qualifies.

Conclusion

Automating your Magisk module build process transforms tedious manual steps into a streamlined, error-free workflow. By starting with simple shell scripts and progressively moving to a full-fledged CI/CD pipeline with GitHub Actions, you can dramatically improve your development efficiency, ensure consistent releases, and provide a better experience for your module’s users. Embrace automation to spend less time on repetitive tasks and more time on innovating your Android modifications.

Android Mobile Specs & Compare Directory

Are you researching mobile hardware properties, processor SoCs, GPU chipsets, or RAM configurations? Access our complete specs catalog to compare up to 5 devices side-by-side!

Compare Devices Specs →
Google AdSense Inline Placement - Content Footer banner