ya-git-jira 2.0.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/.dockerignore +8 -0
  2. package/.opencode/skills/git-confluence/SKILL.md +18 -18
  3. package/.opencode/skills/git-jira/SKILL.md +18 -18
  4. package/.opencode/skills/git-lab/SKILL.md +30 -30
  5. package/Dockerfile +58 -0
  6. package/README.md +31 -12
  7. package/bin/git-api.ts +2 -4
  8. package/bin/git-bump.ts +2 -4
  9. package/bin/git-confluence-page-search.ts +4 -6
  10. package/bin/git-confluence-page-show.ts +3 -5
  11. package/bin/git-confluence-page-update.ts +3 -5
  12. package/bin/git-confluence-page.ts +2 -4
  13. package/bin/git-confluence-space-list.ts +3 -5
  14. package/bin/git-confluence-space.ts +2 -4
  15. package/bin/git-confluence-whoami.ts +3 -5
  16. package/bin/git-confluence.ts +11 -4
  17. package/bin/git-jira-issue-list.ts +2 -4
  18. package/bin/git-jira-issue-show.ts +3 -5
  19. package/bin/git-jira-issue.ts +2 -4
  20. package/bin/git-jira-start.ts +2 -4
  21. package/bin/git-jira-whoami.ts +3 -5
  22. package/bin/git-jira.ts +11 -4
  23. package/bin/git-lab-group-list.ts +4 -6
  24. package/bin/git-lab-group.ts +2 -4
  25. package/bin/git-lab-merge-active.ts +4 -6
  26. package/bin/git-lab-merge-todo.ts +4 -6
  27. package/bin/git-lab-merge-train-list.ts +3 -5
  28. package/bin/git-lab-merge-train.ts +2 -4
  29. package/bin/git-lab-merge.ts +2 -4
  30. package/bin/git-lab-namespace-list.ts +3 -5
  31. package/bin/git-lab-namespace.ts +2 -4
  32. package/bin/git-lab-project-list.ts +4 -6
  33. package/bin/git-lab-project-mr-list.ts +4 -6
  34. package/bin/git-lab-project-mr.ts +2 -4
  35. package/bin/git-lab-project-pipeline-jobs.ts +2 -4
  36. package/bin/git-lab-project-pipeline-latest.ts +2 -4
  37. package/bin/git-lab-project-pipeline-list.ts +4 -6
  38. package/bin/git-lab-project-pipeline-log.ts +2 -4
  39. package/bin/git-lab-project-pipeline.ts +2 -4
  40. package/bin/git-lab-project-whereami.ts +3 -5
  41. package/bin/git-lab-project.ts +2 -4
  42. package/bin/git-lab-whoami.ts +3 -5
  43. package/bin/git-lab.ts +11 -4
  44. package/bin/gitj-install-skills.ts +15 -12
  45. package/bin/gitj.ts +6 -1
  46. package/dist/bin/git-api.js +33 -5
  47. package/dist/bin/git-bump.js +32 -24
  48. package/dist/bin/git-confluence-page-search.js +46 -26
  49. package/dist/bin/git-confluence-page-show.js +24 -4
  50. package/dist/bin/git-confluence-page-update.js +24 -4
  51. package/dist/bin/git-confluence-page.js +30 -16
  52. package/dist/bin/git-confluence-space-list.js +45 -25
  53. package/dist/bin/git-confluence-space.js +46 -28
  54. package/dist/bin/git-confluence-whoami.js +45 -25
  55. package/dist/bin/git-confluence.js +45 -31
  56. package/dist/bin/git-jira-issue-list.js +36 -24
  57. package/dist/bin/git-jira-issue-show.js +16 -4
  58. package/dist/bin/git-jira-issue.js +18 -10
  59. package/dist/bin/git-jira-start.js +16 -4
  60. package/dist/bin/git-jira-whoami.js +37 -25
  61. package/dist/bin/git-jira.js +32 -22
  62. package/dist/bin/git-lab-group-list.js +18 -6
  63. package/dist/bin/git-lab-group.js +19 -9
  64. package/dist/bin/git-lab-merge-active.js +18 -6
  65. package/dist/bin/git-lab-merge-todo.js +18 -6
  66. package/dist/bin/git-lab-merge-train-list.js +17 -5
  67. package/dist/bin/git-lab-merge-train.js +18 -8
  68. package/dist/bin/git-lab-merge.js +25 -21
  69. package/dist/bin/git-lab-namespace-list.js +38 -26
  70. package/dist/bin/git-lab-namespace.js +39 -29
  71. package/dist/bin/git-lab-project-list.js +18 -6
  72. package/dist/bin/git-lab-project-mr-list.js +18 -6
  73. package/dist/bin/git-lab-project-mr.js +19 -9
  74. package/dist/bin/git-lab-project-pipeline-jobs.js +16 -4
  75. package/dist/bin/git-lab-project-pipeline-latest.js +16 -4
  76. package/dist/bin/git-lab-project-pipeline-list.js +18 -6
  77. package/dist/bin/git-lab-project-pipeline-log.js +16 -4
  78. package/dist/bin/git-lab-project-pipeline.js +22 -18
  79. package/dist/bin/git-lab-project-whereami.js +17 -5
  80. package/dist/bin/git-lab-project.js +32 -38
  81. package/dist/bin/git-lab-whoami.js +17 -5
  82. package/dist/bin/git-lab.js +61 -81
  83. package/dist/bin/gitj-install-skills.js +21 -11
  84. package/dist/bin/gitj.js +154 -153
  85. package/dist/index.js +32 -1
  86. package/install-docker-gitj.sh +77 -0
  87. package/lib/api.ts +19 -1
  88. package/lib/confluence/api.ts +12 -0
  89. package/lib/confluence/config.ts +3 -3
  90. package/lib/gitlab/api.ts +4 -0
  91. package/lib/gitlab/config.ts +2 -2
  92. package/lib/is_main.ts +11 -0
  93. package/lib/jira.ts +7 -3
  94. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -717,6 +717,10 @@ async function confluenceApi(endpoint) {
717
717
  };
718
718
  let request = new Request(uri, options);
719
719
  const response = await fetch(request);
720
+ if (!response.ok) {
721
+ const text = await response.text();
722
+ throw new Error(`Confluence API ${endpoint} failed (${response.status}): ${text}`);
723
+ }
720
724
  let link = getNextLink(response.headers.get("Link"));
721
725
  const body = await response.json();
722
726
  if (!body.results) {
@@ -777,6 +781,10 @@ async function confluenceSearch(cql) {
777
781
  while (uri) {
778
782
  const request = new Request(uri, options);
779
783
  const response = await fetch(request);
784
+ if (!response.ok) {
785
+ const text = await response.text();
786
+ throw new Error(`Confluence search failed (${response.status}): ${text}`);
787
+ }
780
788
  const body = await response.json();
781
789
  if (body.results) {
782
790
  allResults = allResults.concat(body.results);
@@ -805,6 +813,10 @@ async function confluenceApiV1(endpoint) {
805
813
  };
806
814
  const request = new Request(uri, options);
807
815
  const response = await fetch(request);
816
+ if (!response.ok) {
817
+ const text = await response.text();
818
+ throw new Error(`Confluence API v1 ${endpoint} failed (${response.status}): ${text}`);
819
+ }
808
820
  const result = await response.json();
809
821
  return result;
810
822
  }
@@ -817,7 +829,7 @@ async function getGitlabConfig() {
817
829
  const host = await hostP || "gitlab.com";
818
830
  const user = await gitEmailP2 || await gitlabEmailP;
819
831
  if (!user)
820
- throw new Error("Neither user.email nor gitlab.email in git config");
832
+ throw new Error("Neither user.email nor gitlab.user in git config");
821
833
  const token = await tokenP;
822
834
  if (!token)
823
835
  throw new Error("gitlab.token not in git config");
@@ -854,6 +866,10 @@ async function gitlabApi(endpoint) {
854
866
  };
855
867
  let request = new Request(uri, options);
856
868
  const response = await fetch(request);
869
+ if (!response.ok) {
870
+ const text = await response.text();
871
+ throw new Error(`GitLab API ${endpoint} failed (${response.status}): ${text}`);
872
+ }
857
873
  let link = getNextLink2(response.headers.get("Link"));
858
874
  let partial = await response.json();
859
875
  let result = partial;
@@ -1016,6 +1032,16 @@ function isMain(self) {
1016
1032
  const result = argv1Base === selfBase;
1017
1033
  return result;
1018
1034
  }
1035
+ async function runMain(self, create) {
1036
+ if (!isMain(self))
1037
+ return;
1038
+ try {
1039
+ await create().parseAsync(Bun.argv);
1040
+ } catch (err) {
1041
+ console.error(`error: ${err instanceof Error ? err.message : String(err)}`);
1042
+ process.exit(1);
1043
+ }
1044
+ }
1019
1045
  // lib/jira.ts
1020
1046
  var gitEmailP3 = getConfig("user.email");
1021
1047
  var jiraEmailP2 = getConfig("jira.user");
@@ -1052,6 +1078,10 @@ async function jiraApi(endpoint) {
1052
1078
  };
1053
1079
  const request = new Request(uri, options);
1054
1080
  const response = await fetch(request);
1081
+ if (!response.ok) {
1082
+ const text = await response.text();
1083
+ throw new Error(`Jira API ${endpoint} failed (${response.status}): ${text}`);
1084
+ }
1055
1085
  const result = await response.json();
1056
1086
  return result;
1057
1087
  }
@@ -1072,6 +1102,7 @@ async function myUnresolvedIssues() {
1072
1102
  export {
1073
1103
  whoami,
1074
1104
  spawn,
1105
+ runMain,
1075
1106
  projectScopedGetText,
1076
1107
  projectScopedGet,
1077
1108
  myUnresolvedIssues,
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ IMAGE_NAME="gitj"
5
+ WRAPPER_NAME="gitj"
6
+
7
+ # --- Find a writable bin directory on PATH under $HOME ---
8
+
9
+ find_bin_dir() {
10
+ # Prefer ~/.local/bin if it exists and is on PATH
11
+ if [ -d "$HOME/.local/bin" ]; then
12
+ case ":$PATH:" in
13
+ *":$HOME/.local/bin:"*) echo "$HOME/.local/bin"; return 0 ;;
14
+ esac
15
+ fi
16
+
17
+ # Search PATH for any directory under $HOME
18
+ IFS=: read -ra path_dirs <<< "$PATH"
19
+ for dir in "${path_dirs[@]}"; do
20
+ case "$dir" in
21
+ "$HOME"/*)
22
+ if [ -d "$dir" ] && [ -w "$dir" ]; then
23
+ echo "$dir"
24
+ return 0
25
+ fi
26
+ ;;
27
+ esac
28
+ done
29
+
30
+ # Fall back to creating ~/.local/bin
31
+ echo ""
32
+ return 1
33
+ }
34
+
35
+ # --- Main ---
36
+
37
+ echo "Building Docker image '$IMAGE_NAME'..."
38
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
39
+ docker build -t "$IMAGE_NAME" "$SCRIPT_DIR"
40
+ echo ""
41
+
42
+ BIN_DIR=$(find_bin_dir) || true
43
+
44
+ if [ -z "$BIN_DIR" ]; then
45
+ BIN_DIR="$HOME/.local/bin"
46
+ echo "No bin directory found under \$HOME on your PATH."
47
+ echo "Creating $BIN_DIR ..."
48
+ mkdir -p "$BIN_DIR"
49
+ echo ""
50
+ echo "WARNING: $BIN_DIR is not on your PATH."
51
+ echo "Add this to your shell profile (~/.bashrc, ~/.zshrc, etc.):"
52
+ echo ""
53
+ echo " export PATH=\"\$HOME/.local/bin:\$PATH\""
54
+ echo ""
55
+ fi
56
+
57
+ WRAPPER="$BIN_DIR/$WRAPPER_NAME"
58
+
59
+ cat > "$WRAPPER" << 'SCRIPT'
60
+ #!/usr/bin/env bash
61
+ exec docker run --rm \
62
+ --user "$(id -u):$(id -g)" \
63
+ -e HOME="$HOME" \
64
+ -v "$HOME:$HOME" \
65
+ -v "$(pwd):$(pwd)" -w "$(pwd)" \
66
+ gitj "$@"
67
+ SCRIPT
68
+
69
+ chmod +x "$WRAPPER"
70
+
71
+ echo "Installed $WRAPPER"
72
+ echo ""
73
+ echo "Usage:"
74
+ echo " gitj --version"
75
+ echo " gitj jira start BUG-42"
76
+ echo " gitj lab merge active"
77
+ echo " gitj --help-all"
package/lib/api.ts CHANGED
@@ -82,6 +82,18 @@ export async function apiRequest(
82
82
 
83
83
  const request = new Request(url, fetchOptions)
84
84
  const response = await fetch(request)
85
+
86
+ if (!response.ok) {
87
+ const text = await response.text()
88
+ let body: JSONValue
89
+ try {
90
+ body = JSON.parse(text) as JSONValue
91
+ } catch {
92
+ throw new Error(`${serviceName} API ${method} ${endpoint} failed (${response.status}): ${text}`)
93
+ }
94
+ return { status: response.status, headers: response.headers, body }
95
+ }
96
+
85
97
  const body = await response.json() as JSONValue
86
98
 
87
99
  return {
@@ -138,7 +150,13 @@ export async function apiPaginate(
138
150
  lastHeaders = response.headers
139
151
 
140
152
  if (!response.ok) {
141
- const body = await response.json() as JSONValue
153
+ const text = await response.text()
154
+ let body: JSONValue
155
+ try {
156
+ body = JSON.parse(text) as JSONValue
157
+ } catch {
158
+ throw new Error(`${serviceName} API paginate ${endpoint} failed (${response.status}): ${text}`)
159
+ }
142
160
  return { status: response.status, headers: response.headers, body }
143
161
  }
144
162
 
@@ -30,6 +30,10 @@ export async function confluenceApi(endpoint: string): Promise<JSONValue> {
30
30
  }
31
31
  let request = new Request(uri, options)
32
32
  const response = await fetch(request)
33
+ if (!response.ok) {
34
+ const text = await response.text()
35
+ throw new Error(`Confluence API ${endpoint} failed (${response.status}): ${text}`)
36
+ }
33
37
  let link = getNextLink(response.headers.get('Link'))
34
38
  const body = await response.json() as JSONValue & { results?: Array<JSONValue> }
35
39
  if (!body.results) {
@@ -94,6 +98,10 @@ export async function confluenceSearch(cql: string): Promise<JSONValue> {
94
98
  while (uri) {
95
99
  const request = new Request(uri, options)
96
100
  const response = await fetch(request)
101
+ if (!response.ok) {
102
+ const text = await response.text()
103
+ throw new Error(`Confluence search failed (${response.status}): ${text}`)
104
+ }
97
105
  const body = await response.json() as JSONValue & {
98
106
  results?: Array<JSONValue>
99
107
  _links?: { next?: string }
@@ -127,6 +135,10 @@ export async function confluenceApiV1(endpoint: string): Promise<JSONValue> {
127
135
  }
128
136
  const request = new Request(uri, options)
129
137
  const response = await fetch(request)
138
+ if (!response.ok) {
139
+ const text = await response.text()
140
+ throw new Error(`Confluence API v1 ${endpoint} failed (${response.status}): ${text}`)
141
+ }
130
142
  const result = await response.json()
131
143
  return result
132
144
  }
@@ -15,11 +15,11 @@ const confluenceTokenP = getConfig('confluence.token', { expectQuiet: true })
15
15
 
16
16
  export async function getConfluenceConfig(): Promise<ConfluenceConfig> {
17
17
  const host = await confluenceHostP || await jiraHostP
18
- if (!host) throw new Error('confluence.host or jira.host not in git config')
18
+ if (!host) throw new Error('confluence.host or jira.host not in git config (see: gitj confluence --help)')
19
19
  const user = await confluenceEmailP || await jiraEmailP || await gitEmailP
20
- if (!user) throw new Error('confluence.user, jira.user, or user.email not in git config')
20
+ if (!user) throw new Error('confluence.user, jira.user, or user.email not in git config (see: gitj confluence --help)')
21
21
  const pat = await confluenceTokenP || await jiraTokenP
22
- if (!pat) throw new Error('confluence.token or jira.token not in git config')
22
+ if (!pat) throw new Error('confluence.token or jira.token not in git config (see: gitj confluence --help)')
23
23
  const token = Buffer.from(`${user}:${pat}`).toString('base64')
24
24
  return { host, token }
25
25
  }
package/lib/gitlab/api.ts CHANGED
@@ -31,6 +31,10 @@ export async function gitlabApi(endpoint: string): Promise<JSONValue> {
31
31
  }
32
32
  let request = new Request(uri, options)
33
33
  const response = await fetch(request)
34
+ if (!response.ok) {
35
+ const text = await response.text()
36
+ throw new Error(`GitLab API ${endpoint} failed (${response.status}): ${text}`)
37
+ }
34
38
  let link = getNextLink(response.headers.get('Link'))
35
39
  let partial = (await response.json()) as Array<JSONValue>
36
40
  let result: Array<JSONValue> = partial
@@ -14,8 +14,8 @@ const tokenP = getConfig("gitlab.token")
14
14
  export async function getGitlabConfig(): Promise<GitlabConfig> {
15
15
  const host = await hostP || 'gitlab.com'
16
16
  const user = await gitEmailP || await gitlabEmailP
17
- if (!user) throw new Error("Neither user.email nor gitlab.email in git config")
17
+ if (!user) throw new Error("Neither user.email nor gitlab.user in git config (see: gitj lab --help)")
18
18
  const token = await tokenP
19
- if (!token) throw new Error("gitlab.token not in git config")
19
+ if (!token) throw new Error("gitlab.token not in git config (see: gitj lab --help)")
20
20
  return { host, user, token }
21
21
  }
package/lib/is_main.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import path from 'node:path'
2
+ import type { Command } from 'commander'
2
3
 
3
4
  function justBase(filename: string): string {
4
5
  const ext = path.extname(filename)
@@ -22,3 +23,13 @@ export function isMain(self: string): boolean {
22
23
  // }
23
24
  return result
24
25
  }
26
+
27
+ export async function runMain(self: string, create: () => Command): Promise<void> {
28
+ if (!isMain(self)) return
29
+ try {
30
+ await create().parseAsync(Bun.argv)
31
+ } catch (err) {
32
+ console.error(`error: ${err instanceof Error ? err.message : String(err)}`)
33
+ process.exit(1)
34
+ }
35
+ }
package/lib/jira.ts CHANGED
@@ -21,11 +21,11 @@ const tokenP = getConfig("jira.token")
21
21
 
22
22
  export async function getJiraConfig(): Promise<JiraConfig> {
23
23
  const host = await hostP
24
- if (!host) throw new Error("jira.host not in git config")
24
+ if (!host) throw new Error("jira.host not in git config (see: gitj jira --help)")
25
25
  const user = await jiraEmailP || await gitEmailP
26
- if (!user) throw new Error("jira.user or user.email not in git config")
26
+ if (!user) throw new Error("jira.user or user.email not in git config (see: gitj jira --help)")
27
27
  const pat = await tokenP
28
- if (!pat) throw new Error("jira.token not in git config")
28
+ if (!pat) throw new Error("jira.token not in git config (see: gitj jira --help)")
29
29
  const token = Buffer.from(`${user}:${pat}`).toString('base64')
30
30
  return { host, token }
31
31
  }
@@ -48,6 +48,10 @@ export async function jiraApi(endpoint: string): Promise<JSONValue> {
48
48
  }
49
49
  const request = new Request(uri, options)
50
50
  const response = await fetch(request)
51
+ if (!response.ok) {
52
+ const text = await response.text()
53
+ throw new Error(`Jira API ${endpoint} failed (${response.status}): ${text}`)
54
+ }
51
55
  const result = await response.json()
52
56
  return result;
53
57
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ya-git-jira",
3
3
  "description": "git extensions for Jira integration. Assumes bun is installed.",
4
- "version": "2.0.0",
4
+ "version": "2.1.1",
5
5
  "module": "dist/index.js",
6
6
  "type": "module",
7
7
  "bin": {