Skip to content
Open
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
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
* text=auto eol=lf

*.png binary
125 changes: 6 additions & 119 deletions .github/workflows/portfolio-audit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,125 +21,12 @@ jobs:
with:
php-version: '8.4'
coverage: none
tools: phpunit:11.5

- name: Check PHP example syntax
shell: bash
run: |
set -euo pipefail
while IFS= read -r file; do
php -l "$file"
done < <(find examples -name '*.php' -type f | sort)
- name: Validate Composer config
run: composer validate --strict --no-check-publish

- name: Run public PHPUnit examples
run: phpunit
- name: Install Composer dependencies
run: composer install --no-interaction --prefer-dist --no-progress

- name: Check local Markdown links
shell: python
run: |
from pathlib import Path
import re
import sys

failures = []
pattern = re.compile(r'!?\[[^\]]*\]\(([^)]+)\)')

for path in Path('.').rglob('*.md'):
text = path.read_text(encoding='utf-8')
for match in pattern.finditer(text):
target = match.group(1).strip()
if not target or target.startswith(('http://', 'https://', 'mailto:', '#')):
continue
clean = target.split('#', 1)[0]
if not clean:
continue
if not (path.parent / clean).exists():
failures.append(f'{path}: missing local link target {target}')

if failures:
print('\n'.join(failures))
sys.exit(1)

- name: Check public wording consistency
shell: python
run: |
from pathlib import Path
import sys

forbidden = [
'Kapitel als ' + 'Kategorien',
'Kategorien ' + 'exportieren',
'Kapitel werden ' + 'Kategorien',
'chapters become ' + 'categories',
'Lessons als Produkte, ' + 'Kapitel',
]
suffixes = {'.md', '.php', '.svg', '.txt', '.yml', '.yaml'}
failures = []

for path in Path('.').rglob('*'):
if '.git' in path.parts:
continue
if path.suffix.lower() not in suffixes:
continue
text = path.read_text(encoding='utf-8', errors='ignore')
lower = text.lower()
for phrase in forbidden:
if phrase.lower() in lower:
failures.append(f'{path}: outdated wording found')

if failures:
print('\n'.join(failures))
sys.exit(1)

- name: Check environment file policy
shell: python
run: |
from pathlib import Path
import sys

allowed = {'.env.example'}
forbidden = []

for path in Path('.').rglob('.env*'):
if '.git' in path.parts:
continue
if path.name not in allowed:
forbidden.append(str(path))

if forbidden:
print('Real environment files must not be committed:')
print('\n'.join(forbidden))
sys.exit(1)

- name: Check for obvious secret patterns
shell: python
run: |
from pathlib import Path
import re
import sys

patterns = [
re.compile(r'github_pat_[A-Za-z0-9_]+'),
re.compile(r'ghp_[A-Za-z0-9_]+'),
re.compile(r'sk-[A-Za-z0-9]{20,}'),
re.compile(r'APP_SECRET\s*='),
re.compile(r'DATABASE_URL\s*='),
]
suffixes = {'.md', '.php', '.svg', '.txt', '.yml', '.yaml'}
failures = []

for path in Path('.').rglob('*'):
if '.git' in path.parts or '.github' in path.parts:
continue
if path.name == '.env.example':
continue
if path.suffix.lower() not in suffixes:
continue
text = path.read_text(encoding='utf-8', errors='ignore')
for pattern in patterns:
if pattern.search(text):
failures.append(f'{path}: possible secret pattern found')

if failures:
print('\n'.join(failures))
sys.exit(1)
- name: Run public showcase verification
run: composer verify
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
/vendor/
/.phpunit.cache/
/.phpstan.cache/
/__pycache__/
**/__pycache__/
/.env
/.env.*
!/.env.example
!/screenshots/.gitkeep
/config/secrets/
/private-config/
/public/uploads/
/var/
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Changelog

## 2026-06-15

- Added Composer-based reproducible verification with `composer verify` and a separate `composer smoke:public` live check.
- Added ADRs for the public/private repository split, Composer verification, and the Shopware synchronization boundary.
- Added a security and privacy boundary document for public showcase review.
- Added PHPStan `level: max` for public PHP examples and wired it into the Portfolio Audit workflow.
- Added a Senior Symfony review guide that explains architecture boundaries, trade-offs, operational evidence, and reviewer prompts.
- Added PHPUnit coverage for the public next-due-question example.
- Added a repeatable public showcase safety audit for env-file policy, obvious secret patterns, private-code path leakage, local links, screenshots, wording consistency, and PHPUnit XML parsing.
- Added public live readiness evidence for the live demo's public routes and security headers.
- Extended the Portfolio Audit workflow to run the showcase safety audit and lint PHP files in both `examples/` and `tests/`.

## 2026-05-26

- Added interview demo script for SkillBuilder and the Shopware bridge.
Expand Down
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@ Only published SkillBuilder lessons are synchronized as Shopware products. Draft
- [Portfolio audit report](docs/audit-report-2026-05-23.md)
- [PHPStan audit evidence](docs/phpstan-audit-2026-06-05.md)
- [Evidence index](docs/evidence/README.md)
- [Senior Symfony review guide](docs/senior-symfony-review.md)
- [Security and privacy boundary](docs/security-privacy.md)
- [Architecture decisions](docs/adr/)
- [Demo script](docs/demo-script.md)
- [Changelog](CHANGELOG.md)
- [Demo checklist](docs/demo-checklist.md)
Expand Down Expand Up @@ -186,14 +189,16 @@ This repository does not include:
Public showcase checks:

```bash
phpunit
composer install
composer verify
composer smoke:public
```

Current public example test result:

```text
4 tests
7 assertions
7 tests
10 assertions
OK
```

Expand All @@ -210,6 +215,8 @@ OK

Additional checks performed:

- public showcase safety audit for env-file policy, obvious secret patterns, private-code path leakage, local links, screenshots, and wording consistency
- PHPStan `level: max` for public PHP examples
- PHP syntax check across 180 files
- Twig lint across 38 templates
- YAML lint across 18 config files
Expand All @@ -219,6 +226,7 @@ Additional checks performed:
- live smoke checks for public and protected routes
- PHPStan audit setup added in the private codebase; baseline reduced from 56 findings to 17 Doctrine generated-ID findings
- Evidence notes added for the PHPStan baseline reduction and private codebase verification limits
- Public live readiness evidence added for 2026-06-15

## Portfolio Positioning

Expand Down
26 changes: 26 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "roadynet/skillbuilder-showcase",
"description": "Public portfolio evidence for the private SkillBuilder Symfony learning platform.",
"type": "project",
"license": "proprietary",
"require": {
"php": ">=8.4"
},
"require-dev": {
"phpstan/phpstan": "^2.2",
"phpunit/phpunit": "^11.5"
},
"scripts": {
"lint:php": "python tests/php_lint.py",
"analyse": "phpstan analyse --configuration=phpstan.neon --no-progress",
"test": "phpunit",
"audit:showcase": "python tests/showcase_audit.py",
"smoke:public": "python tests/public_smoke.py",
"verify": [
"@lint:php",
"@analyse",
"@test",
"@audit:showcase"
]
}
}
Loading
Loading