1use crate::DbPool;
2use chrono::Utc;
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct Deploy {
7 pub id: i64,
8 pub project_id: Option<i64>,
9 pub git_sha: String,
10 pub version: Option<String>,
11 pub env: Option<String>,
12 pub deployed_at: String,
13 pub description: Option<String>,
14 pub deployer: Option<String>,
15}
16
17impl Deploy {
18 pub fn short_sha(&self) -> &str {
19 if self.git_sha.len() >= 7 {
20 &self.git_sha[..7]
21 } else {
22 &self.git_sha
23 }
24 }
25}
26
27#[derive(Debug, Deserialize)]
28pub struct IncomingDeploy {
29 pub git_sha: String,
30 pub version: Option<String>,
31 pub env: Option<String>,
32 pub description: Option<String>,
33 pub deployer: Option<String>,
34 pub timestamp: Option<String>,
35}
36
37pub fn insert(
38 pool: &DbPool,
39 deploy: &IncomingDeploy,
40 project_id: Option<i64>,
41) -> anyhow::Result<i64> {
42 let conn = pool.get()?;
43 let now = Utc::now().to_rfc3339();
44 let timestamp = deploy.timestamp.as_ref().unwrap_or(&now);
45
46 conn.execute(
47 r#"
48 INSERT INTO deploys (project_id, git_sha, version, env, deployed_at, description, deployer)
49 VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)
50 "#,
51 (
52 project_id,
53 &deploy.git_sha,
54 &deploy.version,
55 &deploy.env,
56 timestamp,
57 &deploy.description,
58 &deploy.deployer,
59 ),
60 )?;
61
62 Ok(conn.last_insert_rowid())
63}
64
65pub fn list(pool: &DbPool, project_id: Option<i64>, limit: i64) -> anyhow::Result<Vec<Deploy>> {
66 let conn = pool.get()?;
67 let mut stmt = conn.prepare(
68 r#"
69 SELECT id, project_id, git_sha, version, env,
70 strftime('%Y-%m-%d %H:%M', deployed_at) as deployed_at,
71 description, deployer
72 FROM deploys
73 WHERE (?1 IS NULL OR project_id = ?1)
74 ORDER BY deployed_at DESC
75 LIMIT ?2
76 "#,
77 )?;
78
79 let deploys = stmt
80 .query_map(rusqlite::params![project_id, limit], |row| {
81 Ok(Deploy {
82 id: row.get(0)?,
83 project_id: row.get(1)?,
84 git_sha: row.get(2)?,
85 version: row.get(3)?,
86 env: row.get(4)?,
87 deployed_at: row.get(5)?,
88 description: row.get(6)?,
89 deployer: row.get(7)?,
90 })
91 })?
92 .collect::<Result<Vec<_>, _>>()?;
93
94 Ok(deploys)
95}
96
97pub fn list_since(
99 pool: &DbPool,
100 project_id: Option<i64>,
101 since: &str,
102) -> anyhow::Result<Vec<Deploy>> {
103 let conn = pool.get()?;
104 let mut stmt = conn.prepare(
105 r#"
106 SELECT id, project_id, git_sha, version, env,
107 deployed_at,
108 description, deployer
109 FROM deploys
110 WHERE deployed_at >= ?1 AND (?2 IS NULL OR project_id = ?2)
111 ORDER BY deployed_at ASC
112 "#,
113 )?;
114
115 let deploys = stmt
116 .query_map(rusqlite::params![since, project_id], |row| {
117 Ok(Deploy {
118 id: row.get(0)?,
119 project_id: row.get(1)?,
120 git_sha: row.get(2)?,
121 version: row.get(3)?,
122 env: row.get(4)?,
123 deployed_at: row.get(5)?,
124 description: row.get(6)?,
125 deployer: row.get(7)?,
126 })
127 })?
128 .collect::<Result<Vec<_>, _>>()?;
129
130 Ok(deploys)
131}
132
133pub fn latest(pool: &DbPool, project_id: Option<i64>) -> anyhow::Result<Option<Deploy>> {
135 let conn = pool.get()?;
136 let deploy = conn
137 .query_row(
138 r#"
139 SELECT id, project_id, git_sha, version, env,
140 strftime('%Y-%m-%d %H:%M', deployed_at) as deployed_at,
141 description, deployer
142 FROM deploys
143 WHERE (?1 IS NULL OR project_id = ?1)
144 ORDER BY deployed_at DESC
145 LIMIT 1
146 "#,
147 rusqlite::params![project_id],
148 |row| {
149 Ok(Deploy {
150 id: row.get(0)?,
151 project_id: row.get(1)?,
152 git_sha: row.get(2)?,
153 version: row.get(3)?,
154 env: row.get(4)?,
155 deployed_at: row.get(5)?,
156 description: row.get(6)?,
157 deployer: row.get(7)?,
158 })
159 },
160 )
161 .ok();
162
163 Ok(deploy)
164}
165
166pub fn delete_before(pool: &DbPool, before: &str) -> anyhow::Result<usize> {
167 let conn = pool.get()?;
168 let deleted = conn.execute("DELETE FROM deploys WHERE deployed_at < ?1", [before])?;
169 Ok(deleted)
170}