Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions campaigns/TEMPLATE-DRIFT-CAMPAIGN.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@ No rule in `AGENTIC.a2ml` assigns these recipes; that grant is the owner's.
| `workflow-linter.yml` with the rule `head -1 "$file" \| grep -q "^# SPDX-License-Identifier:"`.
The tool `gh actions-lock` writes its comment on line 1, so each lock run
makes each managed workflow fail the lint.
Also a `workflow-linter.yml` with the first form of the template rule,
which stops only at a line in column 1 (`/^[^#[:space:]]/`). A workflow
with an indented body then passes with a licence line after the body.
| The rule of the template: the licence line can be at any position in the
leading comment block.
leading comment block, and the block ends at the first line of the body.

| `recipe-sonarqube-token-guard`
| `fix-sonarqube-token-guard.sh`
Expand Down Expand Up @@ -73,6 +76,9 @@ Each recipe was also run on the files of `system-et` at `main` (`04a8b6e`):
* SPDX rule: the step is identical to the step of the template branch
(`rsr-template-repo` pull request 323, `915acb2`). That pull request makes
the rule stop at an indented workflow body also.
* SPDX rule, first form of the template rule: run on the linter of
`system-et` at `main` (`bbe1d13`), which has that form. 1 line changes, and
the step is identical to the step of the template at `main` (`0208563`).
* Token guard: the job is identical to the job in pull request 92
(`9f529ef`). The guard step is identical to the guard step of the template
branch (`rsr-template-repo` pull request 321, `806dac0`).
Expand Down Expand Up @@ -102,10 +108,10 @@ and `fix-lock-self-sha-refs.sh` already cover these.
inactive file (`e2e.yml.template`).
* The token guard recipe edits only the shape of the old template. It does
not rewrite a workflow that a person changed.
* The SPDX recipe has 1 target: the `head -1` rule. A linter that has the
first form of the template rule (the form before `rsr-template-repo` pull
request 323, for example in `system-et`) is not a target, and keeps that
form.
* The SPDX recipe has 2 targets: the `head -1` rule, and the first form of
the template rule (the form before `rsr-template-repo` pull request 323,
for example in `system-et`). A linter with any other rule is not a target,
and keeps its rule.
* The number of repositories with each target is not known. No estate scan
was made.

Expand Down
56 changes: 47 additions & 9 deletions scripts/fix-workflow-linter-spdx-rule.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,30 @@
#
# fix-workflow-linter-spdx-rule.sh: accept the SPDX line in the leading comment block
#
# Older copies of the estate template have this rule in
# .github/workflows/workflow-linter.yml:
# Older copies of the estate template have 1 of 2 rules in
# .github/workflows/workflow-linter.yml.
#
# Form 1 reads line 1 only:
#
# if ! head -1 "$file" | grep -q "^# SPDX-License-Identifier:"; then
#
# The tool gh actions-lock writes its own comment on line 1 of each workflow
# that it manages. Each lock run thus makes every managed workflow fail the
# lint ("missing SPDX header"). The current template (rsr-template-repo)
# accepts the licence line at any position in the leading comment block.
# This recipe replaces the old rule with the rule of the template.
# lint ("missing SPDX header").
#
# Form 2 is the first template rule that reads the leading comment block:
#
# if ! awk '... /^[^#[:space:]]/ { exit } ...' "$file"; then
#
# It stops only at a line that starts in column 1. A workflow with an
# indented body thus passes with a licence line after the body
# (rsr-template-repo pull request 323).
#
# The current template (rsr-template-repo) accepts the licence line at any
# position in the leading comment block, and stops at the first line of the
# body. This recipe replaces each form with the rule of the template.
#
# Only the exact old rule is a target. A linter with a different rule (for
# Only the 2 exact forms are targets. A linter with a different rule (for
# example `head -5`) is not changed.
#
# Idempotent: a file that has the new rule is not changed.
Expand All @@ -27,7 +39,23 @@ set -euo pipefail

REL=".github/workflows/workflow-linter.yml"
OLD_RULE='^[[:space:]]*if ! head -1 "\$[A-Za-z_][A-Za-z0-9_]*" \| grep -q "\^# SPDX-License-Identifier:"; then[[:space:]]*$'
OLD_AWK_RULE='/^# SPDX-License-Identifier:/ { found=1 } /^[^#[:space:]]/ { exit } END { exit !found }'
NEW_RULE='/^# SPDX-License-Identifier:/ { found=1 } /^[[:space:]]*[^#[:space:]]/ { exit } END { exit !found }'
export OLD_AWK_RULE NEW_RULE

# awk function: the position of form 2 in a line, or 0. A line is a target
# only when the whole line is `if ! awk '<form 2>' "$name"; then`.
OLD_AWK_AT=$(cat <<'AWK'
function old_awk_at(s, lit, p) {
lit = "awk '" ENVIRON["OLD_AWK_RULE"] "' \"$"
p = index(s, lit)
if (p == 0) return 0
if (substr(s, 1, p - 1) !~ /^[[:space:]]*if ! $/) return 0
if (substr(s, p + length(lit)) !~ /^[A-Za-z_][A-Za-z0-9_]*"; then[[:space:]]*$/) return 0
return p
}
AWK
)

# Print the path of the linter when it is a regular file inside the repository.
# A missing file, a symlink, or a path that leaves the repository is no target.
Expand All @@ -41,9 +69,11 @@ target_file() {

# Print the number of old-rule lines in the repository (0 when no target file).
count_targets() {
local file
local file form1 form2
file=$(target_file "$1") || { echo 0; return 0; }
grep -cE -- "$OLD_RULE" "$file" || true
form1=$(grep -cE -- "$OLD_RULE" "$file" || true)
form2=$(awk "$OLD_AWK_AT"' old_awk_at($0) { n++ } END { print n + 0 }' "$file")
echo $((form1 + form2))
}

if [[ "${1:-}" == "--check" ]]; then
Expand Down Expand Up @@ -76,11 +106,19 @@ PROGRAM=$(cat <<'AWK'
/echo "Add '# SPDX-License-Identifier: [A-Za-z0-9.+-]+' as first line"/ {
sub(/ as first line"/, " to the leading comment block\"")
}
{
# Form 2: only the rule text changes. The 2 comment lines are there.
p = old_awk_at($0)
if (p) {
rest = substr($0, p + length("awk '" ENVIRON["OLD_AWK_RULE"] "' \"$"))
$0 = substr($0, 1, p - 1) "awk '" ENVIRON["NEW_RULE"] "' \"$" rest
}
}
{ print }
AWK
)

NEW_RULE="$NEW_RULE" awk "$PROGRAM" "$FILE" > "$TMP"
awk "$OLD_AWK_AT"$'\n'"$PROGRAM" "$FILE" > "$TMP"
[[ -s "$TMP" ]] || { echo "fix-workflow-linter-spdx-rule: the edit gave no text; $REL is not changed" >&2; exit 1; }
cat "$TMP" > "$FILE"

Expand Down
17 changes: 17 additions & 0 deletions tests/template-drift-recipes-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,23 @@ printf '# SPDX-License-Identifier: MPL-2.0\n name: a\n jobs: {}\n' > "$TMP/s5"
if awk "$RULE" "$TMP/s4"; then fail "$R: the new rule accepts a licence after an indented body"; fi
awk "$RULE" "$TMP/s5" || fail "$R: the new rule refuses a licence before an indented body"

# Form 2, the first template rule that reads the leading comment block, is a
# target too. Only the rule text changes: the result is the same file.
REPO=$(new_repo spdx-form2); F="$REPO/.github/workflows/workflow-linter.yml"
OLD_STOP='/^[^#[:space:]]/ { exit }'
NEW_STOP='/^[[:space:]]*[^#[:space:]]/ { exit }'
awk -v old="$OLD_STOP" -v new="$NEW_STOP" '{ p = index($0, new); if (p) $0 = substr($0, 1, p - 1) old substr($0, p + length(new)); print }' "$TMP/expected" > "$F"
grep -qF -- "$OLD_STOP" "$F" || fail "$R: the test file does not have form 2"
# Form 2 accepts a licence after an indented body. That is its defect.
FORM2=$(sed -n "s/^ *if ! awk '\(.*\)' \"\$file\"; then\$/\1/p" "$F")
awk "$FORM2" "$TMP/s4" || fail "$R: the test file does not have the defect of form 2"
repair "$R" "$REPO" "$F" 1 "$TMP/expected"

# The rule text of form 2 in a line of a different shape is not a target.
REPO=$(new_repo spdx-form2-other); F="$REPO/.github/workflows/workflow-linter.yml"
printf '%s\n' " awk '/^# SPDX-License-Identifier:/ { found=1 } $OLD_STOP END { exit !found }' \"\$file\" || failed=1" > "$F"
expect_untouched "$R" "$REPO" "$F"

# A linter with a different rule is not a target.
REPO=$(new_repo spdx-other); F="$REPO/.github/workflows/workflow-linter.yml"
printf '%s\n' ' if ! head -5 "$f" | grep -q "SPDX-License-Identifier"; then' > "$F"
Expand Down
Loading