Compare commits

..

No commits in common. "main" and "v1.0.0" have entirely different histories.
main ... v1.0.0

5 changed files with 100 additions and 244 deletions

164
Cargo.lock generated
View file

@ -11,12 +11,6 @@ dependencies = [
"memchr",
]
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
@ -81,16 +75,17 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.38"
version = "0.4.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-integer",
"num-traits",
"time",
"wasm-bindgen",
"windows-targets",
"winapi",
]
[[package]]
@ -179,12 +174,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "expanduser"
version = "1.2.2"
@ -204,14 +193,14 @@ checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
"cfg-if",
"libc",
"wasi",
"wasi 0.9.0+wasi-snapshot-preview1",
]
[[package]]
name = "hashbrown"
version = "0.14.3"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "iana-time-zone"
@ -239,11 +228,11 @@ dependencies = [
[[package]]
name = "indexmap"
version = "2.2.6"
version = "1.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
dependencies = [
"equivalent",
"autocfg",
"hashbrown",
]
@ -292,6 +281,25 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "nom8"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8"
dependencies = [
"memchr",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.15"
@ -409,16 +417,16 @@ dependencies = [
[[package]]
name = "serde_spanned"
version = "0.6.5"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4"
dependencies = [
"serde",
]
[[package]]
name = "soyuz-cli"
version = "1.0.2"
version = "1.0.0"
dependencies = [
"chrono",
"expanduser",
@ -468,10 +476,21 @@ dependencies = [
]
[[package]]
name = "toml"
version = "0.7.8"
name = "time"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257"
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
dependencies = [
"libc",
"wasi 0.10.0+wasi-snapshot-preview1",
"winapi",
]
[[package]]
name = "toml"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7afcae9e3f0fe2c370fd4657108972cbb2fa9db1b9f84849cefd80741b01cb6"
dependencies = [
"serde",
"serde_spanned",
@ -481,24 +500,24 @@ dependencies = [
[[package]]
name = "toml_datetime"
version = "0.6.5"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.19.15"
version = "0.19.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
checksum = "5e6a7712b49e1775fb9a7b998de6635b299237f48b404dde71704f2e0e7f37e5"
dependencies = [
"indexmap",
"nom8",
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
]
[[package]]
@ -519,6 +538,12 @@ version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]]
name = "wasm-bindgen"
version = "0.2.84"
@ -603,76 +628,3 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-targets"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
[[package]]
name = "windows_i686_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
[[package]]
name = "windows_i686_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
[[package]]
name = "winnow"
version = "0.5.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
dependencies = [
"memchr",
]

View file

@ -1,13 +1,13 @@
[package]
name = "soyuz-cli"
version = "1.0.2"
version = "1.0.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
chrono = "~0.4.38"
expanduser = "~1.2.2"
regex = "~1.7.1"
serde = { version = "~1.0.152", features = ["derive"] }
toml = { version = "~0.7.3", features = ["parse"] }
chrono = "0.4.23"
expanduser = "1.2.2"
regex = "1.7.1"
serde = { version = "1.0.152", features = ["derive"] }
toml = { version = "0.7.2", features = ["parse"] }

View file

@ -1,47 +1,23 @@
# soyuz-cli
This is the sister application to [`soyuz-web`](https://github.com/hughrun/soyuz-web).
This is the sister application to `soyuz-web`.
`soyuz-cli` can be used alone or on combination with `soyuz-web` to publish and syncronise a gemlog between your local machine and your gemini server.
It is mostly a wrapper around `rsync`. Additionally it will maintain an archive of posts for each year, and the latest 5 posts on your gemlog homepage.
It is essentially a wrapper around `rsync`. Additionally it will maintain an archive of posts for each year, and the latest 5 posts on your gemlog homepage.
## Assumptions
1. You are using MacOS locally and a unix-like OS on the server (it probably works on n*x locally but I haven't tested it)
2. If you have text _under_ the list of latest posts on your homepage, you have at least 5 posts listed already (I could have put in some logic to deal with this but I am lazy)
3. You have `rsync` and `ssh` installed on your local machine
3. You have `rsync` installed on your local machine
4. You have read and write permission for your gemlog files on the server, with the same username as your local machine (otherwise file permissions get weird)
5. You have permission to install files at `/usr/local/bin` on your local machine
6. You publish posts one at a time, so each post will be published prior to the next one being drafted
## Installation
The easiest way to install `soyuz-cli` is using the install script. Note that these scripts **will overwrite any previously installed version**.
If you are using a recent version of MacOS, run this command in `Terminal`:
The easiest way to install `soyuz-cli` is using the install script:
```sh
curl -kL https://hugh.run/install-soyuz | bash
```
To install for MacOS Mojave, run this command instead:
```sh
curl -kL https://hugh.run/install-soyuz-mojave | bash
```
Alternatively, you can build from source if you have rust and cargo installed.
## Set up
Once installed, run `soyuz settings` to create a new settings file. The settings file has the following values:
* `local_dir` - the directory on your local machine where your Gemini files are saved
* `remote_dir` - the SSH remote directory path to your files on the server. This includes the server name or ip address, so should look something like `gemini-server:/srv/gemini/example.com` or `username@123.456.789:/srv/gemini/example.com`.
* `editor` - the command to open the text editor you want to use. Defaults to `nano`.
* `index_heading` - the heading text above the latest posts listing on your homepage. If unsure, leave this on the default.
## Commands
Run `soyuz help` for a list of commands and what they do.
curl https://hugh.run/install-soyuz | bash
```

View file

@ -1,13 +1,10 @@
#!/bin/sh
# This script installs soyuz-cli
# Download latest binary
wget -q --show-progress https://github.com/hughrun/soyuz-cli/releases/latest/download/MacOS
# mv to /usr/local/bin and rename
mv MacOS /usr/local/bin/soyuz
# Download latest binary and save in /usr/local/bin
curl -L https://github.com/hughrun/soyuz-cli/releases/download/latest/MacOS > /usr/local/bin/soyuz
# make it executable
chmod +x /usr/local/bin/soyuz
chmod +x /usr/local/bin/yawp_test
cat 1>&2 << 'EOM'

View file

@ -9,7 +9,7 @@ use std::path::PathBuf;
use regex::Regex;
use serde::Deserialize;
use std::fs;
use std::process::{Command, Stdio};
use std::process::Command;
use toml;
#[derive(Debug, Deserialize)]
@ -59,32 +59,31 @@ fn create_or_update_homepage(string: String) -> Result<(), std::io::Error> {
}
fn help() {
println!("\x1B[1;37;41mСоюз (Soyuz) - v{}\x1B[0m", option_env!("CARGO_PKG_VERSION").unwrap());
println!("A command line program for publishing Gemini posts\n\x1B[0m");
println!(" \x1B[1;31mhelp | settings | write | publish | sync [ up | down [ --overwrite | --delete ]]\n\x1B[0m");
println!(" \x1B[1;31mhelp\x1B[0m");
println!("Союз (Soyuz) - v{}", option_env!("CARGO_PKG_VERSION").unwrap());
println!("A command line program for publishing Gemini posts.\n");
println!(" soyuz ( help | settings | write | publish | sync [ --overwrite | --delete ] )\n");
println!(" help");
println!(" - shows this help screen\n");
println!(" \x1B[1;31msettings\x1B[0m");
println!(" settings");
println!(" - create or edit the settings file\n");
println!(" \x1B[1;31mwrite\x1B[0m");
println!(" write");
println!(" - create or edit today's gempost using the editor specified in the settings file\n");
println!(" \x1B[1;31mpublish\x1B[0m");
println!(" - update the homepage and year archive lists of posts, and publish to server");
println!(" Syncs new or changed files from the server before updating indexes and syncing up\n");
println!(" If you wish to make manual homepage changes you should use 'sync down' first\n");
println!(" \x1B[1;31msync\x1B[0m");
println!(" - synchronise files between local machine and server");
println!(" publish");
println!(" - update the homepage and year archive lists of posts, and publish to server.");
println!(" Syncs new or changed files from the server before updating indexes and syncing up.\n");
println!(" sync");
println!(" - synchronise files between local machine and server.");
println!(" This is a wrapper around rsync, the default being 'rsync -rtOq'");
println!(" Without arguments sync defaults to 'sync up' which is the equivalent of 'publish'\n");
println!(" \x1B[1;31msync up\x1B[0m");
println!(" - synchronise new or changed files from local machine to server\n");
println!(" \x1B[1;31msync down\x1B[0m");
println!(" - synchronise new or changed files from server to local machine\n");
println!(" \x1B[1;31msync (down | up) --overwrite\x1B[0m");
println!(" - sync, and overwrite all files at the destination regardless of last edit date");
println!(" Without this flag, sync will ignore files at the destination edited more recently than the source\n");
println!(" \x1B[1;31msync (down | up) --delete\x1B[0m");
println!(" - sync, and delete any files at the destination that do not exist at the source\n");
println!(" sync up");
println!(" - syncronise new or changed files from local machine to server.\n");
println!(" sync down");
println!(" - syncronise new or changed files from server to local machine.\n");
println!(" sync (down | up) --overwrite");
println!(" - sync, and overwrite all files at the destination regardless of last edit date.");
println!(" Without this flag, sync will ignore files at the destination edited more recently than the source.\n");
println!(" sync (down | up) --delete");
println!(" - sync, and delete any files at the destination that do not exist at the source.\n");
}
fn open_file(filepath: std::path::PathBuf) {
@ -101,7 +100,6 @@ fn open_file(filepath: std::path::PathBuf) {
}
fn publish() -> Result<(), Box<dyn std::error::Error>> {
println!("Publishing, please wait...");
let config = read_config()?;
// sync down from server (only files that are newer on the server than local)
let mut sync = Command::new("rsync")
@ -140,11 +138,6 @@ fn publish() -> Result<(), Box<dyn std::error::Error>> {
let post = fs::read_to_string(format!("{}/{}", dirpath, &filename))?;
let post_lines: Vec<&str> = post.lines().collect();
let title = post_lines[0].strip_prefix("# ");
if title.is_none() {
println!("ABORTING: Your latest post does not have a title.");
println!("Use a '# Heading' on the first line.");
return Ok(())
}
let entry_string = format!("=> {} {} ({})", filename, sliced, title.unwrap());
let home_entry_string = format!("=> /{}/{} {} ({})", &year, filename, sliced, title.unwrap());
if let Ok(index) = fs::read_to_string(&indexfilepath) {
@ -234,7 +227,7 @@ fn settings() -> Result<(), Box<dyn std::error::Error>> {
fn sync(args: &Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
let config = read_config()?;
let rsync_args = match args.len() {
2 => ["-rtOq", "--update", &config.local_dir, &config.remote_dir],
2 => ["-rtOq", "--update", &config.remote_dir, &config.local_dir],
3 | 4 => {
match args[2].as_str() {
"down" => {
@ -284,75 +277,19 @@ fn sync(args: &Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
}
}
fn write(args: &Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
fn write() -> Result<(), Box<dyn std::error::Error>> {
let config = read_config()?;
let dt = Local::now();
let year = &dt.format("%Y").to_string();
let local_dir: std::path::PathBuf = expanduser(&config.local_dir)?;
let local_dir: std::path::PathBuf = expanduser(config.local_dir)?;
fs::create_dir_all(format!("{}{}", &local_dir.display(), &year))?;
let filepath = format!("{}{}/{}.gmi", local_dir.display(), year, dt.format("%Y-%m-%d"));
match args.len() {
2 => {
let remote_dir: std::path::PathBuf = expanduser(&config.remote_dir)?;
let spath = format!("{}", remote_dir.display());
let remote_vec = spath.split(':').collect::<Vec<_>>();
let server_name = remote_vec[0];
let server_path = remote_vec[1];
let remote_filepath = format!("{}{}/{}.gmi", server_path, year, dt.format("%Y-%m-%d"));
let cmd = format!("[[ -f {} ]] && echo 'true' || echo 'false';", &remote_filepath);
println!("Checking server for latest post...");
let check = Command::new("ssh")
.args(["-q", &server_name, &cmd])
.stdout(Stdio::piped())
.spawn()
.expect("reading from server failed");
let output = check
.wait_with_output()
.expect("something fucked up");
let out = std::str::from_utf8(&output.stdout);
let out2 = out.clone()?.trim();
match out?.trim() {
"true" => {
// user has already published today
println!("\x1B[1;31mYou have already published today!\x1B[0m");
println!("To edit a post published with soyuz-web, run 'soyuz sync down' first.");
println!("To edit a published post already saved locally, use 'soyuz write --edit'");
Ok(())
},
"false" => {
// open a new file
Ok(open_file(filepath.into()))
},
_ => {
println!("Something went wrong checking your gemini server");
println!("Error: {:?}", out2);
Ok(())
}
}
},
3 => {
match args[2].as_str() {
"--edit" => Ok(open_file(filepath.into())),
_ => {
println!("Unknown command. Try 'soyuz help'.");
Ok(())
}
}
},
_ => {
println!("Unknown command. Try 'soyuz help'.");
Ok(())
}
}
Ok(open_file(filepath.into()))
}
fn match_single_arg(args: &Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
match args[1].as_str() {
"write" => write(args),
"write" => write(),
"settings" => settings(),
"publish" => publish(),
"sync" => sync(args),
@ -365,13 +302,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>>{
match &args.len() {
1 => Ok(help()),
2 => match_single_arg(&args),
3 | 4 => {
match args[1].as_str() {
"sync" => sync(&args),
"write" => write(&args),
&_ => Ok(help())
}
},
3 | 4 => sync(&args),
_ => Ok(help())
}
}