From: Daniel Stenberg Date: Tue, 20 May 2025 14:49:57 +0000 (+0200) Subject: GHA: add a job to check function complexity X-Git-Tag: curl-8_14_0~29 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=203b4349af3e1bfd06226ebb574af524b0a37276;p=thirdparty%2Fcurl.git GHA: add a job to check function complexity - Done with the new top-complexity script which uses the pmccabe tool. - Any function scoring over 100 makes the test fail - The script outputs all functions scoring over 70 - Two >100 functions are whitelisted by name, but they are not allowed to increase their scores. Closes #17398 --- diff --git a/.github/workflows/checksrc.yml b/.github/workflows/checksrc.yml index 990551aeab..ae11a8bdda 100644 --- a/.github/workflows/checksrc.yml +++ b/.github/workflows/checksrc.yml @@ -94,6 +94,26 @@ jobs: - name: REUSE Compliance Check uses: fsfe/reuse-action@bb774aa972c2a89ff34781233d275075cbddf542 # v5 + complexity: + runs-on: ubuntu-latest + timeout-minutes: 3 + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + persist-credentials: false + name: checkout + + - name: install pmccabe + run: | + sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list + sudo apt-get -o Dpkg::Use-Pty=0 update + sudo rm -f /var/lib/man-db/auto-update + sudo apt-get -o Dpkg::Use-Pty=0 install \ + pmccabe + + - name: check complexity scores + run: ./scripts/top-complexity + miscchecks: runs-on: ubuntu-latest timeout-minutes: 5 diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 03eb8044ad..0b278e62b0 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -25,7 +25,7 @@ EXTRA_DIST = coverage.sh completion.pl firefox-db2pem.sh checksrc.pl checksrc-all.sh \ mk-ca-bundle.pl mk-unity.pl schemetable.c cd2nroff nroff2cd cdall cd2cd managen \ dmaketgz maketgz release-tools.sh verify-release cmakelint.sh mdlinkcheck \ - CMakeLists.txt pythonlint.sh randdisable wcurl + CMakeLists.txt pythonlint.sh randdisable wcurl top-complexity dist_bin_SCRIPTS = wcurl diff --git a/scripts/top-complexity b/scripts/top-complexity new file mode 100755 index 0000000000..c7129a1eb8 --- /dev/null +++ b/scripts/top-complexity @@ -0,0 +1,125 @@ +#!/usr/bin/env perl +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### + +use strict; +use warnings; + +####################################################################### +# Check for a command in the PATH of the test server. +# +sub checkcmd { + my ($cmd)=@_; + my @paths; + if ($^O eq 'MSWin32' || $^O eq 'dos' || $^O eq 'os2') { + # PATH separator is different + @paths=(split(';', $ENV{'PATH'})); + } + else { + @paths=(split(':', $ENV{'PATH'}), "/usr/sbin", "/usr/local/sbin", + "/sbin", "/usr/bin", "/usr/local/bin"); + } + for(@paths) { + if( -x "$_/$cmd" && ! -d "$_/$cmd" ) { + # executable bit but not a directory! + return "$_/$cmd"; + } + } + return ""; +} + +my $pmccabe = checkcmd("pmccabe"); +if(!$pmccabe) { + print "Make sure 'pmccabe' exists in your PATH\n"; + exit 1; +} +if(! -r "lib/url.c" || ! -r "lib/urldata.h") { + print "Invoke this script in the curl source tree root\n"; + exit 1; +} + +my @files; +open(F, "git ls-files '*.c'|"); +while() { + chomp $_; + my $file = $_; + # we can't filter these with git so do it here + if($file =~ /^(lib|src)/) { + push @files, $file; + } +} + +my $cmd = "$pmccabe ".join(" ", @files); +my @output=`$cmd`; + +# these functions can have these scores, but not higher +my %whitelist = ( + 'getparameter' => 142, + 'single_transfer' => 124 + ); + +# functions with complexity above this level causes the function to return error +my $cutoff = 100; + +# functions above this complexity level are shown +my $show = 70; + +my $error = 0; +my %where; +my %perm; +# each line starts with the complexity score +# 142 417 809 1677 1305 src/tool_getparam.c(1677): getparameter +for my $l (@output) { + chomp $l; + if($l =~/^(\d+)\t\d+\t\d+\t\d+\t\d+\t([^\(]+).*: ([^ ]*)/) { + my ($score, $path, $func)=($1, $2, $3); + + if($score > $show) { + my $allow = 0; + if($whitelist{$func} && + ($score <= $whitelist{$func})) { + $allow = 1; + } + $where{"$path:$func"}=$score; + $perm{"$path:$func"}=$allow; + if(($score > $cutoff) && !$allow) { + $error++; + } + } + } + +} + +my $showncutoff; +for my $e (sort {$where{$b} <=> $where{$a}} keys %where) { + if(!$showncutoff && + ($where{$e} <= $cutoff)) { + print "\n---- threshold: $cutoff ----\n\n"; + $showncutoff = 1; + } + printf "%-5d %s%s\n", $where{$e}, $e, + $perm{$e} ? " [ALLOWED]": ""; +} + +exit $error;