Skip to content

Commit f15283e

Browse files
committed
wip: begin to show all paid commits
1 parent 29a0cc8 commit f15283e

File tree

3 files changed

+76
-42
lines changed

3 files changed

+76
-42
lines changed

src/api/mod.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use crate::cli::AppState;
2-
use crate::repo::Transaction;
32
use askama_axum::Template;
43
use axum::extract::State;
54
use axum::{
@@ -12,6 +11,13 @@ use float_pretty_print::PrettyPrintFloat;
1211
use rust_embed::Embed;
1312
use tracing::debug;
1413

14+
#[derive(Debug, Clone)]
15+
pub struct PaidCommit {
16+
pub amount: String,
17+
pub timestamp: u64,
18+
pub contributor_name: String,
19+
}
20+
1521
#[derive(Template, Debug, Clone, Default)]
1622
#[template(path = "index.html")]
1723
pub struct IndexTemplate {
@@ -21,7 +27,7 @@ pub struct IndexTemplate {
2127
monero_network: String,
2228
monero_wallet_address: String,
2329
repository_url: String,
24-
monero_transactions: Vec<Transaction>,
30+
commits: Vec<PaidCommit>,
2531
monero_balance_usd: String,
2632
}
2733

src/repo.rs

Lines changed: 65 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use crate::currency::Address;
22
use anyhow::{bail, Result};
33
use base64::prelude::*;
4+
use cached::proc_macro::once;
45
use chrono::{DateTime, Utc};
56
use git2::{Commit, Oid, Repository, Sort};
6-
use std::process::Command;
7+
use std::{path::PathBuf, process::Command};
78
use tempfile::TempDir;
89
use tracing::{debug, info, instrument};
910

@@ -29,13 +30,6 @@ impl Contributor {
2930
}
3031
}
3132

32-
#[derive(Debug, Clone)]
33-
pub struct Transaction {
34-
pub amount: String,
35-
pub timestamp: u64,
36-
pub contributor_name: String,
37-
}
38-
3933
///
4034
pub struct TurbineRepo {
4135
/// Branch to track
@@ -91,6 +85,41 @@ fn get_public_key_id(commit: &Commit) -> Result<String> {
9185
bail!("Failed to get GPG public key ID");
9286
}
9387

88+
/// Receive the public key for the given commit.
89+
#[once(time = "36000", result = true)]
90+
fn import_public_key(commit: &Commit) -> Result<()> {
91+
Command::new("gpg")
92+
.arg("--keyserver")
93+
.arg(std::env::var("TURBINE_GPG_KEYSERVER").unwrap_or("hkp://keyserver.ubuntu.com".into()))
94+
.arg("--recv-keys")
95+
.arg(get_public_key_id(&commit)?)
96+
.spawn()?
97+
.wait()?;
98+
99+
Ok(())
100+
}
101+
102+
/// Verify a commit's GPG signature and return its key ID.
103+
#[once(result = true)]
104+
fn verify_signature(repo: PathBuf, commit: &Commit) -> Result<String> {
105+
// Receive the public key first
106+
import_public_key(commit)?;
107+
108+
// TODO verify the signature ourselves (gpgme?)
109+
if Command::new("git")
110+
.arg("verify-commit")
111+
.arg(commit.id().to_string())
112+
.current_dir(repo)
113+
.spawn()?
114+
.wait()?
115+
.success()
116+
{
117+
Ok(get_public_key_id(&commit)?)
118+
} else {
119+
bail!("Failed to verify signature");
120+
}
121+
}
122+
94123
impl TurbineRepo {
95124
pub fn new(remote: &str, branch: &str) -> Result<Self> {
96125
let tmp = tempfile::tempdir()?;
@@ -132,34 +161,33 @@ impl TurbineRepo {
132161
}
133162
}
134163

135-
/// Verify a commit's GPG signature and return its key ID.
136-
#[instrument(skip(self), ret, level = "trace")]
137-
fn verify_signature(&self, commit: &Commit) -> Result<String> {
138-
// Receive the public key first
139-
Command::new("gpg")
140-
.arg("--keyserver")
141-
.arg(
142-
std::env::var("TURBINE_GPG_KEYSERVER")
143-
.unwrap_or("hkp://keyserver.ubuntu.com".into()),
144-
)
145-
.arg("--recv-keys")
146-
.arg(get_public_key_id(&commit)?)
147-
.spawn()?
148-
.wait()?;
149-
150-
// TODO verify the signature ourselves (gpgme?)
151-
if Command::new("git")
152-
.arg("verify-commit")
153-
.arg(commit.id().to_string())
154-
.current_dir(self.tmp.path())
155-
.spawn()?
156-
.wait()?
157-
.success()
158-
{
159-
Ok(get_public_key_id(&commit)?)
160-
} else {
161-
bail!("Failed to verify signature");
164+
/// Get all signed commits (whether their signatures are valid or not).
165+
pub fn get_signed_commits(&self) -> Result<Vec<Oid>> {
166+
let mut revwalk = self.container.revwalk()?;
167+
let branch = self
168+
.container
169+
.find_branch(&self.branch, git2::BranchType::Local)?;
170+
let branch_ref = branch.into_reference();
171+
172+
revwalk.push(branch_ref.target().unwrap())?;
173+
174+
let mut commits = Vec::new();
175+
loop {
176+
if let Some(next) = revwalk.next() {
177+
let commit = self.container.find_commit(next?)?;
178+
179+
// Check for GPG signature
180+
if let Some(header) = commit.raw_header() {
181+
if header.contains("gpgsig") {
182+
commits.push(commit.id());
183+
}
184+
}
185+
} else {
186+
break;
187+
}
162188
}
189+
190+
Ok(commits)
163191
}
164192

165193
pub fn refresh(&mut self) -> Result<()> {
@@ -197,7 +225,7 @@ impl TurbineRepo {
197225
}
198226
}
199227

200-
if let Ok(key_id) = self.verify_signature(&commit) {
228+
if let Ok(key_id) = verify_signature(self.tmp.path().to_path_buf(), &commit) {
201229
if let Some(message) = commit.message() {
202230
#[cfg(feature = "monero")]
203231
if let Some((_, address)) = message.split_once("XMR") {
@@ -250,7 +278,7 @@ impl TurbineRepo {
250278
}
251279
}
252280

253-
if let Ok(key_id) = self.verify_signature(&commit) {
281+
if let Ok(key_id) = verify_signature(self.tmp.path().to_path_buf(), &commit) {
254282
if let Some(contributor) = self
255283
.contributors
256284
.iter_mut()

templates/index.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -220,10 +220,10 @@
220220
</div>
221221
</div>
222222
<div class="transaction-block">
223-
<p>Transaction log</p>
223+
<p>Paid commits</p>
224224
<ul>
225-
{% for transaction in monero_transactions %}
226-
<li class="list-item">{{ transaction.amount }} XMR</li>
225+
{% for commit in commits %}
226+
<li class="list-item">{{ commit.author }} XMR</li>
227227
{% endfor %}
228228
</ul>
229229
</div>

0 commit comments

Comments
 (0)