From c6b9aa5f3fc204dfdb5998ab8b99687c49bc415d Mon Sep 17 00:00:00 2001 From: sky Date: Tue, 13 Jan 2026 20:45:12 +0800 Subject: [PATCH 1/3] feat: trigger ci pipeline to build aenv image when env created or updated --- envhub/clients/aci_hack_client.go | 8 ++++---- envhub/clients/aci_hook.go | 13 +++++++++++-- envhub/controller/env.go | 13 +++++++++---- envhub/main.go | 16 +++++++++++++++- envhub/service/ci_trigger.go | 7 +++++++ 5 files changed, 46 insertions(+), 11 deletions(-) create mode 100644 envhub/service/ci_trigger.go diff --git a/envhub/clients/aci_hack_client.go b/envhub/clients/aci_hack_client.go index 56f4a32f..179fdb22 100644 --- a/envhub/clients/aci_hack_client.go +++ b/envhub/clients/aci_hack_client.go @@ -24,8 +24,8 @@ import ( type ACIHackClient struct{} -func (cli *ACIHackClient) trigger(name string, version string) error { - cmd := exec.Command("aci_hack", name, version) +func (cli *ACIHackClient) trigger(name string, version string, templateId string, callbackURL string) error { + cmd := exec.Command("aci_hack", name, version, "--template", templateId, "--callback-url", callbackURL) // Get output output, err := cmd.Output() if err != nil { @@ -37,6 +37,6 @@ func (cli *ACIHackClient) trigger(name string, version string) error { var aci = &ACIHackClient{} -func Trigger(name string, version string) error { - return aci.trigger(name, version) +func Trigger(name string, version string, templateId string, callbackURL string) error { + return aci.trigger(name, version, templateId, callbackURL) } diff --git a/envhub/clients/aci_hook.go b/envhub/clients/aci_hook.go index fca86fa1..33e95e72 100644 --- a/envhub/clients/aci_hook.go +++ b/envhub/clients/aci_hook.go @@ -21,7 +21,16 @@ import ( "log" ) -func ACIHook(env *models.Env) { +type ACITrigger struct { + TemplateId string + CallbackURL string +} + +func (c ACITrigger) Trigger(env *models.Env) { + ACIHook(env, c.TemplateId, c.CallbackURL) +} + +func ACIHook(env *models.Env, templateId string, callbackURL string) { // Try to trigger pipeline, use hack logic first (crying) artis := env.Artifacts buildConfig := env.BuildConfig @@ -38,7 +47,7 @@ func ACIHook(env *models.Env) { return } } - err := Trigger(env.Name, env.Version) + err := Trigger(env.Name, env.Version, templateId, callbackURL) if err != nil { log.Printf("Trigger aenv aci image build err:%v", err) } diff --git a/envhub/controller/env.go b/envhub/controller/env.go index 4608e317..6547fa85 100644 --- a/envhub/controller/env.go +++ b/envhub/controller/env.go @@ -24,7 +24,6 @@ import ( "github.com/gin-gonic/gin" - "envhub/clients" "envhub/models" "envhub/service" ) @@ -32,12 +31,14 @@ import ( type EnvController struct { storage service.EnvStorage ossStorage *service.OssStorage + ciTrigger service.CITrigger } -func NewEnvController(storage service.EnvStorage, ossStorage *service.OssStorage) *EnvController { +func NewEnvController(storage service.EnvStorage, ossStorage *service.OssStorage, ciTrigger service.CITrigger) *EnvController { return &EnvController{ storage: storage, ossStorage: ossStorage, + ciTrigger: ciTrigger, } } @@ -160,7 +161,9 @@ func (ctrl *EnvController) CreateEnv(c *gin.Context) { } // aci hook image build - go clients.ACIHook(env) + if ctrl.ciTrigger != nil { + go ctrl.ciTrigger.Trigger(env) + } models.JSONSuccess(c, true) } @@ -239,7 +242,9 @@ func (ctrl *EnvController) UpdateEnv(c *gin.Context) { } // Code changes trigger image build - go clients.ACIHook(env) + if ctrl.ciTrigger != nil { + go ctrl.ciTrigger.Trigger(env) + } models.JSONSuccess(c, true) } diff --git a/envhub/main.go b/envhub/main.go index a7a14f5e..24ba6093 100644 --- a/envhub/main.go +++ b/envhub/main.go @@ -17,6 +17,7 @@ limitations under the License. package main import ( + "envhub/clients" "fmt" "log" "net/http" @@ -41,6 +42,9 @@ var ( redisPassword string redisDB int redisKeyPrefix string + + templateId string + callbackURL string ) func init() { @@ -52,6 +56,8 @@ func init() { pflag.StringVar(&redisPassword, "redis-password", "", "Redis password") pflag.IntVar(&redisDB, "redis-db", 0, "Redis DB index") pflag.StringVar(&redisKeyPrefix, "redis-key-prefix", "env", "Redis key prefix for env data") + pflag.StringVar(&templateId, "template-id", "", "Template ID for pipeline or workflow (optional)") + pflag.StringVar(&callbackURL, "callback-url", "", "Callback URL to notify after operation completion (optional)") } func main() { @@ -81,8 +87,16 @@ func main() { log.Printf("OSS storage is not configured, OSS-related features will be disabled") } + var ciTrigger clients.ACITrigger + if templateId != "" && callbackURL != "" { + ciTrigger = clients.ACITrigger{ + TemplateId: templateId, + CallbackURL: callbackURL, + } + } + // Initialize controllers - envController := controller.NewEnvController(envStorage, ossStorage) + envController := controller.NewEnvController(envStorage, ossStorage, ciTrigger) healthController := controller.NewHealthController(metrics, healthChecker) dataController := controller.NewDatasourceController() diff --git a/envhub/service/ci_trigger.go b/envhub/service/ci_trigger.go new file mode 100644 index 00000000..b41877e0 --- /dev/null +++ b/envhub/service/ci_trigger.go @@ -0,0 +1,7 @@ +package service + +import "envhub/models" + +type CITrigger interface { + Trigger(env *models.Env) +} From 9867349f63f4712c956c532c2846ae6f4cc10d4d Mon Sep 17 00:00:00 2001 From: sky Date: Tue, 13 Jan 2026 21:13:55 +0800 Subject: [PATCH 2/3] fix ci trigger interface in main --- envhub/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/envhub/main.go b/envhub/main.go index 24ba6093..5b298a2a 100644 --- a/envhub/main.go +++ b/envhub/main.go @@ -87,7 +87,7 @@ func main() { log.Printf("OSS storage is not configured, OSS-related features will be disabled") } - var ciTrigger clients.ACITrigger + var ciTrigger service.CITrigger if templateId != "" && callbackURL != "" { ciTrigger = clients.ACITrigger{ TemplateId: templateId, From 49f7d75d0a11be9aa6500f7b673256cf719d8fd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=99=8F=E6=96=87?= Date: Fri, 30 Jan 2026 11:42:14 +0800 Subject: [PATCH 3/3] lint fix --- .pre-commit-config.yaml | 2 +- .../extends/artifacts/artifacts_builder.py | 21 +++++++++---------- aenv/src/cli/templates/default/Dockerfile | 2 +- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 380ea0d2..2007e557 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -58,7 +58,7 @@ repos: name: golangci-lint (api-service) files: ^api-service/ args: [--timeout=5m] - entry: bash -c 'cd api-service && golangci-lint run --timeout=5m' + entry: bash -c 'cd api-service && golangci-lint run -v --timeout=5m' pass_filenames: false - id: golangci-lint name: golangci-lint (controller) diff --git a/aenv/src/cli/extends/artifacts/artifacts_builder.py b/aenv/src/cli/extends/artifacts/artifacts_builder.py index 7582483b..79cda822 100644 --- a/aenv/src/cli/extends/artifacts/artifacts_builder.py +++ b/aenv/src/cli/extends/artifacts/artifacts_builder.py @@ -13,7 +13,6 @@ # limitations under the License. import sys -import time from abc import ABC, abstractmethod from dataclasses import dataclass from pathlib import Path @@ -207,17 +206,11 @@ def _build_image( ) # Process streaming build logs current_step = 0 - last_output_time = time.time() - heartbeat_interval = 30 # Show heartbeat every 30 seconds if no output - last_heartbeat_time = time.time() for log_line in response: if not log_line: continue - current_time = time.time() - last_output_time = current_time - # Handle different types of log messages if "stream" in log_line: stream_text = log_line["stream"].strip() @@ -317,18 +310,24 @@ def _build_image( print(f" ❌ Error: {error_msg}") sys.stdout.flush() raise docker.errors.BuildError(error_msg, [log_line]) - + # Show heartbeat if no output for a while (handled in a separate check) # Note: This is a simple approach. For better UX, consider using threading # to show periodic heartbeats during long-running steps - + # Handle any other log line types that might be present else: # Log unknown log line types for debugging if log_line: # Only print if it's not empty and might be useful - if any(key in log_line for key in ["message", "log", "output"]): - message = log_line.get("message") or log_line.get("log") or log_line.get("output", "") + if any( + key in log_line for key in ["message", "log", "output"] + ): + message = ( + log_line.get("message") + or log_line.get("log") + or log_line.get("output", "") + ) if message: print(f" {message}") sys.stdout.flush() diff --git a/aenv/src/cli/templates/default/Dockerfile b/aenv/src/cli/templates/default/Dockerfile index 3c2789b3..2076d5b7 100644 --- a/aenv/src/cli/templates/default/Dockerfile +++ b/aenv/src/cli/templates/default/Dockerfile @@ -6,4 +6,4 @@ ENV PYTHONPATH=/app/src COPY . . RUN python -m pip install --no-cache-dir -r /app/requirements.txt ENTRYPOINT ["/bin/bash", "-c"] -CMD ["python3 -m aenv.main /app/src"] \ No newline at end of file +CMD ["python3 -m aenv.main /app/src"]