new: add interactive wizard and remaining ecosystem templates
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
//! The `go` template: a Go module built through dh-golang.
|
||||
//!
|
||||
//! The source stanza carries `XS-Go-Import-Path`, probed from the `module`
|
||||
//! line of `go.mod` when the packaged tree has one, defaulting to the
|
||||
//! package name (fresh skeletons embed the package name in their own
|
||||
//! `go.mod`).
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use super::{OutputFile, Template, source_dir_of};
|
||||
use crate::new::options::{NewOptions, TemplateId};
|
||||
|
||||
/// Go module (`go.mod`).
|
||||
pub struct Go;
|
||||
|
||||
impl Template for Go {
|
||||
fn id(&self) -> TemplateId {
|
||||
TemplateId::Go
|
||||
}
|
||||
|
||||
/// A stdlib-only `main.go` (no archive dependencies needed to build) and
|
||||
/// the matching `go.mod` whose module path is the package name.
|
||||
fn skeleton(&self, opts: &NewOptions) -> Vec<OutputFile> {
|
||||
vec![
|
||||
OutputFile::new(
|
||||
"go.mod",
|
||||
format!(
|
||||
"module {name}\n\
|
||||
\n\
|
||||
go 1.21\n",
|
||||
name = opts.name,
|
||||
),
|
||||
),
|
||||
OutputFile::new(
|
||||
"main.go",
|
||||
format!(
|
||||
"// Placeholder for {name}, generated by `pkh new`.\n\
|
||||
package main\n\
|
||||
\n\
|
||||
import \"fmt\"\n\
|
||||
\n\
|
||||
func main() {{\n\
|
||||
\tfmt.Println(\"Hello from {command}!\")\n\
|
||||
}}\n",
|
||||
name = opts.name,
|
||||
command = opts.command,
|
||||
),
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
/// No extra debian/ files: dh-golang drives the build.
|
||||
fn debian(&self, _opts: &NewOptions) -> Vec<OutputFile> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
fn build_depends(&self, _opts: &NewOptions) -> Vec<String> {
|
||||
vec!["golang-any".to_string(), "dh-golang".to_string()]
|
||||
}
|
||||
|
||||
fn architecture(&self, _opts: &NewOptions) -> &'static str {
|
||||
"any"
|
||||
}
|
||||
|
||||
fn rules_dh_line(&self) -> String {
|
||||
"dh $@ --buildsystem=golang".to_string()
|
||||
}
|
||||
|
||||
fn source_fields(&self, opts: &NewOptions) -> Vec<(String, String)> {
|
||||
vec![("XS-Go-Import-Path".to_string(), import_path(opts))]
|
||||
}
|
||||
|
||||
/// Name (and default command) from the `module` line of `go.mod`: the
|
||||
/// last path segment is the conventional binary/package name.
|
||||
fn probe(&self, dir: &Path) -> Option<super::ProbeResult> {
|
||||
let module = read_module_line(dir)?;
|
||||
let name = module.rsplit('/').next()?.to_string();
|
||||
if name.is_empty() {
|
||||
return None;
|
||||
}
|
||||
Some(super::ProbeResult {
|
||||
name: Some(name.clone()),
|
||||
command: Some(name),
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// The `XS-Go-Import-Path` value: probed from `go.mod` when the packaged
|
||||
/// tree has one, the package name otherwise (skeleton mode embeds the name
|
||||
/// in the generated `go.mod` anyway).
|
||||
fn import_path(opts: &NewOptions) -> String {
|
||||
source_dir_of(opts)
|
||||
.as_deref()
|
||||
.and_then(read_module_line)
|
||||
.unwrap_or_else(|| opts.name.clone())
|
||||
}
|
||||
|
||||
/// The `module <path>` line of `dir/go.mod`, when present.
|
||||
fn read_module_line(dir: &Path) -> Option<String> {
|
||||
let content = std::fs::read_to_string(dir.join("go.mod")).ok()?;
|
||||
for line in content.lines() {
|
||||
let line = line.trim();
|
||||
if let Some(rest) = line.strip_prefix("module ") {
|
||||
let module = rest.trim();
|
||||
if !module.is_empty() {
|
||||
return Some(module.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::new::options::{License, SourceDir};
|
||||
use tempfile::tempdir;
|
||||
|
||||
fn opts(source_dir: SourceDir) -> NewOptions {
|
||||
NewOptions {
|
||||
name: "mytool".into(),
|
||||
template: TemplateId::Go,
|
||||
source_dir,
|
||||
upstream_version: "0.1.0".into(),
|
||||
revision: 1,
|
||||
summary: "A tool".into(),
|
||||
long_description: "A tool".into(),
|
||||
homepage: None,
|
||||
license: License::Mit,
|
||||
command: "mytool".into(),
|
||||
maintainer: ("Jane".into(), "jane@example.com".into()),
|
||||
dist: "ubuntu".into(),
|
||||
series: "resolute".into(),
|
||||
release: false,
|
||||
depends: Vec::new(),
|
||||
native: false,
|
||||
git: true,
|
||||
autopkgtest: false,
|
||||
pkg_config: false,
|
||||
watch: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn go_template_shape() {
|
||||
let o = opts(SourceDir::Skeleton);
|
||||
let template = super::super::get(TemplateId::Go).unwrap();
|
||||
|
||||
assert_eq!(template.architecture(&o), "any");
|
||||
assert_eq!(
|
||||
template.build_depends(&o),
|
||||
vec!["golang-any".to_string(), "dh-golang".to_string()]
|
||||
);
|
||||
assert_eq!(template.rules_dh_line(), "dh $@ --buildsystem=golang");
|
||||
assert!(template.debian(&o).is_empty());
|
||||
|
||||
let skeleton = template.skeleton(&o);
|
||||
assert!(
|
||||
skeleton
|
||||
.iter()
|
||||
.any(|f| f.path == "go.mod" && f.contents.starts_with("module mytool\n"))
|
||||
);
|
||||
assert!(skeleton.iter().any(|f| f.path == "main.go"));
|
||||
|
||||
// Skeleton mode: no go.mod to probe, the import path is the name.
|
||||
assert_eq!(
|
||||
template.source_fields(&o),
|
||||
vec![("XS-Go-Import-Path".to_string(), "mytool".to_string())]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn go_probe_reads_module_line() {
|
||||
let dir = tempdir().unwrap();
|
||||
std::fs::write(
|
||||
dir.path().join("go.mod"),
|
||||
"module example.com/org/mytool\n\ngo 1.21\n",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let template = super::super::get(TemplateId::Go).unwrap();
|
||||
let probe = template.probe(dir.path()).expect("probe result");
|
||||
assert_eq!(probe.name.as_deref(), Some("mytool"));
|
||||
assert_eq!(probe.command.as_deref(), Some("mytool"));
|
||||
|
||||
// The probed module line wins over the package name for the import
|
||||
// path when packaging an existing tree.
|
||||
let o = opts(SourceDir::Path(dir.path().to_path_buf()));
|
||||
assert_eq!(
|
||||
template.source_fields(&o),
|
||||
vec![(
|
||||
"XS-Go-Import-Path".to_string(),
|
||||
"example.com/org/mytool".to_string()
|
||||
)]
|
||||
);
|
||||
|
||||
// No go.mod: no probe result.
|
||||
let empty = tempdir().unwrap();
|
||||
assert!(template.probe(empty.path()).is_none());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user