ci: add Coverity Scan scheduled workflow
[openwrt/openwrt.git] / .github / workflows / build.yml
index 511d9596fa21db3850057d5d1c304e00f4d8a3b4..8744bc7737a6635a28754037572420fb11651999 100644 (file)
@@ -2,6 +2,8 @@ name: Build sub target
 
 on:
   workflow_call:
+    secrets:
+      coverity_api_token:
     inputs:
       target:
         required: true
@@ -14,19 +16,41 @@ on:
         type: boolean
       build_full:
         type: boolean
+      build_kernel:
+        type: boolean
       build_all_modules:
         type: boolean
       build_all_kmods:
         type: boolean
       build_all_boards:
         type: boolean
+      use_openwrt_container:
+        type: boolean
+        default: true
+      coverity_project_name:
+        type: string
+        default: OpenWrt
+      coverity_check_packages:
+        type: string
+      coverity_compiler_template_list:
+        type: string
+        default: >-
+          arm-openwrt-linux-gcc
+      coverity_force_compile_packages:
+        type: string
+        default: >-
+          curl
+          libnl
+          mbedtls
+          wolfssl
+          openssl
 
 permissions:
   contents: read
 
 jobs:
   setup_build:
-    name: Setup build
+    name: Setup build ${{ inputs.target }}
     runs-on: ubuntu-latest
     outputs:
       owner_lc: ${{ steps.lower_owner.outputs.owner_lc }}
@@ -42,6 +66,11 @@ jobs:
         run: |
           OWNER_LC=$(echo "${{ github.repository_owner }}" \
             | tr '[:upper:]' '[:lower:]')
+
+          if [ ${{ inputs.use_openwrt_container }} == "true" ]; then
+            OWNER_LC=openwrt
+          fi
+
           echo "owner_lc=$OWNER_LC" >> $GITHUB_OUTPUT
 
       - name: Generate ccache hash
@@ -62,23 +91,25 @@ jobs:
         run: |
           CONTAINER_TAG=latest
           if [ -n "${{ github.base_ref }}" ]; then
-            if echo "${{ github.base_ref }}" | grep -q -E 'openwrt-[0-9][0-9]\.[0-9][0-9]'; then
+            if echo "${{ github.base_ref }}" | grep -q -E '^openwrt-[0-9][0-9]\.[0-9][0-9]$'; then
               CONTAINER_TAG="${{ github.base_ref }}"
             fi
           elif [ ${{ github.ref_type }} == "branch" ]; then
-            if echo "${{ github.ref_name }}" | grep -q -E 'openwrt-[0-9][0-9]\.[0-9][0-9]-'; then
+            if echo "${{ github.ref_name }}" | grep -q -E '^openwrt-[0-9][0-9]\.[0-9][0-9]$'; then
+              CONTAINER_TAG=${{ github.ref_name }}
+            elif echo "${{ github.ref_name }}" | grep -q -E '^openwrt-[0-9][0-9]\.[0-9][0-9]-'; then
               CONTAINER_TAG="$(echo ${{ github.ref_name }} | sed 's/^\(openwrt-[0-9][0-9]\.[0-9][0-9]\)-.*/\1/')"
             fi
           elif [ ${{ github.ref_type }} == "tag" ]; then
-            if echo "${{ github.ref_name }}" | grep -q -E 'v[0-9][0-9]\.[0-9][0-9]\..+'; then
-              CONTAINER_TAG=openwrt-"$(echo ${{ github.ref_name }} | sed 's/v\([0-9][0-9]\.[0-9][0-9]\)\..\+/\1/')"
+            if echo "${{ github.ref_name }}" | grep -q -E '^v[0-9][0-9]\.[0-9][0-9]\..+'; then
+              CONTAINER_TAG=openwrt-"$(echo ${{ github.ref_name }} | sed 's/^v\([0-9][0-9]\.[0-9][0-9]\)\..\+/\1/')"
             fi
           fi
           echo "Tools container to use tools:$CONTAINER_TAG"
           echo "container_tag=$CONTAINER_TAG" >> $GITHUB_OUTPUT
 
   build:
-    name: Build with external toolchain
+    name: Build ${{ inputs.target }}
     needs: setup_build
     runs-on: ubuntu-latest
 
@@ -133,6 +164,16 @@ jobs:
           echo "TARGET=$TARGET" >> "$GITHUB_ENV"
           echo "SUBTARGET=$SUBTARGET" >> "$GITHUB_ENV"
 
+      - name: Prepare prebuilt tools
+        shell: su buildbot -c "sh -e {0}"
+        working-directory: openwrt
+        run: |
+          mkdir -p staging_dir build_dir
+          ln -s /prebuilt_tools/staging_dir/host staging_dir/host
+          ln -s /prebuilt_tools/build_dir/host build_dir/host
+
+          ./scripts/ext-tools.sh --refresh
+
       - name: Update & Install feeds
         if: inputs.include_feeds == true
         shell: su buildbot -c "sh -e {0}"
@@ -149,21 +190,23 @@ jobs:
           TOOLCHAIN_PATH=snapshots
 
           if [ -n "${{ github.base_ref }}" ]; then
-            if echo "${{ github.base_ref }}" | grep -q -E 'openwrt-[0-9][0-9]\.[0-9][0-9]'; then
-              major_ver="$(echo ${{ github.base_ref }} | sed 's/openwrt-/v/')"
+            if echo "${{ github.base_ref }}" | grep -q -E '^openwrt-[0-9][0-9]\.[0-9][0-9]$'; then
+              major_ver="$(echo ${{ github.base_ref }} | sed 's/^openwrt-/v/')"
             fi
           elif [ "${{ github.ref_type }}" = "branch" ]; then
-            if echo "${{ github.ref_name }}" | grep -q -E 'openwrt-[0-9][0-9]\.[0-9][0-9]-'; then
-              major_ver="$(echo ${{ github.ref_name }} | sed 's/openwrt-\([0-9][0-9]\.[0-9][0-9]\)-.*/v\1/')"
+            if echo "${{ github.ref_name }}" | grep -q -E '^openwrt-[0-9][0-9]\.[0-9][0-9]$'; then
+              major_ver="$(echo ${{ github.ref_name }} | sed 's/^openwrt-/v/')"
+            elif echo "${{ github.ref_name }}" | grep -q -E '^openwrt-[0-9][0-9]\.[0-9][0-9]-'; then
+              major_ver="$(echo ${{ github.ref_name }} | sed 's/^openwrt-\([0-9][0-9]\.[0-9][0-9]\)-.*/v\1/')"
             fi
           elif [ "${{ github.ref_type }}" = "tag" ]; then
-            if echo "${{ github.ref_name }}" | grep -q -E 'v[0-9][0-9]\.[0-9][0-9]\..+'; then
-              major_ver="$(sed 's/\(v[0-9][0-9]\.[0-9][0-9]\)\..\+/\1/')"
+            if echo "${{ github.ref_name }}" | grep -q -E '^v[0-9][0-9]\.[0-9][0-9]\..+'; then
+              major_ver="$(echo ${{ github.ref_name }} | sed 's/^\(v[0-9][0-9]\.[0-9][0-9]\)\..\+/\1/')"
             fi
           fi
 
           if [ -n "$major_ver" ]; then
-            git fetch --tags
+            git fetch --tags -f
             latest_tag="$(git tag --sort=-creatordate -l $major_ver* | head -n1)"
             if [ -n "$latest_tag" ]; then
               TOOLCHAIN_PATH=releases/$(echo $latest_tag | sed 's/^v//')
@@ -174,29 +217,20 @@ jobs:
           if curl $SUMS_FILE | grep -q ".*openwrt-toolchain.*tar.xz"; then
             TOOLCHAIN_STRING="$( curl $SUMS_FILE | grep ".*openwrt-toolchain.*tar.xz")"
             TOOLCHAIN_FILE=$(echo "$TOOLCHAIN_STRING" | sed -n -e 's/.*\(openwrt-toolchain.*\).tar.xz/\1/p')
-            TOOLCHAIN_SHA256=$(echo "$TOOLCHAIN_STRING" | cut -d ' ' -f 1)
             
             echo "toolchain-type=external_toolchain" >> $GITHUB_OUTPUT
           elif curl $SUMS_FILE | grep -q ".*openwrt-sdk.*tar.xz"; then
             TOOLCHAIN_STRING="$( curl $SUMS_FILE | grep ".*openwrt-sdk.*tar.xz")"
             TOOLCHAIN_FILE=$(echo "$TOOLCHAIN_STRING" | sed -n -e 's/.*\(openwrt-sdk.*\).tar.xz/\1/p')
-            TOOLCHAIN_SHA256=$(echo "$TOOLCHAIN_STRING" | cut -d ' ' -f 1)
 
             echo "toolchain-type=external_sdk" >> $GITHUB_OUTPUT
+          else
+            echo "toolchain-type=internal" >> $GITHUB_OUTPUT
           fi
 
           echo "TOOLCHAIN_FILE=$TOOLCHAIN_FILE" >> "$GITHUB_ENV"
-          echo "TOOLCHAIN_SHA256=$TOOLCHAIN_SHA256" >> "$GITHUB_ENV"
           echo "TOOLCHAIN_PATH=$TOOLCHAIN_PATH" >> "$GITHUB_ENV"
 
-      - name: Cache external toolchain/sdk
-        if: inputs.build_toolchain == false
-        id: cache-external-toolchain
-        uses: actions/cache@v3
-        with:
-          path: openwrt/${{ env.TOOLCHAIN_FILE }}
-          key: ${{ env.TOOLCHAIN_FILE }}-${{ steps.parse-toolchain.outputs.toolchain-type }}-${{ env.TOOLCHAIN_SHA256 }}
-
       - name: Cache ccache
         uses: actions/cache@v3
         with:
@@ -206,18 +240,13 @@ jobs:
             ccache-kernel-${{ env.TARGET }}/${{ env.SUBTARGET }}-
 
       - name: Download external toolchain/sdk
-        if: inputs.build_toolchain == false && steps.cache-external-toolchain.outputs.cache-hit != 'true'
+        if: inputs.build_toolchain == false && steps.parse-toolchain.outputs.toolchain-type != 'internal'
         shell: su buildbot -c "sh -e {0}"
         working-directory: openwrt
         run: |
           wget -O - https://downloads.cdn.openwrt.org/${{ env.TOOLCHAIN_PATH }}/targets/${{ env.TARGET }}/${{ env.SUBTARGET }}/${{ env.TOOLCHAIN_FILE }}.tar.xz \
             | tar --xz -xf -
 
-      - name: Extract prebuilt tools
-        shell: su buildbot -c "sh -e {0}"
-        working-directory: openwrt
-        run: ./scripts/ext-tools.sh --tools /tools.tar
-
       - name: Configure testing kernel
         if: inputs.testing == true
         shell: su buildbot -c "sh -e {0}"
@@ -262,6 +291,34 @@ jobs:
             --overwrite-config \
             --config ${{ env.TARGET }}/${{ env.SUBTARGET }}
 
+      - name: Adapt external sdk to external toolchain format
+        if: inputs.build_toolchain == false && steps.parse-toolchain.outputs.toolchain-type == 'external_sdk'
+        shell: su buildbot -c "sh -e {0}"
+        working-directory: openwrt
+        run: |
+          TOOLCHAIN_DIR=${{ env.TOOLCHAIN_FILE }}/staging_dir/$(ls ${{ env.TOOLCHAIN_FILE }}/staging_dir | grep toolchain)
+          TOOLCHAIN_BIN=$TOOLCHAIN_DIR/bin
+          OPENWRT_DIR=$(pwd)
+
+          # Find target name from toolchain info.mk
+          GNU_TARGET_NAME=$(cat $TOOLCHAIN_DIR/info.mk | grep TARGET_CROSS | sed 's/^TARGET_CROSS=\(.*\)-$/\1/')
+
+          cd $TOOLCHAIN_BIN
+
+          # Revert sdk wrapper scripts applied to all the bins
+          for app in $(find . -name "*.bin"); do
+            TARGET_APP=$(echo $app | sed 's/\.\/\.\(.*\)\.bin/\1/')
+            rm $TARGET_APP
+            mv .$TARGET_APP.bin $TARGET_APP
+          done
+
+          # Setup the wrapper script in the sdk toolchain dir simulating an external toolchain build
+          cp $OPENWRT_DIR/target/toolchain/files/wrapper.sh $GNU_TARGET_NAME-wrapper.sh
+          for app in cc gcc g++ c++ cpp ld as ; do
+            [ -f $GNU_TARGET_NAME-$app ] && mv $GNU_TARGET_NAME-$app $GNU_TARGET_NAME-$app.bin
+            ln -sf $GNU_TARGET_NAME-wrapper.sh $GNU_TARGET_NAME-$app
+          done
+
       - name: Configure external toolchain with sdk
         if: inputs.build_toolchain == false && steps.parse-toolchain.outputs.toolchain-type == 'external_sdk'
         shell: su buildbot -c "sh -e {0}"
@@ -277,7 +334,7 @@ jobs:
             --config ${{ env.TARGET }}/${{ env.SUBTARGET }}
 
       - name: Configure internal toolchain
-        if: inputs.build_toolchain == true
+        if: inputs.build_toolchain == true || steps.parse-toolchain.outputs.toolchain-type == 'internal'
         shell: su buildbot -c "sh -e {0}"
         working-directory: openwrt
         run: |
@@ -306,11 +363,13 @@ jobs:
         run: make toolchain/install -j$(nproc) BUILD_LOG=1 || ret=$? .github/workflows/scripts/show_build_failures.sh
 
       - name: Build Kernel
+        if: inputs.build_kernel == true
         shell: su buildbot -c "sh -e {0}"
         working-directory: openwrt
         run: make target/compile -j$(nproc) BUILD_LOG=1 || ret=$? .github/workflows/scripts/show_build_failures.sh
 
       - name: Build Kernel Kmods
+        if: inputs.build_kernel == true
         shell: su buildbot -c "sh -e {0}"
         working-directory: openwrt
         run: make package/linux/compile -j$(nproc) BUILD_LOG=1 || ret=$? .github/workflows/scripts/show_build_failures.sh
@@ -321,6 +380,57 @@ jobs:
         working-directory: openwrt
         run: make -j$(nproc) BUILD_LOG=1 || ret=$? .github/workflows/scripts/show_build_failures.sh
 
+      - name: Coverity prepare toolchain
+        if: inputs.coverity_check_packages != ''
+        shell: su buildbot -c "sh -e {0}"
+        working-directory: openwrt
+        run: |
+          wget -q https://scan.coverity.com/download/linux64 --post-data "token=${{ secrets.coverity_api_token }}&project=${{ inputs.coverity_project_name }}" -O coverity.tar.gz
+          wget -q https://scan.coverity.com/download/linux64 --post-data "token=${{ secrets.coverity_api_token }}&project=${{ inputs.coverity_project_name }}&md5=1" -O coverity.tar.gz.md5
+          echo ' coverity.tar.gz' >> coverity.tar.gz.md5
+          md5sum -c coverity.tar.gz.md5
+
+          mkdir cov-analysis-linux64
+          tar xzf coverity.tar.gz --strip 1 -C cov-analysis-linux64
+          export PATH=$(pwd)/cov-analysis-linux64/bin:$PATH
+
+          for template in ${{ inputs.coverity_compiler_template_list }}; do
+            cov-configure --template --comptype gcc --compiler "$template"
+          done
+
+      - name: Clean and recompile packages with Coverity toolchain
+        if: inputs.coverity_check_packages != ''
+        shell: su buildbot -c "bash {0}"
+        working-directory: openwrt
+        run: |
+          set -o pipefail -o errexit
+
+          coverity_check_packages=(${{ inputs.coverity_check_packages }})
+          printf -v clean_packages "package/%s/clean " "${coverity_check_packages[@]}"
+          make -j$(nproc) BUILD_LOG=1 $clean_packages || ret=$? .github/workflows/scripts/show_build_failures.sh
+
+          coverity_force_compile_packages=(${{ inputs.coverity_force_compile_packages }})
+          printf -v force_compile_packages "package/%s/compile " "${coverity_force_compile_packages[@]}"
+          make -j$(nproc) BUILD_LOG=1 $force_compile_packages || ret=$? .github/workflows/scripts/show_build_failures.sh
+
+          printf -v compile_packages "package/%s/compile " "${coverity_check_packages[@]}"
+          export PATH=$(pwd)/cov-analysis-linux64/bin:$PATH
+          cov-build --dir cov-int make -j $(nproc) BUILD_LOG=1 $compile_packages || ret=$? .github/workflows/scripts/show_build_failures.sh
+
+      - name: Upload build to Coverity for analysis
+        if: inputs.coverity_check_packages != ''
+        shell: su buildbot -c "sh -e {0}"
+        working-directory: openwrt
+        run: |
+          tar czf cov-int.tar.gz ./cov-int
+          curl \
+            --form token="${{ secrets.coverity_api_token }}" \
+            --form email="contact@openwrt.org" \
+            --form file=@cov-int.tar.gz \
+            --form version="${{ github.ref_name }}-${{ github.sha }}" \
+            --form description="OpenWrt ${{ github.ref_name }}-${{ github.sha }}" \
+            "https://scan.coverity.com/builds?project=${{ inputs.coverity_project_name }}"
+
       - name: Upload logs
         if: failure()
         uses: actions/upload-artifact@v3