diff --git a/.gitea/workflows/quality-gates-github.yml b/.gitea/workflows/quality-gates-github.yml index cba0f4b..ffd74b5 100644 --- a/.gitea/workflows/quality-gates-github.yml +++ b/.gitea/workflows/quality-gates-github.yml @@ -78,6 +78,89 @@ jobs: 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 @@ -88,74 +171,9 @@ jobs: " 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`. - args=$(${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() - } - ' "$f" "$f" 2>/dev/null) + # (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} @@ -166,7 +184,11 @@ jobs: done if [ -z "$findings" ]; then - echo "Dockerfile: registry @pzeta e ARG per stage in regola." + if [ "$argcheck" = "1" ]; then + echo "Dockerfile: registry @pzeta e ARG per stage in regola." + else + echo "Dockerfile: registry @pzeta in regola; ARG per stage non verificate (vedi l'avviso sopra)." + fi exit 0 fi diff --git a/.gitea/workflows/quality-gates.yml b/.gitea/workflows/quality-gates.yml index 03798ba..2d29e4e 100644 --- a/.gitea/workflows/quality-gates.yml +++ b/.gitea/workflows/quality-gates.yml @@ -157,6 +157,89 @@ jobs: 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 @@ -167,74 +250,9 @@ jobs: " 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`. - args=$(${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() - } - ' "$f" "$f" 2>/dev/null) + # (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} @@ -245,7 +263,11 @@ jobs: done if [ -z "$findings" ]; then - echo "Dockerfile: registry @pzeta e ARG per stage in regola." + if [ "$argcheck" = "1" ]; then + echo "Dockerfile: registry @pzeta e ARG per stage in regola." + else + echo "Dockerfile: registry @pzeta in regola; ARG per stage non verificate (vedi l'avviso sopra)." + fi exit 0 fi