From 48a9832a7a6e586cf949a74ddab1fb45f08dc7a5 Mon Sep 17 00:00:00 2001 From: Rostislav Raykov Date: Sat, 21 Sep 2019 23:34:52 +0200 Subject: [PATCH 1/8] fix: Correct release artifact downloaded file names --- .github/workflows/publish_release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish_release.yml b/.github/workflows/publish_release.yml index 8d7aee0..5ec16f3 100644 --- a/.github/workflows/publish_release.yml +++ b/.github/workflows/publish_release.yml @@ -60,8 +60,8 @@ jobs: name: cloudflare-ddns v${{ steps.get_version.outputs.VERSION }} files: | LICENSE - cloudflare-ddns-macOS-${{ steps.get_version.outputs.VERSION }} - cloudflare-ddns-ubuntu-${{ steps.get_version.outputs.VERSION }} + cloudflare-ddns-macOS-${{ steps.get_version.outputs.VERSION }}.tar.xz + cloudflare-ddns-ubuntu-${{ steps.get_version.outputs.VERSION }}.tar.xz env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} publish_crate: From afaf2918c6144a1776d4e1e89963ade1ac8c2a67 Mon Sep 17 00:00:00 2001 From: Rostislav Raykov Date: Sun, 22 Sep 2019 22:27:12 +0200 Subject: [PATCH 2/8] refactor: Use quicli convenience functions for fs stuff --- src/file.rs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/file.rs b/src/file.rs index 9fb14fc..2782813 100644 --- a/src/file.rs +++ b/src/file.rs @@ -1,19 +1,12 @@ use failure::Error; -use std::fs::File; -use std::io::prelude::*; +use quicli::fs::{write_to_file, read_file}; use std::path::PathBuf; pub fn read_cache_file(path: &PathBuf) -> Result { - let mut file = File::open(&path)?; - let mut s = String::new(); - file.read_to_string(&mut s)?; - - Ok(s.clone()) + Ok(read_file(path)?) } pub fn write_cache_file(path: &PathBuf, ip: &str) -> Result<(), Error> { - let mut file = File::create(&path)?; - file.write_all(ip.as_bytes())?; - + write_to_file(path, ip)?; Ok(()) } From 403ec58e21685973ac3a05e4c1977b0a5fcd4249 Mon Sep 17 00:00:00 2001 From: Rostislav Raykov Date: Sun, 22 Sep 2019 22:27:58 +0200 Subject: [PATCH 3/8] chore: Add toml as a dependency This is gonna be used for the config file parsing --- Cargo.lock | 10 ++++++++++ Cargo.toml | 3 ++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 0ae0a25..3c01f9b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -163,6 +163,7 @@ dependencies = [ "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1551,6 +1552,14 @@ dependencies = [ "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "toml" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "try-lock" version = "0.2.2" @@ -1926,6 +1935,7 @@ dependencies = [ "checksum tokio-threadpool 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "90ca01319dea1e376a001e8dc192d42ebde6dd532532a5bad988ac37db365b19" "checksum tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "f2106812d500ed25a4f38235b9cae8f78a09edf43203e16e59c3b769a342a60e" "checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" +"checksum toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7aabe75941d914b72bf3e5d3932ed92ce0664d49d8432305a8b547c37227724" "checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" "checksum try_from 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "283d3b89e1368717881a9d51dad843cc435380d8109c9e47d38780a324698d8b" "checksum unicase 2.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2e2e6bd1e59e56598518beb94fd6db628ded570326f0a98c679a304bd9f00150" diff --git a/Cargo.toml b/Cargo.toml index 4dadce8..c4f9223 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,4 +22,5 @@ serde = "1.0.101" serde_json = "1.0.40" exitcode = "1.1.2" human-panic = "1.0.1" -failure = "0.1.5" \ No newline at end of file +failure = "0.1.5" +toml = "0.5.3" From b654f4523e0ca318d31b980b69140876e2528dba Mon Sep 17 00:00:00 2001 From: Rostislav Raykov Date: Mon, 23 Sep 2019 10:58:04 +0200 Subject: [PATCH 4/8] fix: Check the response object before trying to extract a dns record id from it --- src/network.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/network.rs b/src/network.rs index 40a5c17..3fd8957 100644 --- a/src/network.rs +++ b/src/network.rs @@ -77,7 +77,16 @@ pub fn get_dns_record_id( return Err(format_err!("API Error: {}", err)); } - Ok(response.result[0].id.clone()) + let id = match response.result.first() { + Some(v) => v.id.clone(), + None => { + return Err(format_err!( + "Unexpected API result for DNS record. Check if you passed the right options." + )) + } + }; + + Ok(id) } pub fn get_current_ip() -> Result { From 8ed08c5fa067dcd94d6c55482d675adde225b4cd Mon Sep 17 00:00:00 2001 From: Rostislav Raykov Date: Mon, 23 Sep 2019 10:58:17 +0200 Subject: [PATCH 5/8] style: Remove comment from Cargo.toml --- Cargo.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c4f9223..fa59f52 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,8 +12,6 @@ license = "MIT" documentation = "https://github.com/zbrox/cloudflare-ddns" readme = "README.md" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] quicli = "0.4" structopt = "0.3.1" From 8cbad9d7f11e4933c385dce121c9693065ac8179 Mon Sep 17 00:00:00 2001 From: Rostislav Raykov Date: Mon, 23 Sep 2019 10:58:41 +0200 Subject: [PATCH 6/8] feat: Add option to pass a config file --- src/main.rs | 80 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 59 insertions(+), 21 deletions(-) diff --git a/src/main.rs b/src/main.rs index 86272b8..37db21f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,28 +4,41 @@ mod network; use file::{read_cache_file, write_cache_file}; use human_panic::setup_panic; use network::{get_current_ip, get_dns_record_id, get_zone_identifier, update_ddns}; +use quicli::fs::read_file; use quicli::prelude::*; use std::path::PathBuf; use structopt::StructOpt; +#[derive(Deserialize)] +struct Config { + email: String, + auth_key: String, + zone: String, + domain: String, +} + #[derive(Debug, StructOpt)] /// Inform Cloudflare's DDNS service of the current IP address for your domain struct Cli { + /// Your TOML config file containing all the required options (email, auth_key, zone, domain) which you can use instead of passing the arguments to the command line + #[structopt(long = "config", short = "f")] + config: Option, + /// Your Cloudflare login email - #[structopt(long = "email", short = "e")] - email: String, + #[structopt(long = "email", short = "e", required_unless = "config")] + email: Option, /// The auth key you need to generate in your Cloudflare profile - #[structopt(long = "key", short = "k")] - auth_key: String, + #[structopt(long = "key", short = "k", required_unless = "config")] + auth_key: Option, /// The zone in which your domain is (usually that is your base domain name) - #[structopt(long = "zone", short = "z")] - zone: String, + #[structopt(long = "zone", short = "z", required_unless = "config")] + zone: Option, /// The domain for which you want to report the current IP address - #[structopt(long = "domain", short = "d")] - domain: String, + #[structopt(long = "domain", short = "d", required_unless = "config")] + domain: Option, /// Cache file for previously reported IP address (if skipped the IP will be reported on every execution) #[structopt(long = "cache", short = "c")] @@ -34,10 +47,9 @@ struct Cli { fn main() -> CliResult { setup_panic!(); - let args = Cli::from_args(); - let should_use_cache = args.cache.is_some(); + let should_use_cache = args.cache.is_some(); let cached_ip: Option = match args.cache.clone() { Some(v) => { if v.exists() { @@ -64,22 +76,48 @@ fn main() -> CliResult { write_cache_file(&args.cache.unwrap(), ¤t_ip)?; } - let zone_id = get_zone_identifier(&args.zone, &args.email, &args.auth_key)?; - let record_id = get_dns_record_id(&zone_id, &args.domain, &args.email, &args.auth_key)?; + let (email, auth_key, zone, domain) = match args.config { + Some(c) => { + let config_str = read_file(c)?; + let config: Config = toml::from_str(&config_str)?; + (config.email, config.auth_key, config.zone, config.domain) + } + None => ( + args.email.expect("Email is not set"), + args.auth_key.expect("Auth key is not set"), + args.zone.expect("Zone is not set"), + args.domain.expect("Domain is not set"), + ), + }; - update_ddns( - ¤t_ip, - &args.domain, - &zone_id, - &record_id, - &args.email, - &args.auth_key, - )?; + update(¤t_ip, &email, &auth_key, &zone, &domain)?; println!( "Successfully updated the A record for {} to {}", - &args.domain, ¤t_ip + &domain, ¤t_ip ); Ok(()) } + +fn update( + current_ip: &str, + email: &str, + auth_key: &str, + zone: &str, + domain: &str, +) -> Result<(), Error> { + let zone_id = get_zone_identifier(&zone, &email, &auth_key)?; + let record_id = get_dns_record_id(&zone_id, &domain, &email, &auth_key)?; + + update_ddns( + ¤t_ip, + &domain, + &zone_id, + &record_id, + &email, + &auth_key, + )?; + + Ok(()) +} From 44d2279271472f6af14f7ffaea49cf8459aa4d92 Mon Sep 17 00:00:00 2001 From: Rostislav Raykov Date: Mon, 23 Sep 2019 11:03:00 +0200 Subject: [PATCH 7/8] docs: Update README with info on new config option --- README.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 83e9f05..1f62359 100644 --- a/README.md +++ b/README.md @@ -8,12 +8,26 @@ This is a simple CLI you can use to continuously update an A DNS record for a do ``` -k, --key The auth key you need to generate in your Cloudflare profile - -c, --cache Cache file for previously reported IP address (if skipped the IP will be reported on every execution) + -c, --cache Cache file for previously reported IP address (if skipped the IP will be reported on every + execution) + -f, --config Your TOML config file containing all the required options (email, auth_key, zone, domain) + which you can use instead of passing the arguments to the command line -d, --domain The domain for which you want to report the current IP address -e, --email Your Cloudflare login email -z, --zone The zone in which your domain is (usually that is your base domain name) ``` +### Config + +You Can pass a path to a configuration file (`-f` or `--config`) instead of each option as a command line argument. The configuration should be a [TOML](https://github.com/toml-lang/toml) file and hold the same options. Here's a sample: + +```TOML +email = "example@example.com" +auth_key = "secretkey" +domain = "example.example.com" +zone = "example.com" +``` + ## Cloudflare Setup You need to do some preparatory work in Cloudflare. Firstly this assumes you're using Cloudflare already to manage the DNS records for your domain. From 3803843ba29a3181244d50f6a7741dae14965575 Mon Sep 17 00:00:00 2001 From: Rostislav Raykov Date: Mon, 23 Sep 2019 12:01:25 +0200 Subject: [PATCH 8/8] chore: Bump up version number to 0.2.0 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3c01f9b..e98e33e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -153,7 +153,7 @@ dependencies = [ [[package]] name = "cloudflare-ddns" -version = "0.1.8" +version = "0.2.0" dependencies = [ "exitcode 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index fa59f52..030b08d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cloudflare-ddns" -version = "0.1.8" +version = "0.2.0" authors = ["Rostislav Raykov "] edition = "2018" description = "A simple CLI tool to use Cloudflare's free DDNS service"