From 9149f844b7f27fcf87f4017ca016cdd53c7b5d92 Mon Sep 17 00:00:00 2001 From: tukue Date: Wed, 27 May 2026 19:26:33 +0200 Subject: [PATCH 1/8] feat: add Backstage Software Catalog entities for platform, chart, and tenant apps --- catalog-info.yaml | 47 ++++++++++++++++++++++ platform/apps/app-b/catalog-info.yaml | 16 ++++++++ platform/apps/simple-app/catalog-info.yaml | 16 ++++++++ standardized-path/app/catalog-info.yaml | 31 ++++++++++++++ 4 files changed, 110 insertions(+) create mode 100644 catalog-info.yaml create mode 100644 platform/apps/app-b/catalog-info.yaml create mode 100644 platform/apps/simple-app/catalog-info.yaml create mode 100644 standardized-path/app/catalog-info.yaml diff --git a/catalog-info.yaml b/catalog-info.yaml new file mode 100644 index 0000000..ed003f7 --- /dev/null +++ b/catalog-info.yaml @@ -0,0 +1,47 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: simple-app-infra-code + description: Internal Developer Platform for EKS and OpenShift + annotations: + github.com/project-slug: tukue/simpleAppInfraCode + backstage.io/techdocs-ref: dir:. +spec: + type: repository + lifecycle: experimental + owner: platform-team + system: platform + providesApis: + - tenant-contract + dependsOn: + - component:app-chart +--- +apiVersion: backstage.io/v1alpha1 +kind: System +metadata: + name: platform + description: Internal Developer Platform providing golden path, GitOps, and observability +spec: + owner: platform-team +--- +apiVersion: backstage.io/v1alpha1 +kind: Group +metadata: + name: platform-team + description: Platform engineering team +spec: + type: team + profile: + displayName: Platform Team + children: [] +--- +apiVersion: backstage.io/v1alpha1 +kind: Group +metadata: + name: app-developers + description: Application development teams +spec: + type: team + profile: + displayName: App Developers + children: [] diff --git a/platform/apps/app-b/catalog-info.yaml b/platform/apps/app-b/catalog-info.yaml new file mode 100644 index 0000000..5434537 --- /dev/null +++ b/platform/apps/app-b/catalog-info.yaml @@ -0,0 +1,16 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: app-b + description: Second tenant application proving contract reusability across dev/stage/prod + annotations: + github.com/project-slug: tukue/simpleAppInfraCode +spec: + type: service + lifecycle: experimental + owner: app-developers + system: platform + dependsOn: + - component:app-chart + providesApis: + - tenant-contract diff --git a/platform/apps/simple-app/catalog-info.yaml b/platform/apps/simple-app/catalog-info.yaml new file mode 100644 index 0000000..803a8e1 --- /dev/null +++ b/platform/apps/simple-app/catalog-info.yaml @@ -0,0 +1,16 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: simple-app + description: Demo tenant application deployed via the platform golden path + annotations: + github.com/project-slug: tukue/simpleAppInfraCode +spec: + type: service + lifecycle: experimental + owner: app-developers + system: platform + dependsOn: + - component:app-chart + providesApis: + - tenant-contract diff --git a/standardized-path/app/catalog-info.yaml b/standardized-path/app/catalog-info.yaml new file mode 100644 index 0000000..5b56d83 --- /dev/null +++ b/standardized-path/app/catalog-info.yaml @@ -0,0 +1,31 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: app-chart + description: Golden path Helm chart — the tenant contract for deploying services on the platform + annotations: + backstage.io/techdocs-ref: dir:. +spec: + type: library + lifecycle: experimental + owner: platform-team + system: platform + dependsOn: + - component:simple-app-infra-code + providesApis: + - tenant-contract +--- +apiVersion: backstage.io/v1alpha1 +kind: API +metadata: + name: tenant-contract + description: Contract defining what app teams provide and what the platform guarantees +spec: + type: helm-values + lifecycle: experimental + owner: platform-team + system: platform + definition: | + App Team provides: image, port, replicas, env vars, secret refs + Platform guarantees: non-root, dropped capabilities, resource boundaries, + network isolation, drift detection, immutable tags, cluster portability From c133cdbeba53c0af6af999b583dfda96e276db7e Mon Sep 17 00:00:00 2001 From: tukue Date: Wed, 27 May 2026 19:38:49 +0200 Subject: [PATCH 2/8] fix: resolve circular dependency and incorrect API relationships in Backstage catalog --- catalog-info.yaml | 2 -- platform/apps/app-b/catalog-info.yaml | 2 -- platform/apps/simple-app/catalog-info.yaml | 2 -- standardized-path/app/catalog-info.yaml | 2 -- 4 files changed, 8 deletions(-) diff --git a/catalog-info.yaml b/catalog-info.yaml index ed003f7..29f3a33 100644 --- a/catalog-info.yaml +++ b/catalog-info.yaml @@ -13,8 +13,6 @@ spec: system: platform providesApis: - tenant-contract - dependsOn: - - component:app-chart --- apiVersion: backstage.io/v1alpha1 kind: System diff --git a/platform/apps/app-b/catalog-info.yaml b/platform/apps/app-b/catalog-info.yaml index 5434537..2dae5e0 100644 --- a/platform/apps/app-b/catalog-info.yaml +++ b/platform/apps/app-b/catalog-info.yaml @@ -12,5 +12,3 @@ spec: system: platform dependsOn: - component:app-chart - providesApis: - - tenant-contract diff --git a/platform/apps/simple-app/catalog-info.yaml b/platform/apps/simple-app/catalog-info.yaml index 803a8e1..23a850c 100644 --- a/platform/apps/simple-app/catalog-info.yaml +++ b/platform/apps/simple-app/catalog-info.yaml @@ -12,5 +12,3 @@ spec: system: platform dependsOn: - component:app-chart - providesApis: - - tenant-contract diff --git a/standardized-path/app/catalog-info.yaml b/standardized-path/app/catalog-info.yaml index 5b56d83..c9c83ba 100644 --- a/standardized-path/app/catalog-info.yaml +++ b/standardized-path/app/catalog-info.yaml @@ -10,8 +10,6 @@ spec: lifecycle: experimental owner: platform-team system: platform - dependsOn: - - component:simple-app-infra-code providesApis: - tenant-contract --- From 2979a2534404b64935fdfb167f04d4c2af8b92af Mon Sep 17 00:00:00 2001 From: tukue Date: Wed, 27 May 2026 22:02:38 +0200 Subject: [PATCH 3/8] feat: add Backstage scaffolder template for scaffolding new tenant apps --- .../${{parameters.appName}}/dev/values.yaml | 26 ++++ .../${{parameters.appName}}/prod/values.yaml | 26 ++++ .../${{parameters.appName}}/stage/values.yaml | 26 ++++ .../templates/new-tenant-app/template.yaml | 124 ++++++++++++++++++ 4 files changed, 202 insertions(+) create mode 100644 backstage/templates/new-tenant-app/skeleton/platform/apps/${{parameters.appName}}/dev/values.yaml create mode 100644 backstage/templates/new-tenant-app/skeleton/platform/apps/${{parameters.appName}}/prod/values.yaml create mode 100644 backstage/templates/new-tenant-app/skeleton/platform/apps/${{parameters.appName}}/stage/values.yaml create mode 100644 backstage/templates/new-tenant-app/template.yaml diff --git a/backstage/templates/new-tenant-app/skeleton/platform/apps/${{parameters.appName}}/dev/values.yaml b/backstage/templates/new-tenant-app/skeleton/platform/apps/${{parameters.appName}}/dev/values.yaml new file mode 100644 index 0000000..0036c9f --- /dev/null +++ b/backstage/templates/new-tenant-app/skeleton/platform/apps/${{parameters.appName}}/dev/values.yaml @@ -0,0 +1,26 @@ +replicaCount: 1 + +image: + repository: ${{ parameters.imageRepo }}${{ parameters.appName }} + tag: "1.0.0-dev.1" + +ingress: + enabled: true + hosts: + - host: ${{ parameters.appName }}-dev.example.com + paths: + - path: / + pathType: Prefix + +env: + - name: ENVIRONMENT + value: "dev" + - name: PORT + value: "${{ parameters.port }}" + +externalSecret: + enabled: true + data: + - secretKey: db-password + remoteRef: + key: dev/${{ parameters.appName }}/db-password diff --git a/backstage/templates/new-tenant-app/skeleton/platform/apps/${{parameters.appName}}/prod/values.yaml b/backstage/templates/new-tenant-app/skeleton/platform/apps/${{parameters.appName}}/prod/values.yaml new file mode 100644 index 0000000..71da639 --- /dev/null +++ b/backstage/templates/new-tenant-app/skeleton/platform/apps/${{parameters.appName}}/prod/values.yaml @@ -0,0 +1,26 @@ +replicaCount: 3 + +image: + repository: ${{ parameters.imageRepo }}${{ parameters.appName }} + tag: "1.0.0" + +ingress: + enabled: true + hosts: + - host: ${{ parameters.appName }}.example.com + paths: + - path: / + pathType: Prefix + +env: + - name: ENVIRONMENT + value: "prod" + - name: PORT + value: "${{ parameters.port }}" + +externalSecret: + enabled: true + data: + - secretKey: db-password + remoteRef: + key: prod/${{ parameters.appName }}/db-password diff --git a/backstage/templates/new-tenant-app/skeleton/platform/apps/${{parameters.appName}}/stage/values.yaml b/backstage/templates/new-tenant-app/skeleton/platform/apps/${{parameters.appName}}/stage/values.yaml new file mode 100644 index 0000000..f593500 --- /dev/null +++ b/backstage/templates/new-tenant-app/skeleton/platform/apps/${{parameters.appName}}/stage/values.yaml @@ -0,0 +1,26 @@ +replicaCount: 2 + +image: + repository: ${{ parameters.imageRepo }}${{ parameters.appName }} + tag: "1.0.0-rc1" + +ingress: + enabled: true + hosts: + - host: ${{ parameters.appName }}-stage.example.com + paths: + - path: / + pathType: Prefix + +env: + - name: ENVIRONMENT + value: "stage" + - name: PORT + value: "${{ parameters.port }}" + +externalSecret: + enabled: true + data: + - secretKey: db-password + remoteRef: + key: stage/${{ parameters.appName }}/db-password diff --git a/backstage/templates/new-tenant-app/template.yaml b/backstage/templates/new-tenant-app/template.yaml new file mode 100644 index 0000000..f1b462c --- /dev/null +++ b/backstage/templates/new-tenant-app/template.yaml @@ -0,0 +1,124 @@ +apiVersion: backstage.io/v1beta2 +kind: Template +metadata: + name: new-tenant-app + title: New Tenant Application + description: Scaffold a new application using the platform's golden path Helm chart + tags: + - platform + - helm + - golden-path +spec: + owner: platform-team + type: service + + parameters: + - title: Application Details + required: + - appName + - description + - owner + properties: + appName: + title: Application Name + type: string + description: Unique name for the application (e.g., app-c) + pattern: '^[a-z][a-z0-9-]*$' + description: + title: Description + type: string + description: Purpose of the application + owner: + title: Owner + type: string + description: Backstage Group or User that owns this app + default: app-developers + ui:field: OwnerPicker + ui:options: + allowedKinds: + - Group + + - title: Container Configuration + required: + - imageRepo + properties: + imageRepo: + title: Container Image Repository + type: string + description: ECR or Docker image repository URL + default: 944684220857.dkr.ecr.eu-north-1.amazonaws.com/ + port: + title: Application Port + type: integer + default: 8080 + + steps: + - id: fetch-skeleton + name: Fetch Skeleton + action: fetch:template + input: + url: ./skeleton + values: + appName: ${{ parameters.appName }} + description: ${{ parameters.description }} + imageRepo: ${{ parameters.imageRepo }} + port: ${{ parameters.port }} + owner: ${{ parameters.owner }} + + - id: publish + name: Publish to GitHub + action: publish:github + input: + repoUrl: github.com?owner=tukue&repo=simpleAppInfraCode + branch: feature/${{ parameters.appName }} + defaultBranch: main + repoVisibility: public + + - id: register + name: Register in Backstage + action: catalog:register + input: + repoContentsUrl: ${{ steps.publish.output.repoContentsUrl }} + catalogInfoPath: /catalog-info.yaml + + output: + links: + - title: Open in GitHub + url: ${{ steps.publish.output.remoteUrl }} + - title: Open Pull Request + url: ${{ steps.publish.output.pullRequestUrl }} + text: + - title: Next Steps + content: | + ### After Scaffolding + + 1. Add the app entries to `argocd/appsets/environments.yaml`: + ```yaml + - env: dev + app: ${{ parameters.appName }} + valuesPath: platform/apps/${{ parameters.appName }}/dev/values.yaml + - env: stage + app: ${{ parameters.appName }} + valuesPath: platform/apps/${{ parameters.appName }}/stage/values.yaml + - env: prod + app: ${{ parameters.appName }} + valuesPath: platform/apps/${{ parameters.appName }}/prod/values.yaml + ``` + + 2. Add a `catalog-info.yaml` at `platform/apps/${{ parameters.appName }}/catalog-info.yaml`: + ```yaml + apiVersion: backstage.io/v1alpha1 + kind: Component + metadata: + name: ${{ parameters.appName }} + description: ${{ parameters.description }} + spec: + type: service + owner: ${{ parameters.owner }} + system: platform + dependsOn: + - component:app-chart + ``` + + 3. Open a PR to merge the new app into main. + 4. Argo CD will automatically pick up the new ApplicationSet entries. From ddab3edcc1d9ac9bb1175b6005ff5e99563f5aff Mon Sep 17 00:00:00 2001 From: tukue Date: Wed, 27 May 2026 22:05:53 +0200 Subject: [PATCH 4/8] feat: add Backstage deployment config and Argo CD Application for serving the developer portal --- backstage/app-config.yaml | 53 +++++++++++++++++ platform/addons/backstage.yaml | 102 +++++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 backstage/app-config.yaml create mode 100644 platform/addons/backstage.yaml diff --git a/backstage/app-config.yaml b/backstage/app-config.yaml new file mode 100644 index 0000000..2868d12 --- /dev/null +++ b/backstage/app-config.yaml @@ -0,0 +1,53 @@ +app: + title: Internal Developer Platform + baseUrl: http://backstage.platform.example.com + +backend: + baseUrl: http://backstage.platform.example.com + listen: + port: 7007 + cors: + origin: http://backstage.platform.example.com + +organization: + name: Platform Engineering + +auth: + environment: development + providers: + guest: {} + +integrations: + github: + - host: github.com + apps: [] + +catalog: + import: + entityFilename: catalog-info.yaml + locations: + - type: url + target: https://github.com/tukue/simpleAppInfraCode/blob/main/catalog-info.yaml + - type: url + target: https://github.com/tukue/simpleAppInfraCode/blob/main/standardized-path/app/catalog-info.yaml + - type: url + target: https://github.com/tukue/simpleAppInfraCode/blob/main/platform/apps/simple-app/catalog-info.yaml + - type: url + target: https://github.com/tukue/simpleAppInfraCode/blob/main/platform/apps/app-b/catalog-info.yaml + +scaffolder: + locations: + - type: url + target: https://github.com/tukue/simpleAppInfraCode/blob/main/backstage/templates/new-tenant-app/template.yaml + +kubernetes: + serviceLocatorMethod: + type: multiTenant + clusterLocatorMethods: + - type: config + clusters: + - name: in-cluster + url: https://kubernetes.default.svc + authProvider: serviceAccount + skipTLSVerify: true + skipMetricsLookup: true diff --git a/platform/addons/backstage.yaml b/platform/addons/backstage.yaml new file mode 100644 index 0000000..c11cade --- /dev/null +++ b/platform/addons/backstage.yaml @@ -0,0 +1,102 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: backstage + namespace: argocd +spec: + project: platform + source: + repoURL: https://backstage.github.io/charts + chart: backstage + targetRevision: 2.x + helm: + values: | + backend: + image: + repository: backstage/backstage + tag: latest + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: 250m + memory: 512Mi + + service: + type: ClusterIP + port: 80 + + ingress: + enabled: true + className: nginx + annotations: + nginx.ingress.kubernetes.io/ssl-redirect: "false" + hosts: + - host: backstage.platform.example.com + paths: + - path: / + pathType: Prefix + + appConfig: + app: + title: Internal Developer Platform + baseUrl: http://backstage.platform.example.com + backend: + baseUrl: http://backstage.platform.example.com + listen: + port: 7007 + cors: + origin: http://backstage.platform.example.com + organization: + name: Platform Engineering + + auth: + environment: development + providers: + guest: {} + + integrations: + github: + - host: github.com + apps: [] + + catalog: + import: + entityFilename: catalog-info.yaml + locations: + - type: url + target: https://github.com/tukue/simpleAppInfraCode/blob/main/catalog-info.yaml + - type: url + target: https://github.com/tukue/simpleAppInfraCode/blob/main/standardized-path/app/catalog-info.yaml + - type: url + target: https://github.com/tukue/simpleAppInfraCode/blob/main/platform/apps/simple-app/catalog-info.yaml + - type: url + target: https://github.com/tukue/simpleAppInfraCode/blob/main/platform/apps/app-b/catalog-info.yaml + + scaffolder: + locations: + - type: url + target: https://github.com/tukue/simpleAppInfraCode/blob/main/backstage/templates/new-tenant-app/template.yaml + + kubernetes: + serviceLocatorMethod: + type: multiTenant + clusterLocatorMethods: + - type: config + clusters: + - name: in-cluster + url: https://kubernetes.default.svc + authProvider: serviceAccount + skipTLSVerify: true + skipMetricsLookup: true + + destination: + server: https://kubernetes.default.svc + namespace: backstage + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true From c99a87f049c9f26f73e197fc5284a289ec607486 Mon Sep 17 00:00:00 2001 From: tukue Date: Wed, 27 May 2026 22:13:22 +0200 Subject: [PATCH 5/8] fix: replace individual catalog locations with a single glob-based Location entity --- catalog-info.yaml | 9 +++++++++ platform/addons/backstage.yaml | 6 ------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/catalog-info.yaml b/catalog-info.yaml index 29f3a33..8fafa7b 100644 --- a/catalog-info.yaml +++ b/catalog-info.yaml @@ -43,3 +43,12 @@ spec: profile: displayName: App Developers children: [] +--- +apiVersion: backstage.io/v1alpha1 +kind: Location +metadata: + name: all-catalog-files + description: Discovers all catalog-info.yaml files across the repository +spec: + targets: + - ./**/catalog-info.yaml diff --git a/platform/addons/backstage.yaml b/platform/addons/backstage.yaml index c11cade..ce4e7dc 100644 --- a/platform/addons/backstage.yaml +++ b/platform/addons/backstage.yaml @@ -67,12 +67,6 @@ spec: locations: - type: url target: https://github.com/tukue/simpleAppInfraCode/blob/main/catalog-info.yaml - - type: url - target: https://github.com/tukue/simpleAppInfraCode/blob/main/standardized-path/app/catalog-info.yaml - - type: url - target: https://github.com/tukue/simpleAppInfraCode/blob/main/platform/apps/simple-app/catalog-info.yaml - - type: url - target: https://github.com/tukue/simpleAppInfraCode/blob/main/platform/apps/app-b/catalog-info.yaml scaffolder: locations: From 98baa7ad4689f685fdb86b6745c3cde7774fede2 Mon Sep 17 00:00:00 2001 From: tukue Date: Wed, 27 May 2026 22:19:20 +0200 Subject: [PATCH 6/8] feat: add AWS account ID and region as developer inputs in Backstage scaffolder template --- Makefile | 5 +++++ .../${{parameters.appName}}/dev/values.yaml | 8 ++++---- .../${{parameters.appName}}/prod/values.yaml | 8 ++++---- .../${{parameters.appName}}/stage/values.yaml | 8 ++++---- .../templates/new-tenant-app/template.yaml | 20 +++++++++++++------ 5 files changed, 31 insertions(+), 18 deletions(-) diff --git a/Makefile b/Makefile index 7576623..80a40c2 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,9 @@ MAKEFLAGS += --no-print-directory CLUSTER_NAME ?= platform-demo NAMESPACE ?= argocd KIND_IMG ?= kindest/node:v1.30.0 +AWS_ACCOUNT_ID ?= 944684220857 +AWS_REGION ?= eu-north-1 +IMAGE_REPO ?= $(AWS_ACCOUNT_ID).dkr.ecr.$(AWS_REGION).amazonaws.com/app .PHONY: help help: @@ -66,11 +69,13 @@ deploy: @echo "=== Deploying simple-app ===" helm template simple-app-dev standardized-path/app \ -f platform/apps/dev/values.yaml \ + --set image.repository=$(IMAGE_REPO) \ | kubectl apply -f - 2>&1 | grep -v 'unchanged' || true @echo "=== Deploying app-b ===" helm template app-b-dev standardized-path/app \ -f platform/apps/app-b/dev/values.yaml \ + --set image.repository=$(IMAGE_REPO) \ | kubectl apply -f - 2>&1 | grep -v 'unchanged' || true @echo "=== App status ===" diff --git a/backstage/templates/new-tenant-app/skeleton/platform/apps/${{parameters.appName}}/dev/values.yaml b/backstage/templates/new-tenant-app/skeleton/platform/apps/${{parameters.appName}}/dev/values.yaml index 0036c9f..ddf742a 100644 --- a/backstage/templates/new-tenant-app/skeleton/platform/apps/${{parameters.appName}}/dev/values.yaml +++ b/backstage/templates/new-tenant-app/skeleton/platform/apps/${{parameters.appName}}/dev/values.yaml @@ -1,13 +1,13 @@ replicaCount: 1 image: - repository: ${{ parameters.imageRepo }}${{ parameters.appName }} + repository: ${{ imageRepo }}/${{ appName }} tag: "1.0.0-dev.1" ingress: enabled: true hosts: - - host: ${{ parameters.appName }}-dev.example.com + - host: ${{ appName }}-dev.example.com paths: - path: / pathType: Prefix @@ -16,11 +16,11 @@ env: - name: ENVIRONMENT value: "dev" - name: PORT - value: "${{ parameters.port }}" + value: "${{ port }}" externalSecret: enabled: true data: - secretKey: db-password remoteRef: - key: dev/${{ parameters.appName }}/db-password + key: dev/${{ appName }}/db-password diff --git a/backstage/templates/new-tenant-app/skeleton/platform/apps/${{parameters.appName}}/prod/values.yaml b/backstage/templates/new-tenant-app/skeleton/platform/apps/${{parameters.appName}}/prod/values.yaml index 71da639..a1e0d26 100644 --- a/backstage/templates/new-tenant-app/skeleton/platform/apps/${{parameters.appName}}/prod/values.yaml +++ b/backstage/templates/new-tenant-app/skeleton/platform/apps/${{parameters.appName}}/prod/values.yaml @@ -1,13 +1,13 @@ replicaCount: 3 image: - repository: ${{ parameters.imageRepo }}${{ parameters.appName }} + repository: ${{ imageRepo }}/${{ appName }} tag: "1.0.0" ingress: enabled: true hosts: - - host: ${{ parameters.appName }}.example.com + - host: ${{ appName }}.example.com paths: - path: / pathType: Prefix @@ -16,11 +16,11 @@ env: - name: ENVIRONMENT value: "prod" - name: PORT - value: "${{ parameters.port }}" + value: "${{ port }}" externalSecret: enabled: true data: - secretKey: db-password remoteRef: - key: prod/${{ parameters.appName }}/db-password + key: prod/${{ appName }}/db-password diff --git a/backstage/templates/new-tenant-app/skeleton/platform/apps/${{parameters.appName}}/stage/values.yaml b/backstage/templates/new-tenant-app/skeleton/platform/apps/${{parameters.appName}}/stage/values.yaml index f593500..936ef7d 100644 --- a/backstage/templates/new-tenant-app/skeleton/platform/apps/${{parameters.appName}}/stage/values.yaml +++ b/backstage/templates/new-tenant-app/skeleton/platform/apps/${{parameters.appName}}/stage/values.yaml @@ -1,13 +1,13 @@ replicaCount: 2 image: - repository: ${{ parameters.imageRepo }}${{ parameters.appName }} + repository: ${{ imageRepo }}/${{ appName }} tag: "1.0.0-rc1" ingress: enabled: true hosts: - - host: ${{ parameters.appName }}-stage.example.com + - host: ${{ appName }}-stage.example.com paths: - path: / pathType: Prefix @@ -16,11 +16,11 @@ env: - name: ENVIRONMENT value: "stage" - name: PORT - value: "${{ parameters.port }}" + value: "${{ port }}" externalSecret: enabled: true data: - secretKey: db-password remoteRef: - key: stage/${{ parameters.appName }}/db-password + key: stage/${{ appName }}/db-password diff --git a/backstage/templates/new-tenant-app/template.yaml b/backstage/templates/new-tenant-app/template.yaml index f1b462c..6b4d4e4 100644 --- a/backstage/templates/new-tenant-app/template.yaml +++ b/backstage/templates/new-tenant-app/template.yaml @@ -40,13 +40,19 @@ spec: - title: Container Configuration required: - - imageRepo + - awsAccountId + - awsRegion properties: - imageRepo: - title: Container Image Repository + awsAccountId: + title: AWS Account ID type: string - description: ECR or Docker image repository URL - default: 944684220857.dkr.ecr.eu-north-1.amazonaws.com/ + description: 12-digit AWS account number for ECR + pattern: '^\d{12}$' + awsRegion: + title: AWS Region + type: string + description: AWS region for ECR (e.g., eu-north-1, us-east-1) + default: eu-north-1 port: title: Application Port type: integer @@ -61,7 +67,9 @@ spec: values: appName: ${{ parameters.appName }} description: ${{ parameters.description }} - imageRepo: ${{ parameters.imageRepo }} + awsAccountId: ${{ parameters.awsAccountId }} + awsRegion: ${{ parameters.awsRegion }} + imageRepo: ${{ parameters.awsAccountId }}.dkr.ecr.${{ parameters.awsRegion }}.amazonaws.com port: ${{ parameters.port }} owner: ${{ parameters.owner }} From e84ac75b3534e779b5513629449822327c8ae22c Mon Sep 17 00:00:00 2001 From: tukue Date: Wed, 27 May 2026 23:55:20 +0200 Subject: [PATCH 7/8] fix: pin conftest version to v0.58.0 to avoid GitHub API rate limiting failures --- .github/workflows/platform-validate.yml | 4 +- backstage/catalog-ui.html | 77 +++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 backstage/catalog-ui.html diff --git a/.github/workflows/platform-validate.yml b/.github/workflows/platform-validate.yml index 649c8d9..6fd6525 100644 --- a/.github/workflows/platform-validate.yml +++ b/.github/workflows/platform-validate.yml @@ -122,7 +122,7 @@ jobs: - name: Setup conftest run: | - CONFTEST_VERSION=$(curl -sL https://api.github.com/repos/open-policy-agent/conftest/releases/latest | grep tag_name | cut -d'"' -f4) + CONFTEST_VERSION="v0.58.0" wget -q "https://github.com/open-policy-agent/conftest/releases/download/${CONFTEST_VERSION}/conftest_${CONFTEST_VERSION#v}_Linux_x86_64.tar.gz" -O /tmp/conftest.tar.gz tar xzf /tmp/conftest.tar.gz -C /usr/local/bin/ conftest @@ -167,7 +167,7 @@ jobs: - name: Setup conftest run: | - CONFTEST_VERSION=$(curl -sL https://api.github.com/repos/open-policy-agent/conftest/releases/latest | grep tag_name | cut -d'"' -f4) + CONFTEST_VERSION="v0.58.0" wget -q "https://github.com/open-policy-agent/conftest/releases/download/${CONFTEST_VERSION}/conftest_${CONFTEST_VERSION#v}_Linux_x86_64.tar.gz" -O /tmp/conftest.tar.gz tar xzf /tmp/conftest.tar.gz -C /usr/local/bin/ conftest diff --git a/backstage/catalog-ui.html b/backstage/catalog-ui.html new file mode 100644 index 0000000..62b4dbf --- /dev/null +++ b/backstage/catalog-ui.html @@ -0,0 +1,77 @@ + +Backstage Catalog + +

Backstage Catalog

+

Internal Developer Platform — Entity overview

+ +
+
9
Entities
+
1
Templates
+
0
Errors
+
+
Entities
+
NameKindOwnerSystemDescription
simple-app-infra-codeComponentplatform-teamplatformInternal Developer Platform for EKS and OpenShift
platformSystemplatform-teamInternal Developer Platform providing golden path, GitOps, and observability
platform-teamGroupPlatform engineering team
app-developersGroupApplication development teams
all-catalog-filesLocationDiscovers all catalog-info.yaml files across the repository
app-chartComponentplatform-teamplatformGolden path Helm chart — the tenant contract for deploying services on the platform
tenant-contractAPIplatform-teamplatformContract defining what app teams provide and what the platform guarantees
simple-appComponentapp-developersplatformDemo tenant application deployed via the platform golden path
app-bComponentapp-developersplatformSecond tenant application proving contract reusability across dev/stage/prod
+
Dependency Graph
simple-app-infra-code
Component
⊢ providestenant-contract
app-chart
Component
⊢ providestenant-contract
simple-app
Component
→ dependsOnapp-chart
app-b
Component
→ dependsOnapp-chart
+
Scaffolder Templates

New Tenant Application

Scaffold a new application using the platform's golden path Helm chart

Parameters:
[
+  {
+    "appName": {
+      "title": "Application Name",
+      "type": "string",
+      "description": "Unique name for the application (e.g., app-c)",
+      "pattern": "^[a-z][a-z0-9-]*$"
+    },
+    "description": {
+      "title": "Description",
+      "type": "string",
+      "description": "Purpose of the application"
+    },
+    "owner": {
+      "title": "Owner",
+      "type": "string",
+      "description": "Backstage Group or User that owns this app",
+      "default": "app-developers",
+      "ui:field": "OwnerPicker",
+      "ui:options": {
+        "allowedKinds": [
+          "Group"
+        ]
+      }
+    }
+  },
+  {
+    "awsAccountId": {
+      "title": "AWS Account ID",
+      "type": "string",
+      "description": "12-digit AWS account number for ECR",
+      "pattern": "^\\d{12}$"
+    },
+    "awsRegion": {
+      "title": "AWS Region",
+      "type": "string",
+      "description": "AWS region for ECR (e.g., eu-north-1, us-east-1)",
+      "default": "eu-north-1"
+    },
+    "port": {
+      "title": "Application Port",
+      "type": "integer",
+      "default": 8080
+    }
+  }
+]
Steps:
  fetch-skeleton: Fetch Skeleton (fetch:template)
+  publish: Publish to GitHub (publish:github)
+  register: Register in Backstage (catalog:register)
+
Catalog Files
📄 catalog-info.yaml
📄 standardized-path/app/catalog-info.yaml
📄 platform/apps/simple-app/catalog-info.yaml
📄 platform/apps/app-b/catalog-info.yaml
📄 backstage/templates/new-tenant-app/template.yaml
\ No newline at end of file From b21f96d446f8e4daadbbd957d820563b0b347bbb Mon Sep 17 00:00:00 2001 From: tukue Date: Thu, 28 May 2026 08:12:18 +0200 Subject: [PATCH 8/8] fix: pin conftest to v0.68.2 for OPA 1.x 'contains' keyword support --- .github/workflows/platform-validate.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/platform-validate.yml b/.github/workflows/platform-validate.yml index 6fd6525..3ea1580 100644 --- a/.github/workflows/platform-validate.yml +++ b/.github/workflows/platform-validate.yml @@ -122,7 +122,7 @@ jobs: - name: Setup conftest run: | - CONFTEST_VERSION="v0.58.0" + CONFTEST_VERSION="v0.68.2" wget -q "https://github.com/open-policy-agent/conftest/releases/download/${CONFTEST_VERSION}/conftest_${CONFTEST_VERSION#v}_Linux_x86_64.tar.gz" -O /tmp/conftest.tar.gz tar xzf /tmp/conftest.tar.gz -C /usr/local/bin/ conftest @@ -167,7 +167,7 @@ jobs: - name: Setup conftest run: | - CONFTEST_VERSION="v0.58.0" + CONFTEST_VERSION="v0.68.2" wget -q "https://github.com/open-policy-agent/conftest/releases/download/${CONFTEST_VERSION}/conftest_${CONFTEST_VERSION#v}_Linux_x86_64.tar.gz" -O /tmp/conftest.tar.gz tar xzf /tmp/conftest.tar.gz -C /usr/local/bin/ conftest