Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
506212b
fix: support 'null' workaround as optional input in Panel
grubmeshi Mar 16, 2026
84092ae
feat(git-repository): support action_secrets, refactor ske starterkit…
grubmeshi Mar 16, 2026
e970c7c
feat(ske): register forgejo connector as tenant building block
grubmeshi Mar 16, 2026
656d321
feat(ske): write stage-specific forgejo repo secrets
grubmeshi Mar 16, 2026
dafa0df
feat(ske): compose tenant forgejo connectors in starterkit
grubmeshi Mar 16, 2026
b4b7b76
feat(ske): pass git repository definition uuid to starterkit
grubmeshi Mar 16, 2026
56326a4
feat(stackit): expose git-repository definition uuid output
grubmeshi Mar 16, 2026
1236697
fix(ske): use harbor vars for image pull secret auth
grubmeshi Mar 16, 2026
6389548
fix(ske): declare supported platform for tenant connector
grubmeshi Mar 16, 2026
32fb777
refactor(ske): drop obsolete git repository definition input
grubmeshi Mar 16, 2026
0cd74b4
chore(ske): commit pending forgejo connector adjustments
grubmeshi Mar 16, 2026
3a3a792
refactor(ske): remove unused additional connector env vars
grubmeshi Mar 16, 2026
d4eb0ab
fix(ske): restore kubeconfig stub and file input wiring
grubmeshi Mar 16, 2026
2ca4690
feat(ske): rework forgejo connector kubeconfig wiring
grubmeshi Mar 17, 2026
86f793b
fix: get ske/starterkit tg apply to work
grubmeshi Mar 17, 2026
76af2aa
fix(ske): align repository_id wiring and platform support
grubmeshi Mar 17, 2026
b836d1e
feat(ske): simplify kubeconfig flow and align forgejo env inputs
grubmeshi Mar 17, 2026
9864f8d
feat(ske): align composition with BBD output object
grubmeshi Mar 18, 2026
e8b1d42
feat(ske): simplify forgejo connector kubeconfig input
grubmeshi Mar 18, 2026
89ba134
feat(ske): configure default SA to use harbor pull secret
grubmeshi Mar 18, 2026
b1aec4b
feat(ske): add action variables and trigger pipeline dispatch
grubmeshi Mar 18, 2026
0583aac
chore: ignore python build files
grubmeshi Mar 18, 2026
176571e
feat(ske): refactor forgejo connector workflow trigger wiring
grubmeshi Mar 18, 2026
37b3b61
feat: ske-starterkit workflow trigger based on stage
j0g3sc Mar 18, 2026
40adab2
feat(ske): pass only_stage to forgejo workflow
grubmeshi Mar 18, 2026
c48db02
feat(ske): generalize connector secrets and stage input
grubmeshi Mar 18, 2026
3b73eb3
chore: change restapi object in forgejo connector
j0g3sc Mar 18, 2026
66a179e
feat(ske): align connector and CODE input wiring
grubmeshi Mar 18, 2026
3772424
fix(ske): align forgejo variable API usage and naming
grubmeshi Mar 18, 2026
28ef849
fix: simpler Connector name
grubmeshi Mar 18, 2026
81eadb9
fix(ske): harden forgejo workflow polling compatibility
grubmeshi Mar 18, 2026
d2cde62
harden workflow trigger script with parallel-dispatch safety
grubmeshi Mar 18, 2026
e4e1299
add stage-specific app hostname to forgejo connector
grubmeshi Mar 18, 2026
6d7bd1f
propagate stage hostnames through SKE starterkit
grubmeshi Mar 18, 2026
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ yarn-error.log*
*tfvars*
.terraform.lock.hcl
.env
__pycache__/
*.pyc

# Generated assets
website/public/assets/building-block-logos/
Expand Down
38 changes: 0 additions & 38 deletions modules/ske/forgejo-connector/backplane/README.md

This file was deleted.

11 changes: 0 additions & 11 deletions modules/ske/forgejo-connector/backplane/main.tf

This file was deleted.

30 changes: 0 additions & 30 deletions modules/ske/forgejo-connector/backplane/outputs.tf

This file was deleted.

28 changes: 0 additions & 28 deletions modules/ske/forgejo-connector/backplane/variables.tf

This file was deleted.

10 changes: 0 additions & 10 deletions modules/ske/forgejo-connector/backplane/versions.tf

This file was deleted.

21 changes: 14 additions & 7 deletions modules/ske/forgejo-connector/buildingblock/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ the backplane module's `config_tf` output.

| Name | Version |
|------|---------|
| <a name="requirement_external"></a> [external](#requirement\_external) | ~> 2.3.0 |
| <a name="requirement_kubernetes"></a> [kubernetes](#requirement\_kubernetes) | 2.35.1 |
| <a name="requirement_restapi"></a> [restapi](#requirement\_restapi) | 3.0.0 |

## Modules

Expand All @@ -55,27 +57,32 @@ No modules.

| Name | Type |
|------|------|
| [forgejo_repository_action_secret.additional](https://registry.terraform.io/providers/svalabs/forgejo/latest/docs/resources/repository_action_secret) | resource |
| [forgejo_repository_action_secret.container_registry](https://registry.terraform.io/providers/svalabs/forgejo/latest/docs/resources/repository_action_secret) | resource |
| [forgejo_repository_action_secret.kubeconfig](https://registry.terraform.io/providers/svalabs/forgejo/latest/docs/resources/repository_action_secret) | resource |
| [forgejo_repository_action_secret.this](https://registry.terraform.io/providers/svalabs/forgejo/latest/docs/resources/repository_action_secret) | resource |
| [kubernetes_cluster_role.clusterissuer_reader](https://registry.terraform.io/providers/hashicorp/kubernetes/2.35.1/docs/resources/cluster_role) | resource |
| [kubernetes_cluster_role_binding.forgejo_actions_clusterissuer_access](https://registry.terraform.io/providers/hashicorp/kubernetes/2.35.1/docs/resources/cluster_role_binding) | resource |
| [kubernetes_default_service_account.namespace_default](https://registry.terraform.io/providers/hashicorp/kubernetes/2.35.1/docs/resources/default_service_account) | resource |
| [kubernetes_role_binding.forgejo_actions](https://registry.terraform.io/providers/hashicorp/kubernetes/2.35.1/docs/resources/role_binding) | resource |
| [kubernetes_secret.additional](https://registry.terraform.io/providers/hashicorp/kubernetes/2.35.1/docs/resources/secret) | resource |
| [kubernetes_secret.forgejo_actions](https://registry.terraform.io/providers/hashicorp/kubernetes/2.35.1/docs/resources/secret) | resource |
| [kubernetes_secret.image_pull](https://registry.terraform.io/providers/hashicorp/kubernetes/2.35.1/docs/resources/secret) | resource |
| [kubernetes_service_account.forgejo_actions](https://registry.terraform.io/providers/hashicorp/kubernetes/2.35.1/docs/resources/service_account) | resource |
| [forgejo_repository.this](https://registry.terraform.io/providers/svalabs/forgejo/latest/docs/data-sources/repository) | data source |
| [random_string.clusterissuer_reader_name_suffix](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/string) | resource |
| [restapi_object.action_variable](https://registry.terraform.io/providers/Mastercard/restapi/3.0.0/docs/resources/object) | resource |
| [terraform_data.await_pipeline_workflow](https://registry.terraform.io/providers/hashicorp/terraform/latest/docs/resources/data) | resource |
| [external_external.repository_context](https://registry.terraform.io/providers/hashicorp/external/latest/docs/data-sources/external) | data source |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_additional_environment_variables"></a> [additional\_environment\_variables](#input\_additional\_environment\_variables) | Map of additional environment variable key/value pairs to set as Forgejo repository action secrets. | `map(string)` | `{}` | no |
| <a name="input_forgejo_repository_name"></a> [forgejo\_repository\_name](#input\_forgejo\_repository\_name) | The name of the Forgejo repository. | `string` | n/a | yes |
| <a name="input_forgejo_repository_owner"></a> [forgejo\_repository\_owner](#input\_forgejo\_repository\_owner) | The owner of the Forgejo repository. | `string` | n/a | yes |
| <a name="input_additional_kubernetes_secrets"></a> [additional\_kubernetes\_secrets](#input\_additional\_kubernetes\_secrets) | Additional Kubernetes secrets to create in the tenant namespace. Map keys are secret names, values are secret data maps. | `map(map(string))` | `{}` | no |
| <a name="input_app_hostname"></a> [app\_hostname](#input\_app\_hostname) | Public application hostname for this stage (used by deploy workflow and ingress). | `string` | n/a | yes |
| <a name="input_harbor_host"></a> [harbor\_host](#input\_harbor\_host) | The URL of the Harbor registry. | `string` | `"https://registry.onstackit.cloud"` | no |
| <a name="input_harbor_password"></a> [harbor\_password](#input\_harbor\_password) | The password for the Harbor registry. | `string` | n/a | yes |
| <a name="input_harbor_username"></a> [harbor\_username](#input\_harbor\_username) | The username for the Harbor registry. | `string` | n/a | yes |
| <a name="input_namespace"></a> [namespace](#input\_namespace) | Associated namespace in kubernetes cluster. | `string` | n/a | yes |
| <a name="input_repository_id"></a> [repository\_id](#input\_repository\_id) | The ID of the Forgejo repository. | `number` | n/a | yes |
| <a name="input_stage"></a> [stage](#input\_stage) | Deployment stage used for Forgejo workflow dispatch and action secret naming. | `string` | n/a | yes |

## Outputs

Expand Down
138 changes: 97 additions & 41 deletions modules/ske/forgejo-connector/buildingblock/forgejo.tf
Original file line number Diff line number Diff line change
@@ -1,55 +1,111 @@
provider "forgejo" {
# configured via env variables FORGEJO_HOST, FORGEJO_API_TOKEN
}

provider "restapi" {
uri = data.external.repository_context.result.forgejo_host
write_returns_object = false

headers = {
Authorization = "token ${data.external.repository_context.result.forgejo_api_token}"
Content-Type = "application/json"
}
}

data "external" "repository_context" {
program = ["python3", "${path.module}/get_forgejo_repository_context.py"]

query = {
FORGEJO_REPOSITORY_ID = tostring(var.repository_id)
}
}

locals {
kubeconfig_user = {
users = [
{
name = kubernetes_service_account.forgejo_actions.metadata[0].name
user = {
"token" = kubernetes_secret.forgejo_actions.data.token
repository_owner = data.external.repository_context.result.owner
repository_name = data.external.repository_context.result.name

action_secret = {
"KUBECONFIG_${upper(var.stage)}" = yamlencode(merge(local.kubeconfig, {
current-context = local.kubeconfig_cluster_name

users = [
{
name = kubernetes_service_account.forgejo_actions.metadata[0].name
user = {
"token" = kubernetes_secret.forgejo_actions.data.token
}
}
}
]

contexts = [
{
name = "stackit_k8s"
context = {
cluster = "stackit_k8s"
namespace = var.namespace
user = kubernetes_service_account.forgejo_actions.metadata[0].name
]

contexts = [
{
name = local.kubeconfig_cluster_name
context = {
cluster = local.kubeconfig_cluster_name
namespace = var.namespace
user = kubernetes_service_account.forgejo_actions.metadata[0].name
}
}
}
]
]
}))
}

action_variable = {
"K8S_NAMESPACE_${upper(var.stage)}" = var.namespace
"APP_HOSTNAME_${upper(var.stage)}" = var.app_hostname
}
kubeconfig = merge(local.stackit_kubeconfig_stub, local.kubeconfig_user)
}

data "forgejo_repository" "this" {
name = var.forgejo_repository_name
owner = var.forgejo_repository_owner
resource "forgejo_repository_action_secret" "this" {
for_each = local.action_secret

repository_id = var.repository_id
name = each.key
data = each.value
}

resource "forgejo_repository_action_secret" "kubeconfig" {
repository_id = data.forgejo_repository.this.id
name = "KUBECONFIG"
data = yamlencode(local.kubeconfig)
resource "restapi_object" "action_variable" {
for_each = local.action_variable

path = "/api/v1/repos/${local.repository_owner}/${local.repository_name}/actions/variables/${each.key}"
id_attribute = "name"
object_id = each.key
update_method = "PUT"
data = jsonencode({
value = each.value
})
ignore_server_additions = true
}

resource "forgejo_repository_action_secret" "container_registry" {
for_each = {
HOST = var.harbor_host
USERNAME = var.harbor_username
PASSWORD = var.harbor_password
}
resource "terraform_data" "await_pipeline_workflow" {
depends_on = [
forgejo_repository_action_secret.this,
restapi_object.action_variable,
]

repository_id = data.forgejo_repository.this.id
name = "STACKIT_HARBOR_${each.key}"
data = each.value
triggers_replace = [
sha256(file("${path.module}/trigger_and_await_forgejo_workflow.py")),
nonsensitive(sha256(jsonencode(local.action_secret))),
sha256(jsonencode(local.action_variable)),
]

provisioner "local-exec" {
command = "${path.module}/trigger_and_await_forgejo_workflow.py"
environment = {
REPOSITORY_ID = tostring(var.repository_id)
WORKFLOW_NAME = "pipeline.yaml"
WORKFLOW_ONLY_STAGE = var.stage
EXPECTED_WORKFLOW_TASK_NAME = "deploy_${var.stage}"
}
}
}

resource "forgejo_repository_action_secret" "additional" {
for_each = var.additional_environment_variables
moved {
from = forgejo_repository_action_secret.action_secrets
to = forgejo_repository_action_secret.this
}

repository_id = data.forgejo_repository.this.id
name = each.key
data = each.value
}
moved {
from = restapi_object.action_variables
to = restapi_object.action_variable
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env python3

import json
import os
import sys
import urllib.request


def normalize_host(raw_host: str) -> str:
host = raw_host.strip()
if not host.startswith(("https://", "http://")):
host = f"https://{host}"
return host.rstrip("/")


def main() -> None:
query = json.loads(sys.stdin.read())

forgejo_host = normalize_host(os.environ["FORGEJO_HOST"])
forgejo_api_token = os.environ["FORGEJO_API_TOKEN"]
repository_id = query["FORGEJO_REPOSITORY_ID"]

req = urllib.request.Request(
f"{forgejo_host}/api/v1/repositories/{repository_id}",
headers={"Authorization": f"token {forgejo_api_token}", "Content-Type": "application/json"},
method="GET",
)
with urllib.request.urlopen(req, timeout=30) as resp:
payload = json.loads(resp.read().decode("utf-8"))

print(
json.dumps(
{
"forgejo_host": forgejo_host,
"forgejo_api_token": forgejo_api_token,
"owner": payload["owner"]["username"],
"name": payload["name"],
"default_branch": payload.get("default_branch", "main"),
}
)
)


if __name__ == "__main__":
main()
Loading
Loading