Compare commits
2 commits
307e463c87
...
708bcb0b91
Author | SHA1 | Date | |
---|---|---|---|
708bcb0b91 | |||
eea2e14fb0 |
2 changed files with 172 additions and 17 deletions
|
@ -6,3 +6,5 @@ edition = "2021"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
regex = "1.10"
|
regex = "1.10"
|
||||||
|
ureq = "2.10"
|
||||||
|
kxio = "1.2"
|
||||||
|
|
187
src/main.rs
187
src/main.rs
|
@ -1,28 +1,181 @@
|
||||||
use anyhow::Context;
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
use anyhow::{Context, Result};
|
||||||
|
use kxio::fs::DirItem;
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
println!("Forgejo TODO Checker!");
|
println!("Forgejo TODO Checker!");
|
||||||
|
|
||||||
let ci_repo = std::env::var("GITHUB_REPOSITORY").context("GITHUB_REPOSITORY")?;
|
let config = Config {
|
||||||
let ci_server_url = std::env::var("GITHUB_SERVER_URL").context("GITHUB_SERVER_URL")?;
|
fs: kxio::fs::new(
|
||||||
let ci_repo_url = format!("{ci_server_url}/{ci_repo}");
|
std::env::var("GITHUB_WORKSPACE")
|
||||||
|
.context("GITHUB_WORKSPACE")?
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
|
repo: std::env::var("GITHUB_REPOSITORY").context("GITHUB_REPOSITORY")?,
|
||||||
|
server: std::env::var("GITHUB_SERVER_URL").context("GITHUB_SERVER_URL")?,
|
||||||
|
auth_token: std::env::var("REPO_TOKEN").ok(),
|
||||||
|
prefix_pattern: regex::Regex::new(r"(#|//)\s*(TODO|FIXME)").context("prefix regex")?,
|
||||||
|
issue_pattern: regex::Regex::new(r"( |)(\(|\(#)(?P<ISSUE_NUMBER>\d+)(\))")
|
||||||
|
.context("issue regex")?,
|
||||||
|
};
|
||||||
|
|
||||||
// optional arg for private repos
|
println!("Repo: {}", config.repo);
|
||||||
let repo_token = std::env::var("REPO_TOKEN").ok();
|
println!("Prefix: {}", config.prefix_pattern);
|
||||||
|
println!("Issues: {}", config.issue_pattern);
|
||||||
|
|
||||||
// Regex to match a todo or fixme comment
|
// TODO: scan files in workdir
|
||||||
let prefix = regex::Regex::new(r"(#|//)\s*(TODO|FIXME)").context("prefix regex")?;
|
// TODO: ignore files listed in .rgignore .ignore or .gitignore
|
||||||
// Regex to match the rest of the line to find an issue number
|
|
||||||
let issue =
|
|
||||||
regex::Regex::new(r"( |)(\(|\(#)(?P<ISSUE_NUMBER>\d+)(\))").context("issue regex")?;
|
|
||||||
|
|
||||||
println!("[ci_repo:{ci_repo}][ci_repo_url:{ci_repo_url}][repo_token:{repo_token:?}][prefix:{prefix}][issue:{issue}]");
|
let mut found_markers = FoundMarkers::default();
|
||||||
|
|
||||||
|
scan_files(&config, &mut found_markers)?;
|
||||||
|
|
||||||
|
println!("{found_markers:?}");
|
||||||
|
|
||||||
// list files in current directory to get a feel for what we have access to
|
// list files in current directory to get a feel for what we have access to
|
||||||
std::fs::read_dir(".")?
|
// std::fs::read_dir(workdir)?
|
||||||
.filter_map(Result::ok)
|
// .filter_map(Result::ok)
|
||||||
.map(|e| e.path())
|
// .map(|e| e.path())
|
||||||
.for_each(|e| println!("{e:?}"));
|
// .for_each(|e| println!("{e:?}"));
|
||||||
|
|
||||||
|
// Collect open issues:
|
||||||
|
// let limit = 10;
|
||||||
|
// let page = 0;
|
||||||
|
// let api = format!(
|
||||||
|
// "{ci_server_url}/api/v1/repos/{ci_repo}/issues?state=open&limit=${limit}&page=${page}"
|
||||||
|
// );
|
||||||
|
// TODO: add authentication when provided
|
||||||
|
// let issues = ureq::get(&api).call()?.into_string()?;
|
||||||
|
// TODO: parse issues to get list of open issue numbers
|
||||||
|
|
||||||
|
// TODO: loop over list of expected issues and drop any where they do exist and are open
|
||||||
|
// TODO: if remaining list is not empty - add all to error list
|
||||||
|
//
|
||||||
|
// TODO: if error list is empty - exit okay
|
||||||
|
// TODO: if error list is not empty - log erros and exit not okay
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Config {
|
||||||
|
fs: kxio::fs::FileSystem,
|
||||||
|
repo: String,
|
||||||
|
server: String,
|
||||||
|
auth_token: Option<String>,
|
||||||
|
prefix_pattern: Regex,
|
||||||
|
issue_pattern: Regex,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct FoundMarkers {
|
||||||
|
markers: Vec<Marker>,
|
||||||
|
issue_markers: Vec<IssueMarker>,
|
||||||
|
}
|
||||||
|
impl FoundMarkers {
|
||||||
|
fn add_marker(&mut self, marker: Marker) {
|
||||||
|
self.markers.push(marker);
|
||||||
|
}
|
||||||
|
fn add_issue_marker(&mut self, issue_marker: IssueMarker) {
|
||||||
|
self.issue_markers.push(issue_marker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scan_files(config: &Config, found_markers: &mut FoundMarkers) -> Result<()> {
|
||||||
|
scan_dir(config.fs.base(), config, found_markers)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scan_dir(dir: &Path, config: &Config, found_markers: &mut FoundMarkers) -> Result<()> {
|
||||||
|
let read_dir = config.fs.dir_read(dir)?;
|
||||||
|
for entry in read_dir {
|
||||||
|
match entry? {
|
||||||
|
DirItem::File(file) => scan_file(&file, config, found_markers),
|
||||||
|
DirItem::Dir(dir) => scan_dir(&dir, config, found_markers),
|
||||||
|
DirItem::SymLink(_) | DirItem::Fifo(_) | DirItem::Unsupported(_) => Ok(()),
|
||||||
|
}?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scan_file(path: &Path, config: &Config, found_markers: &mut FoundMarkers) -> Result<()> {
|
||||||
|
config
|
||||||
|
.fs
|
||||||
|
.file_read_to_string(path)?
|
||||||
|
.lines()
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(n, line)| prefix_match(n, line, path, config))
|
||||||
|
.for_each(|marker| {
|
||||||
|
if let Some(issue) = config
|
||||||
|
.issue_pattern
|
||||||
|
.find(&marker.line.value)
|
||||||
|
.map(|issue| issue.as_str())
|
||||||
|
.and_then(|issue| issue.parse::<usize>().ok())
|
||||||
|
{
|
||||||
|
found_markers.add_issue_marker(marker.into_issue_marker(issue));
|
||||||
|
} else {
|
||||||
|
found_markers.add_marker(marker);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prefix_match(num: usize, line: &str, path: &Path, config: &Config) -> Option<Marker> {
|
||||||
|
let find = config.prefix_pattern.find(line)?;
|
||||||
|
let marker_type = match find.as_str() {
|
||||||
|
"TODO" => Some(MarkerType::Todo),
|
||||||
|
"FIXME" => Some(MarkerType::Fixme),
|
||||||
|
_ => None,
|
||||||
|
}?;
|
||||||
|
Some(Marker {
|
||||||
|
marker_type,
|
||||||
|
file: path.to_path_buf(),
|
||||||
|
line: Line {
|
||||||
|
num,
|
||||||
|
value: line.to_owned(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// What type of comment
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum MarkerType {
|
||||||
|
Todo,
|
||||||
|
Fixme,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Line {
|
||||||
|
num: usize,
|
||||||
|
value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a TODO or FIXME comment that doesn't have any issue number
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Marker {
|
||||||
|
/// What type of marker
|
||||||
|
marker_type: MarkerType,
|
||||||
|
|
||||||
|
/// Path of the file
|
||||||
|
file: PathBuf,
|
||||||
|
|
||||||
|
/// The line from the file
|
||||||
|
line: Line,
|
||||||
|
}
|
||||||
|
impl Marker {
|
||||||
|
fn into_issue_marker(self, issue: usize) -> IssueMarker {
|
||||||
|
IssueMarker {
|
||||||
|
marker: self,
|
||||||
|
issue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct IssueMarker {
|
||||||
|
/// The marker
|
||||||
|
marker: Marker,
|
||||||
|
|
||||||
|
/// The issue number
|
||||||
|
issue: usize,
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue