//! A fork reaches the git credential and status held above it in its fork chain. //! //! Fork creation copies the parent's git-sync repositories but neither the //! recorded credential status nor the credential itself, both of which are //! server-owned per-workspace state. Without the fallback a fresh fork stops //! qualifying, and every deploy until the next credential pass pushes its branch //! and opens no PR — silently, because nothing about a skipped PR surfaces //! anywhere. Chains nest (a fork of a dev workspace, a fork of that), so the //! depth-2 cases here are what keep the lookup from regressing to the parent. #![cfg(all(feature = "enterprise", feature = "private"))] use sqlx::{Pool, Postgres}; use windmill_common::git_sync_ee::{ git_credential_for_url, repo_supports_managed_git_features, set_git_credential, }; const REPO: &str = "$res:u/admin/repo"; const URL: &str = "https://gitlab.com/grp/proj.git"; #[sqlx::test(fixtures("git_sync_fork_credential"))] async fn a_fork_qualifies_through_the_nearest_ancestors_credential( db: Pool, ) -> anyhow::Result<()> { assert!( repo_supports_managed_git_features(&db, "parent-ws", REPO).await, "the workspace holding the credential qualifies" ); assert!( repo_supports_managed_git_features(&db, "fork-ws", REPO).await, "a fork with no credential of its own qualifies through its parent" ); assert!( repo_supports_managed_git_features(&db, "deep-fork-ws", REPO).await, "a fork of a fork qualifies through the root, two levels up" ); assert!( !repo_supports_managed_git_features(&db, "errored-fork-ws", REPO).await, "a fork whose own credential failed stays disqualified, rather than \ borrowing the parent's healthy one" ); assert!( !repo_supports_managed_git_features(&db, "orphan-ws", REPO).await, "a workspace with no credential and no parent does not qualify" ); Ok(()) } /// The stored credential is shared with forks and bound to one repository. /// /// Both properties are the point of keeping it in `workspace_settings`: sharing /// is what stops a rotation from stranding every fork on a revoked token, and /// the binding is what stops a rewritten resource URL from carrying the token to /// a host of the writer's choosing. #[sqlx::test(fixtures("git_sync_fork_credential"))] async fn a_fork_reads_an_ancestors_credential_for_the_bound_repository_only( db: Pool, ) -> anyhow::Result<()> { set_git_credential(&db, "parent-ws", REPO, URL, "glpat-secret").await?; assert_eq!( git_credential_for_url(&db, "parent-ws", REPO, URL) .await? .as_deref(), Some("glpat-secret"), "the workspace that stored it reads it back" ); assert_eq!( git_credential_for_url(&db, "fork-ws", REPO, URL) .await? .as_deref(), Some("glpat-secret"), "a fork stores none of its own and resolves the parent's" ); assert_eq!( git_credential_for_url(&db, "deep-fork-ws", REPO, URL) .await? .as_deref(), Some("glpat-secret"), "a fork of a fork resolves the root's, two levels up" ); assert_eq!( git_credential_for_url(&db, "fork-ws", REPO, "https://evil.example/grp/proj.git").await?, None, "a resource repointed at another repository resolves to no credential" ); assert_eq!( git_credential_for_url(&db, "orphan-ws", REPO, URL).await?, None, "a workspace with no credential and no parent resolves nothing" ); Ok(()) }