Skip to content

Auto-Build New RouterOS Versions #146

Auto-Build New RouterOS Versions

Auto-Build New RouterOS Versions #146

Workflow file for this run

name: "Auto-Build New RouterOS Versions"
# Runs daily to check for new RouterOS versions and trigger schema builds automatically.
# This is the "one stop shop" — it checks ALL build artifacts for every current release
# channel version and dispatches builds for any that are missing:
# - Base schema: docs/{version}/schema.raml
# - Extra-packages schema: docs/{version}/extra/schema.raml
# - /app YAML schemas: docs/{version}/routeros-app-yaml-schema.json
# - Deep-inspect multi-arch: docs/{version}/extra/deep-inspect.{x86,arm64}.json
#
# Can also be triggered manually to retry/fix any missing builds.
#
# MikroTik publishes the latest version for each channel at:
# https://upgrade.mikrotik.com/routeros/NEWESTa7.<channel>
# where <channel> is one of: stable, testing, development, long-term
#
# Stable releases (e.g. "7.22") are hosted on download.mikrotik.com.
# Beta/rc/testing releases (e.g. "7.22rc2", "7.22beta4") are on cdn.mikrotik.com.
on:
workflow_dispatch:
inputs:
skip_versions:
description: >
Comma-separated list of RouterOS versions to skip ALL builds for.
Use this for beta/RC versions with known schema issues that are pending
a fix from MikroTik (e.g. duplicate YAML keys, array-typed env vars).
Versions here are excluded from base, extra-packages, /app YAML schema,
and deep-inspect multi-arch builds.
required: false
default: "7.23rc1,7.23beta5,7.23beta4"
schedule:
- cron: "0 4 * * *"
jobs:
check:
runs-on: ubuntu-latest
name: Check for New RouterOS Versions
permissions:
contents: read
outputs:
base_versions: ${{ steps.needs-builds.outputs.base_versions }}
extra_versions: ${{ steps.needs-builds.outputs.extra_versions }}
app_schema_versions: ${{ steps.needs-builds.outputs.app_schema_versions }}
deep_inspect_versions: ${{ steps.needs-builds.outputs.deep_inspect_versions }}
skipped_versions: ${{ steps.needs-builds.outputs.skipped_versions }}
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Get RouterOS "stable" channel version
id: get-stable
# Stable channel typically has the highest production-ready version.
# Fail-fast: stable must always be available.
run: |
ROSVER=$(curl -sS --max-time 10 --fail https://upgrade.mikrotik.com/routeros/NEWESTa7.stable | awk '{print $1}')
[ -z "$ROSVER" ] && echo "::error::Failed to get stable channel version" && exit 1
echo "version=$ROSVER" >> $GITHUB_OUTPUT
echo "Stable: $ROSVER"
- name: Get RouterOS "testing" channel version
id: get-testing
# Testing channel has rc/beta versions being prepared for stable release.
# May not always differ from stable; allow failure gracefully.
continue-on-error: true
run: |
ROSVER=$(curl -sS --max-time 10 --fail https://upgrade.mikrotik.com/routeros/NEWESTa7.testing | awk '{print $1}')
[ -z "$ROSVER" ] && echo "::warning::No version returned for testing channel, skipping" && exit 0
echo "version=$ROSVER" >> $GITHUB_OUTPUT
echo "Testing: $ROSVER"
- name: Get RouterOS "development" channel version
id: get-development
# Development channel has the latest bleeding-edge builds (beta/nightly).
# May not always return a version; allow failure gracefully.
continue-on-error: true
run: |
ROSVER=$(curl -sS --max-time 10 --fail https://upgrade.mikrotik.com/routeros/NEWESTa7.development | awk '{print $1}')
[ -z "$ROSVER" ] && echo "::warning::No version returned for development channel, skipping" && exit 0
echo "version=$ROSVER" >> $GITHUB_OUTPUT
echo "Development: $ROSVER"
- name: Get RouterOS "long-term" channel version
id: get-longterm
# Long-term channel provides older, stable releases for conservative deployments.
# May not always differ from stable; allow failure gracefully.
continue-on-error: true
run: |
ROSVER=$(curl -sS --max-time 10 --fail https://upgrade.mikrotik.com/routeros/NEWESTa7.long-term | awk '{print $1}')
[ -z "$ROSVER" ] && echo "::warning::No version returned for long-term channel, skipping" && exit 0
echo "version=$ROSVER" >> $GITHUB_OUTPUT
echo "Long-term: $ROSVER"
- name: Determine which builds are needed
# Checks all build artifacts for each channel version:
# 1. docs/{version}/schema.raml + openapi.json — base schema (triggers base build)
# 2. docs/{version}/extra/schema.raml + extra/openapi.json — extra-packages schema (triggers extra build)
# 3. docs/{version}/routeros-app-yaml-schema.json — /app YAML schemas (triggers app schema build)
# 4. docs/{version}/extra/deep-inspect.{x86,arm64}.json + diff-deep-inspect.json — multi-arch enrichment
# Each is tracked independently so a failed extra build can be retried without
# re-running the base build. Deduplication is applied so the same version reported
# by multiple channels only triggers one build per artifact type.
#
# Versions in skip_versions input are excluded from ALL build types. Use this for
# beta/RC releases with known upstream issues (e.g. duplicate YAML keys, invalid app YAML)
# that are pending a fix from MikroTik rather than a schema change.
# Manual dispatch defaults can be broader for historical backfill; the scheduled
# fallback stays narrow so current channels can recover automatically.
id: needs-builds
env:
STABLE: ${{ steps.get-stable.outputs.version }}
TESTING: ${{ steps.get-testing.outputs.version }}
DEVELOPMENT: ${{ steps.get-development.outputs.version }}
LONGTERM: ${{ steps.get-longterm.outputs.version }}
SKIP_VERSIONS_INPUT: ${{ github.event_name == 'schedule' && '7.23beta5' || inputs.skip_versions }}
run: |
BASE_VERSIONS=()
EXTRA_VERSIONS=()
APP_SCHEMA_VERSIONS=()
DEEP_INSPECT_VERSIONS=()
SKIPPED_VERSIONS=()
# Parse the skip list (comma-separated, trim whitespace)
SKIP_LIST=()
IFS=',' read -ra SKIP_RAW <<< "$SKIP_VERSIONS_INPUT"
for S in "${SKIP_RAW[@]}"; do
S=$(echo "$S" | tr -d ' ')
[ -n "$S" ] && SKIP_LIST+=("$S")
done
# Helper: returns 0 (true) if version is in skip list
is_skipped() {
local ver="$1"
for s in "${SKIP_LIST[@]}"; do
[ "$s" = "$ver" ] && return 0
done
return 1
}
# Collect unique versions across all channels
UNIQUE_VERSIONS=()
for VER in "$STABLE" "$TESTING" "$DEVELOPMENT" "$LONGTERM"; do
[ -z "$VER" ] && continue
ALREADY=false
for V in "${UNIQUE_VERSIONS[@]}"; do [ "$V" = "$VER" ] && ALREADY=true && break; done
$ALREADY && continue
UNIQUE_VERSIONS+=("$VER")
done
for VER in "${UNIQUE_VERSIONS[@]}"; do
# Skip versions explicitly listed in skip_versions input
if is_skipped "$VER"; then
echo "$VER is in skip list — skipping all builds for this version"
SKIPPED_VERSIONS+=("$VER")
continue
fi
# Check base schema (schema.raml + openapi.json)
if [ ! -f "docs/${VER}/schema.raml" ] || [ ! -f "docs/${VER}/openapi.json" ]; then
echo "docs/${VER}/schema.raml or openapi.json not found — $VER needs base build"
BASE_VERSIONS+=("$VER")
else
echo "docs/${VER}/schema.raml + openapi.json exist — base OK"
fi
# Check extra-packages schema (extra/schema.raml + extra/openapi.json)
if [ ! -f "docs/${VER}/extra/schema.raml" ] || [ ! -f "docs/${VER}/extra/openapi.json" ]; then
echo "docs/${VER}/extra/schema.raml or extra/openapi.json not found — $VER needs extra-packages build"
EXTRA_VERSIONS+=("$VER")
else
echo "docs/${VER}/extra/schema.raml + extra/openapi.json exist — extra OK"
fi
# Check /app YAML schema
if [ ! -f "docs/${VER}/routeros-app-yaml-schema.json" ]; then
echo "docs/${VER}/routeros-app-yaml-schema.json not found — $VER needs app-schema build"
APP_SCHEMA_VERSIONS+=("$VER")
else
echo "docs/${VER}/routeros-app-yaml-schema.json exists — app-schema OK"
fi
# Check deep-inspect artifacts (requires extra-packages)
if [ ! -f "docs/${VER}/extra/deep-inspect.x86.json" ] || [ ! -f "docs/${VER}/extra/deep-inspect.arm64.json" ] || [ ! -f "docs/${VER}/extra/diff-deep-inspect.json" ]; then
echo "docs/${VER}/extra/deep-inspect.{x86,arm64}.json or diff-deep-inspect.json not found — $VER needs deep-inspect build"
DEEP_INSPECT_VERSIONS+=("$VER")
else
echo "docs/${VER}/extra/deep-inspect.{x86,arm64}.json + diff-deep-inspect.json exist — deep-inspect OK"
fi
done
# Use jq to produce properly-formatted JSON arrays
to_json() {
local -n arr=$1
if [ ${#arr[@]} -eq 0 ]; then echo "[]"
else printf '%s\n' "${arr[@]}" | jq -R . | jq -sc .
fi
}
BASE_JSON=$(to_json BASE_VERSIONS)
EXTRA_JSON=$(to_json EXTRA_VERSIONS)
APP_JSON=$(to_json APP_SCHEMA_VERSIONS)
DEEP_INSPECT_JSON=$(to_json DEEP_INSPECT_VERSIONS)
SKIPPED_JSON=$(to_json SKIPPED_VERSIONS)
echo "base_versions=${BASE_JSON}" >> $GITHUB_OUTPUT
echo "extra_versions=${EXTRA_JSON}" >> $GITHUB_OUTPUT
echo "app_schema_versions=${APP_JSON}" >> $GITHUB_OUTPUT
echo "deep_inspect_versions=${DEEP_INSPECT_JSON}" >> $GITHUB_OUTPUT
echo "skipped_versions=${SKIPPED_JSON}" >> $GITHUB_OUTPUT
echo "Skipped versions: ${SKIPPED_JSON}"
echo "Base builds needed: ${BASE_JSON}"
echo "Extra-packages builds needed: ${EXTRA_JSON}"
echo "/app YAML schema builds needed: ${APP_JSON}"
echo "Deep-inspect builds needed: ${DEEP_INSPECT_JSON}"
- name: Summary
env:
STABLE: ${{ steps.get-stable.outputs.version }}
TESTING: ${{ steps.get-testing.outputs.version }}
DEVELOPMENT: ${{ steps.get-development.outputs.version }}
LONGTERM: ${{ steps.get-longterm.outputs.version }}
run: |
echo "### Channel Versions" >> $GITHUB_STEP_SUMMARY
echo "| Channel | Version |" >> $GITHUB_STEP_SUMMARY
echo "|---|---|" >> $GITHUB_STEP_SUMMARY
echo "| Stable | $STABLE |" >> $GITHUB_STEP_SUMMARY
echo "| Testing | $TESTING |" >> $GITHUB_STEP_SUMMARY
echo "| Development | $DEVELOPMENT |" >> $GITHUB_STEP_SUMMARY
echo "| Long-term | $LONGTERM |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Builds Needed" >> $GITHUB_STEP_SUMMARY
echo "- Base: ${{ steps.needs-builds.outputs.base_versions }}" >> $GITHUB_STEP_SUMMARY
echo "- Extra-packages: ${{ steps.needs-builds.outputs.extra_versions }}" >> $GITHUB_STEP_SUMMARY
echo "- /app YAML schemas: ${{ steps.needs-builds.outputs.app_schema_versions }}" >> $GITHUB_STEP_SUMMARY
echo "- Deep-inspect multi-arch: ${{ steps.needs-builds.outputs.deep_inspect_versions }}" >> $GITHUB_STEP_SUMMARY
SKIPPED='${{ steps.needs-builds.outputs.skipped_versions }}'
if [ "$SKIPPED" != "[]" ] && [ -n "$SKIPPED" ]; then
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Skipped Versions" >> $GITHUB_STEP_SUMMARY
echo "> Versions below were excluded from all builds (see \`skip_versions\` input)." >> $GITHUB_STEP_SUMMARY
echo "- $SKIPPED" >> $GITHUB_STEP_SUMMARY
fi
build-base:
needs: check
runs-on: ubuntu-latest
permissions:
actions: write
contents: read
if: ${{ fromJSON(needs.check.outputs.base_versions)[0] != null }}
strategy:
matrix:
version: ${{ fromJSON(needs.check.outputs.base_versions) }}
max-parallel: 1
name: "Trigger base build: ${{ matrix.version }}"
steps:
- name: Dispatch base schema build for ${{ matrix.version }}
uses: actions/github-script@v9
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
console.log(`Triggering base schema build for RouterOS ${{ matrix.version }}`)
await github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: 'manual-using-docker-in-docker.yaml',
ref: 'main',
inputs: {
rosver: '${{ matrix.version }}'
}
})
console.log('Dispatched successfully')
build-extra:
needs: check
runs-on: ubuntu-latest
permissions:
actions: write
contents: read
# Runs independently of build-base — checks its own list of versions missing
# docs/{version}/extra/schema.raml. This means a failed extra build can be
# retried by re-running auto.yaml without re-running the base build.
if: ${{ fromJSON(needs.check.outputs.extra_versions)[0] != null }}
strategy:
matrix:
version: ${{ fromJSON(needs.check.outputs.extra_versions) }}
max-parallel: 1
name: "Trigger extra-packages build: ${{ matrix.version }}"
steps:
- name: Dispatch extra-packages schema build for ${{ matrix.version }}
uses: actions/github-script@v9
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
console.log(`Triggering extra-packages schema build for RouterOS ${{ matrix.version }}`)
await github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: 'manual-using-extra-docker-in-docker.yaml',
ref: 'main',
inputs: {
rosver: '${{ matrix.version }}'
}
})
console.log('Dispatched successfully')
build-app-yaml-schemas:
needs: check
runs-on: ubuntu-latest
permissions:
actions: write
contents: read
# Runs independently — checks its own list of versions missing
# docs/{version}/routeros-app-yaml-schema.json.
if: ${{ fromJSON(needs.check.outputs.app_schema_versions)[0] != null }}
strategy:
matrix:
version: ${{ fromJSON(needs.check.outputs.app_schema_versions) }}
max-parallel: 1
name: "Trigger /app YAML schema build: ${{ matrix.version }}"
steps:
- name: Dispatch /app YAML schema build for ${{ matrix.version }}
uses: actions/github-script@v9
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
console.log(`Triggering /app YAML schema build for RouterOS ${{ matrix.version }}`)
await github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: 'appyamlschemas.yaml',
ref: 'main',
inputs: {
rosver: '${{ matrix.version }}'
}
})
console.log('Dispatched successfully')
build-deep-inspect:
needs: check
runs-on: ubuntu-latest
permissions:
actions: write
contents: read
# Runs independently — checks its own list of versions missing
# docs/{version}/extra/deep-inspect.x86.json, deep-inspect.arm64.json,
# or diff-deep-inspect.json.
if: ${{ fromJSON(needs.check.outputs.deep_inspect_versions)[0] != null }}
strategy:
matrix:
version: ${{ fromJSON(needs.check.outputs.deep_inspect_versions) }}
max-parallel: 1
name: "Trigger deep-inspect build: ${{ matrix.version }}"
steps:
- name: Dispatch deep-inspect multi-arch build for ${{ matrix.version }}
uses: actions/github-script@v9
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
console.log(`Triggering deep-inspect multi-arch build for RouterOS ${{ matrix.version }}`)
await github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: 'deep-inspect-multi-arch.yaml',
ref: 'main',
inputs: {
rosver: '${{ matrix.version }}'
}
})
console.log('Dispatched successfully')