File doc/use_cases.md added (mode: 100644) (index 0000000..bf16fcd) |
|
1 |
|
# Migrate your software. |
|
2 |
|
|
|
3 |
|
Saying goodbye to a reliable old framework can be scary. |
|
4 |
|
You need to make sure that every piece of software has been migrated and |
|
5 |
|
working correctly when it gets in production. |
|
6 |
|
With Ambassade it is easy to set up dependencies and pieces of your code in a |
|
7 |
|
hybrid way, making your migrations smoother. |
|
8 |
|
Once you have migrated a part of your code, Ambassade checks for remaining |
|
9 |
|
dependencies in your configuration before you remove your old code. |
|
10 |
|
|
|
11 |
|
# Control over heterogenous packages. |
|
12 |
|
|
|
13 |
|
In some cases, you want to make use of libraries/packages that take some effort |
|
14 |
|
to be compatible with your project. Take the OPC-UA protocol for example. The |
|
15 |
|
protocol has limited implementations because of a 400+ pages counting |
|
16 |
|
specification. There are some useful C libraries available that support the |
|
17 |
|
OPC-UA protocol. If you want to combine the library with Node.js, you have some |
|
18 |
|
solutions, for example: |
|
19 |
|
|
|
20 |
|
* Create an NPM package that interfaces with the C implementation using Node's |
|
21 |
|
Native API. |
|
22 |
|
|
|
23 |
|
* Seperate your project in a server-side part, using [Oat++](https://oatpp.io) |
|
24 |
|
to create an executable for the server, containing the back-end code. The |
|
25 |
|
client-side part will consist of front-end code. |
|
26 |
|
|
|
27 |
|
* Compile the library to WebAssembly and "glue" the compiled binary to your |
|
28 |
|
Node.js code. |
|
29 |
|
|
|
30 |
|
Without having a central package manager that organizes all the different parts |
|
31 |
|
of the code in different languages, keeping control over your software will be |
|
32 |
|
difficult. There are some package managers and git features (like submodules |
|
33 |
|
and mergetrees) that can help you with managing your packages. However, most |
|
34 |
|
of them will not provide a satisfying solution. Ambassade is dedicated to manage |
|
35 |
|
heterogenous packages, and does not even require special configuration files in |
|
36 |
|
the dependencies themselves. |
|
37 |
|
|
|
38 |
|
# Make your software more modular. |
|
39 |
|
|
|
40 |
|
Because Ambassade takes care of dependencies, it gives you as developer more |
|
41 |
|
freedom to make your projects more modular. You could split your project in |
|
42 |
|
more repositories. The advantage of modulating your project is that you can |
|
43 |
|
re-use code more easily. If a deprecated feature should not be implemented |
|
44 |
|
in your project anymore, you can rebuild your project without having to delete |
|
45 |
|
files. Maybe that code could be useful later for future projects. |
|
46 |
|
|
|
47 |
|
If you already have certain features implemented in project A, you can import |
|
48 |
|
that feature in project B with one command, so you only have to focus on |
|
49 |
|
calling the feature from your project. |
|
50 |
|
|
|
51 |
|
# Keep the structure of non-trivial projects simple. |
|
52 |
|
|
|
53 |
|
Once a successful project grows, and grows, it's structure will be less obvious. |
|
54 |
|
The developers that developed the project from the start, know exactly the |
|
55 |
|
relationships between dependencies. New developers that join the development |
|
56 |
|
team will have a harder time figuring the project's structure out. An |
|
57 |
|
easy-to-read configuration file and a flat dependency structure would be a |
|
58 |
|
solution. |
|
59 |
|
|
|
60 |
|
## What is a 'flat dependency structure'? |
|
61 |
|
|
|
62 |
|
NPM version 2 uses a nested dependency structure. This basically means that a |
|
63 |
|
dependency resides in a dependency, which resides in another dependency and so |
|
64 |
|
on. |
|
65 |
|
|
|
66 |
|
A flat dependency structure has all dependencies at the same level. Ambassade |
|
67 |
|
puts all dependencies in one directory, the `dep` directory. This makes finding |
|
68 |
|
dependencies easier and keeps directory paths relatively small. |
|
69 |
|
|
|
70 |
|
# Backup all of your code with one command. |
|
71 |
|
|
|
72 |
|
Having to checkout and backup all of your code manually, could take a lot of |
|
73 |
|
time and joy. Automating checkouts and backups of your code takes a lot of time |
|
74 |
|
as well and isn't that enjoyable. Initializing a Ambassade project with all your |
|
75 |
|
code as dependencies saves a lot of time. This way you can use Ambassade as a |
|
76 |
|
system to backup all important files. |
File src/backend/build.rs changed (mode: 100644) (index 2db696b..be084c6) |
... |
... |
fn build_module(config: serde_json::Value) -> Result<String, String> { |
60 |
60 |
let build_cmd = &config["build"]["linux"]; |
let build_cmd = &config["build"]["linux"]; |
61 |
61 |
|
|
62 |
62 |
if !build_cmd.is_string() { |
if !build_cmd.is_string() { |
63 |
|
return Err(String::from("beheer.json: 'build->linux' should be a string.")); |
|
|
63 |
|
return Err(String::from("ambassade.json: 'build->linux' should be a string.")); |
64 |
64 |
} |
} |
65 |
65 |
|
|
66 |
66 |
super::fetch::build(env::current_dir().unwrap(), String::from(build_cmd.as_str().unwrap())) |
super::fetch::build(env::current_dir().unwrap(), String::from(build_cmd.as_str().unwrap())) |
|
... |
... |
fn build_module(config: serde_json::Value) -> Result<String, String> { |
73 |
73 |
let build_cmd = &config["build"]["os-x"]; |
let build_cmd = &config["build"]["os-x"]; |
74 |
74 |
|
|
75 |
75 |
if !build_cmd.is_string() { |
if !build_cmd.is_string() { |
76 |
|
return Err(String::from("beheer.json: 'build->os-x' should be a string.")); |
|
|
76 |
|
return Err(String::from("ambassade.json: 'build->os-x' should be a string.")); |
77 |
77 |
} |
} |
78 |
78 |
|
|
79 |
79 |
super::fetch::fetch(env::get_current_dir().unwrap(), String::from(build_cmd.as_str().unwrap())) |
super::fetch::fetch(env::get_current_dir().unwrap(), String::from(build_cmd.as_str().unwrap())) |
|
... |
... |
fn build_module(config: serde_json::Value) -> Result<String, String> { |
86 |
86 |
let build_cmd = &config["build"]["windows"]; |
let build_cmd = &config["build"]["windows"]; |
87 |
87 |
|
|
88 |
88 |
if !build_cmd.is_string() { |
if !build_cmd.is_string() { |
89 |
|
return Err(String::from("beheer.json: 'build->windows' should be a string.")); |
|
|
89 |
|
return Err(String::from("ambassade.json: 'build->windows' should be a string.")); |
90 |
90 |
} |
} |
91 |
91 |
|
|
92 |
92 |
super::fetch::fetch(env::get_current_dir().unwrap(), String::from(build_cmd.as_str().unwrap())) |
super::fetch::fetch(env::get_current_dir().unwrap(), String::from(build_cmd.as_str().unwrap())) |
File src/backend/config.rs changed (mode: 100644) (index 4ece9c9..98b1644) |
... |
... |
use std::path::PathBuf; |
7 |
7 |
use std::result::Result; |
use std::result::Result; |
8 |
8 |
|
|
9 |
9 |
pub fn create(mut path: PathBuf) -> Result<(), Error> { |
pub fn create(mut path: PathBuf) -> Result<(), Error> { |
10 |
|
path.push("beheer.json"); |
|
|
10 |
|
path.push("ambassade.json"); |
11 |
11 |
init(&path) |
init(&path) |
12 |
12 |
} |
} |
13 |
13 |
|
|
|
... |
... |
fn init(path: &PathBuf) -> Result<(), Error> { |
26 |
26 |
}); |
}); |
27 |
27 |
|
|
28 |
28 |
match File::open(path.to_str().unwrap()) { |
match File::open(path.to_str().unwrap()) { |
29 |
|
Ok(_) => return Err(Error::new(ErrorKind::AlreadyExists, "Already found a 'beheer.json' file.")), |
|
|
29 |
|
Ok(_) => return Err(Error::new(ErrorKind::AlreadyExists, "Already found a 'ambassade.json' file.")), |
30 |
30 |
Err(_) => { |
Err(_) => { |
31 |
31 |
match File::create(path) { |
match File::create(path) { |
32 |
32 |
Ok(mut file) => { |
Ok(mut file) => { |
|
... |
... |
fn init(path: &PathBuf) -> Result<(), Error> { |
41 |
41 |
} |
} |
42 |
42 |
|
|
43 |
43 |
pub fn update(mut path: PathBuf, value: serde_json::Value) -> Result<(), String> { |
pub fn update(mut path: PathBuf, value: serde_json::Value) -> Result<(), String> { |
44 |
|
path.push("beheer.json"); |
|
|
44 |
|
path.push("ambassade.json"); |
45 |
45 |
|
|
46 |
46 |
match File::create(path) { |
match File::create(path) { |
47 |
47 |
Ok(mut file) => { |
Ok(mut file) => { |
|
... |
... |
pub fn update(mut path: PathBuf, value: serde_json::Value) -> Result<(), String> |
57 |
57 |
fn read(path: &mut PathBuf) -> Result<String, Error> { |
fn read(path: &mut PathBuf) -> Result<String, Error> { |
58 |
58 |
let mut config = String::new(); |
let mut config = String::new(); |
59 |
59 |
|
|
60 |
|
path.push("beheer.json"); |
|
|
60 |
|
path.push("ambassade.json"); |
61 |
61 |
check(&path); |
check(&path); |
62 |
62 |
|
|
63 |
63 |
match File::open(path.to_str().unwrap()) { |
match File::open(path.to_str().unwrap()) { |
File src/backend/dep.rs changed (mode: 100644) (index 6f7d34b..530863b) |
... |
... |
pub fn json(config: String) -> Result<serde_json::Value, String> { |
21 |
21 |
return Err(error); |
return Err(error); |
22 |
22 |
}, |
}, |
23 |
23 |
serde_json::error::Category::Syntax => { |
serde_json::error::Category::Syntax => { |
24 |
|
error.push_str("Syntax error in 'beheer.json'"); |
|
|
24 |
|
error.push_str("Syntax error in 'ambassade.json'"); |
25 |
25 |
return Err(error); |
return Err(error); |
26 |
26 |
}, |
}, |
27 |
27 |
serde_json::error::Category::Data => { |
serde_json::error::Category::Data => { |
28 |
|
error.push_str("Semantic error in 'beheer.json'"); |
|
|
28 |
|
error.push_str("Semantic error in 'ambassade.json'"); |
29 |
29 |
return Err(error); |
return Err(error); |
30 |
30 |
}, |
}, |
31 |
31 |
serde_json::error::Category::Eof => { |
serde_json::error::Category::Eof => { |
32 |
|
error.push_str("Unexpected end-of-file in 'beheer.json'"); |
|
|
32 |
|
error.push_str("Unexpected end-of-file in 'ambassade.json'"); |
33 |
33 |
return Err(error); |
return Err(error); |
34 |
34 |
} |
} |
35 |
35 |
} |
} |
|
... |
... |
pub fn dep(config: serde_json::Value, os: &OS) -> Result<Vec<(String, String)>, |
73 |
73 |
Some(object) => { |
Some(object) => { |
74 |
74 |
for dep in object.iter() { |
for dep in object.iter() { |
75 |
75 |
if !dep.1.is_string() { |
if !dep.1.is_string() { |
76 |
|
return Err(String::from("beheer.json: all deps should be strings!")) |
|
|
76 |
|
return Err(String::from("ambassade.json: all deps should be strings!")) |
77 |
77 |
} |
} |
78 |
78 |
output.push((dep.0.to_string(), String::from(dep.1.as_str().unwrap()))); |
output.push((dep.0.to_string(), String::from(dep.1.as_str().unwrap()))); |
79 |
79 |
} |
} |
80 |
80 |
}, |
}, |
81 |
|
None => return Err(String::from("beheer.json: 'deps->linux' should be an object.")) |
|
|
81 |
|
None => return Err(String::from("ambassade.json: 'deps->linux' should be an object.")) |
82 |
82 |
} |
} |
83 |
83 |
} |
} |
84 |
84 |
}, |
}, |
|
... |
... |
pub fn dep(config: serde_json::Value, os: &OS) -> Result<Vec<(String, String)>, |
89 |
89 |
Some(object) => { |
Some(object) => { |
90 |
90 |
for dep in object.iter() { |
for dep in object.iter() { |
91 |
91 |
if !dep.1.is_string() { |
if !dep.1.is_string() { |
92 |
|
return Err(String::from("beheer.json: all deps should be strings!")) |
|
|
92 |
|
return Err(String::from("ambassade.json: all deps should be strings!")) |
93 |
93 |
} |
} |
94 |
94 |
output.push((dep.0.to_string(), String::from(dep.1.as_str().unwrap()))); |
output.push((dep.0.to_string(), String::from(dep.1.as_str().unwrap()))); |
95 |
95 |
} |
} |
96 |
96 |
}, |
}, |
97 |
|
None => return Err(String::from("beheer.json: 'deps->os-x' should be an object.")) |
|
|
97 |
|
None => return Err(String::from("ambassade.json: 'deps->os-x' should be an object.")) |
98 |
98 |
} |
} |
99 |
99 |
} |
} |
100 |
100 |
}, |
}, |
|
... |
... |
pub fn dep(config: serde_json::Value, os: &OS) -> Result<Vec<(String, String)>, |
105 |
105 |
Some(object) => { |
Some(object) => { |
106 |
106 |
for dep in object.iter() { |
for dep in object.iter() { |
107 |
107 |
if !dep.1.is_string() { |
if !dep.1.is_string() { |
108 |
|
return Err(String::from("beheer.json: all deps should be strings!")) |
|
|
108 |
|
return Err(String::from("ambassade.json: all deps should be strings!")) |
109 |
109 |
} |
} |
110 |
110 |
output.push((dep.0.to_string(), String::from(dep.1.as_str().unwrap()))); |
output.push((dep.0.to_string(), String::from(dep.1.as_str().unwrap()))); |
111 |
111 |
} |
} |
112 |
112 |
}, |
}, |
113 |
|
None => return Err(String::from("beheer.json: 'deps->windows' should be an object.")) |
|
|
113 |
|
None => return Err(String::from("ambassade.json: 'deps->windows' should be an object.")) |
114 |
114 |
} |
} |
115 |
115 |
} |
} |
116 |
116 |
} |
} |
File src/backend/fetch.rs changed (mode: 100644) (index e98a1f3..2ddfbe7) |
... |
... |
fn fetch(dep: PathBuf, command: String) -> Result<String, String> { |
44 |
44 |
}, |
}, |
45 |
45 |
Err(e) => { |
Err(e) => { |
46 |
46 |
let mut config_file = dep.clone(); |
let mut config_file = dep.clone(); |
47 |
|
config_file.push("beheer.json"); |
|
|
47 |
|
config_file.push("ambassade.json"); |
48 |
48 |
|
|
49 |
49 |
let mut error = String::from("Fetching failed: command '"); |
let mut error = String::from("Fetching failed: command '"); |
50 |
50 |
error.push_str(command); |
error.push_str(command); |
|
... |
... |
fn fetch(dep: PathBuf, command: String) -> Result<String, String> { |
54 |
54 |
|
|
55 |
55 |
match config_file.to_str() { |
match config_file.to_str() { |
56 |
56 |
Some(path) => error.push_str(path), |
Some(path) => error.push_str(path), |
57 |
|
None => error.push_str("beheer.json") |
|
|
57 |
|
None => error.push_str("ambassade.json") |
58 |
58 |
} |
} |
59 |
59 |
|
|
60 |
60 |
error.push_str("' file."); |
error.push_str("' file."); |
File src/backend/project.rs changed (mode: 100644) (index 7540211..d4affa5) |
... |
... |
pub fn exe<I>(args: &mut I) -> Result<String, String> where I: Iterator<Item=Str |
46 |
46 |
if cfg!(target_os = "linux") { |
if cfg!(target_os = "linux") { |
47 |
47 |
match config["run"]["linux"].as_str() { |
match config["run"]["linux"].as_str() { |
48 |
48 |
Some(string) => args = String::from(string), |
Some(string) => args = String::from(string), |
49 |
|
None => return Err(String::from("beheer.json: 'run->linux' should be a string.")) |
|
|
49 |
|
None => return Err(String::from("ambassade.json: 'run->linux' should be a string.")) |
50 |
50 |
} |
} |
51 |
51 |
} |
} |
52 |
52 |
if cfg!(target_os = "macos") { |
if cfg!(target_os = "macos") { |
53 |
53 |
match config["run"]["os-x"].as_str() { |
match config["run"]["os-x"].as_str() { |
54 |
54 |
Some(string) => args = String::from(string), |
Some(string) => args = String::from(string), |
55 |
|
None => return Err(String::from("beheer.json: 'run->os-x' should be a string.")) |
|
|
55 |
|
None => return Err(String::from("ambassade.json: 'run->os-x' should be a string.")) |
56 |
56 |
} |
} |
57 |
57 |
} |
} |
58 |
58 |
if cfg!(target_os = "windows") { |
if cfg!(target_os = "windows") { |
59 |
59 |
match config["run"]["windows"].as_str() { |
match config["run"]["windows"].as_str() { |
60 |
60 |
Some(string) => args = String::from(string), |
Some(string) => args = String::from(string), |
61 |
|
None => return Err(String::from("beheer.json: 'run->windows' should be a string.")) |
|
|
61 |
|
None => return Err(String::from("ambassade.json: 'run->windows' should be a string.")) |
62 |
62 |
} |
} |
63 |
63 |
} |
} |
64 |
64 |
}, |
}, |
|
... |
... |
pub fn ignore(args: &Vec<String>) -> Result<(), String> { |
130 |
130 |
match super::filesystem::get_current_dep_root() { |
match super::filesystem::get_current_dep_root() { |
131 |
131 |
Ok(mut dir) => { |
Ok(mut dir) => { |
132 |
132 |
dir.push(&entry); |
dir.push(&entry); |
133 |
|
super::git::ignore::add(&mut dir, &mut entry) // entry moet "beheer.json" worden. |
|
|
133 |
|
super::git::ignore::add(&mut dir, &mut entry) // entry moet "ambassade.json" worden. |
134 |
134 |
}, |
}, |
135 |
135 |
Err(e) => Err(e.to_string()) |
Err(e) => Err(e.to_string()) |
136 |
136 |
} |
} |
|
... |
... |
pub fn dep_tree<I>(args: &mut I) -> Result<deptree::Node, String> where I: Itera |
154 |
154 |
|
|
155 |
155 |
pub fn help() { |
pub fn help() { |
156 |
156 |
println!("Syntax:"); |
println!("Syntax:"); |
157 |
|
println!("$ beheer [FLAG] [COMMAND [ARGUMENTS]]"); |
|
|
157 |
|
println!("$ ambassade [FLAG] [COMMAND [ARGUMENTS]]"); |
158 |
158 |
println!(""); |
println!(""); |
159 |
159 |
|
|
160 |
160 |
println!("--help -h\t\t\t\tShow this message"); |
println!("--help -h\t\t\t\tShow this message"); |