diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 91cdb8a6..4896bb47 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -19,7 +19,7 @@ body: options: - label: I have searched existing issues to ensure this is not a duplicate required: true - - label: I have read the documentation at https://rou-cru.github.io/idp-blueprint/ + - label: I have read the documentation at https://idp-blueprint.roura.xyz/ required: true - label: I am using the latest version from the main branch required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index b86751dd..b7226a38 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,7 +1,7 @@ blank_issues_enabled: false contact_links: - name: 📖 Documentation - url: https://rou-cru.github.io/idp-blueprint/ + url: https://idp-blueprint.roura.xyz/ about: Read the comprehensive documentation for the IDP Blueprint - name: 💬 Discussions url: https://github.com/rou-cru/idp-blueprint/discussions diff --git a/.github/ISSUE_TEMPLATE/documentation.yml b/.github/ISSUE_TEMPLATE/documentation.yml index a8fdb6ae..037235ae 100644 --- a/.github/ISSUE_TEMPLATE/documentation.yml +++ b/.github/ISSUE_TEMPLATE/documentation.yml @@ -19,7 +19,7 @@ body: options: - label: I have searched existing issues to ensure this is not a duplicate required: true - - label: I have checked the documentation site at https://rou-cru.github.io/idp-blueprint/ + - label: I have checked the documentation site at https://idp-blueprint.roura.xyz/ required: true - type: dropdown @@ -67,7 +67,7 @@ body: attributes: label: Page URL or File Path description: Link to the documentation page or file path - placeholder: "https://rou-cru.github.io/idp-blueprint/guides/getting-started or Docs/src/content/docs/guides/example.md" + placeholder: "https://idp-blueprint.roura.xyz/guides/getting-started or Docs/src/content/docs/guides/example.md" validations: required: false diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 26d1c83e..e625cf1c 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -19,7 +19,7 @@ body: options: - label: I have searched existing issues to ensure this is not a duplicate required: true - - label: I have read the documentation at https://rou-cru.github.io/idp-blueprint/ + - label: I have read the documentation at https://idp-blueprint.roura.xyz/ required: true - label: This feature aligns with the IDP Blueprint's goals (GitOps, Observability, Security, Policy) required: true diff --git a/.github/labeler.yml b/.github/labeler.yml index 5aaf962b..baf990f3 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -19,8 +19,7 @@ github-actions: - ".github/dependabot.yml" - ".github/labeler.yml" - ".github/ISSUE_TEMPLATE/**/*" - - ".github/PULL_REQUEST_TEMPLATE.md" - - ".github/CODEOWNERS" + - ".github/pull_request_template.md" # Task Automation automation: @@ -42,30 +41,31 @@ configuration: argocd: - changed-files: - any-glob-to-any-file: - - "Bootstrap/argocd/**/*" - - "Stacks/argocd/**/*" + - "IT/argocd/**/*" + - "K8s/**/argocd*/**/*" - "**/argocd-*.yaml" # Cilium / Networking cilium: - changed-files: - any-glob-to-any-file: - - "Bootstrap/cilium/**/*" + - "IT/cilium/**/*" - "**/cilium-*.yaml" # Vault / Secrets vault: - changed-files: - any-glob-to-any-file: - - "Bootstrap/vault/**/*" - - "Stacks/vault/**/*" + - "IT/vault/**/*" + - "K8s/**/vault/**/*" - "**/vault-*.yaml" # External Secrets external-secrets: - changed-files: - any-glob-to-any-file: - - "Bootstrap/external-secrets/**/*" + - "IT/external-secrets/**/*" + - "K8s/**/external-secrets/**/*" - "**/external-secret*.yaml" - "**/secret-store*.yaml" @@ -73,8 +73,7 @@ external-secrets: kyverno: - changed-files: - any-glob-to-any-file: - - "Policies/**/*" - - "Stacks/kyverno/**/*" + - "K8s/policies/**/*" - "**/kyverno-*.yaml" - "**/*policy*.yaml" @@ -82,10 +81,7 @@ kyverno: observability: - changed-files: - any-glob-to-any-file: - - "Stacks/prometheus/**/*" - - "Stacks/grafana/**/*" - - "Stacks/loki/**/*" - - "Stacks/fluent-bit/**/*" + - "K8s/observability/**/*" - "**/prometheus-*.yaml" - "**/grafana-*.yaml" - "**/loki-*.yaml" @@ -94,8 +90,7 @@ observability: cicd: - changed-files: - any-glob-to-any-file: - - "Stacks/argo-workflows/**/*" - - "Stacks/sonarqube/**/*" + - "K8s/cicd/**/*" - "**/argo-workflow*.yaml" - "**/sonarqube-*.yaml" @@ -103,7 +98,7 @@ cicd: cert-manager: - changed-files: - any-glob-to-any-file: - - "Bootstrap/cert-manager/**/*" + - "IT/cert-manager/**/*" - "**/cert-manager-*.yaml" - "**/certificate*.yaml" - "**/clusterissuer*.yaml" @@ -112,7 +107,7 @@ cert-manager: gateway: - changed-files: - any-glob-to-any-file: - - "Bootstrap/gateway/**/*" + - "IT/gateway/**/*" - "**/gateway*.yaml" - "**/httproute*.yaml" @@ -121,6 +116,7 @@ k3d: - changed-files: - any-glob-to-any-file: - "Task/k3d.yaml" + - "IT/k3d-cluster.yaml" - ".devcontainer/**/*" - "**/k3d-*.yaml" @@ -145,7 +141,6 @@ security: - any-glob-to-any-file: - "SECURITY.md" - ".config/lint/.trufflehog-ignore" - - "Policies/**/*" - "**/rbac*.yaml" - "**/networkpolicy*.yaml" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..47a4fcc3 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,13 @@ +## Summary +- What does this change do? +- Any user-facing or breaking changes? + +## Testing +- [ ] Not run (explain why) +- [ ] Added/updated automated tests +- [ ] Manually tested (``) + +## Checklist +- [ ] Docs updated if needed +- [ ] Conventional commit in title +- [ ] Linked issue (if relevant) diff --git a/.github/workflows/auto-merge.yaml b/.github/workflows/auto-merge.yaml new file mode 100644 index 00000000..4093198b --- /dev/null +++ b/.github/workflows/auto-merge.yaml @@ -0,0 +1,83 @@ +name: Auto Merge with CodeRabbit + +on: + pull_request_target: + types: [opened, reopened, synchronize, ready_for_review, review_requested] + workflow_run: + workflows: ["CI Pipeline", "Documentation"] + types: [completed] + +permissions: + contents: write + pull-requests: write + checks: read + statuses: read + +jobs: + auto-merge: + runs-on: ubuntu-latest + if: github.event.pull_request.head.repo.full_name == github.repository || github.event.workflow_run.event == 'pull_request' + steps: + - name: Get PR context + id: ctx + uses: actions/github-script@v8 + with: + script: | + const pr = context.payload.pull_request + ? context.payload.pull_request + : await (async () => { + // workflow_run path + const run = context.payload.workflow_run + const prData = run.pull_requests && run.pull_requests[0] + if (!prData) return null + const { data } = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: prData.number, + }) + return data + })() + + if (!pr) { + core.setFailed('No pull_request context available') + return + } + + // basic guards + if (pr.draft) { + core.setOutput('status', 'skip-draft') + return + } + if (pr.state !== 'open') { + core.setOutput('status', 'skip-closed') + return + } + if (pr.mergeable_state === 'behind') { + core.setOutput('status', 'skip-behind') + return + } + if (!pr.mergeable_state || pr.mergeable_state === 'unknown') { + core.setOutput('status', 'skip-unknown') + core.info('Mergeable state is unknown, waiting for GitHub to compute it') + return + } + const acceptableStates = ['clean', 'unstable', 'has_hooks'] + if (!acceptableStates.includes(pr.mergeable_state)) { + core.setOutput('status', `skip-${pr.mergeable_state}`) + core.info(`Mergeable state '${pr.mergeable_state}' not acceptable for auto-merge`) + return + } + + core.setOutput('pr-number', pr.number.toString()) + core.setOutput('mergeable_state', pr.mergeable_state || 'unknown') + core.setOutput('status', 'ready') + + - name: Enable auto-merge + if: steps.ctx.outputs.status == 'ready' && + steps.ctx.outputs.mergeable_state != 'behind' && + steps.ctx.outputs.mergeable_state != 'unknown' + uses: peter-evans/enable-pull-request-automerge@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + pull-request-number: ${{ steps.ctx.outputs.pr-number }} + merge-method: squash diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 36644b3c..d5214005 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -4,15 +4,6 @@ on: # Run CI as Quality Gate on PRs only (not on push to main) pull_request: branches: [main] - paths-ignore: - - 'Docs/**' - - 'mkdocs.yml' - - '**.md' - - 'pyproject.toml' - - 'uv.lock' - - 'Scripts/generate-chart-metadata.sh' - - 'Scripts/helm-docs-*.sh' - - '.github/workflows/docs.yaml' # Prevent concurrent runs on the same PR concurrency: @@ -23,34 +14,176 @@ permissions: contents: read jobs: - build-and-test: + changes: + name: Detect changed areas runs-on: ubuntu-latest - timeout-minutes: 20 + outputs: + docs: ${{ steps.set.outputs.docs }} + values: ${{ steps.set.outputs.values }} + full: ${{ steps.set.outputs.full }} + targets: ${{ steps.set.outputs.targets }} steps: - - name: Checkout code - uses: actions/checkout@v6 + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + docs: + - 'Docs/**' + - '**/*.md' + values: + - '**/values.yaml' + - '**/values*.yaml' + base: + - 'Dockerfile' + - '.devcontainer/Dockerfile' + - 'docker-bake.hcl' + - 'Task/image.yaml' + dev: + - 'devbox.json' + - 'devbox.lock' + minimal: + - '.devcontainer/devbox-minimal.json' + ops: + - '.devcontainer/devbox-ops.json' + portal: + - 'UI/**' + + - name: Calculate Matrix Targets + id: set + run: | + # Retrieve the list of modified filters (e.g., ["docs", "minimal"]) + changes='${{ steps.filter.outputs.changes }}' + + # If 'base' changed, force all build targets + if echo "$changes" | grep -q "base"; then + targets='["dev", "minimal", "ops", "portal"]' + else + # Otherwise, filter the changes list to only include build targets + # jq select filters out 'docs', 'values', 'base' from the list + targets=$(echo "$changes" | jq -c 'map(select(. == "dev" or . == "minimal" or . == "ops" or . == "portal"))') + fi + + echo "targets=$targets" >> "$GITHUB_OUTPUT" + + # Maintain compatibility flags + echo "docs=${{ steps.filter.outputs.docs }}" >> "$GITHUB_OUTPUT" + echo "values=${{ steps.filter.outputs.values }}" >> "$GITHUB_OUTPUT" - - name: Prepare Devbox configuration - run: ln -sf devbox-minimal.json devbox.json - working-directory: .devcontainer + # Determine 'full' run (any build target or values changed) + if [ "$targets" != "[]" ] || [ "${{ steps.filter.outputs.values }}" == "true" ]; then + echo "full=true" >> "$GITHUB_OUTPUT" + else + echo "full=false" >> "$GITHUB_OUTPUT" + fi + docs-pr: + name: Docs only + needs: changes + if: needs.changes.outputs.docs == 'true' && needs.changes.outputs.full == 'false' + runs-on: ubuntu-latest + env: + DEVBOX_CONFIG: .devcontainer/devbox-minimal.json + steps: + - uses: actions/checkout@v6 - name: Install Devbox uses: jetpack-io/devbox-install-action@v0.14.0 with: project-path: .devcontainer enable-cache: true + - name: Build docs + run: devbox run --config $DEVBOX_CONFIG -- task utils:docs:astro:build + core-ci: + name: Core CI + needs: changes + if: needs.changes.outputs.full == 'true' + runs-on: ubuntu-latest + timeout-minutes: 50 + env: + DEVBOX_CONFIG: .devcontainer/devbox-minimal.json + steps: + - name: Checkout code + uses: actions/checkout@v6 + - name: Install Devbox + uses: jetpack-io/devbox-install-action@v0.14.0 + with: + project-path: .devcontainer + enable-cache: true - name: Run Linting Tasks - run: devbox run -- task quality:lint - working-directory: .devcontainer + run: devbox run --config $DEVBOX_CONFIG -- task quality:lint timeout-minutes: 10 - - name: Run Security Scans - run: devbox run -- task quality:security - working-directory: .devcontainer + run: devbox run --config $DEVBOX_CONFIG -- task quality:security timeout-minutes: 15 - - name: Run Validations - run: devbox run -- task quality:validate - working-directory: .devcontainer - timeout-minutes: 10 + run: devbox run --config $DEVBOX_CONFIG -- task quality:validate + timeout-minutes: 12 + - name: Generate Helm docs (values.yaml changed) + if: needs.changes.outputs.values == 'true' + run: devbox run --config $DEVBOX_CONFIG -- task utils:docs:helm + + docker-build: + name: Build ${{ matrix.target }} + needs: changes + if: needs.changes.outputs.full == 'true' && needs.changes.outputs.targets != '[]' + runs-on: ubuntu-latest + env: + DOCKER_IMAGE_NAME: ${{ vars.DOCKER_IMAGE_NAME }} + DOCKER_IMAGE_TAG: ${{ vars.DOCKER_IMAGE_TAG }} + DOCKER_IMAGE_TAG_MINIMAL: ${{ vars.DOCKER_IMAGE_TAG_MINIMAL }} + DOCKER_IMAGE_TAG_OPS: ${{ vars.DOCKER_IMAGE_TAG_OPS }} + strategy: + fail-fast: false + matrix: + target: ${{ fromJson(needs.changes.outputs.targets) }} + steps: + - uses: actions/checkout@v6 + - name: Free Disk Space (Ubuntu) + uses: jlumbroso/free-disk-space@main + with: + tool-cache: false + android: true # ~12Gib Free + dotnet: true # ~4Gib Free + haskell: true # ~6Gib Free + large-packages: true # ~4.7Gib Free + swap-storage: true # ~4GiB Saved + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Install Devbox + uses: jetpack-io/devbox-install-action@v0.14.0 + with: + project-path: . + enable-cache: true + - name: Setup Node.js (Portal only) + if: matrix.target == 'portal' + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: yarn + cache-dependency-path: UI/yarn.lock + - name: Build Target + run: | + case "${{ matrix.target }}" in + "dev") devbox run -- task image:image:build ;; + "minimal") devbox run -- task image:image:build:minimal ;; + "ops") devbox run -- task image:image:build:ops ;; + "portal") devbox run -- task image:dev-portal:build ;; + esac + + ci-green: + name: CI green + needs: [changes, docs-pr, core-ci, docker-build] + if: always() + runs-on: ubuntu-latest + steps: + - name: Check for failures + if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') + run: | + echo "::error::One or more required jobs failed or were cancelled" + exit 1 + - name: Success + run: echo "✅ All CI checks passed" diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index c3d52423..3deb901a 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -52,7 +52,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v6 with: - node-version: 24 + node-version: 22 cache: 'pnpm' cache-dependency-path: Docs/pnpm-lock.yaml diff --git a/Task/bootstrap.yaml b/Task/bootstrap.yaml index 3fe845cd..af12059b 100644 --- a/Task/bootstrap.yaml +++ b/Task/bootstrap.yaml @@ -64,6 +64,7 @@ tasks: cilium:deploy: desc: "Install Cilium as CNI" + internal: true dir: IT/cilium silent: false cmds: @@ -79,6 +80,7 @@ tasks: it:bootstrap: desc: "Bootstrap cluster prerequisites and preload images" + internal: true silent: true deps: - cilium:preload @@ -88,6 +90,7 @@ tasks: cert-manager:deploy: desc: "Deploy Cert-Manager to handle CA" + internal: true dir: IT/cert-manager silent: true cmds: @@ -103,6 +106,7 @@ tasks: external-secrets:deploy: desc: "Install External Secrets Operator" + internal: true dir: IT/external-secrets silent: false cmds: @@ -119,6 +123,7 @@ tasks: it:eso:apply-resources: desc: "Apply ESO SecretStores and ExternalSecrets" + internal: true dir: IT/external-secrets cmds: - kustomize build . | kubectl apply -f - --server-side @@ -140,6 +145,7 @@ tasks: vault:generate-secrets: desc: "Generate initial secrets for all services using Vault" + internal: true vars: ARGOCD_USERNAME: "admin" SECRETS: @@ -219,6 +225,7 @@ tasks: vault:deploy: desc: "Deploy HashiCorp Vault" + internal: true dir: IT/vault cmds: - | @@ -232,6 +239,7 @@ tasks: it:deploy-secret-and-certs: desc: "Deploy cert-manager and vault" + internal: true silent: true deps: - cert-manager:deploy @@ -239,6 +247,7 @@ tasks: argocd:deploy: desc: "Deploy ArgoCD GitOps Engine" + internal: true dir: IT/argocd silent: true env: @@ -265,6 +274,7 @@ tasks: gateway:deploy: desc: "Deploy Gateway API Gateway resource" + internal: true dir: IT/gateway silent: true cmds: diff --git a/Task/stacks.yaml b/Task/stacks.yaml index 9d788d9e..9108432a 100644 --- a/Task/stacks.yaml +++ b/Task/stacks.yaml @@ -79,6 +79,7 @@ tasks: deploy: desc: "Deploy all application stacks via ArgoCD ApplicationSets (feature-gated)" + internal: true cmds: - task: policies - task: observability diff --git a/Taskfile.yaml b/Taskfile.yaml index d1b34c57..6c4d8ecf 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -36,6 +36,7 @@ tasks: deploy-core: desc: "Core deployment logic (do not run directly)" + internal: true deps: - internal:validate-tools preconditions: diff --git a/UI/packages/app/package.json b/UI/packages/app/package.json index d010b6fb..74ef3cf9 100644 --- a/UI/packages/app/package.json +++ b/UI/packages/app/package.json @@ -15,38 +15,39 @@ }, "dependencies": { "@backstage-community/plugin-github-actions": "^0.18.0", + "@backstage-community/plugin-grafana": "^0.12.0", "@backstage-community/plugin-sonarqube": "^0.20.1", "@backstage-community/plugin-sonarqube-react": "^0.12.0", "@backstage-community/plugin-topology": "^2.8.0", "@backstage-community/plugin-vault": "^0.14.0", - "@backstage/app-defaults": "^1.7.2", + "@backstage/app-defaults": "^1.7.3", "@backstage/catalog-model": "^1.7.6", - "@backstage/cli": "^0.34.5", - "@backstage/core-app-api": "^1.19.2", - "@backstage/core-components": "^0.18.3", - "@backstage/core-plugin-api": "^1.12.0", - "@backstage/integration-react": "^1.2.12", - "@backstage/plugin-api-docs": "^0.13.1", - "@backstage/plugin-catalog": "^1.32.0", + "@backstage/cli": "^0.35.1", + "@backstage/core-app-api": "^1.19.3", + "@backstage/core-components": "^0.18.4", + "@backstage/core-plugin-api": "^1.12.1", + "@backstage/integration-react": "^1.2.13", + "@backstage/plugin-api-docs": "^0.13.2", + "@backstage/plugin-catalog": "^1.32.1", "@backstage/plugin-catalog-common": "^1.1.7", - "@backstage/plugin-catalog-graph": "^0.5.3", - "@backstage/plugin-catalog-import": "^0.13.7", - "@backstage/plugin-catalog-react": "^1.21.3", - "@backstage/plugin-home": "^0.8.14", - "@backstage/plugin-kubernetes": "^0.12.13", - "@backstage/plugin-notifications": "^0.5.11", - "@backstage/plugin-org": "^0.6.46", - "@backstage/plugin-permission-react": "^0.4.38", - "@backstage/plugin-scaffolder": "^1.34.3", - "@backstage/plugin-search": "^1.5.0", - "@backstage/plugin-search-react": "^1.10.0", - "@backstage/plugin-signals": "^0.0.25", - "@backstage/plugin-techdocs": "^1.16.0", - "@backstage/plugin-techdocs-module-addons-contrib": "^1.1.30", - "@backstage/plugin-techdocs-react": "^1.3.5", - "@backstage/plugin-user-settings": "^0.8.29", - "@backstage/theme": "^0.7.0", - "@backstage/ui": "^0.9.0", + "@backstage/plugin-catalog-graph": "^0.5.4", + "@backstage/plugin-catalog-import": "^0.13.8", + "@backstage/plugin-catalog-react": "^1.21.4", + "@backstage/plugin-home": "^0.8.15", + "@backstage/plugin-kubernetes": "^0.12.14", + "@backstage/plugin-notifications": "^0.5.12", + "@backstage/plugin-org": "^0.6.47", + "@backstage/plugin-permission-react": "^0.4.39", + "@backstage/plugin-scaffolder": "^1.35.0", + "@backstage/plugin-search": "^1.5.1", + "@backstage/plugin-search-react": "^1.10.1", + "@backstage/plugin-signals": "^0.0.26", + "@backstage/plugin-techdocs": "^1.16.1", + "@backstage/plugin-techdocs-module-addons-contrib": "^1.1.31", + "@backstage/plugin-techdocs-react": "^1.3.6", + "@backstage/plugin-user-settings": "^0.8.30", + "@backstage/theme": "^0.7.1", + "@backstage/ui": "^0.10.0", "@dweber019/backstage-plugin-relations": "^0.1.1", "@dweber019/backstage-plugin-simple-icons": "^0.1.1", "@kyverno/backstage-plugin-policy-reporter": "^2.4.1", @@ -59,7 +60,7 @@ "react-router-dom": "^6.3.0" }, "devDependencies": { - "@backstage/test-utils": "^1.7.13", + "@backstage/test-utils": "^1.7.14", "@playwright/test": "^1.32.3", "@testing-library/dom": "^9.0.0", "@testing-library/jest-dom": "^6.0.0", diff --git a/UI/packages/app/src/components/catalog/EntityPage.tsx b/UI/packages/app/src/components/catalog/EntityPage.tsx index 8e2a8f1d..4966bab4 100644 --- a/UI/packages/app/src/components/catalog/EntityPage.tsx +++ b/UI/packages/app/src/components/catalog/EntityPage.tsx @@ -34,7 +34,7 @@ import { EntityOwnershipCard, } from '@backstage/plugin-org'; import { EntityTechdocsContent } from '@backstage/plugin-techdocs'; -import { EmptyState } from '@backstage/core-components'; +import { EmptyState, WarningPanel } from '@backstage/core-components'; import { Direction, EntityCatalogGraphCard, @@ -48,6 +48,7 @@ import { RELATION_HAS_PART, RELATION_PART_OF, RELATION_PROVIDES_API, + Entity, } from '@backstage/catalog-model'; import { TechDocsAddons } from '@backstage/plugin-techdocs-react'; @@ -65,7 +66,96 @@ import { } from '@roadiehq/backstage-plugin-argo-cd'; import { TopologyPage } from '@backstage-community/plugin-topology'; import { EntityKyvernoPoliciesContent } from '@kyverno/backstage-plugin-policy-reporter'; - +import { useEntity } from '@backstage/plugin-catalog-react'; +import { configApiRef, useApi } from '@backstage/core-plugin-api'; +import { makeStyles } from '@material-ui/core/styles'; + +// Helper functions to check for relations +const hasSubcomponents = (entity: Entity) => { + return entity?.relations?.some( + (r) => r.type === RELATION_HAS_PART + ) ?? false; +}; + +const hasApis = (entity: Entity) => { + return entity?.relations?.some( + (r) => r.type === RELATION_PROVIDES_API || r.type === RELATION_CONSUMES_API + ) ?? false; +}; + +const useStyles = makeStyles({ + iframe: { + width: '100%', + height: '800px', + border: 'none', + }, +}); + +const EntityLokiLogs = () => { + const classes = useStyles(); + const { entity } = useEntity(); + const config = useApi(configApiRef); + + const grafanaUrl = config.getOptionalString('grafana.domain'); + + if (!grafanaUrl) { + return ( + + ); + } + + const annotations = entity.metadata.annotations || {}; + const namespace = annotations['backstage.io/kubernetes-namespace'] || entity.metadata.namespace || 'default'; + + // Dashboard UID: fRIvzUZMz (Container Log Dashboard) + const dashboardPath = '/d/fRIvzUZMz/container-log-dashboard'; + const queryParams = `?var-namespace=${namespace}&var-pod=All&var-stream=All&var-searchable_pattern=&kiosk`; + const src = `${grafanaUrl}${dashboardPath}${queryParams}`; + + return ( +