Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/crates_io_database/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,5 @@ utoipa = { version = "=5.4.0", features = ["chrono"] }
claims = "=0.8.0"
crates_io_test_db = { path = "../crates_io_test_db" }
googletest = "=0.14.2"
insta = { version = "=1.43.2", features = ["json"] }
insta = { version = "=1.43.2", features = ["filters", "json"] }
tokio = { version = "=1.47.1", features = ["macros", "rt"] }
98 changes: 98 additions & 0 deletions crates/crates_io_database/src/models/trustpub/gitlab_config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
use crate::schema::trustpub_configs_gitlab;
use chrono::{DateTime, Utc};
use diesel::prelude::*;
use diesel_async::{AsyncPgConnection, RunQueryDsl};
use serde::Serialize;

#[derive(Debug, Identifiable, Queryable, Selectable, Serialize)]
#[diesel(table_name = trustpub_configs_gitlab, check_for_backend(diesel::pg::Pg))]
pub struct GitLabConfig {
pub id: i32,
pub created_at: DateTime<Utc>,
pub crate_id: i32,
pub namespace: String,
pub namespace_id: Option<String>,
pub project: String,
pub workflow_filepath: String,
pub environment: Option<String>,
}

#[derive(Debug, Insertable)]
#[diesel(table_name = trustpub_configs_gitlab, check_for_backend(diesel::pg::Pg))]
pub struct NewGitLabConfig<'a> {
pub crate_id: i32,
pub namespace: &'a str,
pub project: &'a str,
pub workflow_filepath: &'a str,
pub environment: Option<&'a str>,
}

impl NewGitLabConfig<'_> {
pub async fn insert(&self, conn: &mut AsyncPgConnection) -> QueryResult<GitLabConfig> {
self.insert_into(trustpub_configs_gitlab::table)
.returning(GitLabConfig::as_returning())
.get_result(conn)
.await
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::models::krate::*;
use crate::schema::crates;
use crates_io_test_db::TestDatabase;
use diesel_async::RunQueryDsl;
use insta::assert_debug_snapshot;

#[tokio::test]
async fn test_gitlab_config_insert_and_retrieve() {
let test_db = TestDatabase::new();
let mut conn = test_db.async_connect().await;

// Create a test crate first
let test_crate = diesel::insert_into(crates::table)
.values((crates::name.eq("test-crate"),))
.returning(Crate::as_returning())
.get_result(&mut conn)
.await
.unwrap();

// Create a new GitLab config
let new_config = NewGitLabConfig {
crate_id: test_crate.id,
namespace: "rust-lang",
project: "cargo",
workflow_filepath: ".gitlab-ci.yml",
environment: Some("production"),
};

// Insert the config
let inserted_config = new_config.insert(&mut conn).await.unwrap();

// Retrieve the config
let retrieved_config = trustpub_configs_gitlab::table
.filter(trustpub_configs_gitlab::id.eq(inserted_config.id))
.first::<GitLabConfig>(&mut conn)
.await
.unwrap();

// Snapshot test the structure
insta::with_settings!({ filters => vec![(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z", "[datetime]")] }, {
assert_debug_snapshot!(retrieved_config, @r#"
GitLabConfig {
id: 1,
created_at: [datetime],
crate_id: 1,
namespace: "rust-lang",
namespace_id: None,
project: "cargo",
workflow_filepath: ".gitlab-ci.yml",
environment: Some(
"production",
),
}
"#);
});
}
}
2 changes: 2 additions & 0 deletions crates/crates_io_database/src/models/trustpub/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
mod data;
mod github_config;
mod gitlab_config;
mod token;
mod used_jti;

pub use self::data::TrustpubData;
pub use self::github_config::{GitHubConfig, NewGitHubConfig};
pub use self::gitlab_config::{GitLabConfig, NewGitLabConfig};
pub use self::token::NewToken;
pub use self::used_jti::NewUsedJti;
2 changes: 1 addition & 1 deletion crates/crates_io_database/src/schema.patch
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@
diesel::joinable!(readme_renderings -> versions (version_id));
+diesel::joinable!(recent_crate_downloads -> crates (crate_id));
diesel::joinable!(trustpub_configs_github -> crates (crate_id));
diesel::joinable!(trustpub_configs_gitlab -> crates (crate_id));
diesel::joinable!(version_downloads -> versions (version_id));
diesel::joinable!(version_owner_actions -> api_tokens (api_token_id));
@@ -1140,6 +1152,7 @@
publish_limit_buckets,
publish_rate_overrides,
Expand Down
24 changes: 24 additions & 0 deletions crates/crates_io_database/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,28 @@ diesel::table! {
}
}

diesel::table! {
/// Trusted Publisher configuration for GitLab CI
trustpub_configs_gitlab (id) {
/// Unique identifier of the `trustpub_configs_gitlab` row
id -> Int4,
/// Date and time when the configuration was created
created_at -> Timestamptz,
/// Unique identifier of the crate that this configuration is for
crate_id -> Int4,
/// GitLab namespace (user or group) that owns the project
namespace -> Varchar,
/// GitLab namespace ID, populated on first token exchange for resurrection attack protection
namespace_id -> Nullable<Varchar>,
/// Name of the GitLab project that this configuration is for
project -> Varchar,
/// Path to the CI/CD configuration file that will be used to publish the crate
workflow_filepath -> Varchar,
/// GitLab environment that will be used to publish the crate (if `NULL` the environment is unrestricted)
environment -> Nullable<Varchar>,
}
}

diesel::table! {
/// Temporary access tokens for Trusted Publishing
trustpub_tokens (id) {
Expand Down Expand Up @@ -1137,6 +1159,7 @@ diesel::joinable!(publish_rate_overrides -> users (user_id));
diesel::joinable!(readme_renderings -> versions (version_id));
diesel::joinable!(recent_crate_downloads -> crates (crate_id));
diesel::joinable!(trustpub_configs_github -> crates (crate_id));
diesel::joinable!(trustpub_configs_gitlab -> crates (crate_id));
diesel::joinable!(version_downloads -> versions (version_id));
diesel::joinable!(version_owner_actions -> api_tokens (api_token_id));
diesel::joinable!(version_owner_actions -> users (user_id));
Expand Down Expand Up @@ -1171,6 +1194,7 @@ diesel::allow_tables_to_appear_in_same_query!(
reserved_crate_names,
teams,
trustpub_configs_github,
trustpub_configs_gitlab,
trustpub_tokens,
trustpub_used_jtis,
users,
Expand Down
12 changes: 12 additions & 0 deletions crates/crates_io_database_dump/src/dump-db.toml
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,18 @@ repository_name = "private"
workflow_filename = "private"
environment = "private"

[trustpub_configs_gitlab]
dependencies = ["crates"]
[trustpub_configs_gitlab.columns]
id = "private"
created_at = "private"
crate_id = "private"
namespace = "private"
namespace_id = "private"
project = "private"
workflow_filepath = "private"
environment = "private"

[trustpub_tokens.columns]
id = "private"
created_at = "private"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP TABLE trustpub_configs_gitlab;
20 changes: 20 additions & 0 deletions migrations/2025-09-24-104418_add_trustpub_configs_gitlab/up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
CREATE TABLE trustpub_configs_gitlab (
id SERIAL PRIMARY KEY,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
crate_id INTEGER NOT NULL REFERENCES crates ON DELETE CASCADE,
namespace VARCHAR NOT NULL,
namespace_id VARCHAR,
project VARCHAR NOT NULL,
workflow_filepath VARCHAR NOT NULL,
environment VARCHAR
);

comment on table trustpub_configs_gitlab is 'Trusted Publisher configuration for GitLab CI';
comment on column trustpub_configs_gitlab.id is 'Unique identifier of the `trustpub_configs_gitlab` row';
comment on column trustpub_configs_gitlab.created_at is 'Date and time when the configuration was created';
comment on column trustpub_configs_gitlab.crate_id is 'Unique identifier of the crate that this configuration is for';
comment on column trustpub_configs_gitlab.namespace is 'GitLab namespace (user or group) that owns the project';
comment on column trustpub_configs_gitlab.namespace_id is 'GitLab namespace ID, populated on first token exchange for resurrection attack protection';
comment on column trustpub_configs_gitlab.project is 'Name of the GitLab project that this configuration is for';
comment on column trustpub_configs_gitlab.workflow_filepath is 'Path to the CI/CD configuration file that will be used to publish the crate';
comment on column trustpub_configs_gitlab.environment is 'GitLab environment that will be used to publish the crate (if `NULL` the environment is unrestricted)';