From 9cf79bb6f8fc7817cb35612379b851b11e479ade Mon Sep 17 00:00:00 2001 From: Dmitrii Creed Date: Thu, 7 May 2026 16:15:28 +0400 Subject: [PATCH 01/11] ci: add security-scan + semgrep + dependabot Wires this repo to the org-wide reusable workflows in simple-container-com/actions: - security-scan: TruffleHog (secrets) + Syft/CycloneDX SBOM + Trivy + Grype, sticky PR comment, status gate - semgrep: SC custom ruleset + optional consumer rules / registry packs, sticky PR comment, status gate Both follow the GitHub Security Lab 'preventing pwn requests' split: scan jobs run in pull_request context (read-only token, no secrets, fork-PR safe); comment posting lives in a workflow_run-triggered job that never reads PR code. Adds .github/dependabot.yml with weekly bumps for both github-actions and gomod ecosystems, with minor+patch grouped to reduce PR noise. --- .github/dependabot.yml | 40 +++++++++++++++++++++ .github/workflows/security-scan-comment.yml | 15 ++++++++ .github/workflows/security-scan.yml | 14 ++++++++ .github/workflows/semgrep-comment.yml | 15 ++++++++ .github/workflows/semgrep.yml | 14 ++++++++ 5 files changed, 98 insertions(+) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/security-scan-comment.yml create mode 100644 .github/workflows/security-scan.yml create mode 100644 .github/workflows/semgrep-comment.yml create mode 100644 .github/workflows/semgrep.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..3551bab2 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,40 @@ +version: 2 + +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + day: monday + open-pull-requests-limit: 5 + labels: + - dependencies + - github-actions + commit-message: + prefix: deps + include: scope + groups: + actions-minor-and-patch: + applies-to: version-updates + update-types: + - minor + - patch + + - package-ecosystem: gomod + directory: / + schedule: + interval: weekly + day: monday + open-pull-requests-limit: 10 + labels: + - dependencies + - go + commit-message: + prefix: deps + include: scope + groups: + gomod-minor-and-patch: + applies-to: version-updates + update-types: + - minor + - patch diff --git a/.github/workflows/security-scan-comment.yml b/.github/workflows/security-scan-comment.yml new file mode 100644 index 00000000..ddcf0924 --- /dev/null +++ b/.github/workflows/security-scan-comment.yml @@ -0,0 +1,15 @@ +name: Security Scan Comment +on: + workflow_run: + workflows: ["Security Scan"] + types: [completed] +permissions: + pull-requests: write + actions: read +jobs: + comment: + if: github.event.workflow_run.event == 'pull_request' + uses: simple-container-com/actions/.github/workflows/security-scan-comment.yml@main + permissions: + pull-requests: write + actions: read diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml new file mode 100644 index 00000000..1adaaf2f --- /dev/null +++ b/.github/workflows/security-scan.yml @@ -0,0 +1,14 @@ +name: Security Scan +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: +permissions: + contents: read +jobs: + security: + uses: simple-container-com/actions/.github/workflows/security-scan.yml@main + permissions: + contents: read diff --git a/.github/workflows/semgrep-comment.yml b/.github/workflows/semgrep-comment.yml new file mode 100644 index 00000000..9b524ad7 --- /dev/null +++ b/.github/workflows/semgrep-comment.yml @@ -0,0 +1,15 @@ +name: Semgrep Comment +on: + workflow_run: + workflows: ["Semgrep"] + types: [completed] +permissions: + pull-requests: write + actions: read +jobs: + comment: + if: github.event.workflow_run.event == 'pull_request' + uses: simple-container-com/actions/.github/workflows/semgrep-comment.yml@main + permissions: + pull-requests: write + actions: read diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml new file mode 100644 index 00000000..468f6beb --- /dev/null +++ b/.github/workflows/semgrep.yml @@ -0,0 +1,14 @@ +name: Semgrep +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: +permissions: + contents: read +jobs: + semgrep: + uses: simple-container-com/actions/.github/workflows/semgrep.yml@main + permissions: + contents: read From 5d4d273a11a46a93f92f375a0c29f8cb011aa699 Mon Sep 17 00:00:00 2001 From: Dmitrii Creed Date: Thu, 7 May 2026 16:25:00 +0400 Subject: [PATCH 02/11] ci: retrigger after org-access enabled From 8a8418c2ad69121d7b4ea080f1c98e8ac25c79f3 Mon Sep 17 00:00:00 2001 From: Dmitrii Creed Date: Thu, 7 May 2026 16:54:09 +0400 Subject: [PATCH 03/11] ci: retrigger after actions repo set public Signed-off-by: Dmitrii Creed From 82130953109b71f3cbb280804c55af175f90cdeb Mon Sep 17 00:00:00 2001 From: Dmitrii Creed Date: Thu, 7 May 2026 19:02:42 +0400 Subject: [PATCH 04/11] fix(security-scan): replace placeholder credentials at source + narrow path-excludes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Brings the security-scan workflow to 0 TruffleHog findings while preserving detection coverage. Approach (per Codex review of PR-A's design tradeoffs and the public-actions-repo concern): 1. SOURCE-LEVEL REPLACEMENTS (preferred — no exclusion needed) - MongoDB / Postgres URIs in docs/.md and pkg/**/*_test.go: 'user:pass@host' -> ':@'. Angle brackets break the alphanumeric regex of TruffleHog's URI detectors. ~25 lines across docs and Go tests. - Gitlab CI placeholder text in docs/product-manager/container-security/acceptance-criteria.md: 'gitlab.com/myorg/myrepo' / 'CI_PROJECT_PATH=myorg/myrepo' replaced with '//' style; defuses the GitLab project-path detector. - Mailgun + Cloudflare fake tokens in docs/.../secrets.yaml: replaced with '' and '' literals. 2. INLINE TRUFFLEHOG IGNORE (when format-locked but file accepts comments) - pkg/clouds/docker/types_test.go: nested base64 of fake user:password test fixture. TruffleHog scans decoded base64 (per Codex), so no source replacement defeats it without breaking the test. Added '// trufflehog:ignore' Go comment on the affected line with justification. 3. NOSEMGREP suppressions (org-owned bootstrap scripts; same posture as go-aws-lambda-sdk's welder.sh) - .github/workflows/branch-preview.yaml lines 52, 73: 'curl ... | bash' for dist.simple-container.com/sc.sh and welder.simple-container.com/welder.sh. Both are org-owned services. Justifications inline; checksum-pinned welder release tracked as a follow-up. 4. NARROW PATH-EXCLUDES via wrapper input (last resort, file-specific) - docs/docs/examples/.*/secrets\\.yaml — example SC secrets.yaml files containing placeholder GCP service-account JSON; the GCP detector's regex matches even '@.iam.gserviceaccount.com' placeholders. - docs/docs/examples/secrets/.*/README\\.md — corresponding example READMEs. - docs/docs/guides/parent-gcp-gke-autopilot\\.md — single guide file. - pkg/api/secrets/testdata/repo/ + pkg/provisioner/testdata/ — raw OpenSSH private-key fixtures (no comment syntax in the file format). These are the only public excludes we accept. The actions repo's PR #2 README explicitly frames such excludes as last-resort + file-specific, given that exclusion patterns in public repos become attacker evasion hints. 5. WIRING - Wrapper now references simple-container-com/actions/.github/workflows/security-scan.yml@feat/secret-scan-extra-excludes for testing. Will bump to @main once that PR merges. Local scan with the new wrapper input + content: 0 findings (down from 39 on first run, with FormBucket already excluded by the actions-repo default). Signed-off-by: Dmitrii Creed --- .github/workflows/branch-preview.yaml | 2 ++ .github/workflows/security-scan.yml | 17 ++++++++++++++++- .../ai-assistant/bmad/PROTOTYPE_EXAMPLES.md | 2 +- .../cloud-api/08-deployment-architecture.md | 2 +- .../COMPUTE_PROCESSORS_ADOPTION.md | 14 +++++++------- .../DEPLOYMENT_FLOW_VALIDATION.md | 8 ++++---- .../cloud-api/08-deployment-architecture.md | 2 +- docs/docs/ai-assistant/developer-mode.md | 4 ++-- .../examples/nodejs-express-api.md | 2 +- docs/docs/ai-assistant/usage-examples.md | 2 +- .../multi-tier-node-isolation/secrets.yaml | 6 +++--- .../.sc/stacks/infrastructure/secrets.yaml | 6 +++--- .../examples/resource-adoption/secrets.yaml | 6 +++--- .../README.md | 4 ++-- .../secrets.yaml | 4 ++-- .../secrets/kube-and-gcp-auth/README.md | 4 ++-- .../secrets/kube-and-gcp-auth/secrets.yaml | 2 +- docs/docs/guides/parent-gcp-gke-autopilot.md | 6 +++--- docs/docs/guides/secrets-management.md | 2 +- docs/docs/reference/supported-resources.md | 4 ++-- .../container-security/acceptance-criteria.md | 18 +++++++++--------- pkg/clouds/docker/types_test.go | 2 +- .../simple_container_advanced_test.go | 2 +- pkg/clouds/pulumi/mongodb/util_test.go | 8 ++++---- 24 files changed, 73 insertions(+), 56 deletions(-) diff --git a/.github/workflows/branch-preview.yaml b/.github/workflows/branch-preview.yaml index 2409d579..fcab1d5d 100644 --- a/.github/workflows/branch-preview.yaml +++ b/.github/workflows/branch-preview.yaml @@ -49,6 +49,7 @@ jobs: - name: install sc tool (latest release) shell: bash run: |- + # nosemgrep: shell-curl-pipe-to-shell -- dist.simple-container.com/sc.sh is the org-owned bootstrap; pin to a checksumed release as a follow-up curl -s "https://dist.simple-container.com/sc.sh" | bash - name: prepare secrets for build run: | @@ -70,6 +71,7 @@ jobs: SKIP_EMBEDDINGS: "true" run: |- git remote set-url origin https://${{ secrets.GITHUB_TOKEN }}@github.com/simple-container-com/api.git + # nosemgrep: shell-curl-pipe-to-shell -- welder.simple-container.com/welder.sh is the org-owned build tool bootstrap bash <(curl -Ls "https://welder.simple-container.com/welder.sh") run rebuild - name: clean run: | diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml index 1adaaf2f..48aed942 100644 --- a/.github/workflows/security-scan.yml +++ b/.github/workflows/security-scan.yml @@ -9,6 +9,21 @@ permissions: contents: read jobs: security: - uses: simple-container-com/actions/.github/workflows/security-scan.yml@main + # TEMPORARY: pinned to PR-A branch for testing (simple-container-com/actions PR #2). + # Bump to @main once that PR merges. + uses: simple-container-com/actions/.github/workflows/security-scan.yml@feat/secret-scan-extra-excludes permissions: contents: read + with: + # File-specific path excludes for fixtures/examples that intentionally + # contain placeholder credentials in formats the scanner detects (raw + # OpenSSH key bodies, GCP service-account JSON examples). Each line is + # a TruffleHog Go regex, substring-matched against the full path. + # Source-level placeholder replacements are preferred and have already + # been applied for ~25 docs/test URIs in this PR. + secret-scan-extra-excludes: | + docs/docs/examples/.*/secrets\.yaml + docs/docs/examples/secrets/.*/README\.md + docs/docs/guides/parent-gcp-gke-autopilot\.md + pkg/api/secrets/testdata/repo/ + pkg/provisioner/testdata/ diff --git a/docs/design/archive/ai-assistant/bmad/PROTOTYPE_EXAMPLES.md b/docs/design/archive/ai-assistant/bmad/PROTOTYPE_EXAMPLES.md index b959b1c2..26d0b8ae 100644 --- a/docs/design/archive/ai-assistant/bmad/PROTOTYPE_EXAMPLES.md +++ b/docs/design/archive/ai-assistant/bmad/PROTOTYPE_EXAMPLES.md @@ -54,7 +54,7 @@ User: "Yes, let's do it!" "Excellent! I need these secure values: 1. MongoDB connection string (for database access) - Example: mongodb+srv://user:pass@cluster.mongodb.net/dbname + Example: mongodb+srv://:@.example.com/ 2. JWT secret key (for token signing) Example: your-256-bit-secret-key diff --git a/docs/design/archive/cloud-api/08-deployment-architecture.md b/docs/design/archive/cloud-api/08-deployment-architecture.md index 01ac72bd..261d98de 100644 --- a/docs/design/archive/cloud-api/08-deployment-architecture.md +++ b/docs/design/archive/cloud-api/08-deployment-architecture.md @@ -259,7 +259,7 @@ values: aws-secret-key: your-aws-secret-key-here # Application secrets for deployment - MONGODB_CONNECTION_STRING: mongodb+srv://user:pass@cluster.mongodb.net/db + MONGODB_CONNECTION_STRING: mongodb+srv://:@.example.com/ REDIS_URL: redis://redis-cluster:6379 JWT_SECRET: your-jwt-secret-key diff --git a/docs/design/archive/resources-adoption/COMPUTE_PROCESSORS_ADOPTION.md b/docs/design/archive/resources-adoption/COMPUTE_PROCESSORS_ADOPTION.md index b02a7d4c..a4712c4a 100644 --- a/docs/design/archive/resources-adoption/COMPUTE_PROCESSORS_ADOPTION.md +++ b/docs/design/archive/resources-adoption/COMPUTE_PROCESSORS_ADOPTION.md @@ -20,7 +20,7 @@ graph TD ### **Current Output Example** (Provisioned PostgreSQL) ```bash # What SC currently generates for provisioned PostgreSQL -export DATABASE_URL="postgresql://sc_user:auto_generated_password@10.1.2.3:5432/app_db" +export DATABASE_URL="postgresql://:@:5432/app_db" export POSTGRES_HOST="10.1.2.3" export POSTGRES_PORT="5432" export POSTGRES_USER="sc_user" @@ -579,7 +579,7 @@ func (r *ResourceResolver) ResolveResourceReference(resourceRef string) (string, #### **Adopted PostgreSQL Output** ```bash # Generated for adopted PostgreSQL (existing production database) -export DATABASE_URL="postgresql://acme_app:existing_password@10.1.0.5:5432/acme_production?sslmode=require" +export DATABASE_URL="postgresql://:@:5432/acme_production?sslmode=require" export POSTGRES_HOST="10.1.0.5" export POSTGRES_PORT="5432" export POSTGRES_USER="acme_app" @@ -592,7 +592,7 @@ export POSTGRES_CA_CERT="-----BEGIN CERTIFICATE-----\nMIIDbT..." #### **Provisioned PostgreSQL Output** ```bash # Generated for SC-provisioned PostgreSQL (new analytics database) -export DATABASE_URL="postgresql://sc_analytics_user:auto_generated_pass@10.1.0.8:5432/analytics?sslmode=require" +export DATABASE_URL="postgresql://:@:5432/analytics?sslmode=require" export POSTGRES_HOST="10.1.0.8" export POSTGRES_PORT="5432" export POSTGRES_USER="sc_analytics_user" @@ -620,7 +620,7 @@ mongodb-cluster: #### **MongoDB Compute Processor Output** ```bash # Generated environment variables (adopted MongoDB Atlas) -export MONGODB_URI="mongodb+srv://acme_user:existing_pass@acme-production.mongodb.net/acme_db?retryWrites=true&w=majority" +export MONGODB_URI="mongodb+srv://:@.example.com/?retryWrites=true&w=majority" export MONGO_HOST="acme-production.mongodb.net" export MONGO_PORT="27017" export MONGO_USER="acme_user" @@ -723,10 +723,10 @@ stacks: #### **Runtime Environment** (Generated by compute processors) ```bash # What the sample-app container receives at runtime -DATABASE_URL="postgresql://acme_app:prod_pass@10.1.0.5:5432/acme_production" -MONGO_URL="mongodb+srv://acme_user:atlas_pass@acme-prod.mongodb.net/acme_db" +DATABASE_URL="postgresql://:@:5432/acme_production" +MONGO_URL="mongodb+srv://:@.example.com/" REDIS_URL="redis://:redis_auth@10.1.0.6:6379" -ANALYTICS_DB="postgresql://sc_analytics:auto_gen_pass@10.1.0.8:5432/analytics" +ANALYTICS_DB="postgresql://:@:5432/analytics" METRICS_STORE="redis://:auto_gen_auth@10.1.0.9:6379" ``` diff --git a/docs/design/archive/resources-adoption/DEPLOYMENT_FLOW_VALIDATION.md b/docs/design/archive/resources-adoption/DEPLOYMENT_FLOW_VALIDATION.md index f1016cc9..e2c3ad9c 100644 --- a/docs/design/archive/resources-adoption/DEPLOYMENT_FLOW_VALIDATION.md +++ b/docs/design/archive/resources-adoption/DEPLOYMENT_FLOW_VALIDATION.md @@ -241,14 +241,14 @@ pulumi stack output service-a-mongo-user **Environment Variables Received**: ```bash # PostgreSQL (from adopted resource) -DATABASE_URL="postgresql://service-a:generated_password_123@10.1.0.5:5432/service-a" +DATABASE_URL="postgresql://:@:5432/service-a" POSTGRES_HOST="10.1.0.5" POSTGRES_PORT="5432" POSTGRES_USER="service-a" POSTGRES_PASSWORD="generated_password_123" # MongoDB (from adopted resource) -MONGO_URI="mongodb+srv://service-a:generated_password_456@acme-staging.mongodb.net/service-a" +MONGO_URI="mongodb+srv://:@.example.com/" MONGO_USER="service-a" MONGO_PASSWORD="generated_password_456" @@ -301,11 +301,11 @@ sc deploy -s service-b -e staging **Validation**: ```sql -- From service-a pod, try to access service-b database -psql postgresql://service-a:password@host/service-b -c "SELECT 1;" +psql postgresql://:@/service-b -c "SELECT 1;" -- Expected: ERROR: permission denied for database service-b -- Verify service-b has its own credentials -psql postgresql://service-b:password@host/service-b -c "SELECT 1;" +psql postgresql://:@/service-b -c "SELECT 1;" -- Expected: Success ``` diff --git a/docs/design/cloud-api/08-deployment-architecture.md b/docs/design/cloud-api/08-deployment-architecture.md index 01ac72bd..261d98de 100644 --- a/docs/design/cloud-api/08-deployment-architecture.md +++ b/docs/design/cloud-api/08-deployment-architecture.md @@ -259,7 +259,7 @@ values: aws-secret-key: your-aws-secret-key-here # Application secrets for deployment - MONGODB_CONNECTION_STRING: mongodb+srv://user:pass@cluster.mongodb.net/db + MONGODB_CONNECTION_STRING: mongodb+srv://:@.example.com/ REDIS_URL: redis://redis-cluster:6379 JWT_SECRET: your-jwt-secret-key diff --git a/docs/docs/ai-assistant/developer-mode.md b/docs/docs/ai-assistant/developer-mode.md index e204cb52..bac9c851 100644 --- a/docs/docs/ai-assistant/developer-mode.md +++ b/docs/docs/ai-assistant/developer-mode.md @@ -115,7 +115,7 @@ services: - "3000:3000" environment: - NODE_ENV=development - - DATABASE_URL=postgresql://user:pass@postgres:5432/myapp + - DATABASE_URL=postgresql://:@:5432/myapp - REDIS_URL=redis://redis:6379 depends_on: - postgres @@ -129,7 +129,7 @@ services: command: npm run worker environment: - NODE_ENV=development - - DATABASE_URL=postgresql://user:pass@postgres:5432/myapp + - DATABASE_URL=postgresql://:@:5432/myapp - REDIS_URL=redis://redis:6379 depends_on: - postgres diff --git a/docs/docs/ai-assistant/examples/nodejs-express-api.md b/docs/docs/ai-assistant/examples/nodejs-express-api.md index 51cc398d..75ffcaf8 100644 --- a/docs/docs/ai-assistant/examples/nodejs-express-api.md +++ b/docs/docs/ai-assistant/examples/nodejs-express-api.md @@ -223,7 +223,7 @@ services: environment: - NODE_ENV=development - PORT=3000 - - DATABASE_URL=postgresql://taskuser:taskpass@postgres:5432/taskdb + - DATABASE_URL=postgresql://:@:5432/taskdb - REDIS_URL=redis://redis:6379 - JWT_SECRET=dev-jwt-secret-key-123 depends_on: diff --git a/docs/docs/ai-assistant/usage-examples.md b/docs/docs/ai-assistant/usage-examples.md index 92e7baa9..77a200e7 100644 --- a/docs/docs/ai-assistant/usage-examples.md +++ b/docs/docs/ai-assistant/usage-examples.md @@ -315,7 +315,7 @@ services: environment: - GO_ENV=development - PORT=8080 - - DATABASE_URL=postgres://user:password@db:5432/myapp + - DATABASE_URL=postgres://:@:5432/myapp volumes: - .:/app depends_on: diff --git a/docs/docs/examples/kubernetes-affinity/multi-tier-node-isolation/secrets.yaml b/docs/docs/examples/kubernetes-affinity/multi-tier-node-isolation/secrets.yaml index f6cef886..741b1a0e 100644 --- a/docs/docs/examples/kubernetes-affinity/multi-tier-node-isolation/secrets.yaml +++ b/docs/docs/examples/kubernetes-affinity/multi-tier-node-isolation/secrets.yaml @@ -13,8 +13,8 @@ auth: "project_id": "mycompany-production-12345", "private_key_id": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0", "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC8Q7HgL...\n-----END PRIVATE KEY-----\n", - "client_email": "mycompany-service@mycompany-production-12345.iam.gserviceaccount.com", - "client_id": "123456789012345678901", + "client_email": "@.iam.gserviceaccount.com", + "client_id": "", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", @@ -36,7 +36,7 @@ values: redis-password: "R3d1s!Auth789" # JWT and encryption keys - jwt-secret: "jwt-secret-key-a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" + jwt-secret: "jwt-secret-" encryption-key: "32-char-encryption-key-123456789012" # External API keys diff --git a/docs/docs/examples/kubernetes-vpa/.sc/stacks/infrastructure/secrets.yaml b/docs/docs/examples/kubernetes-vpa/.sc/stacks/infrastructure/secrets.yaml index 1f230c64..19b61fb7 100644 --- a/docs/docs/examples/kubernetes-vpa/.sc/stacks/infrastructure/secrets.yaml +++ b/docs/docs/examples/kubernetes-vpa/.sc/stacks/infrastructure/secrets.yaml @@ -15,8 +15,8 @@ auth: "project_id": "my-gcp-project-12345", "private_key_id": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0", "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC8Q7HgL...\n-----END PRIVATE KEY-----\n", - "client_email": "vpa-demo-service@my-gcp-project-12345.iam.gserviceaccount.com", - "client_id": "123456789012345678901", + "client_email": "@.iam.gserviceaccount.com", + "client_id": "", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", @@ -31,7 +31,7 @@ values: mongodb-atlas-private-key: "12345678-1234-1234-1234-123456789012" # Application secrets for VPA demo - app-jwt-secret: "jwt-secret-key-a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" + app-jwt-secret: "jwt-secret-" app-encryption-key: "32-char-encryption-key-123456789012" # Note: In production, these values should be: diff --git a/docs/docs/examples/resource-adoption/secrets.yaml b/docs/docs/examples/resource-adoption/secrets.yaml index 8f5964d4..4aeb237f 100644 --- a/docs/docs/examples/resource-adoption/secrets.yaml +++ b/docs/docs/examples/resource-adoption/secrets.yaml @@ -32,7 +32,7 @@ auth: "project_id": "your-production-gcp-project", "private_key_id": "your-private-key-id", "private_key": "-----BEGIN PRIVATE KEY-----\nyour-private-key-content\n-----END PRIVATE KEY-----\n", - "client_email": "your-service-account@your-production-gcp-project.iam.gserviceaccount.com", + "client_email": "@.iam.gserviceaccount.com", "client_id": "your-client-id", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", @@ -52,7 +52,7 @@ auth: "project_id": "your-staging-gcp-project", "private_key_id": "your-private-key-id", "private_key": "-----BEGIN PRIVATE KEY-----\nyour-private-key-content\n-----END PRIVATE KEY-----\n", - "client_email": "your-service-account@your-staging-gcp-project.iam.gserviceaccount.com", + "client_email": "@.iam.gserviceaccount.com", "client_id": "your-client-id", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", @@ -72,7 +72,7 @@ auth: "project_id": "your-production-gcp-project", "private_key_id": "your-private-key-id", "private_key": "-----BEGIN PRIVATE KEY-----\nyour-private-key-content\n-----END PRIVATE KEY-----\n", - "client_email": "your-service-account@your-production-gcp-project.iam.gserviceaccount.com", + "client_email": "@.iam.gserviceaccount.com", "client_id": "your-client-id", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", diff --git a/docs/docs/examples/secrets/gcp-auth-cloudflare-mongodb-discord-telegram/README.md b/docs/docs/examples/secrets/gcp-auth-cloudflare-mongodb-discord-telegram/README.md index 7b4fd00f..78960362 100644 --- a/docs/docs/examples/secrets/gcp-auth-cloudflare-mongodb-discord-telegram/README.md +++ b/docs/docs/examples/secrets/gcp-auth-cloudflare-mongodb-discord-telegram/README.md @@ -76,12 +76,12 @@ gcloud iam service-accounts create simple-container-deploy-bot \ # Grant necessary permissions gcloud projects add-iam-policy-binding your-project-id \ - --member="serviceAccount:simple-container-deploy-bot@your-project-id.iam.gserviceaccount.com" \ + --member="serviceAccount:@.iam.gserviceaccount.com" \ --role="roles/editor" # Create and download service account key gcloud iam service-accounts keys create service-account.json \ - --iam-account=simple-container-deploy-bot@your-project-id.iam.gserviceaccount.com + --iam-account=@.iam.gserviceaccount.com ``` #### Configure Multiple Environments diff --git a/docs/docs/examples/secrets/gcp-auth-cloudflare-mongodb-discord-telegram/secrets.yaml b/docs/docs/examples/secrets/gcp-auth-cloudflare-mongodb-discord-telegram/secrets.yaml index 4e55eaff..4a1f7350 100644 --- a/docs/docs/examples/secrets/gcp-auth-cloudflare-mongodb-discord-telegram/secrets.yaml +++ b/docs/docs/examples/secrets/gcp-auth-cloudflare-mongodb-discord-telegram/secrets.yaml @@ -10,7 +10,7 @@ auth: "project_id": "your-gcp-project-id", "private_key_id": "your-private-key-id-here", "private_key": "-----BEGIN PRIVATE KEY-----\nYOUR-ACTUAL-PRIVATE-KEY-CONTENT-GOES-HERE\n-----END PRIVATE KEY-----\n", - "client_email": "your-service-account@your-project.iam.gserviceaccount.com", + "client_email": "@.iam.gserviceaccount.com", "client_id": "your-client-id-here", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", @@ -28,7 +28,7 @@ auth: "project_id": "your-staging-gcp-project-id", "private_key_id": "your-staging-private-key-id-here", "private_key": "-----BEGIN PRIVATE KEY-----\nYOUR-ACTUAL-STAGING-PRIVATE-KEY-CONTENT-GOES-HERE\n-----END PRIVATE KEY-----\n", - "client_email": "your-staging-service-account@your-staging-project.iam.gserviceaccount.com", + "client_email": "@.iam.gserviceaccount.com", "client_id": "your-staging-client-id-here", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", diff --git a/docs/docs/examples/secrets/kube-and-gcp-auth/README.md b/docs/docs/examples/secrets/kube-and-gcp-auth/README.md index 60a8340b..8e10ca42 100644 --- a/docs/docs/examples/secrets/kube-and-gcp-auth/README.md +++ b/docs/docs/examples/secrets/kube-and-gcp-auth/README.md @@ -26,8 +26,8 @@ auth: "project_id": "project-id", "private_key_id": "...", "private_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n", - "client_email": "deployer-bot@ai-asia-382012.iam.gserviceaccount.com", - "client_id": "104019626917208012368", + "client_email": "@.iam.gserviceaccount.com", + "client_id": "", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", diff --git a/docs/docs/examples/secrets/kube-and-gcp-auth/secrets.yaml b/docs/docs/examples/secrets/kube-and-gcp-auth/secrets.yaml index 2af677df..f5b41ed7 100644 --- a/docs/docs/examples/secrets/kube-and-gcp-auth/secrets.yaml +++ b/docs/docs/examples/secrets/kube-and-gcp-auth/secrets.yaml @@ -10,7 +10,7 @@ auth: "project_id": "your-gcp-project-id", "private_key_id": "your-private-key-id-here", "private_key": "-----BEGIN PRIVATE KEY-----\\nYOUR-ACTUAL-PRIVATE-KEY-CONTENT-GOES-HERE\n-----END PRIVATE KEY-----\n", - "client_email": "your-service-account@your-project.iam.gserviceaccount.com", + "client_email": "@.iam.gserviceaccount.com", "client_id": "your-client-id-here", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", diff --git a/docs/docs/guides/parent-gcp-gke-autopilot.md b/docs/docs/guides/parent-gcp-gke-autopilot.md index 68456786..6a2f1536 100644 --- a/docs/docs/guides/parent-gcp-gke-autopilot.md +++ b/docs/docs/guides/parent-gcp-gke-autopilot.md @@ -50,10 +50,10 @@ auth: { "type": "service_account", "project_id": "my-gcp-project-id", - "private_key_id": "60bb42f229bc21f6d303b5967b6cd59265cb316d", + "private_key_id": "", "private_key": "-----BEGIN PRIVATE KEY-----\nBLABLABLA\n-----END PRIVATE KEY-----\n", - "client_email": "deploy-bot@my-gcp-project-id.iam.gserviceaccount.com", - "client_id": "2387492479284792742398427", + "client_email": "@.iam.gserviceaccount.com", + "client_id": "", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", diff --git a/docs/docs/guides/secrets-management.md b/docs/docs/guides/secrets-management.md index 91d376e4..590caa42 100644 --- a/docs/docs/guides/secrets-management.md +++ b/docs/docs/guides/secrets-management.md @@ -622,7 +622,7 @@ env: ```yaml # .sc/stacks/your-parent-stack/secrets.yaml values: - database-url: "postgresql://user:pass@host:5432/db" + database-url: "postgresql://:@:5432/db" api-key: "your-secret-api-key-here" ``` diff --git a/docs/docs/reference/supported-resources.md b/docs/docs/reference/supported-resources.md index 4cca3f70..a92f6825 100644 --- a/docs/docs/reference/supported-resources.md +++ b/docs/docs/reference/supported-resources.md @@ -436,7 +436,7 @@ auth: values: # Exact literal values - NO placeholders processed in secrets.yaml - DATABASE_URL: "postgresql://user:pass@host:5432/db" + DATABASE_URL: "postgresql://:@:5432/db" API_KEY: "your-secret-api-key-here" ``` @@ -462,7 +462,7 @@ values: # Exact literal values - NO placeholders processed in secrets.yaml DATABASE_PASSWORD: "mySecurePassword123" API_KEY: "sk-1234567890abcdef" - CLOUDFLARE_API_TOKEN: "gEYRal5hQm4XJWE5WROP6DAEsdb3NxOgQUcpKjzB" + CLOUDFLARE_API_TOKEN: "" ``` ### **Provisioner Configuration** (goes to `provisioner` section in `server.yaml`) diff --git a/docs/product-manager/container-security/acceptance-criteria.md b/docs/product-manager/container-security/acceptance-criteria.md index c984ab10..d0c7acba 100644 --- a/docs/product-manager/container-security/acceptance-criteria.md +++ b/docs/product-manager/container-security/acceptance-criteria.md @@ -59,7 +59,7 @@ Given: Running in GitHub Actions with id-token: write permission When: Workflow executes `sc deploy -s mystack -e production` Then: - OIDC token is automatically obtained from GitHub Actions - - Image is signed with identity: https://github.com/myorg/myrepo/.github/workflows/deploy.yml@refs/heads/main + - Image is signed with identity: https://///.github/workflows/deploy.yml@refs/heads/main - Signature includes Rekor transparency log entry - Signature can be verified with: cosign verify --certificate-identity-regexp "^https://github.com/myorg/.*$" ``` @@ -303,8 +303,8 @@ Then: - Provenance follows SLSA v1.0 schema - Provenance includes: - buildType: "https://github.com/simple-container-com/api@v1" - - builder.id: "https://github.com/myorg/myrepo/.github/workflows/deploy.yml@refs/heads/main" - - invocation.configSource.uri: "git+https://github.com/myorg/myrepo@refs/heads/main" + - builder.id: "https://///.github/workflows/deploy.yml@refs/heads/main" + - invocation.configSource.uri: "git+https:////@refs/heads/main" - invocation.configSource.digest.sha1: "" ``` @@ -313,21 +313,21 @@ Then: **Test Case:** TC-3.2.1 - GitHub Actions Detection ```yaml Given: Running in GitHub Actions - And: GITHUB_REPOSITORY=myorg/myrepo + And: GITHUB_REPOSITORY=/ And: GITHUB_WORKFLOW=Deploy When: Provenance is generated Then: - - builder.id: "https://github.com/myorg/myrepo/.github/workflows/Deploy@refs/heads/main" + - builder.id: "https://///.github/workflows/Deploy@refs/heads/main" ``` **Test Case:** TC-3.2.2 - GitLab CI Detection ```yaml Given: Running in GitLab CI - And: CI_PROJECT_PATH=myorg/myrepo - And: CI_PIPELINE_URL=https://gitlab.com/myorg/myrepo/-/pipelines/12345 + And: CI_PROJECT_PATH=/ + And: CI_PIPELINE_URL=https://///-/pipelines/12345 When: Provenance is generated Then: - - builder.id: "https://gitlab.com/myorg/myrepo/-/pipelines/12345" + - builder.id: "https://///-/pipelines/12345" ``` ### AC-3.3: Source Materials Inclusion @@ -339,7 +339,7 @@ Given: security.provenance.metadata.includeMaterials=true When: Provenance is generated Then: - materials array includes: - - uri: "git+https://github.com/myorg/myrepo@refs/heads/main" + - uri: "git+https:////@refs/heads/main" - digest.sha1: "abc123" ``` diff --git a/pkg/clouds/docker/types_test.go b/pkg/clouds/docker/types_test.go index 865e68ed..f0baf802 100644 --- a/pkg/clouds/docker/types_test.go +++ b/pkg/clouds/docker/types_test.go @@ -18,7 +18,7 @@ func Test_GenerateImagePullSecret(t *testing.T) { }{ { name: "happy-path", - expectResult: "eyJhdXRocyI6eyJkb2NrZXIuc2ltcGxlLWNvbnRhaW5lci5jb20iOnsiYXV0aCI6ImRYTmxjanB3WVhOemQyOXlaQT09IiwidXNlcm5hbWUiOiJ1c2VyIiwicGFzc3dvcmQiOiJwYXNzd29yZCJ9fX0=", + expectResult: "eyJhdXRocyI6eyJkb2NrZXIuc2ltcGxlLWNvbnRhaW5lci5jb20iOnsiYXV0aCI6ImRYTmxjanB3WVhOemQyOXlaQT09IiwidXNlcm5hbWUiOiJ1c2VyIiwicGFzc3dvcmQiOiJwYXNzd29yZCJ9fX0=", // trufflehog:ignore (nested base64 of fake user:password test fixture) creds: RegistryCredentials{ DockerRegistryURL: lo.ToPtr("docker.simple-container.com"), DockerRegistryUsername: lo.ToPtr("user"), diff --git a/pkg/clouds/pulumi/kubernetes/simple_container_advanced_test.go b/pkg/clouds/pulumi/kubernetes/simple_container_advanced_test.go index 3fceff58..6915c157 100644 --- a/pkg/clouds/pulumi/kubernetes/simple_container_advanced_test.go +++ b/pkg/clouds/pulumi/kubernetes/simple_container_advanced_test.go @@ -167,7 +167,7 @@ func TestSimpleContainer_ComplexVolumeConfiguration(t *testing.T) { // Secret environment variables SecretEnvs: map[string]string{ - "DATABASE_URL": "postgresql://user:pass@db:5432/mydb", + "DATABASE_URL": "postgresql://:@:5432/mydb", "API_KEY": "super-secret-api-key", "ENCRYPTION_SECRET": "encryption-key-123", }, diff --git a/pkg/clouds/pulumi/mongodb/util_test.go b/pkg/clouds/pulumi/mongodb/util_test.go index 7535b8fd..4cdfc9ef 100644 --- a/pkg/clouds/pulumi/mongodb/util_test.go +++ b/pkg/clouds/pulumi/mongodb/util_test.go @@ -17,22 +17,22 @@ func Test_appendUserPasswordToMongoUri(t *testing.T) { { name: "happy-path", args: args{ - mongoUri: "mongodb://ac-gvptdqa-shard-00-00.qo081kw.mongodb.net:27017,ac-gvptdqa-shard-00-01.qo081kw.mongodb.net:27017,ac-gvptdqa-shard-00-02.qo081kw.mongodb.net:27017/?param1=value1¶m2=value2", + mongoUri: "mongodb://ac-gvptdqa-shard-00-00.qo081kw.mongodb.net:27017,.example.com:27017,.example.com:27017/?param1=value1¶m2=value2", user: "test-user", password: "test-password", dbName: "test-db", }, - want: "mongodb://test-user:test-password@ac-gvptdqa-shard-00-00.qo081kw.mongodb.net:27017,ac-gvptdqa-shard-00-01.qo081kw.mongodb.net:27017,ac-gvptdqa-shard-00-02.qo081kw.mongodb.net:27017/test-db?param1=value1¶m2=value2", + want: "mongodb://:@.example.com:27017,.example.com:27017,.example.com:27017/test-db?param1=value1¶m2=value2", }, { name: "happy-path mongodb+srv", args: args{ - mongoUri: "mongodb+srv://ac-gvptdqa-shard-00-00.qo081kw.mongodb.net:27017,ac-gvptdqa-shard-00-01.qo081kw.mongodb.net:27017,ac-gvptdqa-shard-00-02.qo081kw.mongodb.net:27017/?param1=value1¶m2=value2", + mongoUri: "mongodb+srv://ac-gvptdqa-shard-00-00.qo081kw.mongodb.net:27017,.example.com:27017,.example.com:27017/?param1=value1¶m2=value2", user: "test-user", password: "test-password", dbName: "test-db", }, - want: "mongodb+srv://test-user:test-password@ac-gvptdqa-shard-00-00.qo081kw.mongodb.net:27017,ac-gvptdqa-shard-00-01.qo081kw.mongodb.net:27017,ac-gvptdqa-shard-00-02.qo081kw.mongodb.net:27017/test-db?param1=value1¶m2=value2", + want: "mongodb+srv://:@.example.com:27017,.example.com:27017,.example.com:27017/test-db?param1=value1¶m2=value2", }, } for _, tt := range tests { From 9e04b519dc6a1ad3459530c6478972449d611ac9 Mon Sep 17 00:00:00 2001 From: Dmitrii Creed Date: Thu, 7 May 2026 21:22:26 +0400 Subject: [PATCH 05/11] ci: harden CI with composites; fix mongo URI test; address script-injection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WHAT changed ============ * `pkg/clouds/pulumi/mongodb/util_test.go` — replace mongo URI placeholder hostnames with `example.com` style. The previous round's `` used angle brackets, which are not valid URI characters; `url.Parse` silently returned the raw input and the test broke. example.com hostnames parse normally and don't trip TruffleHog's MongoDB detector. * `welder.yaml` — `docker-login` task no longer bootstraps `sc` via `bash <(curl … sc.sh)`. The CI workflow now installs `sc` upstream via the `install-sc` composite, so welder can use the binary already on PATH. Removes a curl-pipe-to-shell finding inside CI-executed code (welder.yaml is invoked by `welder run` on the runner, so it IS in scope for our shell rules). * `.github/workflows/*.yml{,aml}` — workflow hardening: - **Composites instead of curl-pipe** for tool installs and Telegram notifications: `simple-container-com/actions/install-sc@main`, `install-welder@main`, `notify-telegram@main`. Replaces `curl … | bash`, `bash <(curl …)`, and `yanzay/notify-telegram@v0.1.0` (third-party, tag-pinned) at all consumer sites. - **Script-injection mitigation** for attacker-controlled values. Heredocs that wrote `${{ github.event.head_commit.message }}` into `/tmp/commit_message.txt` now route the value through `env:` and `printf` to the file. Same pattern for `${{ github.ref_name }}` in docker-buildx tag construction and in echoes. - **`actions/checkout` pinned by SHA** to `v6.0.2` (`de0fac2e4500dabe0009e67214ff5f5447ce83dd`). Replaces the floating `@v4` tag, which would also have started warning about Node.js 20 deprecation in June 2026. * `.semgrepignore` — `sc.sh` (the published end-user installer; runs on user machines, different threat model from CI). * `.github/workflows/security-scan.yml` — drop the temporary "@feat" branch comment now that `simple-container-com/actions` PR #2 merged. * `.github/workflows/branch-preview.yaml` — preview-build step summary no longer hands users `curl … | bash`; instead it shows the `install-sc` action invocation pinned to the preview version. WHY === PR #228 added the security-scan + Semgrep + Dependabot workflows. The first run on `ci/security-scans` flagged 26 ERROR-severity Semgrep findings (16 curl-pipe-to-shell, 10 script-injection) and 15 TruffleHog findings (cross-ref bug — `actions@main` pre-merge — now resolved). This commit clears all of them at the source rather than via suppressions. Side effects: removes one third-party action with an unpinned tag (`yanzay`), prevents the Telegram bot token from leaking in CI logs (handled inside the new composite via `::add-mask::`), and gets ahead of the Node.js 20 deprecation. Signed-off-by: Dmitrii Creed --- .github/workflows/branch-preview.yaml | 77 ++++++++++++------------ .github/workflows/branch.yaml | 50 ++++++++-------- .github/workflows/build-staging.yml | 61 ++++++++----------- .github/workflows/push.yaml | 83 ++++++++++++-------------- .github/workflows/security-scan.yml | 4 +- .github/workflows/simple-forge.yml | 4 +- .semgrepignore | 9 +++ pkg/clouds/pulumi/mongodb/util_test.go | 8 +-- welder.yaml | 9 ++- 9 files changed, 146 insertions(+), 159 deletions(-) create mode 100644 .semgrepignore diff --git a/.github/workflows/branch-preview.yaml b/.github/workflows/branch-preview.yaml index fcab1d5d..f53fbfd0 100644 --- a/.github/workflows/branch-preview.yaml +++ b/.github/workflows/branch-preview.yaml @@ -17,7 +17,7 @@ jobs: outputs: version: ${{ steps.version.outputs.version }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Get next version uses: reecetech/version-increment@2023.10.2 id: base-version @@ -40,17 +40,16 @@ jobs: cicd-bot-telegram-token: ${{ steps.telegram-secrets.outputs.cicd-bot-telegram-token }} cicd-bot-telegram-chat-id: ${{ steps.telegram-secrets.outputs.cicd-bot-telegram-chat-id }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: fregante/setup-git-user@v2 - name: Set up Go with Blacksmith caching uses: useblacksmith/setup-go@v6 with: go-version: '1.25' - - name: install sc tool (latest release) - shell: bash - run: |- - # nosemgrep: shell-curl-pipe-to-shell -- dist.simple-container.com/sc.sh is the org-owned bootstrap; pin to a checksumed release as a follow-up - curl -s "https://dist.simple-container.com/sc.sh" | bash + - name: install sc tool + uses: simple-container-com/actions/install-sc@main + - name: install welder tool + uses: simple-container-com/actions/install-welder@main - name: prepare secrets for build run: | cat << EOF > ./.sc/cfg.default.yaml @@ -71,8 +70,7 @@ jobs: SKIP_EMBEDDINGS: "true" run: |- git remote set-url origin https://${{ secrets.GITHUB_TOKEN }}@github.com/simple-container-com/api.git - # nosemgrep: shell-curl-pipe-to-shell -- welder.simple-container.com/welder.sh is the org-owned build tool bootstrap - bash <(curl -Ls "https://welder.simple-container.com/welder.sh") run rebuild + welder run rebuild - name: clean run: | mkdir -p dist @@ -126,7 +124,7 @@ jobs: - os: darwin arch: amd64 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Go with Blacksmith caching uses: useblacksmith/setup-go@v6 with: @@ -168,7 +166,7 @@ jobs: cmd: cloud-helpers output: dist/cloud-helpers steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Go with Blacksmith caching uses: useblacksmith/setup-go@v6 with: @@ -194,7 +192,7 @@ jobs: runs-on: blacksmith-8vcpu-ubuntu-2204 needs: [prepare, build-setup] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Go with Blacksmith caching uses: useblacksmith/setup-go@v6 with: @@ -219,7 +217,7 @@ jobs: dockerfile: cloud-helpers.aws.Dockerfile tag_prefix: "simplecontainer/cloud-helpers:aws-" steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: download ${{ matrix.target }} binary uses: actions/download-artifact@v4 with: @@ -227,10 +225,8 @@ jobs: path: dist - name: fix binary permissions run: chmod +x dist/${{ matrix.binary }} - - name: install sc tool (latest release) - shell: bash - run: |- - curl -s "https://dist.simple-container.com/sc.sh" | bash + - name: install sc tool + uses: simple-container-com/actions/install-sc@main - name: prepare secrets for build run: | cat << EOF > ./.sc/cfg.default.yaml @@ -269,7 +265,7 @@ jobs: # Runs in parallel with publish-git-tag. needs: [prepare, build-setup, build-platforms, test] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: download all sc platform artifacts uses: actions/download-artifact@v4 with: @@ -282,10 +278,8 @@ jobs: path: bin - name: fix bin tools permissions run: chmod +x bin/* - - name: install sc tool (latest release) - shell: bash - run: |- - curl -s "https://dist.simple-container.com/sc.sh" | bash + - name: install sc tool + uses: simple-container-com/actions/install-sc@main - name: prepare secrets for build run: | cat << EOF > ./.sc/cfg.default.yaml @@ -321,7 +315,7 @@ jobs: # Does not need build-platforms or publish-sc-preview. Runs in parallel with publish-sc-preview. needs: [prepare, docker-build] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - uses: fregante/setup-git-user@v2 @@ -366,18 +360,17 @@ jobs: - publish-git-tag - docker-build steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 if: ${{ always() }} - name: Extract git reference id: extract_git_ref if: ${{ always() }} shell: bash + env: + COMMIT_MESSAGE: ${{ github.event.head_commit.message || github.event.workflow_run.head_commit.message }} run: |- - cat <<'EOF' > /tmp/commit_message.txt - ${{ github.event.head_commit.message || github.event.workflow_run.head_commit.message }} - EOF - message="$(cat /tmp/commit_message.txt | tr -d '\n')" + message="$(printf '%s' "$COMMIT_MESSAGE" | tr -d '\n')" if [ ${#message} -gt 200 ]; then truncated_message="${message:0:80}...${message: -80}" message="$truncated_message" @@ -422,29 +415,33 @@ jobs: Docker image: \`simplecontainer/github-actions:${VERSION}\` - ### Install this SC version via CLI + ### Install this SC version in your CI - \`\`\`bash - SIMPLE_CONTAINER_VERSION=${VERSION} curl -s "https://dist.simple-container.com/sc.sh" | bash + \`\`\`yaml + - uses: simple-container-com/actions/install-sc@main + with: + version: ${VERSION} \`\`\` - > Preview build from branch \`${BRANCH}\`. Running \`sc.sh\` without \`SIMPLE_CONTAINER_VERSION\` will **not** pick up this version. + > Preview build from branch \`${BRANCH}\`. The \`install-sc\` action pins the binary to the exact preview version above and verifies it on download. ENDSUMMARY - - uses: yanzay/notify-telegram@v0.1.0 + - name: Notify Telegram (success) if: ${{ success() && !contains(needs.*.result, 'failure') }} continue-on-error: true + uses: simple-container-com/actions/notify-telegram@main with: - chat: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} - token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} - status: ✅ preview published (${{ steps.extract_git_ref.outputs.branch }}) (v${{ needs.prepare.outputs.version }}) - ${{ steps.extract_git_ref.outputs.message }} by ${{ steps.extract_git_ref.outputs.author }} - - uses: yanzay/notify-telegram@v0.1.0 + chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} + token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} + text: ✅ preview published (${{ steps.extract_git_ref.outputs.branch }}) (v${{ needs.prepare.outputs.version }}) - ${{ steps.extract_git_ref.outputs.message }} by ${{ steps.extract_git_ref.outputs.author }} + - name: Notify Telegram (failure) if: ${{ failure() || contains(needs.*.result, 'failure') }} continue-on-error: true + uses: simple-container-com/actions/notify-telegram@main with: - chat: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} - token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} - status: ❗ preview failed (${{ steps.extract_git_ref.outputs.branch }}) - ${{ steps.extract_git_ref.outputs.message }} by ${{ steps.extract_git_ref.outputs.author }} + chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} + token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} + text: ❗ preview failed (${{ steps.extract_git_ref.outputs.branch }}) - ${{ steps.extract_git_ref.outputs.message }} by ${{ steps.extract_git_ref.outputs.author }} - name: Build failed due to previously failed steps id: fail_if_needed diff --git a/.github/workflows/branch.yaml b/.github/workflows/branch.yaml index 9220acf7..5578860f 100644 --- a/.github/workflows/branch.yaml +++ b/.github/workflows/branch.yaml @@ -17,16 +17,16 @@ jobs: cicd-bot-telegram-token: ${{ steps.telegram-secrets.outputs.cicd-bot-telegram-token }} cicd-bot-telegram-chat-id: ${{ steps.telegram-secrets.outputs.cicd-bot-telegram-chat-id }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: fregante/setup-git-user@v2 - name: Set up Go with Blacksmith caching uses: useblacksmith/setup-go@v6 with: go-version: '1.25' - - name: install sc tool (latest release) - shell: bash - run: |- - curl -s "https://dist.simple-container.com/sc.sh" | bash + - name: install sc tool + uses: simple-container-com/actions/install-sc@main + - name: install welder tool + uses: simple-container-com/actions/install-welder@main - name: prepare secrets for build run: | cat << EOF > ./.sc/cfg.default.yaml @@ -47,7 +47,7 @@ jobs: SKIP_EMBEDDINGS: "true" run: |- git remote set-url origin https://${{ secrets.GITHUB_TOKEN }}@github.com/simple-container-com/api.git - bash <(curl -Ls "https://welder.simple-container.com/welder.sh") run rebuild + welder run rebuild - name: clean run: | mkdir -p dist @@ -101,7 +101,7 @@ jobs: - os: darwin arch: amd64 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Go with Blacksmith caching uses: useblacksmith/setup-go@v6 with: @@ -141,7 +141,7 @@ jobs: cmd: cloud-helpers output: dist/cloud-helpers steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Go with Blacksmith caching uses: useblacksmith/setup-go@v6 with: @@ -166,7 +166,7 @@ jobs: runs-on: blacksmith-8vcpu-ubuntu-2204 needs: build-setup steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Go with Blacksmith caching uses: useblacksmith/setup-go@v6 with: @@ -187,7 +187,7 @@ jobs: runs-on: blacksmith-8vcpu-ubuntu-2204 needs: build-setup steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Go with Blacksmith caching uses: useblacksmith/setup-go@v6 with: @@ -209,38 +209,38 @@ jobs: - build-github-actions-staging - test steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 if: ${{ always() }} - name: Extract git reference id: extract_git_ref if: ${{ always() }} shell: bash + env: + COMMIT_MESSAGE: ${{ github.event.head_commit.message || github.event.workflow_run.head_commit.message }} run: |- - cat <<'EOF' > /tmp/commit_message.txt - ${{ github.event.head_commit.message || github.event.workflow_run.head_commit.message }} - EOF - message="$(cat /tmp/commit_message.txt | tr -d '\n')" + message="$(printf '%s' "$COMMIT_MESSAGE" | tr -d '\n')" echo "branch=$GITHUB_REF_NAME" >> $GITHUB_OUTPUT echo "message=$message" >> $GITHUB_OUTPUT echo "author=$GITHUB_ACTOR" >> $GITHUB_OUTPUT echo "url=$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" >> $GITHUB_OUTPUT - # Notify telegram - - uses: yanzay/notify-telegram@v0.1.0 - if: ${{ success() && !contains(needs.*.result, 'failure') }} + - name: Notify Telegram (success) + if: ${{ success() && !contains(needs.*.result, 'failure') }} continue-on-error: true + uses: simple-container-com/actions/notify-telegram@main with: - chat: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} - token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} - status: ✅ success (${{ steps.extract_git_ref.outputs.branch }}) - ${{ steps.extract_git_ref.outputs.message }} by ${{ steps.extract_git_ref.outputs.author }} - - uses: yanzay/notify-telegram@v0.1.0 + chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} + token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} + text: ✅ success (${{ steps.extract_git_ref.outputs.branch }}) - ${{ steps.extract_git_ref.outputs.message }} by ${{ steps.extract_git_ref.outputs.author }} + - name: Notify Telegram (failure) if: ${{ failure() || contains(needs.*.result, 'failure') }} continue-on-error: true + uses: simple-container-com/actions/notify-telegram@main with: - chat: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} - token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} - status: ❗ failure (${{ steps.extract_git_ref.outputs.branch }}) - ${{ steps.extract_git_ref.outputs.message }} by ${{ steps.extract_git_ref.outputs.author }} + chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} + token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} + text: ❗ failure (${{ steps.extract_git_ref.outputs.branch }}) - ${{ steps.extract_git_ref.outputs.message }} by ${{ steps.extract_git_ref.outputs.author }} - name: Build failed due to previously failed steps id: fail_if_needed diff --git a/.github/workflows/build-staging.yml b/.github/workflows/build-staging.yml index 49c458fa..3a44b99a 100644 --- a/.github/workflows/build-staging.yml +++ b/.github/workflows/build-staging.yml @@ -16,7 +16,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Go with Blacksmith caching uses: useblacksmith/setup-go@v6 @@ -39,23 +39,10 @@ jobs: restore-keys: | cli-tools-${{ runner.os }}- - - name: Install CLI tools - run: | - # Install SC CLI if not cached - if ! command -v sc &> /dev/null; then - echo "Installing Simple Container CLI..." - curl -s "https://dist.simple-container.com/sc.sh" | bash - else - echo "✅ SC CLI already cached and available" - fi - - # Install Welder if not cached - if ! command -v welder &> /dev/null; then - echo "Installing Welder..." - curl -Ls "https://welder.simple-container.com/welder.sh" | bash - else - echo "✅ Welder already cached and available" - fi + - name: install sc tool + uses: simple-container-com/actions/install-sc@main + - name: install welder tool + uses: simple-container-com/actions/install-welder@main - name: Prepare secrets for build run: | @@ -92,6 +79,7 @@ jobs: - name: Build and push Docker image with BuildKit caching env: DOCKER_BUILDKIT: 1 + REF_NAME: ${{ github.ref_name }} run: | # Build and push with advanced caching using BuildKit docker buildx build \ @@ -100,7 +88,7 @@ jobs: --cache-to type=gha,mode=max \ --file github-actions-staging.Dockerfile \ --tag simplecontainer/github-actions:staging \ - --tag simplecontainer/github-actions:${{ github.ref_name }} \ + --tag "simplecontainer/github-actions:$REF_NAME" \ --push \ . @@ -121,12 +109,14 @@ jobs: . - name: Image built successfully + env: + REF_NAME: ${{ github.ref_name }} run: | echo "🎉 GitHub Actions and Caddy staging images built successfully with BuildKit + GitHub Actions caching!" echo "" echo "🚀 The staging images are now available:" echo " simplecontainer/github-actions:staging" - echo " simplecontainer/github-actions:${{ github.ref_name }}" + echo " simplecontainer/github-actions:$REF_NAME" echo " simplecontainer/caddy:staging" echo " simplecontainer/caddy:$VERSION" echo "" @@ -148,40 +138,39 @@ jobs: needs: - build-staging steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 if: ${{ always() }} - name: Extract git reference id: extract_git_ref if: ${{ always() }} shell: bash + env: + COMMIT_MESSAGE: ${{ github.event.head_commit.message || github.event.workflow_run.head_commit.message }} run: |- - cat <<'EOF' > /tmp/commit_message.txt - ${{ github.event.head_commit.message || github.event.workflow_run.head_commit.message }} - EOF - message="$(cat /tmp/commit_message.txt | tr -d '\n')" + message="$(printf '%s' "$COMMIT_MESSAGE" | tr -d '\n')" echo "branch=$GITHUB_REF_NAME" >> $GITHUB_OUTPUT echo "message=$message" >> $GITHUB_OUTPUT echo "author=$GITHUB_ACTOR" >> $GITHUB_OUTPUT echo "url=$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" >> $GITHUB_OUTPUT - # Notify telegram on success - - uses: yanzay/notify-telegram@v0.1.0 - if: ${{ success() && !contains(needs.*.result, 'failure') }} + - name: Notify Telegram (success) + if: ${{ success() && !contains(needs.*.result, 'failure') }} continue-on-error: true + uses: simple-container-com/actions/notify-telegram@main with: - chat: ${{ needs.build-staging.outputs.cicd-bot-telegram-chat-id }} - token: ${{ needs.build-staging.outputs.cicd-bot-telegram-token }} - status: ✅ staging build success (${{ steps.extract_git_ref.outputs.branch }}) - ${{ steps.extract_git_ref.outputs.message }} by ${{ steps.extract_git_ref.outputs.author }} + chat-id: ${{ needs.build-staging.outputs.cicd-bot-telegram-chat-id }} + token: ${{ needs.build-staging.outputs.cicd-bot-telegram-token }} + text: ✅ staging build success (${{ steps.extract_git_ref.outputs.branch }}) - ${{ steps.extract_git_ref.outputs.message }} by ${{ steps.extract_git_ref.outputs.author }} - # Notify telegram on failure - - uses: yanzay/notify-telegram@v0.1.0 + - name: Notify Telegram (failure) if: ${{ failure() || contains(needs.*.result, 'failure') }} continue-on-error: true + uses: simple-container-com/actions/notify-telegram@main with: - chat: ${{ needs.build-staging.outputs.cicd-bot-telegram-chat-id }} - token: ${{ needs.build-staging.outputs.cicd-bot-telegram-token }} - status: ❗ staging build failure (${{ steps.extract_git_ref.outputs.branch }}) - ${{ steps.extract_git_ref.outputs.message }} by ${{ steps.extract_git_ref.outputs.author }} + chat-id: ${{ needs.build-staging.outputs.cicd-bot-telegram-chat-id }} + token: ${{ needs.build-staging.outputs.cicd-bot-telegram-token }} + text: ❗ staging build failure (${{ steps.extract_git_ref.outputs.branch }}) - ${{ steps.extract_git_ref.outputs.message }} by ${{ steps.extract_git_ref.outputs.author }} - name: Build failed due to previously failed steps id: fail_if_needed diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml index 58e6b57f..7f497713 100644 --- a/.github/workflows/push.yaml +++ b/.github/workflows/push.yaml @@ -20,7 +20,7 @@ jobs: outputs: version: ${{ steps.version.outputs.version }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Get next version uses: reecetech/version-increment@2023.10.2 id: version @@ -37,17 +37,16 @@ jobs: cicd-bot-telegram-token: ${{ steps.telegram-secrets.outputs.cicd-bot-telegram-token }} cicd-bot-telegram-chat-id: ${{ steps.telegram-secrets.outputs.cicd-bot-telegram-chat-id }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: fregante/setup-git-user@v2 - name: Set up Go with Blacksmith caching uses: useblacksmith/setup-go@v6 with: go-version: '1.25' - - name: install sc tool (latest release) - shell: bash - run: |- - # Install latest SC release to get secrets for embeddings generation - curl -s "https://dist.simple-container.com/sc.sh" | bash + - name: install sc tool + uses: simple-container-com/actions/install-sc@main + - name: install welder tool + uses: simple-container-com/actions/install-welder@main - name: prepare secrets for build run: | cat << EOF > ./.sc/cfg.default.yaml @@ -68,7 +67,7 @@ jobs: SKIP_EMBEDDINGS: "true" run: |- git remote set-url origin https://${{ secrets.GITHUB_TOKEN }}@github.com/simple-container-com/api.git - bash <(curl -Ls "https://welder.simple-container.com/welder.sh") run rebuild + welder run rebuild - name: clean run: | mkdir -p dist @@ -127,7 +126,7 @@ jobs: - os: darwin arch: amd64 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Go with Blacksmith caching uses: useblacksmith/setup-go@v6 with: @@ -175,7 +174,7 @@ jobs: cmd: cloud-helpers output: dist/cloud-helpers steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Go with Blacksmith caching uses: useblacksmith/setup-go@v6 with: @@ -201,7 +200,7 @@ jobs: runs-on: blacksmith-8vcpu-ubuntu-2204 needs: [prepare, build-setup] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Go with Blacksmith caching uses: useblacksmith/setup-go@v6 with: @@ -225,7 +224,7 @@ jobs: runs-on: blacksmith-8vcpu-ubuntu-2204 needs: [prepare, build-setup] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Go with Blacksmith caching uses: useblacksmith/setup-go@v6 with: @@ -239,7 +238,7 @@ jobs: runs-on: ubuntu-latest needs: [prepare, build-setup] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: restore cached schemas uses: actions/cache@v4 with: @@ -286,7 +285,7 @@ jobs: simplecontainer/cloud-helpers:aws-latest simplecontainer/cloud-helpers:aws-${{ needs.prepare.outputs.version }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: download github-actions-staging binary if: matrix.image == 'github-actions-staging' uses: actions/download-artifact@v4 @@ -305,11 +304,8 @@ jobs: with: name: cloud-helpers-binary path: dist - - name: install sc tool (latest release) - shell: bash - run: |- - # Install latest SC release to get secrets for embeddings generation - curl -s "https://dist.simple-container.com/sc.sh" | bash + - name: install sc tool + uses: simple-container-com/actions/install-sc@main - name: prepare secrets for build run: | cat << EOF > ./.sc/cfg.default.yaml @@ -353,13 +349,12 @@ jobs: runs-on: blacksmith-8vcpu-ubuntu-2204 needs: [prepare, build-setup, build-platforms, build-binaries, build-github-actions-staging, test, build-docs, docker-build] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: fregante/setup-git-user@v2 - - name: install sc tool (latest release) - shell: bash - run: |- - # Install latest SC release to get secrets for embeddings generation - curl -s "https://dist.simple-container.com/sc.sh" | bash + - name: install sc tool + uses: simple-container-com/actions/install-sc@main + - name: install welder tool + uses: simple-container-com/actions/install-welder@main - name: prepare secrets for build run: | cat << EOF > ./.sc/cfg.default.yaml @@ -401,13 +396,13 @@ jobs: VERSION: ${{ needs.prepare.outputs.version }} run: |- git remote set-url origin https://${{ secrets.GITHUB_TOKEN }}@github.com/simple-container-com/api.git - bash <(curl -Ls "https://welder.simple-container.com/welder.sh") run tag-release + welder run tag-release - name: publish sc tool shell: bash env: VERSION: ${{ needs.prepare.outputs.version }} run: |- - bash <(curl -Ls "https://welder.simple-container.com/welder.sh") deploy -e prod --timestamps + welder deploy -e prod --timestamps finalize: name: Finalize build and deploy for ${{ needs.prepare.outputs.stack-name }} @@ -420,46 +415,46 @@ jobs: - build-setup - docker-finalize steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 if: ${{ always() }} - name: Extract git reference id: extract_git_ref if: ${{ always() }} shell: bash + env: + COMMIT_MESSAGE: ${{ github.event.head_commit.message || github.event.workflow_run.head_commit.message }} run: |- - cat <<'EOF' > /tmp/commit_message.txt - ${{ github.event.head_commit.message || github.event.workflow_run.head_commit.message }} - EOF - message="$(cat /tmp/commit_message.txt | tr -d '\n')" - + message="$(printf '%s' "$COMMIT_MESSAGE" | tr -d '\n')" + # Truncate message if too long for Telegram (max ~200 chars to leave room for other content) if [ ${#message} -gt 200 ]; then # Take first 80 chars and last 80 chars with separator truncated_message="${message:0:80}...${message: -80}" message="$truncated_message" fi - + echo "branch=$GITHUB_REF_NAME" >> $GITHUB_OUTPUT echo "message=$message" >> $GITHUB_OUTPUT echo "author=$GITHUB_ACTOR" >> $GITHUB_OUTPUT echo "url=$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" >> $GITHUB_OUTPUT - # Notify telegram - - uses: yanzay/notify-telegram@v0.1.0 - if: ${{ success() && !contains(needs.*.result, 'failure') }} + - name: Notify Telegram (success) + if: ${{ success() && !contains(needs.*.result, 'failure') }} continue-on-error: true + uses: simple-container-com/actions/notify-telegram@main with: - chat: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} - token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} - status: ✅ success (${{ steps.extract_git_ref.outputs.branch }}) (v${{ needs.prepare.outputs.version }}) - ${{ steps.extract_git_ref.outputs.message }} by ${{ steps.extract_git_ref.outputs.author }} - - uses: yanzay/notify-telegram@v0.1.0 + chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} + token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} + text: ✅ success (${{ steps.extract_git_ref.outputs.branch }}) (v${{ needs.prepare.outputs.version }}) - ${{ steps.extract_git_ref.outputs.message }} by ${{ steps.extract_git_ref.outputs.author }} + - name: Notify Telegram (failure) if: ${{ failure() || contains(needs.*.result, 'failure') }} continue-on-error: true + uses: simple-container-com/actions/notify-telegram@main with: - chat: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} - token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} - status: ❗ failure (${{ steps.extract_git_ref.outputs.branch }}) - ${{ steps.extract_git_ref.outputs.message }} by ${{ steps.extract_git_ref.outputs.author }} + chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} + token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} + text: ❗ failure (${{ steps.extract_git_ref.outputs.branch }}) - ${{ steps.extract_git_ref.outputs.message }} by ${{ steps.extract_git_ref.outputs.author }} - name: Build failed due to previously failed steps id: fail_if_needed diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml index 48aed942..a25d1cb1 100644 --- a/.github/workflows/security-scan.yml +++ b/.github/workflows/security-scan.yml @@ -9,9 +9,7 @@ permissions: contents: read jobs: security: - # TEMPORARY: pinned to PR-A branch for testing (simple-container-com/actions PR #2). - # Bump to @main once that PR merges. - uses: simple-container-com/actions/.github/workflows/security-scan.yml@feat/secret-scan-extra-excludes + uses: simple-container-com/actions/.github/workflows/security-scan.yml@main permissions: contents: read with: diff --git a/.github/workflows/simple-forge.yml b/.github/workflows/simple-forge.yml index 0af2c70f..43b9135f 100644 --- a/.github/workflows/simple-forge.yml +++ b/.github/workflows/simple-forge.yml @@ -44,7 +44,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} fetch-depth: 0 @@ -55,7 +55,7 @@ jobs: go-version: '1.25' - name: Install welder - run: curl -s "https://welder.simple-container.com/welder.sh" | bash + uses: simple-container-com/actions/install-welder@main - name: Run Simple Forge uses: simple-container-com/forge-action/.github/actions/dockerless@v1 diff --git a/.semgrepignore b/.semgrepignore new file mode 100644 index 00000000..c56dd714 --- /dev/null +++ b/.semgrepignore @@ -0,0 +1,9 @@ +# Semgrep ignore — applied by the security-scan workflow. +# Scope: this file excludes paths whose security model is not the same as +# our CI scripts. Keep it minimal and per-path; do not blanket-ignore +# directories without a specific reason. + +# Published end-user install bootstrap. Runs on the user's machine, not in +# our CI. The `curl ... | sh` in this script is the upstream pulumi +# installer's own pattern and is out of scope for our CI shell rules. +sc.sh diff --git a/pkg/clouds/pulumi/mongodb/util_test.go b/pkg/clouds/pulumi/mongodb/util_test.go index 4cdfc9ef..ccdc090c 100644 --- a/pkg/clouds/pulumi/mongodb/util_test.go +++ b/pkg/clouds/pulumi/mongodb/util_test.go @@ -17,22 +17,22 @@ func Test_appendUserPasswordToMongoUri(t *testing.T) { { name: "happy-path", args: args{ - mongoUri: "mongodb://ac-gvptdqa-shard-00-00.qo081kw.mongodb.net:27017,.example.com:27017,.example.com:27017/?param1=value1¶m2=value2", + mongoUri: "mongodb://shard-00-00.example.com:27017,shard-00-01.example.com:27017,shard-00-02.example.com:27017/?param1=value1¶m2=value2", user: "test-user", password: "test-password", dbName: "test-db", }, - want: "mongodb://:@.example.com:27017,.example.com:27017,.example.com:27017/test-db?param1=value1¶m2=value2", + want: "mongodb://test-user:test-password@shard-00-00.example.com:27017,shard-00-01.example.com:27017,shard-00-02.example.com:27017/test-db?param1=value1¶m2=value2", }, { name: "happy-path mongodb+srv", args: args{ - mongoUri: "mongodb+srv://ac-gvptdqa-shard-00-00.qo081kw.mongodb.net:27017,.example.com:27017,.example.com:27017/?param1=value1¶m2=value2", + mongoUri: "mongodb+srv://shard-00-00.example.com:27017,shard-00-01.example.com:27017,shard-00-02.example.com:27017/?param1=value1¶m2=value2", user: "test-user", password: "test-password", dbName: "test-db", }, - want: "mongodb+srv://:@.example.com:27017,.example.com:27017,.example.com:27017/test-db?param1=value1¶m2=value2", + want: "mongodb+srv://test-user:test-password@shard-00-00.example.com:27017,shard-00-01.example.com:27017,shard-00-02.example.com:27017/test-db?param1=value1¶m2=value2", }, } for _, tt := range tests { diff --git a/welder.yaml b/welder.yaml index e26d7075..9f467182 100644 --- a/welder.yaml +++ b/welder.yaml @@ -112,12 +112,11 @@ tasks: - cp ${project:root}/.sc/stacks/dist/site/* ${project:root}/.sc/stacks/dist/bundle/ docker-login: runOn: host + # `sc` is provisioned on the runner by the install-sc composite action + # before welder is invoked (see .github/workflows/*.yaml). script: - - |- - bash -c ' - bash <(curl -Ls "https://dist.simple-container.com/sc.sh") secrets reveal - sc stack secret-get -s dist dockerhub-cicd-token | docker login --username simplecontainer --password-stdin - ' + - sc secrets reveal + - sc stack secret-get -s dist dockerhub-cicd-token | docker login --username simplecontainer --password-stdin build-cloud-helpers: runOn: host script: From cf3f9bc835d9904c04b13a2501be4968f83b10ec Mon Sep 17 00:00:00 2001 From: Dmitrii Creed Date: Thu, 7 May 2026 22:38:19 +0400 Subject: [PATCH 06/11] test(mongodb): assert on parsed URL fields, not full-string equality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-run TruffleHog over PR #228 was flagging the test's `want:` literal because the rendered post-injection URI matched the MongoDB detector's `://:@` shape — even with `example.com` hosts, the connection-string structure itself triggers the detector. Refactor the test to: 1. Call AppendUserPasswordAndDBToMongoUri. 2. Parse the result with `net/url`. 3. Assert: scheme + host + raw query unchanged from input; user info exactly matches the supplied user/password; path becomes `/`. This covers the same behavior as the prior full-string equality check (the function is itself just `url.Parse` + assignment + `String()`), without any `mongodb://user:pass@host` literal in source. Signed-off-by: Dmitrii Creed --- pkg/clouds/pulumi/mongodb/util_test.go | 38 ++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/pkg/clouds/pulumi/mongodb/util_test.go b/pkg/clouds/pulumi/mongodb/util_test.go index ccdc090c..4b92da1c 100644 --- a/pkg/clouds/pulumi/mongodb/util_test.go +++ b/pkg/clouds/pulumi/mongodb/util_test.go @@ -1,6 +1,9 @@ package mongodb -import "testing" +import ( + "net/url" + "testing" +) func Test_appendUserPasswordToMongoUri(t *testing.T) { type args struct { @@ -12,7 +15,6 @@ func Test_appendUserPasswordToMongoUri(t *testing.T) { tests := []struct { name string args args - want string }{ { name: "happy-path", @@ -22,7 +24,6 @@ func Test_appendUserPasswordToMongoUri(t *testing.T) { password: "test-password", dbName: "test-db", }, - want: "mongodb://test-user:test-password@shard-00-00.example.com:27017,shard-00-01.example.com:27017,shard-00-02.example.com:27017/test-db?param1=value1¶m2=value2", }, { name: "happy-path mongodb+srv", @@ -32,13 +33,38 @@ func Test_appendUserPasswordToMongoUri(t *testing.T) { password: "test-password", dbName: "test-db", }, - want: "mongodb+srv://test-user:test-password@shard-00-00.example.com:27017,shard-00-01.example.com:27017,shard-00-02.example.com:27017/test-db?param1=value1¶m2=value2", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := AppendUserPasswordAndDBToMongoUri(tt.args.mongoUri, tt.args.user, tt.args.password, tt.args.dbName); got != tt.want { - t.Errorf("AppendUserPasswordAndDBToMongoUri() = %v, want %v", got, tt.want) + got := AppendUserPasswordAndDBToMongoUri(tt.args.mongoUri, tt.args.user, tt.args.password, tt.args.dbName) + + gotURL, err := url.Parse(got) + if err != nil { + t.Fatalf("AppendUserPasswordAndDBToMongoUri returned a value that failed to parse: %v", err) + } + inputURL, err := url.Parse(tt.args.mongoUri) + if err != nil { + t.Fatalf("test setup: input URI did not parse: %v", err) + } + + if gotURL.Scheme != inputURL.Scheme { + t.Errorf("scheme mutated: got %q, want %q", gotURL.Scheme, inputURL.Scheme) + } + if gotURL.Host != inputURL.Host { + t.Errorf("host mutated: got %q, want %q", gotURL.Host, inputURL.Host) + } + if gotURL.RawQuery != inputURL.RawQuery { + t.Errorf("query mutated: got %q, want %q", gotURL.RawQuery, inputURL.RawQuery) + } + if got, want := gotURL.User.Username(), tt.args.user; got != want { + t.Errorf("username: got %q, want %q", got, want) + } + if pw, ok := gotURL.User.Password(); !ok || pw != tt.args.password { + t.Errorf("password: got %q (set=%v), want %q", pw, ok, tt.args.password) + } + if got, want := gotURL.Path, "/"+tt.args.dbName; got != want { + t.Errorf("path: got %q, want %q", got, want) } }) } From e36ded4fc756c705a2c77a8d7877559ad377f67c Mon Sep 17 00:00:00 2001 From: Dmitrii Creed Date: Thu, 7 May 2026 22:55:54 +0400 Subject: [PATCH 07/11] chore(security-scan): per-line nosemgrep on sc.sh; drop .semgrepignore Replace the file-level `.semgrepignore` exclusion with a single `# nosemgrep: shell-curl-pipe-to-shell` comment above the one problematic line in `sc.sh` (the upstream pulumi installer bootstrap). Why: blanket-ignoring the whole file means any future curl-pipe added to `sc.sh` for any other reason would also slip through. With a per-line ignore, only the specific pulumi-installer line is exempt and everything else in `sc.sh` stays in-scope for the shell rules. `.semgrepignore` is now empty and removed. Signed-off-by: Dmitrii Creed --- .semgrepignore | 9 --------- sc.sh | 1 + 2 files changed, 1 insertion(+), 9 deletions(-) delete mode 100644 .semgrepignore diff --git a/.semgrepignore b/.semgrepignore deleted file mode 100644 index c56dd714..00000000 --- a/.semgrepignore +++ /dev/null @@ -1,9 +0,0 @@ -# Semgrep ignore — applied by the security-scan workflow. -# Scope: this file excludes paths whose security model is not the same as -# our CI scripts. Keep it minimal and per-path; do not blanket-ignore -# directories without a specific reason. - -# Published end-user install bootstrap. Runs on the user's machine, not in -# our CI. The `curl ... | sh` in this script is the upstream pulumi -# installer's own pattern and is out of scope for our CI shell rules. -sc.sh diff --git a/sc.sh b/sc.sh index a37f90fb..5570bb1d 100755 --- a/sc.sh +++ b/sc.sh @@ -520,6 +520,7 @@ if ! [ -x "$(command -v pulumi)" ]; then echo "🔧 Pulumi not found, installing..." if [[ "$PLATFORM" == "linux" ]]; then echo "📦 Installing Pulumi for Linux..." + # nosemgrep: shell-curl-pipe-to-shell -- pulumi's documented upstream installer (https://www.pulumi.com/docs/install/), invoked from this end-user installer running on the user's own machine, not in CI if curl -fsSL https://get.pulumi.com | sh; then echo "✅ Pulumi installed successfully" else From b5dfb33ca3184f929a4433d33c3f8aa0b4e9220e Mon Sep 17 00:00:00 2001 From: Dmitrii Creed Date: Thu, 7 May 2026 23:17:47 +0400 Subject: [PATCH 08/11] ci(notify): inline clickable title via notify-telegram link-url/link-text/suffix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wire the api workflows to the new notify-telegram inputs (clickable title inline, no separate URL line, no Telegram preview card). Per workflow: * branch.yaml (pull_request) — link-url = PR html_url; link-text = `PR #N: `. Falls back to run URL + workflow name when somehow triggered without a PR. * branch-preview.yaml (workflow_dispatch) — link-url = run URL; link-text = `v<preview-version>`. * push.yaml (push to main) — link-url = head commit URL with run URL fallback; link-text = `v<release-version>`. * build-staging.yml (push to main) — link-url = head commit URL with run URL fallback; link-text = workflow name (job-level VERSION env isn't visible in the finalize job, and adding an inter-job output just for this is more change than warranted). Branch ref pinned to `@feat/notify-telegram-append-run-url` for testing the actions PR end-to-end. Will bump to `@main` after that PR merges. Signed-off-by: Dmitrii Creed <creeed22@gmail.com> --- .github/workflows/branch-preview.yaml | 22 ++++++++++++++-------- .github/workflows/branch.yaml | 22 ++++++++++++++-------- .github/workflows/build-staging.yml | 22 ++++++++++++++-------- .github/workflows/push.yaml | 22 ++++++++++++++-------- 4 files changed, 56 insertions(+), 32 deletions(-) diff --git a/.github/workflows/branch-preview.yaml b/.github/workflows/branch-preview.yaml index f53fbfd0..1e7fe438 100644 --- a/.github/workflows/branch-preview.yaml +++ b/.github/workflows/branch-preview.yaml @@ -429,19 +429,25 @@ jobs: - name: Notify Telegram (success) if: ${{ success() && !contains(needs.*.result, 'failure') }} continue-on-error: true - uses: simple-container-com/actions/notify-telegram@main + uses: simple-container-com/actions/notify-telegram@feat/notify-telegram-append-run-url with: - chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} - token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} - text: ✅ preview published (${{ steps.extract_git_ref.outputs.branch }}) (v${{ needs.prepare.outputs.version }}) - ${{ steps.extract_git_ref.outputs.message }} by ${{ steps.extract_git_ref.outputs.author }} + chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} + token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} + text: '✅ preview ' + link-url: ${{ format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} + link-text: 'v${{ needs.prepare.outputs.version }}' + suffix: ' on ${{ steps.extract_git_ref.outputs.branch }} by ${{ steps.extract_git_ref.outputs.author }}' - name: Notify Telegram (failure) if: ${{ failure() || contains(needs.*.result, 'failure') }} continue-on-error: true - uses: simple-container-com/actions/notify-telegram@main + uses: simple-container-com/actions/notify-telegram@feat/notify-telegram-append-run-url with: - chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} - token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} - text: ❗ preview failed (${{ steps.extract_git_ref.outputs.branch }}) - ${{ steps.extract_git_ref.outputs.message }} by ${{ steps.extract_git_ref.outputs.author }} + chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} + token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} + text: '❗ preview ' + link-url: ${{ format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} + link-text: 'v${{ needs.prepare.outputs.version }}' + suffix: ' failed on ${{ steps.extract_git_ref.outputs.branch }} — by ${{ steps.extract_git_ref.outputs.author }}' - name: Build failed due to previously failed steps id: fail_if_needed diff --git a/.github/workflows/branch.yaml b/.github/workflows/branch.yaml index 5578860f..475fe5b8 100644 --- a/.github/workflows/branch.yaml +++ b/.github/workflows/branch.yaml @@ -228,19 +228,25 @@ jobs: - name: Notify Telegram (success) if: ${{ success() && !contains(needs.*.result, 'failure') }} continue-on-error: true - uses: simple-container-com/actions/notify-telegram@main + uses: simple-container-com/actions/notify-telegram@feat/notify-telegram-append-run-url with: - chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} - token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} - text: ✅ success (${{ steps.extract_git_ref.outputs.branch }}) - ${{ steps.extract_git_ref.outputs.message }} by ${{ steps.extract_git_ref.outputs.author }} + chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} + token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} + text: '✅ ' + link-url: ${{ github.event.pull_request.html_url || format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} + link-text: ${{ github.event.pull_request.title && format('PR #{0}: {1}', github.event.pull_request.number, github.event.pull_request.title) || github.workflow }} + suffix: ' by ${{ steps.extract_git_ref.outputs.author }}' - name: Notify Telegram (failure) if: ${{ failure() || contains(needs.*.result, 'failure') }} continue-on-error: true - uses: simple-container-com/actions/notify-telegram@main + uses: simple-container-com/actions/notify-telegram@feat/notify-telegram-append-run-url with: - chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} - token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} - text: ❗ failure (${{ steps.extract_git_ref.outputs.branch }}) - ${{ steps.extract_git_ref.outputs.message }} by ${{ steps.extract_git_ref.outputs.author }} + chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} + token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} + text: '❗ ' + link-url: ${{ github.event.pull_request.html_url || format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} + link-text: ${{ github.event.pull_request.title && format('PR #{0}: {1}', github.event.pull_request.number, github.event.pull_request.title) || github.workflow }} + suffix: ' failed — by ${{ steps.extract_git_ref.outputs.author }}' - name: Build failed due to previously failed steps id: fail_if_needed diff --git a/.github/workflows/build-staging.yml b/.github/workflows/build-staging.yml index 3a44b99a..cf3dd3e6 100644 --- a/.github/workflows/build-staging.yml +++ b/.github/workflows/build-staging.yml @@ -157,20 +157,26 @@ jobs: - name: Notify Telegram (success) if: ${{ success() && !contains(needs.*.result, 'failure') }} continue-on-error: true - uses: simple-container-com/actions/notify-telegram@main + uses: simple-container-com/actions/notify-telegram@feat/notify-telegram-append-run-url with: - chat-id: ${{ needs.build-staging.outputs.cicd-bot-telegram-chat-id }} - token: ${{ needs.build-staging.outputs.cicd-bot-telegram-token }} - text: ✅ staging build success (${{ steps.extract_git_ref.outputs.branch }}) - ${{ steps.extract_git_ref.outputs.message }} by ${{ steps.extract_git_ref.outputs.author }} + chat-id: ${{ needs.build-staging.outputs.cicd-bot-telegram-chat-id }} + token: ${{ needs.build-staging.outputs.cicd-bot-telegram-token }} + text: '✅ staging ' + link-url: ${{ github.event.head_commit.url || format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} + link-text: '${{ github.workflow }}' + suffix: ' on ${{ steps.extract_git_ref.outputs.branch }} by ${{ steps.extract_git_ref.outputs.author }}' - name: Notify Telegram (failure) if: ${{ failure() || contains(needs.*.result, 'failure') }} continue-on-error: true - uses: simple-container-com/actions/notify-telegram@main + uses: simple-container-com/actions/notify-telegram@feat/notify-telegram-append-run-url with: - chat-id: ${{ needs.build-staging.outputs.cicd-bot-telegram-chat-id }} - token: ${{ needs.build-staging.outputs.cicd-bot-telegram-token }} - text: ❗ staging build failure (${{ steps.extract_git_ref.outputs.branch }}) - ${{ steps.extract_git_ref.outputs.message }} by ${{ steps.extract_git_ref.outputs.author }} + chat-id: ${{ needs.build-staging.outputs.cicd-bot-telegram-chat-id }} + token: ${{ needs.build-staging.outputs.cicd-bot-telegram-token }} + text: '❗ staging ' + link-url: ${{ github.event.head_commit.url || format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} + link-text: '${{ github.workflow }}' + suffix: ' failed on ${{ steps.extract_git_ref.outputs.branch }} — by ${{ steps.extract_git_ref.outputs.author }}' - name: Build failed due to previously failed steps id: fail_if_needed diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml index 7f497713..0456f88d 100644 --- a/.github/workflows/push.yaml +++ b/.github/workflows/push.yaml @@ -442,19 +442,25 @@ jobs: - name: Notify Telegram (success) if: ${{ success() && !contains(needs.*.result, 'failure') }} continue-on-error: true - uses: simple-container-com/actions/notify-telegram@main + uses: simple-container-com/actions/notify-telegram@feat/notify-telegram-append-run-url with: - chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} - token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} - text: ✅ success (${{ steps.extract_git_ref.outputs.branch }}) (v${{ needs.prepare.outputs.version }}) - ${{ steps.extract_git_ref.outputs.message }} by ${{ steps.extract_git_ref.outputs.author }} + chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} + token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} + text: '✅ release ' + link-url: ${{ github.event.head_commit.url || format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} + link-text: 'v${{ needs.prepare.outputs.version }}' + suffix: ' on ${{ steps.extract_git_ref.outputs.branch }} by ${{ steps.extract_git_ref.outputs.author }}' - name: Notify Telegram (failure) if: ${{ failure() || contains(needs.*.result, 'failure') }} continue-on-error: true - uses: simple-container-com/actions/notify-telegram@main + uses: simple-container-com/actions/notify-telegram@feat/notify-telegram-append-run-url with: - chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} - token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} - text: ❗ failure (${{ steps.extract_git_ref.outputs.branch }}) - ${{ steps.extract_git_ref.outputs.message }} by ${{ steps.extract_git_ref.outputs.author }} + chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} + token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} + text: '❗ release ' + link-url: ${{ github.event.head_commit.url || format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} + link-text: 'v${{ needs.prepare.outputs.version }}' + suffix: ' failed on ${{ steps.extract_git_ref.outputs.branch }} — by ${{ steps.extract_git_ref.outputs.author }}' - name: Build failed due to previously failed steps id: fail_if_needed From 27ef642c36d75ee18584026c1648cf493bcb8063 Mon Sep 17 00:00:00 2001 From: Dmitrii Creed <creeed22@gmail.com> Date: Thu, 7 May 2026 23:31:18 +0400 Subject: [PATCH 09/11] fix(branch.yaml): quote link-text expression to survive YAML colon Run 25516915000 startup-failed (0 jobs) because the link-text value: ${{ ... format('PR #{0}: {1}', ...) || ... }} was an unquoted YAML scalar. The `: ` inside the format() string literal got interpreted by the YAML parser as a key/value boundary inside the scalar, which then made the embedded `'` look like an unterminated quote to the GHA expression lexer (actionlint reported "unexpected EOF while lexing end of string literal"). Wrap the whole expression in double quotes so YAML treats it as one opaque scalar and forwards it to the expression engine intact. Single quotes inside (the format() literal) survive YAML double-quoted parsing without escaping. Signed-off-by: Dmitrii Creed <creeed22@gmail.com> --- .github/workflows/branch.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/branch.yaml b/.github/workflows/branch.yaml index 475fe5b8..746e9caf 100644 --- a/.github/workflows/branch.yaml +++ b/.github/workflows/branch.yaml @@ -234,7 +234,7 @@ jobs: token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} text: '✅ ' link-url: ${{ github.event.pull_request.html_url || format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} - link-text: ${{ github.event.pull_request.title && format('PR #{0}: {1}', github.event.pull_request.number, github.event.pull_request.title) || github.workflow }} + link-text: "${{ github.event.pull_request.title && format('PR #{0}: {1}', github.event.pull_request.number, github.event.pull_request.title) || github.workflow }}" suffix: ' by ${{ steps.extract_git_ref.outputs.author }}' - name: Notify Telegram (failure) if: ${{ failure() || contains(needs.*.result, 'failure') }} @@ -245,7 +245,7 @@ jobs: token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} text: '❗ ' link-url: ${{ github.event.pull_request.html_url || format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} - link-text: ${{ github.event.pull_request.title && format('PR #{0}: {1}', github.event.pull_request.number, github.event.pull_request.title) || github.workflow }} + link-text: "${{ github.event.pull_request.title && format('PR #{0}: {1}', github.event.pull_request.number, github.event.pull_request.title) || github.workflow }}" suffix: ' failed — by ${{ steps.extract_git_ref.outputs.author }}' - name: Build failed due to previously failed steps From 8042cedf1631886a4d2fc6c0906845764c82de7f Mon Sep 17 00:00:00 2001 From: Dmitrii Creed <creeed22@gmail.com> Date: Thu, 7 May 2026 23:59:15 +0400 Subject: [PATCH 10/11] ci(notify): clearer telegram text per workflow + use head_ref not merge ref MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dev feedback: prior text was too vague — "✅ success (228/merge) - by Cre-eD" doesn't tell you what just happened (CI passed? deploy? release?) and "228/merge" is GitHub's auto-merge ref, not the actual branch you'd checkout. Per workflow: branch.yaml: '✅ CI passed: <PR title> on <head_ref> by <author>' branch-preview.yaml: '✅ Preview published: v<version> on <head_ref> by <author>' push.yaml: '🚀 Released: v<version> by <author>' build-staging.yml: '🏗️ Staging built: <workflow> on <head_ref> by <author>' (failure variants use ❗ + "<verb> failed:") Branch source bumped from `steps.extract_git_ref.outputs.branch` (which is `$GITHUB_REF_NAME` = `228/merge` on PR runs) to `github.head_ref || github.ref_name` — head_ref is the source branch on PR events; ref_name is the target on push events. Signed-off-by: Dmitrii Creed <creeed22@gmail.com> --- .github/workflows/branch-preview.yaml | 8 ++++---- .github/workflows/branch.yaml | 8 ++++---- .github/workflows/build-staging.yml | 8 ++++---- .github/workflows/push.yaml | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/branch-preview.yaml b/.github/workflows/branch-preview.yaml index 1e7fe438..9bc0cc88 100644 --- a/.github/workflows/branch-preview.yaml +++ b/.github/workflows/branch-preview.yaml @@ -433,10 +433,10 @@ jobs: with: chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} - text: '✅ preview ' + text: '✅ Preview published: ' link-url: ${{ format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} link-text: 'v${{ needs.prepare.outputs.version }}' - suffix: ' on ${{ steps.extract_git_ref.outputs.branch }} by ${{ steps.extract_git_ref.outputs.author }}' + suffix: ' on ${{ github.head_ref || github.ref_name }} by ${{ steps.extract_git_ref.outputs.author }}' - name: Notify Telegram (failure) if: ${{ failure() || contains(needs.*.result, 'failure') }} continue-on-error: true @@ -444,10 +444,10 @@ jobs: with: chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} - text: '❗ preview ' + text: '❗ Preview failed: ' link-url: ${{ format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} link-text: 'v${{ needs.prepare.outputs.version }}' - suffix: ' failed on ${{ steps.extract_git_ref.outputs.branch }} — by ${{ steps.extract_git_ref.outputs.author }}' + suffix: ' on ${{ github.head_ref || github.ref_name }} by ${{ steps.extract_git_ref.outputs.author }}' - name: Build failed due to previously failed steps id: fail_if_needed diff --git a/.github/workflows/branch.yaml b/.github/workflows/branch.yaml index 746e9caf..a3ff5147 100644 --- a/.github/workflows/branch.yaml +++ b/.github/workflows/branch.yaml @@ -232,10 +232,10 @@ jobs: with: chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} - text: '✅ ' + text: '✅ CI passed: ' link-url: ${{ github.event.pull_request.html_url || format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} link-text: "${{ github.event.pull_request.title && format('PR #{0}: {1}', github.event.pull_request.number, github.event.pull_request.title) || github.workflow }}" - suffix: ' by ${{ steps.extract_git_ref.outputs.author }}' + suffix: ' on ${{ github.head_ref || github.ref_name }} by ${{ steps.extract_git_ref.outputs.author }}' - name: Notify Telegram (failure) if: ${{ failure() || contains(needs.*.result, 'failure') }} continue-on-error: true @@ -243,10 +243,10 @@ jobs: with: chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} - text: '❗ ' + text: '❗ CI failed: ' link-url: ${{ github.event.pull_request.html_url || format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} link-text: "${{ github.event.pull_request.title && format('PR #{0}: {1}', github.event.pull_request.number, github.event.pull_request.title) || github.workflow }}" - suffix: ' failed — by ${{ steps.extract_git_ref.outputs.author }}' + suffix: ' on ${{ github.head_ref || github.ref_name }} by ${{ steps.extract_git_ref.outputs.author }}' - name: Build failed due to previously failed steps id: fail_if_needed diff --git a/.github/workflows/build-staging.yml b/.github/workflows/build-staging.yml index cf3dd3e6..3f05ac20 100644 --- a/.github/workflows/build-staging.yml +++ b/.github/workflows/build-staging.yml @@ -161,10 +161,10 @@ jobs: with: chat-id: ${{ needs.build-staging.outputs.cicd-bot-telegram-chat-id }} token: ${{ needs.build-staging.outputs.cicd-bot-telegram-token }} - text: '✅ staging ' + text: '🏗️ Staging built: ' link-url: ${{ github.event.head_commit.url || format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} link-text: '${{ github.workflow }}' - suffix: ' on ${{ steps.extract_git_ref.outputs.branch }} by ${{ steps.extract_git_ref.outputs.author }}' + suffix: ' on ${{ github.head_ref || github.ref_name }} by ${{ steps.extract_git_ref.outputs.author }}' - name: Notify Telegram (failure) if: ${{ failure() || contains(needs.*.result, 'failure') }} @@ -173,10 +173,10 @@ jobs: with: chat-id: ${{ needs.build-staging.outputs.cicd-bot-telegram-chat-id }} token: ${{ needs.build-staging.outputs.cicd-bot-telegram-token }} - text: '❗ staging ' + text: '❗ Staging failed: ' link-url: ${{ github.event.head_commit.url || format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} link-text: '${{ github.workflow }}' - suffix: ' failed on ${{ steps.extract_git_ref.outputs.branch }} — by ${{ steps.extract_git_ref.outputs.author }}' + suffix: ' on ${{ github.head_ref || github.ref_name }} by ${{ steps.extract_git_ref.outputs.author }}' - name: Build failed due to previously failed steps id: fail_if_needed diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml index 0456f88d..fa8b6248 100644 --- a/.github/workflows/push.yaml +++ b/.github/workflows/push.yaml @@ -446,10 +446,10 @@ jobs: with: chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} - text: '✅ release ' + text: '🚀 Released: ' link-url: ${{ github.event.head_commit.url || format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} link-text: 'v${{ needs.prepare.outputs.version }}' - suffix: ' on ${{ steps.extract_git_ref.outputs.branch }} by ${{ steps.extract_git_ref.outputs.author }}' + suffix: ' by ${{ steps.extract_git_ref.outputs.author }}' - name: Notify Telegram (failure) if: ${{ failure() || contains(needs.*.result, 'failure') }} continue-on-error: true @@ -457,10 +457,10 @@ jobs: with: chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} - text: '❗ release ' + text: '❗ Release failed: ' link-url: ${{ github.event.head_commit.url || format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} link-text: 'v${{ needs.prepare.outputs.version }}' - suffix: ' failed on ${{ steps.extract_git_ref.outputs.branch }} — by ${{ steps.extract_git_ref.outputs.author }}' + suffix: ' by ${{ steps.extract_git_ref.outputs.author }}' - name: Build failed due to previously failed steps id: fail_if_needed From 95177a79ab6878233bf6c889ccca151403ba07a7 Mon Sep 17 00:00:00 2001 From: Dmitrii Creed <creeed22@gmail.com> Date: Fri, 8 May 2026 00:11:52 +0400 Subject: [PATCH 11/11] ci(notify): bump notify-telegram refs to @main now that PR#5 has merged simple-container-com/actions PR #5 merged into main, so the inline-link inputs (link-url / link-text / suffix), HTML escaping, and `grep -z` multiline-rejection are all on @main. Drop the temporary @feat ref across all four api workflows. Signed-off-by: Dmitrii Creed <creeed22@gmail.com> --- .github/workflows/branch-preview.yaml | 4 ++-- .github/workflows/branch.yaml | 4 ++-- .github/workflows/build-staging.yml | 4 ++-- .github/workflows/push.yaml | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/branch-preview.yaml b/.github/workflows/branch-preview.yaml index 9bc0cc88..92ba2e80 100644 --- a/.github/workflows/branch-preview.yaml +++ b/.github/workflows/branch-preview.yaml @@ -429,7 +429,7 @@ jobs: - name: Notify Telegram (success) if: ${{ success() && !contains(needs.*.result, 'failure') }} continue-on-error: true - uses: simple-container-com/actions/notify-telegram@feat/notify-telegram-append-run-url + uses: simple-container-com/actions/notify-telegram@main with: chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} @@ -440,7 +440,7 @@ jobs: - name: Notify Telegram (failure) if: ${{ failure() || contains(needs.*.result, 'failure') }} continue-on-error: true - uses: simple-container-com/actions/notify-telegram@feat/notify-telegram-append-run-url + uses: simple-container-com/actions/notify-telegram@main with: chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} diff --git a/.github/workflows/branch.yaml b/.github/workflows/branch.yaml index a3ff5147..1b30b373 100644 --- a/.github/workflows/branch.yaml +++ b/.github/workflows/branch.yaml @@ -228,7 +228,7 @@ jobs: - name: Notify Telegram (success) if: ${{ success() && !contains(needs.*.result, 'failure') }} continue-on-error: true - uses: simple-container-com/actions/notify-telegram@feat/notify-telegram-append-run-url + uses: simple-container-com/actions/notify-telegram@main with: chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} @@ -239,7 +239,7 @@ jobs: - name: Notify Telegram (failure) if: ${{ failure() || contains(needs.*.result, 'failure') }} continue-on-error: true - uses: simple-container-com/actions/notify-telegram@feat/notify-telegram-append-run-url + uses: simple-container-com/actions/notify-telegram@main with: chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} diff --git a/.github/workflows/build-staging.yml b/.github/workflows/build-staging.yml index 3f05ac20..76def95a 100644 --- a/.github/workflows/build-staging.yml +++ b/.github/workflows/build-staging.yml @@ -157,7 +157,7 @@ jobs: - name: Notify Telegram (success) if: ${{ success() && !contains(needs.*.result, 'failure') }} continue-on-error: true - uses: simple-container-com/actions/notify-telegram@feat/notify-telegram-append-run-url + uses: simple-container-com/actions/notify-telegram@main with: chat-id: ${{ needs.build-staging.outputs.cicd-bot-telegram-chat-id }} token: ${{ needs.build-staging.outputs.cicd-bot-telegram-token }} @@ -169,7 +169,7 @@ jobs: - name: Notify Telegram (failure) if: ${{ failure() || contains(needs.*.result, 'failure') }} continue-on-error: true - uses: simple-container-com/actions/notify-telegram@feat/notify-telegram-append-run-url + uses: simple-container-com/actions/notify-telegram@main with: chat-id: ${{ needs.build-staging.outputs.cicd-bot-telegram-chat-id }} token: ${{ needs.build-staging.outputs.cicd-bot-telegram-token }} diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml index fa8b6248..30e1bf4c 100644 --- a/.github/workflows/push.yaml +++ b/.github/workflows/push.yaml @@ -442,7 +442,7 @@ jobs: - name: Notify Telegram (success) if: ${{ success() && !contains(needs.*.result, 'failure') }} continue-on-error: true - uses: simple-container-com/actions/notify-telegram@feat/notify-telegram-append-run-url + uses: simple-container-com/actions/notify-telegram@main with: chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }} @@ -453,7 +453,7 @@ jobs: - name: Notify Telegram (failure) if: ${{ failure() || contains(needs.*.result, 'failure') }} continue-on-error: true - uses: simple-container-com/actions/notify-telegram@feat/notify-telegram-append-run-url + uses: simple-container-com/actions/notify-telegram@main with: chat-id: ${{ needs.build-setup.outputs.cicd-bot-telegram-chat-id }} token: ${{ needs.build-setup.outputs.cicd-bot-telegram-token }}