feat(time): readable time tables with totals and gh-style feedback

time list printed Go durations (1h30m0s), raw timestamps and no issue
title. It now shows ID ISSUE USER SPENT WHEN with "#1 Login fails",
"1h 30m" and relative times on a terminal, followed by the total of the
listed time, and points at --mine when no repository is selected.
--issue takes issue URLs, --username takes @me, and --since replaces
--from, which still works.

time create (also add and log) reports "✓ Logged 1h 25m on alice/x#1
(title)"; time delete and reset name the time they delete in their
confirmation and ✓ line. Time records gain an issue_title JSON field.

Signed-off-by: bircni <bircni@icloud.com>
This commit is contained in:
bircni committed 2026-10-12 00:17:03 +02:00
1 parent c161a63eb6
commit 0f090654a4
5 files changed
+261 -96

No files matched your search

+3 -4
View File
@@ -213,10 +213,10 @@ func TestUsageErrorsPrecedeRequests(t *testing.T) {
{[]string{"team", "repo", "add", "2", "service", "-R", "acme/service"}, "--repo is not used by tea team repo add"},
{[]string{"time", "create", "1", "0s"}, `invalid duration "0s": time spent must be positive`},
{[]string{"time", "create", "1", "1ms"}, `invalid duration "1ms": use whole seconds`},
{[]string{"time", "list", "--issue", "0"}, "--issue must be positive"},
{[]string{"time", "list", "--issue", "0"}, `invalid issue "0"`},
{[]string{"time", "list", "--mine", "--issue", "1"}, "--mine, --issue and --username select mutually exclusive scopes"},
{[]string{"time", "list", "--from", "2026-10-02", "--until", "2026-10-01"}, "--from must not be later than --until"},
{[]string{"time", "list", "--from", "1h", "--until", "2h"}, "--from must not be later than --until"},
{[]string{"time", "list", "--from", "2026-10-02", "--until", "2026-10-01"}, "--since must not be later than --until"},
{[]string{"time", "list", "--since", "1h", "--until", "2h"}, "--since must not be later than --until"},
{[]string{"time", "list", "--total", "--json"}, "--total is a human-output summary"},
{[]string{"variable", "set", "BUILD_MODE", "--value="}, "the value of BUILD_MODE is empty"},
{[]string{"variable", "set", "GITEA_RESERVED", "--value", "text"}, "CI and names starting with GITEA_ or GITHUB_ are reserved"},
@@ -257,7 +257,6 @@ func TestConfirmationsNameResolvedValues(t *testing.T) {
want string
}{
{[]string{"release", "create"}, map[string]string{"Tag": "v1.0.0"}, "Create release v1.0.0 in alice/project?"},
{[]string{"time", "delete", "5", "7"}, nil, "delete tracked time entry 7 on alice/project #5"},
} {
t.Run(strings.Join(tc.args, " "), func(t *testing.T) {
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {