blob: da01bc421a7c7f97208e2a83fabe6012aeee569a (
plain)
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
|
#!/bin/sh
# github_issue_sync.sh
# Synchronizes GitHub and Linear issues with Taskwarrior
# Set new line and tab for word splitting
IFS='
'
# Logger with timestamp
log() {
echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*"
}
# Function to trim leading and trailing whitespace using sed
trim_whitespace() {
input="$1"
echo "$input" | sed 's/^[ \t]*//;s/[ \t]*$//'
}
# Validate necessary environment variables
validate_env_vars() {
oldIFS="$IFS"
IFS=' '
required_vars="api userid"
for var in $required_vars; do
val=$(pass show "api/linear/$var")
if [ -z "$val" ]; then
echo "Error: Environment variable $var is not set." >&2
exit 1
fi
done
IFS="$oldIFS"
}
# Retrieve and format GitHub issues
get_github_issues() {
issues=$(gh api -X GET /search/issues \
-f q='is:issue is:open assignee:TheSiahxyz' \
--jq '.items[] | {id: .number, description: .title, repository: .repository_url, html_url: .html_url}') || {
echo "Error: Unable to fetch GitHub issues" >&2
return 1
}
echo "$issues"
}
# Retrieve and format Linear issues
get_linear_issues() {
issues=$(curl -s -X POST \
-H "Content-Type: application/json" \
-H "Authorization: $LINEAR_API_KEY" \
--data '{"query": "query { user(id: \"'"$LINEAR_USER_ID"'\") { id name assignedIssues(filter: { state: { name: { nin: [\"Released\", \"Canceled\"] } } }) { nodes { id title url } } } }"}' \
https://api.linear.app/graphql | jq -c '.data.user.assignedIssues.nodes[] | {id: .id, description: .title, repository: "linear", html_url: .url}') || {
echo "Error: Unable to fetch Linear issues" >&2
return 1
}
echo "$issues"
}
# Synchronize a single issue with Taskwarrior
sync_to_taskwarrior() {
issue_line="$1"
issue_id=$(echo "$issue_line" | jq -r '.id')
issue_description=$(echo "$issue_line" | jq -r '.description')
issue_repo_name=$(echo "$issue_line" | jq -r '.repository' | awk -F/ '{print $NF}')
issue_url=$(echo "$issue_line" | jq -r '.html_url')
log "Processing Issue ID: $issue_id, Description: $issue_description"
task_uuid=$(get_task_id_by_description "$issue_description")
if [ -z "$task_uuid" ]; then
log "Creating new task for issue: $issue_description"
task_uuid=$(create_task "$issue_description" "+$issue_repo_name" "project:$issue_repo_name")
if [ -n "$task_uuid" ]; then
annotate_task "$task_uuid" "$issue_url"
log "Task created and annotated for: $issue_description"
else
log "Error: Failed to create task for: $issue_description" >&2
fi
else
log "Task already exists for: $issue_description (UUID: $task_uuid)"
fi
}
# Mark a GitHub issue as completed in Taskwarrior
sync_github_issue() {
task_description="$1"
task_uuid=$(get_task_id_by_description "$task_description")
if [ -n "$task_uuid" ]; then
mark_task_completed "$task_uuid"
log "Task marked as completed: $task_description (UUID: $task_uuid)"
else
log "Task UUID not found for: $task_description" >&2
fi
}
# Compare existing Taskwarrior tasks with current issues and mark as completed if not present
compare_and_display_tasks_not_in_issues() {
existing_task_descriptions="$1"
issues_descriptions="$2"
log "Starting comparison of Taskwarrior tasks and current issues."
existing_task_descriptions_array=$(echo "$existing_task_descriptions" | tr '\n' ' ')
issues_descriptions_array=$(echo "$issues_descriptions" | tr '\n' ' ')
for task_description in $existing_task_descriptions_array; do
trimmed_task_description=$(trim_whitespace "$task_description")
issue_exists=false
for issue_description in $issues_descriptions_array; do
trimmed_issue_description=$(trim_whitespace "$issue_description")
if [ "$(echo "$trimmed_task_description" | tr '[:upper:]' '[:lower:]')" = "$(echo "$trimmed_issue_description" | tr '[:upper:]' '[:lower:]')" ]; then
issue_exists=true
break
fi
done
if [ "$issue_exists" = false ]; then
log "No matching issue found for task: $trimmed_task_description. Marking as completed."
sync_github_issue "$trimmed_task_description"
fi
done
log "Comparison of Taskwarrior tasks and issues completed."
}
# Retrieve existing Taskwarrior task descriptions with +github or +linear tags and pending status
get_existing_task_descriptions() {
task +github or +linear status:pending export | jq -r '.[] | .description'
}
# Log retrieved issues
log_issues() {
issue_type="$1"
issues="$2"
log "Retrieved $issue_type issues: $(echo "$issues" | jq '.')"
}
# Synchronize all issues to Taskwarrior
sync_issues_to_taskwarrior() {
issues="$1"
echo "$issues" | jq -c '.' | while IFS= read -r line; do
sync_to_taskwarrior "$line"
done
}
# Main function to orchestrate the synchronization
main() {
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
echo "Not a git repository."
exit 1
fi
validate_env_vars
github_issues=$(get_github_issues)
linear_issues=$(get_linear_issues)
if [ -z "$github_issues" ] && [ -z "$linear_issues" ]; then
log "No issues retrieved from GitHub or Linear. Exiting."
exit 0
fi
if [ -n "$github_issues" ]; then
log_issues "GitHub" "$github_issues"
sync_issues_to_taskwarrior "$github_issues"
fi
if [ -n "$linear_issues" ]; then
log_issues "Linear" "$linear_issues"
sync_issues_to_taskwarrior "$linear_issues"
fi
existing_task_descriptions=$(get_existing_task_descriptions)
compare_and_display_tasks_not_in_issues "$existing_task_descriptions" "$(
echo "$github_issues" | jq -r '.description'
echo "$linear_issues" | jq -r '.description'
)"
}
main
|