This plugin fetches secrets from a GitHub repository and injects them as environment variables into Kubernetes pod manifests. The plugin runs as a sidecar container alongside the argocd-repo-server. The sidecar is built and deployed via GitHub Actions, which has access to GitHub Secrets values during the build process.
- GitHub Action - Builds sidecar with secrets baked in at build time
- Sidecar container - Runs the ConfigManagementPlugin server with pre-loaded secrets
- Plugin script - Reads secrets from environment and modifies manifests
- Plugin configuration - Defines how the plugin discovers and generates manifests
Note: Secrets can only use
inheritfrom intra-org workflow
Create .github/workflows/build-argocd-plugin.yml as a completely self-contained reusable workflow:
name: Build and Deploy Argo CD Plugin Sidecar (Self-Contained)
on:
workflow_call:
inputs:
registry:
description: "Container registry URL (optional override for AF_DOCKER_HOST)"
required: false
type: string
default: "" # Will use AF_DOCKER_HOST secret from tpe-automation runner
image_name:
description: "Image name (defaults to repository name with suffix)"
required: false
type: string
default: ""
argocd_namespace:
description: "ArgoCD namespace"
required: false
type: string
default: "argocd"
deployment_name:
description: "ArgoCD repo-server deployment name"
required: false
type: string
default: "argocd-repo-server"
container_name:
description: "Plugin container name in deployment"
required: false
type: string
default: "github-secrets-plugin"
excluded_secrets:
description: 'Comma-separated list of secret names to exclude from injection (e.g., "GITHUB_TOKEN,KUBE_CONFIG")'
required: false
type: string
default: "GITHUB_TOKEN,KUBE_CONFIG"
enable_k8s_deployment:
description: "Whether to update Kubernetes deployment (requires KUBE_CONFIG)"
required: false
type: boolean
default: true
env:
IMAGE_NAME: ${{ inputs.image_name != '' && inputs.image_name || format('{0}/argocd-github-secrets-plugin', github.repository) }}
jobs:
build-and-push:
runs-on: [self-hosted, tpe-automation] # Use Windows self-hosted runner for JFrog access
permissions:
contents: read
packages: write
steps:
- name: Add Git to PATH (Windows)
shell: pwsh
run: echo "C:\Program Files\Git\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- name: Create temporary build directory
shell: bash
run: |
mkdir -p /tmp/plugin-build
cd /tmp/plugin-build
- name: Generate Python secrets injector script
shell: bash
run: |
cat > /tmp/plugin-build/github-secrets-injector.py << 'EOF'
import os
import yaml
import sys
from pathlib import Path
def load_secrets_from_env():
"""Load secrets that were baked into the container during build"""
secrets = {}
# Read all environment variables prefixed with GITHUB_SECRET_
for key, value in os.environ.items():
if key.startswith("GITHUB_SECRET_"):
secret_name = key.replace("GITHUB_SECRET_", "")
secrets[secret_name] = value
return secrets
def inject_env_vars(manifest, secrets):
"""Inject environment variables into Pod/Deployment manifests"""
if manifest.get("kind") in ["Pod", "Deployment", "StatefulSet", "DaemonSet"]:
containers = manifest.get("spec", {}).get("containers", [])
if manifest.get("kind") == "Deployment":
containers = manifest.get("spec", {}).get("template", {}).get("spec", {}).get("containers", [])
for container in containers:
env = container.get("env", [])
# Add secrets as direct environment variables
for secret_name, secret_value in secrets.items():
env.append({
"name": secret_name,
"value": secret_value
})
container["env"] = env
return manifest
def main():
secrets = load_secrets_from_env()
if not secrets:
print("No GITHUB_SECRET_* environment variables found", file=sys.stderr)
# Read all YAML files in current directory
found_files = False
for yaml_file in Path(".").glob("*.yaml"):
found_files = True
with open(yaml_file) as f:
manifests = yaml.safe_load_all(f)
for manifest in manifests:
if manifest:
modified = inject_env_vars(manifest, secrets)
print("---")
print(yaml.dump(modified))
if not found_files:
print("No YAML files found in current directory", file=sys.stderr)
if __name__ == "__main__":
main()
EOF
- name: Generate plugin configuration
shell: bash
run: |
cat > /tmp/plugin-build/plugin.yaml << 'EOF'
apiVersion: argoproj.io/v1alpha1
kind: ConfigManagementPlugin
metadata:
name: github-secrets-injector
spec:
version: v1.0
init:
command: [sh, -c, 'echo "Initializing GitHub Secrets Injector"']
generate:
command: [python3, /usr/local/bin/github-secrets-injector.py]
discover:
fileName: "*.yaml"
EOF
- name: Generate entrypoint wrapper script
shell: bash
run: |
cat > /tmp/plugin-build/entrypoint-wrapper.sh << 'EOF'
#!/bin/bash
# This script dynamically sets environment variables from build-time environment variables
# All GITHUB_SECRET_* variables are already set during container build
echo "GitHub Secrets Injector starting..."
# List available GITHUB_SECRET_* environment variables (without values for security)
echo "Available secrets:"
env | grep "^GITHUB_SECRET_" | cut -d= -f1 | sort
# Execute the original command
exec "$@"
EOF
- name: Generate dynamic Dockerfile with secrets
shell: bash
run: |
# Start building the Dockerfile
cat > /tmp/plugin-build/Dockerfile << 'EOF'
FROM quay.io/argoproj/argocd:latest
USER root
RUN apt-get update && \
apt-get install -y python3 python3-pip && \
pip3 install pyyaml && \
rm -rf /var/lib/apt/lists/*
EOF
# Add ARG and ENV statements dynamically for each secret
echo "# Dynamic ARG and ENV statements for secrets" >> /tmp/plugin-build/Dockerfile
# This jq command processes all GitHub secrets and generates secret names
echo '${{ toJSON(secrets) }}' | jq -r --arg excluded '${{ inputs.excluded_secrets }}' '
# Split the excluded_secrets string into an array, filtering out empty values
($excluded | split(",") | map(select(length > 0))) as $exclude_list |
# Convert the secrets object to an array of key-value pairs
to_entries |
# Filter out any secrets that are in the exclusion list OR start with AF_DOCKER_
map(select(.key as $k | ($exclude_list | index($k) | not) and ($k | startswith("AF_DOCKER_") | not))) |
# Extract just the secret names and prefix with GITHUB_SECRET_
.[] | "GITHUB_SECRET_" + .key
' | while read secret_name; do
# For each secret, add both ARG (build-time) and ENV (runtime) statements
echo "ARG ${secret_name}" >> /tmp/plugin-build/Dockerfile
echo "ENV ${secret_name}=\${${secret_name}}" >> /tmp/plugin-build/Dockerfile
done
# Complete the Dockerfile
cat >> /tmp/plugin-build/Dockerfile << 'EOF'
COPY github-secrets-injector.py /usr/local/bin/
COPY plugin.yaml /home/argocd/cmp-server/config/
COPY entrypoint-wrapper.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/github-secrets-injector.py /usr/local/bin/entrypoint-wrapper.sh
USER argocd
ENTRYPOINT ["/usr/local/bin/entrypoint-wrapper.sh", "/var/run/argocd/argocd-cmp-server"]
EOF
echo "Generated Dockerfile:"
cat /tmp/plugin-build/Dockerfile
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ secrets.AF_DOCKER_HOST }}
username: ${{ secrets.AF_DOCKER_USER }}
password: ${{ secrets.AF_DOCKER_PASS }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ inputs.registry != '' && inputs.registry || secrets.AF_DOCKER_HOST }}/${{ env.IMAGE_NAME }}
tags: |
type=sha,prefix={{branch}}-
type=ref,event=branch
type=semver,pattern={{version}}
- name: Prepare secrets as build args
id: build-args
shell: bash
run: |
# Convert excluded secrets input to array for jq processing
EXCLUDED_SECRETS='${{ inputs.excluded_secrets }}'
echo "Processing secrets (excluding: $EXCLUDED_SECRETS)"
# Create build args from secrets, excluding specified ones
BUILD_ARGS=""
echo '${{ toJSON(secrets) }}' | jq -r --arg excluded "$EXCLUDED_SECRETS" '
($excluded | split(",") | map(select(length > 0))) as $exclude_list |
to_entries |
map(select(.key as $k | ($exclude_list | index($k) | not) and ($k | startswith("AF_DOCKER_") | not))) |
.[] | "GITHUB_SECRET_" + .key + "=" + .value
' | while IFS='=' read -r key value; do
if [ -n "$BUILD_ARGS" ]; then
BUILD_ARGS="$BUILD_ARGS\n"
fi
# Escape special characters for build args
escaped_value=$(echo "$value" | sed 's/\\/\\\\/g' | sed 's/"/\\"/g')
BUILD_ARGS="${BUILD_ARGS}${key}=\"${escaped_value}\""
done
# Output build args for the Docker build step
echo "build-args<<EOF" >> $GITHUB_OUTPUT
echo '${{ toJSON(secrets) }}' | jq -r --arg excluded "$EXCLUDED_SECRETS" '
($excluded | split(",") | map(select(length > 0))) as $exclude_list |
to_entries |
map(select(.key as $k | ($exclude_list | index($k) | not) and ($k | startswith("AF_DOCKER_") | not))) |
.[] | "GITHUB_SECRET_" + .key + "=" + .value
' >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
# Show which secrets will be included (names only, no values)
echo "Secrets to be included:"
echo '${{ toJSON(secrets) }}' | jq -r --arg excluded "$EXCLUDED_SECRETS" '
($excluded | split(",") | map(select(length > 0))) as $exclude_list |
to_entries |
map(select(.key as $k | ($exclude_list | index($k) | not) and ($k | startswith("AF_DOCKER_") | not))) |
.[] | "GITHUB_SECRET_" + .key
'
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: /tmp/plugin-build
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: ${{ steps.build-args.outputs.build-args }}
- name: Clean up build directory
if: always()
shell: bash
run: |
rm -rf /tmp/plugin-build
- name: Update Argo CD repo-server deployment
if: ${{ inputs.enable_k8s_deployment == true }}
shell: bash
run: |
# Check if KUBE_CONFIG is available
if [ -z "${{ secrets.KUBE_CONFIG }}" ]; then
echo "Warning: KUBE_CONFIG secret not provided. Skipping Kubernetes deployment update."
exit 0
fi
# Install kubectl
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
# Configure kubectl with your cluster credentials
echo "${{ secrets.KUBE_CONFIG }}" | base64 -d > kubeconfig
export KUBECONFIG=./kubeconfig
# Update the image in the deployment
./kubectl set image deployment/${{ inputs.deployment_name }} \
${{ inputs.container_name }}=${{ inputs.registry != '' && inputs.registry || secrets.AF_DOCKER_HOST }}/${{ env.IMAGE_NAME }}:${{ github.sha }} \
-n ${{ inputs.argocd_namespace }}
# Restart the deployment
./kubectl rollout restart deployment/${{ inputs.deployment_name }} -n ${{ inputs.argocd_namespace }}
./kubectl rollout status deployment/${{ inputs.deployment_name }} -n ${{ inputs.argocd_namespace }}Let's break down the complex secret processing logic step by step:
Step 1: Secret Filtering with jq
echo '${{ toJSON(secrets) }}' | jq -r --arg excluded '${{ inputs.excluded_secrets }}' '...'${{ toJSON(secrets) }}: GitHub Actions converts all repository secrets to a JSON object--arg excluded '${{ inputs.excluded_secrets }}': Passes the exclusion list as a jq variable-r: Raw output (no JSON quotes around strings)
Step 2: Parse Exclusion List
($excluded | split(",") | map(select(length > 0))) as $exclude_list- Takes
"GITHUB_TOKEN,KUBE_CONFIG,OTHER_SECRET" - Splits by comma:
["GITHUB_TOKEN", "KUBE_CONFIG", "OTHER_SECRET"] - Filters out empty strings in case of trailing commas
- Stores as
$exclude_listvariable
Step 3: Process Secrets Object
to_entries | map(select(.key as $k | ($exclude_list | index($k) | not) and ($k | startswith("AF_DOCKER_") | not)))to_entries: Converts{"DB_PASS":"value1", "API_KEY":"value2"}to[{"key":"DB_PASS","value":"value1"}, ...]select(.key as $k | ($exclude_list | index($k) | not) and ($k | startswith("AF_DOCKER_") | not)): For each secret, check if:- Its name is NOT in the exclusion list, AND
- Its name does NOT start with "AFDOCKER"
- Result: Only application secrets that should be included
Step 4: Generate Secret Names
.[] | "GITHUB_SECRET_" + .key- Extracts each secret name and prefixes with
GITHUB_SECRET_ - Example:
"DB_PASS"becomes"GITHUB_SECRET_DB_PASS"
Step 5: Generate Dockerfile Statements
while read secret_name; do
echo "ARG ${secret_name}" >> /tmp/plugin-build/Dockerfile
echo "ENV ${secret_name}=\${${secret_name}}" >> /tmp/plugin-build/Dockerfile
done- For each secret name, adds two Dockerfile instructions:
ARG GITHUB_SECRET_DB_PASS: Accepts build-time argumentENV GITHUB_SECRET_DB_PASS=${GITHUB_SECRET_DB_PASS}: Sets runtime environment variable
Example Transformation:
Input Secrets:
{
"DB_PASSWORD": "secret123",
"API_KEY": "key456",
"GITHUB_TOKEN": "github789",
"KUBE_CONFIG": "k8s-config",
"AF_DOCKER_HOST": "jfrog.company.com",
"AF_DOCKER_USER": "svc-account",
"AF_DOCKER_PASS": "jfrog-token"
}Exclusion List: "GITHUB_TOKEN,KUBE_CONFIG"
Auto-Excluded: All AF_DOCKER_* secrets
Generated Dockerfile Additions:
# Dynamic ARG and ENV statements for secrets
ARG GITHUB_SECRET_DB_PASSWORD
ENV GITHUB_SECRET_DB_PASSWORD=${GITHUB_SECRET_DB_PASSWORD}
ARG GITHUB_SECRET_API_KEY
ENV GITHUB_SECRET_API_KEY=${GITHUB_SECRET_API_KEY}Why ARG + ENV Pattern:
ARG: Allows Docker build to accept the secret value via--build-argENV: Makes the secret available to the running container as an environment variable${GITHUB_SECRET_DB_PASSWORD}: References the ARG value to set the ENV value
Basic Usage - No local files required, just reference the workflow:
name: Deploy Argo CD Plugin
on:
workflow_dispatch:
push:
branches:
- main
jobs:
deploy-plugin:
uses: vertexinc/shared-workflows/.github/workflows/build-argocd-plugin.yml@main
with:
# Uses AF_DOCKER_HOST from tpe-automation runner automatically
argocd_namespace: argocd
secrets:
# Pass through ALL secrets automatically
inheritWith Custom Exclusions - Control which secrets to include:
name: Deploy Argo CD Plugin (Custom Configuration)
on:
workflow_dispatch:
push:
branches:
- main
jobs:
deploy-plugin:
uses: vertexinc/shared-workflows/.github/workflows/build-argocd-plugin.yml@main
with:
# Uses AF_DOCKER_HOST from tpe-automation runner
argocd_namespace: argocd
# Exclude different secrets (infrastructure secrets auto-excluded)
excluded_secrets: "KUBE_CONFIG,SOME_OTHER_SECRET,INTERNAL_TOKEN"
# Disable automatic K8s deployment if no KUBE_CONFIG
enable_k8s_deployment: false
secrets: inheritExplicit Secret Passing - Full control over which secrets are shared:
name: Deploy Argo CD Plugin (Explicit Secrets)
on:
workflow_dispatch:
push:
branches:
- main
jobs:
deploy-plugin:
uses: vertexinc/shared-workflows/.github/workflows/build-argocd-plugin.yml@main
with:
# Uses AF_DOCKER_HOST from tpe-automation runner
argocd_namespace: argocd
# No exclusions needed since we're explicitly passing secrets
excluded_secrets: ""
enable_k8s_deployment: true
secrets:
# Infrastructure secrets (required for workflow operation)
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
KUBE_CONFIG: ${{ secrets.KUBE_CONFIG }}
# Application-specific secrets (only these will be injected)
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
API_KEY: ${{ secrets.API_KEY }}
SERVICE_TOKEN: ${{ secrets.SERVICE_TOKEN }}
REDIS_PASSWORD: ${{ secrets.REDIS_PASSWORD }}
SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD }}
# Optional: Environment-specific secrets
# DEV_DB_URL: ${{ secrets.DEV_DB_URL }}
# PROD_API_ENDPOINT: ${{ secrets.PROD_API_ENDPOINT }}Container Registry Only - Just build and push, no K8s deployment:
name: Build Plugin Container Only
on:
workflow_dispatch:
jobs:
build-only:
uses: vertexinc/shared-workflows/.github/workflows/build-argocd-plugin.yml@main
with:
# Uses AF_DOCKER_HOST from tpe-automation runner
# Disable Kubernetes deployment
enable_k8s_deployment: false
# Include all secrets except infrastructure ones (AF_DOCKER_* auto-excluded)
excluded_secrets: ""
secrets: inheritMulti-Environment Setup:
name: Deploy to Multiple Environments
on:
workflow_dispatch:
push:
branches: [main, develop]
jobs:
deploy-dev:
if: github.ref == 'refs/heads/develop'
uses: vertexinc/shared-workflows/.github/workflows/build-argocd-plugin.yml@main
with:
# Uses AF_DOCKER_HOST from tpe-automation runner
excluded_secrets: "PROD_DB_PASSWORD,PROD_API_KEY"
argocd_namespace: argocd-dev
image_name: ${{ github.repository }}/argocd-plugin-dev
secrets: inherit
deploy-prod:
if: github.ref == 'refs/heads/main'
uses: vertexinc/shared-workflows/.github/workflows/build-argocd-plugin.yml@main
with:
# Uses AF_DOCKER_HOST from tpe-automation runner
excluded_secrets: "DEV_DB_PASSWORD,DEV_API_KEY"
argocd_namespace: argocd-prod
image_name: ${{ github.repository }}/argocd-plugin-prod
secrets: inheritExternal Registry Override - Only if you need a different registry:
name: Deploy to External Registry
on:
workflow_dispatch:
jobs:
deploy-external:
uses: vertexinc/shared-workflows/.github/workflows/build-argocd-plugin.yml@main
with:
# Override only when needed for external registries
registry: "ghcr.io"
# AF_DOCKER_* secrets automatically excluded (not needed for GHCR)
excluded_secrets: ""
secrets: inheritAdd the sidecar to your argocd-repo-server deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: argocd-repo-server
spec:
template:
spec:
containers:
- name: argocd-repo-server
# ... existing config
- name: github-secrets-plugin
image: your-jfrog-host.jfrog.io/vertexinc/your-repo/argocd-github-secrets-plugin:main
command: [/var/run/argocd/argocd-cmp-server]
volumeMounts:
- name: var-files
mountPath: /var/run/argocd
- name: plugins
mountPath: /home/argocd/cmp-server/plugins
- name: tmp
mountPath: /tmp
- name: cmp-tmp
mountPath: /home/argocd/cmp-server/tmp
volumes:
- name: cmp-tmp
emptyDir: {}
# ... existing volumesapiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
spec:
source:
repoURL: https://github.com/vertexinc/your-repo
targetRevision: main
path: manifests
plugin:
name: github-secrets-injector- π Zero Setup: No files required in application repositories - just call the workflow
- π§ Centralized Maintenance: All plugin code maintained in one place
- π¦ Self-Contained: Everything needed is generated dynamically in the workflow
- π Secure Secret Handling: Secrets passed through build args, never stored in files
- ποΈ Fully Configurable: Same flexibility with secret exclusions and deployment options
- π Version Control: Plugin logic versioned with the shared workflow repository
- β‘ Instant Adoption: New repositories can use it immediately without setup
- π οΈ Easy Updates: Plugin improvements automatically available to all consumers
- π Simplified Documentation: No complex setup instructions for developers
Enhanced Secret Security:
- β No File Storage: Secrets never written to temporary files or JSON
- β Build-time Only: Secrets only exist during Docker build process
- β Environment Variables: Secrets baked into container as environment variables
- β No Disk Persistence: No secrets left on GitHub Actions runners
- β Dynamic Generation: Dockerfile generated with exact secrets needed
Security Flow:
GitHub Secrets β Build Args β Dockerfile ARG/ENV β Container Environment β Application
Use in Workflows:
# Developer just adds to workflow:
uses: vertexinc/shared-workflows/.github/workflows/build-argocd-plugin.yml@main- Security: Secrets are baked into the container image at build time. Ensure your container registry has appropriate access controls.
- Secret Updates: When secrets change in GitHub, trigger the workflow to rebuild and redeploy the sidecar, this can be done at the same time as application deployments - prior to the deployment step.
- Sidecar Resources: Ensure adequate CPU/memory limits for the sidecar container.
- Plugin Discovery: The plugin will process all
.yamlfiles in the specified path.
graph TB
subgraph "GitHub Repository"
GS[GitHub Secrets<br/>π¦ Application Secrets<br/>π Infrastructure Secrets<br/>AF_DOCKER_...]
GW[GitHub Actions Workflow]
DR[Docker Registry<br/>GHCR/ECR/JFrog]
end
subgraph "Build Process"
GW -->|1. Triggered on push| BA[Build Action]
GS -->|2. Provides all secrets| BA
BA -->|3. Auto-filters secrets| FILTER[Secret Filtering<br/>β
Application Secrets<br/>β AF_DOCKER_<br/>β User exclusions]
FILTER -->|4. Creates build args| ARGS[Docker Build Args<br/>GITHUB_SECRET_...<br/>π± App secrets only]
BA -->|5. Builds container| CI[Container Image<br/>with app env vars]
CI -->|6. Pushes using AF_DOCKER_*| DR
end
subgraph "Kubernetes Cluster"
subgraph "ArgoCD Namespace"
ARS[argocd-repo-server<br/>Deployment]
subgraph "Repo Server Pod"
ARC[argocd-repo-server<br/>container]
PSC[Plugin Sidecar<br/>container]
end
end
subgraph "Application Namespace"
APP[Application<br/>Pods]
end
end
subgraph "ArgoCD Application Process"
AAPP[ArgoCD Application]
MANIFEST[Kubernetes<br/>Manifests]
end
DR -->|7. Pulls image| PSC
PSC -->|8. App secrets already<br/>in environment| ENV[Environment Variables<br/>GITHUB_SECRET_*<br/>π― App secrets only]
AAPP -->|9. Processes manifests<br/>via plugin| PSC
PSC -->|10. Injects app secrets<br/>as env vars| MANIFEST
MANIFEST -->|11. Deploys with<br/>injected app secrets| APP
classDef github fill:#f9f,stroke:#333,stroke-width:2px
classDef k8s fill:#9cf,stroke:#333,stroke-width:2px
classDef argocd fill:#fcf,stroke:#333,stroke-width:2px
classDef process fill:#cfc,stroke:#333,stroke-width:2px
classDef security fill:#ffe0e0,stroke:#d32f2f,stroke-width:2px
class GS,GW,DR github
class ARS,ARC,PSC,APP k8s
class AAPP,MANIFEST argocd
class BA,ARGS,CI,ENV process
class FILTER security
graph LR
subgraph "Plugin Files"
PY[github-secrets-injector.py<br/>Python Script<br/>π Reads GITHUB_SECRET_*<br/>π± App secrets only]
PC[plugin.yaml<br/>Plugin Config]
EW[entrypoint-wrapper.sh<br/>Startup Script<br/>π Zero file processing]
end
subgraph "ArgoCD Integration"
CMP[ConfigManagementPlugin<br/>Server]
DISC[Discovery Phase<br/>*.yaml files]
GEN[Generation Phase<br/>Inject app secrets only]
end
subgraph "Kubernetes Manifests"
ORIG[Original Manifest<br/>kind: Deployment]
MOD[Modified Manifest<br/>+ env vars with app secrets<br/>π― No infrastructure secrets]
end
subgraph "Secret Filtering"
ENV_ALL[All Environment Variables<br/>GITHUB_SECRET_*<br/>π Infrastructure filtered out]
ENV_APP[Application Secrets<br/>GITHUB_SECRET_DB_PASSWORD<br/>GITHUB_SECRET_API_KEY<br/>etc.]
end
EW -->|1. Container starts with<br/>pre-filtered secrets| ENV_ALL
ENV_ALL -->|2. Only app secrets available| ENV_APP
PC -->|3. Configures| CMP
CMP -->|4. Discovers| DISC
DISC -->|5. Triggers| GEN
ENV_APP -->|6. Provides app secrets to| PY
PY -->|7. Processes| ORIG
PY -->|8. Outputs with app secrets| MOD
classDef files fill:#e1f5fe,stroke:#0277bd,stroke-width:2px
classDef argocd fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
classDef k8s fill:#e8f5e8,stroke:#2e7d32,stroke-width:2px
classDef process fill:#fff3e0,stroke:#ef6c00,stroke-width:2px
classDef security fill:#ffe0e0,stroke:#d32f2f,stroke-width:2px
class PY,PC,EW files
class CMP,DISC,GEN argocd
class ORIG,MOD k8s
class ENV_ALL,ENV_APP security
sequenceDiagram
participant Dev as Developer
participant GH as GitHub Repository
participant GA as GitHub Actions
participant REG as JFrog Artifactory
participant K8S as Kubernetes Cluster
participant ARGO as ArgoCD
participant APP as Application
Note over Dev,APP: Setup Phase
Dev->>GH: 1. Push .argo-plugin/ files
Dev->>GH: 2. Configure secrets in repository
Dev->>GH: 3. Setup reusable workflow
Note over Dev,APP: Build Phase
Dev->>GH: 4. Push code changes
GH->>GA: 5. Trigger workflow
GA->>GA: 6. Load all repository secrets
GA->>GA: 7. Auto-filter AF_DOCKER_* and user-excluded secrets
GA->>GA: 8. Create build args with GITHUB_SECRET_* prefix (app secrets only)
GA->>GA: 9. Generate Dockerfile with dynamic ARG/ENV statements
GA->>GA: 10. Build Docker image with application secrets as build args
GA->>REG: 11. Push container image (using AF_DOCKER_* for authentication)
Note over Dev,APP: Deployment Phase
GA->>K8S: 12. Update argocd-repo-server deployment (optional)
K8S->>REG: 13. Pull new plugin sidecar image
K8S->>K8S: 14. Start plugin sidecar container
Note over Dev,APP: Runtime Phase
ARGO->>ARGO: 15. Application sync triggered
ARGO->>K8S: 16. Request manifest processing via plugin
activate K8S
K8S->>K8S: 17. entrypoint-wrapper.sh starts (app secrets already in env)
K8S->>K8S: 18. github-secrets-injector.py processes manifests
K8S->>K8S: 19. Inject application secrets as environment variables
K8S-->>ARGO: 20. Return modified manifests
deactivate K8S
ARGO->>APP: 21. Deploy with injected application secrets
Note over Dev,APP: Application Running
APP->>APP: 22. Application uses secrets from environment variables
Note over GA,REG: Infrastructure secrets transparent to developer
Note over K8S,APP: Only application secrets exposed to containers
sequenceDiagram
participant REPO as Application Repository
participant SHARED as Shared Workflows Repository
participant GA as GitHub Actions Runner
participant REG as JFrog Artifactory
participant K8S as Kubernetes
REPO->>SHARED: 1. calls: uses shared-workflows/.../build-argocd-plugin.yml
REPO->>SHARED: 2. with: configuration inputs (no AF_DOCKER_* knowledge needed)
REPO->>SHARED: 3. secrets: inherit (all secrets)
activate SHARED
SHARED->>GA: 4. Start reusable workflow
GA->>GA: 5. Process inputs (registry auto-detected, excluded_secrets, etc.)
GA->>GA: 6. Auto-filter AF_DOCKER_* and user-excluded secrets
GA->>GA: 7. Transform remaining secrets to GITHUB_SECRET_* format
GA->>GA: 8. Generate Dockerfile with dynamic ARG/ENV statements
GA->>GA: 9. docker build with application secrets as build args
GA->>REG: 10. Push container image (using AF_DOCKER_* internally)
alt enable_k8s_deployment == true
GA->>K8S: 11. Update deployment with new image
K8S-->>GA: 12. Deployment status
end
SHARED-->>REPO: 13. Workflow completion
deactivate SHARED
Note over GA,REG: Infrastructure secrets (AF_DOCKER_*) handled transparently
Note over GA,K8S: Only application secrets injected into containers