From 34a04451c10c8b7003dba6b3fa99eef6f1e55bb9 Mon Sep 17 00:00:00 2001 From: chynasan Date: Mon, 15 Sep 2025 11:21:33 -0500 Subject: [PATCH 1/7] Created pattern specification validation CI checks. --- .github/workflows/validate-pattern-specs.yml | 73 ++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 .github/workflows/validate-pattern-specs.yml diff --git a/.github/workflows/validate-pattern-specs.yml b/.github/workflows/validate-pattern-specs.yml new file mode 100644 index 00000000..53708444 --- /dev/null +++ b/.github/workflows/validate-pattern-specs.yml @@ -0,0 +1,73 @@ +--- +name: Validate Pattern Spec Version + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true + +on: + pull_request: + branches: + - main + - stable-* + tags: + - "*" +jobs: + version_check: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Fetch full history to allow diffing against previous commits + + - name: Get changed files + id: changed-files + run: | + # Determine the range of commits to analyze + if [ "${{ github.event_name }}" == "pull_request" ]; then + BASE_COMMIT=${{ github.event.pull_request.base.sha }} + HEAD_COMMIT=${{ github.event.pull_request.head.sha }} + CHANGED_FILES=$(git diff --name-only pattern_service/specifications/ $BASE_COMMIT..$HEAD_COMMIT) + else + CHANGED_FILES=$(git diff --name-only pattern_service/specifications/ HEAD^..HEAD) + fi + + echo "Changed files: $CHANGED_FILES" # TODO: limit to specifications/ directory, except dev? + + # Check if the specific file was modified + if echo "$CHANGED_FILES" | grep -q "\$id": "https://raw\.githubusercontent\.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-([0-9]+(\.[0-9]+)+)\.json"; then + echo "::set-output name=important_file_changed::true" + else + echo "::set-output name=important_file_changed::false" + fi + + # need to instead of checking regex for that, grep for changed files includes anything inside of specifications? + # Because right now it will only output for pattern-schema rather than pattern-schema-latest or or pattern spec + # I currently have it set so that the diff should only run in specifications/ which might be better still + # that way I can reuse this portion of the code and just split it so that we check schema version vs pattern spec version on two separate actions + + - name: Run action if pattern schema version changed + if: steps.changed-files.outputs.important_file_changed == 'true' + run: | + echo "pattern-schema-1.0.0.json was modified! Changes to this file are not permitted." + f1 + # Add your specific commands here, e.g., run a script, send a notification, etc. + echo "This is the content of the changed file:" + cat important-file.txt + # if a file gets added check ID variable - check everything in last file name after "$id" - regex? + + - name: Run action if pattern specification version changed + if: steps.changed-files.outputs.important_file_changed == 'true' + run: | + echo "pattern-specification-1.0.0.json was modified! Changes to this file are not permitted." + f1 + # Add your specific commands here, e.g., run a script, send a notification, etc. + echo "This is the content of the changed file:" + cat important-file.txt # this needs to be distinct from the schema version, so I don't think we can lump that into "important-file.txt" or whatever + # if a file gets added check ID variable - check everything in last file name after "$id" - regex? + + - name: For new files check if file name and ID variable match + if: steps.changed-files.outputs.important_file_changed == 'true' + run: | # if new file check name and id variable match? From a5bb92fd7b284bdd05974c364ab2c248f0321206 Mon Sep 17 00:00:00 2001 From: chynasan Date: Mon, 15 Sep 2025 11:35:54 -0500 Subject: [PATCH 2/7] Updated with a few notes --- .github/workflows/validate-pattern-specs.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/validate-pattern-specs.yml b/.github/workflows/validate-pattern-specs.yml index 53708444..e0ad4df3 100644 --- a/.github/workflows/validate-pattern-specs.yml +++ b/.github/workflows/validate-pattern-specs.yml @@ -51,17 +51,20 @@ jobs: - name: Run action if pattern schema version changed if: steps.changed-files.outputs.important_file_changed == 'true' run: | - echo "pattern-schema-1.0.0.json was modified! Changes to this file are not permitted." + echo "pattern-schema-1.0.0.json was modified! Changes to this file are not permitted." # echo "::error::" will display red in the logs f1 # Add your specific commands here, e.g., run a script, send a notification, etc. echo "This is the content of the changed file:" cat important-file.txt # if a file gets added check ID variable - check everything in last file name after "$id" - regex? + # maybe what I do is move grep here for pattern schema file, then that way the output can be specific to that and I can use it as a variable, e.g. $pattern_schema_changed sort of thing? if != regex-for-dev blah blah + # if I go this route I have to repeat this code for pattern spec version also? + - name: Run action if pattern specification version changed if: steps.changed-files.outputs.important_file_changed == 'true' run: | - echo "pattern-specification-1.0.0.json was modified! Changes to this file are not permitted." + echo "pattern-schema-1.0.0.json was modified! Changes to this file are not permitted." # echo "::error::" f1 # Add your specific commands here, e.g., run a script, send a notification, etc. echo "This is the content of the changed file:" @@ -70,4 +73,4 @@ jobs: - name: For new files check if file name and ID variable match if: steps.changed-files.outputs.important_file_changed == 'true' - run: | # if new file check name and id variable match? + run: | # if new file check name and id variable match? Will new files be included in changed files? I assume they are From f8cdbebe4631b8167039ab8d93ec815e7c268508 Mon Sep 17 00:00:00 2001 From: chynasan Date: Mon, 15 Sep 2025 13:08:17 -0500 Subject: [PATCH 3/7] Updated some pieces of this in a pairing session --- .github/workflows/validate-pattern-specs.yml | 90 +++++++++++++------- 1 file changed, 59 insertions(+), 31 deletions(-) diff --git a/.github/workflows/validate-pattern-specs.yml b/.github/workflows/validate-pattern-specs.yml index e0ad4df3..d229df2a 100644 --- a/.github/workflows/validate-pattern-specs.yml +++ b/.github/workflows/validate-pattern-specs.yml @@ -21,6 +21,9 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 # Fetch full history to allow diffing against previous commits + - name: Checkout main branch + run: | + git fetch origin main:main - name: Get changed files id: changed-files @@ -29,48 +32,73 @@ jobs: if [ "${{ github.event_name }}" == "pull_request" ]; then BASE_COMMIT=${{ github.event.pull_request.base.sha }} HEAD_COMMIT=${{ github.event.pull_request.head.sha }} - CHANGED_FILES=$(git diff --name-only pattern_service/specifications/ $BASE_COMMIT..$HEAD_COMMIT) + CHANGED_FILES=$(git diff --name-only $BASE_COMMIT..$HEAD_COMMIT) else - CHANGED_FILES=$(git diff --name-only pattern_service/specifications/ HEAD^..HEAD) + CHANGED_FILES=$(git diff --name-only HEAD^..HEAD) fi - echo "Changed files: $CHANGED_FILES" # TODO: limit to specifications/ directory, except dev? + echo "Changed files: $CHANGED_FILES" - # Check if the specific file was modified - if echo "$CHANGED_FILES" | grep -q "\$id": "https://raw\.githubusercontent\.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-([0-9]+(\.[0-9]+)+)\.json"; then - echo "::set-output name=important_file_changed::true" - else - echo "::set-output name=important_file_changed::false" - fi + # Check for pattern specification files (excluding dev versions) + SPEC_FILES=$(echo "$CHANGED_FILES" | grep -E '^specifications/pattern-specification/.*\.md$' | grep -v 'dev\.md$' || true) + echo "spec_files<> $GITHUB_OUTPUT + echo "$SPEC_FILES" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT - # need to instead of checking regex for that, grep for changed files includes anything inside of specifications? - # Because right now it will only output for pattern-schema rather than pattern-schema-latest or or pattern spec - # I currently have it set so that the diff should only run in specifications/ which might be better still - # that way I can reuse this portion of the code and just split it so that we check schema version vs pattern spec version on two separate actions + # Check for pattern schema files (excluding dev versions) + SCHEMA_FILES=$(echo "$CHANGED_FILES" | grep -E '^specifications/pattern-schema/.*\.json$' | grep -v 'dev\.json$' || true) + echo "schema_files<> $GITHUB_OUTPUT + echo "$SCHEMA_FILES" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT - - name: Run action if pattern schema version changed - if: steps.changed-files.outputs.important_file_changed == 'true' - run: | - echo "pattern-schema-1.0.0.json was modified! Changes to this file are not permitted." # echo "::error::" will display red in the logs - f1 - # Add your specific commands here, e.g., run a script, send a notification, etc. - echo "This is the content of the changed file:" - cat important-file.txt - # if a file gets added check ID variable - check everything in last file name after "$id" - regex? - # maybe what I do is move grep here for pattern schema file, then that way the output can be specific to that and I can use it as a variable, e.g. $pattern_schema_changed sort of thing? if != regex-for-dev blah blah - # if I go this route I have to repeat this code for pattern spec version also? + # Check for new pattern specification files that don't exist in main + NEW_SPEC_FILES="" # https://community.unix.com/t/if-condition-to-check-one-file-newer-than-the-other-first-file-name-uncertain/243995/7 + if [ -n "$SPEC_FILES" ]; then + echo "$SPEC_FILES" | while read -r file; do + if [ -n "$file" ]; then + if ! git show main:$file >/dev/null 2>&1' then + echo "$file" + fi + fi + done > /tmp/new_spec_files.txt + NEW_SPEC_FILES=$(cat /tmp/new_spec_files.txt 2>/dev/null || true) + # Check for new pattern schema files that don't exist in main + NEW_SCHEMA_FILES="" + if [ -n "$SCHEMA_FILES" ]; then + echo "$SCHEMA_FILES" | while read -r file; do + if [ -n "$file" ]; then + if ! git show main:$file >/dev/null 2>&1' then + echo "$file" + fi + fi + done > /tmp/new_spec_files.txt + NEW_SPEC_FILES=$(cat /tmp/new_spec_files.txt 2>/dev/null || true) - name: Run action if pattern specification version changed - if: steps.changed-files.outputs.important_file_changed == 'true' + if: steps.changed-files.outputs.spec_files != '' run: | - echo "pattern-schema-1.0.0.json was modified! Changes to this file are not permitted." # echo "::error::" - f1 + echo "::error::pattern-schema-1.0.0.json was modified! Changes to this file are not permitted." + echo "This is the content of the changed file:" + cat spec_files.txt + + - name: Run action if pattern schema version changed + if: steps.changed-files.outputs.spec_files != '' + run: | + echo "::error:: pattern-schema-1.0.0.json was modified! Changes to this file are not permitted." # Add your specific commands here, e.g., run a script, send a notification, etc. echo "This is the content of the changed file:" - cat important-file.txt # this needs to be distinct from the schema version, so I don't think we can lump that into "important-file.txt" or whatever - # if a file gets added check ID variable - check everything in last file name after "$id" - regex? + cat schema_files.txt + + # eventually, we may want to create a new if chunk to check that what is in this file is different than what is in main + # throw an error telling you not to do this in main + # if there is a totally new file, we would want to check the version in the file name against the version in the file text 'version' variable + # throw another error if it doesn't match + - name: For new files check if file name and ID variable match - if: steps.changed-files.outputs.important_file_changed == 'true' - run: | # if new file check name and id variable match? Will new files be included in changed files? I assume they are + if: steps.changed-files.outputs.important_file_changed != '' + #if the file is new, make the file version a variable (same regex?), then check that variable against the "version: " inside the file by using grep? + run: | + grep -q + #will new files be included in changed files? I assume they are From b09999a41c66c2603a5a5add24cd820444bc8c95 Mon Sep 17 00:00:00 2001 From: chynasan Date: Tue, 16 Sep 2025 10:49:30 -0500 Subject: [PATCH 4/7] Updated based on feedback and with simplified tasks and including schema validation --- .github/workflows/validate-pattern-specs.yml | 126 ++++++++++++------- 1 file changed, 82 insertions(+), 44 deletions(-) diff --git a/.github/workflows/validate-pattern-specs.yml b/.github/workflows/validate-pattern-specs.yml index d229df2a..19758970 100644 --- a/.github/workflows/validate-pattern-specs.yml +++ b/.github/workflows/validate-pattern-specs.yml @@ -1,5 +1,5 @@ --- -name: Validate Pattern Spec Version +name: Validate Pattern Specs concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} @@ -9,18 +9,20 @@ on: pull_request: branches: - main - - stable-* - tags: - - "*" + push: + branches: + - main + jobs: - version_check: + validate-pattern-specs: runs-on: ubuntu-latest steps: - - name: Checkout code + - name: Checkout PR branch uses: actions/checkout@v4 with: - fetch-depth: 0 # Fetch full history to allow diffing against previous commits + fetch-depth: 0 + - name: Checkout main branch run: | git fetch origin main:main @@ -28,16 +30,9 @@ jobs: - name: Get changed files id: changed-files run: | - # Determine the range of commits to analyze - if [ "${{ github.event_name }}" == "pull_request" ]; then - BASE_COMMIT=${{ github.event.pull_request.base.sha }} - HEAD_COMMIT=${{ github.event.pull_request.head.sha }} - CHANGED_FILES=$(git diff --name-only $BASE_COMMIT..$HEAD_COMMIT) - else - CHANGED_FILES=$(git diff --name-only HEAD^..HEAD) - fi - - echo "Changed files: $CHANGED_FILES" + BASE_COMMIT=${{ github.event.pull_request.base.sha }} + HEAD_COMMIT=${{ github.event.pull_request.head.sha }} + CHANGED_FILES=$(git diff --name-only $BASE_COMMIT..$HEAD_COMMIT) # Check for pattern specification files (excluding dev versions) SPEC_FILES=$(echo "$CHANGED_FILES" | grep -E '^specifications/pattern-specification/.*\.md$' | grep -v 'dev\.md$' || true) @@ -51,54 +46,97 @@ jobs: echo "$SCHEMA_FILES" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT - # Check for new pattern specification files that don't exist in main - NEW_SPEC_FILES="" # https://community.unix.com/t/if-condition-to-check-one-file-newer-than-the-other-first-file-name-uncertain/243995/7 + # Check for new specification files that don't exist on main branch + NEW_SPEC_FILES="" if [ -n "$SPEC_FILES" ]; then echo "$SPEC_FILES" | while read -r file; do if [ -n "$file" ]; then - if ! git show main:$file >/dev/null 2>&1' then + if ! git show main:$file >/dev/null 2>&1; then echo "$file" fi fi done > /tmp/new_spec_files.txt NEW_SPEC_FILES=$(cat /tmp/new_spec_files.txt 2>/dev/null || true) + fi + echo "new_spec_files<> $GITHUB_OUTPUT + echo "$NEW_SPEC_FILES" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT - # Check for new pattern schema files that don't exist in main + # Check for new schema files that don't exist on main branch NEW_SCHEMA_FILES="" if [ -n "$SCHEMA_FILES" ]; then echo "$SCHEMA_FILES" | while read -r file; do if [ -n "$file" ]; then - if ! git show main:$file >/dev/null 2>&1' then + if ! git show main:$file >/dev/null 2>&1; then echo "$file" fi fi - done > /tmp/new_spec_files.txt - NEW_SPEC_FILES=$(cat /tmp/new_spec_files.txt 2>/dev/null || true) + done > /tmp/new_schema_files.txt + NEW_SCHEMA_FILES=$(cat /tmp/new_schema_files.txt 2>/dev/null || true) + fi + echo "new_schema_files<> $GITHUB_OUTPUT + echo "$NEW_SCHEMA_FILES" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT - - name: Run action if pattern specification version changed + - name: Validate pattern specification files if: steps.changed-files.outputs.spec_files != '' run: | - echo "::error::pattern-schema-1.0.0.json was modified! Changes to this file are not permitted." - echo "This is the content of the changed file:" - cat spec_files.txt + echo "${{ steps.changed-files.outputs.spec_files }}" | while read -r file; do + if [ -n "$file" ]; then + # Check if file exists in main branch + if git show main:$file >/dev/null 2>&1; then + # Compare with main branch - fail if different + if ! git diff --quiet main:$file HEAD:$file; then + echo "::error::Pattern specification file $file differs from main branch" + exit 1 + fi + else + # New specification file - validate version consistency + # Extract version once + FILENAME_VERSION=$(echo "$file" | sed -n 's/.*pattern-specification-\([^.]*\)\.md/\1/p') + FILE_VERSION=$(git show HEAD:$file | sed -n 's/^version:[[:space:]]*\([^[:space:]]*\).*/\1/p' | head -1) - - name: Run action if pattern schema version changed - if: steps.changed-files.outputs.spec_files != '' + # Validate filename vs content version + if [ "$FILENAME_VERSION" != "$FILE_VERSION" ]; then + echo "::error::Version mismatch..." + exit 1 + fi + + # Validate latest file points to new version + LATEST_VERSION=$(git show HEAD:specifications/pattern-specification/pattern-specification-latest.md | sed -n 's/^version:[[:space:]]*\([^[:space:]]*\).*/\1/p' | head -1) + if [ "$LATEST_VERSION" != "$FILENAME_VERSION" ]; then + echo "::error::Latest file not updated..." + exit 1 + fi + fi + fi + done + + - name: Validate pattern schema files + if: steps.changed-files.outputs.schema_files != '' run: | - echo "::error:: pattern-schema-1.0.0.json was modified! Changes to this file are not permitted." - # Add your specific commands here, e.g., run a script, send a notification, etc. - echo "This is the content of the changed file:" - cat schema_files.txt + echo "${{ steps.changed-files.outputs.schema_files }}" | while read -r file; do + if [ -n "$file" ]; then + # Extract version from filename + VERSION=$(echo "$file" | sed -n 's/.*pattern-schema-\([^.]*\)\.json/\1/p') - # eventually, we may want to create a new if chunk to check that what is in this file is different than what is in main - # throw an error telling you not to do this in main - # if there is a totally new file, we would want to check the version in the file name against the version in the file text 'version' variable - # throw another error if it doesn't match + if [ -n "$VERSION" ]; then + # Construct expected ID once + EXPECTED_ID="https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-${VERSION}.json" + # Validate new schema file's $id field + ACTUAL_ID=$(git show HEAD:$file | jq -r '.["$id"] // empty') + if [ "$ACTUAL_ID" != "$EXPECTED_ID" ]; then + echo "::error::Schema file $file has incorrect \$id field. Expected: $EXPECTED_ID, Got: $ACTUAL_ID" + exit 1 + fi - - name: For new files check if file name and ID variable match - if: steps.changed-files.outputs.important_file_changed != '' - #if the file is new, make the file version a variable (same regex?), then check that variable against the "version: " inside the file by using grep? - run: | - grep -q - #will new files be included in changed files? I assume they are + # Validate latest schema file points to new version + LATEST_ID=$(git show HEAD:specifications/pattern-schema/pattern-schema-latest.json | jq -r '.["$id"] // empty') + if [ "$LATEST_ID" != "$EXPECTED_ID" ]; then + echo "::error::Latest schema file was not updated to point to new version $VERSION. Expected \$id: $EXPECTED_ID, Got: $LATEST_ID" + exit 1 + fi + fi + fi + done From 76f165e250a095ecbb4938d3a85dd4a7d4dca8f6 Mon Sep 17 00:00:00 2001 From: chynasan Date: Tue, 16 Sep 2025 12:34:14 -0500 Subject: [PATCH 5/7] Updated to ensure meaningful error messages, and to ensure github comments were addressed. --- .github/workflows/validate-pattern-specs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/validate-pattern-specs.yml b/.github/workflows/validate-pattern-specs.yml index 19758970..7d42f6c6 100644 --- a/.github/workflows/validate-pattern-specs.yml +++ b/.github/workflows/validate-pattern-specs.yml @@ -98,14 +98,14 @@ jobs: # Validate filename vs content version if [ "$FILENAME_VERSION" != "$FILE_VERSION" ]; then - echo "::error::Version mismatch..." + echo "::error::Specification file name does not match the specification version. Expected: $FILENAME_VERSION, Got: $FILE_VERSION" exit 1 fi # Validate latest file points to new version LATEST_VERSION=$(git show HEAD:specifications/pattern-specification/pattern-specification-latest.md | sed -n 's/^version:[[:space:]]*\([^[:space:]]*\).*/\1/p' | head -1) if [ "$LATEST_VERSION" != "$FILENAME_VERSION" ]; then - echo "::error::Latest file not updated..." + echo "::error::Latest version of pattern specification does not point to the latest numeric version. Expected: $LATEST_VERSION, Got: $FILENAME_VERSION" exit 1 fi fi From 50e6386664e94ee8af748b0c10739e45de2e5860 Mon Sep 17 00:00:00 2001 From: chynasan Date: Wed, 17 Sep 2025 12:28:54 -0500 Subject: [PATCH 6/7] Updating the pattern spec validation to address comments in PR and fix bugs related to the latest version checks --- .github/workflows/validate-pattern-specs.yml | 139 +++++++++---------- 1 file changed, 63 insertions(+), 76 deletions(-) diff --git a/.github/workflows/validate-pattern-specs.yml b/.github/workflows/validate-pattern-specs.yml index 7d42f6c6..6aa9c089 100644 --- a/.github/workflows/validate-pattern-specs.yml +++ b/.github/workflows/validate-pattern-specs.yml @@ -23,115 +23,102 @@ jobs: with: fetch-depth: 0 - - name: Checkout main branch - run: | - git fetch origin main:main - - name: Get changed files id: changed-files run: | - BASE_COMMIT=${{ github.event.pull_request.base.sha }} - HEAD_COMMIT=${{ github.event.pull_request.head.sha }} - CHANGED_FILES=$(git diff --name-only $BASE_COMMIT..$HEAD_COMMIT) - - # Check for pattern specification files (excluding dev versions) - SPEC_FILES=$(echo "$CHANGED_FILES" | grep -E '^specifications/pattern-specification/.*\.md$' | grep -v 'dev\.md$' || true) - echo "spec_files<> $GITHUB_OUTPUT - echo "$SPEC_FILES" >> $GITHUB_OUTPUT + # New specification files + NEW_SPEC_FILES=$(git diff origin/main --name-only --diff-filter=A -- specifications/pattern-specification/*.md ":(exclude)*dev.md" || true) + echo "new_spec_files<> $GITHUB_OUTPUT + echo "$NEW_SPEC_FILES" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT - # Check for pattern schema files (excluding dev versions) - SCHEMA_FILES=$(echo "$CHANGED_FILES" | grep -E '^specifications/pattern-schema/.*\.json$' | grep -v 'dev\.json$' || true) - echo "schema_files<> $GITHUB_OUTPUT - echo "$SCHEMA_FILES" >> $GITHUB_OUTPUT + # Modified specification files + MODIFIED_SPEC_FILES=$(git diff origin/main --name-only --diff-filter=M -- specifications/pattern-specification/*.md ":(exclude)*dev.md" || true) + echo "modified_spec_files<> $GITHUB_OUTPUT + echo "$MODIFIED_SPEC_FILES" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT - # Check for new specification files that don't exist on main branch - NEW_SPEC_FILES="" - if [ -n "$SPEC_FILES" ]; then - echo "$SPEC_FILES" | while read -r file; do - if [ -n "$file" ]; then - if ! git show main:$file >/dev/null 2>&1; then - echo "$file" - fi - fi - done > /tmp/new_spec_files.txt - NEW_SPEC_FILES=$(cat /tmp/new_spec_files.txt 2>/dev/null || true) - fi - echo "new_spec_files<> $GITHUB_OUTPUT - echo "$NEW_SPEC_FILES" >> $GITHUB_OUTPUT + # All changed specification files + ALL_SPEC_FILES=$(git diff origin/main --name-only --diff-filter=AM -- specifications/pattern-specification/*.md ":(exclude)*dev.md" || true) + echo "spec_files<> $GITHUB_OUTPUT + echo "$ALL_SPEC_FILES" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT - # Check for new schema files that don't exist on main branch - NEW_SCHEMA_FILES="" - if [ -n "$SCHEMA_FILES" ]; then - echo "$SCHEMA_FILES" | while read -r file; do - if [ -n "$file" ]; then - if ! git show main:$file >/dev/null 2>&1; then - echo "$file" - fi - fi - done > /tmp/new_schema_files.txt - NEW_SCHEMA_FILES=$(cat /tmp/new_schema_files.txt 2>/dev/null || true) - fi + # New schema files + NEW_SCHEMA_FILES=$(git diff origin/main --name-only --diff-filter=A -- specifications/pattern-schema/*.json ":(exclude)*dev.json" || true) echo "new_schema_files<> $GITHUB_OUTPUT echo "$NEW_SCHEMA_FILES" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT + # Modified schema files + MODIFIED_SCHEMA_FILES=$(git diff origin/main --name-only --diff-filter=M -- specifications/pattern-schema/*.json ":(exclude)*dev.json" || true) + echo "modified_schema_files<> $GITHUB_OUTPUT + echo "$MODIFIED_SCHEMA_FILES" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + # All changed schema files + ALL_SCHEMA_FILES=$(git diff origin/main --name-only --diff-filter=AM -- specifications/pattern-schema/*.json ":(exclude)*dev.json" || true) + echo "schema_files<> $GITHUB_OUTPUT + echo "$ALL_SCHEMA_FILES" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + - name: Validate pattern specification files if: steps.changed-files.outputs.spec_files != '' run: | - echo "${{ steps.changed-files.outputs.spec_files }}" | while read -r file; do - if [ -n "$file" ]; then - # Check if file exists in main branch - if git show main:$file >/dev/null 2>&1; then - # Compare with main branch - fail if different - if ! git diff --quiet main:$file HEAD:$file; then - echo "::error::Pattern specification file $file differs from main branch" - exit 1 - fi - else - # New specification file - validate version consistency - # Extract version once - FILENAME_VERSION=$(echo "$file" | sed -n 's/.*pattern-specification-\([^.]*\)\.md/\1/p') - FILE_VERSION=$(git show HEAD:$file | sed -n 's/^version:[[:space:]]*\([^[:space:]]*\).*/\1/p' | head -1) - - # Validate filename vs content version - if [ "$FILENAME_VERSION" != "$FILE_VERSION" ]; then - echo "::error::Specification file name does not match the specification version. Expected: $FILENAME_VERSION, Got: $FILE_VERSION" - exit 1 - fi + # First, check if any existing specification files were modified (not allowed) + if [ -n "${{ steps.changed-files.outputs.modified_spec_files }}" ]; then + echo "::error::Modifying existing versioned specification files is not allowed. Modified files: ${{ steps.changed-files.outputs.modified_spec_files }}" + exit 1 + fi - # Validate latest file points to new version - LATEST_VERSION=$(git show HEAD:specifications/pattern-specification/pattern-specification-latest.md | sed -n 's/^version:[[:space:]]*\([^[:space:]]*\).*/\1/p' | head -1) - if [ "$LATEST_VERSION" != "$FILENAME_VERSION" ]; then - echo "::error::Latest version of pattern specification does not point to the latest numeric version. Expected: $LATEST_VERSION, Got: $FILENAME_VERSION" - exit 1 - fi + # Validate new specification files + NEW_SPEC_FILES="${{ steps.changed-files.outputs.new_spec_files }}" + while IFS= read -r file; do + if [ -n "$file" ]; then + # New specification file - validate version consistency + # Extract version once + FILENAME_VERSION=$(echo "$file" | sed -n 's/.*pattern-specification-\([^/]*\)\.md/\1/p') + FILE_VERSION=$(sed -n 's/^version:[[:space:]]*\([^[:space:]]*\).*/\1/p' "$file" | head -1) + + # Validate filename vs content version + if [ "$FILENAME_VERSION" != "$FILE_VERSION" ]; then + echo "::error::Specification file name does not match the specification version. Expected: $FILENAME_VERSION, Got: $FILE_VERSION" + exit 1 fi fi - done + done <<< "$NEW_SPEC_FILES" - name: Validate pattern schema files if: steps.changed-files.outputs.schema_files != '' run: | - echo "${{ steps.changed-files.outputs.schema_files }}" | while read -r file; do + SCHEMA_FILES="${{ steps.changed-files.outputs.schema_files }}" + while IFS= read -r file; do if [ -n "$file" ]; then - # Extract version from filename - VERSION=$(echo "$file" | sed -n 's/.*pattern-schema-\([^.]*\)\.json/\1/p') + # Skip the latest file to ensure proper validation of $id field + if [[ "$file" == *"pattern-schema-latest.json" ]]; then + continue + fi + + # Check if this is a modified existing versioned file (not allowed) + if git show origin/main:$file >/dev/null 2>&1; then + echo "::error::Modifying existing versioned schema files is not allowed. Modified file: $file" + exit 1 + fi + # Extract version from filename + VERSION=$(echo "$file" | sed -n 's/.*pattern-schema-\([^/]*\)\.json/\1/p') if [ -n "$VERSION" ]; then - # Construct expected ID once + # Construct expected ID for the new versioned file EXPECTED_ID="https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-${VERSION}.json" - # Validate new schema file's $id field + # Validate versioned schema file's $id field ACTUAL_ID=$(git show HEAD:$file | jq -r '.["$id"] // empty') if [ "$ACTUAL_ID" != "$EXPECTED_ID" ]; then echo "::error::Schema file $file has incorrect \$id field. Expected: $EXPECTED_ID, Got: $ACTUAL_ID" exit 1 fi - # Validate latest schema file points to new version + # Validate latest schema file $id points to new version LATEST_ID=$(git show HEAD:specifications/pattern-schema/pattern-schema-latest.json | jq -r '.["$id"] // empty') if [ "$LATEST_ID" != "$EXPECTED_ID" ]; then echo "::error::Latest schema file was not updated to point to new version $VERSION. Expected \$id: $EXPECTED_ID, Got: $LATEST_ID" @@ -139,4 +126,4 @@ jobs: fi fi fi - done + done <<< "$SCHEMA_FILES" \ No newline at end of file From 8614cf08fac2a00994615b2d3bbe65332d7c3414 Mon Sep 17 00:00:00 2001 From: chynasan Date: Wed, 17 Sep 2025 12:34:47 -0500 Subject: [PATCH 7/7] fixed minor formatting issue --- .github/workflows/validate-pattern-specs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/validate-pattern-specs.yml b/.github/workflows/validate-pattern-specs.yml index 6aa9c089..3efe6ea5 100644 --- a/.github/workflows/validate-pattern-specs.yml +++ b/.github/workflows/validate-pattern-specs.yml @@ -126,4 +126,4 @@ jobs: fi fi fi - done <<< "$SCHEMA_FILES" \ No newline at end of file + done <<< "$SCHEMA_FILES"