Add PHP Analyzer Plugin and Composer Lock Data Handling
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled

- Implemented the PhpAnalyzerPlugin to analyze PHP projects.
- Created ComposerLockData class to represent data from composer.lock files.
- Developed ComposerLockReader to load and parse composer.lock files asynchronously.
- Introduced ComposerPackage class to encapsulate package details.
- Added PhpPackage class to represent PHP packages with metadata and evidence.
- Implemented PhpPackageCollector to gather packages from ComposerLockData.
- Created PhpLanguageAnalyzer to perform analysis and emit results.
- Added capability signals for known PHP frameworks and CMS.
- Developed unit tests for the PHP language analyzer and its components.
- Included sample composer.lock and expected output for testing.
- Updated project files for the new PHP analyzer library and tests.
This commit is contained in:
StellaOps Bot
2025-11-22 14:02:49 +02:00
parent a7f3c7869a
commit b6b9ffc050
158 changed files with 16272 additions and 809 deletions

View File

@@ -0,0 +1,5 @@
node_modules
.dist
output
.cache
.DS_Store

View File

@@ -0,0 +1,12 @@
# DevPortal Tasks · Sprint 0206.0001.0001
Keep this file in sync with `docs/implplan/SPRINT_0206_0001_0001_devportal.md`.
| Task ID | Status | Notes | Last Updated (UTC) |
| --- | --- | --- | --- |
| DEVPORT-62-001 | DOING | Select SSG, wire aggregate spec, nav/search scaffold. | 2025-11-22 |
| DEVPORT-62-002 | TODO | Schema viewer, examples, copy-curl, version selector. | 2025-11-22 |
| DEVPORT-63-001 | TODO | Try-It console against sandbox; token onboarding UX. | 2025-11-22 |
| DEVPORT-63-002 | TODO | Embed SDK snippets/quick starts from tested examples. | 2025-11-22 |
| DEVPORT-64-001 | TODO | Offline bundle target with specs + SDK archives; zero external assets. | 2025-11-22 |
| DEVPORT-64-002 | TODO | Accessibility tests, link checker, performance budgets. | 2025-11-22 |

View File

@@ -0,0 +1,69 @@
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
import starlight from '@astrojs/starlight';
export default defineConfig({
site: 'https://devportal.stellaops.local',
srcDir: 'src',
outDir: 'dist',
trailingSlash: 'never',
integrations: [
mdx(),
starlight({
title: 'StellaOps DevPortal',
description: 'Deterministic, offline-first developer portal for the StellaOps platform.',
favicon: {
src: '/logo.svg',
sizes: 'any',
type: 'image/svg+xml',
},
logo: {
src: '/logo.svg',
alt: 'StellaOps DevPortal',
},
customCss: ['./src/styles/custom.css'],
social: {
github: 'https://git.stella-ops.org',
},
search: {
provider: 'local',
algolia: undefined,
},
sidebar: [
{
label: 'Overview',
items: [
{ slug: 'index' },
{ slug: 'guides/getting-started' },
{ slug: 'guides/navigation-search' },
],
},
{
label: 'API',
items: [{ slug: 'api-reference' }],
},
{
label: 'Roadmap',
items: [{ slug: 'release-notes' }],
},
],
tableOfContents: {
minHeadingLevel: 2,
maxHeadingLevel: 4,
},
pagination: true,
editLink: {
baseUrl: 'https://git.stella-ops.org/devportal',
},
head: [
{
tag: 'meta',
attrs: {
name: 'theme-color',
content: '#0f172a',
},
},
],
}),
],
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,29 @@
{
"name": "@stellaops/devportal-site",
"version": "0.1.0",
"private": true,
"type": "module",
"license": "AGPL-3.0-or-later",
"engines": {
"node": ">=18.18.0"
},
"scripts": {
"dev": "astro dev",
"start": "astro dev --host",
"build": "astro build",
"preview": "astro preview",
"check": "astro check",
"sync:spec": "node scripts/sync-spec.mjs",
"prepare:static": "npm run sync:spec && astro check"
},
"dependencies": {
"rapidoc": "9.3.8"
},
"devDependencies": {
"@astrojs/mdx": "4.3.12",
"@astrojs/starlight": "0.36.2",
"@types/node": "24.10.1",
"astro": "5.16.0",
"typescript": "5.9.3"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" role="img" aria-labelledby="title desc">
<title id="title">StellaOps DevPortal</title>
<desc id="desc">Stylised starburst mark for the StellaOps developer portal.</desc>
<defs>
<linearGradient id="g" x1="0%" x2="100%" y1="0%" y2="100%">
<stop offset="0%" stop-color="#0ea5e9" />
<stop offset="100%" stop-color="#22d3ee" />
</linearGradient>
</defs>
<rect width="200" height="200" rx="28" fill="#0b1220" />
<path fill="url(#g)" d="M100 22l16 46h48l-39 28 15 46-40-27-40 27 15-46-39-28h48z"/>
<circle cx="100" cy="100" r="16" fill="#0b1220" stroke="#22d3ee" stroke-width="6" />
</svg>

After

Width:  |  Height:  |  Size: 679 B

View File

@@ -0,0 +1,32 @@
#!/usr/bin/env node
import fs from 'node:fs';
import path from 'node:path';
import crypto from 'node:crypto';
import { fileURLToPath } from 'node:url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const moduleRoot = path.resolve(__dirname, '..');
const repoRoot = path.resolve(moduleRoot, '..', '..', '..');
const sourceSpec = path.join(repoRoot, 'src/Api/StellaOps.Api.OpenApi/stella.yaml');
const targetDir = path.join(moduleRoot, 'public', 'api');
const targetSpec = path.join(targetDir, 'stella.yaml');
function hashFile(filePath) {
const hash = crypto.createHash('sha256');
hash.update(fs.readFileSync(filePath));
return hash.digest('hex');
}
if (!fs.existsSync(sourceSpec)) {
console.error(`[devportal:sync-spec] missing source spec at ${sourceSpec}`);
process.exitCode = 1;
process.exit();
}
fs.mkdirSync(targetDir, { recursive: true });
fs.copyFileSync(sourceSpec, targetSpec);
const sizeKb = (fs.statSync(targetSpec).size / 1024).toFixed(1);
const digest = hashFile(targetSpec).slice(0, 12);
console.log(`[devportal:sync-spec] copied aggregate spec -> public/api/stella.yaml (${sizeKb} KiB, sha256:${digest}...)`);

View File

@@ -0,0 +1,17 @@
import { defineCollection, z } from 'astro:content';
const docs = defineCollection({
type: 'content',
schema: z.object({
title: z.string(),
description: z.string().optional(),
sidebar: z
.object({
label: z.string().optional(),
})
.optional(),
order: z.number().optional(),
}),
});
export const collections = { docs };

View File

@@ -0,0 +1,37 @@
---
title: API Reference
description: Aggregate OpenAPI surface for StellaOps services with schema-first navigation.
---
import 'rapidoc/dist/rapidoc-min.js';
> The aggregate spec is composed from per-service OpenAPI files and namespaced by service (e.g., `/authority/...`). The bundled copy lives at `/api/stella.yaml` so offline builds stay self-contained.
<rapi-doc
spec-url="/api/stella.yaml"
render-style="read"
theme="dark"
bg-color="#0b1220"
text-color="#e5e7eb"
primary-color="#0ea5e9"
nav-bg-color="#0f172a"
nav-text-color="#cbd5e1"
show-header="false"
allow-try="false"
allow-spec-url-load="false"
allow-spec-file-load="false"
regular-font="Space Grotesk"
mono-font="JetBrains Mono"
schema-style="tree"
default-schema-tab="schema"
sort-tags="true"
sort-endpoints-by="path"
hide-schema-titles="false"
layout="row"
style="height: 80vh; border: 1px solid #1f2937; border-radius: 12px;"
></rapi-doc>
## What to look for
- Per-operation `x-service` and `x-original-path` values expose provenance.
- Shared schemas live under `#/components/schemas` with namespaced keys.
- Servers list includes one entry per service; sandbox URLs will be added alongside prod.

View File

@@ -0,0 +1,38 @@
---
title: Getting Started
description: Build and preview the DevPortal locally with deterministic inputs.
---
## Prerequisites
- Node.js 18.18 or later (offline-friendly install).
- `npm install --package-lock-only` to capture the lockfile; `npm ci --progress=false` when you need a full install.
- Aggregate OpenAPI file at `src/Api/StellaOps.Api.OpenApi/stella.yaml` (generated via `npm run api:compose` from the repo root).
## Build locally
1. Sync the aggregate spec into the portal assets:
```bash
npm run sync:spec
```
2. Install dependencies (skips network analytics):
```bash
npm ci --ignore-scripts --progress=false --no-fund --no-audit
```
3. Run the site locally:
```bash
npm run dev -- --host
```
4. Generate a production bundle (offline-ready):
```bash
npm run build
```
## Determinism & offline posture
- The portal never pulls fonts or JS from CDNs; all assets live under `public/`.
- The aggregate spec is stored at `/api/stella.yaml` and is bundled into exports.
- Search uses a local index generated at build time—no third-party calls.
## Where things live
- Content: `src/content/docs/**`
- Styling tokens: `src/styles/custom.css`
- Spec sync helper: `scripts/sync-spec.mjs`
- Build output: `dist/` (ready for static serving or offline export)

View File

@@ -0,0 +1,24 @@
---
title: Navigation & Search
description: How the DevPortal organizes content and builds offline search indices.
---
## Navigation model
- **Overview** for narrative journeys and onboarding.
- **API** for the aggregate OpenAPI viewer and schema-aware tools.
- **Roadmap** for release notes and drop-specific changes.
- Sidebar order is pinned in `astro.config.mjs` to keep builds deterministic.
## Search
- Provider: **local** (FlexSearch) generated at build time.
- Works offline; indexes titles, headings, and descriptions across docs.
- Search box appears in the top nav. Keyboard shortcut: `/` (press in any page).
## Content guidelines
- Every page must declare `title` and `description` frontmatter to land in the index.
- Prefer short headings (≤60 characters) for clean search snippets.
- Keep code examples deterministic: pin versions and avoid network calls.
## Upcoming
- API operation deep-links will join the index once schema viewer (DEVPORT-62-002) lands.
- Try-It console (DEVPORT-63-001) will expose a sandbox surface gated by scopes.

View File

@@ -0,0 +1,30 @@
---
title: Welcome to the StellaOps DevPortal
description: Deterministic, offline-first documentation and API reference for the StellaOps platform.
---
import { Card, CardGrid } from '@astrojs/starlight/components';
The StellaOps DevPortal binds specs, runnable examples, and SDK entrypoints into a single, deterministic build. Everything here is designed to work online or fully air-gapped so auditors and engineers see the same evidence.
<CardGrid>
<Card title="Aggregate API" icon="tabler:api" href="/docs/api-reference/">
Browse the composed OpenAPI surface, schema-first paths, and auth expectations.
</Card>
<Card title="Get started" icon="tabler:flag" href="/docs/guides/getting-started/">
Install tooling, sync the aggregate spec, and render the portal locally.
</Card>
<Card title="Navigation & search" icon="tabler:search" href="/docs/guides/navigation-search/">
Learn how content is organized and how offline search works.
</Card>
</CardGrid>
## Why now
- Offline parity: the same portal ships as static HTML with bundled assets.
- Deterministic rebuilds: aggregate spec and examples are pinned in-source.
- Audit-ready: schema-first views, provenance attached to specs, and upcoming try-it sandbox.
## What lives here
- Aggregate OpenAPI (namespaced by service) with schema explorer.
- Guides for tokens, scopes, SDKs, and export bundles.
- Release notes aligned to platform drops.

View File

@@ -0,0 +1,15 @@
---
title: Release Notes
description: Drop-by-drop updates for the DevPortal surface.
---
## 2025-11 (Sprint 0206.0001.0001)
- ✅ Selected Astro + Starlight as the static site generator for deterministic offline builds.
- ✅ Added navigation scaffolding (Overview, Guides, API, Roadmap) with local search enabled.
- ✅ Embedded aggregate OpenAPI via RapiDoc using bundled `/api/stella.yaml`.
- 🔜 Schema explorer UI and copy-curl snippets (DEVPORT-62-002).
- 🔜 Try-It console against sandbox scopes (DEVPORT-63-001).
## How to contribute release entries
- Add a dated section with bullet points grouped by task ID when features land.
- Keep entries aligned to sprint IDs and include any risks or follow-ups.

View File

@@ -0,0 +1,2 @@
/// <reference path="../.astro/types.d.ts" />
/// <reference types="astro/client" />

View File

@@ -0,0 +1,45 @@
:root {
--sl-font-sans: "Space Grotesk", "Segoe UI", "Inter", system-ui, -apple-system, sans-serif;
--sl-font-mono: "JetBrains Mono", "SFMono-Regular", ui-monospace, Menlo, Consolas, monospace;
--sl-color-accent: #0ea5e9;
--sl-color-text: #e5e7eb;
--sl-color-text-accent: #a5f3fc;
--sl-color-text-muted: #cbd5e1;
--sl-color-bg: #0b1220;
--sl-color-bg-soft: #0f172a;
--sl-color-hairline: #1f2937;
--sl-heading-font-weight: 700;
--sl-body-font-weight: 400;
}
body {
background: radial-gradient(circle at 20% 20%, rgba(14, 165, 233, 0.12), transparent 25%),
radial-gradient(circle at 80% 10%, rgba(99, 102, 241, 0.14), transparent 25%),
linear-gradient(180deg, #0b1220 0%, #0f172a 60%, #0b1220 100%);
color: var(--sl-color-text);
}
.sl-link-card {
border: 1px solid var(--sl-color-hairline);
background: linear-gradient(180deg, rgba(255, 255, 255, 0.03), rgba(255, 255, 255, 0.01));
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.25);
}
:where(.sl-markdown) h2 {
letter-spacing: -0.02em;
}
:where(.sl-markdown) code {
background: rgba(15, 23, 42, 0.7);
border: 1px solid var(--sl-color-hairline);
}
nav.sl-topnav {
border-bottom: 1px solid var(--sl-color-hairline);
backdrop-filter: blur(10px);
}
.sl-search-box input {
background: rgba(255, 255, 255, 0.08);
border: 1px solid var(--sl-color-hairline);
}

View File

@@ -0,0 +1,7 @@
{
"extends": "astro/tsconfigs/strict",
"compilerOptions": {
"types": ["astro/client"],
"baseUrl": "."
}
}