1212// See the License for the specific language governing permissions and
1313// limitations under the License.
1414
15- use std:: { collections :: HashMap , sync:: Arc , time:: Duration } ;
15+ use std:: { sync:: Arc , time:: Duration } ;
1616
1717use k8s_openapi:: apimachinery:: pkg:: apis:: meta:: v1:: Condition ;
1818use kube:: {
19- Api ,
19+ Api , ResourceExt ,
2020 api:: { ApiResource , DynamicObject , GroupVersionKind , PostParams } ,
2121} ;
22- use serde_json:: json;
22+ use serde_json:: { Error as JsonError , Value , json} ;
2323use tokio:: time:: interval;
2424use tracing:: debug;
25+ use uuid:: Uuid ;
2526
2627use crate :: {
2728 context:: Context ,
@@ -42,13 +43,13 @@ impl PipelineService {
4243 }
4344
4445 /// Triggers a Tekton PipelineRun for the given repository.
45- pub async fn trigger ( & self , repo : & str , course : & str , current_stage_slug : & str ) -> Result < ( ) > {
46+ pub async fn trigger ( & self , repo : & str , course : & str , stage : & str ) -> Result < String > {
4647 debug ! ( "Triggering PipelineRun for repository: {course} - {repo}" ) ;
4748
48- let resource = self . generate ( repo, course, current_stage_slug ) . await ?;
49- self . api ( ) . create ( & PostParams :: default ( ) , & resource) . await ?;
49+ let resource = self . generate ( repo, course, stage ) . await ?;
50+ let res = self . api ( ) . create ( & PostParams :: default ( ) , & resource) . await ?;
5051
51- Ok ( ( ) )
52+ Ok ( res . name_any ( ) )
5253 }
5354
5455 /// Watches a PipelineRun and invokes the callback only on success.
@@ -108,8 +109,17 @@ impl PipelineService {
108109 )
109110 }
110111
111- /// Generates a PipelineRun resource for the given repository name.
112- async fn generate ( & self , name : & str , course : & str , stage : & str ) -> Result < DynamicObject > {
112+ /// Generates a PipelineRun resource for the given repository.
113+ async fn generate ( & self , repo : & str , course : & str , stage : & str ) -> Result < DynamicObject > {
114+ let name = Uuid :: new_v4 ( ) . to_string ( ) ;
115+
116+ // Define labels for identification
117+ let labels = vec ! [
118+ ( "stackclass.dev/repo" , repo. to_string( ) ) ,
119+ ( "stackclass.dev/course" , course. to_string( ) ) ,
120+ ( "stackclass.dev/stage" , stage. to_string( ) ) ,
121+ ] ;
122+
113123 // Build test cases JSON value from all stages up to the current stage
114124 let stages = StageRepository :: find_stages_until ( & self . ctx . database , course, stage) . await ?;
115125 let slugs: Vec < & str > = stages. iter ( ) . map ( |stage| stage. slug . as_str ( ) ) . collect ( ) ;
@@ -119,30 +129,26 @@ impl PipelineService {
119129 let registry = url:: hostname ( & self . ctx . config . docker_registry_endpoint ) ?;
120130 let org = & self . ctx . config . namespace ;
121131
122- // Define params as a HashMap and then convert it to JSON value
123- let params = HashMap :: from ( [
124- ( "REPO_URL" , format ! ( "{git_endpoint}/{org}/{name}.git" ) ) ,
125- ( "REPO_REF" , "main" . to_string ( ) ) ,
126- ( "COURSE_IMAGE" , format ! ( "{registry}/{org}/{name}:latest" ) ) ,
132+ // Define parameters for the PipelineRun
133+ let params = vec ! [
134+ ( "REPO_URL" , format!( "{git_endpoint}/{org}/{repo}.git" ) ) ,
135+ ( "COURSE_IMAGE" , format!( "{registry}/{org}/{repo}:latest" ) ) ,
127136 ( "TESTER_IMAGE" , format!( "ghcr.io/stackclass/{course}-tester" ) ) ,
128- ( "TEST_IMAGE" , format ! ( "{registry}/{org}/{name }-test:latest" ) ) ,
137+ ( "TEST_IMAGE" , format!( "{registry}/{org}/{repo }-test:latest" ) ) ,
129138 ( "COMMAND" , format!( "/app/{course}-tester" ) ) ,
130139 ( "TEST_CASES_JSON" , cases) ,
131- ( "DEBUG_MODE" , "false" . to_string ( ) ) ,
132- ( "TIMEOUT_SECONDS" , "15" . to_string ( ) ) ,
133- ( "SKIP_ANTI_CHEAT" , "false" . to_string ( ) ) ,
134- ] ) ;
140+ ] ;
135141
136- // Render a PipelineRun resource using the provided repo and parameters
137- resource ( name, params) . map_err ( ApiError :: SerializationError )
142+ // Render a PipelineRun resource with the given name, labels, and params
143+ resource ( & name, labels , params) . map_err ( ApiError :: SerializationError )
138144 }
139145}
140146
141147/// Builds a JSON string representing test cases from a list of slugs.
142148fn build_test_cases_json ( slugs : & [ & str ] ) -> String {
143149 let mut test_cases = Vec :: new ( ) ;
144150 for ( index, slug) in slugs. iter ( ) . enumerate ( ) {
145- test_cases. push ( serde_json :: json!( {
151+ test_cases. push ( json ! ( {
146152 "slug" : slug,
147153 "log_prefix" : format!( "test-{}" , index + 1 ) ,
148154 "title" : format!( "Stage #{}: {}" , index + 1 , slug) ,
@@ -152,18 +158,19 @@ fn build_test_cases_json(slugs: &[&str]) -> String {
152158}
153159
154160/// Creates a new DynamicObject representing a Tekton PipelineRun resource.
155- fn resource (
156- name : & str ,
157- params : HashMap < & ' static str , String > ,
158- ) -> Result < DynamicObject , serde_json :: Error > {
159- let params : serde_json :: Value =
160- params. into_iter ( ) . map ( |( name , value ) | json ! ( { "name" : name , "value" : value } ) ) . collect ( ) ;
161+ fn resource < T > ( name : & str , labels : T , params : T ) -> Result < DynamicObject , JsonError >
162+ where
163+ T : IntoIterator < Item = ( & ' static str , String ) > ,
164+ {
165+ let labels : Value = labels . into_iter ( ) . collect ( ) ;
166+ let params : Value = params. into_iter ( ) . map ( |( k , v ) | json ! ( { "name" : k , "value" : v } ) ) . collect ( ) ;
161167
162168 let resource = json ! ( {
163169 "apiVersion" : "tekton.dev/v1" ,
164170 "kind" : "PipelineRun" ,
165171 "metadata" : {
166- "name" : name
172+ "name" : name,
173+ "labels" : labels
167174 } ,
168175 "spec" : {
169176 "pipelineRef" : {
0 commit comments