diff --git a/.github/workflows/nuget-reference-check.yml b/.github/workflows/nuget-reference-check.yml
index 224ce2df..6bbc4755 100644
--- a/.github/workflows/nuget-reference-check.yml
+++ b/.github/workflows/nuget-reference-check.yml
@@ -6,133 +6,183 @@ on:
schedule:
- cron: '0 8 * * *'
+permissions:
+ contents: read
+ issues: write
+
jobs:
- build:
+ nuget-package-check:
runs-on: ubuntu-latest
+
steps:
- - name: Checkout repository
- uses: actions/checkout@v6.0.2
- with:
- # We must fetch at least the immediate parents so that if this is
- # a pull request then we can checkout the head.
- fetch-depth: 2
-
- - name: Setup .NET Environment
- uses: actions/setup-dotnet@v5.1.0
- with:
- dotnet-version: '10.0.x'
-
- - name: Check for outdated packages
- id: outdated
- run: |
- set -e
-
- # Packages to ignore (SDK/runtime-managed)
- IGNORE_PACKAGES="Microsoft\.NETCore\.Platforms|Microsoft\.NETCore\.Targets"
-
- dotnet list SysML2.NET.sln package --outdated --include-transitive > outdated-raw.log
-
- # Filter out ignored core packages
- grep -v -E "$IGNORE_PACKAGES" outdated-raw.log > outdated.log || true
-
- # Print full outdated report (including test projects) to action log
- echo "=== Full outdated packages report ==="
- cat outdated.log
-
- # Build issue log: exclude test project sections
- # dotnet list output groups packages under project headers like:
- # Project `ProjectName` has the following updates available:
- # We remove sections for *.Tests projects
- awk '
- /^Project .*.Tests/ { skip=1; next }
- /^Project / { skip=0 }
- !skip { print }
- ' outdated.log > outdated-issue.log
-
- # Check if non-test outdated packages exist (look for > lines indicating actual packages)
- if grep -q ">" outdated-issue.log; then
- echo "Outdated packages found (non-test)"
- echo "outdated=true" >> $GITHUB_OUTPUT
- else
- echo "No outdated packages found in non-test projects"
- echo "outdated=false" >> $GITHUB_OUTPUT
- fi
-
- - name: Check for deprecated packages
- id: deprecated
- run: |
- set -e
- dotnet list SysML2.NET.sln package --deprecated --include-transitive > deprecated.log
- if [ -s deprecated.log ]; then
- echo "Deprecated packages found"
- echo "deprecated=true" >> $GITHUB_OUTPUT
- else
- echo "No deprecated packages found"
- echo "deprecated=false" >> $GITHUB_OUTPUT
- fi
-
- - name: Check for vulnerable packages
- id: vulnerable
- run: |
- set -e
- dotnet list SysML2.NET.sln package --vulnerable --include-transitive > vulnerabilities.log
- if grep -q -i "\bcritical\b\|\bhigh\b\|\bmoderate\b\|\blow\b" vulnerabilities.log; then
- echo "Security Vulnerabilities found"
- echo "vulnerable=true" >> $GITHUB_OUTPUT
- else
- echo "No Security Vulnerabilities found"
- echo "vulnerable=false" >> $GITHUB_OUTPUT
- fi
-
- - name: Create GitHub Issue if issues found
- if: steps.outdated.outputs.outdated == 'true' || steps.deprecated.outputs.deprecated == 'true' || steps.vulnerable.outputs.vulnerable == 'true'
- uses: actions/github-script@v6
- with:
- github-token: ${{ secrets.GITHUB_TOKEN }}
- script: |
- const fs = require('fs');
-
- let issueBody = `### NuGet Package Issues Detected in [SysML2.NET](${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY})\n\n`;
-
- if ('${{ steps.outdated.outputs.outdated }}' === 'true') {
- const outdatedLog = fs.readFileSync('outdated-issue.log', 'utf8');
- issueBody += `#### Outdated Packages\n\`\`\`\n${outdatedLog}\n\`\`\`\n\n`;
- }
-
- if ('${{ steps.deprecated.outputs.deprecated }}' === 'true') {
- const deprecatedLog = fs.readFileSync('deprecated.log', 'utf8');
- issueBody += `#### Deprecated Packages\n\`\`\`\n${deprecatedLog}\n\`\`\`\n\n`;
- }
-
- if ('${{ steps.vulnerable.outputs.vulnerable }}' === 'true') {
- const vulnerabilitiesLog = fs.readFileSync('vulnerabilities.log', 'utf8');
- issueBody += `#### Vulnerable Packages\n\`\`\`\n${vulnerabilitiesLog}\n\`\`\`\n\n`;
- }
-
- issueBody += '**Action Required:** Please review and update the affected packages.';
-
- const issueTitle = 'NuGet Package Issues Detected';
- const { data: issues } = await github.rest.issues.listForRepo({
- owner: context.repo.owner,
- repo: context.repo.repo,
- state: 'open',
- });
-
- const existingIssue = issues.find(issue => issue.title === issueTitle);
-
- if (existingIssue) {
- await github.rest.issues.createComment({
- owner: context.repo.owner,
- repo: context.repo.repo,
- issue_number: existingIssue.number,
- body: `New check results:\n${issueBody}`,
- });
- } else {
- await github.rest.issues.create({
+ - name: Checkout repository
+ uses: actions/checkout@v6.0.2
+ with:
+ fetch-depth: 2
+
+ - name: Setup .NET Environment
+ uses: actions/setup-dotnet@v5.1.0
+ with:
+ dotnet-version: '10.0.x'
+
+ - name: Check for outdated packages
+ id: outdated
+ run: |
+ set -e
+
+ IGNORE_PACKAGES="Microsoft\.NETCore\.Platforms|Microsoft\.NETCore\.Targets"
+
+ # Full report for the workflow logs only, including transitive packages
+ dotnet list SysML2.NET.sln package --outdated --include-transitive > outdated-full-raw.log
+ grep -v -E "$IGNORE_PACKAGES" outdated-full-raw.log > outdated-full.log || true
+
+ echo "=== Full outdated packages report including transitive packages ==="
+ cat outdated-full.log
+
+ # Direct/top-level outdated packages only, used for GitHub issue creation
+ dotnet list SysML2.NET.sln package --outdated > outdated-raw.log
+ grep -v -E "$IGNORE_PACKAGES" outdated-raw.log > outdated.log || true
+
+ echo "=== Direct outdated packages report used for issue detection ==="
+ cat outdated.log
+
+ awk '
+ /^Project .*.Tests/ { skip=1; next }
+ /^Project / { skip=0 }
+ !skip { print }
+ ' outdated.log > outdated-issue.log
+
+ if grep -q "^ *>" outdated-issue.log; then
+ echo "Outdated direct packages found in non-test projects"
+ echo "outdated=true" >> $GITHUB_OUTPUT
+ else
+ echo "No outdated direct packages found in non-test projects"
+ echo "outdated=false" >> $GITHUB_OUTPUT
+ fi
+
+ - name: Check for deprecated packages
+ id: deprecated
+ run: |
+ set -e
+
+ dotnet list SysML2.NET.sln package --deprecated --include-transitive > deprecated.log
+
+ echo "=== Full deprecated packages report ==="
+ cat deprecated.log
+
+ awk '
+ /^Project .*.Tests/ { skip=1; next }
+ /^Project / { skip=0 }
+ !skip { print }
+ ' deprecated.log > deprecated-issue.log
+
+ if grep -q "^ *>" deprecated-issue.log; then
+ echo "Deprecated packages found in non-test projects"
+ echo "deprecated=true" >> $GITHUB_OUTPUT
+ else
+ echo "No deprecated packages found in non-test projects"
+ echo "deprecated=false" >> $GITHUB_OUTPUT
+ fi
+
+ - name: Check for vulnerable packages
+ id: vulnerable
+ run: |
+ set -e
+
+ dotnet list SysML2.NET.sln package --vulnerable --include-transitive > vulnerabilities.log
+
+ echo "=== Full vulnerable packages report ==="
+ cat vulnerabilities.log
+
+ awk '
+ /^Project .*.Tests/ { skip=1; next }
+ /^Project / { skip=0 }
+ !skip { print }
+ ' vulnerabilities.log > vulnerabilities-issue.log
+
+ if grep -q "^ *>" vulnerabilities-issue.log; then
+ echo "Security vulnerabilities found in non-test projects"
+ echo "vulnerable=true" >> $GITHUB_OUTPUT
+ else
+ echo "No security vulnerabilities found in non-test projects"
+ echo "vulnerable=false" >> $GITHUB_OUTPUT
+ fi
+
+ - name: Synchronize NuGet package issue
+ if: github.event_name != 'pull_request'
+ uses: actions/github-script@v8
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const fs = require('fs');
+
+ const issueTitle = 'NuGet Package Issues Detected';
+
+ const hasOutdated = '${{ steps.outdated.outputs.outdated }}' === 'true';
+ const hasDeprecated = '${{ steps.deprecated.outputs.deprecated }}' === 'true';
+ const hasVulnerable = '${{ steps.vulnerable.outputs.vulnerable }}' === 'true';
+
+ const hasIssues = hasOutdated || hasDeprecated || hasVulnerable;
+
+ const { data: issues } = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
- title: issueTitle,
- body: issueBody,
- labels: ['dependencies', 'maintenance'],
+ state: 'open',
});
- }
+
+ const existingIssue = issues.find(issue => issue.title === issueTitle);
+
+ if (!hasIssues) {
+ if (existingIssue) {
+ await github.rest.issues.createComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: existingIssue.number,
+ body: 'NuGet package check is clean again. Closing this issue automatically.',
+ });
+
+ await github.rest.issues.update({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: existingIssue.number,
+ state: 'closed',
+ });
+ }
+
+ return;
+ }
+
+ let issueBody = `### NuGet Package Issues Detected in [SysML2.NET](${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY})\n\n`;
+
+ if (hasOutdated) {
+ const outdatedLog = fs.readFileSync('outdated-issue.log', 'utf8');
+ issueBody += `#### Outdated Packages\n\`\`\`\n${outdatedLog}\n\`\`\`\n\n`;
+ }
+
+ if (hasDeprecated) {
+ const deprecatedLog = fs.readFileSync('deprecated-issue.log', 'utf8');
+ issueBody += `#### Deprecated Packages\n\`\`\`\n${deprecatedLog}\n\`\`\`\n\n`;
+ }
+
+ if (hasVulnerable) {
+ const vulnerabilitiesLog = fs.readFileSync('vulnerabilities-issue.log', 'utf8');
+ issueBody += `#### Vulnerable Packages\n\`\`\`\n${vulnerabilitiesLog}\n\`\`\`\n\n`;
+ }
+
+ issueBody += '**Action Required:** Please review and update the affected packages.';
+
+ if (existingIssue) {
+ await github.rest.issues.createComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: existingIssue.number,
+ body: `New check results:\n\n${issueBody}`,
+ });
+ } else {
+ await github.rest.issues.create({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ title: issueTitle,
+ body: issueBody,
+ });
+ }
\ No newline at end of file
diff --git a/SysML2.NET.Viewer/SysML2.NET.Viewer.csproj b/SysML2.NET.Viewer/SysML2.NET.Viewer.csproj
index c8442f5d..34596224 100644
--- a/SysML2.NET.Viewer/SysML2.NET.Viewer.csproj
+++ b/SysML2.NET.Viewer/SysML2.NET.Viewer.csproj
@@ -23,7 +23,7 @@
-
+