name: Quality Gates on: workflow_call: inputs: working-directory: description: 'Cartella che contiene package.json, relativa alla root del repo. Default: la root.' type: string default: '.' node-version: type: string default: '24.16.0' npm-version: type: string default: '' jobs: quality-gates: runs-on: ubuntu-latest defaults: run: working-directory: ${{ inputs.working-directory || '.' }} steps: - name: Checkout uses: https://github.com/actions/checkout@v6 - name: Setup Node.js uses: https://github.com/actions/setup-node@v4 with: node-version: ${{ inputs.node-version || '24.16.0' }} - name: Upgrade npm if: inputs.npm-version != '' run: npm install -g npm@${{ inputs.npm-version }} - name: Cache npm uses: https://github.com/actions/cache@v4 with: path: ~/.npm key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-node- - name: Configure npm private registry run: | echo "@pzeta:registry=https://gitea.pzetatouch.it/api/packages/PZeta_Touch/npm/" >> ~/.npmrc echo "//gitea.pzetatouch.it/api/packages/PZeta_Touch/npm/:_authToken=${{ secrets.NPM_TOKEN }}" >> ~/.npmrc - name: Dockerfile - registry @pzeta e ARG per stage (solo avviso) shell: bash # Solo avviso finche' i ~46 repo con il registry in minuscolo non sono # corretti: bloccante oggi li farebbe diventare rossi tutti insieme. # Diventa bloccante con la tappa 2 di PZeta_Touch/flux-repo#133. continue-on-error: true run: | # Due difetti che hanno rotto le release di node-xmlvalidation per due # mesi senza che nessuno se ne accorgesse (PZeta_Touch/flux-repo#129, # #133): (a) il registry @pzeta scritto in minuscolo nell'.npmrc # generato dal Dockerfile, mentre i lockfile risolvono su PZeta_Touch; # (b) una ARG usata in uno stage che non la dichiara, e che li' arriva # vuota. Insieme, `npm install -g npm@` installa npm 12, che rifiuta # l'URL scritto diverso (EALLOWREMOTE). Questo job non puo' accorgersene # da solo: qui l'immagine non si costruisce, e la release fallisce solo # al tag. set +e # La radice del repository e non working-directory: i Dockerfile stanno # spesso fuori dalla cartella del package. cd "${GITHUB_WORKSPACE:-.}" || exit 0 files=$(find . \( -name node_modules -o -name .git -o -name dist -o -name build \ -o -name .nuxt -o -name .output -o -name coverage \) -prune -o \ -type f \( -name 'Dockerfile' -o -name 'Dockerfile.*' -o -name '*.Dockerfile' \ -o -name '*.dockerfile' \) -print 2>/dev/null | sed 's|^\./||' | sort) if [ -z "$files" ]; then echo "Nessun Dockerfile: controllo non applicabile." exit 0 fi # (b) ARG fuori scope: una ARG vale nello stage che la dichiara e in # quelli che ne derivano (FROM ), come le ENV; quelle dichiarate # prima del primo FROM valgono solo nelle righe FROM. Verificato sul # log di BuildKit di nuxt-vue-components-docs v1.0.13: `FROM base AS # deps` vede la NPM_VERSION dichiarata in `base`. arg_scope() { ${AWK:-awk} ' function trim(s) { sub(/^[ \t]+/, "", s); sub(/[ \t]+$/, "", s); return s } # Nomi dichiarati da ARG/ENV: "A", "A=1", "A=1 B=2", "A valore". function declared(rest, out, n, i, t) { rest = trim(rest) split("", out) if (rest !~ /=/) { n = split(rest, t, /[ \t]+/); if (n > 0) out[t[1]] = 1; return } n = split(rest, t, /[ \t]+/) for (i = 1; i <= n; i++) if (t[i] ~ /^[A-Za-z_][A-Za-z0-9_]*(=|$)/) { sub(/=.*/, "", t[i]); out[t[i]] = 1 } } function handle(kw, rest, ln, n, i, t, base, alias, k, p, s, v, d) { if (pass == 1) { if (kw == "ARG") { declared(rest, d); for (k in d) allargs[k] = 1 } return } if (kw == "FROM") { n = split(trim(rest), t, /[ \t]+/); i = 1 while (i <= n && t[i] ~ /^--/) i++ base = tolower(t[i]); alias = "" if (i + 2 <= n && toupper(t[i + 1]) == "AS") alias = tolower(t[i + 2]) stages++ stage = (alias != "" ? alias : "#" stages) split("", scope); split("", inh) for (k in varof) { split(k, p, SUBSEP); if (p[1] == base) inh[p[2]] = 1 } for (k in inh) { scope[k] = 1; varof[stage, k] = 1 } return } if (stage == "") return if (kw == "ARG") { declared(rest, d); for (k in d) { scope[k] = 1; varof[stage, k] = 1 }; return } s = rest while (match(s, /[$][{]?[A-Za-z_][A-Za-z0-9_]*/)) { v = substr(s, RSTART, RLENGTH); sub(/^[$][{]?/, "", v) s = substr(s, RSTART + RLENGTH) if ((v in allargs) && !(v in scope) && !(v in predefined) && !((ln, v) in seen)) { seen[ln, v] = 1 printf "%d\t%s\t%s\n", ln, v, stage } } if (kw == "ENV") { declared(rest, d); for (k in d) { scope[k] = 1; varof[stage, k] = 1 } } } function flush() { if (buf != "" && match(buf, /^[ \t]*[A-Za-z]+/)) { kw = toupper(trim(substr(buf, RSTART, RLENGTH))) handle(kw, substr(buf, RSTART + RLENGTH), start) } buf = "" } BEGIN { np = split("TARGETPLATFORM TARGETOS TARGETARCH TARGETVARIANT BUILDPLATFORM BUILDOS BUILDARCH BUILDVARIANT HTTP_PROXY HTTPS_PROXY FTP_PROXY NO_PROXY ALL_PROXY http_proxy https_proxy ftp_proxy no_proxy all_proxy", pp, " ") for (i = 1; i <= np; i++) predefined[pp[i]] = 1 } FNR == 1 { pass++; buf = ""; stage = ""; stages = 0; split("", scope); split("", varof) } { line = $0; sub(/\r$/, "", line) if (line ~ /^[ \t]*#/) next # commenti, anche dentro una continuazione if (buf == "" && line ~ /^[ \t]*$/) next if (buf == "") start = FNR if (line ~ /\\[ \t]*$/) { sub(/\\[ \t]*$/, " ", line); buf = buf line; next } buf = buf line flush() } ' "$1" "$1" } # Caso di prova: se l'awk di questo runner non riconosce una ARG fuori # scope nota, il controllo (b) non e' affidabile e lo si dice, invece di # dichiarare in regola un Dockerfile che non si e' potuto leggere. argcheck=1 prova=$(mktemp) printf 'ARG X=1\nFROM scratch AS a\nRUN echo ${X}\n' > "$prova" if [ "$(arg_scope "$prova" 2>/dev/null | cut -f2)" != "X" ]; then argcheck=0 echo "::warning::Guardia Dockerfile: l'analisi delle ARG non funziona con l'awk di questo runner ($(${AWK:-awk} -W version 2>&1 | head -1)). Il controllo (b) e' saltato, il (a) resta." fi rm -f "$prova" findings="" for f in $files; do # (a) registry: npm confronta l'URL del lockfile come stringa, e i lockfile # risolvono tutti su PZeta_Touch. reg=$(grep -nE 'packages/(pzeta_touch|pzeta)/' "$f" 2>/dev/null | cut -d: -f1 | tr '\n' ' ' | sed 's/ *$//') if [ -n "$reg" ]; then findings="${findings}${f} ${reg%% *} registry $(echo "$reg" | sed 's/ /, /g') - " fi # (b) ARG fuori scope, con arg_scope definita sopra. args="" [ "$argcheck" = "1" ] && args=$(arg_scope "$f" 2>/dev/null) while IFS=' ' read -r n v st; do [ -n "$n" ] || continue findings="${findings}${f} ${n} arg ${v} ${st} " done <> "$GITHUB_STEP_SUMMARY" 2>/dev/null fi exit 0 - name: Check outdated packages run: npm outdated || true - name: Install dependencies run: npm ci - name: Security audit run: npm audit --audit-level=high || true - name: Lint run: npm run lint:check - name: Type check run: npm run typecheck - name: Format check run: npm run format:check - name: Build run: npm run build - name: Test run: npm run test