• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

GothenburgBitFactory / taskwarrior / 12847355197

18 Jan 2025 08:55PM UTC coverage: 85.069% (+0.02%) from 85.054%
12847355197

Pull #3758

github

web-flow
Merge 36884cad4 into aeeec1698
Pull Request #3758: Fix #3571: Added detailed feedback for successful task synchronization

4 of 5 new or added lines in 1 file covered. (80.0%)

21 existing lines in 1 file now uncovered.

19508 of 22932 relevant lines covered (85.07%)

23152.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

49.41
/src/commands/CmdSync.cpp
1
////////////////////////////////////////////////////////////////////////////////
2
//
3
// Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez.
4
//
5
// Permission is hereby granted, free of charge, to any person obtaining a copy
6
// of this software and associated documentation files (the "Software"), to deal
7
// in the Software without restriction, including without limitation the rights
8
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
// copies of the Software, and to permit persons to whom the Software is
10
// furnished to do so, subject to the following conditions:
11
//
12
// The above copyright notice and this permission notice shall be included
13
// in all copies or substantial portions of the Software.
14
//
15
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
// SOFTWARE.
22
//
23
// https://www.opensource.org/licenses/mit-license.php
24
//
25
////////////////////////////////////////////////////////////////////////////////
26

27
#include <cmake.h>
28
// cmake.h include header must come first
29

30
#include <CmdSync.h>
31
#include <Color.h>
32
#include <Context.h>
33
#include <Filter.h>
34
#include <format.h>
35
#include <inttypes.h>
36
#include <shared.h>
37
#include <signal.h>
38
#include <taskchampion-cpp/lib.h>
39
#include <util.h>
40

41
#include <iostream>
42
#include <sstream>
43

44
////////////////////////////////////////////////////////////////////////////////
45
CmdSync::CmdSync() {
4,499✔
46
  _keyword = "synchronize";
4,499✔
47
  _usage = "task          synchronize [initialize]";
4,499✔
48
  _description = "Synchronizes data with the Taskserver";
4,499✔
49
  _read_only = false;
4,499✔
50
  _displays_id = false;
4,499✔
51
  _needs_gc = false;
4,499✔
52
  _needs_recur_update = false;
4,499✔
53
  _uses_context = false;
4,499✔
54
  _accepts_filter = false;
4,499✔
55
  _accepts_modifications = false;
4,499✔
56
  _accepts_miscellaneous = true;
4,499✔
57
  _category = Command::Category::migration;
4,499✔
58
}
4,499✔
59

60
////////////////////////////////////////////////////////////////////////////////
61
int CmdSync::execute(std::string& output) {
2✔
62
  int status = 0;
2✔
63

64
  Context& context = Context::getContext();
2✔
65
  auto& replica = context.tdb2.replica();
2✔
66
  std::stringstream out;
2✔
67
  bool avoid_snapshots = false;
2✔
68
  bool verbose = Context::getContext().verbose("sync");
4✔
69

70
  std::string origin = Context::getContext().config.get("sync.server.origin");
4✔
71
  std::string url = Context::getContext().config.get("sync.server.url");
4✔
72
  std::string server_dir = Context::getContext().config.get("sync.local.server_dir");
4✔
73
  std::string client_id = Context::getContext().config.get("sync.server.client_id");
4✔
74
  std::string aws_bucket = Context::getContext().config.get("sync.aws.bucket");
4✔
75
  std::string gcp_bucket = Context::getContext().config.get("sync.gcp.bucket");
4✔
76
  std::string encryption_secret = Context::getContext().config.get("sync.encryption_secret");
4✔
77

78
  // sync.server.origin is a deprecated synonym for sync.server.url
79
  std::string server_url = url == "" ? origin : url;
2✔
80
  if (origin != "") {
2✔
81
    out << "sync.server.origin is deprecated. Use sync.server.url instead.\n";
×
82
  }
83

84
  if (server_dir != "") {
2✔
85
    if (verbose) {
2✔
86
      out << format("Syncing with {1}", server_dir) << '\n';
6✔
87
    }
88
    replica->sync_to_local(server_dir, avoid_snapshots);
2✔
UNCOV
89
  } else if (aws_bucket != "") {
×
UNCOV
90
    std::string aws_region = Context::getContext().config.get("sync.aws.region");
×
91
    std::string aws_profile = Context::getContext().config.get("sync.aws.profile");
×
92
    std::string aws_access_key_id = Context::getContext().config.get("sync.aws.access_key_id");
×
93
    std::string aws_secret_access_key =
94
        Context::getContext().config.get("sync.aws.secret_access_key");
×
95
    std::string aws_default_credentials =
96
        Context::getContext().config.get("sync.aws.default_credentials");
×
UNCOV
97
    if (aws_region == "") {
×
98
      throw std::string("sync.aws.region is required");
×
99
    }
100
    if (encryption_secret == "") {
×
UNCOV
101
      throw std::string("sync.encryption_secret is required");
×
102
    }
103

UNCOV
104
    bool using_profile = false;
×
UNCOV
105
    bool using_creds = false;
×
106
    bool using_default = false;
×
107
    if (aws_profile != "") {
×
108
      using_profile = true;
×
109
    }
110
    if (aws_access_key_id != "" || aws_secret_access_key != "") {
×
UNCOV
111
      using_creds = true;
×
112
    }
113
    if (aws_default_credentials != "") {
×
UNCOV
114
      using_default = true;
×
115
    }
116

UNCOV
117
    if (using_profile + using_creds + using_default != 1) {
×
UNCOV
118
      throw std::string("exactly one method of specifying AWS credentials is required");
×
119
    }
120

UNCOV
121
    if (verbose) {
×
UNCOV
122
      out << format("Syncing with AWS bucket {1}", aws_bucket) << '\n';
×
123
    }
124

UNCOV
125
    if (using_profile) {
×
UNCOV
126
      replica->sync_to_aws_with_profile(aws_region, aws_bucket, aws_profile, encryption_secret,
×
127
                                        avoid_snapshots);
128
    } else if (using_creds) {
×
UNCOV
129
      replica->sync_to_aws_with_access_key(aws_region, aws_bucket, aws_access_key_id,
×
130
                                           aws_secret_access_key, encryption_secret,
131
                                           avoid_snapshots);
132
    } else {
UNCOV
133
      replica->sync_to_aws_with_default_creds(aws_region, aws_bucket, encryption_secret,
×
134
                                              avoid_snapshots);
135
    }
136

UNCOV
137
  } else if (gcp_bucket != "") {
×
138
    std::string gcp_credential_path = Context::getContext().config.get("sync.gcp.credential_path");
×
139
    if (encryption_secret == "") {
×
NEW
140
      throw std::string("sync.encryption_secret is required");
×
141
    }
142
    if (verbose) {
×
UNCOV
143
      out << format("Syncing with GCP bucket {1}", gcp_bucket) << '\n';
×
144
    }
145
    replica->sync_to_gcp(gcp_bucket, gcp_credential_path, encryption_secret, avoid_snapshots);
×
146

147
  } else if (server_url != "") {
×
148
    if (client_id == "" || encryption_secret == "") {
×
149
      throw std::string("sync.server.client_id and sync.encryption_secret are required");
×
150
    }
UNCOV
151
    if (verbose) {
×
UNCOV
152
      out << format("Syncing with sync server at {1}", server_url) << '\n';
×
153
    }
154
    replica->sync_to_remote(server_url, tc::uuid_from_string(client_id), encryption_secret,
×
155
                            avoid_snapshots);
156

157
  } else {
UNCOV
158
    throw std::string("No sync.* settings are configured. See task-sync(5).");
×
159
  }
160

161
  if (context.config.getBoolean("purge.on-sync")) {
6✔
162
    context.tdb2.expire_tasks();
1✔
163
  }
164

165
  if (verbose) {
2✔
166
    out << format("Successfully synchronized {1} operations", replica->num_local_operations())
8✔
167
        << '\n';
2✔
168
  }
169

170
  output = out.str();
2✔
171
  return status;
2✔
172
}
2✔
173

174
////////////////////////////////////////////////////////////////////////////////
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc