@@ -243,14 +243,16 @@ pub struct PushRunnableWorkflow {
243243}
244244
245245/// One workflow-YAML reference to a secret/var/environment name, with the
246- /// repository and workflow it came from. Names only — never values.
246+ /// repository and workflow it came from and — when something satisfies it —
247+ /// the id of the configured row. Names and ids only, never values.
247248#[ derive( Debug , sqlx:: FromRow ) ]
248249pub struct RequirementRefRow {
249250 pub name : String ,
250251 pub repository_id : Uuid ,
251252 pub repository_name : String ,
252253 pub workflow_id : Uuid ,
253254 pub workflow_path : String ,
255+ pub configured_id : Option < Uuid > ,
254256}
255257
256258/// CTE expanding one metadata ref array across a workspace's workflows.
@@ -279,31 +281,37 @@ fn refs_cte(key: &str) -> String {
279281 )
280282}
281283
282- /// Secret names referenced by workflow YAML with no configured secret that
283- /// could satisfy them: a name counts as configured when a workspace-scoped
284- /// secret, a repository-scoped secret on the referencing repo, or an
285- /// environment-scoped secret in any environment carries it (which environment
286- /// applies is a dispatch-time question — "any environment" keeps the rule
287- /// simple and matches how precedence is explained in the UI).
288- pub async fn missing_secret_refs (
284+ /// Every secret name referenced by workflow YAML, with the id of the secret
285+ /// that would satisfy it when one exists: a name counts as configured for a
286+ /// referencing repo when a workspace-scoped secret, a repository-scoped
287+ /// secret on that repo, or an environment-scoped secret in any environment
288+ /// carries it (which environment applies is a dispatch-time question). The
289+ /// LATERAL picks the highest-precedence match so the UI can link straight
290+ /// to it.
291+ pub async fn secret_ref_states (
289292 pool : & PgPool ,
290293 workspace_id : Uuid ,
291294) -> sqlx:: Result < Vec < RequirementRefRow > > {
292295 let sql = refs_cte ( "secretRefs" )
293296 + r#"
294- SELECT name, repository_id, repository_name, workflow_id, workflow_path
297+ SELECT refs.name, refs.repository_id, refs.repository_name,
298+ refs.workflow_id, refs.workflow_path, s.id AS configured_id
295299 FROM refs
296- WHERE NOT EXISTS (
297- SELECT 1 FROM secrets s
300+ LEFT JOIN LATERAL (
301+ SELECT s.id
302+ FROM secrets s
298303 WHERE s.workspace_id = $1
299304 AND s.name = refs.name
300305 AND (
301306 (s.repository_id IS NULL AND s.environment_id IS NULL)
302307 OR s.repository_id = refs.repository_id
303308 OR s.environment_id IS NOT NULL
304309 )
305- )
306- ORDER BY name, repository_name, workflow_path
310+ ORDER BY (s.environment_id IS NOT NULL) DESC,
311+ (s.repository_id IS NOT NULL) DESC
312+ LIMIT 1
313+ ) s ON TRUE
314+ ORDER BY refs.name, refs.repository_name, refs.workflow_path
307315 LIMIT 1000
308316 "# ;
309317 sqlx:: query_as :: < _ , RequirementRefRow > ( & sql)
@@ -313,14 +321,15 @@ pub async fn missing_secret_refs(
313321}
314322
315323/// Every `${{ vars.NAME }}` reference across the workspace — informational
316- /// only (the platform doesn't manage plain variables), so no anti-join .
324+ /// only (the platform doesn't manage plain variables), never configured .
317325pub async fn workspace_var_refs (
318326 pool : & PgPool ,
319327 workspace_id : Uuid ,
320328) -> sqlx:: Result < Vec < RequirementRefRow > > {
321329 let sql = refs_cte ( "varRefs" )
322330 + r#"
323- SELECT name, repository_id, repository_name, workflow_id, workflow_path
331+ SELECT name, repository_id, repository_name, workflow_id, workflow_path,
332+ NULL::uuid AS configured_id
324333 FROM refs
325334 ORDER BY name, repository_name, workflow_path
326335 LIMIT 1000
@@ -331,21 +340,21 @@ pub async fn workspace_var_refs(
331340 . await
332341}
333342
334- /// Environment names bound by workflow YAML with no matching environment row
335- /// (case-insensitive, like dispatch resolution).
336- pub async fn missing_environment_refs (
343+ /// Every environment name bound by workflow YAML, with the id of the
344+ /// matching environment row when it exists (case-insensitive, like dispatch
345+ /// resolution).
346+ pub async fn environment_ref_states (
337347 pool : & PgPool ,
338348 workspace_id : Uuid ,
339349) -> sqlx:: Result < Vec < RequirementRefRow > > {
340350 let sql = refs_cte ( "environments" )
341351 + r#"
342- SELECT name, repository_id, repository_name, workflow_id, workflow_path
352+ SELECT refs.name, refs.repository_id, refs.repository_name,
353+ refs.workflow_id, refs.workflow_path, e.id AS configured_id
343354 FROM refs
344- WHERE NOT EXISTS (
345- SELECT 1 FROM environments e
346- WHERE e.workspace_id = $1 AND lower(e.name) = lower(refs.name)
347- )
348- ORDER BY name, repository_name, workflow_path
355+ LEFT JOIN environments e
356+ ON e.workspace_id = $1 AND lower(e.name) = lower(refs.name)
357+ ORDER BY refs.name, refs.repository_name, refs.workflow_path
349358 LIMIT 1000
350359 "# ;
351360 sqlx:: query_as :: < _ , RequirementRefRow > ( & sql)
@@ -363,7 +372,8 @@ pub async fn list_binding_environment(
363372) -> sqlx:: Result < Vec < RequirementRefRow > > {
364373 let sql = refs_cte ( "environments" )
365374 + r#"
366- SELECT name, repository_id, repository_name, workflow_id, workflow_path
375+ SELECT name, repository_id, repository_name, workflow_id, workflow_path,
376+ NULL::uuid AS configured_id
367377 FROM refs
368378 WHERE lower(refs.name) = lower($2)
369379 ORDER BY repository_name, workflow_path
0 commit comments