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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 43 additions & 46 deletions .github/scripts/check_duplicates.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import csv
import os
import yaml
from pathlib import Path
Expand Down Expand Up @@ -101,51 +100,49 @@ def compare_components(prof1, prof2):

return True

with open(str(Path.home()) + '/files.csv', 'r') as csvfile:
csvreader = csv.reader(csvfile)
changed_files = next(csvreader)

for file in changed_files:
file_basename = os.path.basename(file)
file_directory = os.path.dirname(file)

if '/profiles/' in file:
print('\nNEW PROFILE:\n%s is a profile! Comparing to other profiles...' % file)

os.chdir(file_directory)
new_profile = file_basename

# Skip deleted files and track them for warning
if not os.path.exists(new_profile):
print("Skipping %s - file was deleted" % new_profile)
deleted_profiles.append(file)
os.chdir(cwd)
continue

for current_profile in os.listdir("./"):
# compare to YAML files that are not the same file
# Compare only .yml files and only files that have not already been found to be a duplicate
if current_profile != new_profile and Path(current_profile).suffix == ".yml" and (current_profile, new_profile) not in duplicate_pairs:
print("Comparing %s vs %s" % (new_profile, current_profile))
with open(new_profile) as new_data, open(current_profile) as current_data:
new_profile_map = yaml.safe_load(new_data)
current_profile_map = yaml.safe_load(current_data)

''' Compare profiles. A duplicate is defined as follows:
- categories must be the same
- capabilities must be the same, with some ordering restrictions
- top capability must match, but subsequent ordering does not matter
- embedded configs must be the same, but certain values can be ordered differently (i.e. enabledValues)
- preferences must be the same
'''
if(compare_preferences(new_profile_map, current_profile_map) == True and
compare_metadata(new_profile_map, current_profile_map) == True and
compare_components(new_profile_map, current_profile_map) == True):
print("%s and %s are duplicates!\n" % (new_profile, current_profile))
duplicate_pairs.append((new_profile, current_profile))

# return to original directory
os.chdir(cwd)
changed_files = os.environ.get('ALL_CHANGED_FILES', '').split()

for file in changed_files:
file_basename = os.path.basename(file)
file_directory = os.path.dirname(file)

if '/profiles/' in file:
print('\nNEW PROFILE:\n%s is a profile! Comparing to other profiles...' % file)

os.chdir(file_directory)
new_profile = file_basename

# Skip deleted files and track them for warning
if not os.path.exists(new_profile):
print("Skipping %s - file was deleted" % new_profile)
deleted_profiles.append(file)
os.chdir(cwd)
continue

for current_profile in os.listdir("./"):
# compare to YAML files that are not the same file
# Compare only .yml files and only files that have not already been found to be a duplicate
if current_profile != new_profile and Path(current_profile).suffix == ".yml" and (current_profile, new_profile) not in duplicate_pairs:
print("Comparing %s vs %s" % (new_profile, current_profile))
with open(new_profile) as new_data, open(current_profile) as current_data:
new_profile_map = yaml.safe_load(new_data)
current_profile_map = yaml.safe_load(current_data)

''' Compare profiles. A duplicate is defined as follows:
- categories must be the same
- capabilities must be the same, with some ordering restrictions
- top capability must match, but subsequent ordering does not matter
- embedded configs must be the same, but certain values can be ordered differently (i.e. enabledValues)
- preferences must be the same
'''
if(compare_preferences(new_profile_map, current_profile_map) == True and
compare_metadata(new_profile_map, current_profile_map) == True and
compare_components(new_profile_map, current_profile_map) == True):
print("%s and %s are duplicates!\n" % (new_profile, current_profile))
duplicate_pairs.append((new_profile, current_profile))

# return to original directory
os.chdir(cwd)

with open("profile-comment-body.md", "w") as f:
if duplicate_pairs:
Expand Down
82 changes: 82 additions & 0 deletions .github/scripts/check_profile_categories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import os
import sys
import yaml
from pathlib import Path

cwd = os.getcwd()
missing_category_profiles = []
deleted_profiles = []

changed_files = os.environ.get('ALL_CHANGED_FILES', '').split()

for file in changed_files:
file_basename = os.path.basename(file)
file_directory = os.path.dirname(file)

if '/profiles/' in file and file.endswith('.yml'):
print('\nCHECKING PROFILE:\n%s' % file)

os.chdir(file_directory)

if not os.path.exists(file_basename):
print("Skipping %s - file was deleted" % file_basename)
deleted_profiles.append(file)
os.chdir(cwd)
continue

with open(file_basename) as fp:
try:
profile = yaml.safe_load(fp)
except yaml.YAMLError as e:
print("Error parsing %s: %s" % (file_basename, e))
os.chdir(cwd)
continue

if not profile or 'components' not in profile:
print("Skipping %s - no components found" % file_basename)
os.chdir(cwd)
continue

# Find the main component and verify it has a categories field
main_component = next(
(c for c in profile['components'] if c.get('id') == 'main'),
None
)

if main_component is None:
print("Warning: %s has no 'main' component" % file_basename)
os.chdir(cwd)
continue

if not main_component.get('categories'):
print("MISSING CATEGORY: %s" % file)
missing_category_profiles.append(file)
else:
print("OK: %s has categories: %s" % (
file_basename,
[c['name'] for c in main_component['categories']]
))

os.chdir(cwd)

with open("profile-categories-comment-body.md", "w") as f:
if missing_category_profiles:
f.write("Profile category check: :x: **Missing categories detected.**\n\n")
f.write("The following profiles are missing a `categories` field on the `main` component:\n\n")
for profile in missing_category_profiles:
f.write("- `%s`\n" % profile)
f.write("\nPlease add a `categories` entry to the `main` component. Example:\n")
f.write("```yaml\ncomponents:\n - id: main\n categories:\n - name: Switch\n capabilities:\n ...\n```\n")
else:
f.write("Profile category check: :white_check_mark: Passed - all profiles have a category defined.\n")

if deleted_profiles:
f.write("\n:warning: **Deleted profile files detected:**\n")
for deleted in deleted_profiles:
f.write("- `%s`\n" % deleted)

with open("profile-categories-comment-body.md", "r") as f:
print("\n" + f.read())

if missing_category_profiles:
sys.exit(1)
34 changes: 34 additions & 0 deletions .github/workflows/check-profile-categories-comment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Post profile categories comment
on:
workflow_run:
workflows: [Check profile categories]
types:
- completed

jobs:
comment-on-pr:
runs-on: ubuntu-latest
steps:
- name: Download comment artifact
uses: dawidd6/action-download-artifact@v6
with:
workflow: check-profile-categories.yml
run_id: ${{ github.event.workflow_run.id }}

- run: echo "pr_number=$(cat pr_number/pr_number.txt)" >> $GITHUB_ENV

- name: Find comment
uses: peter-evans/find-comment@v2
id: fc
with:
body-includes: Profile category check
comment-author: 'github-actions[bot]'
issue-number: ${{ env.pr_number }}

- name: Post comment
uses: peter-evans/create-or-update-comment@v2
with:
comment-id: ${{ steps.fc.outputs.comment-id }}
body-file: 'profile_categories_comment/profile-categories-comment-body.md'
edit-mode: replace
issue-number: ${{ env.pr_number }}
41 changes: 41 additions & 0 deletions .github/workflows/check-profile-categories.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Check profile categories
on:
pull_request:
types: [opened, synchronize]
paths:
- 'drivers/**/profiles/*.yml'

jobs:
check-profile-categories:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3

- name: Gather file changes
id: changed-files
uses: tj-actions/changed-files@v47

- name: Run python script
env:
ALL_CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
run: |
python ./.github/scripts/check_profile_categories.py

- name: Upload profile categories comment artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: profile_categories_comment
path: |
profile-categories-comment-body.md

- run: echo ${{ github.event.number }} > pr_number.txt

- name: Upload PR info
if: always()
uses: actions/upload-artifact@v4
with:
name: pr_number
path: |
pr_number.txt
17 changes: 5 additions & 12 deletions .github/workflows/duplicate-profiles.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,13 @@ jobs:
- name: Checkout
uses: actions/checkout@v3

# Creates file "$/files.csv", among others
- id: file_changes
name: Gather file changes
uses: trilom/file-changes-action@1.2.4
with:
output: ','
fileOutput: ','

# For verification
- name: Show files changed
run: |
cat $HOME/files.csv
- name: Gather file changes
id: changed-files
uses: tj-actions/changed-files@v47

- name: Run python script
env:
ALL_CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably worth a check that this still detects duplicate profiles

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like it is still working as it did before:

Duplicate profile check: Warning - duplicate profiles detected.
2-button-battery-copy.yml == 2-button-battery.yml

run: |
python ./.github/scripts/check_duplicates.py

Expand Down