Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented pipeline detection for PGXS and pgrx #12

Merged
merged 1 commit into from
Nov 22, 2024
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
2 changes: 1 addition & 1 deletion .ci/test-cover
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ grcov "${DESTDIR}" \
--ignore 'build.rs' \
--ignore 'target/**' \
--excl-start '#(\[cfg\(test\)\]|\[test\])' \
--excl-line 'unreachable\!\(\)' \
--excl-line 'unreachable\!\(' \
--llvm \
--binary-path "target/debug/" \
-s . \
Expand Down
64 changes: 64 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ exclude = [ ".github", ".vscode", ".gitignore", ".ci", ".pre-*.yaml"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
cargo_toml = "0.20.5"
chrono = "0.4.38"
hex = "0.4.3"
iri-string = "0.7.7"
log = { version = "0.4.22", features = ["kv"] }
pgxn_meta = "0.5.1"
regex = "1.11.1"
semver = "1.0.23"
serde = "1.0.215"
serde_json = "1.0.133"
Expand Down
4 changes: 4 additions & 0 deletions src/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ pub enum BuildError {
#[error("unknown build pipeline `{0}`")]
UnknownPipeline(String),

/// Unable to detect the pipeline.
#[error("cannot detect build pipeline and none specified")]
NoPipeline(),

/// IO error.
#[error(transparent)]
Io(#[from] io::Error),
Expand Down
64 changes: 54 additions & 10 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ mod pipeline;

use crate::{error::BuildError, pgrx::Pgrx, pgxs::Pgxs, pipeline::Pipeline};
use pgxn_meta::{dist, release::Release};
use std::path::Path;
use std::path::{Path, PathBuf};

/// Defines the types of builders.
#[derive(Debug, PartialEq)]
Expand All @@ -27,6 +27,46 @@ enum Build {
Pgrx(Pgrx),
}

impl Build {
/// Returns a build pipeline identified by `pipe`, or an error if `pipe`
/// is unknown.
fn new(pipe: &dist::Pipeline, dir: PathBuf, sudo: bool) -> Result<Build, BuildError> {
match pipe {
dist::Pipeline::Pgxs => Ok(Build::Pgxs(Pgxs::new(dir, sudo))),
dist::Pipeline::Pgrx => Ok(Build::Pgrx(Pgrx::new(dir, sudo))),
_ => Err(BuildError::UnknownPipeline(pipe.to_string())),
}
}

/// Attempts to detect and return the appropriate build pipeline to build
/// the contents of `dir`. Returns an error if no pipeline can do so.
fn detect(dir: PathBuf, sudo: bool) -> Result<Build, BuildError> {
// Start with PGXS.
let mut score = Pgxs::confidence(&dir);
let mut pipe = dist::Pipeline::Pgxs;

// Does pgrx have a higher score?
let c = Pgrx::confidence(&dir);
if c > score {
score = c;
pipe = dist::Pipeline::Pgrx;
}

// Try each of the others as they're added.
// Return an error if no confidence.
if score == 0 {
return Err(BuildError::NoPipeline());
}

// Construct the winner.
match pipe {
dist::Pipeline::Pgrx => Ok(Build::Pgrx(Pgrx::new(dir, sudo))),
dist::Pipeline::Pgxs => Ok(Build::Pgxs(Pgxs::new(dir, sudo))),
_ => unreachable!("unknown pipelines {pipe}"),
}
}
}

/// Builder builds PGXN releases.
#[derive(Debug, PartialEq)]
pub struct Builder {
Expand All @@ -36,20 +76,16 @@ pub struct Builder {

impl Builder {
/// Creates and returns a new builder using the appropriate pipeline.
pub fn new<P: AsRef<Path>>(dir: P, meta: Release) -> Result<Self, BuildError> {
pub fn new<P: AsRef<Path>>(dir: P, meta: Release, sudo: bool) -> Result<Self, BuildError> {
let dir = dir.as_ref().to_path_buf();
let pipeline = if let Some(deps) = meta.dependencies() {
if let Some(pipe) = deps.pipeline() {
let dir = dir.as_ref().to_path_buf();
match pipe {
dist::Pipeline::Pgxs => Build::Pgxs(Pgxs::new(dir, true)),
dist::Pipeline::Pgrx => Build::Pgrx(Pgrx::new(dir, true)),
_ => return Err(BuildError::UnknownPipeline(pipe.to_string())),
}
Build::new(pipe, dir, sudo)?
} else {
todo!("Detect pipeline");
Build::detect(dir, sudo)?
}
} else {
todo!("Detect pipeline");
Build::detect(dir, sudo)?
};

Ok(Builder { pipeline, meta })
Expand Down Expand Up @@ -79,6 +115,14 @@ impl Builder {
Build::Pgrx(pgrx) => pgrx.test(),
}
}

/// Installs a distribution on a particular platform and Postgres version.
pub fn install(&self) -> Result<(), BuildError> {
match &self.pipeline {
Build::Pgxs(pgxs) => pgxs.install(),
Build::Pgrx(pgrx) => pgrx.install(),
}
}
}

#[cfg(test)]
Expand Down
35 changes: 34 additions & 1 deletion src/pgrx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@

use crate::error::BuildError;
use crate::pipeline::Pipeline;
use std::path::PathBuf;
use std::path::{Path, PathBuf};

#[cfg(test)]
mod tests;

/// Builder implementation for [pgrx] Pipelines.
///
Expand All @@ -20,15 +23,45 @@ impl Pipeline for Pgrx {
Pgrx { dir, sudo }
}

/// Determines the confidence that the Pgrx pipeline can build the
/// contents of `dir`. Returns 255 if it contains a file named
/// `Cargo.toml` and lists pgrx as a dependency. Otherwise returns 1 if
/// `Cargo.toml` exists and 0 if it does not.
fn confidence(dir: &Path) -> u8 {
let file = dir.join("Cargo.toml");
if !file.exists() {
return 0;
}

// Does Cargo.toml mention pgrx?
if let Ok(cargo) = cargo_toml::Manifest::from_path(file) {
if cargo.dependencies.contains_key("pgrx") {
// Full confidence
return 255;
}
}

// Have Cargo.toml but no dependence on pgrx. Weak confidence.
1
}

/// Runs `cargo init`.
fn configure(&self) -> Result<(), BuildError> {
Ok(())
}

/// Runs `cargo build`.
fn compile(&self) -> Result<(), BuildError> {
Ok(())
}

/// Runs `cargo test`.
fn test(&self) -> Result<(), BuildError> {
Ok(())
}

/// Runs `cargo install`.
fn install(&self) -> Result<(), BuildError> {
Ok(())
}
}
48 changes: 48 additions & 0 deletions src/pgrx/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use super::*;
use std::{fs::File, io::Write};
use tempfile::tempdir;

#[test]
fn confidence() -> Result<(), BuildError> {
let tmp = tempdir()?;
// No Cargo.toml.
assert_eq!(0, Pgrx::confidence(tmp.as_ref()));

// Create a Cargo.toml.
let mut file = File::create(tmp.as_ref().join("Cargo.toml"))?;
assert_eq!(1, Pgrx::confidence(tmp.as_ref()));

// Add a pgrx dependency.
writeln!(&file, "[dependencies]\npgrx = \"0.12.6\"")?;
file.flush().unwrap();
assert_eq!(255, Pgrx::confidence(tmp.as_ref()));

// Add another dependency (to be ignored).
writeln!(&file, "serde_json = \"1.0\"")?;
assert_eq!(255, Pgrx::confidence(tmp.as_ref()));

Ok(())
}

#[test]
fn new() {
let dir = Path::new(env!("CARGO_MANIFEST_DIR"));
let pipe = Pgrx::new(dir.to_path_buf(), false);
assert_eq!(dir, pipe.dir);
assert!(!pipe.sudo);

let dir2 = dir.join("corpus");
let pipe = Pgrx::new(dir2.to_path_buf(), true);
assert_eq!(dir2, pipe.dir);
assert!(pipe.sudo);
}

#[test]
fn configure_et_al() {
let dir = Path::new(env!("CARGO_MANIFEST_DIR"));
let pipe = Pgrx::new(dir.to_path_buf(), false);
assert!(pipe.configure().is_ok());
assert!(pipe.compile().is_ok());
assert!(pipe.test().is_ok());
assert!(pipe.install().is_ok());
}
Loading