Auto-Build New RouterOS Versions #146
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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') |