1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
package askcli
import (
"context"
"fmt"
"io"
"strings"
)
type resolvedTaskSelector struct {
Input string
UUID string
Alias string
UsedAlias bool
}
func (d *Dispatcher) resolveTaskSelector(ctx context.Context, selector string, stderr io.Writer) (resolvedTaskSelector, []TaskExport, int, error) {
normalized, requiresLookup, err := normalizeTaskSelectorInput(selector)
if err != nil {
return resolvedTaskSelector{}, nil, 1, err
}
resolved, err := resolveTaskSelectorFromCache(normalized, requiresLookup)
if err != nil {
return resolvedTaskSelector{}, nil, 1, err
}
tasks, code, err := d.exportTasks(ctx, []string{"uuid:" + resolved.UUID, "export"}, stderr)
if err != nil {
if resolved.UsedAlias && strings.Contains(err.Error(), "task not found") {
return resolvedTaskSelector{}, nil, 1, fmt.Errorf("alias %q is stale: task %s was not found", selector, resolved.UUID)
}
return resolvedTaskSelector{}, nil, code, err
}
aliases, err := ensureTaskAliases(tasks)
if err != nil {
return resolvedTaskSelector{}, nil, 1, err
}
if alias, ok := aliases[resolved.UUID]; ok {
resolved.Alias = alias
}
return resolved, tasks, 0, nil
}
func normalizeTaskSelectorInput(selector string) (string, bool, error) {
normalized := NormalizeUUID(selector)
if selector != normalized && IsNumericID(normalized) {
return "", false, fmt.Errorf(strings.TrimSpace(RejectNumericID()))
}
return normalized, selector == normalized, nil
}
func resolveTaskSelectorFromCache(selector string, allowAlias bool) (resolvedTaskSelector, error) {
resolved := resolvedTaskSelector{Input: selector, UUID: selector}
if !allowAlias || !looksLikeTaskAlias(selector) {
return resolved, nil
}
cache, path, err := loadTaskAliasCache()
if err != nil {
return resolvedTaskSelector{}, err
}
now := nowTaskAliasCache().UTC()
changed := cache.prune(now)
uuidFromAlias, aliasFound, aliasChanged := cache.lookupUUIDByAlias(selector, now)
changed = changed || aliasChanged
aliasForUUID, uuidFound, uuidChanged := cache.lookupAliasByUUID(selector, now)
changed = changed || uuidChanged
switch {
case aliasFound && uuidFound && uuidFromAlias != selector:
if changed {
if err := cache.save(path); err != nil {
return resolvedTaskSelector{}, err
}
}
return resolvedTaskSelector{}, fmt.Errorf("task selector %q is ambiguous: it matches alias for %s and UUID %s; use uuid:%s to force UUID", selector, uuidFromAlias, selector, selector)
case aliasFound:
if changed {
if err := cache.save(path); err != nil {
return resolvedTaskSelector{}, err
}
}
return resolvedTaskSelector{
Input: selector,
UUID: uuidFromAlias,
Alias: selector,
UsedAlias: true,
}, nil
case uuidFound:
if changed {
if err := cache.save(path); err != nil {
return resolvedTaskSelector{}, err
}
}
return resolvedTaskSelector{
Input: selector,
UUID: selector,
Alias: aliasForUUID,
}, nil
default:
if IsNumericID(selector) {
return resolvedTaskSelector{}, fmt.Errorf(strings.TrimSpace(RejectNumericID()))
}
if changed {
if err := cache.save(path); err != nil {
return resolvedTaskSelector{}, err
}
}
return resolvedTaskSelector{}, fmt.Errorf("task selector %q did not match a known alias; use uuid:%s to force UUID", selector, selector)
}
}
func looksLikeTaskAlias(selector string) bool {
_, ok := decodeTaskAliasID(selector)
return ok && !strings.Contains(selector, "-")
}
|