mirror of
https://github.com/elseif/MikroTikPatch.git
synced 2026-09-04 22:45:47 +00:00
274 lines
9.2 KiB
YAML
274 lines
9.2 KiB
YAML
|
|
name: Patch Mikrotik RouterOS
|
|
on:
|
|
schedule:
|
|
- cron: "25 * * * *" # Every hour at minute 25 (UTC)
|
|
workflow_dispatch:
|
|
inputs:
|
|
stable:
|
|
description: 'Build stable channel'
|
|
required: false
|
|
type: boolean
|
|
default: true
|
|
long_term:
|
|
description: 'Build long-term channel'
|
|
required: false
|
|
type: boolean
|
|
default: false
|
|
development:
|
|
description: 'Build development channel'
|
|
required: false
|
|
type: boolean
|
|
default: false
|
|
testing:
|
|
description: 'Build testing channel'
|
|
required: false
|
|
type: boolean
|
|
default: false
|
|
arch:
|
|
description: 'Architectures (x86, arm64)'
|
|
required: true
|
|
default: 'all'
|
|
type: choice
|
|
options:
|
|
- all
|
|
- x86
|
|
- arm64
|
|
major_version:
|
|
description: 'RouterOS major version'
|
|
required: true
|
|
default: 'all'
|
|
type: choice
|
|
options:
|
|
- all
|
|
- v6
|
|
- v7
|
|
version:
|
|
description: '(e.g. 7.xx.x), empty for latest'
|
|
required: false
|
|
default: ''
|
|
type: string
|
|
buildtime:
|
|
description: "build imestamp, leave empty for original"
|
|
required: false
|
|
default: ''
|
|
type: string
|
|
release:
|
|
description: 'Release'
|
|
required: false
|
|
default: false
|
|
type: boolean
|
|
force:
|
|
description: 'Force patch even if already patched'
|
|
required: false
|
|
default: false
|
|
type: boolean
|
|
|
|
|
|
|
|
jobs:
|
|
|
|
Prepare_Patch:
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
upgrades_v6: ${{ steps.prepare.outputs.upgrades_v6 }}
|
|
upgrades_v7: ${{ steps.prepare.outputs.upgrades_v7 }}
|
|
patch_v6: ${{ steps.prepare.outputs.patch_v6 }}
|
|
patch_v7: ${{ steps.prepare.outputs.patch_v7 }}
|
|
archs: ${{ steps.prepare.outputs.archs }}
|
|
steps:
|
|
- name: Prepare Patch
|
|
id: prepare
|
|
run: |
|
|
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
|
channels=()
|
|
[[ "${{ inputs.stable }}" == "true" ]] && channels+=("stable")
|
|
[[ "${{ inputs.long_term }}" == "true" ]] && channels+=("long-term")
|
|
[[ "${{ inputs.development }}" == "true" ]] && channels+=("development")
|
|
[[ "${{ inputs.testing }}" == "true" ]] && channels+=("testing")
|
|
if [ ${#channels[@]} -eq 0 ]; then
|
|
echo "ERROR: At least one channel must be selected"
|
|
exit 1
|
|
fi
|
|
CHANNELS=$(printf '%s\n' "${channels[@]}" | jq -Rsc 'split("\n")[:-1]')
|
|
VERSION_INPUT='${{ inputs.version }}'
|
|
MAJOR_VERSION='${{ inputs.major_version }}'
|
|
FORCE="${{ inputs.force || 'false' }}"
|
|
|
|
if [[ -n "$VERSION_INPUT" && "$MAJOR_VERSION" != "all" ]]; then
|
|
case "$MAJOR_VERSION" in
|
|
v6)
|
|
if [[ ! "$VERSION_INPUT" =~ ^6\. ]]; then
|
|
echo "ERROR: $VERSION_INPUT is not RouterOS v6"
|
|
exit 1
|
|
fi
|
|
;;
|
|
v7)
|
|
if [[ ! "$VERSION_INPUT" =~ ^7\. ]]; then
|
|
echo "ERROR: $VERSION_INPUT is not RouterOS v7"
|
|
exit 1
|
|
fi
|
|
;;
|
|
esac
|
|
fi
|
|
else
|
|
CHANNELS='["stable","long-term","development","testing"]'
|
|
VERSION_INPUT=""
|
|
MAJOR_VERSION="all"
|
|
FORCE="false"
|
|
fi
|
|
|
|
ARCH_INPUT="${{ inputs.arch || 'all' }}"
|
|
|
|
if [[ "$ARCH_INPUT" == "all" ]]; then
|
|
if [[ "$MAJOR_VERSION" == "v6" ]]; then
|
|
ARCHS='["x86"]'
|
|
else
|
|
ARCHS='["x86","arm64"]'
|
|
fi
|
|
else
|
|
if [[ "$ARCH_INPUT" == "arm64" && "$MAJOR_VERSION" == "v6" ]]; then
|
|
echo "ERROR: RouterOS v6 does not support arm64"
|
|
exit 1
|
|
fi
|
|
ARCHS=$(jq -c -n --arg arch "$ARCH_INPUT" '[$arch]')
|
|
fi
|
|
|
|
echo "Major Version: $MAJOR_VERSION"
|
|
echo "Force: $FORCE"
|
|
echo "Architectures:"
|
|
echo "$ARCHS" | jq .
|
|
echo "Channels:"
|
|
echo "$CHANNELS" | jq .
|
|
|
|
|
|
if [[ -n "$VERSION_INPUT" ]]; then
|
|
echo "Using fixed version: $VERSION_INPUT"
|
|
CANDIDATES=$(jq -cn --arg version "$VERSION_INPUT" --argjson channels "$CHANNELS" '
|
|
[{ version:$version, channels:$channels}]
|
|
')
|
|
else
|
|
echo "Resolving version from channels"
|
|
OFFICIAL_UPGRADE=$(curl -fsSL https://upgrade.mikrotik.com/routeros/upgrade.json) || { echo "ERROR: Failed to fetch OFFICIAL_UPGRADE" >&2; exit 1; }
|
|
echo "$OFFICIAL_UPGRADE" | jq -e . >/dev/null 2>&1 || { echo "ERROR: OFFICIAL_UPGRADE is not valid JSON" >&2; exit 1; }
|
|
|
|
CANDIDATES=$( echo "$OFFICIAL_UPGRADE" |
|
|
jq -c --argjson channels "$CHANNELS" --arg major "$MAJOR_VERSION" '
|
|
[
|
|
$channels[] as $c |
|
|
if $major == "v6" then
|
|
{
|
|
version: .upgradePaths["NEWEST6."+ $c].version,
|
|
channel: $c
|
|
}
|
|
elif $major == "v7" then
|
|
{
|
|
version: .upgradePaths["NEWESTa7."+ $c].version,
|
|
channel: $c
|
|
}
|
|
else
|
|
[
|
|
{
|
|
version: .upgradePaths["NEWEST6."+ $c].version,
|
|
channel: $c
|
|
},
|
|
{
|
|
version: .upgradePaths["NEWESTa7."+ $c].version,
|
|
channel: $c
|
|
}
|
|
]
|
|
end
|
|
] |
|
|
flatten |
|
|
map(select(.version != null)) |
|
|
sort_by(.version) |
|
|
group_by(.version) |
|
|
map({
|
|
version: .[0].version,
|
|
channels: map(.channel)
|
|
})
|
|
'
|
|
)
|
|
fi
|
|
|
|
echo "Candidates:"
|
|
echo "$CANDIDATES" | jq .
|
|
|
|
if [[ "$FORCE" == "true" ]]; then
|
|
echo "Force mode enabled: skip patched version check"
|
|
else
|
|
echo "Checking patched versions"
|
|
PATCH_UPGRADE=$(curl -fsSL https://upgrade.mikrotik.ltd/routeros/upgrade.json) || { echo "ERROR: Failed to fetch PATCH_UPGRADE" >&2; exit 1; }
|
|
echo "$PATCH_UPGRADE" | jq -e . >/dev/null 2>&1 || { echo "ERROR: PATCH_UPGRADE is not valid JSON" >&2; exit 1; }
|
|
|
|
PATCH_VERSIONS=$(echo "$PATCH_UPGRADE" | jq -c '
|
|
[.upgradePaths[].version] | unique
|
|
')
|
|
|
|
ALREADY_PATCHED=$(echo "$CANDIDATES" | jq -c --argjson patched "$PATCH_VERSIONS" '
|
|
map(select(
|
|
(.version as $v | ($patched | index($v))) != null
|
|
))
|
|
')
|
|
|
|
echo "Already patched:"
|
|
echo "$ALREADY_PATCHED" | jq .
|
|
|
|
|
|
CANDIDATES=$(echo "$CANDIDATES" | jq -c --argjson patched "$PATCH_VERSIONS" '
|
|
map(select(
|
|
(.version as $v | ($patched | index($v))) == null
|
|
))
|
|
')
|
|
fi
|
|
|
|
V6_MATRIX=$(echo "$CANDIDATES" | jq -c 'map(select(.version | startswith("6.")))')
|
|
V7_MATRIX=$(echo "$CANDIDATES" | jq -c 'map(select(.version | startswith("7.")))')
|
|
|
|
echo "upgrades_v6=$V6_MATRIX" >> "$GITHUB_OUTPUT"
|
|
echo "upgrades_v7=$V7_MATRIX" >> "$GITHUB_OUTPUT"
|
|
echo "archs=$ARCHS" >> "$GITHUB_OUTPUT"
|
|
|
|
echo "patch_v6=$(jq 'length > 0' <<< "$V6_MATRIX")" >> "$GITHUB_OUTPUT"
|
|
echo "patch_v7=$(jq 'length > 0' <<< "$V7_MATRIX")" >> "$GITHUB_OUTPUT"
|
|
|
|
echo "=== Upgrades V6 ==="
|
|
echo "$V6_MATRIX" | jq .
|
|
|
|
echo "=== Upgrades V7 ==="
|
|
echo "$V7_MATRIX" | jq .
|
|
|
|
echo "=== Architectures ==="
|
|
echo "$ARCHS" | jq .
|
|
|
|
Patch_V6:
|
|
needs: Prepare_Patch
|
|
if: needs.Prepare_Patch.result == 'success' && needs.Prepare_Patch.outputs.patch_v6 == 'true'
|
|
uses: ./.github/workflows/patch_v6.yml
|
|
strategy:
|
|
matrix:
|
|
target: ${{ fromJson(needs.Prepare_Patch.outputs.upgrades_v6) }}
|
|
fail-fast: false
|
|
with:
|
|
version: ${{ matrix.target.version }}
|
|
channels: ${{ toJson(matrix.target.channels) }}
|
|
archs: ${{ needs.Prepare_Patch.outputs.archs }}
|
|
buildtime: ${{ inputs.buildtime }}
|
|
release: ${{ github.event_name != 'workflow_dispatch' || inputs.release }}
|
|
secrets: inherit
|
|
|
|
Patch_V7:
|
|
needs: Prepare_Patch
|
|
if: needs.Prepare_Patch.result == 'success' && needs.Prepare_Patch.outputs.patch_v7 == 'true'
|
|
uses: ./.github/workflows/patch_v7.yml
|
|
strategy:
|
|
matrix:
|
|
target: ${{ fromJson(needs.Prepare_Patch.outputs.upgrades_v7) }}
|
|
fail-fast: false
|
|
with:
|
|
version: ${{ matrix.target.version }}
|
|
channels: ${{ toJson(matrix.target.channels) }}
|
|
archs: ${{ needs.Prepare_Patch.outputs.archs }}
|
|
buildtime: ${{ inputs.buildtime }}
|
|
release: ${{ github.event_name != 'workflow_dispatch' || inputs.release }}
|
|
secrets: inherit |