From 0577defaf155bd986c3cb99bbb41a3a1b8cd29f5 Mon Sep 17 00:00:00 2001 From: chynasan Date: Mon, 15 Sep 2025 11:21:33 -0500 Subject: [PATCH 01/51] 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 cf64adc2be84d9c7c3e882e37de25b5f05273060 Mon Sep 17 00:00:00 2001 From: chynasan Date: Mon, 15 Sep 2025 11:35:54 -0500 Subject: [PATCH 02/51] 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 3a31ea570394973f502cf5018d99d021c5ab565d Mon Sep 17 00:00:00 2001 From: chynasan Date: Mon, 15 Sep 2025 13:08:17 -0500 Subject: [PATCH 03/51] 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 1ccb60653559097d9e6b56dbd830d31045e89fa0 Mon Sep 17 00:00:00 2001 From: chynasan Date: Tue, 16 Sep 2025 10:49:30 -0500 Subject: [PATCH 04/51] 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 eb05b6673dea30868ce7b8f4f5b9a752a0472d8a Mon Sep 17 00:00:00 2001 From: chynasan Date: Tue, 16 Sep 2025 12:34:14 -0500 Subject: [PATCH 05/51] 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 206f2b2e61b35c69257f578cfaa73b229ae4dda6 Mon Sep 17 00:00:00 2001 From: chynasan Date: Tue, 16 Sep 2025 12:42:48 -0500 Subject: [PATCH 06/51] Updated dev to 2.0.0 for testing. --- specifications/pattern-schema/pattern-schema-dev.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specifications/pattern-schema/pattern-schema-dev.json b/specifications/pattern-schema/pattern-schema-dev.json index 22b5a9f0..f43f42a4 100644 --- a/specifications/pattern-schema/pattern-schema-dev.json +++ b/specifications/pattern-schema/pattern-schema-dev.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-dev.json", + "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-dev-2.0.0.json", "title": "Ansible Pattern Schema", "description": "A schema for validating Ansible pattern definitions", "type": "object", From c6c7d909b8a9f58223a01a20f785d9c9247b65d7 Mon Sep 17 00:00:00 2001 From: chynasan Date: Tue, 16 Sep 2025 12:49:05 -0500 Subject: [PATCH 07/51] Testing the creation of pattern-schema-2.0.0, expected pass in CI --- .../pattern-schema/pattern-schema-2.0.0.json | 303 ++++++++++++++++++ .../pattern-schema/pattern-schema-dev.json | 2 +- 2 files changed, 304 insertions(+), 1 deletion(-) create mode 100644 specifications/pattern-schema/pattern-schema-2.0.0.json diff --git a/specifications/pattern-schema/pattern-schema-2.0.0.json b/specifications/pattern-schema/pattern-schema-2.0.0.json new file mode 100644 index 00000000..b940af4e --- /dev/null +++ b/specifications/pattern-schema/pattern-schema-2.0.0.json @@ -0,0 +1,303 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-2.0.0.json", + "title": "Ansible Pattern Schema", + "description": "A schema for validating Ansible pattern definitions", + "type": "object", + "x-ansible-lint": [ + "patterns/*/meta/pattern.json" + ], + "required": [ + "schema_version", + "name", + "title", + "description", + "short_description", + "aap_resources" + ], + "properties": { + "schema_version": { + "description": "Version of this schema being used", + "type": "string" + }, + "name": { + "description": "Machine-readable name for the pattern. MUST match the pattern directory name, and be limited to lowercase letters, numbers, and underscores, with a maximum length of 64 character.", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-z0-9_]+$", + "type": "string" + }, + "title": { + "description": "Human readable title for the pattern, may be used for pattern catalog and display purposes. It should meaningfully convey what the pattern does. It MUST be limited to 64 characters and should start with an action verb (e.g. Create an AWS EC2 instance).", + "maxLength": 64, + "type": "string" + }, + "description": { + "description": "A detailed explanation of what the pattern does. Use complete sentences with correct grammar to describe the functionality of the pattern and provide usage guidance. The description should provide context to the user so that they understand the purpose of the pattern and what they will gain by using it.", + "type": "string" + }, + "short_description": { + "description": "A one-line description of the entry point. Ideally it is a phrase and not a sentence. The short description should always be a string and never a list, and should not end in a period.", + "type": "string" + }, + "tags": { + "description": "List of tags associated with the pattern. Tags may be used to filter patterns in catalog UIs such as automation hub and Red Hat Developer Hub.", + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "aap_resources": { + "description": "Define the resources required in Ansible Automation Platform (AAP) to run the pattern automation.", + "type": "object", + "required": [ + "controller_project", + "controller_job_templates" + ], + "properties": { + "controller_project": { + "description": "Information about the project to be created in automation controller. Every pattern MUST specify a controller project, which will be sourced from the pattern's parent collection published in private automation hub.", + "type": "object", + "required": [ + "name", + "description" + ], + "properties": { + "name": { + "description": "Name for the project to be created in automation controller. The value MUST not exceed 200 characters.", + "maxLength": 200, + "type": "string" + }, + "description": { + "description": "Description of the project to be created in automation controller.", + "type": "string" + } + } + }, + "controller_execution_environment": { + "description": "Definition of the execution environment (EE) to be used by the pattern. An EE should be included if the pattern requires dependencies that are not available in the default controller EE. The EE image MUST be available in Private Automation Hub.", + "type": "object", + "required": [ + "name", + "description", + "image_name" + ], + "properties": { + "name": { + "description": "Name for the EE to be created in automation controller. The value MUST not exceed 200 characters.", + "maxLength": 200, + "type": "string" + }, + "description": { + "description": "Description for the EE to be created in automation controller.", + "type": "string" + }, + "image_name": { + "description": "Name of the execution environment (EE) image as published in Private Automation Hub (PAH). Use the format /, such as 'cloud/aws_ops_ee'. If the pattern is created before the image is published, the image MUST be uploaded to PAH with the name specified here. If the image is already in PAH, this value MUST exactly match the existing image name.", + "type": "string" + }, + "pull": { + "description": "Whether to pull the EE image before running.", + "type": "string", + "enum": [ + "always", + "missing", + "never" + ] + } + } + }, + "controller_labels": { + "description": "Labels to create for the pattern if not already present in automation controller.", + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "uniqueItems": true + }, + "controller_job_templates": { + "description": "A list of one or more job templates to be created in automation controller.", + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": [ + "name", + "description", + "playbook" + ], + "properties": { + "name": { + "description": "Name for the job template to be created in automation controller. The value MUST not exceed 200 characters.", + "maxLength": 200, + "type": "string" + }, + "description": { + "description": "Description for the job template to be created in automation controller.", + "type": "string" + }, + "execution_environment": { + "description": "Name of the execution environment to use for job template execution. MUST match the EE name defined in controller_execution_environment, IF defined.", + "type": "string" + }, + "playbook": { + "description": "Name of the playbook file included in the pattern `automations` directory.", + "type": "string" + }, + "primary": { + "description": "Whether the playbook is the primary playbook for this job template. If there are multiple job templates included, one should be labeled as the primary job template.", + "type": "boolean" + }, + "ask_credential_on_launch": { + "description": "Whether the user must select a credential when launching the job template.", + "type": "boolean" + }, + "ask_inventory_on_launch": { + "description": "Whether the user must select an inventory when launching the job template.", + "type": "boolean" + }, + "ask_verbosity_on_launch": { + "description": "Whether the user must choose a verbosity level when launching the job template.", + "type": "boolean" + }, + "labels": { + "description": "List of labels to associate with this resource. Each label MUST either already exist in automation controller or be defined in the 'controller_labels' field of this pattern to be created during pattern loading.", + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "uniqueItems": true + }, + "survey": { + "description": "A survey that captures required and optional variables from the user for configuring job templates.", + "type": "object", + "required": [ + "name", + "description", + "spec" + ], + "properties": { + "name": { + "type": "string", + "description": "Name of the survey." + }, + "description": { + "type": "string", + "description": "Description of the survey." + }, + "spec": { + "type": "array", + "description": "List of survey questions.", + "items": { + "type": "object", + "required": [ + "type", + "question_name", + "question_description", + "variable", + "required" + ], + "properties": { + "type": { + "type": "string", + "description": "Type of input field.", + "enum": [ + "text", + "textarea", + "password", + "integer", + "float", + "multiplechoice", + "multiselect" + ] + }, + "question_name": { + "type": "string", + "description": "Human-readable question title." + }, + "question_description": { + "type": "string", + "description": "A prompt that instructs the user to supply information or other input to the pattern. Use a complete sentence with correct grammar. Start with an imperative verb form and be specific and actionable. Address the user directly in a conversational and professional way." + }, + "variable": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-z0-9_]+$", + "description": "Variable name used to store the answer." + }, + "required": { + "type": "boolean", + "description": "Whether this question must be answered." + }, + "choices": { + "type": "array", + "description": "List of selectable values for multiple choice questions.", + "items": { + "type": "string" + } + }, + "default": { + "type": "string", + "description": "Default value for the input field." + } + }, + "allOf": [ + { + "if": { + "properties": { + "type": { + "const": "multiplechoice" + } + } + }, + "then": { + "required": [ + "choices" + ] + } + } + ] + } + } + } + } + } + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "aap_resources": { + "properties": { + "controller_job_templates": { + "type": "array", + "contains": { + "required": [ + "labels" + ] + } + } + } + } + } + }, + "then": { + "properties": { + "aap_resources": { + "required": [ + "controller_labels" + ] + } + } + } + } + ] +} diff --git a/specifications/pattern-schema/pattern-schema-dev.json b/specifications/pattern-schema/pattern-schema-dev.json index f43f42a4..22b5a9f0 100644 --- a/specifications/pattern-schema/pattern-schema-dev.json +++ b/specifications/pattern-schema/pattern-schema-dev.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-dev-2.0.0.json", + "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-dev.json", "title": "Ansible Pattern Schema", "description": "A schema for validating Ansible pattern definitions", "type": "object", From e8189b19ee1698cf37aaa7ab4635492bacace9e1 Mon Sep 17 00:00:00 2001 From: chynasan Date: Tue, 16 Sep 2025 12:52:32 -0500 Subject: [PATCH 08/51] Test for mismatch 2.0.0/2.2.2 spec file - should fail CI. --- specifications/pattern-schema/pattern-schema-2.0.0.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specifications/pattern-schema/pattern-schema-2.0.0.json b/specifications/pattern-schema/pattern-schema-2.0.0.json index b940af4e..c1780764 100644 --- a/specifications/pattern-schema/pattern-schema-2.0.0.json +++ b/specifications/pattern-schema/pattern-schema-2.0.0.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-2.0.0.json", + "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-2.2.2.json", "title": "Ansible Pattern Schema", "description": "A schema for validating Ansible pattern definitions", "type": "object", From d864ab71c3115cf20c6ecd309ccc6bc683beb81c Mon Sep 17 00:00:00 2001 From: chynasan Date: Tue, 16 Sep 2025 12:56:35 -0500 Subject: [PATCH 09/51] Test for mismatch 2.0.0/2.2.2 schema file - should fail CI. --- .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 7d42f6c6..a2c54f28 100644 --- a/.github/workflows/validate-pattern-specs.yml +++ b/.github/workflows/validate-pattern-specs.yml @@ -123,7 +123,7 @@ jobs: 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" - + echo "$EXPECTED_ID" # Validate new schema file's $id field ACTUAL_ID=$(git show HEAD:$file | jq -r '.["$id"] // empty') if [ "$ACTUAL_ID" != "$EXPECTED_ID" ]; then From 85ab63e57a33211febe2d2a848dc30b5cad915d0 Mon Sep 17 00:00:00 2001 From: chynasan Date: Tue, 16 Sep 2025 13:02:00 -0500 Subject: [PATCH 10/51] Test for mismatch 2.0.0/2.2.2 schema file - should fail CI, added echo for debug. --- .github/workflows/validate-pattern-specs.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/validate-pattern-specs.yml b/.github/workflows/validate-pattern-specs.yml index a2c54f28..2a7041ae 100644 --- a/.github/workflows/validate-pattern-specs.yml +++ b/.github/workflows/validate-pattern-specs.yml @@ -123,9 +123,10 @@ jobs: 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" - echo "$EXPECTED_ID" + echo "EXPECTED_ID: '$EXPECTED_ID'" # Validate new schema file's $id field ACTUAL_ID=$(git show HEAD:$file | jq -r '.["$id"] // empty') + echo "ACTUAL_ID: '$ACTUAL_ID'" if [ "$ACTUAL_ID" != "$EXPECTED_ID" ]; then echo "::error::Schema file $file has incorrect \$id field. Expected: $EXPECTED_ID, Got: $ACTUAL_ID" exit 1 From 28da52b71bf42fc1f3995f64ce8eb312af662925 Mon Sep 17 00:00:00 2001 From: chynasan Date: Tue, 16 Sep 2025 13:05:50 -0500 Subject: [PATCH 11/51] Test for mismatch 2.0.0/2.2.2 schema file - should fail CI, added echo for debug, specified bash shell. --- .github/workflows/validate-pattern-specs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/validate-pattern-specs.yml b/.github/workflows/validate-pattern-specs.yml index 2a7041ae..aa45b56a 100644 --- a/.github/workflows/validate-pattern-specs.yml +++ b/.github/workflows/validate-pattern-specs.yml @@ -114,6 +114,7 @@ jobs: - name: Validate pattern schema files if: steps.changed-files.outputs.schema_files != '' + shell: bash run: | echo "${{ steps.changed-files.outputs.schema_files }}" | while read -r file; do if [ -n "$file" ]; then From e2131ede0b3c6599b982692d4292087dfd7f71f3 Mon Sep 17 00:00:00 2001 From: chynasan Date: Tue, 16 Sep 2025 13:10:30 -0500 Subject: [PATCH 12/51] Test for mismatch 1.0.0 schema file with 1.1.1 id value - should fail CI, added echo for debug. --- .github/workflows/validate-pattern-specs.yml | 1 - .../pattern-schema/pattern-schema-1.0.0.json | 2 +- .../pattern-schema/pattern-schema-2.0.0.json | 303 ------------------ 3 files changed, 1 insertion(+), 305 deletions(-) delete mode 100644 specifications/pattern-schema/pattern-schema-2.0.0.json diff --git a/.github/workflows/validate-pattern-specs.yml b/.github/workflows/validate-pattern-specs.yml index aa45b56a..2a7041ae 100644 --- a/.github/workflows/validate-pattern-specs.yml +++ b/.github/workflows/validate-pattern-specs.yml @@ -114,7 +114,6 @@ jobs: - name: Validate pattern schema files if: steps.changed-files.outputs.schema_files != '' - shell: bash run: | echo "${{ steps.changed-files.outputs.schema_files }}" | while read -r file; do if [ -n "$file" ]; then diff --git a/specifications/pattern-schema/pattern-schema-1.0.0.json b/specifications/pattern-schema/pattern-schema-1.0.0.json index 08d46b4e..82c67429 100644 --- a/specifications/pattern-schema/pattern-schema-1.0.0.json +++ b/specifications/pattern-schema/pattern-schema-1.0.0.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-1.0.0.json", + "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-1.1.1.json", "title": "Ansible Pattern Schema", "description": "A schema for validating Ansible pattern definitions", "type": "object", diff --git a/specifications/pattern-schema/pattern-schema-2.0.0.json b/specifications/pattern-schema/pattern-schema-2.0.0.json deleted file mode 100644 index c1780764..00000000 --- a/specifications/pattern-schema/pattern-schema-2.0.0.json +++ /dev/null @@ -1,303 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-2.2.2.json", - "title": "Ansible Pattern Schema", - "description": "A schema for validating Ansible pattern definitions", - "type": "object", - "x-ansible-lint": [ - "patterns/*/meta/pattern.json" - ], - "required": [ - "schema_version", - "name", - "title", - "description", - "short_description", - "aap_resources" - ], - "properties": { - "schema_version": { - "description": "Version of this schema being used", - "type": "string" - }, - "name": { - "description": "Machine-readable name for the pattern. MUST match the pattern directory name, and be limited to lowercase letters, numbers, and underscores, with a maximum length of 64 character.", - "minLength": 1, - "maxLength": 64, - "pattern": "^[a-z0-9_]+$", - "type": "string" - }, - "title": { - "description": "Human readable title for the pattern, may be used for pattern catalog and display purposes. It should meaningfully convey what the pattern does. It MUST be limited to 64 characters and should start with an action verb (e.g. Create an AWS EC2 instance).", - "maxLength": 64, - "type": "string" - }, - "description": { - "description": "A detailed explanation of what the pattern does. Use complete sentences with correct grammar to describe the functionality of the pattern and provide usage guidance. The description should provide context to the user so that they understand the purpose of the pattern and what they will gain by using it.", - "type": "string" - }, - "short_description": { - "description": "A one-line description of the entry point. Ideally it is a phrase and not a sentence. The short description should always be a string and never a list, and should not end in a period.", - "type": "string" - }, - "tags": { - "description": "List of tags associated with the pattern. Tags may be used to filter patterns in catalog UIs such as automation hub and Red Hat Developer Hub.", - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "aap_resources": { - "description": "Define the resources required in Ansible Automation Platform (AAP) to run the pattern automation.", - "type": "object", - "required": [ - "controller_project", - "controller_job_templates" - ], - "properties": { - "controller_project": { - "description": "Information about the project to be created in automation controller. Every pattern MUST specify a controller project, which will be sourced from the pattern's parent collection published in private automation hub.", - "type": "object", - "required": [ - "name", - "description" - ], - "properties": { - "name": { - "description": "Name for the project to be created in automation controller. The value MUST not exceed 200 characters.", - "maxLength": 200, - "type": "string" - }, - "description": { - "description": "Description of the project to be created in automation controller.", - "type": "string" - } - } - }, - "controller_execution_environment": { - "description": "Definition of the execution environment (EE) to be used by the pattern. An EE should be included if the pattern requires dependencies that are not available in the default controller EE. The EE image MUST be available in Private Automation Hub.", - "type": "object", - "required": [ - "name", - "description", - "image_name" - ], - "properties": { - "name": { - "description": "Name for the EE to be created in automation controller. The value MUST not exceed 200 characters.", - "maxLength": 200, - "type": "string" - }, - "description": { - "description": "Description for the EE to be created in automation controller.", - "type": "string" - }, - "image_name": { - "description": "Name of the execution environment (EE) image as published in Private Automation Hub (PAH). Use the format /, such as 'cloud/aws_ops_ee'. If the pattern is created before the image is published, the image MUST be uploaded to PAH with the name specified here. If the image is already in PAH, this value MUST exactly match the existing image name.", - "type": "string" - }, - "pull": { - "description": "Whether to pull the EE image before running.", - "type": "string", - "enum": [ - "always", - "missing", - "never" - ] - } - } - }, - "controller_labels": { - "description": "Labels to create for the pattern if not already present in automation controller.", - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1, - "uniqueItems": true - }, - "controller_job_templates": { - "description": "A list of one or more job templates to be created in automation controller.", - "type": "array", - "minItems": 1, - "items": { - "type": "object", - "required": [ - "name", - "description", - "playbook" - ], - "properties": { - "name": { - "description": "Name for the job template to be created in automation controller. The value MUST not exceed 200 characters.", - "maxLength": 200, - "type": "string" - }, - "description": { - "description": "Description for the job template to be created in automation controller.", - "type": "string" - }, - "execution_environment": { - "description": "Name of the execution environment to use for job template execution. MUST match the EE name defined in controller_execution_environment, IF defined.", - "type": "string" - }, - "playbook": { - "description": "Name of the playbook file included in the pattern `automations` directory.", - "type": "string" - }, - "primary": { - "description": "Whether the playbook is the primary playbook for this job template. If there are multiple job templates included, one should be labeled as the primary job template.", - "type": "boolean" - }, - "ask_credential_on_launch": { - "description": "Whether the user must select a credential when launching the job template.", - "type": "boolean" - }, - "ask_inventory_on_launch": { - "description": "Whether the user must select an inventory when launching the job template.", - "type": "boolean" - }, - "ask_verbosity_on_launch": { - "description": "Whether the user must choose a verbosity level when launching the job template.", - "type": "boolean" - }, - "labels": { - "description": "List of labels to associate with this resource. Each label MUST either already exist in automation controller or be defined in the 'controller_labels' field of this pattern to be created during pattern loading.", - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1, - "uniqueItems": true - }, - "survey": { - "description": "A survey that captures required and optional variables from the user for configuring job templates.", - "type": "object", - "required": [ - "name", - "description", - "spec" - ], - "properties": { - "name": { - "type": "string", - "description": "Name of the survey." - }, - "description": { - "type": "string", - "description": "Description of the survey." - }, - "spec": { - "type": "array", - "description": "List of survey questions.", - "items": { - "type": "object", - "required": [ - "type", - "question_name", - "question_description", - "variable", - "required" - ], - "properties": { - "type": { - "type": "string", - "description": "Type of input field.", - "enum": [ - "text", - "textarea", - "password", - "integer", - "float", - "multiplechoice", - "multiselect" - ] - }, - "question_name": { - "type": "string", - "description": "Human-readable question title." - }, - "question_description": { - "type": "string", - "description": "A prompt that instructs the user to supply information or other input to the pattern. Use a complete sentence with correct grammar. Start with an imperative verb form and be specific and actionable. Address the user directly in a conversational and professional way." - }, - "variable": { - "type": "string", - "minLength": 1, - "maxLength": 64, - "pattern": "^[a-z0-9_]+$", - "description": "Variable name used to store the answer." - }, - "required": { - "type": "boolean", - "description": "Whether this question must be answered." - }, - "choices": { - "type": "array", - "description": "List of selectable values for multiple choice questions.", - "items": { - "type": "string" - } - }, - "default": { - "type": "string", - "description": "Default value for the input field." - } - }, - "allOf": [ - { - "if": { - "properties": { - "type": { - "const": "multiplechoice" - } - } - }, - "then": { - "required": [ - "choices" - ] - } - } - ] - } - } - } - } - } - } - } - } - } - }, - "allOf": [ - { - "if": { - "properties": { - "aap_resources": { - "properties": { - "controller_job_templates": { - "type": "array", - "contains": { - "required": [ - "labels" - ] - } - } - } - } - } - }, - "then": { - "properties": { - "aap_resources": { - "required": [ - "controller_labels" - ] - } - } - } - } - ] -} From bf5b01d3b9e41255f8a8214738b752f972cc7455 Mon Sep 17 00:00:00 2001 From: chynasan Date: Tue, 16 Sep 2025 13:15:43 -0500 Subject: [PATCH 13/51] Test for mismatch 1.0.0 schema file with 1.1.1 id value - should fail CI, fixed echo output in bash to test. --- .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 2a7041ae..313f50f8 100644 --- a/.github/workflows/validate-pattern-specs.yml +++ b/.github/workflows/validate-pattern-specs.yml @@ -123,10 +123,10 @@ jobs: 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" - echo "EXPECTED_ID: '$EXPECTED_ID'" + echo "EXPECTED_ID: $EXPECTED_ID" # Validate new schema file's $id field ACTUAL_ID=$(git show HEAD:$file | jq -r '.["$id"] // empty') - echo "ACTUAL_ID: '$ACTUAL_ID'" + echo "ACTUAL_ID: $ACTUAL_ID" if [ "$ACTUAL_ID" != "$EXPECTED_ID" ]; then echo "::error::Schema file $file has incorrect \$id field. Expected: $EXPECTED_ID, Got: $ACTUAL_ID" exit 1 From eda1ea6a6fe4d911e6f9757e3b15d8dee3a75bb3 Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Tue, 16 Sep 2025 15:31:21 -0400 Subject: [PATCH 14/51] Reconfigure the task to correctly exit the workflow step vs doing a subshell exit --- .github/workflows/validate-pattern-specs.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/validate-pattern-specs.yml b/.github/workflows/validate-pattern-specs.yml index 313f50f8..68923a2c 100644 --- a/.github/workflows/validate-pattern-specs.yml +++ b/.github/workflows/validate-pattern-specs.yml @@ -115,7 +115,8 @@ jobs: - 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') @@ -140,4 +141,4 @@ jobs: fi fi fi - done + done <<< "$SCHEMA_FILES" \ No newline at end of file From 58d4b665e37af0d46b3f2225bf411d7b4285283a Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Tue, 16 Sep 2025 15:37:48 -0400 Subject: [PATCH 15/51] Add more debugging statements --- .github/workflows/validate-pattern-specs.yml | 21 ++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/.github/workflows/validate-pattern-specs.yml b/.github/workflows/validate-pattern-specs.yml index 68923a2c..dfcc9a36 100644 --- a/.github/workflows/validate-pattern-specs.yml +++ b/.github/workflows/validate-pattern-specs.yml @@ -34,6 +34,10 @@ jobs: HEAD_COMMIT=${{ github.event.pull_request.head.sha }} CHANGED_FILES=$(git diff --name-only $BASE_COMMIT..$HEAD_COMMIT) + echo "=== DEBUG: All changed files ===" + echo "$CHANGED_FILES" + echo "================================" + # 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 @@ -42,6 +46,9 @@ jobs: # 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 "=== DEBUG: Schema files found ===" + echo "SCHEMA_FILES: $SCHEMA_FILES" + echo "================================" echo "schema_files<> $GITHUB_OUTPUT echo "$SCHEMA_FILES" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT @@ -116,12 +123,17 @@ jobs: if: steps.changed-files.outputs.schema_files != '' run: | SCHEMA_FILES="${{ steps.changed-files.outputs.schema_files }}" + echo "DEBUG: SCHEMA_FILES = $SCHEMA_FILES" while IFS= read -r file; do + echo "DEBUG: Processing file = $file" if [ -n "$file" ]; then # Extract version from filename - VERSION=$(echo "$file" | sed -n 's/.*pattern-schema-\([^.]*\)\.json/\1/p') + VERSION=$(echo "$file" | sed -n 's/.*pattern-schema-\([^/]*\)\.json/\1/p') + echo "DEBUG: Extracted VERSION = $VERSION" + echo "DEBUG: sed result: $(echo "$file" | sed -n 's/.*pattern-schema-\([^.]*\)\.json/\1/p')" if [ -n "$VERSION" ]; then + echo "DEBUG: VERSION is not empty, proceeding with validation" # Construct expected ID once EXPECTED_ID="https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-${VERSION}.json" echo "EXPECTED_ID: $EXPECTED_ID" @@ -135,10 +147,15 @@ jobs: # Validate latest schema file points to new version LATEST_ID=$(git show HEAD:specifications/pattern-schema/pattern-schema-latest.json | jq -r '.["$id"] // empty') + echo "LATEST_ID: $LATEST_ID" 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 + else + echo "DEBUG: VERSION is empty, skipping validation" fi + else + echo "DEBUG: File is empty, skipping" fi - done <<< "$SCHEMA_FILES" \ No newline at end of file + done <<< "$SCHEMA_FILES" From 726b96f3f3eb947c973e8ff756eac19de0c459c6 Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Tue, 16 Sep 2025 15:51:36 -0400 Subject: [PATCH 16/51] Remove debug statements --- .github/workflows/validate-pattern-specs.yml | 21 +------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/.github/workflows/validate-pattern-specs.yml b/.github/workflows/validate-pattern-specs.yml index dfcc9a36..e9e30cd3 100644 --- a/.github/workflows/validate-pattern-specs.yml +++ b/.github/workflows/validate-pattern-specs.yml @@ -34,10 +34,6 @@ jobs: HEAD_COMMIT=${{ github.event.pull_request.head.sha }} CHANGED_FILES=$(git diff --name-only $BASE_COMMIT..$HEAD_COMMIT) - echo "=== DEBUG: All changed files ===" - echo "$CHANGED_FILES" - echo "================================" - # 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 @@ -46,9 +42,6 @@ jobs: # 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 "=== DEBUG: Schema files found ===" - echo "SCHEMA_FILES: $SCHEMA_FILES" - echo "================================" echo "schema_files<> $GITHUB_OUTPUT echo "$SCHEMA_FILES" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT @@ -100,7 +93,7 @@ jobs: else # New specification file - validate version consistency # Extract version once - FILENAME_VERSION=$(echo "$file" | sed -n 's/.*pattern-specification-\([^.]*\)\.md/\1/p') + 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 @@ -123,23 +116,16 @@ jobs: if: steps.changed-files.outputs.schema_files != '' run: | SCHEMA_FILES="${{ steps.changed-files.outputs.schema_files }}" - echo "DEBUG: SCHEMA_FILES = $SCHEMA_FILES" while IFS= read -r file; do - echo "DEBUG: Processing file = $file" if [ -n "$file" ]; then # Extract version from filename VERSION=$(echo "$file" | sed -n 's/.*pattern-schema-\([^/]*\)\.json/\1/p') - echo "DEBUG: Extracted VERSION = $VERSION" - echo "DEBUG: sed result: $(echo "$file" | sed -n 's/.*pattern-schema-\([^.]*\)\.json/\1/p')" if [ -n "$VERSION" ]; then - echo "DEBUG: VERSION is not empty, proceeding with validation" # Construct expected ID once EXPECTED_ID="https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-${VERSION}.json" - echo "EXPECTED_ID: $EXPECTED_ID" # Validate new schema file's $id field ACTUAL_ID=$(git show HEAD:$file | jq -r '.["$id"] // empty') - echo "ACTUAL_ID: $ACTUAL_ID" if [ "$ACTUAL_ID" != "$EXPECTED_ID" ]; then echo "::error::Schema file $file has incorrect \$id field. Expected: $EXPECTED_ID, Got: $ACTUAL_ID" exit 1 @@ -147,15 +133,10 @@ jobs: # Validate latest schema file points to new version LATEST_ID=$(git show HEAD:specifications/pattern-schema/pattern-schema-latest.json | jq -r '.["$id"] // empty') - echo "LATEST_ID: $LATEST_ID" 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 - else - echo "DEBUG: VERSION is empty, skipping validation" fi - else - echo "DEBUG: File is empty, skipping" fi done <<< "$SCHEMA_FILES" From 57bbe3f6dd230ff1842d5ef069dacdc76179a5ea Mon Sep 17 00:00:00 2001 From: chynasan Date: Tue, 16 Sep 2025 15:14:46 -0500 Subject: [PATCH 17/51] Updated dev to 2.0.0 for testing, expected to pass validate-pattern-specs CI test. --- specifications/pattern-schema/pattern-schema-dev.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specifications/pattern-schema/pattern-schema-dev.json b/specifications/pattern-schema/pattern-schema-dev.json index 22b5a9f0..f43f42a4 100644 --- a/specifications/pattern-schema/pattern-schema-dev.json +++ b/specifications/pattern-schema/pattern-schema-dev.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-dev.json", + "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-dev-2.0.0.json", "title": "Ansible Pattern Schema", "description": "A schema for validating Ansible pattern definitions", "type": "object", From e9991ca9537cdc4e527c74e05d515145c19ac595 Mon Sep 17 00:00:00 2001 From: chynasan Date: Tue, 16 Sep 2025 15:18:17 -0500 Subject: [PATCH 18/51] Updated dev to 2.0.0 for testing, expected to pass validate-pattern-specs CI test. --- specifications/pattern-schema/pattern-schema-1.0.0.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specifications/pattern-schema/pattern-schema-1.0.0.json b/specifications/pattern-schema/pattern-schema-1.0.0.json index 82c67429..08d46b4e 100644 --- a/specifications/pattern-schema/pattern-schema-1.0.0.json +++ b/specifications/pattern-schema/pattern-schema-1.0.0.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-1.1.1.json", + "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-1.0.0.json", "title": "Ansible Pattern Schema", "description": "A schema for validating Ansible pattern definitions", "type": "object", From fa0b4b73feae2aa480a30e0be4bdabc56c4a8449 Mon Sep 17 00:00:00 2001 From: chynasan Date: Tue, 16 Sep 2025 15:34:28 -0500 Subject: [PATCH 19/51] Created pattern-schema-2.0.0 (but that was a lie), expected to pass validate-pattern-specs CI test. --- .../pattern-schema/pattern-schema-2.0.0.json | 303 ++++++++++++++++++ .../pattern-schema/pattern-schema-dev.json | 2 +- 2 files changed, 304 insertions(+), 1 deletion(-) create mode 100644 specifications/pattern-schema/pattern-schema-2.0.0.json diff --git a/specifications/pattern-schema/pattern-schema-2.0.0.json b/specifications/pattern-schema/pattern-schema-2.0.0.json new file mode 100644 index 00000000..b940af4e --- /dev/null +++ b/specifications/pattern-schema/pattern-schema-2.0.0.json @@ -0,0 +1,303 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-2.0.0.json", + "title": "Ansible Pattern Schema", + "description": "A schema for validating Ansible pattern definitions", + "type": "object", + "x-ansible-lint": [ + "patterns/*/meta/pattern.json" + ], + "required": [ + "schema_version", + "name", + "title", + "description", + "short_description", + "aap_resources" + ], + "properties": { + "schema_version": { + "description": "Version of this schema being used", + "type": "string" + }, + "name": { + "description": "Machine-readable name for the pattern. MUST match the pattern directory name, and be limited to lowercase letters, numbers, and underscores, with a maximum length of 64 character.", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-z0-9_]+$", + "type": "string" + }, + "title": { + "description": "Human readable title for the pattern, may be used for pattern catalog and display purposes. It should meaningfully convey what the pattern does. It MUST be limited to 64 characters and should start with an action verb (e.g. Create an AWS EC2 instance).", + "maxLength": 64, + "type": "string" + }, + "description": { + "description": "A detailed explanation of what the pattern does. Use complete sentences with correct grammar to describe the functionality of the pattern and provide usage guidance. The description should provide context to the user so that they understand the purpose of the pattern and what they will gain by using it.", + "type": "string" + }, + "short_description": { + "description": "A one-line description of the entry point. Ideally it is a phrase and not a sentence. The short description should always be a string and never a list, and should not end in a period.", + "type": "string" + }, + "tags": { + "description": "List of tags associated with the pattern. Tags may be used to filter patterns in catalog UIs such as automation hub and Red Hat Developer Hub.", + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "aap_resources": { + "description": "Define the resources required in Ansible Automation Platform (AAP) to run the pattern automation.", + "type": "object", + "required": [ + "controller_project", + "controller_job_templates" + ], + "properties": { + "controller_project": { + "description": "Information about the project to be created in automation controller. Every pattern MUST specify a controller project, which will be sourced from the pattern's parent collection published in private automation hub.", + "type": "object", + "required": [ + "name", + "description" + ], + "properties": { + "name": { + "description": "Name for the project to be created in automation controller. The value MUST not exceed 200 characters.", + "maxLength": 200, + "type": "string" + }, + "description": { + "description": "Description of the project to be created in automation controller.", + "type": "string" + } + } + }, + "controller_execution_environment": { + "description": "Definition of the execution environment (EE) to be used by the pattern. An EE should be included if the pattern requires dependencies that are not available in the default controller EE. The EE image MUST be available in Private Automation Hub.", + "type": "object", + "required": [ + "name", + "description", + "image_name" + ], + "properties": { + "name": { + "description": "Name for the EE to be created in automation controller. The value MUST not exceed 200 characters.", + "maxLength": 200, + "type": "string" + }, + "description": { + "description": "Description for the EE to be created in automation controller.", + "type": "string" + }, + "image_name": { + "description": "Name of the execution environment (EE) image as published in Private Automation Hub (PAH). Use the format /, such as 'cloud/aws_ops_ee'. If the pattern is created before the image is published, the image MUST be uploaded to PAH with the name specified here. If the image is already in PAH, this value MUST exactly match the existing image name.", + "type": "string" + }, + "pull": { + "description": "Whether to pull the EE image before running.", + "type": "string", + "enum": [ + "always", + "missing", + "never" + ] + } + } + }, + "controller_labels": { + "description": "Labels to create for the pattern if not already present in automation controller.", + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "uniqueItems": true + }, + "controller_job_templates": { + "description": "A list of one or more job templates to be created in automation controller.", + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": [ + "name", + "description", + "playbook" + ], + "properties": { + "name": { + "description": "Name for the job template to be created in automation controller. The value MUST not exceed 200 characters.", + "maxLength": 200, + "type": "string" + }, + "description": { + "description": "Description for the job template to be created in automation controller.", + "type": "string" + }, + "execution_environment": { + "description": "Name of the execution environment to use for job template execution. MUST match the EE name defined in controller_execution_environment, IF defined.", + "type": "string" + }, + "playbook": { + "description": "Name of the playbook file included in the pattern `automations` directory.", + "type": "string" + }, + "primary": { + "description": "Whether the playbook is the primary playbook for this job template. If there are multiple job templates included, one should be labeled as the primary job template.", + "type": "boolean" + }, + "ask_credential_on_launch": { + "description": "Whether the user must select a credential when launching the job template.", + "type": "boolean" + }, + "ask_inventory_on_launch": { + "description": "Whether the user must select an inventory when launching the job template.", + "type": "boolean" + }, + "ask_verbosity_on_launch": { + "description": "Whether the user must choose a verbosity level when launching the job template.", + "type": "boolean" + }, + "labels": { + "description": "List of labels to associate with this resource. Each label MUST either already exist in automation controller or be defined in the 'controller_labels' field of this pattern to be created during pattern loading.", + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "uniqueItems": true + }, + "survey": { + "description": "A survey that captures required and optional variables from the user for configuring job templates.", + "type": "object", + "required": [ + "name", + "description", + "spec" + ], + "properties": { + "name": { + "type": "string", + "description": "Name of the survey." + }, + "description": { + "type": "string", + "description": "Description of the survey." + }, + "spec": { + "type": "array", + "description": "List of survey questions.", + "items": { + "type": "object", + "required": [ + "type", + "question_name", + "question_description", + "variable", + "required" + ], + "properties": { + "type": { + "type": "string", + "description": "Type of input field.", + "enum": [ + "text", + "textarea", + "password", + "integer", + "float", + "multiplechoice", + "multiselect" + ] + }, + "question_name": { + "type": "string", + "description": "Human-readable question title." + }, + "question_description": { + "type": "string", + "description": "A prompt that instructs the user to supply information or other input to the pattern. Use a complete sentence with correct grammar. Start with an imperative verb form and be specific and actionable. Address the user directly in a conversational and professional way." + }, + "variable": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-z0-9_]+$", + "description": "Variable name used to store the answer." + }, + "required": { + "type": "boolean", + "description": "Whether this question must be answered." + }, + "choices": { + "type": "array", + "description": "List of selectable values for multiple choice questions.", + "items": { + "type": "string" + } + }, + "default": { + "type": "string", + "description": "Default value for the input field." + } + }, + "allOf": [ + { + "if": { + "properties": { + "type": { + "const": "multiplechoice" + } + } + }, + "then": { + "required": [ + "choices" + ] + } + } + ] + } + } + } + } + } + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "aap_resources": { + "properties": { + "controller_job_templates": { + "type": "array", + "contains": { + "required": [ + "labels" + ] + } + } + } + } + } + }, + "then": { + "properties": { + "aap_resources": { + "required": [ + "controller_labels" + ] + } + } + } + } + ] +} diff --git a/specifications/pattern-schema/pattern-schema-dev.json b/specifications/pattern-schema/pattern-schema-dev.json index f43f42a4..22b5a9f0 100644 --- a/specifications/pattern-schema/pattern-schema-dev.json +++ b/specifications/pattern-schema/pattern-schema-dev.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-dev-2.0.0.json", + "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-dev.json", "title": "Ansible Pattern Schema", "description": "A schema for validating Ansible pattern definitions", "type": "object", From ec3059adb569fc125b7d9e97ece721aa216af4bd Mon Sep 17 00:00:00 2001 From: chynasan Date: Tue, 16 Sep 2025 15:47:12 -0500 Subject: [PATCH 20/51] Created pattern-schema-2.0.0 (but that was a lie), updated latest, expected to pass validate-pattern-specs CI test. --- specifications/pattern-schema/pattern-schema-latest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specifications/pattern-schema/pattern-schema-latest.json b/specifications/pattern-schema/pattern-schema-latest.json index 08d46b4e..b940af4e 100644 --- a/specifications/pattern-schema/pattern-schema-latest.json +++ b/specifications/pattern-schema/pattern-schema-latest.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-1.0.0.json", + "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-2.0.0.json", "title": "Ansible Pattern Schema", "description": "A schema for validating Ansible pattern definitions", "type": "object", From 9b58479d5868460dcbc56141e4944feea9b6b023 Mon Sep 17 00:00:00 2001 From: chynasan Date: Tue, 16 Sep 2025 15:49:05 -0500 Subject: [PATCH 21/51] Created pattern-schema-2.0.0 (but that was a lie), did not change the id to 2.0.0, expected to fail validate-pattern-specs CI test. --- specifications/pattern-schema/pattern-schema-2.0.0.json | 2 +- specifications/pattern-schema/pattern-schema-latest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/specifications/pattern-schema/pattern-schema-2.0.0.json b/specifications/pattern-schema/pattern-schema-2.0.0.json index b940af4e..08d46b4e 100644 --- a/specifications/pattern-schema/pattern-schema-2.0.0.json +++ b/specifications/pattern-schema/pattern-schema-2.0.0.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-2.0.0.json", + "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-1.0.0.json", "title": "Ansible Pattern Schema", "description": "A schema for validating Ansible pattern definitions", "type": "object", diff --git a/specifications/pattern-schema/pattern-schema-latest.json b/specifications/pattern-schema/pattern-schema-latest.json index b940af4e..08d46b4e 100644 --- a/specifications/pattern-schema/pattern-schema-latest.json +++ b/specifications/pattern-schema/pattern-schema-latest.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-2.0.0.json", + "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-1.0.0.json", "title": "Ansible Pattern Schema", "description": "A schema for validating Ansible pattern definitions", "type": "object", From e0fb8a90b2e112dddbc9ad313f7bced5b30bb2fd Mon Sep 17 00:00:00 2001 From: chynasan Date: Tue, 16 Sep 2025 15:51:08 -0500 Subject: [PATCH 22/51] Modified version number of pattern spec to 1.1.1, did not change the filename, expected to fail validate-pattern-specs CI test. --- .../pattern-schema/pattern-schema-2.0.0.json | 303 ------------------ .../pattern-specification-1.0.0.md | 2 +- 2 files changed, 1 insertion(+), 304 deletions(-) delete mode 100644 specifications/pattern-schema/pattern-schema-2.0.0.json diff --git a/specifications/pattern-schema/pattern-schema-2.0.0.json b/specifications/pattern-schema/pattern-schema-2.0.0.json deleted file mode 100644 index 08d46b4e..00000000 --- a/specifications/pattern-schema/pattern-schema-2.0.0.json +++ /dev/null @@ -1,303 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-1.0.0.json", - "title": "Ansible Pattern Schema", - "description": "A schema for validating Ansible pattern definitions", - "type": "object", - "x-ansible-lint": [ - "patterns/*/meta/pattern.json" - ], - "required": [ - "schema_version", - "name", - "title", - "description", - "short_description", - "aap_resources" - ], - "properties": { - "schema_version": { - "description": "Version of this schema being used", - "type": "string" - }, - "name": { - "description": "Machine-readable name for the pattern. MUST match the pattern directory name, and be limited to lowercase letters, numbers, and underscores, with a maximum length of 64 character.", - "minLength": 1, - "maxLength": 64, - "pattern": "^[a-z0-9_]+$", - "type": "string" - }, - "title": { - "description": "Human readable title for the pattern, may be used for pattern catalog and display purposes. It should meaningfully convey what the pattern does. It MUST be limited to 64 characters and should start with an action verb (e.g. Create an AWS EC2 instance).", - "maxLength": 64, - "type": "string" - }, - "description": { - "description": "A detailed explanation of what the pattern does. Use complete sentences with correct grammar to describe the functionality of the pattern and provide usage guidance. The description should provide context to the user so that they understand the purpose of the pattern and what they will gain by using it.", - "type": "string" - }, - "short_description": { - "description": "A one-line description of the entry point. Ideally it is a phrase and not a sentence. The short description should always be a string and never a list, and should not end in a period.", - "type": "string" - }, - "tags": { - "description": "List of tags associated with the pattern. Tags may be used to filter patterns in catalog UIs such as automation hub and Red Hat Developer Hub.", - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "aap_resources": { - "description": "Define the resources required in Ansible Automation Platform (AAP) to run the pattern automation.", - "type": "object", - "required": [ - "controller_project", - "controller_job_templates" - ], - "properties": { - "controller_project": { - "description": "Information about the project to be created in automation controller. Every pattern MUST specify a controller project, which will be sourced from the pattern's parent collection published in private automation hub.", - "type": "object", - "required": [ - "name", - "description" - ], - "properties": { - "name": { - "description": "Name for the project to be created in automation controller. The value MUST not exceed 200 characters.", - "maxLength": 200, - "type": "string" - }, - "description": { - "description": "Description of the project to be created in automation controller.", - "type": "string" - } - } - }, - "controller_execution_environment": { - "description": "Definition of the execution environment (EE) to be used by the pattern. An EE should be included if the pattern requires dependencies that are not available in the default controller EE. The EE image MUST be available in Private Automation Hub.", - "type": "object", - "required": [ - "name", - "description", - "image_name" - ], - "properties": { - "name": { - "description": "Name for the EE to be created in automation controller. The value MUST not exceed 200 characters.", - "maxLength": 200, - "type": "string" - }, - "description": { - "description": "Description for the EE to be created in automation controller.", - "type": "string" - }, - "image_name": { - "description": "Name of the execution environment (EE) image as published in Private Automation Hub (PAH). Use the format /, such as 'cloud/aws_ops_ee'. If the pattern is created before the image is published, the image MUST be uploaded to PAH with the name specified here. If the image is already in PAH, this value MUST exactly match the existing image name.", - "type": "string" - }, - "pull": { - "description": "Whether to pull the EE image before running.", - "type": "string", - "enum": [ - "always", - "missing", - "never" - ] - } - } - }, - "controller_labels": { - "description": "Labels to create for the pattern if not already present in automation controller.", - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1, - "uniqueItems": true - }, - "controller_job_templates": { - "description": "A list of one or more job templates to be created in automation controller.", - "type": "array", - "minItems": 1, - "items": { - "type": "object", - "required": [ - "name", - "description", - "playbook" - ], - "properties": { - "name": { - "description": "Name for the job template to be created in automation controller. The value MUST not exceed 200 characters.", - "maxLength": 200, - "type": "string" - }, - "description": { - "description": "Description for the job template to be created in automation controller.", - "type": "string" - }, - "execution_environment": { - "description": "Name of the execution environment to use for job template execution. MUST match the EE name defined in controller_execution_environment, IF defined.", - "type": "string" - }, - "playbook": { - "description": "Name of the playbook file included in the pattern `automations` directory.", - "type": "string" - }, - "primary": { - "description": "Whether the playbook is the primary playbook for this job template. If there are multiple job templates included, one should be labeled as the primary job template.", - "type": "boolean" - }, - "ask_credential_on_launch": { - "description": "Whether the user must select a credential when launching the job template.", - "type": "boolean" - }, - "ask_inventory_on_launch": { - "description": "Whether the user must select an inventory when launching the job template.", - "type": "boolean" - }, - "ask_verbosity_on_launch": { - "description": "Whether the user must choose a verbosity level when launching the job template.", - "type": "boolean" - }, - "labels": { - "description": "List of labels to associate with this resource. Each label MUST either already exist in automation controller or be defined in the 'controller_labels' field of this pattern to be created during pattern loading.", - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1, - "uniqueItems": true - }, - "survey": { - "description": "A survey that captures required and optional variables from the user for configuring job templates.", - "type": "object", - "required": [ - "name", - "description", - "spec" - ], - "properties": { - "name": { - "type": "string", - "description": "Name of the survey." - }, - "description": { - "type": "string", - "description": "Description of the survey." - }, - "spec": { - "type": "array", - "description": "List of survey questions.", - "items": { - "type": "object", - "required": [ - "type", - "question_name", - "question_description", - "variable", - "required" - ], - "properties": { - "type": { - "type": "string", - "description": "Type of input field.", - "enum": [ - "text", - "textarea", - "password", - "integer", - "float", - "multiplechoice", - "multiselect" - ] - }, - "question_name": { - "type": "string", - "description": "Human-readable question title." - }, - "question_description": { - "type": "string", - "description": "A prompt that instructs the user to supply information or other input to the pattern. Use a complete sentence with correct grammar. Start with an imperative verb form and be specific and actionable. Address the user directly in a conversational and professional way." - }, - "variable": { - "type": "string", - "minLength": 1, - "maxLength": 64, - "pattern": "^[a-z0-9_]+$", - "description": "Variable name used to store the answer." - }, - "required": { - "type": "boolean", - "description": "Whether this question must be answered." - }, - "choices": { - "type": "array", - "description": "List of selectable values for multiple choice questions.", - "items": { - "type": "string" - } - }, - "default": { - "type": "string", - "description": "Default value for the input field." - } - }, - "allOf": [ - { - "if": { - "properties": { - "type": { - "const": "multiplechoice" - } - } - }, - "then": { - "required": [ - "choices" - ] - } - } - ] - } - } - } - } - } - } - } - } - } - }, - "allOf": [ - { - "if": { - "properties": { - "aap_resources": { - "properties": { - "controller_job_templates": { - "type": "array", - "contains": { - "required": [ - "labels" - ] - } - } - } - } - } - }, - "then": { - "properties": { - "aap_resources": { - "required": [ - "controller_labels" - ] - } - } - } - } - ] -} diff --git a/specifications/pattern-specification/pattern-specification-1.0.0.md b/specifications/pattern-specification/pattern-specification-1.0.0.md index e5327b9b..e84c58b3 100644 --- a/specifications/pattern-specification/pattern-specification-1.0.0.md +++ b/specifications/pattern-specification/pattern-specification-1.0.0.md @@ -1,7 +1,7 @@ --- title: Ansible Patterns Specification author: Ansible Cloud Content team -version: 1.0.0 +version: 1.1.1 --- # Ansible Pattern Specification From e79534532fefb436b011a9da7ca0a244ee9fd692 Mon Sep 17 00:00:00 2001 From: chynasan Date: Tue, 16 Sep 2025 15:53:56 -0500 Subject: [PATCH 23/51] New 2.0.0 schema file with version match (should pass) --- .../pattern-schema/pattern-schema-2.0.0.json | 303 ++++++++++++++++++ .../pattern-specification-1.0.0.md | 2 +- 2 files changed, 304 insertions(+), 1 deletion(-) create mode 100644 specifications/pattern-schema/pattern-schema-2.0.0.json diff --git a/specifications/pattern-schema/pattern-schema-2.0.0.json b/specifications/pattern-schema/pattern-schema-2.0.0.json new file mode 100644 index 00000000..b940af4e --- /dev/null +++ b/specifications/pattern-schema/pattern-schema-2.0.0.json @@ -0,0 +1,303 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-2.0.0.json", + "title": "Ansible Pattern Schema", + "description": "A schema for validating Ansible pattern definitions", + "type": "object", + "x-ansible-lint": [ + "patterns/*/meta/pattern.json" + ], + "required": [ + "schema_version", + "name", + "title", + "description", + "short_description", + "aap_resources" + ], + "properties": { + "schema_version": { + "description": "Version of this schema being used", + "type": "string" + }, + "name": { + "description": "Machine-readable name for the pattern. MUST match the pattern directory name, and be limited to lowercase letters, numbers, and underscores, with a maximum length of 64 character.", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-z0-9_]+$", + "type": "string" + }, + "title": { + "description": "Human readable title for the pattern, may be used for pattern catalog and display purposes. It should meaningfully convey what the pattern does. It MUST be limited to 64 characters and should start with an action verb (e.g. Create an AWS EC2 instance).", + "maxLength": 64, + "type": "string" + }, + "description": { + "description": "A detailed explanation of what the pattern does. Use complete sentences with correct grammar to describe the functionality of the pattern and provide usage guidance. The description should provide context to the user so that they understand the purpose of the pattern and what they will gain by using it.", + "type": "string" + }, + "short_description": { + "description": "A one-line description of the entry point. Ideally it is a phrase and not a sentence. The short description should always be a string and never a list, and should not end in a period.", + "type": "string" + }, + "tags": { + "description": "List of tags associated with the pattern. Tags may be used to filter patterns in catalog UIs such as automation hub and Red Hat Developer Hub.", + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "aap_resources": { + "description": "Define the resources required in Ansible Automation Platform (AAP) to run the pattern automation.", + "type": "object", + "required": [ + "controller_project", + "controller_job_templates" + ], + "properties": { + "controller_project": { + "description": "Information about the project to be created in automation controller. Every pattern MUST specify a controller project, which will be sourced from the pattern's parent collection published in private automation hub.", + "type": "object", + "required": [ + "name", + "description" + ], + "properties": { + "name": { + "description": "Name for the project to be created in automation controller. The value MUST not exceed 200 characters.", + "maxLength": 200, + "type": "string" + }, + "description": { + "description": "Description of the project to be created in automation controller.", + "type": "string" + } + } + }, + "controller_execution_environment": { + "description": "Definition of the execution environment (EE) to be used by the pattern. An EE should be included if the pattern requires dependencies that are not available in the default controller EE. The EE image MUST be available in Private Automation Hub.", + "type": "object", + "required": [ + "name", + "description", + "image_name" + ], + "properties": { + "name": { + "description": "Name for the EE to be created in automation controller. The value MUST not exceed 200 characters.", + "maxLength": 200, + "type": "string" + }, + "description": { + "description": "Description for the EE to be created in automation controller.", + "type": "string" + }, + "image_name": { + "description": "Name of the execution environment (EE) image as published in Private Automation Hub (PAH). Use the format /, such as 'cloud/aws_ops_ee'. If the pattern is created before the image is published, the image MUST be uploaded to PAH with the name specified here. If the image is already in PAH, this value MUST exactly match the existing image name.", + "type": "string" + }, + "pull": { + "description": "Whether to pull the EE image before running.", + "type": "string", + "enum": [ + "always", + "missing", + "never" + ] + } + } + }, + "controller_labels": { + "description": "Labels to create for the pattern if not already present in automation controller.", + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "uniqueItems": true + }, + "controller_job_templates": { + "description": "A list of one or more job templates to be created in automation controller.", + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": [ + "name", + "description", + "playbook" + ], + "properties": { + "name": { + "description": "Name for the job template to be created in automation controller. The value MUST not exceed 200 characters.", + "maxLength": 200, + "type": "string" + }, + "description": { + "description": "Description for the job template to be created in automation controller.", + "type": "string" + }, + "execution_environment": { + "description": "Name of the execution environment to use for job template execution. MUST match the EE name defined in controller_execution_environment, IF defined.", + "type": "string" + }, + "playbook": { + "description": "Name of the playbook file included in the pattern `automations` directory.", + "type": "string" + }, + "primary": { + "description": "Whether the playbook is the primary playbook for this job template. If there are multiple job templates included, one should be labeled as the primary job template.", + "type": "boolean" + }, + "ask_credential_on_launch": { + "description": "Whether the user must select a credential when launching the job template.", + "type": "boolean" + }, + "ask_inventory_on_launch": { + "description": "Whether the user must select an inventory when launching the job template.", + "type": "boolean" + }, + "ask_verbosity_on_launch": { + "description": "Whether the user must choose a verbosity level when launching the job template.", + "type": "boolean" + }, + "labels": { + "description": "List of labels to associate with this resource. Each label MUST either already exist in automation controller or be defined in the 'controller_labels' field of this pattern to be created during pattern loading.", + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "uniqueItems": true + }, + "survey": { + "description": "A survey that captures required and optional variables from the user for configuring job templates.", + "type": "object", + "required": [ + "name", + "description", + "spec" + ], + "properties": { + "name": { + "type": "string", + "description": "Name of the survey." + }, + "description": { + "type": "string", + "description": "Description of the survey." + }, + "spec": { + "type": "array", + "description": "List of survey questions.", + "items": { + "type": "object", + "required": [ + "type", + "question_name", + "question_description", + "variable", + "required" + ], + "properties": { + "type": { + "type": "string", + "description": "Type of input field.", + "enum": [ + "text", + "textarea", + "password", + "integer", + "float", + "multiplechoice", + "multiselect" + ] + }, + "question_name": { + "type": "string", + "description": "Human-readable question title." + }, + "question_description": { + "type": "string", + "description": "A prompt that instructs the user to supply information or other input to the pattern. Use a complete sentence with correct grammar. Start with an imperative verb form and be specific and actionable. Address the user directly in a conversational and professional way." + }, + "variable": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-z0-9_]+$", + "description": "Variable name used to store the answer." + }, + "required": { + "type": "boolean", + "description": "Whether this question must be answered." + }, + "choices": { + "type": "array", + "description": "List of selectable values for multiple choice questions.", + "items": { + "type": "string" + } + }, + "default": { + "type": "string", + "description": "Default value for the input field." + } + }, + "allOf": [ + { + "if": { + "properties": { + "type": { + "const": "multiplechoice" + } + } + }, + "then": { + "required": [ + "choices" + ] + } + } + ] + } + } + } + } + } + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "aap_resources": { + "properties": { + "controller_job_templates": { + "type": "array", + "contains": { + "required": [ + "labels" + ] + } + } + } + } + } + }, + "then": { + "properties": { + "aap_resources": { + "required": [ + "controller_labels" + ] + } + } + } + } + ] +} diff --git a/specifications/pattern-specification/pattern-specification-1.0.0.md b/specifications/pattern-specification/pattern-specification-1.0.0.md index e84c58b3..e5327b9b 100644 --- a/specifications/pattern-specification/pattern-specification-1.0.0.md +++ b/specifications/pattern-specification/pattern-specification-1.0.0.md @@ -1,7 +1,7 @@ --- title: Ansible Patterns Specification author: Ansible Cloud Content team -version: 1.1.1 +version: 1.0.0 --- # Ansible Pattern Specification From 83e5c739220522a836888596a3433c92443abed9 Mon Sep 17 00:00:00 2001 From: chynasan Date: Tue, 16 Sep 2025 15:54:59 -0500 Subject: [PATCH 24/51] Updated latest to 2.0.0, should pass. --- specifications/pattern-schema/pattern-schema-latest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specifications/pattern-schema/pattern-schema-latest.json b/specifications/pattern-schema/pattern-schema-latest.json index 08d46b4e..b940af4e 100644 --- a/specifications/pattern-schema/pattern-schema-latest.json +++ b/specifications/pattern-schema/pattern-schema-latest.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-1.0.0.json", + "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-2.0.0.json", "title": "Ansible Pattern Schema", "description": "A schema for validating Ansible pattern definitions", "type": "object", From 0c6750b7d5aa7d6a9c900a23370da22ab8ef4ec3 Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Tue, 16 Sep 2025 17:39:39 -0400 Subject: [PATCH 25/51] Update spec file validation task for subtask exit, add debug statements --- .github/workflows/validate-pattern-specs.yml | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/workflows/validate-pattern-specs.yml b/.github/workflows/validate-pattern-specs.yml index e9e30cd3..61e6f21a 100644 --- a/.github/workflows/validate-pattern-specs.yml +++ b/.github/workflows/validate-pattern-specs.yml @@ -81,7 +81,8 @@ jobs: - 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 + SPEC_FILES="${{ steps.changed-files.outputs.spec_files }}" + while IFS= 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 @@ -110,11 +111,18 @@ jobs: fi fi fi - done + done <<< "$SPEC_FILES" - name: Validate pattern schema files if: steps.changed-files.outputs.schema_files != '' run: | + echo "=== DEBUG: Starting schema validation ===" + echo "Current working directory: $(pwd)" + echo "Git HEAD: $(git rev-parse HEAD)" + echo "Git status:" + git status + echo "=== END DEBUG ===" + SCHEMA_FILES="${{ steps.changed-files.outputs.schema_files }}" while IFS= read -r file; do if [ -n "$file" ]; then @@ -137,6 +145,11 @@ jobs: echo "::error::Latest schema file was not updated to point to new version $VERSION. Expected \$id: $EXPECTED_ID, Got: $LATEST_ID" exit 1 fi + echo "DEBUG: VERSION = $VERSION" + echo "DEBUG: EXPECTED_ID = $EXPECTED_ID" + echo "DEBUG: LATEST_ID = $LATEST_ID" + echo "DEBUG: ACTUAL_ID = $ACTUAL_ID" + fi fi done <<< "$SCHEMA_FILES" From f25d24881cadcf0b9a44265ef5833e678a51b04d Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Wed, 17 Sep 2025 11:16:16 -0400 Subject: [PATCH 26/51] Make use of --diff-filter --- .github/workflows/validate-pattern-specs.yml | 40 +-- .../pattern-schema/pattern-schema-2.0.0.json | 303 ------------------ .../pattern-schema/pattern-schema-latest.json | 2 +- 3 files changed, 17 insertions(+), 328 deletions(-) delete mode 100644 specifications/pattern-schema/pattern-schema-2.0.0.json diff --git a/.github/workflows/validate-pattern-specs.yml b/.github/workflows/validate-pattern-specs.yml index 61e6f21a..e2d9ce3e 100644 --- a/.github/workflows/validate-pattern-specs.yml +++ b/.github/workflows/validate-pattern-specs.yml @@ -30,14 +30,22 @@ jobs: - 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) + # Get only new spec files (excluding dev versions) + 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 + + # Get only modified spec files (excluding dev versions) + 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 pattern specification files (excluding dev versions) - SPEC_FILES=$(echo "$CHANGED_FILES" | grep -E '^specifications/pattern-specification/.*\.md$' | grep -v 'dev\.md$' || true) + # Get all changed spec files (new + modified) + 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 "$SPEC_FILES" >> $GITHUB_OUTPUT + echo "$ALL_SPEC_FILES" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT # Check for pattern schema files (excluding dev versions) @@ -46,22 +54,6 @@ jobs: echo "$SCHEMA_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 - echo "EOF" >> $GITHUB_OUTPUT - # Check for new schema files that don't exist on main branch NEW_SCHEMA_FILES="" if [ -n "$SCHEMA_FILES" ]; then @@ -95,7 +87,7 @@ jobs: # 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) + FILE_VERSION=$(sed -n 's/^version:[[:space:]]*\([^[:space:]]*\).*/\1/p' "$file" | head -1) # Validate filename vs content version if [ "$FILENAME_VERSION" != "$FILE_VERSION" ]; then @@ -104,7 +96,7 @@ jobs: 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) + LATEST_VERSION=$(sed -n 's/^version:[[:space:]]*\([^[:space:]]*\).*/\1/p' "specifications/pattern-specification/pattern-specification-latest.md" | 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 diff --git a/specifications/pattern-schema/pattern-schema-2.0.0.json b/specifications/pattern-schema/pattern-schema-2.0.0.json deleted file mode 100644 index b940af4e..00000000 --- a/specifications/pattern-schema/pattern-schema-2.0.0.json +++ /dev/null @@ -1,303 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-2.0.0.json", - "title": "Ansible Pattern Schema", - "description": "A schema for validating Ansible pattern definitions", - "type": "object", - "x-ansible-lint": [ - "patterns/*/meta/pattern.json" - ], - "required": [ - "schema_version", - "name", - "title", - "description", - "short_description", - "aap_resources" - ], - "properties": { - "schema_version": { - "description": "Version of this schema being used", - "type": "string" - }, - "name": { - "description": "Machine-readable name for the pattern. MUST match the pattern directory name, and be limited to lowercase letters, numbers, and underscores, with a maximum length of 64 character.", - "minLength": 1, - "maxLength": 64, - "pattern": "^[a-z0-9_]+$", - "type": "string" - }, - "title": { - "description": "Human readable title for the pattern, may be used for pattern catalog and display purposes. It should meaningfully convey what the pattern does. It MUST be limited to 64 characters and should start with an action verb (e.g. Create an AWS EC2 instance).", - "maxLength": 64, - "type": "string" - }, - "description": { - "description": "A detailed explanation of what the pattern does. Use complete sentences with correct grammar to describe the functionality of the pattern and provide usage guidance. The description should provide context to the user so that they understand the purpose of the pattern and what they will gain by using it.", - "type": "string" - }, - "short_description": { - "description": "A one-line description of the entry point. Ideally it is a phrase and not a sentence. The short description should always be a string and never a list, and should not end in a period.", - "type": "string" - }, - "tags": { - "description": "List of tags associated with the pattern. Tags may be used to filter patterns in catalog UIs such as automation hub and Red Hat Developer Hub.", - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "aap_resources": { - "description": "Define the resources required in Ansible Automation Platform (AAP) to run the pattern automation.", - "type": "object", - "required": [ - "controller_project", - "controller_job_templates" - ], - "properties": { - "controller_project": { - "description": "Information about the project to be created in automation controller. Every pattern MUST specify a controller project, which will be sourced from the pattern's parent collection published in private automation hub.", - "type": "object", - "required": [ - "name", - "description" - ], - "properties": { - "name": { - "description": "Name for the project to be created in automation controller. The value MUST not exceed 200 characters.", - "maxLength": 200, - "type": "string" - }, - "description": { - "description": "Description of the project to be created in automation controller.", - "type": "string" - } - } - }, - "controller_execution_environment": { - "description": "Definition of the execution environment (EE) to be used by the pattern. An EE should be included if the pattern requires dependencies that are not available in the default controller EE. The EE image MUST be available in Private Automation Hub.", - "type": "object", - "required": [ - "name", - "description", - "image_name" - ], - "properties": { - "name": { - "description": "Name for the EE to be created in automation controller. The value MUST not exceed 200 characters.", - "maxLength": 200, - "type": "string" - }, - "description": { - "description": "Description for the EE to be created in automation controller.", - "type": "string" - }, - "image_name": { - "description": "Name of the execution environment (EE) image as published in Private Automation Hub (PAH). Use the format /, such as 'cloud/aws_ops_ee'. If the pattern is created before the image is published, the image MUST be uploaded to PAH with the name specified here. If the image is already in PAH, this value MUST exactly match the existing image name.", - "type": "string" - }, - "pull": { - "description": "Whether to pull the EE image before running.", - "type": "string", - "enum": [ - "always", - "missing", - "never" - ] - } - } - }, - "controller_labels": { - "description": "Labels to create for the pattern if not already present in automation controller.", - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1, - "uniqueItems": true - }, - "controller_job_templates": { - "description": "A list of one or more job templates to be created in automation controller.", - "type": "array", - "minItems": 1, - "items": { - "type": "object", - "required": [ - "name", - "description", - "playbook" - ], - "properties": { - "name": { - "description": "Name for the job template to be created in automation controller. The value MUST not exceed 200 characters.", - "maxLength": 200, - "type": "string" - }, - "description": { - "description": "Description for the job template to be created in automation controller.", - "type": "string" - }, - "execution_environment": { - "description": "Name of the execution environment to use for job template execution. MUST match the EE name defined in controller_execution_environment, IF defined.", - "type": "string" - }, - "playbook": { - "description": "Name of the playbook file included in the pattern `automations` directory.", - "type": "string" - }, - "primary": { - "description": "Whether the playbook is the primary playbook for this job template. If there are multiple job templates included, one should be labeled as the primary job template.", - "type": "boolean" - }, - "ask_credential_on_launch": { - "description": "Whether the user must select a credential when launching the job template.", - "type": "boolean" - }, - "ask_inventory_on_launch": { - "description": "Whether the user must select an inventory when launching the job template.", - "type": "boolean" - }, - "ask_verbosity_on_launch": { - "description": "Whether the user must choose a verbosity level when launching the job template.", - "type": "boolean" - }, - "labels": { - "description": "List of labels to associate with this resource. Each label MUST either already exist in automation controller or be defined in the 'controller_labels' field of this pattern to be created during pattern loading.", - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1, - "uniqueItems": true - }, - "survey": { - "description": "A survey that captures required and optional variables from the user for configuring job templates.", - "type": "object", - "required": [ - "name", - "description", - "spec" - ], - "properties": { - "name": { - "type": "string", - "description": "Name of the survey." - }, - "description": { - "type": "string", - "description": "Description of the survey." - }, - "spec": { - "type": "array", - "description": "List of survey questions.", - "items": { - "type": "object", - "required": [ - "type", - "question_name", - "question_description", - "variable", - "required" - ], - "properties": { - "type": { - "type": "string", - "description": "Type of input field.", - "enum": [ - "text", - "textarea", - "password", - "integer", - "float", - "multiplechoice", - "multiselect" - ] - }, - "question_name": { - "type": "string", - "description": "Human-readable question title." - }, - "question_description": { - "type": "string", - "description": "A prompt that instructs the user to supply information or other input to the pattern. Use a complete sentence with correct grammar. Start with an imperative verb form and be specific and actionable. Address the user directly in a conversational and professional way." - }, - "variable": { - "type": "string", - "minLength": 1, - "maxLength": 64, - "pattern": "^[a-z0-9_]+$", - "description": "Variable name used to store the answer." - }, - "required": { - "type": "boolean", - "description": "Whether this question must be answered." - }, - "choices": { - "type": "array", - "description": "List of selectable values for multiple choice questions.", - "items": { - "type": "string" - } - }, - "default": { - "type": "string", - "description": "Default value for the input field." - } - }, - "allOf": [ - { - "if": { - "properties": { - "type": { - "const": "multiplechoice" - } - } - }, - "then": { - "required": [ - "choices" - ] - } - } - ] - } - } - } - } - } - } - } - } - } - }, - "allOf": [ - { - "if": { - "properties": { - "aap_resources": { - "properties": { - "controller_job_templates": { - "type": "array", - "contains": { - "required": [ - "labels" - ] - } - } - } - } - } - }, - "then": { - "properties": { - "aap_resources": { - "required": [ - "controller_labels" - ] - } - } - } - } - ] -} diff --git a/specifications/pattern-schema/pattern-schema-latest.json b/specifications/pattern-schema/pattern-schema-latest.json index b940af4e..08d46b4e 100644 --- a/specifications/pattern-schema/pattern-schema-latest.json +++ b/specifications/pattern-schema/pattern-schema-latest.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-2.0.0.json", + "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-1.0.0.json", "title": "Ansible Pattern Schema", "description": "A schema for validating Ansible pattern definitions", "type": "object", From e482b16a793faee403b22c04ce1b4e20335ffbe2 Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Wed, 17 Sep 2025 11:18:44 -0400 Subject: [PATCH 27/51] Add new spec file w/ version updated correctly (should pass) --- .github/workflows/validate-pattern-specs.yml | 7 -- .../pattern-specification-2.0.0.md | 101 ++++++++++++++++++ 2 files changed, 101 insertions(+), 7 deletions(-) create mode 100644 specifications/pattern-specification/pattern-specification-2.0.0.md diff --git a/.github/workflows/validate-pattern-specs.yml b/.github/workflows/validate-pattern-specs.yml index e2d9ce3e..bdf3122a 100644 --- a/.github/workflows/validate-pattern-specs.yml +++ b/.github/workflows/validate-pattern-specs.yml @@ -94,13 +94,6 @@ jobs: 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=$(sed -n 's/^version:[[:space:]]*\([^[:space:]]*\).*/\1/p' "specifications/pattern-specification/pattern-specification-latest.md" | 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 fi fi done <<< "$SPEC_FILES" diff --git a/specifications/pattern-specification/pattern-specification-2.0.0.md b/specifications/pattern-specification/pattern-specification-2.0.0.md new file mode 100644 index 00000000..193d2493 --- /dev/null +++ b/specifications/pattern-specification/pattern-specification-2.0.0.md @@ -0,0 +1,101 @@ +--- +title: Ansible Patterns Specification +author: Ansible Cloud Content team +version: 2.0.0 +--- + +# Ansible Pattern Specification + +## Introduction + +This document describes the technical specification for an Ansible pattern, an extension of collections, which are the standard method for packaging and distributing Ansible content. Patterns are used by Ansible Automation Platform (AAP) to offer users the ability to start automating with minimal setup, enhancing productivity and efficiency. + +A pattern defines an Ansible automation, such as a playbook, and the resources needed in AAP to run that automation, such as a project, execution environment, job template, and survey. The pattern definition can be consumed by the AAP API to create and configure those resources, allowing pattern catalogs to provide users with a seamless journey from identifying relevant patterns to using them in AAP. + +Patterns are delivered as files within an Ansible collection. This approach ensures consistent integration with the Ansible ecosystem by leveraging the existing collection framework. These patterns are bundled during collection packaging, enabling them to be searchable, downloadable, and publishable alongside roles, modules, and plugins. + +## Conventions + +This document follows the IETF [RFC2119](https://datatracker.ietf.org/doc/html/rfc2119) definitions of Key words for use in RFCs to Indicate Requirement Levels. + +## Support + +This pattern specification is supported and maintained by Ansible. To submit support or other requests for the specification, please contact Red Hat support. + +Individual patterns that implement this specification are the responsibility of the collection maintainers for the collections in which they reside. For support or feature requests for individual patterns, contact their collection owners. + +## Pattern Directory Structure + +- A pattern **MUST** be contained within a single directory in the `/extensions/patterns/` directory of an Ansible collection. +- An Ansible pattern directory name **MUST** be limited to 64 characters and **MUST** only include lowercase ASCII letters, digits, and underscores. +- An Ansible collection **MAY** contain zero or more patterns. + +## Required Files + +### `meta/pattern.json` + +The pattern definition meta file is the machine-readable entry point for creating an instance of the pattern in AAP. It defines the resources required to execute the pattern, such as a controller project, execution environment, job templates, and labels. The pattern definition also includes metadata about the pattern to enable its discovery and use, such as its title, audience, and tags. A [JSON schema](https://github.com/ansible/pattern-service/blob/main/specifications/pattern-schema/pattern-schema-2.0.0.json) has been published to aid with validation of the pattern definition file. + +- A pattern **MUST** include exactly one meta file defining the pattern metadata and AAP resources it requires. +- The pattern definition meta file **MUST** be a valid instance of the [Ansible pattern schema](https://github.com/ansible/pattern-service/blob/main/specifications/pattern-schema/pattern-schema-2.0.0.json). + +### `README.md` + +The README file is the human-readable documentation for the pattern. It provides information on what the pattern does, its inputs, its dependencies, and how it can be used. + +- A pattern **MUST** include a README file. +- The pattern README file **SHOULD** include all of the following: + - A description of what the pattern does + - A list of the AAP resources created by the pattern + - Documentation on how to use the pattern + +### `playbooks/` + +Pattern playbook files are included in the `/extensions/patterns//playbooks/` directory. + +- A pattern **MUST** contain a `playbooks/` directory. +- The `playbooks/` directory **MUST** contain at least one playbook associated with a job template definition in the pattern's `meta/pattern.json` file. +- A pattern **MAY** contain multiple playbooks. +- If a pattern contains multiple playbooks, it **MUST** define a primary playbook in its `meta/pattern.json` file. + +#### Playbook Requirements + +- All required and optional input variables to a pattern playbook **MUST** be defined in a play argument spec following this example of the [play argument spec format](https://github.com/ansible/ansible-creator/blob/main/src/ansible_creator/resources/playbook_project/argspec_validation_plays.meta.yml). +- If a pattern playbook requires any user-provided information other than variables to launch as a job template, such as inventory or credentials, those **MUST** be specified as `ask__at_launch` in the relevant `controller_job_templates` section of the pattern definition meta file. + +## Optional Files + +### `templates/` + +Templates for various types of catalog software in which patterns may be published. A template provides pattern data in the format required for a given catalog, such as Red Hat Developer Hub. + +- A pattern **MAY** contain a `templates/` directory to hold templates specific to catalogs that may publish the pattern, such as Red Hat Developer Hub or ServiceNow. +- The `templates/` directory **MAY** contain one or more catalog template files. + + +## Validation + +Pattern developers can use [ansible-lint](https://github.com/ansible/ansible-lint) to verify the structure of a pattern and its JSON against the pattern schema. + +## Example Pattern Directory + +```txt + +/extensions/patterns/ +├── network.backup/ # Backup pattern directory +│ ├── meta/pattern.json # Backup pattern definition +│ ├── README.md # Documentation for Backup pattern +│ ├── playbooks/ # Directory containing pattern playbooks +│ │ ├── backup.meta.yaml # Backup playbook arg spec +│ │ ├── backup.yaml # Backup playbook +│ ├── templates/ # Directory containing catalog templates +│ │ ├── rhdh.yaml # Display template for RHDH catalog + +``` + +## Other Pattern Requirements + +- A pattern **MUST** inherit the version number of the collection that contains it. +- A pattern **MUST** be valid according to the requirements specified in this document, including validation of each file contained in the pattern against its relevant schema. +- Changes to patterns **MUST** be noted in collection-level changelogs and release notes. +- All system, python, and Ansible collection dependencies needed to run a pattern's automations **MUST** be declared in the collection's dependency files, including but not limited to: `galaxy.yml`, `requirements.txt`, and `execution_environment.yml`. From 892f93fa9e9f3aa85dbd7333124071b465c86953 Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Wed, 17 Sep 2025 11:22:14 -0400 Subject: [PATCH 28/51] Add new spec file w/ version mismatch (should fail) --- .../pattern-specification/pattern-specification-2.0.0.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specifications/pattern-specification/pattern-specification-2.0.0.md b/specifications/pattern-specification/pattern-specification-2.0.0.md index 193d2493..65539b97 100644 --- a/specifications/pattern-specification/pattern-specification-2.0.0.md +++ b/specifications/pattern-specification/pattern-specification-2.0.0.md @@ -1,7 +1,7 @@ --- title: Ansible Patterns Specification author: Ansible Cloud Content team -version: 2.0.0 +version: 1.0.1 --- # Ansible Pattern Specification From cedaae2a3cbf8309d545d3300fd0f617d80357ce Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Wed, 17 Sep 2025 11:26:06 -0400 Subject: [PATCH 29/51] Spec file validation with changes to dev file (should pass) --- .../pattern-specification-2.0.0.md | 101 ------------------ .../pattern-specification-dev.md | 2 +- 2 files changed, 1 insertion(+), 102 deletions(-) delete mode 100644 specifications/pattern-specification/pattern-specification-2.0.0.md diff --git a/specifications/pattern-specification/pattern-specification-2.0.0.md b/specifications/pattern-specification/pattern-specification-2.0.0.md deleted file mode 100644 index 65539b97..00000000 --- a/specifications/pattern-specification/pattern-specification-2.0.0.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -title: Ansible Patterns Specification -author: Ansible Cloud Content team -version: 1.0.1 ---- - -# Ansible Pattern Specification - -## Introduction - -This document describes the technical specification for an Ansible pattern, an extension of collections, which are the standard method for packaging and distributing Ansible content. Patterns are used by Ansible Automation Platform (AAP) to offer users the ability to start automating with minimal setup, enhancing productivity and efficiency. - -A pattern defines an Ansible automation, such as a playbook, and the resources needed in AAP to run that automation, such as a project, execution environment, job template, and survey. The pattern definition can be consumed by the AAP API to create and configure those resources, allowing pattern catalogs to provide users with a seamless journey from identifying relevant patterns to using them in AAP. - -Patterns are delivered as files within an Ansible collection. This approach ensures consistent integration with the Ansible ecosystem by leveraging the existing collection framework. These patterns are bundled during collection packaging, enabling them to be searchable, downloadable, and publishable alongside roles, modules, and plugins. - -## Conventions - -This document follows the IETF [RFC2119](https://datatracker.ietf.org/doc/html/rfc2119) definitions of Key words for use in RFCs to Indicate Requirement Levels. - -## Support - -This pattern specification is supported and maintained by Ansible. To submit support or other requests for the specification, please contact Red Hat support. - -Individual patterns that implement this specification are the responsibility of the collection maintainers for the collections in which they reside. For support or feature requests for individual patterns, contact their collection owners. - -## Pattern Directory Structure - -- A pattern **MUST** be contained within a single directory in the `/extensions/patterns/` directory of an Ansible collection. -- An Ansible pattern directory name **MUST** be limited to 64 characters and **MUST** only include lowercase ASCII letters, digits, and underscores. -- An Ansible collection **MAY** contain zero or more patterns. - -## Required Files - -### `meta/pattern.json` - -The pattern definition meta file is the machine-readable entry point for creating an instance of the pattern in AAP. It defines the resources required to execute the pattern, such as a controller project, execution environment, job templates, and labels. The pattern definition also includes metadata about the pattern to enable its discovery and use, such as its title, audience, and tags. A [JSON schema](https://github.com/ansible/pattern-service/blob/main/specifications/pattern-schema/pattern-schema-2.0.0.json) has been published to aid with validation of the pattern definition file. - -- A pattern **MUST** include exactly one meta file defining the pattern metadata and AAP resources it requires. -- The pattern definition meta file **MUST** be a valid instance of the [Ansible pattern schema](https://github.com/ansible/pattern-service/blob/main/specifications/pattern-schema/pattern-schema-2.0.0.json). - -### `README.md` - -The README file is the human-readable documentation for the pattern. It provides information on what the pattern does, its inputs, its dependencies, and how it can be used. - -- A pattern **MUST** include a README file. -- The pattern README file **SHOULD** include all of the following: - - A description of what the pattern does - - A list of the AAP resources created by the pattern - - Documentation on how to use the pattern - -### `playbooks/` - -Pattern playbook files are included in the `/extensions/patterns//playbooks/` directory. - -- A pattern **MUST** contain a `playbooks/` directory. -- The `playbooks/` directory **MUST** contain at least one playbook associated with a job template definition in the pattern's `meta/pattern.json` file. -- A pattern **MAY** contain multiple playbooks. -- If a pattern contains multiple playbooks, it **MUST** define a primary playbook in its `meta/pattern.json` file. - -#### Playbook Requirements - -- All required and optional input variables to a pattern playbook **MUST** be defined in a play argument spec following this example of the [play argument spec format](https://github.com/ansible/ansible-creator/blob/main/src/ansible_creator/resources/playbook_project/argspec_validation_plays.meta.yml). -- If a pattern playbook requires any user-provided information other than variables to launch as a job template, such as inventory or credentials, those **MUST** be specified as `ask__at_launch` in the relevant `controller_job_templates` section of the pattern definition meta file. - -## Optional Files - -### `templates/` - -Templates for various types of catalog software in which patterns may be published. A template provides pattern data in the format required for a given catalog, such as Red Hat Developer Hub. - -- A pattern **MAY** contain a `templates/` directory to hold templates specific to catalogs that may publish the pattern, such as Red Hat Developer Hub or ServiceNow. -- The `templates/` directory **MAY** contain one or more catalog template files. - - -## Validation - -Pattern developers can use [ansible-lint](https://github.com/ansible/ansible-lint) to verify the structure of a pattern and its JSON against the pattern schema. - -## Example Pattern Directory - -```txt - -/extensions/patterns/ -├── network.backup/ # Backup pattern directory -│ ├── meta/pattern.json # Backup pattern definition -│ ├── README.md # Documentation for Backup pattern -│ ├── playbooks/ # Directory containing pattern playbooks -│ │ ├── backup.meta.yaml # Backup playbook arg spec -│ │ ├── backup.yaml # Backup playbook -│ ├── templates/ # Directory containing catalog templates -│ │ ├── rhdh.yaml # Display template for RHDH catalog - -``` - -## Other Pattern Requirements - -- A pattern **MUST** inherit the version number of the collection that contains it. -- A pattern **MUST** be valid according to the requirements specified in this document, including validation of each file contained in the pattern against its relevant schema. -- Changes to patterns **MUST** be noted in collection-level changelogs and release notes. -- All system, python, and Ansible collection dependencies needed to run a pattern's automations **MUST** be declared in the collection's dependency files, including but not limited to: `galaxy.yml`, `requirements.txt`, and `execution_environment.yml`. diff --git a/specifications/pattern-specification/pattern-specification-dev.md b/specifications/pattern-specification/pattern-specification-dev.md index bfb86f6b..c1fb3748 100644 --- a/specifications/pattern-specification/pattern-specification-dev.md +++ b/specifications/pattern-specification/pattern-specification-dev.md @@ -1,7 +1,7 @@ --- title: Ansible Patterns Specification author: Ansible Cloud Content team -version: dev +version: dev-2.0.0 --- # Ansible Pattern Specification From 4c9ef24628e494b9ee50f518ee4cdd77f86b0133 Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Wed, 17 Sep 2025 11:28:20 -0400 Subject: [PATCH 30/51] Spec file validation with changes to 1.0.0 file (should fail) --- .../pattern-specification/pattern-specification-1.0.0.md | 2 +- .../pattern-specification/pattern-specification-dev.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/specifications/pattern-specification/pattern-specification-1.0.0.md b/specifications/pattern-specification/pattern-specification-1.0.0.md index e5327b9b..ffe3b4b9 100644 --- a/specifications/pattern-specification/pattern-specification-1.0.0.md +++ b/specifications/pattern-specification/pattern-specification-1.0.0.md @@ -1,7 +1,7 @@ --- title: Ansible Patterns Specification author: Ansible Cloud Content team -version: 1.0.0 +version: 1.0.3 --- # Ansible Pattern Specification diff --git a/specifications/pattern-specification/pattern-specification-dev.md b/specifications/pattern-specification/pattern-specification-dev.md index c1fb3748..bfb86f6b 100644 --- a/specifications/pattern-specification/pattern-specification-dev.md +++ b/specifications/pattern-specification/pattern-specification-dev.md @@ -1,7 +1,7 @@ --- title: Ansible Patterns Specification author: Ansible Cloud Content team -version: dev-2.0.0 +version: dev --- # Ansible Pattern Specification From 0abef9050e9e1ecfb1015df2170808049732c586 Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Wed, 17 Sep 2025 11:30:46 -0400 Subject: [PATCH 31/51] Validate schema file with changes to dev (should pass) --- specifications/pattern-schema/pattern-schema-dev.json | 2 +- .../pattern-specification/pattern-specification-1.0.0.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/specifications/pattern-schema/pattern-schema-dev.json b/specifications/pattern-schema/pattern-schema-dev.json index 22b5a9f0..f43f42a4 100644 --- a/specifications/pattern-schema/pattern-schema-dev.json +++ b/specifications/pattern-schema/pattern-schema-dev.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-dev.json", + "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-dev-2.0.0.json", "title": "Ansible Pattern Schema", "description": "A schema for validating Ansible pattern definitions", "type": "object", diff --git a/specifications/pattern-specification/pattern-specification-1.0.0.md b/specifications/pattern-specification/pattern-specification-1.0.0.md index ffe3b4b9..e5327b9b 100644 --- a/specifications/pattern-specification/pattern-specification-1.0.0.md +++ b/specifications/pattern-specification/pattern-specification-1.0.0.md @@ -1,7 +1,7 @@ --- title: Ansible Patterns Specification author: Ansible Cloud Content team -version: 1.0.3 +version: 1.0.0 --- # Ansible Pattern Specification From 4e02828b1d6573f50a4fc92bb33b6cfeff1d86ab Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Wed, 17 Sep 2025 11:33:03 -0400 Subject: [PATCH 32/51] Add new schema file with version match in file + latest (should pass) --- .../pattern-schema/pattern-schema-2.0.0.json | 303 ++++++++++++++++++ .../pattern-schema/pattern-schema-dev.json | 2 +- .../pattern-schema/pattern-schema-latest.json | 2 +- 3 files changed, 305 insertions(+), 2 deletions(-) create mode 100644 specifications/pattern-schema/pattern-schema-2.0.0.json diff --git a/specifications/pattern-schema/pattern-schema-2.0.0.json b/specifications/pattern-schema/pattern-schema-2.0.0.json new file mode 100644 index 00000000..b940af4e --- /dev/null +++ b/specifications/pattern-schema/pattern-schema-2.0.0.json @@ -0,0 +1,303 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-2.0.0.json", + "title": "Ansible Pattern Schema", + "description": "A schema for validating Ansible pattern definitions", + "type": "object", + "x-ansible-lint": [ + "patterns/*/meta/pattern.json" + ], + "required": [ + "schema_version", + "name", + "title", + "description", + "short_description", + "aap_resources" + ], + "properties": { + "schema_version": { + "description": "Version of this schema being used", + "type": "string" + }, + "name": { + "description": "Machine-readable name for the pattern. MUST match the pattern directory name, and be limited to lowercase letters, numbers, and underscores, with a maximum length of 64 character.", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-z0-9_]+$", + "type": "string" + }, + "title": { + "description": "Human readable title for the pattern, may be used for pattern catalog and display purposes. It should meaningfully convey what the pattern does. It MUST be limited to 64 characters and should start with an action verb (e.g. Create an AWS EC2 instance).", + "maxLength": 64, + "type": "string" + }, + "description": { + "description": "A detailed explanation of what the pattern does. Use complete sentences with correct grammar to describe the functionality of the pattern and provide usage guidance. The description should provide context to the user so that they understand the purpose of the pattern and what they will gain by using it.", + "type": "string" + }, + "short_description": { + "description": "A one-line description of the entry point. Ideally it is a phrase and not a sentence. The short description should always be a string and never a list, and should not end in a period.", + "type": "string" + }, + "tags": { + "description": "List of tags associated with the pattern. Tags may be used to filter patterns in catalog UIs such as automation hub and Red Hat Developer Hub.", + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "aap_resources": { + "description": "Define the resources required in Ansible Automation Platform (AAP) to run the pattern automation.", + "type": "object", + "required": [ + "controller_project", + "controller_job_templates" + ], + "properties": { + "controller_project": { + "description": "Information about the project to be created in automation controller. Every pattern MUST specify a controller project, which will be sourced from the pattern's parent collection published in private automation hub.", + "type": "object", + "required": [ + "name", + "description" + ], + "properties": { + "name": { + "description": "Name for the project to be created in automation controller. The value MUST not exceed 200 characters.", + "maxLength": 200, + "type": "string" + }, + "description": { + "description": "Description of the project to be created in automation controller.", + "type": "string" + } + } + }, + "controller_execution_environment": { + "description": "Definition of the execution environment (EE) to be used by the pattern. An EE should be included if the pattern requires dependencies that are not available in the default controller EE. The EE image MUST be available in Private Automation Hub.", + "type": "object", + "required": [ + "name", + "description", + "image_name" + ], + "properties": { + "name": { + "description": "Name for the EE to be created in automation controller. The value MUST not exceed 200 characters.", + "maxLength": 200, + "type": "string" + }, + "description": { + "description": "Description for the EE to be created in automation controller.", + "type": "string" + }, + "image_name": { + "description": "Name of the execution environment (EE) image as published in Private Automation Hub (PAH). Use the format /, such as 'cloud/aws_ops_ee'. If the pattern is created before the image is published, the image MUST be uploaded to PAH with the name specified here. If the image is already in PAH, this value MUST exactly match the existing image name.", + "type": "string" + }, + "pull": { + "description": "Whether to pull the EE image before running.", + "type": "string", + "enum": [ + "always", + "missing", + "never" + ] + } + } + }, + "controller_labels": { + "description": "Labels to create for the pattern if not already present in automation controller.", + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "uniqueItems": true + }, + "controller_job_templates": { + "description": "A list of one or more job templates to be created in automation controller.", + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": [ + "name", + "description", + "playbook" + ], + "properties": { + "name": { + "description": "Name for the job template to be created in automation controller. The value MUST not exceed 200 characters.", + "maxLength": 200, + "type": "string" + }, + "description": { + "description": "Description for the job template to be created in automation controller.", + "type": "string" + }, + "execution_environment": { + "description": "Name of the execution environment to use for job template execution. MUST match the EE name defined in controller_execution_environment, IF defined.", + "type": "string" + }, + "playbook": { + "description": "Name of the playbook file included in the pattern `automations` directory.", + "type": "string" + }, + "primary": { + "description": "Whether the playbook is the primary playbook for this job template. If there are multiple job templates included, one should be labeled as the primary job template.", + "type": "boolean" + }, + "ask_credential_on_launch": { + "description": "Whether the user must select a credential when launching the job template.", + "type": "boolean" + }, + "ask_inventory_on_launch": { + "description": "Whether the user must select an inventory when launching the job template.", + "type": "boolean" + }, + "ask_verbosity_on_launch": { + "description": "Whether the user must choose a verbosity level when launching the job template.", + "type": "boolean" + }, + "labels": { + "description": "List of labels to associate with this resource. Each label MUST either already exist in automation controller or be defined in the 'controller_labels' field of this pattern to be created during pattern loading.", + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "uniqueItems": true + }, + "survey": { + "description": "A survey that captures required and optional variables from the user for configuring job templates.", + "type": "object", + "required": [ + "name", + "description", + "spec" + ], + "properties": { + "name": { + "type": "string", + "description": "Name of the survey." + }, + "description": { + "type": "string", + "description": "Description of the survey." + }, + "spec": { + "type": "array", + "description": "List of survey questions.", + "items": { + "type": "object", + "required": [ + "type", + "question_name", + "question_description", + "variable", + "required" + ], + "properties": { + "type": { + "type": "string", + "description": "Type of input field.", + "enum": [ + "text", + "textarea", + "password", + "integer", + "float", + "multiplechoice", + "multiselect" + ] + }, + "question_name": { + "type": "string", + "description": "Human-readable question title." + }, + "question_description": { + "type": "string", + "description": "A prompt that instructs the user to supply information or other input to the pattern. Use a complete sentence with correct grammar. Start with an imperative verb form and be specific and actionable. Address the user directly in a conversational and professional way." + }, + "variable": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-z0-9_]+$", + "description": "Variable name used to store the answer." + }, + "required": { + "type": "boolean", + "description": "Whether this question must be answered." + }, + "choices": { + "type": "array", + "description": "List of selectable values for multiple choice questions.", + "items": { + "type": "string" + } + }, + "default": { + "type": "string", + "description": "Default value for the input field." + } + }, + "allOf": [ + { + "if": { + "properties": { + "type": { + "const": "multiplechoice" + } + } + }, + "then": { + "required": [ + "choices" + ] + } + } + ] + } + } + } + } + } + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "aap_resources": { + "properties": { + "controller_job_templates": { + "type": "array", + "contains": { + "required": [ + "labels" + ] + } + } + } + } + } + }, + "then": { + "properties": { + "aap_resources": { + "required": [ + "controller_labels" + ] + } + } + } + } + ] +} diff --git a/specifications/pattern-schema/pattern-schema-dev.json b/specifications/pattern-schema/pattern-schema-dev.json index f43f42a4..22b5a9f0 100644 --- a/specifications/pattern-schema/pattern-schema-dev.json +++ b/specifications/pattern-schema/pattern-schema-dev.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-dev-2.0.0.json", + "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-dev.json", "title": "Ansible Pattern Schema", "description": "A schema for validating Ansible pattern definitions", "type": "object", diff --git a/specifications/pattern-schema/pattern-schema-latest.json b/specifications/pattern-schema/pattern-schema-latest.json index 08d46b4e..b940af4e 100644 --- a/specifications/pattern-schema/pattern-schema-latest.json +++ b/specifications/pattern-schema/pattern-schema-latest.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-1.0.0.json", + "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-2.0.0.json", "title": "Ansible Pattern Schema", "description": "A schema for validating Ansible pattern definitions", "type": "object", From 9260074293164c1eee7efe0a959b033c7d0dbcd1 Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Wed, 17 Sep 2025 11:40:08 -0400 Subject: [PATCH 33/51] Update validation workflow --- .github/workflows/validate-pattern-specs.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/validate-pattern-specs.yml b/.github/workflows/validate-pattern-specs.yml index bdf3122a..1a77c2d1 100644 --- a/.github/workflows/validate-pattern-specs.yml +++ b/.github/workflows/validate-pattern-specs.yml @@ -23,13 +23,13 @@ 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) + # Get only new spec files (excluding dev versions) 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 @@ -70,6 +70,7 @@ jobs: echo "$NEW_SCHEMA_FILES" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT + - name: Validate pattern specification files if: steps.changed-files.outputs.spec_files != '' run: | From be4dfdd7490aad055f72b41cc6e9ba950926517a Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Wed, 17 Sep 2025 11:48:39 -0400 Subject: [PATCH 34/51] Add more debug statements --- .github/workflows/validate-pattern-specs.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/validate-pattern-specs.yml b/.github/workflows/validate-pattern-specs.yml index 1a77c2d1..23190fa8 100644 --- a/.github/workflows/validate-pattern-specs.yml +++ b/.github/workflows/validate-pattern-specs.yml @@ -120,6 +120,9 @@ jobs: 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') + echo "DEBUG: Checking new versioned schema file $file" + echo "DEBUG: ACTUAL_ID = $ACTUAL_ID" + echo "DEBUG: EXPECTED_ID = $EXPECTED_ID" if [ "$ACTUAL_ID" != "$EXPECTED_ID" ]; then echo "::error::Schema file $file has incorrect \$id field. Expected: $EXPECTED_ID, Got: $ACTUAL_ID" exit 1 @@ -127,6 +130,9 @@ jobs: # Validate latest schema file points to new version LATEST_ID=$(git show HEAD:specifications/pattern-schema/pattern-schema-latest.json | jq -r '.["$id"] // empty') + echo "DEBUG: Checking 'latest' schema file" + echo "DEBUG: LATEST_ID = $LATEST_ID" + echo "DEBUG: EXPECTED_ID = $EXPECTED_ID" 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 @@ -135,7 +141,6 @@ jobs: echo "DEBUG: EXPECTED_ID = $EXPECTED_ID" echo "DEBUG: LATEST_ID = $LATEST_ID" echo "DEBUG: ACTUAL_ID = $ACTUAL_ID" - fi fi done <<< "$SCHEMA_FILES" From 5863abdb5eceafdb895d4c2d98e41ecd180bc8cc Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Wed, 17 Sep 2025 11:55:37 -0400 Subject: [PATCH 35/51] Try a 'skip' step to see if latest schema file validation works --- .github/workflows/validate-pattern-specs.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/validate-pattern-specs.yml b/.github/workflows/validate-pattern-specs.yml index 23190fa8..c87ddbf1 100644 --- a/.github/workflows/validate-pattern-specs.yml +++ b/.github/workflows/validate-pattern-specs.yml @@ -112,13 +112,19 @@ jobs: SCHEMA_FILES="${{ steps.changed-files.outputs.schema_files }}" while IFS= read -r file; do if [ -n "$file" ]; then + # Skip the latest file - it's handled separately + if [[ "$file" == *"pattern-schema-latest.json" ]]; then + continue + 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') echo "DEBUG: Checking new versioned schema file $file" echo "DEBUG: ACTUAL_ID = $ACTUAL_ID" From f405445e6e83b158f5a6f4a08dbd71d20364d98f Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Wed, 17 Sep 2025 12:00:53 -0400 Subject: [PATCH 36/51] Make more use of --diff-filter, remove debug statements --- .github/workflows/validate-pattern-specs.yml | 60 ++++++-------------- 1 file changed, 17 insertions(+), 43 deletions(-) diff --git a/.github/workflows/validate-pattern-specs.yml b/.github/workflows/validate-pattern-specs.yml index c87ddbf1..8a7cc4bf 100644 --- a/.github/workflows/validate-pattern-specs.yml +++ b/.github/workflows/validate-pattern-specs.yml @@ -26,50 +26,41 @@ jobs: - 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) - - # Get only new spec files (excluding dev versions) + # 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 - # Get only modified spec files (excluding dev versions) + # 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 - # Get all changed spec files (new + modified) + # 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 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 - - # 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 != '' @@ -102,17 +93,10 @@ jobs: - name: Validate pattern schema files if: steps.changed-files.outputs.schema_files != '' run: | - echo "=== DEBUG: Starting schema validation ===" - echo "Current working directory: $(pwd)" - echo "Git HEAD: $(git rev-parse HEAD)" - echo "Git status:" - git status - echo "=== END DEBUG ===" - SCHEMA_FILES="${{ steps.changed-files.outputs.schema_files }}" while IFS= read -r file; do if [ -n "$file" ]; then - # Skip the latest file - it's handled separately + # Skip the latest file to ensure proper validation of $id field if [[ "$file" == *"pattern-schema-latest.json" ]]; then continue fi @@ -126,9 +110,6 @@ jobs: # Validate versioned schema file's $id field ACTUAL_ID=$(git show HEAD:$file | jq -r '.["$id"] // empty') - echo "DEBUG: Checking new versioned schema file $file" - echo "DEBUG: ACTUAL_ID = $ACTUAL_ID" - echo "DEBUG: EXPECTED_ID = $EXPECTED_ID" if [ "$ACTUAL_ID" != "$EXPECTED_ID" ]; then echo "::error::Schema file $file has incorrect \$id field. Expected: $EXPECTED_ID, Got: $ACTUAL_ID" exit 1 @@ -136,17 +117,10 @@ jobs: # Validate latest schema file points to new version LATEST_ID=$(git show HEAD:specifications/pattern-schema/pattern-schema-latest.json | jq -r '.["$id"] // empty') - echo "DEBUG: Checking 'latest' schema file" - echo "DEBUG: LATEST_ID = $LATEST_ID" - echo "DEBUG: EXPECTED_ID = $EXPECTED_ID" 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 - echo "DEBUG: VERSION = $VERSION" - echo "DEBUG: EXPECTED_ID = $EXPECTED_ID" - echo "DEBUG: LATEST_ID = $LATEST_ID" - echo "DEBUG: ACTUAL_ID = $ACTUAL_ID" fi fi done <<< "$SCHEMA_FILES" From 85f90a9261bcc0e345b4201f3c0dcce855afe95b Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Wed, 17 Sep 2025 12:03:46 -0400 Subject: [PATCH 37/51] Validate new schema file with mismatch but matching latest (should fail) --- specifications/pattern-schema/pattern-schema-2.0.0.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specifications/pattern-schema/pattern-schema-2.0.0.json b/specifications/pattern-schema/pattern-schema-2.0.0.json index b940af4e..08d46b4e 100644 --- a/specifications/pattern-schema/pattern-schema-2.0.0.json +++ b/specifications/pattern-schema/pattern-schema-2.0.0.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-2.0.0.json", + "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-1.0.0.json", "title": "Ansible Pattern Schema", "description": "A schema for validating Ansible pattern definitions", "type": "object", From c5bed570bcbe0c4d84022ce03081542d4c818a14 Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Wed, 17 Sep 2025 12:07:01 -0400 Subject: [PATCH 38/51] Validate new schema file with match but mismatching latest (should fail) --- specifications/pattern-schema/pattern-schema-2.0.0.json | 2 +- specifications/pattern-schema/pattern-schema-latest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/specifications/pattern-schema/pattern-schema-2.0.0.json b/specifications/pattern-schema/pattern-schema-2.0.0.json index 08d46b4e..b940af4e 100644 --- a/specifications/pattern-schema/pattern-schema-2.0.0.json +++ b/specifications/pattern-schema/pattern-schema-2.0.0.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-1.0.0.json", + "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-2.0.0.json", "title": "Ansible Pattern Schema", "description": "A schema for validating Ansible pattern definitions", "type": "object", diff --git a/specifications/pattern-schema/pattern-schema-latest.json b/specifications/pattern-schema/pattern-schema-latest.json index b940af4e..08d46b4e 100644 --- a/specifications/pattern-schema/pattern-schema-latest.json +++ b/specifications/pattern-schema/pattern-schema-latest.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-2.0.0.json", + "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-1.0.0.json", "title": "Ansible Pattern Schema", "description": "A schema for validating Ansible pattern definitions", "type": "object", From d5e7e2544012e7bad8dc181b9c237abeff595ef1 Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Wed, 17 Sep 2025 12:09:21 -0400 Subject: [PATCH 39/51] Validate change to 1.0.0 schema file (should fail) --- .../pattern-schema/pattern-schema-1.0.0.json | 2 +- .../pattern-schema/pattern-schema-2.0.0.json | 303 ------------------ 2 files changed, 1 insertion(+), 304 deletions(-) delete mode 100644 specifications/pattern-schema/pattern-schema-2.0.0.json diff --git a/specifications/pattern-schema/pattern-schema-1.0.0.json b/specifications/pattern-schema/pattern-schema-1.0.0.json index 08d46b4e..30c4a296 100644 --- a/specifications/pattern-schema/pattern-schema-1.0.0.json +++ b/specifications/pattern-schema/pattern-schema-1.0.0.json @@ -17,7 +17,7 @@ ], "properties": { "schema_version": { - "description": "Version of this schema being used", + "description": "Version of this schema being used, I have been modified and should trigger an error", "type": "string" }, "name": { diff --git a/specifications/pattern-schema/pattern-schema-2.0.0.json b/specifications/pattern-schema/pattern-schema-2.0.0.json deleted file mode 100644 index b940af4e..00000000 --- a/specifications/pattern-schema/pattern-schema-2.0.0.json +++ /dev/null @@ -1,303 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-2.0.0.json", - "title": "Ansible Pattern Schema", - "description": "A schema for validating Ansible pattern definitions", - "type": "object", - "x-ansible-lint": [ - "patterns/*/meta/pattern.json" - ], - "required": [ - "schema_version", - "name", - "title", - "description", - "short_description", - "aap_resources" - ], - "properties": { - "schema_version": { - "description": "Version of this schema being used", - "type": "string" - }, - "name": { - "description": "Machine-readable name for the pattern. MUST match the pattern directory name, and be limited to lowercase letters, numbers, and underscores, with a maximum length of 64 character.", - "minLength": 1, - "maxLength": 64, - "pattern": "^[a-z0-9_]+$", - "type": "string" - }, - "title": { - "description": "Human readable title for the pattern, may be used for pattern catalog and display purposes. It should meaningfully convey what the pattern does. It MUST be limited to 64 characters and should start with an action verb (e.g. Create an AWS EC2 instance).", - "maxLength": 64, - "type": "string" - }, - "description": { - "description": "A detailed explanation of what the pattern does. Use complete sentences with correct grammar to describe the functionality of the pattern and provide usage guidance. The description should provide context to the user so that they understand the purpose of the pattern and what they will gain by using it.", - "type": "string" - }, - "short_description": { - "description": "A one-line description of the entry point. Ideally it is a phrase and not a sentence. The short description should always be a string and never a list, and should not end in a period.", - "type": "string" - }, - "tags": { - "description": "List of tags associated with the pattern. Tags may be used to filter patterns in catalog UIs such as automation hub and Red Hat Developer Hub.", - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "aap_resources": { - "description": "Define the resources required in Ansible Automation Platform (AAP) to run the pattern automation.", - "type": "object", - "required": [ - "controller_project", - "controller_job_templates" - ], - "properties": { - "controller_project": { - "description": "Information about the project to be created in automation controller. Every pattern MUST specify a controller project, which will be sourced from the pattern's parent collection published in private automation hub.", - "type": "object", - "required": [ - "name", - "description" - ], - "properties": { - "name": { - "description": "Name for the project to be created in automation controller. The value MUST not exceed 200 characters.", - "maxLength": 200, - "type": "string" - }, - "description": { - "description": "Description of the project to be created in automation controller.", - "type": "string" - } - } - }, - "controller_execution_environment": { - "description": "Definition of the execution environment (EE) to be used by the pattern. An EE should be included if the pattern requires dependencies that are not available in the default controller EE. The EE image MUST be available in Private Automation Hub.", - "type": "object", - "required": [ - "name", - "description", - "image_name" - ], - "properties": { - "name": { - "description": "Name for the EE to be created in automation controller. The value MUST not exceed 200 characters.", - "maxLength": 200, - "type": "string" - }, - "description": { - "description": "Description for the EE to be created in automation controller.", - "type": "string" - }, - "image_name": { - "description": "Name of the execution environment (EE) image as published in Private Automation Hub (PAH). Use the format /, such as 'cloud/aws_ops_ee'. If the pattern is created before the image is published, the image MUST be uploaded to PAH with the name specified here. If the image is already in PAH, this value MUST exactly match the existing image name.", - "type": "string" - }, - "pull": { - "description": "Whether to pull the EE image before running.", - "type": "string", - "enum": [ - "always", - "missing", - "never" - ] - } - } - }, - "controller_labels": { - "description": "Labels to create for the pattern if not already present in automation controller.", - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1, - "uniqueItems": true - }, - "controller_job_templates": { - "description": "A list of one or more job templates to be created in automation controller.", - "type": "array", - "minItems": 1, - "items": { - "type": "object", - "required": [ - "name", - "description", - "playbook" - ], - "properties": { - "name": { - "description": "Name for the job template to be created in automation controller. The value MUST not exceed 200 characters.", - "maxLength": 200, - "type": "string" - }, - "description": { - "description": "Description for the job template to be created in automation controller.", - "type": "string" - }, - "execution_environment": { - "description": "Name of the execution environment to use for job template execution. MUST match the EE name defined in controller_execution_environment, IF defined.", - "type": "string" - }, - "playbook": { - "description": "Name of the playbook file included in the pattern `automations` directory.", - "type": "string" - }, - "primary": { - "description": "Whether the playbook is the primary playbook for this job template. If there are multiple job templates included, one should be labeled as the primary job template.", - "type": "boolean" - }, - "ask_credential_on_launch": { - "description": "Whether the user must select a credential when launching the job template.", - "type": "boolean" - }, - "ask_inventory_on_launch": { - "description": "Whether the user must select an inventory when launching the job template.", - "type": "boolean" - }, - "ask_verbosity_on_launch": { - "description": "Whether the user must choose a verbosity level when launching the job template.", - "type": "boolean" - }, - "labels": { - "description": "List of labels to associate with this resource. Each label MUST either already exist in automation controller or be defined in the 'controller_labels' field of this pattern to be created during pattern loading.", - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1, - "uniqueItems": true - }, - "survey": { - "description": "A survey that captures required and optional variables from the user for configuring job templates.", - "type": "object", - "required": [ - "name", - "description", - "spec" - ], - "properties": { - "name": { - "type": "string", - "description": "Name of the survey." - }, - "description": { - "type": "string", - "description": "Description of the survey." - }, - "spec": { - "type": "array", - "description": "List of survey questions.", - "items": { - "type": "object", - "required": [ - "type", - "question_name", - "question_description", - "variable", - "required" - ], - "properties": { - "type": { - "type": "string", - "description": "Type of input field.", - "enum": [ - "text", - "textarea", - "password", - "integer", - "float", - "multiplechoice", - "multiselect" - ] - }, - "question_name": { - "type": "string", - "description": "Human-readable question title." - }, - "question_description": { - "type": "string", - "description": "A prompt that instructs the user to supply information or other input to the pattern. Use a complete sentence with correct grammar. Start with an imperative verb form and be specific and actionable. Address the user directly in a conversational and professional way." - }, - "variable": { - "type": "string", - "minLength": 1, - "maxLength": 64, - "pattern": "^[a-z0-9_]+$", - "description": "Variable name used to store the answer." - }, - "required": { - "type": "boolean", - "description": "Whether this question must be answered." - }, - "choices": { - "type": "array", - "description": "List of selectable values for multiple choice questions.", - "items": { - "type": "string" - } - }, - "default": { - "type": "string", - "description": "Default value for the input field." - } - }, - "allOf": [ - { - "if": { - "properties": { - "type": { - "const": "multiplechoice" - } - } - }, - "then": { - "required": [ - "choices" - ] - } - } - ] - } - } - } - } - } - } - } - } - } - }, - "allOf": [ - { - "if": { - "properties": { - "aap_resources": { - "properties": { - "controller_job_templates": { - "type": "array", - "contains": { - "required": [ - "labels" - ] - } - } - } - } - } - }, - "then": { - "properties": { - "aap_resources": { - "required": [ - "controller_labels" - ] - } - } - } - } - ] -} From f3a55affca0bb00baf2142607b428faff6014354 Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Wed, 17 Sep 2025 12:20:14 -0400 Subject: [PATCH 40/51] Add a check for ANY modification to existing schema files --- .github/workflows/validate-pattern-specs.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/validate-pattern-specs.yml b/.github/workflows/validate-pattern-specs.yml index 8a7cc4bf..924f24ae 100644 --- a/.github/workflows/validate-pattern-specs.yml +++ b/.github/workflows/validate-pattern-specs.yml @@ -101,6 +101,12 @@ jobs: 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') From faede9b5b76f6bd96bc2b40b1dbca8bae28b7f0e Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Wed, 17 Sep 2025 12:23:16 -0400 Subject: [PATCH 41/51] Re-validate new schema file with change to latest (should pass) --- .../pattern-schema/pattern-schema-1.0.0.json | 2 +- .../pattern-schema/pattern-schema-2.0.0.json | 303 ++++++++++++++++++ .../pattern-schema/pattern-schema-latest.json | 2 +- 3 files changed, 305 insertions(+), 2 deletions(-) create mode 100644 specifications/pattern-schema/pattern-schema-2.0.0.json diff --git a/specifications/pattern-schema/pattern-schema-1.0.0.json b/specifications/pattern-schema/pattern-schema-1.0.0.json index 30c4a296..08d46b4e 100644 --- a/specifications/pattern-schema/pattern-schema-1.0.0.json +++ b/specifications/pattern-schema/pattern-schema-1.0.0.json @@ -17,7 +17,7 @@ ], "properties": { "schema_version": { - "description": "Version of this schema being used, I have been modified and should trigger an error", + "description": "Version of this schema being used", "type": "string" }, "name": { diff --git a/specifications/pattern-schema/pattern-schema-2.0.0.json b/specifications/pattern-schema/pattern-schema-2.0.0.json new file mode 100644 index 00000000..b940af4e --- /dev/null +++ b/specifications/pattern-schema/pattern-schema-2.0.0.json @@ -0,0 +1,303 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-2.0.0.json", + "title": "Ansible Pattern Schema", + "description": "A schema for validating Ansible pattern definitions", + "type": "object", + "x-ansible-lint": [ + "patterns/*/meta/pattern.json" + ], + "required": [ + "schema_version", + "name", + "title", + "description", + "short_description", + "aap_resources" + ], + "properties": { + "schema_version": { + "description": "Version of this schema being used", + "type": "string" + }, + "name": { + "description": "Machine-readable name for the pattern. MUST match the pattern directory name, and be limited to lowercase letters, numbers, and underscores, with a maximum length of 64 character.", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-z0-9_]+$", + "type": "string" + }, + "title": { + "description": "Human readable title for the pattern, may be used for pattern catalog and display purposes. It should meaningfully convey what the pattern does. It MUST be limited to 64 characters and should start with an action verb (e.g. Create an AWS EC2 instance).", + "maxLength": 64, + "type": "string" + }, + "description": { + "description": "A detailed explanation of what the pattern does. Use complete sentences with correct grammar to describe the functionality of the pattern and provide usage guidance. The description should provide context to the user so that they understand the purpose of the pattern and what they will gain by using it.", + "type": "string" + }, + "short_description": { + "description": "A one-line description of the entry point. Ideally it is a phrase and not a sentence. The short description should always be a string and never a list, and should not end in a period.", + "type": "string" + }, + "tags": { + "description": "List of tags associated with the pattern. Tags may be used to filter patterns in catalog UIs such as automation hub and Red Hat Developer Hub.", + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "aap_resources": { + "description": "Define the resources required in Ansible Automation Platform (AAP) to run the pattern automation.", + "type": "object", + "required": [ + "controller_project", + "controller_job_templates" + ], + "properties": { + "controller_project": { + "description": "Information about the project to be created in automation controller. Every pattern MUST specify a controller project, which will be sourced from the pattern's parent collection published in private automation hub.", + "type": "object", + "required": [ + "name", + "description" + ], + "properties": { + "name": { + "description": "Name for the project to be created in automation controller. The value MUST not exceed 200 characters.", + "maxLength": 200, + "type": "string" + }, + "description": { + "description": "Description of the project to be created in automation controller.", + "type": "string" + } + } + }, + "controller_execution_environment": { + "description": "Definition of the execution environment (EE) to be used by the pattern. An EE should be included if the pattern requires dependencies that are not available in the default controller EE. The EE image MUST be available in Private Automation Hub.", + "type": "object", + "required": [ + "name", + "description", + "image_name" + ], + "properties": { + "name": { + "description": "Name for the EE to be created in automation controller. The value MUST not exceed 200 characters.", + "maxLength": 200, + "type": "string" + }, + "description": { + "description": "Description for the EE to be created in automation controller.", + "type": "string" + }, + "image_name": { + "description": "Name of the execution environment (EE) image as published in Private Automation Hub (PAH). Use the format /, such as 'cloud/aws_ops_ee'. If the pattern is created before the image is published, the image MUST be uploaded to PAH with the name specified here. If the image is already in PAH, this value MUST exactly match the existing image name.", + "type": "string" + }, + "pull": { + "description": "Whether to pull the EE image before running.", + "type": "string", + "enum": [ + "always", + "missing", + "never" + ] + } + } + }, + "controller_labels": { + "description": "Labels to create for the pattern if not already present in automation controller.", + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "uniqueItems": true + }, + "controller_job_templates": { + "description": "A list of one or more job templates to be created in automation controller.", + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": [ + "name", + "description", + "playbook" + ], + "properties": { + "name": { + "description": "Name for the job template to be created in automation controller. The value MUST not exceed 200 characters.", + "maxLength": 200, + "type": "string" + }, + "description": { + "description": "Description for the job template to be created in automation controller.", + "type": "string" + }, + "execution_environment": { + "description": "Name of the execution environment to use for job template execution. MUST match the EE name defined in controller_execution_environment, IF defined.", + "type": "string" + }, + "playbook": { + "description": "Name of the playbook file included in the pattern `automations` directory.", + "type": "string" + }, + "primary": { + "description": "Whether the playbook is the primary playbook for this job template. If there are multiple job templates included, one should be labeled as the primary job template.", + "type": "boolean" + }, + "ask_credential_on_launch": { + "description": "Whether the user must select a credential when launching the job template.", + "type": "boolean" + }, + "ask_inventory_on_launch": { + "description": "Whether the user must select an inventory when launching the job template.", + "type": "boolean" + }, + "ask_verbosity_on_launch": { + "description": "Whether the user must choose a verbosity level when launching the job template.", + "type": "boolean" + }, + "labels": { + "description": "List of labels to associate with this resource. Each label MUST either already exist in automation controller or be defined in the 'controller_labels' field of this pattern to be created during pattern loading.", + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "uniqueItems": true + }, + "survey": { + "description": "A survey that captures required and optional variables from the user for configuring job templates.", + "type": "object", + "required": [ + "name", + "description", + "spec" + ], + "properties": { + "name": { + "type": "string", + "description": "Name of the survey." + }, + "description": { + "type": "string", + "description": "Description of the survey." + }, + "spec": { + "type": "array", + "description": "List of survey questions.", + "items": { + "type": "object", + "required": [ + "type", + "question_name", + "question_description", + "variable", + "required" + ], + "properties": { + "type": { + "type": "string", + "description": "Type of input field.", + "enum": [ + "text", + "textarea", + "password", + "integer", + "float", + "multiplechoice", + "multiselect" + ] + }, + "question_name": { + "type": "string", + "description": "Human-readable question title." + }, + "question_description": { + "type": "string", + "description": "A prompt that instructs the user to supply information or other input to the pattern. Use a complete sentence with correct grammar. Start with an imperative verb form and be specific and actionable. Address the user directly in a conversational and professional way." + }, + "variable": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[a-z0-9_]+$", + "description": "Variable name used to store the answer." + }, + "required": { + "type": "boolean", + "description": "Whether this question must be answered." + }, + "choices": { + "type": "array", + "description": "List of selectable values for multiple choice questions.", + "items": { + "type": "string" + } + }, + "default": { + "type": "string", + "description": "Default value for the input field." + } + }, + "allOf": [ + { + "if": { + "properties": { + "type": { + "const": "multiplechoice" + } + } + }, + "then": { + "required": [ + "choices" + ] + } + } + ] + } + } + } + } + } + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "aap_resources": { + "properties": { + "controller_job_templates": { + "type": "array", + "contains": { + "required": [ + "labels" + ] + } + } + } + } + } + }, + "then": { + "properties": { + "aap_resources": { + "required": [ + "controller_labels" + ] + } + } + } + } + ] +} diff --git a/specifications/pattern-schema/pattern-schema-latest.json b/specifications/pattern-schema/pattern-schema-latest.json index 08d46b4e..b940af4e 100644 --- a/specifications/pattern-schema/pattern-schema-latest.json +++ b/specifications/pattern-schema/pattern-schema-latest.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-1.0.0.json", + "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-2.0.0.json", "title": "Ansible Pattern Schema", "description": "A schema for validating Ansible pattern definitions", "type": "object", From 2f9531290c2eef4d27afd56373d7f83633a55a55 Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Wed, 17 Sep 2025 12:25:27 -0400 Subject: [PATCH 42/51] Make change to dev schema file (should pass) --- specifications/pattern-schema/pattern-schema-dev.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specifications/pattern-schema/pattern-schema-dev.json b/specifications/pattern-schema/pattern-schema-dev.json index 22b5a9f0..b3a45c25 100644 --- a/specifications/pattern-schema/pattern-schema-dev.json +++ b/specifications/pattern-schema/pattern-schema-dev.json @@ -17,7 +17,7 @@ ], "properties": { "schema_version": { - "description": "Version of this schema being used", + "description": "Version of this schema being used, modifying me should not trigger an error", "type": "string" }, "name": { From 3b19bd98061a609be60a0dde87b9e3a0e886aaf4 Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Wed, 17 Sep 2025 12:27:23 -0400 Subject: [PATCH 43/51] Re-validate new schema file (should pass) --- .../{pattern-schema-2.0.0.json => pattern-schema-3.0.0.json} | 2 +- specifications/pattern-schema/pattern-schema-dev.json | 2 +- specifications/pattern-schema/pattern-schema-latest.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename specifications/pattern-schema/{pattern-schema-2.0.0.json => pattern-schema-3.0.0.json} (99%) diff --git a/specifications/pattern-schema/pattern-schema-2.0.0.json b/specifications/pattern-schema/pattern-schema-3.0.0.json similarity index 99% rename from specifications/pattern-schema/pattern-schema-2.0.0.json rename to specifications/pattern-schema/pattern-schema-3.0.0.json index b940af4e..a4c9e133 100644 --- a/specifications/pattern-schema/pattern-schema-2.0.0.json +++ b/specifications/pattern-schema/pattern-schema-3.0.0.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-2.0.0.json", + "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-3.0.0.json", "title": "Ansible Pattern Schema", "description": "A schema for validating Ansible pattern definitions", "type": "object", diff --git a/specifications/pattern-schema/pattern-schema-dev.json b/specifications/pattern-schema/pattern-schema-dev.json index b3a45c25..22b5a9f0 100644 --- a/specifications/pattern-schema/pattern-schema-dev.json +++ b/specifications/pattern-schema/pattern-schema-dev.json @@ -17,7 +17,7 @@ ], "properties": { "schema_version": { - "description": "Version of this schema being used, modifying me should not trigger an error", + "description": "Version of this schema being used", "type": "string" }, "name": { diff --git a/specifications/pattern-schema/pattern-schema-latest.json b/specifications/pattern-schema/pattern-schema-latest.json index b940af4e..a4c9e133 100644 --- a/specifications/pattern-schema/pattern-schema-latest.json +++ b/specifications/pattern-schema/pattern-schema-latest.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-2.0.0.json", + "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-3.0.0.json", "title": "Ansible Pattern Schema", "description": "A schema for validating Ansible pattern definitions", "type": "object", From 20326767f9803a079bf5fd7364f52bd3ee402260 Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Wed, 17 Sep 2025 12:32:08 -0400 Subject: [PATCH 44/51] Validate any change to existing spec file that isn't dev (should fail) --- .github/workflows/validate-pattern-specs.yml | 45 +-- .../pattern-schema/pattern-schema-3.0.0.json | 303 ------------------ .../pattern-schema/pattern-schema-latest.json | 2 +- .../pattern-specification-1.0.0.md | 2 +- 4 files changed, 27 insertions(+), 325 deletions(-) delete mode 100644 specifications/pattern-schema/pattern-schema-3.0.0.json diff --git a/.github/workflows/validate-pattern-specs.yml b/.github/workflows/validate-pattern-specs.yml index 924f24ae..db3b23b1 100644 --- a/.github/workflows/validate-pattern-specs.yml +++ b/.github/workflows/validate-pattern-specs.yml @@ -65,30 +65,35 @@ jobs: - name: Validate pattern specification files if: steps.changed-files.outputs.spec_files != '' run: | - SPEC_FILES="${{ steps.changed-files.outputs.spec_files }}" + # 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 + + # Then validate new specification files + NEW_SPEC_FILES="${{ steps.changed-files.outputs.new_spec_files }}" while IFS= 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=$(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 + # 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 + + # Validate latest file points to new version + LATEST_VERSION=$(sed -n 's/^version:[[:space:]]*\([^[:space:]]*\).*/\1/p' "specifications/pattern-specification/pattern-specification-latest.md" | 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 fi - done <<< "$SPEC_FILES" + done <<< "$NEW_SPEC_FILES" - name: Validate pattern schema files if: steps.changed-files.outputs.schema_files != '' diff --git a/specifications/pattern-schema/pattern-schema-3.0.0.json b/specifications/pattern-schema/pattern-schema-3.0.0.json deleted file mode 100644 index a4c9e133..00000000 --- a/specifications/pattern-schema/pattern-schema-3.0.0.json +++ /dev/null @@ -1,303 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-3.0.0.json", - "title": "Ansible Pattern Schema", - "description": "A schema for validating Ansible pattern definitions", - "type": "object", - "x-ansible-lint": [ - "patterns/*/meta/pattern.json" - ], - "required": [ - "schema_version", - "name", - "title", - "description", - "short_description", - "aap_resources" - ], - "properties": { - "schema_version": { - "description": "Version of this schema being used", - "type": "string" - }, - "name": { - "description": "Machine-readable name for the pattern. MUST match the pattern directory name, and be limited to lowercase letters, numbers, and underscores, with a maximum length of 64 character.", - "minLength": 1, - "maxLength": 64, - "pattern": "^[a-z0-9_]+$", - "type": "string" - }, - "title": { - "description": "Human readable title for the pattern, may be used for pattern catalog and display purposes. It should meaningfully convey what the pattern does. It MUST be limited to 64 characters and should start with an action verb (e.g. Create an AWS EC2 instance).", - "maxLength": 64, - "type": "string" - }, - "description": { - "description": "A detailed explanation of what the pattern does. Use complete sentences with correct grammar to describe the functionality of the pattern and provide usage guidance. The description should provide context to the user so that they understand the purpose of the pattern and what they will gain by using it.", - "type": "string" - }, - "short_description": { - "description": "A one-line description of the entry point. Ideally it is a phrase and not a sentence. The short description should always be a string and never a list, and should not end in a period.", - "type": "string" - }, - "tags": { - "description": "List of tags associated with the pattern. Tags may be used to filter patterns in catalog UIs such as automation hub and Red Hat Developer Hub.", - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "aap_resources": { - "description": "Define the resources required in Ansible Automation Platform (AAP) to run the pattern automation.", - "type": "object", - "required": [ - "controller_project", - "controller_job_templates" - ], - "properties": { - "controller_project": { - "description": "Information about the project to be created in automation controller. Every pattern MUST specify a controller project, which will be sourced from the pattern's parent collection published in private automation hub.", - "type": "object", - "required": [ - "name", - "description" - ], - "properties": { - "name": { - "description": "Name for the project to be created in automation controller. The value MUST not exceed 200 characters.", - "maxLength": 200, - "type": "string" - }, - "description": { - "description": "Description of the project to be created in automation controller.", - "type": "string" - } - } - }, - "controller_execution_environment": { - "description": "Definition of the execution environment (EE) to be used by the pattern. An EE should be included if the pattern requires dependencies that are not available in the default controller EE. The EE image MUST be available in Private Automation Hub.", - "type": "object", - "required": [ - "name", - "description", - "image_name" - ], - "properties": { - "name": { - "description": "Name for the EE to be created in automation controller. The value MUST not exceed 200 characters.", - "maxLength": 200, - "type": "string" - }, - "description": { - "description": "Description for the EE to be created in automation controller.", - "type": "string" - }, - "image_name": { - "description": "Name of the execution environment (EE) image as published in Private Automation Hub (PAH). Use the format /, such as 'cloud/aws_ops_ee'. If the pattern is created before the image is published, the image MUST be uploaded to PAH with the name specified here. If the image is already in PAH, this value MUST exactly match the existing image name.", - "type": "string" - }, - "pull": { - "description": "Whether to pull the EE image before running.", - "type": "string", - "enum": [ - "always", - "missing", - "never" - ] - } - } - }, - "controller_labels": { - "description": "Labels to create for the pattern if not already present in automation controller.", - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1, - "uniqueItems": true - }, - "controller_job_templates": { - "description": "A list of one or more job templates to be created in automation controller.", - "type": "array", - "minItems": 1, - "items": { - "type": "object", - "required": [ - "name", - "description", - "playbook" - ], - "properties": { - "name": { - "description": "Name for the job template to be created in automation controller. The value MUST not exceed 200 characters.", - "maxLength": 200, - "type": "string" - }, - "description": { - "description": "Description for the job template to be created in automation controller.", - "type": "string" - }, - "execution_environment": { - "description": "Name of the execution environment to use for job template execution. MUST match the EE name defined in controller_execution_environment, IF defined.", - "type": "string" - }, - "playbook": { - "description": "Name of the playbook file included in the pattern `automations` directory.", - "type": "string" - }, - "primary": { - "description": "Whether the playbook is the primary playbook for this job template. If there are multiple job templates included, one should be labeled as the primary job template.", - "type": "boolean" - }, - "ask_credential_on_launch": { - "description": "Whether the user must select a credential when launching the job template.", - "type": "boolean" - }, - "ask_inventory_on_launch": { - "description": "Whether the user must select an inventory when launching the job template.", - "type": "boolean" - }, - "ask_verbosity_on_launch": { - "description": "Whether the user must choose a verbosity level when launching the job template.", - "type": "boolean" - }, - "labels": { - "description": "List of labels to associate with this resource. Each label MUST either already exist in automation controller or be defined in the 'controller_labels' field of this pattern to be created during pattern loading.", - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1, - "uniqueItems": true - }, - "survey": { - "description": "A survey that captures required and optional variables from the user for configuring job templates.", - "type": "object", - "required": [ - "name", - "description", - "spec" - ], - "properties": { - "name": { - "type": "string", - "description": "Name of the survey." - }, - "description": { - "type": "string", - "description": "Description of the survey." - }, - "spec": { - "type": "array", - "description": "List of survey questions.", - "items": { - "type": "object", - "required": [ - "type", - "question_name", - "question_description", - "variable", - "required" - ], - "properties": { - "type": { - "type": "string", - "description": "Type of input field.", - "enum": [ - "text", - "textarea", - "password", - "integer", - "float", - "multiplechoice", - "multiselect" - ] - }, - "question_name": { - "type": "string", - "description": "Human-readable question title." - }, - "question_description": { - "type": "string", - "description": "A prompt that instructs the user to supply information or other input to the pattern. Use a complete sentence with correct grammar. Start with an imperative verb form and be specific and actionable. Address the user directly in a conversational and professional way." - }, - "variable": { - "type": "string", - "minLength": 1, - "maxLength": 64, - "pattern": "^[a-z0-9_]+$", - "description": "Variable name used to store the answer." - }, - "required": { - "type": "boolean", - "description": "Whether this question must be answered." - }, - "choices": { - "type": "array", - "description": "List of selectable values for multiple choice questions.", - "items": { - "type": "string" - } - }, - "default": { - "type": "string", - "description": "Default value for the input field." - } - }, - "allOf": [ - { - "if": { - "properties": { - "type": { - "const": "multiplechoice" - } - } - }, - "then": { - "required": [ - "choices" - ] - } - } - ] - } - } - } - } - } - } - } - } - } - }, - "allOf": [ - { - "if": { - "properties": { - "aap_resources": { - "properties": { - "controller_job_templates": { - "type": "array", - "contains": { - "required": [ - "labels" - ] - } - } - } - } - } - }, - "then": { - "properties": { - "aap_resources": { - "required": [ - "controller_labels" - ] - } - } - } - } - ] -} diff --git a/specifications/pattern-schema/pattern-schema-latest.json b/specifications/pattern-schema/pattern-schema-latest.json index a4c9e133..08d46b4e 100644 --- a/specifications/pattern-schema/pattern-schema-latest.json +++ b/specifications/pattern-schema/pattern-schema-latest.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-3.0.0.json", + "$id": "https://raw.githubusercontent.com/ansible/pattern-service/main/specifications/pattern-schema/pattern-schema-1.0.0.json", "title": "Ansible Pattern Schema", "description": "A schema for validating Ansible pattern definitions", "type": "object", diff --git a/specifications/pattern-specification/pattern-specification-1.0.0.md b/specifications/pattern-specification/pattern-specification-1.0.0.md index e5327b9b..78dea8d7 100644 --- a/specifications/pattern-specification/pattern-specification-1.0.0.md +++ b/specifications/pattern-specification/pattern-specification-1.0.0.md @@ -6,7 +6,7 @@ version: 1.0.0 # Ansible Pattern Specification -## Introduction +## Introduction: Please trigger error! This document describes the technical specification for an Ansible pattern, an extension of collections, which are the standard method for packaging and distributing Ansible content. Patterns are used by Ansible Automation Platform (AAP) to offer users the ability to start automating with minimal setup, enhancing productivity and efficiency. From d228705a2a85c08944c9dbfa01bb18f8b1514c4f Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Wed, 17 Sep 2025 12:38:23 -0400 Subject: [PATCH 45/51] Validate new spec file with version match (should pass) --- .../pattern-specification-1.0.0.md | 2 +- .../pattern-specification-2.0.0.md | 101 ++++++++++++++++++ .../pattern-specification-dev.md | 2 +- 3 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 specifications/pattern-specification/pattern-specification-2.0.0.md diff --git a/specifications/pattern-specification/pattern-specification-1.0.0.md b/specifications/pattern-specification/pattern-specification-1.0.0.md index 78dea8d7..4670069d 100644 --- a/specifications/pattern-specification/pattern-specification-1.0.0.md +++ b/specifications/pattern-specification/pattern-specification-1.0.0.md @@ -6,7 +6,7 @@ version: 1.0.0 # Ansible Pattern Specification -## Introduction: Please trigger error! +## Introduction: This document describes the technical specification for an Ansible pattern, an extension of collections, which are the standard method for packaging and distributing Ansible content. Patterns are used by Ansible Automation Platform (AAP) to offer users the ability to start automating with minimal setup, enhancing productivity and efficiency. diff --git a/specifications/pattern-specification/pattern-specification-2.0.0.md b/specifications/pattern-specification/pattern-specification-2.0.0.md new file mode 100644 index 00000000..f7222b5f --- /dev/null +++ b/specifications/pattern-specification/pattern-specification-2.0.0.md @@ -0,0 +1,101 @@ +--- +title: Ansible Patterns Specification +author: Ansible Cloud Content team +version: 2.0.0 +--- + +# Ansible Pattern Specification + +## Introduction: Please trigger error! + +This document describes the technical specification for an Ansible pattern, an extension of collections, which are the standard method for packaging and distributing Ansible content. Patterns are used by Ansible Automation Platform (AAP) to offer users the ability to start automating with minimal setup, enhancing productivity and efficiency. + +A pattern defines an Ansible automation, such as a playbook, and the resources needed in AAP to run that automation, such as a project, execution environment, job template, and survey. The pattern definition can be consumed by the AAP API to create and configure those resources, allowing pattern catalogs to provide users with a seamless journey from identifying relevant patterns to using them in AAP. + +Patterns are delivered as files within an Ansible collection. This approach ensures consistent integration with the Ansible ecosystem by leveraging the existing collection framework. These patterns are bundled during collection packaging, enabling them to be searchable, downloadable, and publishable alongside roles, modules, and plugins. + +## Conventions + +This document follows the IETF [RFC2119](https://datatracker.ietf.org/doc/html/rfc2119) definitions of Key words for use in RFCs to Indicate Requirement Levels. + +## Support + +This pattern specification is supported and maintained by Ansible. To submit support or other requests for the specification, please contact Red Hat support. + +Individual patterns that implement this specification are the responsibility of the collection maintainers for the collections in which they reside. For support or feature requests for individual patterns, contact their collection owners. + +## Pattern Directory Structure + +- A pattern **MUST** be contained within a single directory in the `/extensions/patterns/` directory of an Ansible collection. +- An Ansible pattern directory name **MUST** be limited to 64 characters and **MUST** only include lowercase ASCII letters, digits, and underscores. +- An Ansible collection **MAY** contain zero or more patterns. + +## Required Files + +### `meta/pattern.json` + +The pattern definition meta file is the machine-readable entry point for creating an instance of the pattern in AAP. It defines the resources required to execute the pattern, such as a controller project, execution environment, job templates, and labels. The pattern definition also includes metadata about the pattern to enable its discovery and use, such as its title, audience, and tags. A [JSON schema](https://github.com/ansible/pattern-service/blob/main/specifications/pattern-schema/pattern-schema-1.0.0.json) has been published to aid with validation of the pattern definition file. + +- A pattern **MUST** include exactly one meta file defining the pattern metadata and AAP resources it requires. +- The pattern definition meta file **MUST** be a valid instance of the [Ansible pattern schema](https://github.com/ansible/pattern-service/blob/main/specifications/pattern-schema/pattern-schema-1.0.0.json). + +### `README.md` + +The README file is the human-readable documentation for the pattern. It provides information on what the pattern does, its inputs, its dependencies, and how it can be used. + +- A pattern **MUST** include a README file. +- The pattern README file **SHOULD** include all of the following: + - A description of what the pattern does + - A list of the AAP resources created by the pattern + - Documentation on how to use the pattern + +### `playbooks/` + +Pattern playbook files are included in the `/extensions/patterns//playbooks/` directory. + +- A pattern **MUST** contain a `playbooks/` directory. +- The `playbooks/` directory **MUST** contain at least one playbook associated with a job template definition in the pattern's `meta/pattern.json` file. +- A pattern **MAY** contain multiple playbooks. +- If a pattern contains multiple playbooks, it **MUST** define a primary playbook in its `meta/pattern.json` file. + +#### Playbook Requirements + +- All required and optional input variables to a pattern playbook **MUST** be defined in a play argument spec following this example of the [play argument spec format](https://github.com/ansible/ansible-creator/blob/main/src/ansible_creator/resources/playbook_project/argspec_validation_plays.meta.yml). +- If a pattern playbook requires any user-provided information other than variables to launch as a job template, such as inventory or credentials, those **MUST** be specified as `ask__at_launch` in the relevant `controller_job_templates` section of the pattern definition meta file. + +## Optional Files + +### `templates/` + +Templates for various types of catalog software in which patterns may be published. A template provides pattern data in the format required for a given catalog, such as Red Hat Developer Hub. + +- A pattern **MAY** contain a `templates/` directory to hold templates specific to catalogs that may publish the pattern, such as Red Hat Developer Hub or ServiceNow. +- The `templates/` directory **MAY** contain one or more catalog template files. + + +## Validation + +Pattern developers can use [ansible-lint](https://github.com/ansible/ansible-lint) to verify the structure of a pattern and its JSON against the pattern schema. + +## Example Pattern Directory + +```txt + +/extensions/patterns/ +├── network.backup/ # Backup pattern directory +│ ├── meta/pattern.json # Backup pattern definition +│ ├── README.md # Documentation for Backup pattern +│ ├── playbooks/ # Directory containing pattern playbooks +│ │ ├── backup.meta.yaml # Backup playbook arg spec +│ │ ├── backup.yaml # Backup playbook +│ ├── templates/ # Directory containing catalog templates +│ │ ├── rhdh.yaml # Display template for RHDH catalog + +``` + +## Other Pattern Requirements + +- A pattern **MUST** inherit the version number of the collection that contains it. +- A pattern **MUST** be valid according to the requirements specified in this document, including validation of each file contained in the pattern against its relevant schema. +- Changes to patterns **MUST** be noted in collection-level changelogs and release notes. +- All system, python, and Ansible collection dependencies needed to run a pattern's automations **MUST** be declared in the collection's dependency files, including but not limited to: `galaxy.yml`, `requirements.txt`, and `execution_environment.yml`. diff --git a/specifications/pattern-specification/pattern-specification-dev.md b/specifications/pattern-specification/pattern-specification-dev.md index bfb86f6b..c1fb3748 100644 --- a/specifications/pattern-specification/pattern-specification-dev.md +++ b/specifications/pattern-specification/pattern-specification-dev.md @@ -1,7 +1,7 @@ --- title: Ansible Patterns Specification author: Ansible Cloud Content team -version: dev +version: dev-2.0.0 --- # Ansible Pattern Specification From 9a70f9dd8e276f0d2eac81bf759fb0112806a873 Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Wed, 17 Sep 2025 12:40:20 -0400 Subject: [PATCH 46/51] Validate new spec file with version match (should pass) --- .github/workflows/validate-pattern-specs.yml | 7 ------- .../pattern-specification/pattern-specification-1.0.0.md | 2 +- .../pattern-specification/pattern-specification-dev.md | 2 +- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/.github/workflows/validate-pattern-specs.yml b/.github/workflows/validate-pattern-specs.yml index db3b23b1..d7686224 100644 --- a/.github/workflows/validate-pattern-specs.yml +++ b/.github/workflows/validate-pattern-specs.yml @@ -85,13 +85,6 @@ jobs: 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=$(sed -n 's/^version:[[:space:]]*\([^[:space:]]*\).*/\1/p' "specifications/pattern-specification/pattern-specification-latest.md" | 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 fi done <<< "$NEW_SPEC_FILES" diff --git a/specifications/pattern-specification/pattern-specification-1.0.0.md b/specifications/pattern-specification/pattern-specification-1.0.0.md index 4670069d..e5327b9b 100644 --- a/specifications/pattern-specification/pattern-specification-1.0.0.md +++ b/specifications/pattern-specification/pattern-specification-1.0.0.md @@ -6,7 +6,7 @@ version: 1.0.0 # Ansible Pattern Specification -## Introduction: +## Introduction This document describes the technical specification for an Ansible pattern, an extension of collections, which are the standard method for packaging and distributing Ansible content. Patterns are used by Ansible Automation Platform (AAP) to offer users the ability to start automating with minimal setup, enhancing productivity and efficiency. diff --git a/specifications/pattern-specification/pattern-specification-dev.md b/specifications/pattern-specification/pattern-specification-dev.md index c1fb3748..bfb86f6b 100644 --- a/specifications/pattern-specification/pattern-specification-dev.md +++ b/specifications/pattern-specification/pattern-specification-dev.md @@ -1,7 +1,7 @@ --- title: Ansible Patterns Specification author: Ansible Cloud Content team -version: dev-2.0.0 +version: dev --- # Ansible Pattern Specification From 33e933424fa5d9c090ed32faec0b7f9e7f9e7ed5 Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Wed, 17 Sep 2025 12:55:07 -0400 Subject: [PATCH 47/51] Validate changes to 1.00 spec file (should fail) --- .../pattern-specification/pattern-specification-1.0.0.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specifications/pattern-specification/pattern-specification-1.0.0.md b/specifications/pattern-specification/pattern-specification-1.0.0.md index e5327b9b..c29d11fd 100644 --- a/specifications/pattern-specification/pattern-specification-1.0.0.md +++ b/specifications/pattern-specification/pattern-specification-1.0.0.md @@ -6,7 +6,7 @@ version: 1.0.0 # Ansible Pattern Specification -## Introduction +## Introduction: Please trigger error This document describes the technical specification for an Ansible pattern, an extension of collections, which are the standard method for packaging and distributing Ansible content. Patterns are used by Ansible Automation Platform (AAP) to offer users the ability to start automating with minimal setup, enhancing productivity and efficiency. From bb4c028d7d0cbf6155bf3db451f53d6c49ba64b3 Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Wed, 17 Sep 2025 12:56:19 -0400 Subject: [PATCH 48/51] Everything back to normal (should pass) --- .../pattern-specification-1.0.0.md | 2 +- .../pattern-specification-2.0.0.md | 101 ------------------ 2 files changed, 1 insertion(+), 102 deletions(-) delete mode 100644 specifications/pattern-specification/pattern-specification-2.0.0.md diff --git a/specifications/pattern-specification/pattern-specification-1.0.0.md b/specifications/pattern-specification/pattern-specification-1.0.0.md index c29d11fd..e5327b9b 100644 --- a/specifications/pattern-specification/pattern-specification-1.0.0.md +++ b/specifications/pattern-specification/pattern-specification-1.0.0.md @@ -6,7 +6,7 @@ version: 1.0.0 # Ansible Pattern Specification -## Introduction: Please trigger error +## Introduction This document describes the technical specification for an Ansible pattern, an extension of collections, which are the standard method for packaging and distributing Ansible content. Patterns are used by Ansible Automation Platform (AAP) to offer users the ability to start automating with minimal setup, enhancing productivity and efficiency. diff --git a/specifications/pattern-specification/pattern-specification-2.0.0.md b/specifications/pattern-specification/pattern-specification-2.0.0.md deleted file mode 100644 index f7222b5f..00000000 --- a/specifications/pattern-specification/pattern-specification-2.0.0.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -title: Ansible Patterns Specification -author: Ansible Cloud Content team -version: 2.0.0 ---- - -# Ansible Pattern Specification - -## Introduction: Please trigger error! - -This document describes the technical specification for an Ansible pattern, an extension of collections, which are the standard method for packaging and distributing Ansible content. Patterns are used by Ansible Automation Platform (AAP) to offer users the ability to start automating with minimal setup, enhancing productivity and efficiency. - -A pattern defines an Ansible automation, such as a playbook, and the resources needed in AAP to run that automation, such as a project, execution environment, job template, and survey. The pattern definition can be consumed by the AAP API to create and configure those resources, allowing pattern catalogs to provide users with a seamless journey from identifying relevant patterns to using them in AAP. - -Patterns are delivered as files within an Ansible collection. This approach ensures consistent integration with the Ansible ecosystem by leveraging the existing collection framework. These patterns are bundled during collection packaging, enabling them to be searchable, downloadable, and publishable alongside roles, modules, and plugins. - -## Conventions - -This document follows the IETF [RFC2119](https://datatracker.ietf.org/doc/html/rfc2119) definitions of Key words for use in RFCs to Indicate Requirement Levels. - -## Support - -This pattern specification is supported and maintained by Ansible. To submit support or other requests for the specification, please contact Red Hat support. - -Individual patterns that implement this specification are the responsibility of the collection maintainers for the collections in which they reside. For support or feature requests for individual patterns, contact their collection owners. - -## Pattern Directory Structure - -- A pattern **MUST** be contained within a single directory in the `/extensions/patterns/` directory of an Ansible collection. -- An Ansible pattern directory name **MUST** be limited to 64 characters and **MUST** only include lowercase ASCII letters, digits, and underscores. -- An Ansible collection **MAY** contain zero or more patterns. - -## Required Files - -### `meta/pattern.json` - -The pattern definition meta file is the machine-readable entry point for creating an instance of the pattern in AAP. It defines the resources required to execute the pattern, such as a controller project, execution environment, job templates, and labels. The pattern definition also includes metadata about the pattern to enable its discovery and use, such as its title, audience, and tags. A [JSON schema](https://github.com/ansible/pattern-service/blob/main/specifications/pattern-schema/pattern-schema-1.0.0.json) has been published to aid with validation of the pattern definition file. - -- A pattern **MUST** include exactly one meta file defining the pattern metadata and AAP resources it requires. -- The pattern definition meta file **MUST** be a valid instance of the [Ansible pattern schema](https://github.com/ansible/pattern-service/blob/main/specifications/pattern-schema/pattern-schema-1.0.0.json). - -### `README.md` - -The README file is the human-readable documentation for the pattern. It provides information on what the pattern does, its inputs, its dependencies, and how it can be used. - -- A pattern **MUST** include a README file. -- The pattern README file **SHOULD** include all of the following: - - A description of what the pattern does - - A list of the AAP resources created by the pattern - - Documentation on how to use the pattern - -### `playbooks/` - -Pattern playbook files are included in the `/extensions/patterns//playbooks/` directory. - -- A pattern **MUST** contain a `playbooks/` directory. -- The `playbooks/` directory **MUST** contain at least one playbook associated with a job template definition in the pattern's `meta/pattern.json` file. -- A pattern **MAY** contain multiple playbooks. -- If a pattern contains multiple playbooks, it **MUST** define a primary playbook in its `meta/pattern.json` file. - -#### Playbook Requirements - -- All required and optional input variables to a pattern playbook **MUST** be defined in a play argument spec following this example of the [play argument spec format](https://github.com/ansible/ansible-creator/blob/main/src/ansible_creator/resources/playbook_project/argspec_validation_plays.meta.yml). -- If a pattern playbook requires any user-provided information other than variables to launch as a job template, such as inventory or credentials, those **MUST** be specified as `ask__at_launch` in the relevant `controller_job_templates` section of the pattern definition meta file. - -## Optional Files - -### `templates/` - -Templates for various types of catalog software in which patterns may be published. A template provides pattern data in the format required for a given catalog, such as Red Hat Developer Hub. - -- A pattern **MAY** contain a `templates/` directory to hold templates specific to catalogs that may publish the pattern, such as Red Hat Developer Hub or ServiceNow. -- The `templates/` directory **MAY** contain one or more catalog template files. - - -## Validation - -Pattern developers can use [ansible-lint](https://github.com/ansible/ansible-lint) to verify the structure of a pattern and its JSON against the pattern schema. - -## Example Pattern Directory - -```txt - -/extensions/patterns/ -├── network.backup/ # Backup pattern directory -│ ├── meta/pattern.json # Backup pattern definition -│ ├── README.md # Documentation for Backup pattern -│ ├── playbooks/ # Directory containing pattern playbooks -│ │ ├── backup.meta.yaml # Backup playbook arg spec -│ │ ├── backup.yaml # Backup playbook -│ ├── templates/ # Directory containing catalog templates -│ │ ├── rhdh.yaml # Display template for RHDH catalog - -``` - -## Other Pattern Requirements - -- A pattern **MUST** inherit the version number of the collection that contains it. -- A pattern **MUST** be valid according to the requirements specified in this document, including validation of each file contained in the pattern against its relevant schema. -- Changes to patterns **MUST** be noted in collection-level changelogs and release notes. -- All system, python, and Ansible collection dependencies needed to run a pattern's automations **MUST** be declared in the collection's dependency files, including but not limited to: `galaxy.yml`, `requirements.txt`, and `execution_environment.yml`. From 0e97037e2caa8206027df134d774c9dfc4b1c87e Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Wed, 17 Sep 2025 12:57:40 -0400 Subject: [PATCH 49/51] Validate new spec file with version mismatch (should fail) --- .../pattern-specification-2.0.0.md | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 specifications/pattern-specification/pattern-specification-2.0.0.md diff --git a/specifications/pattern-specification/pattern-specification-2.0.0.md b/specifications/pattern-specification/pattern-specification-2.0.0.md new file mode 100644 index 00000000..e5327b9b --- /dev/null +++ b/specifications/pattern-specification/pattern-specification-2.0.0.md @@ -0,0 +1,101 @@ +--- +title: Ansible Patterns Specification +author: Ansible Cloud Content team +version: 1.0.0 +--- + +# Ansible Pattern Specification + +## Introduction + +This document describes the technical specification for an Ansible pattern, an extension of collections, which are the standard method for packaging and distributing Ansible content. Patterns are used by Ansible Automation Platform (AAP) to offer users the ability to start automating with minimal setup, enhancing productivity and efficiency. + +A pattern defines an Ansible automation, such as a playbook, and the resources needed in AAP to run that automation, such as a project, execution environment, job template, and survey. The pattern definition can be consumed by the AAP API to create and configure those resources, allowing pattern catalogs to provide users with a seamless journey from identifying relevant patterns to using them in AAP. + +Patterns are delivered as files within an Ansible collection. This approach ensures consistent integration with the Ansible ecosystem by leveraging the existing collection framework. These patterns are bundled during collection packaging, enabling them to be searchable, downloadable, and publishable alongside roles, modules, and plugins. + +## Conventions + +This document follows the IETF [RFC2119](https://datatracker.ietf.org/doc/html/rfc2119) definitions of Key words for use in RFCs to Indicate Requirement Levels. + +## Support + +This pattern specification is supported and maintained by Ansible. To submit support or other requests for the specification, please contact Red Hat support. + +Individual patterns that implement this specification are the responsibility of the collection maintainers for the collections in which they reside. For support or feature requests for individual patterns, contact their collection owners. + +## Pattern Directory Structure + +- A pattern **MUST** be contained within a single directory in the `/extensions/patterns/` directory of an Ansible collection. +- An Ansible pattern directory name **MUST** be limited to 64 characters and **MUST** only include lowercase ASCII letters, digits, and underscores. +- An Ansible collection **MAY** contain zero or more patterns. + +## Required Files + +### `meta/pattern.json` + +The pattern definition meta file is the machine-readable entry point for creating an instance of the pattern in AAP. It defines the resources required to execute the pattern, such as a controller project, execution environment, job templates, and labels. The pattern definition also includes metadata about the pattern to enable its discovery and use, such as its title, audience, and tags. A [JSON schema](https://github.com/ansible/pattern-service/blob/main/specifications/pattern-schema/pattern-schema-1.0.0.json) has been published to aid with validation of the pattern definition file. + +- A pattern **MUST** include exactly one meta file defining the pattern metadata and AAP resources it requires. +- The pattern definition meta file **MUST** be a valid instance of the [Ansible pattern schema](https://github.com/ansible/pattern-service/blob/main/specifications/pattern-schema/pattern-schema-1.0.0.json). + +### `README.md` + +The README file is the human-readable documentation for the pattern. It provides information on what the pattern does, its inputs, its dependencies, and how it can be used. + +- A pattern **MUST** include a README file. +- The pattern README file **SHOULD** include all of the following: + - A description of what the pattern does + - A list of the AAP resources created by the pattern + - Documentation on how to use the pattern + +### `playbooks/` + +Pattern playbook files are included in the `/extensions/patterns//playbooks/` directory. + +- A pattern **MUST** contain a `playbooks/` directory. +- The `playbooks/` directory **MUST** contain at least one playbook associated with a job template definition in the pattern's `meta/pattern.json` file. +- A pattern **MAY** contain multiple playbooks. +- If a pattern contains multiple playbooks, it **MUST** define a primary playbook in its `meta/pattern.json` file. + +#### Playbook Requirements + +- All required and optional input variables to a pattern playbook **MUST** be defined in a play argument spec following this example of the [play argument spec format](https://github.com/ansible/ansible-creator/blob/main/src/ansible_creator/resources/playbook_project/argspec_validation_plays.meta.yml). +- If a pattern playbook requires any user-provided information other than variables to launch as a job template, such as inventory or credentials, those **MUST** be specified as `ask__at_launch` in the relevant `controller_job_templates` section of the pattern definition meta file. + +## Optional Files + +### `templates/` + +Templates for various types of catalog software in which patterns may be published. A template provides pattern data in the format required for a given catalog, such as Red Hat Developer Hub. + +- A pattern **MAY** contain a `templates/` directory to hold templates specific to catalogs that may publish the pattern, such as Red Hat Developer Hub or ServiceNow. +- The `templates/` directory **MAY** contain one or more catalog template files. + + +## Validation + +Pattern developers can use [ansible-lint](https://github.com/ansible/ansible-lint) to verify the structure of a pattern and its JSON against the pattern schema. + +## Example Pattern Directory + +```txt + +/extensions/patterns/ +├── network.backup/ # Backup pattern directory +│ ├── meta/pattern.json # Backup pattern definition +│ ├── README.md # Documentation for Backup pattern +│ ├── playbooks/ # Directory containing pattern playbooks +│ │ ├── backup.meta.yaml # Backup playbook arg spec +│ │ ├── backup.yaml # Backup playbook +│ ├── templates/ # Directory containing catalog templates +│ │ ├── rhdh.yaml # Display template for RHDH catalog + +``` + +## Other Pattern Requirements + +- A pattern **MUST** inherit the version number of the collection that contains it. +- A pattern **MUST** be valid according to the requirements specified in this document, including validation of each file contained in the pattern against its relevant schema. +- Changes to patterns **MUST** be noted in collection-level changelogs and release notes. +- All system, python, and Ansible collection dependencies needed to run a pattern's automations **MUST** be declared in the collection's dependency files, including but not limited to: `galaxy.yml`, `requirements.txt`, and `execution_environment.yml`. From fcc67bd25a5baf2863a3987c3599cbab09fded31 Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Wed, 17 Sep 2025 12:58:25 -0400 Subject: [PATCH 50/51] Validate new spec file with version match (should pass) --- .../pattern-specification/pattern-specification-2.0.0.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specifications/pattern-specification/pattern-specification-2.0.0.md b/specifications/pattern-specification/pattern-specification-2.0.0.md index e5327b9b..6bff73e5 100644 --- a/specifications/pattern-specification/pattern-specification-2.0.0.md +++ b/specifications/pattern-specification/pattern-specification-2.0.0.md @@ -1,7 +1,7 @@ --- title: Ansible Patterns Specification author: Ansible Cloud Content team -version: 1.0.0 +version: 2.0.0 --- # Ansible Pattern Specification From ab01027dcc2a5b02751d341aff7f82e6617b3cf7 Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Wed, 17 Sep 2025 13:04:59 -0400 Subject: [PATCH 51/51] Remove trailing white space --- .github/workflows/validate-pattern-specs.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/validate-pattern-specs.yml b/.github/workflows/validate-pattern-specs.yml index d7686224..9c66e404 100644 --- a/.github/workflows/validate-pattern-specs.yml +++ b/.github/workflows/validate-pattern-specs.yml @@ -32,7 +32,7 @@ jobs: echo "$NEW_SPEC_FILES" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT - # Modified specification files + # 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 @@ -107,7 +107,6 @@ jobs: # Extract version from filename VERSION=$(echo "$file" | sed -n 's/.*pattern-schema-\([^/]*\)\.json/\1/p') - if [ -n "$VERSION" ]; then # 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"