# AGENTS.md - AI Agent Reference Guide for openwrt-ci-ipq60xx > This document is a comprehensive reference for AI agents working with this repository. > Last updated: 2026-03-01 --- ## Project Overview This is a **build-orchestration repository** for OpenWrt/ImmortalWrt IPQ60XX firmware using Woodpecker CI. ### Key Characteristics | Aspect | Description | |--------|-------------| | Type | CI pipeline configuration (NOT a source code repo) | | Target Platform | Qualcomm IPQ60XX routers | | Firmware Source | `VIKINGYFY/immortalwrt.git` (main branch) | | CI System | Woodpecker | | Config Source | VIKINGYFY/OpenWRT-CI + davidtall/OpenWRT-CI | --- ## Architecture: Three-Layer Model ``` ┌─────────────────────────────────────────────────────────────────┐ │ Layer 1: SOURCE (Firmware Code) │ ├─────────────────────────────────────────────────────────────────┤ │ Repository: VIKINGYFY/immortalwrt │ │ Branch: main │ │ Cloned: During CI build (git clone) │ │ Purpose: Provides OpenWrt/ImmortalWrt source code │ └─────────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────┐ │ Layer 2: CI-TEMPLATE (Config Templates) │ ├─────────────────────────────────────────────────────────────────┤ │ Device Sets (Fixed files in repo): │ │ - Config/IPQ60XX-WIFI-YES.txt (VIKINGYFY source) │ │ - Config/IPQ60XX-WIFI.txt (davidtall source) │ │ │ │ General Config (Dynamic, fetched at build time): │ │ - Config/GENERAL.upstream.txt (from davidtall/OpenWRT-CI) │ │ Downloaded by Scripts/sync_upstream_config.sh │ └─────────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────┐ │ Layer 3: YOUR-DELTA (Local Customizations) │ ├─────────────────────────────────────────────────────────────────┤ │ Config/GENERAL.local.txt - Your package additions │ │ Scripts/Packages.sh - Custom package cloning │ │ Scripts/Handles.sh - Package tweaks/fixes │ │ Scripts/Settings.sh - kconfig/custom settings │ │ files/ - Rootfs overlay files │ │ │ │ IMPORTANT: Only edit files in this layer! │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## Woodpecker Build Flow ### Build Steps (.woodpecker/ipq60xx.yml) ``` 1. init-env └─> Install build dependencies └─> Run ImmortalWrt init script 2. sync-configs └─> Execute Scripts/sync_upstream_config.sh └─> Download Config/GENERAL.upstream.txt 3. build-ipq60xx-wifi-yes ├─> Clone firmware source (VIKINGYFY/immortalwrt) ├─> feeds update -a && feeds install -a ├─> Execute Scripts/Packages.sh Handles.sh ├─> Merge configs: IPQ60XX-WIFI-YES.txt + GENERAL.upstream.txt + GENERAL.local.txt ├─> Execute Scripts/Settings.sh ├─> make defconfig -> make download -> make └─> Copy output to artifacts/ipq60xx-wifi-yes/ 4. build-ipq60xx-wifi └─> Same as above, using IPQ60XX-WIFI.txt └─> Copy output to artifacts/ipq60xx-wifi/ ``` ### Config Merging Order During build, `.config` is assembled by concatenating in this order: ```bash cat Config/IPQ60XX-WIFI-*.txt \ Config/GENERAL.upstream.txt \ Config/GENERAL.local.txt > .config ``` **Rule**: Later entries override earlier ones. - Device config settings have lowest priority - Upstream GENERAL can override device config - Your LOCAL overrides have highest priority --- ## File Structure Reference ``` openwrt-ci-ipq60xx/ │ ├── .woodpecker/ │ └── ipq60xx.yml # Woodpecker pipeline definition │ ├── Config/ │ ├── IPQ60XX-WIFI-YES.txt # Device set A (static, 21 devices) │ ├── IPQ60XX-WIFI.txt # Device set B (static, 9 devices) │ ├── GENERAL.upstream.txt # Fetched at build time (DO NOT EDIT) │ └── GENERAL.local.txt # YOUR package additions (EDIT THIS) │ ├── Scripts/ │ ├── sync_upstream_config.sh # Fetch GENERAL.txt from upstream │ ├── Packages.sh # Clone custom GitHub packages │ ├── Handles.sh # Apply fixes to packages │ └── Settings.sh # kconfig and customization │ ├── files/ # Rootfs overlay (optional) ├── artifacts/ # Build outputs (gitignored) ├── docs/ │ ├── SKILLS.md # User-facing documentation │ └── AGENTS.md # THIS FILE - Agent reference │ ├── README.md # Project README └── .gitignore ``` --- ## Scripts Detailed Reference ### Scripts/sync_upstream_config.sh **Purpose**: Download GENERAL.txt from upstream davidtall/OpenWRT-CI **Functions**: ```bash # Variable: COMMIT to pin to specific version (optional, defaults to 'main') UPSTREAM_GENERAL_REF="${UPSTREAM_GENERAL_REF:-main}" # Download target https://raw.githubusercontent.com/davidtall/OpenWRT-CI/${UPSTREAM_GENERAL_REF}/Config/GENERAL.txt ``` **When to modify**: - If upstream repository changes - If you want to change default branch --- ### Scripts/Packages.sh **Purpose**: Clone custom GitHub packages to override feed packages **Key Function**: `UPDATE_PACKAGE` ```bash UPDATE_PACKAGE "PKG_NAME" "USER/REPO" "BRANCH" ["MODE"] ["ALIAS_LIST"] ``` **Parameters**: | Parameter | Description | Example | |-----------|-------------|---------| | PKG_NAME | Package name to find/delete | "argon" | | USER/REPO | GitHub repo | "sbwml/luci-theme-argon" | | BRANCH | Git branch | "openwrt-25.12" | | MODE | Optional: "pkg" or "name" | "pkg" | | ALIAS_LIST | Optional: space-separated aliases | "appfilter oaf" | **Modes**: - `pkg`: Extract package from multi-package repo - `name`: Rename cloned directory to PKG_NAME - (empty): Use repo name as-is **Current Packages** (lines 48-76): - Themes: argon, aurora, kucat (+ configs) - Proxy: homeproxy, momo, nikki, openclash, passwall, passwall2 - Network: tailscale, easytier, vnt - Tools: ddns-go, diskman, fancontrol, partexp, quickfile - Storage: qbittorrent, gecoosac - DNS: mosdns, openlist2 - Misc: qmodem, viking (timewol, wolplus) - Speedtest: netspeedtest (+ homebox, speedtest) **Function**: `UPDATE_VERSION` (lines 79-117) Updates package version from GitHub releases: ```bash UPDATE_VERSION "PKG_NAME" [false] # false=stable only, true=includes prerelease ``` Currently updates: `sing-box` --- ### Scripts/Handles.sh **Purpose**: Apply various fixes and customizations to packages **Handles** (in order): | Handle | Target | Action | |--------|--------|--------| | homeproxy | `$PKG_PATH/homeproxy` | Clone surge-rules, generate IP lists | | luci-theme-argon | Theme colors | Set primary color, font weight | | luci-app-aurora-config | Menu style | Set nav_submenu_type to boxed-dropdown | | qca-nss-drv | Init script | Set START=85 | | qca-nss-pbuf | Init script | Set START=86 | | tailscale | Makefile | Remove /files to avoid config conflict | | rust | Makefile | Disable ci-llvm (fix compile fail) | | diskman | Makefile | Remove ntfs-3g-utils dependency | | netspeedtest | Defaults/speedttest | Add exit 0, fix cert bundle | **Variable Dependency**: Uses `$PKG_PATH` (expected to be `$GITHUB_WORKSPACE/wrt/package/`) **Note**: Some handles check directory presence with `if [ -d *"name"* ]` glob pattern --- ### Scripts/Settings.sh **Purpose**: Apply kconfig settings and custom modifications **Areas Modified**: 1. **LuCI**: - Remove luci-app-attendedsysupgrade - Change default theme (via `$WRT_THEME`) - Set immortalwrt.lan IP (via `$WRT_IP`) - Add build date marker (via `$WRT_MARK, $WRT_DATE`) 2. **WiFi** (auto-detect): - Files: `set-wireless.sh` or `mac80211.uc` - Set SSID (`$WRT_SSID`) - Set password (`$WRT_WORD`) - Set country: CN - Set encryption: psk2+ccmp 3. **Network** (via `config_generate`): - Default IP (`$WRT_IP`) - Hostname (`$WRT_NAME`) 4. **General Config additions**: - `CONFIG_PACKAGE_luci=y` - `CONFIG_LUCI_LANG_zh_Hans=y` - Theme packages (via `$WRT_THEME`) 5. **Custom packages** (via `$WRT_PACKAGE`): - If non-empty, append line by line to .config 6. **Qualcomm IPQ60XX specific** (via `$WRT_TARGET, $WRT_CONFIG`): - Disable NSS feeds - Enable sqm-nss - Set NSS firmware version (11.4=n, 12.2 for IPQ50xx, 12.5 for others) - Handle "nowifi" variants by modifying DTS includes - Enable `kmod-usb-serial-qualcomm` **Environment Variables Expected**: - `$WRT_THEME`, `$WRT_IP`, `$WRT_MARK`, `$WRT_DATE` - `$WRT_SSID`, `$WRT_WORD`, `$WRT_NAME` - `$WRT_PACKAGE`, `$WRT_TARGET`, `$WRT_CONFIG` **Important**: This script does NOT declare these variables - they must be set before calling. --- ## How to Modify Packages ### Step 1: Check if package exists in feeds If yes → Add to `Config/GENERAL.local.txt`: ```makefile CONFIG_PACKAGE_luci-app-mypackage=y ``` If no or needs specific version → Edit `Scripts/Packages.sh` ### Step 2: Add package in Scripts/Packages.sh **Basic clone**: ```bash UPDATE_PACKAGE "mypackage" "username/mypackage-repo" "main" ``` **With aliases (to delete conflicting packages)**: ```bash UPDATE_PACKAGE "open-app-filter" "destan19/OpenAppFilter" "master" "" "luci-app-appfilter oaf" ``` **Extract from multi-package repo**: ```bash UPDATE_PACKAGE "openclash" "vernesong/OpenClash" "dev" "pkg" ``` **Rename cloned directory**: ```bash UPDATE_PACKAGE "custom-name" "user/repo" "branch" "name" ``` ### Step 3: Add fixes in Scripts/Handles.sh (if needed) Create a handle block: ```bash if [ -d *"mypackage"* ]; then echo " " cd ./mypackage/ # Apply fixes here with sed/cp/mv cd $PKG_PATH && echo "mypackage has been fixed!" fi ``` ### Step 4: Add config in Settings.sh (optional) If package needs kconfig flags, add after custom packages section: ```bash # My package specific settings echo "CONFIG_PACKAGE_kmod-mymodule=y" >> ./.config ``` --- ## Common Tasks for Agents ### Task: Add a new LuCI app from feeds ```bash # 1. Edit Config/GENERAL.local.txt echo "CONFIG_PACKAGE_luci-app-newapp=y" >> Config/GENERAL.local.txt # 2. Commit changes git add Config/GENERAL.local.txt git commit -m "add: luci-app-newapp" git push ``` ### Task: Add a custom package from GitHub ```bash # 1. Edit Scripts/Packages.sh (Add line 48-76 area) # Add: # UPDATE_PACKAGE "newpackage" "user/newpackage-repo" "main" # 2. If needed, add fix to Scripts/Handles.sh # 3. Commit and push ``` ### Task: Update an existing package version ```bash # 1. Edit Scripts/Settings.sh if version needs kconfig change # 2. Edit Scripts/Packages.sh UPDATE_VERSION call # 3. OR add new UPDATE_VERSION call: # UPDATE_VERSION "newpackage" # 4. Commit and push ``` ### Task: Pin GENERAL.txt to specific commit ```bash # Option A: Add to Woodpecker CI variables # Variable: UPSTREAM_GENERAL_REF = # Option B: Modify sync_upstream_config.sh default # Edit line 6: UPSTREAM_GENERAL_REF="${UPSTREAM_GENERAL_REF:-main}" # Change 'main' to commit hash ``` ### Task: Add a specific device configuration ```bash # 1. Find the device target from openwrt menuconfig # 2. Add to appropriate config file (IPQ60XX-WIFI-YES.txt or IPQ60XX-WIFI.txt): # CONFIG_TARGET_DEVICE_qualcommax_ipq60xx_DEVICE_=y # 3. Commit and push ``` ### Task: Debug build failure ```bash # 1. Check Woodpecker logs # 2. Identify failing step # 3. Review error messages in CI log # 4. Check if upstream GENERAL.upstream.txt has breaking changes # 5. Try pinning to previous commit with UPSTREAM_GENERAL_REF # 6. Add custom override in GENERAL.local.txt ``` --- ## Build Targets Reference ### IPQ60XX-WIFI-YES (21 devices) Source: VIKINGYFY/OpenWRT-CI Devices: - anysafe_e1, cmiot_ax18, glinet_gl-ax1800, glinet_gl-axt1800 - jdcloud_re-cs-02, jdcloud_re-ss-01 - link_nn6000-v1, link_nn6000-v2, linksys_mr7350, linksys_mr7500 - philips_ly1800, qihoo_360v6 - redmi_ax5, redmi_ax5-jdcloud - sy_y6010, xiaomi_ax1800 - zn_m2 ### IPQ60XX-WIFI (9 devices) Source: davidtall/OpenWRT-CI Devices: - cmiot_ax18, jdcloud_re-ss-01, jdcloud_re-cs-02 - qihoo_360v6, redmi_ax5-jdcloud, redmi_ax5 - xiaomi_ax1800, zn_m2 Includes: - Kernel cgroup configs - NSS configurations (disabled feeds, sqm-nss enabled) - Memory profiles (512MB) --- ## Environment Variables Reference ### Woodpecker Pipeline Variables | Variable | Default | Description | |----------|---------|-------------| | WRT_REPO | https://github.com/VIKINGYFY/immortalwrt.git | Firmware source repo | | WRT_BRANCH | main | Firmware source branch | | UPSTREAM_GENERAL_REF | main | GENERAL.txt version | ### Scripts Settings.sh Variables | Variable | Purpose | Example | |----------|---------|---------| | WRT_THEME | LuCI theme name | argon | | WRT_IP | Default IP | 192.168.1.1 | | WRT_MARK | Build marker | Custom | | WRT_DATE | Build date | Date string | | WRT_SSID | WiFi SSID | MyRouter | | WRT_WORD | WiFi password | password123 | | WRT_NAME | Hostname | OpenWrt | | WRT_PACKAGE | Custom packages | Multi-line string | | WRT_TARGET | Target platform | QUALCOMMAX | | WRT_CONFIG | Config variant | ipq60xx | ### Scripts Handles.sh Variables | Variable | Purpose | Expected | |----------|---------|----------| | PKG_PATH | Package path | `$GITHUB_WORKSPACE/wrt/package/` | | GITHUB_WORKSPACE | CI workspace | Set by Woodpecker | --- ## Troubleshooting Guide ### Issue: Build fails after upstream GENERAL.txt update **Symptoms**: New errors appeared without local changes **Causes**: 1. Upstream added conflicting config 2. Upstream removed a package you depended on **Solutions**: ```bash # Fast fix - Pin to previous working commit UPSTREAM_GENERAL_REF= # Permanent fix - Override in GENERAL.local.txt # Add the conflicting option with your desired value ``` ### Issue: $4: unbound variable error **Symptoms**: Packages.sh fails with unbound variable **Cause**: UPDATE_PACKAGE called without 4th/5th parameter using set -u **Solution**: Already fixed in current version using `${4:-}` default ### Issue: Package not found despite being in Packages.sh **Symptoms**: Package missing in final firmware **Checklist**: 1. Verify UPDATE_PACKAGE has correct repo URL 2. Check if branch exists 3. Look for error in Woodpecker git clone logs 4. Verify package name matches expected directory ### Issue: Handles.sh not applying fix **Symptoms**: Expected modifications not present **Checklist**: 1. Verify `$PKG_PATH` is set correctly 2. Check glob pattern matches: `if [ -d *"packagename"* ]` 3. Verify handle is in correct directory after cd --- ## CI/CD Operations ### Triggering Builds | Trigger | Command/UI | |---------|------------| | Manual | Woodpecker UI > Run Pipeline | | Push | git push to main branch | | Cron | Woodpecker UI > Schedules (configured separately) | ### Artifact Locations After successful build: - `artifacts/ipq60xx-wifi-yes/targets/qualcommax/ipq60xx/` - `artifacts/ipq60xx-wifi/targets/qualcommax/ipq60xx/` Contains: - `*-sysupgrade.bin` - Main firmware image - `*-factory.bin` - Factory flash image (if available) - Other device-specific files --- ## Maintenance Guidelines ### When upstream changes break builds 1. **Enable debug**: Add `V=s` to make command (already present) 2. **Identify failure**: Check Woodpecker logs step by step 3. **Pin version**: Set `UPSTREAM_GENERAL_REF` to last known good commit 4. **Report issue**: Document in this AGENTS.md 5. **Create fix**: Add override or modify scripts ### Adding new package categories 1. Group related packages in Packages.sh with comments 2. Document in this file under "Current Packages" 3. Update README.md if user-facing ### Updating this document When making changes that affect this doc: 1. Update relevant sections 2. Increment Last Updated date at top 3. Commit with message: `docs: update AGENTS.md` --- ## Version History | Date | Change | |------|--------| | 2026-03-01 | Initial document created with full project reference | --- ## Quick Command Reference ```bash # Sync upstream config locally (test) cd Scripts && ./sync_upstream_config.sh # Test config merge cat Config/IPQ60XX-WIFI-YES.txt Config/GENERAL.upstream.txt Config/GENERAL.local.txt > test.config # Fix a package in Handles.sh pattern if [ -d *"packagename"* ]; then echo " " cd ./packagename/ # Your fix here cd $PKG_PATH && echo "fixed!" fi # Add package in Packages.sh format UPDATE_PACKAGE "name" "user/repo" "branch" ["mode"] ["alias1 alias2"] # Add feed package to config echo "CONFIG_PACKAGE_luci-app-pkgname=y" >> Config/GENERAL.local.txt ``` --- ## External References - [VIKINGYFY/immortalwrt](https://github.com/VIKINGYFY/immortalwrt) - [VIKINGYFY/OpenWRT-CI](https://github.com/VIKINGYFY/OpenWRT-CI) - [davidtall/OpenWRT-CI](https://github.com/davidtall/OpenWRT-CI) - [Woodpecker CI Docs](https://woodpecker-ci.org/docs) - [ImmortalWrt Build Scripts](https://build-scripts.immortalwrt.org)