chore: update releases [ci skip]

This commit is contained in:
etwodev
2025-10-05 14:26:43 +01:00
parent da83dabd83
commit bb2213fd3e
3 changed files with 62 additions and 174 deletions

86
main.go
View File

@@ -2,11 +2,14 @@ package main
import (
"bytes"
"crypto/tls"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)
// -------------------------------
@@ -36,17 +39,17 @@ type EnvVar struct {
// -------------------------------
var (
portainerURL = os.Getenv("PORTAINER_URL") // e.g. https://portainer.example.com/api
portainerToken = os.Getenv("PORTAINER_TOKEN") // API access token
endpointID = os.Getenv("PORTAINER_ENDPOINT") // Portainer endpoint ID
stackName = os.Getenv("STACK_NAME") // Name of stack to create/redeploy
repoURL = os.Getenv("REPO_URL") // Git repo URL
repoRef = os.Getenv("REPO_REF") // e.g. refs/heads/main
repoComposeFile = os.Getenv("REPO_COMPOSE_FILE") // e.g. docker-compose.yml
repoUsername = os.Getenv("REPO_USERNAME") // Git username (if needed)
repoPassword = os.Getenv("REPO_PASSWORD") // Git password/token (if needed)
tlsSkipVerify = os.Getenv("TLS_SKIP_VERIFY") // "true" / "false"
envData = os.Getenv("ENV_DATA") // Optional env vars in JSON format
portainerURL = os.Getenv("PORTAINER_URL")
portainerToken = os.Getenv("PORTAINER_TOKEN")
endpointIDStr = os.Getenv("PORTAINER_ENDPOINT")
stackName = os.Getenv("STACK_NAME")
repoURL = os.Getenv("REPO_URL")
repoRef = os.Getenv("REPO_REF")
repoComposeFile = os.Getenv("REPO_COMPOSE_FILE")
repoUsername = os.Getenv("REPO_USERNAME")
repoPassword = os.Getenv("REPO_PASSWORD")
tlsSkipVerify = os.Getenv("TLS_SKIP_VERIFY")
envData = os.Getenv("ENV_DATA")
)
// -------------------------------
@@ -56,13 +59,18 @@ var (
func request(client *http.Client, method, url string, body interface{}) (*http.Response, error) {
var buf io.Reader
if body != nil {
data, _ := json.Marshal(body)
data, err := json.Marshal(body)
if err != nil {
return nil, fmt.Errorf("failed to marshal request body: %w", err)
}
buf = bytes.NewBuffer(data)
}
req, err := http.NewRequest(method, url, buf)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("Authorization", "Bearer "+portainerToken)
req.Header.Set("Content-Type", "application/json")
return client.Do(req)
@@ -73,32 +81,50 @@ func request(client *http.Client, method, url string, body interface{}) (*http.R
// -------------------------------
func main() {
if portainerURL == "" || portainerToken == "" || endpointID == "" || repoURL == "" || repoRef == "" || repoComposeFile == "" || stackName == "" {
fmt.Println("Missing required environment variables.")
// Validate required environment variables
if portainerURL == "" || portainerToken == "" || endpointIDStr == "" || repoURL == "" || repoRef == "" || repoComposeFile == "" || stackName == "" {
fmt.Println("❌ Missing required environment variables.")
os.Exit(1)
}
client := &http.Client{}
// Convert endpointID to integer
endpointID, err := strconv.Atoi(endpointIDStr)
if err != nil {
fmt.Printf("❌ Invalid PORTAINER_ENDPOINT value: %v\n", err)
os.Exit(1)
}
// Configure HTTP client
client := &http.Client{
Timeout: 15 * time.Second,
}
if tlsSkipVerify == "true" {
client.Transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
}
// 1. Fetch existing stacks
resp, err := request(client, "GET", fmt.Sprintf("%s/stacks", portainerURL), nil)
if err != nil {
panic(err)
fmt.Printf("❌ Error fetching stacks: %v\n", err)
os.Exit(1)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
fmt.Printf("Error fetching stacks: %s\n", string(body))
fmt.Printf("Error fetching stacks: %s\n", string(body))
os.Exit(1)
}
var stacks []Stack
if err := json.NewDecoder(resp.Body).Decode(&stacks); err != nil {
panic(err)
fmt.Printf("❌ Failed to parse stacks response: %v\n", err)
os.Exit(1)
}
// 2. Find existing stack from same repo + name
// 2. Find existing stack
var existing *Stack
for _, s := range stacks {
if s.GitConfig.URL == repoURL && s.Name == stackName {
@@ -108,10 +134,10 @@ func main() {
}
// 3. Parse environment variables
var envVars []EnvVar = []EnvVar{}
var envVars []EnvVar
if envData != "" {
if err := json.Unmarshal([]byte(envData), &envVars); err != nil {
fmt.Printf("Error parsing ENV_DATA: %v\n", err)
fmt.Printf("Error parsing ENV_DATA: %v\n", err)
os.Exit(1)
}
}
@@ -131,22 +157,23 @@ func main() {
"tlsskipVerify": tlsSkipVerify == "true",
}
url := fmt.Sprintf("%s/stacks/create/standalone/repository?endpointId=%s", portainerURL, endpointID)
url := fmt.Sprintf("%s/stacks/create/standalone/repository?endpointId=%d", portainerURL, endpointID)
resp, err = request(client, "POST", url, payload)
if err != nil {
panic(err)
fmt.Printf("❌ Error creating stack: %v\n", err)
os.Exit(1)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
body, _ := io.ReadAll(resp.Body)
fmt.Printf("Error creating stack: %s\n", string(body))
fmt.Printf("Error creating stack: %s\n", string(body))
os.Exit(1)
}
fmt.Printf("✅ Stack %s deployed successfully.\n", stackName)
} else {
// 5. Redeploy stack
// 5. Redeploy existing stack
payload := map[string]interface{}{
"env": envVars,
"prune": true,
@@ -159,16 +186,17 @@ func main() {
"stackName": stackName,
}
url := fmt.Sprintf("%s/stacks/%d/git/redeploy?endpointId=%s", portainerURL, existing.ID, endpointID)
url := fmt.Sprintf("%s/stacks/%d/git/redeploy?endpointId=%d", portainerURL, existing.ID, endpointID)
resp, err = request(client, "PUT", url, payload)
if err != nil {
panic(err)
fmt.Printf("❌ Error redeploying stack: %v\n", err)
os.Exit(1)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
fmt.Printf("Error redeploying stack: %s\n", string(body))
fmt.Printf("Error redeploying stack: %s\n", string(body))
os.Exit(1)
}