602 lines
18 KiB
Markdown
602 lines
18 KiB
Markdown
# 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 = <commit-hash>
|
|
|
|
# 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_<device_name>=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=<previous-commit-hash>
|
|
|
|
# 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)
|