yuki-cli 0.1.2__tar.gz → 0.1.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/.github/workflows/ci.yml +15 -0
  2. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/.github/workflows/release.yml +80 -0
  3. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/CHANGELOG.md +10 -0
  4. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/Cargo.lock +1 -1
  5. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/Cargo.toml +1 -1
  6. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/PKG-INFO +3 -1
  7. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/README.md +2 -0
  8. yuki_cli-0.1.4/prek.toml +36 -0
  9. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/src/cli/init.rs +70 -7
  10. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/.gitignore +0 -0
  11. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/CLAUDE.md +0 -0
  12. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/LICENSE +0 -0
  13. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/Makefile +0 -0
  14. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/pyproject.toml +0 -0
  15. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/src/cli/accounts.rs +0 -0
  16. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/src/cli/admin.rs +0 -0
  17. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/src/cli/check.rs +0 -0
  18. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/src/cli/contacts.rs +0 -0
  19. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/src/cli/documents.rs +0 -0
  20. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/src/cli/invoices.rs +0 -0
  21. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/src/cli/mod.rs +0 -0
  22. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/src/cli/projects.rs +0 -0
  23. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/src/cli/upload.rs +0 -0
  24. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/src/cli/vat.rs +0 -0
  25. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/src/client/accounting.rs +0 -0
  26. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/src/client/accounting_info.rs +0 -0
  27. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/src/client/archive.rs +0 -0
  28. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/src/client/contact.rs +0 -0
  29. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/src/client/mod.rs +0 -0
  30. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/src/client/sales.rs +0 -0
  31. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/src/client/soap_client.rs +0 -0
  32. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/src/client/vat.rs +0 -0
  33. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/src/config.rs +0 -0
  34. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/src/error.rs +0 -0
  35. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/src/lib.rs +0 -0
  36. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/src/main.rs +0 -0
  37. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/src/output.rs +0 -0
  38. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/src/period.rs +0 -0
  39. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/src/schema.rs +0 -0
  40. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/tests/config.rs +0 -0
  41. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/tests/error.rs +0 -0
  42. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/tests/output.rs +0 -0
  43. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/tests/parsers.rs +0 -0
  44. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/tests/period.rs +0 -0
  45. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/tests/soap_client.rs +0 -0
  46. {yuki_cli-0.1.2 → yuki_cli-0.1.4}/yuki_cli_py/__init__.py +0 -0
@@ -35,6 +35,21 @@ jobs:
35
35
  - name: Run tests
36
36
  run: make test
37
37
 
38
+ coverage:
39
+ name: Coverage
40
+ runs-on: ubuntu-latest
41
+ steps:
42
+ - uses: actions/checkout@v4
43
+ - uses: dtolnay/rust-toolchain@stable
44
+ - uses: taiki-e/install-action@cargo-tarpaulin
45
+ - name: Generate coverage
46
+ run: cargo tarpaulin --out xml
47
+ - name: Upload to Codecov
48
+ uses: codecov/codecov-action@v5
49
+ with:
50
+ files: cobertura.xml
51
+ token: ${{ secrets.CODECOV_TOKEN }}
52
+
38
53
  all-checks-passed:
39
54
  name: All checks passed
40
55
  runs-on: ubuntu-latest
@@ -212,3 +212,83 @@ jobs:
212
212
  echo "Dry run complete. Artifacts built but nothing published."
213
213
  echo "Archives:"
214
214
  find artifacts/release-* -type f | sort
215
+
216
+ update-homebrew:
217
+ needs: release
218
+ if: ${{ !inputs.dry_run }}
219
+ runs-on: ubuntu-latest
220
+ steps:
221
+ - uses: actions/download-artifact@v4
222
+ with:
223
+ path: /tmp/artifacts
224
+
225
+ - name: Compute SHA256 hashes
226
+ id: hashes
227
+ run: |
228
+ for target in x86_64-apple-darwin aarch64-apple-darwin x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu; do
229
+ sha=$(cat /tmp/artifacts/release-${target}/yuki-cli-${{ github.ref_name }}-${target}.tar.gz.sha256 | awk '{print $1}')
230
+ key=$(echo "${target}" | tr '-' '_')
231
+ echo "${key}=${sha}" >> "$GITHUB_OUTPUT"
232
+ done
233
+
234
+ - name: Update Homebrew formula
235
+ env:
236
+ GH_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
237
+ run: |
238
+ VERSION="${{ github.ref_name }}"
239
+ VERSION_NUM="${VERSION#v}"
240
+
241
+ cat > /tmp/yuki-cli.rb << 'FORMULA'
242
+ class YukiCli < Formula
243
+ desc "CLI for Yuki bookkeeping"
244
+ homepage "https://github.com/rvben/yuki-cli"
245
+ version "VERSION_NUM"
246
+ license "MIT"
247
+
248
+ on_macos do
249
+ if Hardware::CPU.arm?
250
+ url "https://github.com/rvben/yuki-cli/releases/download/VERSION/yuki-cli-VERSION-aarch64-apple-darwin.tar.gz"
251
+ sha256 "SHA_AARCH64_APPLE_DARWIN"
252
+ else
253
+ url "https://github.com/rvben/yuki-cli/releases/download/VERSION/yuki-cli-VERSION-x86_64-apple-darwin.tar.gz"
254
+ sha256 "SHA_X86_64_APPLE_DARWIN"
255
+ end
256
+ end
257
+
258
+ on_linux do
259
+ if Hardware::CPU.arm?
260
+ url "https://github.com/rvben/yuki-cli/releases/download/VERSION/yuki-cli-VERSION-aarch64-unknown-linux-gnu.tar.gz"
261
+ sha256 "SHA_AARCH64_UNKNOWN_LINUX_GNU"
262
+ else
263
+ url "https://github.com/rvben/yuki-cli/releases/download/VERSION/yuki-cli-VERSION-x86_64-unknown-linux-gnu.tar.gz"
264
+ sha256 "SHA_X86_64_UNKNOWN_LINUX_GNU"
265
+ end
266
+ end
267
+
268
+ def install
269
+ bin.install "yuki"
270
+ end
271
+
272
+ test do
273
+ system "#{bin}/yuki", "--version"
274
+ end
275
+ end
276
+ FORMULA
277
+
278
+ sed -i "s/VERSION_NUM/${VERSION_NUM}/g" /tmp/yuki-cli.rb
279
+ sed -i "s/VERSION/${VERSION}/g" /tmp/yuki-cli.rb
280
+ sed -i "s/SHA_AARCH64_APPLE_DARWIN/${{ steps.hashes.outputs.aarch64_apple_darwin }}/g" /tmp/yuki-cli.rb
281
+ sed -i "s/SHA_X86_64_APPLE_DARWIN/${{ steps.hashes.outputs.x86_64_apple_darwin }}/g" /tmp/yuki-cli.rb
282
+ sed -i "s/SHA_AARCH64_UNKNOWN_LINUX_GNU/${{ steps.hashes.outputs.aarch64_unknown_linux_gnu }}/g" /tmp/yuki-cli.rb
283
+ sed -i "s/SHA_X86_64_UNKNOWN_LINUX_GNU/${{ steps.hashes.outputs.x86_64_unknown_linux_gnu }}/g" /tmp/yuki-cli.rb
284
+
285
+ # Clone tap repo, update formula, push
286
+ git clone https://x-access-token:${GH_TOKEN}@github.com/rvben/homebrew-tap.git /tmp/tap
287
+ mkdir -p /tmp/tap/Formula
288
+ cp /tmp/yuki-cli.rb /tmp/tap/Formula/yuki-cli.rb
289
+ cd /tmp/tap
290
+ git config user.name "github-actions[bot]"
291
+ git config user.email "github-actions[bot]@users.noreply.github.com"
292
+ git add Formula/yuki-cli.rb
293
+ git diff --cached --quiet || git commit -m "Update yuki-cli to ${VERSION}"
294
+ git push
@@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/).
6
6
 
7
+
8
+
9
+ ## [0.1.4](https://github.com/rvben/yuki-cli/compare/v0.1.3...v0.1.4) - 2026-04-03
10
+
11
+ ### Added
12
+
13
+ - **init**: add API key guidance, colored status, and next steps ([4a00b76](https://github.com/rvben/yuki-cli/commit/4a00b769c2529284f9b0d3a1d969873ed5d815fc))
14
+
15
+ ## [0.1.3](https://github.com/rvben/yuki-cli/compare/v0.1.2...v0.1.3) - 2026-04-03
16
+
7
17
  ## [0.1.2](https://github.com/rvben/yuki-cli/compare/v0.1.1...v0.1.2) - 2026-04-03
8
18
 
9
19
  ### Added
@@ -1899,7 +1899,7 @@ dependencies = [
1899
1899
 
1900
1900
  [[package]]
1901
1901
  name = "yuki-cli"
1902
- version = "0.1.2"
1902
+ version = "0.1.4"
1903
1903
  dependencies = [
1904
1904
  "anyhow",
1905
1905
  "base64",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "yuki-cli"
3
- version = "0.1.2"
3
+ version = "0.1.4"
4
4
  edition = "2024"
5
5
  rust-version = "1.90"
6
6
  description = "CLI client for the Yuki bookkeeping SOAP API"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yuki-cli
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Intended Audience :: End Users/Desktop
@@ -20,6 +20,8 @@ Project-URL: Repository, https://github.com/rvben/yuki-cli.git
20
20
 
21
21
  # yuki
22
22
 
23
+ [![codecov](https://codecov.io/gh/rvben/yuki-cli/graph/badge.svg)](https://codecov.io/gh/rvben/yuki-cli)
24
+
23
25
  CLI client for the [Yuki](https://www.yukiworks.nl) bookkeeping SOAP API.
24
26
 
25
27
  [Yuki](https://www.yukiworks.nl) is a Dutch bookkeeping SaaS used for accounting, VAT returns, and document archiving. This CLI lets you query your administration, find missing invoices, and upload documents — from the terminal or as part of automated workflows.
@@ -1,5 +1,7 @@
1
1
  # yuki
2
2
 
3
+ [![codecov](https://codecov.io/gh/rvben/yuki-cli/graph/badge.svg)](https://codecov.io/gh/rvben/yuki-cli)
4
+
3
5
  CLI client for the [Yuki](https://www.yukiworks.nl) bookkeeping SOAP API.
4
6
 
5
7
  [Yuki](https://www.yukiworks.nl) is a Dutch bookkeeping SaaS used for accounting, VAT returns, and document archiving. This CLI lets you query your administration, find missing invoices, and upload documents — from the terminal or as part of automated workflows.
@@ -0,0 +1,36 @@
1
+ #:schema: https://www.schemastore.org/prek.json
2
+
3
+ [[repos]]
4
+ repo = "builtin"
5
+ hooks = [
6
+ { id = "trailing-whitespace" },
7
+ { id = "end-of-file-fixer" },
8
+ { id = "check-added-large-files" },
9
+ ]
10
+
11
+ [[repos]]
12
+ repo = "local"
13
+ [[repos.hooks]]
14
+ id = "cargo-fmt"
15
+ name = "cargo fmt"
16
+ entry = "cargo fmt -- --check"
17
+ language = "system"
18
+ types = ["rust"]
19
+ pass_filenames = false
20
+
21
+ [[repos.hooks]]
22
+ id = "cargo-clippy"
23
+ name = "cargo clippy"
24
+ entry = "cargo clippy -- -D warnings"
25
+ language = "system"
26
+ types = ["rust"]
27
+ pass_filenames = false
28
+
29
+ [[repos.hooks]]
30
+ id = "cargo-test"
31
+ name = "cargo test"
32
+ entry = "cargo test"
33
+ language = "system"
34
+ types = ["rust"]
35
+ pass_filenames = false
36
+ stages = ["pre-push"]
@@ -1,9 +1,44 @@
1
1
  use std::collections::BTreeMap;
2
2
  use std::io::{self, BufRead, Write};
3
3
 
4
+ use owo_colors::OwoColorize;
5
+
4
6
  use crate::client::accounting::AccountingClient;
5
7
  use crate::config::{AdminEntry, Config};
6
8
  use crate::error::YukiError;
9
+ use crate::output::is_tty;
10
+
11
+ fn sym_ok() -> String {
12
+ if is_tty() {
13
+ "✔".green().to_string()
14
+ } else {
15
+ "✔".to_owned()
16
+ }
17
+ }
18
+
19
+ fn sym_fail() -> String {
20
+ if is_tty() {
21
+ "✖".red().to_string()
22
+ } else {
23
+ "✖".to_owned()
24
+ }
25
+ }
26
+
27
+ fn bold(s: &str) -> String {
28
+ if is_tty() {
29
+ s.bold().to_string()
30
+ } else {
31
+ s.to_owned()
32
+ }
33
+ }
34
+
35
+ fn dim(s: &str) -> String {
36
+ if is_tty() {
37
+ s.dimmed().to_string()
38
+ } else {
39
+ s.to_owned()
40
+ }
41
+ }
7
42
 
8
43
  /// Convert an administration name to a safe config key.
9
44
  ///
@@ -39,13 +74,14 @@ pub async fn run(api_key: Option<&str>, default_admin: Option<&str>) -> Result<(
39
74
 
40
75
  config.api_key = key;
41
76
  config.save_to(&path)?;
42
- eprintln!("API key updated in {}", path.display());
77
+ eprintln!("{} API key updated in {}", sym_ok(), path.display());
43
78
  return Ok(());
44
79
  }
45
80
 
46
81
  let api_key = match api_key {
47
82
  Some(k) => k.trim().to_string(),
48
83
  None => {
84
+ eprintln!(" {} Yuki Portal → Settings → API keys", dim("→"));
49
85
  eprint!("Yuki API key: ");
50
86
  io::stderr().flush().ok();
51
87
  stdin
@@ -62,12 +98,29 @@ pub async fn run(api_key: Option<&str>, default_admin: Option<&str>) -> Result<(
62
98
  return Err(YukiError::Config("API key cannot be empty".to_string()));
63
99
  }
64
100
 
65
- eprintln!("Authenticating...");
101
+ eprint!("Authenticating...");
102
+ io::stderr().flush().ok();
66
103
  let mut client = AccountingClient::new();
67
- client.authenticate(&api_key).await?;
104
+ match client.authenticate(&api_key).await {
105
+ Ok(_) => eprintln!(" {}", sym_ok()),
106
+ Err(e) => {
107
+ eprintln!(" {}", sym_fail());
108
+ return Err(e);
109
+ }
110
+ }
68
111
 
69
- eprintln!("Fetching administrations...");
70
- let admins = client.administrations().await?;
112
+ eprint!("Fetching administrations...");
113
+ io::stderr().flush().ok();
114
+ let admins = match client.administrations().await {
115
+ Ok(admins) => {
116
+ eprintln!(" {}", sym_ok());
117
+ admins
118
+ }
119
+ Err(e) => {
120
+ eprintln!(" {}", sym_fail());
121
+ return Err(e);
122
+ }
123
+ };
71
124
 
72
125
  if admins.is_empty() {
73
126
  return Err(YukiError::NotFound(
@@ -150,8 +203,18 @@ pub async fn run(api_key: Option<&str>, default_admin: Option<&str>) -> Result<(
150
203
 
151
204
  config.save_to(&path)?;
152
205
 
153
- eprintln!("Configuration saved to {}", path.display());
154
- eprintln!("Default administration: {default_name}");
206
+ eprintln!();
207
+ eprintln!("{} Configuration saved to {}", sym_ok(), path.display());
208
+ eprintln!();
209
+ eprintln!("{}:", bold("Next steps"));
210
+ eprintln!(
211
+ " yuki documents list {}",
212
+ dim("# list archived documents")
213
+ );
214
+ eprintln!(" yuki contacts list {}", dim("# list contacts"));
215
+ eprintln!(" yuki invoices list {}", dim("# list invoices"));
216
+ eprintln!(" yuki completions zsh {}", dim("# shell completions"));
217
+ eprintln!();
155
218
 
156
219
  Ok(())
157
220
  }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes