-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathjira.applescript
More file actions
726 lines (609 loc) · 19.7 KB
/
jira.applescript
File metadata and controls
726 lines (609 loc) · 19.7 KB
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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
--- An Applescript to automate the tedious workflow within JIRA.
---
--- It allows you to change from a given ticket state to any other
--- reachable state and it automates filling in required data
---
--- It can be run from the Agile board (you select ticket to
--- change state for) or from a specific ticket page.
---
--- Requirements:
---
--- 1. Safari browser
--- a. Turn on Develop menu
--- * Preferences
--- * Advanced Tab
--- * Turn on "Show Develop menu in menu bar"
--- b. Enable in Develop menu: "Allow Javascript from Apple Events"
---
--- Features:
---
--- 1. Can be run from Agile page (and you can select ticket to change state on)
--- or from the ticket page
---
--- Keyboard shortcut:
---
--- 1. Easily be done with free Quicksilver app: https://qsapp.com/download.php
---
--- Author:
---
--- David Boyd 6/18/17
---
--- Values required for "Sent for Build" action dialog
set developerAssigned to "na"
set developerReviewerDevLead to "na"
set useHardcodedStates to true
--- Data structure modelling states and actions in JIRA
set states to {¬
"REQUIREMENT", {"IN DESIGN", "Start Design"}, ¬
"BACKLOG(DEV)", {"IN PROGRESS", "Start Dev", "IN DESIGN", "Need Design"}, ¬
"IN DESIGN", {"IN DESIGN REVIEW", "Sent for Design Review"}, ¬
"IN DESIGN REVIEW", {"IN DESIGN", "Need Design Rework", "BACKLOG(DEV)", "Design Approved"}, ¬
"IN DEV REVIEW", {"UX AUDIT", "Sent for UX Audit", "WAITING FOR BUILD", "Sent for Build"}, ¬
"DEV UNIT TEST", {"IN DEV REVIEW", "Sent for Dev Review"}, ¬
"UX AUDIT", {"IN DEV REVIEW", "UX Audit completed"}, ¬
"IN PROGRESS", {"DEV UNIT TEST", "Unit Testing", "IN DEV REVIEW", "Sent for Dev Review"}, ¬
"WAITING FOR BUILD", {"READY FOR DEV TEST", "Build Done"}, ¬
"READY FOR DEV TEST", {"READY FOR QA", "Dev Tested"}, ¬
"READY FOR QA", {"QA IN PROGRESS", "QA takes over"}, ¬
"QA IN PROGRESS", {"QA Blocked", "QA Blocked", "CLOSED", "QA Verified", "READY FOR PRODUCTION", "QA Complete"}, ¬
"QA BLOCKED", {"QA IN PROGRESS", "QA Resumed"}, ¬
"QA REJECTED", {"IN PROGRESS", "Rework on Code"}, ¬
"READY FOR PRODUCTION", {"CLOSED", "Deployed"}, ¬
"CLOSED", {"BACKLOG(DEV)", "Re-Open"}}
set statesDirect to {¬
"ARCHIVE", "Archive", ¬
"DUPLICATE", "Duplicate", ¬
"HIBERNATED", "Hibernated", ¬
"CLOSED", "Closed"}
set statesTo to {¬
"BACKLOG(DEV)", ¬
"IN PROGRESS", ¬
"IN DEV REVIEW", ¬
"WAITING FOR BUILD", ¬
"READY FOR DEV TEST", ¬
"READY FOR QA", ¬
"CLOSED"}
set statesToLabel to {¬
"Backlog", ¬
"In Progress", ¬
"Code Complete", ¬
"Merged", ¬
"Dev Testing", ¬
"QA", ¬
"Done"}
--- Another workflow for bug tickets
set states_SPR_Workflow to {¬
"REQUIREMENT", {"BACKLOG(DEV)", "Queue for Dev"}, ¬
"OPEN", {"BACKLOG(DEV)", "Queue for Dev"}, ¬
"BACKLOG(DEV)", {"IN PROGRESS", "Start", "OPEN", "Need More Info"}, ¬
"IN PROGRESS", {"IN DEV REVIEW", "Sent for Dev Review"}, ¬
"IN DEV REVIEW", {"IN PROGRESS", "Need Dev Rework", "WAITING FOR BUILD", "Ready for Build"}, ¬
"WAITING FOR BUILD", {"READY FOR QA", "Build Done"}, ¬
"READY FOR QA", {"QA IN PROGRESS", "Start QA"}, ¬
"QA IN PROGRESS", {"QA REJECTED", "Reject", "CLOSED", "QA Verified", "READY FOR PRODUCTION", "QA Complete"}, ¬
"QA REJECTED", {"IN PROGRESS", "Rework on Code"}, ¬
"READY FOR PRODUCTION", {"CLOSED", "Deployed"}, ¬
"CLOSED", {"BACKLOG(DEV)", "Re-Open"}}
set statesTo_SPR_Workflow to {¬
"BACKLOG(DEV)", ¬
"IN PROGRESS", ¬
"IN DEV REVIEW", ¬
"WAITING FOR BUILD", ¬
"READY FOR QA", ¬
"CLOSED"}
set statesToLabel_SPR_Workflow to {¬
"Backlog", ¬
"In Progress", ¬
"Code Complete", ¬
"Merged", ¬
"QA", ¬
"Done"}
--- Another workflow for support tickets
set states_L3_Support_Workflow to {¬
"REQUIREMENT", {"BACKLOG(DEV)", "Queue for Dev"}, ¬
"NEED CONFIRMATION", {"BACKLOG(DEV)", "Issue Confirmed", "OPEN", "Need More Info"}, ¬
"BACKLOG(DEV)", {"IN PROGRESS", "Start", "NEED CONFIRMATION", "Need More Info"}, ¬
"IN PROGRESS", {"IN DEV REVIEW", "Sent for Dev Review"}, ¬
"IN DEV REVIEW", {"IN PROGRESS", "Need Dev Rework", "WAITING FOR BUILD", "Ready for Build"}, ¬
"WAITING FOR BUILD", {"READY FOR QA", "Build Done"}, ¬
"READY FOR QA", {"QA IN PROGRESS", "Start QA"}, ¬
"QA IN PROGRESS", {"QA REJECTED", "Reject", "CLOSED", "QA Verified", "READY FOR PRODUCTION", "QA Complete"}, ¬
"QA REJECTED", {"IN PROGRESS", "Rework on Code"}, ¬
"READY FOR PRODUCTION", {"CLOSED", "Deployed"}, ¬
"CLOSED", {"BACKLOG(DEV)", "Re-Open"}}
set statesTo_L3_Support_Workflow to {¬
"NEEDS CONFIRMATION", ¬
"BACKLOG(DEV)", ¬
"IN PROGRESS", ¬
"IN DEV REVIEW", ¬
"WAITING FOR BUILD", ¬
"READY FOR QA", ¬
"CLOSED"}
set statesToLabel_L3_Support_Workflow to {¬
"Requirements", ¬
"Backlog", ¬
"In Progress", ¬
"Code Complete", ¬
"Merged", ¬
"QA", ¬
"Done"}
--- Another workflow for support tickets
set states_Release_Manual_Workflow to {¬
"REQUIREMENT", {"IN DESIGN", "Start Design"}, ¬
"IN DESIGN", {"BACKLOG(DEV)", "Queue for Dev"}, ¬
"BACKLOG(DEV)", {"IN PROGRESS", "Start Dev"}, ¬
"IN DEV REVIEW", {"UX AUDIT", "Sent for UX Audit", "WAITING FOR BUILD", "Sent for Build"}, ¬
"DEV UNIT TEST", {"IN DEV REVIEW", "Sent for Dev Review"}, ¬
"UX AUDIT", {"IN DEV REVIEW", "UX Audit completed"}, ¬
"IN PROGRESS", {"DEV UNIT TEST", "Unit Testing", "IN DEV REVIEW", "Sent for Dev Review"}, ¬
"WAITING FOR BUILD", {"READY FOR DEV TEST", "Build Done"}, ¬
"READY FOR DEV TEST", {"READY FOR QA", "Dev Tested"}, ¬
"READY FOR QA", {"QA IN PROGRESS", "QA takes over"}, ¬
"QA IN PROGRESS", {"QA Blocked", "QA Blocked", "CLOSED", "QA Verified", "READY FOR PRODUCTION", "QA Complete"}, ¬
"QA BLOCKED", {"QA IN PROGRESS", "QA Resumed"}, ¬
"QA REJECTED", {"IN PROGRESS", "Rework on Code"}, ¬
"READY FOR PRODUCTION", {"CLOSED", "Deployed"}, ¬
"CLOSED", {"BACKLOG(DEV)", "Re-Open"}}
set statesTo_Release_Manual_Workflow to {¬
"BACKLOG(DEV)", ¬
"IN PROGRESS", ¬
"IN DEV REVIEW", ¬
"WAITING FOR BUILD", ¬
"READY FOR DEV TEST", ¬
"READY FOR QA", ¬
"CLOSED"}
set statesToLabel_Release_Manual_Workflow to {¬
"Backlog", ¬
"In Progress", ¬
"Code Complete", ¬
"Merged", ¬
"Dev Testing", ¬
"QA", ¬
"Done"}
set browserUrl to browserGetUrl()
log browserUrl
set createdTab to false
--- Select from a list of tickets on our Agile board
if browserUrl does not contain "/DISPLAY-" then
set tickets to browserGetTickets()
set ticket to choose from list tickets with prompt "Choose a JIRA ticket:"
if ticket is false then
error number -128 (* user cancelled *)
else
set ticket to ticket's item 1 (* extract choice from list *)
end if
--- Open the selected ticket in a new tab
browserOpenTicket(getId(ticket))
set createdTab to true
end if
--- display dialog "You choose: " & ticket
--- If SPR Bug Workflow, then use different set of states
if browserIsWorkflow("=SPR+Bug+Workflow") is true then
set states to states_SPR_Workflow
set statesTo to statesTo_SPR_Workflow
set statesToLabel to statesToLabel_SPR_Workflow
end if
--- If L3 Support Workflow, then use different set of states
if browserIsWorkflow("=L3+Support+Workflow") is true then
set states to states_L3_Support_Workflow
set statesTo to statesTo_L3_Support_Workflow
set statesToLabel to statesToLabel_L3_Support_Workflow
end if
--- If Release Manual Workflow, then use different set of states
if browserIsWorkflow("=Release+Manual") is true then
set states to states_Release_Manual_Workflow
set statesTo to statesTo_Release_Manual_Workflow
set statesToLabel to statesToLabel_Release_Manual_Workflow
end if
set stateFrom to browserGetState()
if useHardcodedStates is true then
set availableStates to statesToLabel
else
--- Get all the states that are reachable from this current state
set availableStates to sortArray(getStatePath(states, stateFrom))
set stateFrom to stringUpper(stateFrom)
end if
set stateTo to choose from list availableStates with prompt "Current ticket state is:
" & stateFrom & "
Change to:"
if stateTo is false then
--- Close the tab we created after we're done
if createdTab is true then
browserCloseTab(browserUrl)
end if
error number -128 (* user cancelled *)
else if useHardcodedStates is true then
set foundOffset to findArray(statesToLabel, stateTo)
--- Wasn't working until I made it two-step assignment
set newState to (item foundOffset of statesTo as string)
set stateTo to newState
end if
set actionPath to getActionPath(states, stateFrom, stringTrimTrailing(stateTo))
log actionPath
set i to 1
repeat until i > (count of actionPath)
set action to item i of actionPath
repeat while getAction(action) is ""
delay 1
end repeat
--- log "FOUND ACTION ID: " & getAction(action)
browserClickById(getAction(action))
log "Performing action: " & action
if action is "Start Design" or ¬
action is "Sent for Design Review" or ¬
action is "Design Approved" or ¬
action is "Issue Confirmed" or ¬
action is "Start" or ¬
action is "Start Dev" or ¬
action is "Sent for Dev Review" or ¬
action is "Sent for Build" or ¬
action is "Ready for Build" or ¬
action is "Dev Tested" or ¬
action is "QA Complete" or ¬
action is "QA Verified" or ¬
action is "Deployed" or ¬
action is "Re-Open" then
--- Wait until dialog appears
repeat while browserDialogOpen() is false
delay 1
end repeat
--- Do some required form things for these actions
if action is "Design Approved" then
repeat while browserGetCheckbox("No") is ""
delay 1
end repeat
browserCheckById(browserGetCheckbox("No"))
end if
if action is "Sent for Build" or action is "Ready for Build" then
repeat while browserGetCheckbox("None") is ""
delay 1
end repeat
browserSetTextArea("Assigned Developer(s)", developerAssigned)
browserSetTextArea("Reviewer/Dev Lead", developerReviewerDevLead)
browserCheckById(browserGetCheckbox("None"))
end if
if action is "Start" then
repeat while browserGetCheckbox("None") is ""
delay 1
end repeat
browserSetTextArea("Story Points", "1")
end if
--- Click and wait until dialog disappears
repeat while browserDialogOpen() is true
browserClickById("issue-workflow-transition-submit")
delay 1
end repeat
end if
--- error number -128
set i to i + 1
end repeat
--- Close the tab we created after we're done
if createdTab is true then
browserCloseTab(browserUrl)
end if
(* ==== FUNCTIONS ==== *)
(* ==== FUNCTIONS ==== *)
(* ==== FUNCTIONS ==== *)
on browserGetTickets()
set tickets to {}
set pos to 0
repeat while browserGetTicket(pos) starts with "DISPLAY"
set the end of tickets to browserGetTicket(pos)
set pos to pos + 1
end repeat
return tickets
end browserGetTickets
on browserGetTicket(pos)
tell application "Safari"
tell front window
tell current tab
set ticket to do JavaScript "
var issues = document.getElementsByClassName('ghx-issue');
if (" & (pos) & " < issues.length) {
document.getElementsByClassName('ghx-issue')[" & (pos) & "].attributes['data-issue-key'].value + ': ' +
document.getElementsByClassName('ghx-issue')[" & (pos) & "].children[0].attributes['data-tooltip'].value;
} else {
'NA';
}"
end tell
end tell
end tell
return ticket
end browserGetTicket
on browserGetUrl()
set browserUrl to ""
tell application "Safari"
tell front window
tell current tab
set browserUrl to URL
end tell
end tell
end tell
return browserUrl
end browserGetUrl
on browserCloseTab(browserUrl)
tell application "Safari"
tell front window
close current tab
set current tab to (first tab whose URL contains browserUrl)
end tell
end tell
end browserCloseTab
on browserClickById(id)
tell application "Safari"
tell front window
tell current tab
do JavaScript "document.getElementById('" & id & "').click()"
end tell
end tell
end tell
end browserClickById
on browserCheckById(id)
tell application "Safari"
tell front window
tell current tab
do JavaScript "document.getElementById('" & id & "').checked = true"
end tell
end tell
end tell
end browserCheckById
on browserGetState()
tell application "Safari"
tell front window
tell current tab
set result to do JavaScript "document.getElementById('status-val').children[0].innerHTML;"
end tell
end tell
end tell
return result
end browserGetState
on browserOpenTicket(ticketId)
tell application "Safari"
--- activate
tell front window
set current tab to (make new tab with properties {URL:"https://sprinklr.atlassian.net/browse/" & ticketId})
delay 2
end tell
end tell
end browserOpenTicket
on browserGetCheckbox(value)
set checkId to ""
tell application "Safari"
tell front window
tell current tab
set cmd to "
var list = document.querySelectorAll('div.checkbox');
var x = list.length;
while (x--) {
if (list[x].children[1].innerHTML === '" & value & "') {
var result = list[x].children[0].attributes['id'].value;
!result ? '' : result;
break;
}
}
if (x === -1) '';"
set checkId to do JavaScript cmd
end tell
end tell
end tell
if checkId as string is "missing value" then
set checkId to ""
end if
return checkId
end browserGetCheckbox
on browserSetTextArea(label, value)
tell application "Safari"
tell front window
tell current tab
set cmd to "
var list = document.querySelectorAll('label');
var x = list.length;
while (x--) {
if (list[x].innerHTML === '" & label & "') {
var id = list[x].attributes['for'].value;
document.getElementById(id).value = '" & value & "';
break;
}
}"
set result to do JavaScript cmd
end tell
end tell
end tell
return result
end browserSetTextArea
on browserIsWorkflow(workflow)
tell application "Safari"
tell front window
tell current tab
set cmd to "
document.querySelectorAll(\"a[href*='" & workflow & "']\").length === 0 ? false : true"
set result to do JavaScript cmd
end tell
end tell
end tell
return result
end browserIsWorkflow
on browserDialogOpen()
tell application "Safari"
tell front window
tell current tab
set cmd to "
document.getElementsByClassName('jira-dialog-open').length === 0 ? false : true"
set result to do JavaScript cmd
end tell
end tell
end tell
return result
end browserDialogOpen
on getAction(actionName)
set actions to {}
set pos to 0
repeat while getActionId(pos) starts with "action"
if getActionName(pos) is actionName then
return getActionId(pos)
end if
set pos to pos + 1
end repeat
return ""
end getAction
on getActionId(pos)
tell application "Safari"
tell document 1
set result to do JavaScript "var element = document.getElementsByClassName('issueaction-workflow-transition')[" & (pos) & "]; element.attributes['id'].value;"
end tell
end tell
return stringTrimTrailing(result)
end getActionId
on getActionName(pos)
tell application "Safari"
tell document 1
set result to do JavaScript "var element = document.getElementsByClassName('issueaction-workflow-transition')[" & (pos) & "]; element.children[0].innerHTML;"
end tell
end tell
return stringTrimTrailing(result)
end getActionName
--- Fetch just the ticket number using sed
on getId(value)
set cmd to "echo \"" & value & "\" | sed \"s/.*\\(DISPLAY-[0-9]*\\).*/\\1/\"" as string
set ticketId to do shell script cmd
return ticketId
end getId
on getStatePath(states, stateFrom)
return getStatePathDepth(states, stateFrom, 0, {}, stateFrom)
end getStatePath
on getStatePathDepth(states, stateFrom, depth, stateList, stateExclude)
set itemStates to getStateItems(states, stateFrom)
set i to 1
--- Easy cycle detector. Set to # of states we have.
if depth > 15 then
return stateList
end if
--- log "from:" & stateFrom & " to: " & stateTo & ", depth: " & depth
repeat until i > (count of itemStates)
set subState to item i of itemStates
if findArray(stateList, subState) is -1 then
if subState is not stateExclude then
set stateList to stateList & subState
end if
set stateList to getStatePathDepth(states, subState, depth + 1, stateList, stateExclude)
end if
set i to i + 2
end repeat
return stateList
end getStatePathDepth
on getStatePath2(states, stateFrom, stateTo)
return rest of getStatePathDepth2(states, stateFrom, stateTo, 0)
--- return getStatePathDepth2(states, stateFrom, stateTo, 0)
end getStatePath2
on getStatePathDepth2(states, stateFrom, stateTo, depth)
set itemStates to getStateItems(states, stateFrom)
set i to 1
set found to {9999, "DUMMY"}
--- Easy cycle detector. Set to # of states we have.
if depth > 15 then
return found
end if
--- log "from:" & stateFrom & " to: " & stateTo & ", depth: " & depth
repeat until i > (count of itemStates)
set subState to item i of itemStates
if subState is stateTo then
return {depth} & subState
else
set find to getStatePathDepth2(states, subState, stateTo, depth + 1)
set findDepth to item 1 of find
if findDepth is not 9999 then
if item 1 of found > item 1 of find then
set found to find
set foundStates to rest of found
set foundStates to {subState} & foundStates
set found to item 1 of found & foundStates
end if
end if
end if
set i to i + 2
end repeat
--- log "found: " & found
return found
end getStatePathDepth2
on getActionPath(states, stateFrom, stateTo)
return rest of getActionPathDepth(states, stateFrom, stateTo, 0)
--- return getActionPathDepth(states, stateFrom, stateTo, 0)
end getActionPath
on getActionPathDepth(states, stateFrom, stateTo, depth)
set itemStates to getStateItems(states, stateFrom)
set i to 1
set found to {9999, "DUMMY"}
--- Easy cycle detector. Set to # of states we have.
if depth > 15 then
return found
end if
if depth is 0 then
log "from:" & stateFrom & " to: " & stateTo & ", depth: " & depth
end if
repeat until i > (count of itemStates)
set subState to item i of itemStates
set subAction to item (i + 1) of itemStates
if subState is stateTo then
return {depth} & subAction
else
set find to getActionPathDepth(states, subState, stateTo, depth + 1)
set findDepth to item 1 of find
if findDepth is not 9999 then
if item 1 of found > item 1 of find then
set found to find
set foundActions to rest of found
set foundActions to {subAction} & foundActions
set found to item 1 of found & foundActions
end if
end if
end if
set i to i + 2
end repeat
--- log "found: " & found
return found
end getActionPathDepth
on getStateItems(states, match)
set stateItems to {}
set i to 1
repeat until i > (count of states)
if item i of states is match then return item (i + 1) of states
set i to i + 2
end repeat
return stateItems
end getStateItems
on findArray(source, value)
set i to 1
repeat until i > (count of source)
set sourceItem to item i of source as string
if sourceItem is value as string then
return i
end if
set i to i + 1
end repeat
return -1
end findArray
on sortArray(source)
set saveDelims to AppleScript's text item delimiters
set AppleScript's text item delimiters to {ASCII character 10} -- always a linefeed
set listString to (source as string)
set newString to do shell script "echo " & quoted form of listString & " | sort -f"
set newList to (paragraphs of newString)
set AppleScript's text item delimiters to saveDelims
return newList
end sortArray
on stringTrimSpaces(value)
set cmd to "echo \"" & value & "\" | tr -d ' '" as string
set result to do shell script cmd
return result
end stringTrimSpaces
on stringUpper(value)
set cmd to "echo \"" & value & "\" | tr '[:lower:]' '[:upper:]'" as string
set result to do shell script cmd
return result
end stringUpper
on stringTrimTrailing(value)
set checker to value as string
set cmd to ("echo " & quoted form of checker) & " | sed -e 's/[[:space:]]*$//'" as string
set result to do shell script cmd
return result
end stringTrimTrailing