name: Build CHR Docker on: workflow_dispatch: inputs: version: description: "Specify version (e.g., 7.19.2), empty for latest" required: false default: '' type: string permissions: packages: write jobs: Prepare: runs-on: ubuntu-24.04 outputs: MATRIX: ${{ steps.get_versions.outputs.MATRIX }} steps: - name: Get versions and generate matrix id: get_versions run: | SPECIFIED_VERSION="${{ inputs.version }}" if [ -n "$SPECIFIED_VERSION" ]; then MATRIX=$(jq -c -n ' [ { "name": "specified", "version": env.SPECIFIED_VERSION, "channel": "specified" } ] ' --arg SPECIFIED_VERSION "$SPECIFIED_VERSION") else TIMESTAMP=$(date +%s) LONG_TERM=$(wget -nv -O - "https://upgrade.mikrotik.ltd/routeros/NEWESTa7.long-term?t=$TIMESTAMP" | cut -d ' ' -f1) STABLE=$(wget -nv -O - "https://upgrade.mikrotik.ltd/routeros/NEWESTa7.stable?t=$TIMESTAMP" | cut -d ' ' -f1) echo "Long-term version: $LONG_TERM" echo "Stable version: $STABLE" if [ -z "$LONG_TERM" ]; then echo "Failed to get long-term version" exit 1 fi if [ -z "$STABLE" ]; then echo "Failed to get stable version" exit 1 fi MATRIX=$(jq -c -n ' [ { "name": "long-term", "version": env.LONG_TERM, "channel": "long-term" }, { "name": "stable", "version": env.STABLE, "channel": "stable" } ] ' --arg LONG_TERM "$LONG_TERM" --arg STABLE "$STABLE") fi echo "Generated matrix: $MATRIX" echo "MATRIX=$MATRIX" >> $GITHUB_OUTPUT Build: runs-on: ubuntu-24.04 needs: Prepare strategy: max-parallel: 1 matrix: include: ${{ fromJson(needs.Prepare.outputs.MATRIX) }} env: TZ: 'Asia/Shanghai' VERSION: ${{ matrix.version }} CHANNEL: ${{ matrix.channel }} IMAGE: "ghcr.io/${{ github.repository_owner }}/chr" DOCKER_BUILD_CHECKS_ANNOTATIONS: 'false' DOCKER_BUILD_SUMMARY: 'false' DOCKER_BUILD_RECORD_UPLOAD: 'false' steps: - name: Show build info run: | echo "Building: ${{ matrix.name }}" echo "Version: ${{ env.VERSION }}" echo "Channel: ${{ env.CHANNEL }}" - name: Download CHR image run: | # x86 架构 mkdir -p "amd64" wget -nv -O "amd64/chr-$VERSION.img.zip" \ "https://github.com/elseif/MikroTikPatch/releases/download/$VERSION/chr-$VERSION.img.zip" unzip "amd64/chr-$VERSION.img.zip" -d "amd64/" rm "amd64/chr-$VERSION.img.zip" # arm64 架构 mkdir -p "arm64" wget -nv -O "arm64/chr-$VERSION-arm64.img.zip" \ "https://github.com/elseif/MikroTikPatch/releases/download/$VERSION-arm64/chr-$VERSION-arm64.img.zip" unzip "arm64/chr-$VERSION-arm64.img.zip" -d "arm64/" rm "arm64/chr-$VERSION-arm64.img.zip" ls -la amd64/ arm64/ - name: Create start.sh run: | cat <<'EOF' > start.sh #!/bin/sh set -e MEM="${MEM:=512m}" DISK="${DISK:=128M}" CPUS="${CPUS:=1}" bridge_index=0 get_mac_address() { ifname="$1" ip link show "$ifname" 2>/dev/null | awk '/link\/ether/ {print $2}' } increment_mac() { mac=$1 mac_no_colons=$(echo "$mac" | tr -d ':' | tr '[:lower:]' '[:upper:]') mac_dec=$((16#${mac_no_colons})) mac_dec=$((mac_dec + 1)) mac_hex=$(printf "%012X" "$mac_dec") echo "$mac_hex" | sed 's/.\{2\}/&:/g;s/:$//' } echo "" > /etc/qemu/bridge.conf qemu="qemu-system-x86_64" if [[ `uname -m` == "aarch64" ]]; then qemu="qemu-system-aarch64 -pflash /usr/share/qemu/edk2-aarch64-code.fd -M virt" fi if [ -e /dev/kvm ]; then qemu="$qemu -enable-kvm -cpu host" else if [[ `uname -m` == "aarch64" ]]; then qemu="$qemu -cpu cortex-a57" fi fi img=`ls /diskimage/*.img` qemu="$qemu -nographic -m $MEM -smp cpus=$CPUS -drive file=$img,format=raw" truncate -s $DISK $img while read -r line; do iface=$(echo "$line" | sed "s/ *\([a-z0-9]*\):.*/\1/") if [[ "$iface" =~ " " ]]; then continue fi if [[ "$iface" =~ ^br ]]; then continue fi if [[ "$iface" == "lo" ]]; then continue fi if [[ "$iface" == "" ]]; then continue fi echo "iface: $iface" bridge="br$bridge_index" ip link add name "$bridge" type bridge ip link set dev "$bridge" up ip link set "$iface" master "$bridge" echo "allow $bridge" >> /etc/qemu/bridge.conf veth_mac=$(get_mac_address "$iface") tap_mac=$(increment_mac "$veth_mac") bridge_index=$((bridge_index + 1)) qemu="$qemu -netdev bridge,id=$bridge,br=$bridge -device virtio-net,netdev=$bridge,mac=$tap_mac " done < /proc/net/dev echo "qemu: $qemu" exec $qemu EOF chmod +x start.sh - name: Create Dockerfile run: | cat > Dockerfile << 'EOF' FROM alpine:latest ARG TARGETARCH ARG VERSION RUN case "$TARGETARCH" in \ amd64) apk add --no-cache qemu-system-x86_64 ;; \ arm64) apk add --no-cache qemu-system-aarch64 ;; \ esac \ && rm -rf /var/cache/apk/* RUN mkdir -p /diskimage COPY ${TARGETARCH}/*.img /diskimage/ COPY start.sh /start.sh ENTRYPOINT ["/start.sh"] EOF - name: Log in to GitHub Container Registry uses: docker/login-action@v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Set up QEMU uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 - name: Build and Push uses: docker/build-push-action@v7 with: context: . platforms: linux/amd64,linux/arm64 push: true tags: ${{ env.IMAGE }}:${{ env.VERSION }} build-args: VERSION=${{ env.VERSION }} cache-from: type=gha cache-to: type=gha,mode=max provenance: false sbom: false - name: Update channel tags if: matrix.name != 'specified' run: | TAGS="-t ${{ env.IMAGE }}:${{ env.CHANNEL }}" if [ "${{ env.CHANNEL }}" = "stable" ]; then TAGS="$TAGS -t ${{ env.IMAGE }}:latest" fi echo "Creating tags: $TAGS" docker buildx imagetools create $TAGS ${{ env.IMAGE }}:${{ env.VERSION }} - name: Save Docker Image as tar run: | mkdir -p artifacts echo "Exporting amd64 tar..." docker pull --platform linux/amd64 ${{ env.IMAGE }}:${{ env.VERSION }} docker save ${{ env.IMAGE }}:${{ env.VERSION }} -o artifacts/chr-${{ env.VERSION }}-amd64.tar echo "Exporting arm64 tar..." docker pull --platform linux/arm64 ${{ env.IMAGE }}:${{ env.VERSION }} docker save ${{ env.IMAGE }}:${{ env.VERSION }} -o artifacts/chr-${{ env.VERSION }}-arm64.tar ls -lh artifacts/ - name: Upload amd64 tar artifact uses: actions/upload-artifact@v7 with: name: chr-${{ env.VERSION }}-amd64 path: artifacts/chr-${{ env.VERSION }}-amd64.tar archive: false - name: Upload arm64 tar artifact uses: actions/upload-artifact@v7 with: name: chr-${{ env.VERSION }}-arm64 path: artifacts/chr-${{ env.VERSION }}-arm64.tar archive: false