@gambi97/keel-cli 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -42
- package/dist/bootstrap/github.js +15 -17
- package/dist/bootstrap/infisical.js +25 -10
- package/dist/config.js +118 -14
- package/dist/generate.js +54 -4
- package/dist/index.js +41 -13
- package/dist/prompts.js +35 -8
- package/dist/ui.js +15 -5
- package/package.json +1 -1
- package/templates/.github/scripts/sync-secrets.sh +65 -0
- package/templates/.github/workflows/terraform-drift.yml +1 -1
- package/templates/.github/workflows/terraform-plan.yml +1 -1
- package/templates/README.md +53 -42
- package/templates/_partials/apply-header.yml +33 -0
- package/templates/_partials/apply-job.yml +34 -0
- package/templates/env.tfvars +8 -0
- package/templates/main.tf +2 -1
- package/templates/modules/app_stack/main.tf +34 -0
- package/templates/modules/app_stack/outputs.tf +18 -0
- package/templates/modules/app_stack/variables.tf +5 -0
- package/templates/outputs.tf +22 -0
- package/templates/variables.tf +8 -2
- package/templates/.github/scripts/sync-database-url.sh +0 -49
- package/templates/.github/workflows/terraform-apply.yml +0 -103
- package/templates/prod.tfvars +0 -7
- package/templates/staging.tfvars +0 -7
|
@@ -21,3 +21,21 @@ output "database_url" {
|
|
|
21
21
|
output "container_namespace_id" {
|
|
22
22
|
value = scaleway_container_namespace.this.id
|
|
23
23
|
}
|
|
24
|
+
|
|
25
|
+
output "object_bucket_name" {
|
|
26
|
+
value = var.enable_object_storage ? scaleway_object_bucket.files[0].name : null
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
output "object_bucket_endpoint" {
|
|
30
|
+
value = var.enable_object_storage ? "https://s3.${var.region}.scw.cloud" : null
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
output "object_storage_access_key" {
|
|
34
|
+
value = var.enable_object_storage ? scaleway_iam_api_key.storage[0].access_key : null
|
|
35
|
+
sensitive = true
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
output "object_storage_secret_key" {
|
|
39
|
+
value = var.enable_object_storage ? scaleway_iam_api_key.storage[0].secret_key : null
|
|
40
|
+
sensitive = true
|
|
41
|
+
}
|
|
@@ -47,6 +47,11 @@ variable "enable_basic_auth" {
|
|
|
47
47
|
type = bool
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
variable "enable_object_storage" {
|
|
51
|
+
type = bool
|
|
52
|
+
default = false
|
|
53
|
+
}
|
|
54
|
+
|
|
50
55
|
variable "secret_environment_variables" {
|
|
51
56
|
description = "Secrets injected into the container, sourced from Infisical."
|
|
52
57
|
type = map(string)
|
package/templates/outputs.tf
CHANGED
|
@@ -18,3 +18,25 @@ output "container_namespace_id" {
|
|
|
18
18
|
description = "Scaleway Containers namespace ID."
|
|
19
19
|
value = module.app_stack.container_namespace_id
|
|
20
20
|
}
|
|
21
|
+
|
|
22
|
+
output "object_bucket_name" {
|
|
23
|
+
description = "Object Storage bucket for application files (null unless enabled)."
|
|
24
|
+
value = module.app_stack.object_bucket_name
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# The complete set of secrets the pipeline pushes to Infisical after each apply:
|
|
28
|
+
# always DATABASE_URL, plus the S3_* coordinates when Object Storage is enabled.
|
|
29
|
+
output "infisical_secrets" {
|
|
30
|
+
description = "Secrets synced to Infisical after each apply."
|
|
31
|
+
sensitive = true
|
|
32
|
+
value = merge(
|
|
33
|
+
{ DATABASE_URL = module.app_stack.database_url },
|
|
34
|
+
var.enable_object_storage ? {
|
|
35
|
+
S3_BUCKET = module.app_stack.object_bucket_name
|
|
36
|
+
S3_ENDPOINT = module.app_stack.object_bucket_endpoint
|
|
37
|
+
S3_REGION = var.region
|
|
38
|
+
S3_ACCESS_KEY = module.app_stack.object_storage_access_key
|
|
39
|
+
S3_SECRET_KEY = module.app_stack.object_storage_secret_key
|
|
40
|
+
} : {}
|
|
41
|
+
)
|
|
42
|
+
}
|
package/templates/variables.tf
CHANGED
|
@@ -13,8 +13,8 @@ variable "environment" {
|
|
|
13
13
|
type = string
|
|
14
14
|
|
|
15
15
|
validation {
|
|
16
|
-
condition = contains(
|
|
17
|
-
error_message = "environment must be
|
|
16
|
+
condition = contains(__ENV_SLUGS_TF__, var.environment)
|
|
17
|
+
error_message = "environment must be one of the configured environments."
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -72,6 +72,12 @@ variable "enable_basic_auth" {
|
|
|
72
72
|
default = false
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
variable "enable_object_storage" {
|
|
76
|
+
description = "When true, provision an Object Storage bucket for the app plus a dedicated least-privilege credential; its coordinates are synced to Infisical as S3_*."
|
|
77
|
+
type = bool
|
|
78
|
+
default = false
|
|
79
|
+
}
|
|
80
|
+
|
|
75
81
|
variable "infisical_host" {
|
|
76
82
|
description = "Infisical instance URL."
|
|
77
83
|
type = string
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Pushes the ready-to-use database connection string (dedicated IAM
|
|
3
|
-
# credential included) to Infisical as DATABASE_URL.
|
|
4
|
-
# Usage: sync-database-url.sh <staging|prod>
|
|
5
|
-
# Expects: INFISICAL_HOST, INFISICAL_CLIENT_ID, INFISICAL_CLIENT_SECRET,
|
|
6
|
-
# INFISICAL_PROJECT_ID in the environment; terraform init already run.
|
|
7
|
-
set -euo pipefail
|
|
8
|
-
|
|
9
|
-
ENVIRONMENT="${1:?usage: sync-database-url.sh <staging|prod>}"
|
|
10
|
-
|
|
11
|
-
DB_URL="$(terraform output -raw database_url)"
|
|
12
|
-
if [ -z "$DB_URL" ]; then
|
|
13
|
-
echo "No database_url in outputs, skipping Infisical sync."
|
|
14
|
-
exit 0
|
|
15
|
-
fi
|
|
16
|
-
echo "::add-mask::$DB_URL"
|
|
17
|
-
|
|
18
|
-
ACCESS_TOKEN="$(curl -sS --fail-with-body -X POST \
|
|
19
|
-
"$INFISICAL_HOST/api/v1/auth/universal-auth/login" \
|
|
20
|
-
-H "Content-Type: application/json" \
|
|
21
|
-
-d "{\"clientId\":\"$INFISICAL_CLIENT_ID\",\"clientSecret\":\"$INFISICAL_CLIENT_SECRET\"}" \
|
|
22
|
-
| jq -r .accessToken)"
|
|
23
|
-
echo "::add-mask::$ACCESS_TOKEN"
|
|
24
|
-
|
|
25
|
-
payload="$(jq -n \
|
|
26
|
-
--arg workspaceId "$INFISICAL_PROJECT_ID" \
|
|
27
|
-
--arg environment "$ENVIRONMENT" \
|
|
28
|
-
--arg value "$DB_URL" \
|
|
29
|
-
'{workspaceId: $workspaceId, environment: $environment, secretPath: "/", secretValue: $value, type: "shared"}')"
|
|
30
|
-
|
|
31
|
-
# Update the placeholder seeded at bootstrap; create the secret if missing.
|
|
32
|
-
status="$(curl -sS -o /dev/null -w "%{http_code}" -X PATCH \
|
|
33
|
-
"$INFISICAL_HOST/api/v3/secrets/raw/DATABASE_URL" \
|
|
34
|
-
-H "Authorization: Bearer $ACCESS_TOKEN" \
|
|
35
|
-
-H "Content-Type: application/json" \
|
|
36
|
-
-d "$payload")"
|
|
37
|
-
|
|
38
|
-
if [ "$status" = "404" ]; then
|
|
39
|
-
curl -sS --fail-with-body -o /dev/null -X POST \
|
|
40
|
-
"$INFISICAL_HOST/api/v3/secrets/raw/DATABASE_URL" \
|
|
41
|
-
-H "Authorization: Bearer $ACCESS_TOKEN" \
|
|
42
|
-
-H "Content-Type: application/json" \
|
|
43
|
-
-d "$payload"
|
|
44
|
-
elif [ "$status" -ge 300 ]; then
|
|
45
|
-
echo "Failed to update DATABASE_URL in Infisical (HTTP $status)" >&2
|
|
46
|
-
exit 1
|
|
47
|
-
fi
|
|
48
|
-
|
|
49
|
-
echo "DATABASE_URL synced to Infisical ($ENVIRONMENT)."
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
name: Terraform apply
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: [main]
|
|
6
|
-
|
|
7
|
-
permissions:
|
|
8
|
-
contents: read
|
|
9
|
-
|
|
10
|
-
concurrency:
|
|
11
|
-
group: terraform-apply
|
|
12
|
-
cancel-in-progress: false
|
|
13
|
-
|
|
14
|
-
env:
|
|
15
|
-
# The Scaleway keys double as S3 credentials for the state backend, so the
|
|
16
|
-
# same secret feeds both variables: one credential, one place to rotate.
|
|
17
|
-
AWS_ACCESS_KEY_ID: ${{ secrets.SCW_ACCESS_KEY }}
|
|
18
|
-
AWS_SECRET_ACCESS_KEY: ${{ secrets.SCW_SECRET_KEY }}
|
|
19
|
-
SCW_ACCESS_KEY: ${{ secrets.SCW_ACCESS_KEY }}
|
|
20
|
-
SCW_SECRET_KEY: ${{ secrets.SCW_SECRET_KEY }}
|
|
21
|
-
SCW_DEFAULT_PROJECT_ID: ${{ secrets.SCW_DEFAULT_PROJECT_ID }}
|
|
22
|
-
SCW_DEFAULT_ORGANIZATION_ID: ${{ secrets.SCW_DEFAULT_ORGANIZATION_ID }}
|
|
23
|
-
INFISICAL_CLIENT_ID: ${{ secrets.INFISICAL_CLIENT_ID }}
|
|
24
|
-
INFISICAL_CLIENT_SECRET: ${{ secrets.INFISICAL_CLIENT_SECRET }}
|
|
25
|
-
INFISICAL_PROJECT_ID: ${{ vars.INFISICAL_PROJECT_ID }}
|
|
26
|
-
INFISICAL_HOST: ${{ vars.INFISICAL_HOST }}
|
|
27
|
-
TF_VAR_infisical_client_id: ${{ secrets.INFISICAL_CLIENT_ID }}
|
|
28
|
-
TF_VAR_infisical_client_secret: ${{ secrets.INFISICAL_CLIENT_SECRET }}
|
|
29
|
-
TF_VAR_infisical_project_id: ${{ vars.INFISICAL_PROJECT_ID }}
|
|
30
|
-
TF_VAR_infisical_host: ${{ vars.INFISICAL_HOST }}
|
|
31
|
-
TF_IN_AUTOMATION: "true"
|
|
32
|
-
|
|
33
|
-
jobs:
|
|
34
|
-
apply-staging:
|
|
35
|
-
name: apply (staging)
|
|
36
|
-
runs-on: ubuntu-latest
|
|
37
|
-
environment: staging
|
|
38
|
-
steps:
|
|
39
|
-
- uses: actions/checkout@v4
|
|
40
|
-
|
|
41
|
-
- uses: hashicorp/setup-terraform@v3
|
|
42
|
-
with:
|
|
43
|
-
terraform_version: "~1.12"
|
|
44
|
-
terraform_wrapper: false
|
|
45
|
-
|
|
46
|
-
- name: Write backend config
|
|
47
|
-
run: |
|
|
48
|
-
cat > backend.hcl <<EOF
|
|
49
|
-
bucket = "${{ vars.TF_STATE_BUCKET }}"
|
|
50
|
-
region = "${{ vars.SCW_REGION }}"
|
|
51
|
-
endpoints = {
|
|
52
|
-
s3 = "https://s3.${{ vars.SCW_REGION }}.scw.cloud"
|
|
53
|
-
}
|
|
54
|
-
EOF
|
|
55
|
-
|
|
56
|
-
- name: Terraform init
|
|
57
|
-
run: terraform init -input=false -backend-config=backend.hcl
|
|
58
|
-
|
|
59
|
-
- name: Select workspace
|
|
60
|
-
run: terraform workspace select -or-create staging
|
|
61
|
-
|
|
62
|
-
- name: Terraform apply
|
|
63
|
-
run: terraform apply -input=false -auto-approve -var-file=staging.tfvars
|
|
64
|
-
|
|
65
|
-
- name: Sync DATABASE_URL to Infisical
|
|
66
|
-
run: ./.github/scripts/sync-database-url.sh staging
|
|
67
|
-
|
|
68
|
-
apply-prod:
|
|
69
|
-
name: apply (prod)
|
|
70
|
-
runs-on: ubuntu-latest
|
|
71
|
-
needs: apply-staging
|
|
72
|
-
# The "production" GitHub Environment carries a required-reviewer rule,
|
|
73
|
-
# so this job waits for manual approval.
|
|
74
|
-
environment: production
|
|
75
|
-
steps:
|
|
76
|
-
- uses: actions/checkout@v4
|
|
77
|
-
|
|
78
|
-
- uses: hashicorp/setup-terraform@v3
|
|
79
|
-
with:
|
|
80
|
-
terraform_version: "~1.12"
|
|
81
|
-
terraform_wrapper: false
|
|
82
|
-
|
|
83
|
-
- name: Write backend config
|
|
84
|
-
run: |
|
|
85
|
-
cat > backend.hcl <<EOF
|
|
86
|
-
bucket = "${{ vars.TF_STATE_BUCKET }}"
|
|
87
|
-
region = "${{ vars.SCW_REGION }}"
|
|
88
|
-
endpoints = {
|
|
89
|
-
s3 = "https://s3.${{ vars.SCW_REGION }}.scw.cloud"
|
|
90
|
-
}
|
|
91
|
-
EOF
|
|
92
|
-
|
|
93
|
-
- name: Terraform init
|
|
94
|
-
run: terraform init -input=false -backend-config=backend.hcl
|
|
95
|
-
|
|
96
|
-
- name: Select workspace
|
|
97
|
-
run: terraform workspace select -or-create prod
|
|
98
|
-
|
|
99
|
-
- name: Terraform apply
|
|
100
|
-
run: terraform apply -input=false -auto-approve -var-file=prod.tfvars
|
|
101
|
-
|
|
102
|
-
- name: Sync DATABASE_URL to Infisical
|
|
103
|
-
run: ./.github/scripts/sync-database-url.sh prod
|
package/templates/prod.tfvars
DELETED
package/templates/staging.tfvars
DELETED