Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions .github/actions/deploy-client-stack/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: 'Deploy Simple Container Client Stack'
description: 'Deploy a Simple Container client stack using internal SC APIs'
branding:
icon: 'upload-cloud'
color: 'blue'

inputs:
stack-name:
description: 'Name of the stack to deploy'
required: true
environment:
description: 'Target environment (staging, prod, etc.)'
required: true
default: 'staging'
sc-config:
description: 'Simple Container configuration (SC_CONFIG secret content)'
required: true
version:
description: 'Version to deploy'
required: false
default: 'latest'
slack-webhook-url:
description: 'Slack webhook URL for notifications (optional)'
required: false
discord-webhook-url:
description: 'Discord webhook URL for notifications (optional)'
required: false
telegram-chat-id:
description: 'Telegram chat ID for notifications (optional)'
required: false
telegram-token:
description: 'Telegram bot token for notifications (optional)'
required: false

outputs:
version:
description: 'Version that was deployed'
environment:
description: 'Environment that was deployed to'
stack-name:
description: 'Stack name that was deployed'
duration:
description: 'Deployment duration'
status:
description: 'Deployment status (success/failure)'

runs:
using: 'docker'
image: 'docker://simplecontainer/github-actions:latest'
env:
GITHUB_ACTION_TYPE: 'deploy-client-stack'
STACK_NAME: ${{ inputs.stack-name }}
ENVIRONMENT: ${{ inputs.environment }}
SC_CONFIG: ${{ inputs.sc-config }}
VERSION: ${{ inputs.version }}
SLACK_WEBHOOK_URL: ${{ inputs.slack-webhook-url }}
DISCORD_WEBHOOK_URL: ${{ inputs.discord-webhook-url }}
TELEGRAM_CHAT_ID: ${{ inputs.telegram-chat-id }}
TELEGRAM_TOKEN: ${{ inputs.telegram-token }}
51 changes: 51 additions & 0 deletions .github/actions/destroy-client-stack/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: 'Destroy Simple Container Client Stack'
description: 'Destroy a Simple Container client stack using internal SC APIs'
branding:
icon: 'trash-2'
color: 'red'

inputs:
stack-name:
description: 'Name of the stack to destroy'
required: true
environment:
description: 'Target environment (staging, prod, etc.)'
required: true
sc-config:
description: 'Simple Container configuration (SC_CONFIG secret content)'
required: true
slack-webhook-url:
description: 'Slack webhook URL for notifications (optional)'
required: false
discord-webhook-url:
description: 'Discord webhook URL for notifications (optional)'
required: false
telegram-chat-id:
description: 'Telegram chat ID for notifications (optional)'
required: false
telegram-token:
description: 'Telegram bot token for notifications (optional)'
required: false

outputs:
environment:
description: 'Environment that was destroyed'
stack-name:
description: 'Stack name that was destroyed'
duration:
description: 'Destruction duration'
status:
description: 'Destruction status (success/failure)'

runs:
using: 'docker'
image: 'docker://simplecontainer/github-actions:latest'
env:
GITHUB_ACTION_TYPE: 'destroy-client-stack'
STACK_NAME: ${{ inputs.stack-name }}
ENVIRONMENT: ${{ inputs.environment }}
SC_CONFIG: ${{ inputs.sc-config }}
SLACK_WEBHOOK_URL: ${{ inputs.slack-webhook-url }}
DISCORD_WEBHOOK_URL: ${{ inputs.discord-webhook-url }}
TELEGRAM_CHAT_ID: ${{ inputs.telegram-chat-id }}
TELEGRAM_TOKEN: ${{ inputs.telegram-token }}
45 changes: 45 additions & 0 deletions .github/actions/destroy-parent-stack/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: 'Destroy Simple Container Parent Stack'
description: 'Destroy a Simple Container parent stack using internal SC APIs'
branding:
icon: 'x-circle'
color: 'red'

inputs:
stack-name:
description: 'Name of the parent stack to destroy'
required: true
sc-config:
description: 'Simple Container configuration (SC_CONFIG secret content)'
required: true
slack-webhook-url:
description: 'Slack webhook URL for notifications (optional)'
required: false
discord-webhook-url:
description: 'Discord webhook URL for notifications (optional)'
required: false
telegram-chat-id:
description: 'Telegram chat ID for notifications (optional)'
required: false
telegram-token:
description: 'Telegram bot token for notifications (optional)'
required: false

outputs:
stack-name:
description: 'Parent stack name that was destroyed'
duration:
description: 'Destruction duration'
status:
description: 'Destruction status (success/failure)'

runs:
using: 'docker'
image: 'docker://simplecontainer/github-actions:latest'
env:
GITHUB_ACTION_TYPE: 'destroy-parent-stack'
STACK_NAME: ${{ inputs.stack-name }}
SC_CONFIG: ${{ inputs.sc-config }}
SLACK_WEBHOOK_URL: ${{ inputs.slack-webhook-url }}
DISCORD_WEBHOOK_URL: ${{ inputs.discord-webhook-url }}
TELEGRAM_CHAT_ID: ${{ inputs.telegram-chat-id }}
TELEGRAM_TOKEN: ${{ inputs.telegram-token }}
50 changes: 50 additions & 0 deletions .github/actions/provision-parent-stack/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: 'Provision Simple Container Parent Stack'
description: 'Provision a Simple Container parent stack using internal SC APIs'
branding:
icon: 'layers'
color: 'blue'

inputs:
stack-name:
description: 'Name of the parent stack to provision'
required: true
sc-config:
description: 'Simple Container configuration (SC_CONFIG secret content)'
required: true
environment:
description: 'Target environment profile'
required: false
default: 'default'
slack-webhook-url:
description: 'Slack webhook URL for notifications (optional)'
required: false
discord-webhook-url:
description: 'Discord webhook URL for notifications (optional)'
required: false
telegram-chat-id:
description: 'Telegram chat ID for notifications (optional)'
required: false
telegram-token:
description: 'Telegram bot token for notifications (optional)'
required: false

outputs:
stack-name:
description: 'Parent stack name that was provisioned'
duration:
description: 'Provisioning duration'
status:
description: 'Provisioning status (success/failure)'

runs:
using: 'docker'
image: 'docker://simplecontainer/github-actions:latest'
env:
GITHUB_ACTION_TYPE: 'provision-parent-stack'
STACK_NAME: ${{ inputs.stack-name }}
ENVIRONMENT: ${{ inputs.environment }}
SC_CONFIG: ${{ inputs.sc-config }}
SLACK_WEBHOOK_URL: ${{ inputs.slack-webhook-url }}
DISCORD_WEBHOOK_URL: ${{ inputs.discord-webhook-url }}
TELEGRAM_CHAT_ID: ${{ inputs.telegram-chat-id }}
TELEGRAM_TOKEN: ${{ inputs.telegram-token }}
12 changes: 12 additions & 0 deletions .sc/stacks/dist/server.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@ provisioner:
type: pulumi-cloud
config:
credentials: "${auth:pulumi}"
cicd:
type: github-actions
config:
organization: "simple-container-com"
environments:
production:
type: production
protection: true
auto-deploy: true
runners: ["ubuntu-22"]
workflow-generation:
enabled: true
templates:
static-website:
type: gcp-static-website
Expand Down
21 changes: 21 additions & 0 deletions SYSTEM_PROMPT.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,27 @@
## Project Overview
This is the Simple Container API project with MkDocs documentation. The project provides infrastructure-as-code capabilities for deploying applications across multiple cloud providers including AWS, GCP, and others.

### Recent Major Additions

#### GitHub Actions Implementation (Zero Duplication ✅)
- **Completely refactored to eliminate ALL duplicate implementations**
- Location: `cmd/github-actions/`, `pkg/githubactions/actions/`, `.github/actions/`
- Single Docker image with 4 action types: deploy-client-stack, provision-parent-stack, destroy-client-stack, destroy-parent-stack
- **Uses ONLY SC's internal APIs**: `pkg/api/logger`, `pkg/api/git`, `pkg/clouds/slack`, `pkg/clouds/discord`, `pkg/provisioner`
- **Eliminated custom packages**: Removed `pkg/githubactions/common/notifications`, custom git, logging, config duplicates
- **Zero Code Duplication**: Single source of truth using SC's proven APIs
- Single `github-actions.Dockerfile` in root, built via welder.yaml
- **Status**: ✅ **Production ready with perfect SC API integration**

#### CI/CD Workflow Generation (In Progress)
- **Dynamic GitHub Actions workflow generation** from `server.yaml` configuration
- New CLI commands: `sc cicd generate`, `sc cicd validate`, `sc cicd sync`, `sc cicd preview`
- Enhanced server.yaml schema with comprehensive CI/CD configuration support
- Location: `pkg/cmd/cmd_cicd/`, `pkg/clouds/github/enhanced_config.go`, `pkg/clouds/github/workflow_generator.go`
- Supports organizational-level workflow templates, environment-specific deployments, and notifications
- Internal API refactor plan documented for using SC internal APIs instead of shell commands
- Status: `sc cicd generate` command completed, other commands pending

## Important Guidelines

### Documentation Requirements
Expand Down
124 changes: 124 additions & 0 deletions cmd/github-actions/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package main

import (
"context"
"fmt"
"os"
"os/signal"
"syscall"

"github.com/simple-container-com/api/pkg/api"
"github.com/simple-container-com/api/pkg/api/git"
"github.com/simple-container-com/api/pkg/api/logger"
"github.com/simple-container-com/api/pkg/githubactions/actions"
"github.com/simple-container-com/api/pkg/provisioner"
)

func main() {
// Setup context with cancellation for graceful shutdown
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

// Handle signals for graceful shutdown
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-sigChan
fmt.Println("\nReceived shutdown signal, cancelling operations...")
cancel()
}()

// Determine action type from command line arguments or environment
actionType := os.Getenv("GITHUB_ACTION_TYPE")
if len(os.Args) > 1 {
actionType = os.Args[1]
}

if actionType == "" {
fmt.Fprintf(os.Stderr, "Error: Action type not specified. Set GITHUB_ACTION_TYPE or provide as argument.\n")
fmt.Fprintf(os.Stderr, "Valid actions: deploy-client-stack, provision-parent-stack, destroy-client-stack, destroy-parent-stack\n")
os.Exit(1)
}

// Validate action type early
validActions := map[string]bool{
"deploy-client-stack": true,
"provision-parent-stack": true,
"destroy-client-stack": true,
"destroy-parent-stack": true,
}

if !validActions[actionType] {
fmt.Fprintf(os.Stderr, "Unknown action type: %s\n", actionType)
fmt.Fprintf(os.Stderr, "Valid actions: deploy-client-stack, provision-parent-stack, destroy-client-stack, destroy-parent-stack\n")
os.Exit(1)
}

// Initialize SC's internal logger
log := logger.New()
log.Info(ctx, "Starting Simple Container GitHub Action: %s", actionType)
log.Info(ctx, "Repository: %s, Run ID: %s", os.Getenv("GITHUB_REPOSITORY"), os.Getenv("GITHUB_RUN_ID"))

// Initialize git repository
gitRepo, err := git.New(git.WithDetectRootDir())
if err != nil {
log.Error(ctx, "Failed to initialize git repository: %v", err)
os.Exit(1)
}

// Initialize provisioner with SC's internal APIs
prov, err := provisioner.New(
provisioner.WithGitRepo(gitRepo),
provisioner.WithLogger(log),
)
if err != nil {
log.Error(ctx, "Failed to initialize provisioner: %v", err)
os.Exit(1)
}

// Initialize provisioner
workDir, _ := os.Getwd()
err = prov.Init(ctx, api.InitParams{
ProjectName: os.Getenv("STACK_NAME"),
RootDir: workDir,
SkipInitialCommit: true,
SkipProfileCreation: true,
Profile: os.Getenv("ENVIRONMENT"),
})
if err != nil {
log.Error(ctx, "Failed to initialize provisioner: %v", err)
os.Exit(1)
}

// Execute action using SC's internal APIs
executor := actions.NewExecutor(prov, log, gitRepo)
var execErr error

switch actionType {
case "deploy-client-stack":
execErr = executor.DeployClientStack(ctx)

case "provision-parent-stack":
execErr = executor.ProvisionParentStack(ctx)

case "destroy-client-stack":
execErr = executor.DestroyClientStack(ctx)

case "destroy-parent-stack":
execErr = executor.DestroyParentStack(ctx)
}

// Handle execution result
if execErr != nil {
if ctx.Err() != nil {
log.Warn(ctx, "Action cancelled: %s, error: %v", actionType, execErr)
fmt.Fprintf(os.Stderr, "Action cancelled: %v\n", execErr)
} else {
log.Error(ctx, "Action failed: %s, error: %v", actionType, execErr)
fmt.Fprintf(os.Stderr, "Action failed: %v\n", execErr)
}
os.Exit(1)
}

log.Info(ctx, "Action completed successfully: %s", actionType)
}
2 changes: 2 additions & 0 deletions cmd/sc/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/simple-container-com/api/pkg/api/logger/color"
"github.com/simple-container-com/api/pkg/cmd/cmd_assistant"
"github.com/simple-container-com/api/pkg/cmd/cmd_cancel"
"github.com/simple-container-com/api/pkg/cmd/cmd_cicd"
"github.com/simple-container-com/api/pkg/cmd/cmd_deploy"
"github.com/simple-container-com/api/pkg/cmd/cmd_destroy"
"github.com/simple-container-com/api/pkg/cmd/cmd_init"
Expand Down Expand Up @@ -81,6 +82,7 @@ func main() {
cmd_destroy.NewDestroyCmd(rootCmdInstance),
cmd_upgrade.NewUpgradeCmd(rootCmdInstance),
cmd_stack.NewStackCmd(rootCmdInstance),
cmd_cicd.NewCicdCmd(rootCmdInstance),
)

rootCmd.PersistentFlags().BoolVarP(&rootParams.Verbose, "verbose", "v", rootParams.Verbose, "Verbose mode")
Expand Down
Loading