diff --git a/.github/actionlint.yaml b/.github/actionlint.yaml new file mode 100644 index 000000000..059a71c64 --- /dev/null +++ b/.github/actionlint.yaml @@ -0,0 +1,4 @@ +# Source validation only; this label does not attest runtime isolation or capacity. +self-hosted-runner: + labels: + - cwlab-ci-isolated diff --git a/.github/workflows/foundation-ci.yml b/.github/workflows/foundation-ci.yml index 6b475d6f2..1f55f8fbb 100644 --- a/.github/workflows/foundation-ci.yml +++ b/.github/workflows/foundation-ci.yml @@ -19,7 +19,9 @@ concurrency: jobs: quality: name: Repository quality - runs-on: ubuntu-24.04 + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] timeout-minutes: 60 env: ORGMETRA_POSTGRES_IMAGE: postgres:16.14@sha256:33f923b05f64ca54ac4401c01126a6b92afe839a0aa0a52bc5aeb5cc958e5f20 @@ -46,10 +48,18 @@ jobs: - name: Compile owned Python boundaries run: >- python -m compileall -q tests packages services - - name: Prove explicit GitHub-hosted runner image contract + - name: Prove proposed isolated self-hosted runner contract run: python -m unittest tests.test_github_actions_runner_image + - name: Install Node dependencies + run: npm ci - name: Validate foundation pack run: npm run validate + - name: Build Storybook + run: npm run build-storybook + - name: Install Chromium and Linux dependencies + run: npx playwright install --with-deps chromium + - name: Run HR workspace browser E2E + run: npm run test:e2e - name: Prove Foundation CI dependency hygiene run: bash tests/test_foundation_ci_dependency_hygiene.sh - name: Install reviewed test toolchain @@ -68,6 +78,16 @@ jobs: PYTHONPATH=packages/selection-review/src COVERAGE_FILE=/tmp/orgmetra-selection-review.coverage python -m pytest -c packages/selection-review/pyproject.toml packages/selection-review/tests PYTHONPATH=services/job-analysis-api/src:packages/hris-kernel/src:packages/keyverse-adapter/src COVERAGE_FILE=/tmp/orgmetra-job-analysis-api.coverage python -m pytest -c services/job-analysis-api/pyproject.toml services/job-analysis-api/tests PYTHONPATH=services/people-api/src:packages/hris-kernel/src:packages/keyverse-adapter/src COVERAGE_FILE=/tmp/orgmetra-people-api.coverage python -m pytest -c services/people-api/pyproject.toml services/people-api/tests + - name: Install PostgreSQL 16 host client + run: | + sudo apt-get -o DPkg::Lock::Timeout=120 update + sudo apt-get -o DPkg::Lock::Timeout=120 install --yes --no-install-recommends postgresql-client-16 + echo /usr/lib/postgresql/16/bin >> "$GITHUB_PATH" + /usr/lib/postgresql/16/bin/psql --version | grep -E '^psql \(PostgreSQL\) 16\.' + - name: Prove PostgreSQL 16 host client selection + run: | + test "$(command -v psql)" = /usr/lib/postgresql/16/bin/psql + psql --version | grep -E '^psql \(PostgreSQL\) 16\.' - name: Run PostgreSQL contracts in isolated containers env: PGPASSWORD: orgmetra diff --git a/.github/workflows/recovery-rehearsal-quality.yml b/.github/workflows/recovery-rehearsal-quality.yml index ea0f41ad0..8e542ebe5 100644 --- a/.github/workflows/recovery-rehearsal-quality.yml +++ b/.github/workflows/recovery-rehearsal-quality.yml @@ -33,7 +33,9 @@ concurrency: jobs: restore-rehearsal: name: PostgreSQL restore rehearsal - runs-on: ubuntu-24.04 + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] timeout-minutes: 15 services: source_postgres: @@ -43,7 +45,7 @@ jobs: POSTGRES_PASSWORD: orgmetra POSTGRES_DB: postgres ports: - - 5432:5432 + - 5432 options: >- --health-cmd "pg_isready -U orgmetra -d postgres" --health-interval 5s @@ -56,7 +58,7 @@ jobs: POSTGRES_PASSWORD: orgmetra POSTGRES_DB: postgres ports: - - 5433:5432 + - 5432 options: >- --health-cmd "pg_isready -U orgmetra -d postgres" --health-interval 5s @@ -72,6 +74,18 @@ jobs: env: EXPECTED_SHA: ${{ github.event.pull_request.head.sha || github.sha }} run: test "$(git rev-parse HEAD)" = "${EXPECTED_SHA}" + - name: Set up Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.14" + check-latest: false + - name: Set up Node.js LTS + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: "24" + check-latest: false + - name: Install Node dependencies + run: npm ci - name: Print diagnostic recovery provenance data run: | python - <<'PY' @@ -99,11 +113,21 @@ jobs: run: node --test tests/recovery-rehearsal.test.mjs - name: Validate repository contracts run: npm run validate + - name: Install PostgreSQL 16 host client + run: | + sudo apt-get -o DPkg::Lock::Timeout=120 update + sudo apt-get -o DPkg::Lock::Timeout=120 install --yes --no-install-recommends postgresql-client-16 + echo /usr/lib/postgresql/16/bin >> "$GITHUB_PATH" + /usr/lib/postgresql/16/bin/psql --version | grep -E '^psql \(PostgreSQL\) 16\.' + - name: Prove PostgreSQL 16 host client selection + run: | + test "$(command -v psql)" = /usr/lib/postgresql/16/bin/psql + psql --version | grep -E '^psql \(PostgreSQL\) 16\.' - name: Exercise real cross-cluster dump and restore env: RECOVERY_REHEARSAL_ALLOW_ROLE_DROP: "1" - POSTGRES_SOURCE_ADMIN_URL: postgresql://orgmetra:orgmetra@localhost:5432/postgres - POSTGRES_RESTORE_ADMIN_URL: postgresql://orgmetra:orgmetra@localhost:5433/postgres + POSTGRES_SOURCE_ADMIN_URL: postgresql://orgmetra:orgmetra@localhost:${{ job.services.source_postgres.ports[5432] }}/postgres + POSTGRES_RESTORE_ADMIN_URL: postgresql://orgmetra:orgmetra@localhost:${{ job.services.restore_postgres.ports[5432] }}/postgres POSTGRES_SOURCE_CONTAINER: ${{ job.services.source_postgres.id }} POSTGRES_RESTORE_CONTAINER: ${{ job.services.restore_postgres.id }} run: bash .github/scripts/restore-rehearsal-postgres.sh diff --git a/.gitignore b/.gitignore index 1f0b61ba5..e5d4913eb 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,9 @@ node_modules/ dist/ coverage/ .turbo/ +storybook-static/ +playwright-report/ +test-results/ # Rust /target/ @@ -35,3 +38,4 @@ secrets/ artifacts/ reports/ *.log +/.codegraph/ diff --git a/.storybook/main.js b/.storybook/main.js new file mode 100644 index 000000000..87fa64439 --- /dev/null +++ b/.storybook/main.js @@ -0,0 +1,8 @@ +/** @type { import('@storybook/web-components-vite').StorybookConfig } */ +const config = { + stories: ['../apps/hr-workspace/**/*.stories.@(js|mjs)'], + framework: '@storybook/web-components-vite', + docs: { autodocs: 'tag' } +}; + +export default config; diff --git a/.storybook/preview.js b/.storybook/preview.js new file mode 100644 index 000000000..058f02b9b --- /dev/null +++ b/.storybook/preview.js @@ -0,0 +1,13 @@ +import '../packages/design-tokens/tokens.css'; +import '../apps/hr-workspace/styles.css'; + +/** @type { import('storybook').Preview } */ +const preview = { + parameters: { + layout: 'centered', + controls: { expanded: true } + }, + decorators: [(story) => `