jirasify 2.0.0__tar.gz
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.
- jirasify-2.0.0/PKG-INFO +214 -0
- jirasify-2.0.0/README.md +191 -0
- jirasify-2.0.0/pyproject.toml +42 -0
- jirasify-2.0.0/setup.cfg +4 -0
- jirasify-2.0.0/src/jirasify.egg-info/PKG-INFO +214 -0
- jirasify-2.0.0/src/jirasify.egg-info/SOURCES.txt +18 -0
- jirasify-2.0.0/src/jirasify.egg-info/dependency_links.txt +1 -0
- jirasify-2.0.0/src/jirasify.egg-info/entry_points.txt +4 -0
- jirasify-2.0.0/src/jirasify.egg-info/requires.txt +3 -0
- jirasify-2.0.0/src/jirasify.egg-info/top_level.txt +1 -0
- jirasify-2.0.0/src/jiratui/__init__.py +1 -0
- jirasify-2.0.0/src/jiratui/__main__.py +5 -0
- jirasify-2.0.0/src/jiratui/app.py +1185 -0
- jirasify-2.0.0/src/jiratui/client.py +691 -0
- jirasify-2.0.0/src/jiratui/config.py +162 -0
- jirasify-2.0.0/src/jiratui/dailywork.py +76 -0
- jirasify-2.0.0/src/jiratui/jira_list.py +197 -0
- jirasify-2.0.0/src/jiratui/markdown2jira.py +506 -0
- jirasify-2.0.0/src/jiratui/screens.py +177 -0
- jirasify-2.0.0/src/jiratui/utils.py +133 -0
jirasify-2.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jirasify
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Terminal UI for Jira worklogs and status transitions
|
|
5
|
+
Author-email: Jakob Holst <code@jakobholst.dk>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://gitlab.com/jakobholst/jaho_local_bin
|
|
8
|
+
Project-URL: Repository, https://gitlab.com/jakobholst/jaho_local_bin
|
|
9
|
+
Keywords: jira,tui,worklog,textual,terminal
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: Software Development
|
|
17
|
+
Classifier: Topic :: Utilities
|
|
18
|
+
Requires-Python: >=3.14
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
Requires-Dist: requests>=2.28
|
|
21
|
+
Requires-Dist: textual>=0.50
|
|
22
|
+
Requires-Dist: pyyaml>=6.0
|
|
23
|
+
|
|
24
|
+
# jirasify
|
|
25
|
+
|
|
26
|
+
Terminal UI for logging work and moving Jira issues, built with [Textual](https://textual.textualize.io/).
|
|
27
|
+
|
|
28
|
+
The distribution name on PyPI is `jirasify`; the main CLI command is `jirasify-tui`; the importable Python module is `jiratui`.
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install jirasify
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Configure
|
|
37
|
+
|
|
38
|
+
On first run, `jirasify-tui` writes a template config to `~/.local/etc/jiratui/config.yaml` and prints a usage page. Edit the file (or answer the interactive prompt to fill it) with your Jira URL, project key, and username.
|
|
39
|
+
|
|
40
|
+
Your Jira token is read from the environment variable named by `authentication.token_env` (default: `JIRATOKEN`):
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
export JIRATOKEN=your_token
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
- **Self-hosted Jira (Data Center / Server)**: use a **Personal Access Token**. It is sent as `Authorization: Bearer <token>`.
|
|
47
|
+
- **Atlassian Cloud** (URL contains `atlassian.net`): use an **API token** from your Atlassian account. It is combined with `assignee.username` (your account email) via HTTP Basic auth.
|
|
48
|
+
|
|
49
|
+
Verify connectivity before launching the TUI:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
jirasify-tui --test
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Prints the configured URL and token env var, then hits `/rest/api/2/myself`. Exits with a non-zero status on missing config, unset token, or auth failure.
|
|
56
|
+
|
|
57
|
+
## Usage
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
jirasify-tui
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
| Key | Action |
|
|
64
|
+
| -------- | --------------------------------- |
|
|
65
|
+
| `l` | Log view |
|
|
66
|
+
| `o` | Overview |
|
|
67
|
+
| `r` | Monthly report |
|
|
68
|
+
| `t`/`w` | Today's / this week's report |
|
|
69
|
+
| `m` | This month's report |
|
|
70
|
+
| `s` | Change status of selected issue |
|
|
71
|
+
| `e` | Set estimate on selected issue |
|
|
72
|
+
| `h` | Help |
|
|
73
|
+
| `Ctrl+R` | Refresh |
|
|
74
|
+
| `q` | Quit |
|
|
75
|
+
|
|
76
|
+
Time input accepts `30m`, `1h`, `2h30m`, `1d` (a day = 8h).
|
|
77
|
+
|
|
78
|
+
## `jirasify`
|
|
79
|
+
|
|
80
|
+
`pip install jirasify` also installs a `jirasify` command that turns a
|
|
81
|
+
structured Markdown file into Jira user stories using the same
|
|
82
|
+
`~/.local/etc/jiratui/config.yaml` and `JIRATOKEN` env var as `jirasify-tui`.
|
|
83
|
+
|
|
84
|
+
Expected Markdown layout:
|
|
85
|
+
|
|
86
|
+
```markdown
|
|
87
|
+
# My Epic Name - User Stories
|
|
88
|
+
|
|
89
|
+
## 1. Project Overview
|
|
90
|
+
|
|
91
|
+
Free text.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
# Phase 1 - Foundations
|
|
96
|
+
|
|
97
|
+
## User Story 001 - Inventory Environment
|
|
98
|
+
|
|
99
|
+
**Estimate:** 15 h
|
|
100
|
+
|
|
101
|
+
> As a developer, I will document ... so that ...
|
|
102
|
+
|
|
103
|
+
### Scope
|
|
104
|
+
- Item A
|
|
105
|
+
- Item B
|
|
106
|
+
|
|
107
|
+
### Acceptance Criteria
|
|
108
|
+
- Criterion 1
|
|
109
|
+
- Criterion 2
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
- The top-level `# ...` heading (any H1 that is not a `# Phase ...`) is used
|
|
113
|
+
as the Epic name. A trailing ` - User Stories` suffix is stripped.
|
|
114
|
+
- Stories are only picked up under a `## User Story ...` heading inside a
|
|
115
|
+
`# Phase ...` section.
|
|
116
|
+
- Recognised subsections: `Scope`, `Validate`, `Include`, `Examples`,
|
|
117
|
+
`Acceptance Criteria`.
|
|
118
|
+
|
|
119
|
+
### Commands
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
jirasify --example # write ./example.md (plain markdown starter)
|
|
123
|
+
jirasify --template --file stories.md.j2 # write an Ansible/Jinja2 template
|
|
124
|
+
|
|
125
|
+
jirasify --file stories.md # parse + write .jirastories.state.json
|
|
126
|
+
jirasify --file stories.md --create # ensure Epic exists, create missing stories linked to it (idempotent)
|
|
127
|
+
jirasify --file stories.md --status # key/status/assignee/epic/estimate/used per story, grouped by phase with totals
|
|
128
|
+
jirasify --file stories.md --epic VOS-100 # relink stories to a specific epic
|
|
129
|
+
jirasify --file stories.md --epic VOS-100 --story VOS-142 # relink a single story
|
|
130
|
+
|
|
131
|
+
jirasify --reverse VOS-1234 # print a markdown reconstruction of an Epic and its stories
|
|
132
|
+
jirasify --reverse VOS-1234 --save # write it to <Epic_Name>.md (spaces -> _)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
`--create` skips stories that already have an `issue_key` recorded in
|
|
136
|
+
`.jirastories.state.json`, and also skips summaries that already exist in the
|
|
137
|
+
target project, so re-runs won't duplicate issues. If no Epic with the H1's
|
|
138
|
+
name exists, one is created before the stories.
|
|
139
|
+
|
|
140
|
+
### Ansible / Jinja2 template
|
|
141
|
+
|
|
142
|
+
`--template` emits a `.j2` scaffold expecting these variables:
|
|
143
|
+
|
|
144
|
+
- `epic_name` (string)
|
|
145
|
+
- `project_overview` (string, optional)
|
|
146
|
+
- `phases`: list of `{ name, stories: [ { id, summary, estimate, description, scope?, acceptance_criteria? } ] }`
|
|
147
|
+
|
|
148
|
+
Render it with `ansible.builtin.template` (or `jinja2.Template`) to produce
|
|
149
|
+
a `stories.md`, then run `jirasify --file stories.md --create`.
|
|
150
|
+
|
|
151
|
+
## `jirasify-list`
|
|
152
|
+
|
|
153
|
+
`pip install jirasify` also installs a `jirasify-list` command for read-only
|
|
154
|
+
listing of Jira objects, using the same config as `jirasify-tui`.
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
jirasify-list --epics # Epics in jira.project (config), excluding Cancelled and Done
|
|
158
|
+
jirasify-list --epics --key VOS # override the project key
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Status filters (mutually exclusive)
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
jirasify-list --epics --active # status = Implementing
|
|
165
|
+
jirasify-list --epics --planning # status = Planning
|
|
166
|
+
jirasify-list --epics --done # status = Done
|
|
167
|
+
jirasify-list --epics --cancelled # status = Cancelled
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Without any of these flags, Cancelled and Done epics are hidden by default.
|
|
171
|
+
|
|
172
|
+
### Parent Link filter
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
jirasify-list --epics --parent VOS-100 # exact parent (JQL: "Parent Link" = VOS-100)
|
|
176
|
+
jirasify-list --epics --parent VOS # any parent whose key starts with VOS-
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Full hierarchy
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
jirasify-list --epics --full # also show stories under each epic and subtasks under each story
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
`--full` issues one extra batched JQL for all epic children (`"Epic Link" in (…)`)
|
|
186
|
+
and one for all subtasks (`parent in (…)`), regardless of item count.
|
|
187
|
+
|
|
188
|
+
### Output
|
|
189
|
+
|
|
190
|
+
Epics are grouped by their Parent Link and printed as a tree with box-drawing
|
|
191
|
+
branches (`├──`, `└──`, `│`):
|
|
192
|
+
|
|
193
|
+
```
|
|
194
|
+
VOS-100 Digital Platform Modernization
|
|
195
|
+
├── VOS-215 Implementing Jane Doe Artifactory PyPI
|
|
196
|
+
│ ├── VOS-231 In Progress Bob Provision PyPI local repo
|
|
197
|
+
│ │ └── VOS-232 To Do Alice Configure retention
|
|
198
|
+
│ └── VOS-233 To Do Bob Assemble virtual PyPI
|
|
199
|
+
└── VOS-220 To Do Bob Sisyphos Onboarding
|
|
200
|
+
|
|
201
|
+
VOS-105 Infrastructure 2026
|
|
202
|
+
└── VOS-311 Implementing Alice VMware 9.1 Enablement
|
|
203
|
+
|
|
204
|
+
(no parent)
|
|
205
|
+
└── VOS-999 Planning Unassigned Ad-hoc Epic
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Columns per row: issue key, status, assignee (truncated to 32 chars with `…`),
|
|
209
|
+
and the label — Epic Name for epics (falls back to summary when the Epic Name
|
|
210
|
+
custom field is absent), summary for stories and subtasks. Without `--full`
|
|
211
|
+
only the epic level is shown.
|
|
212
|
+
|
|
213
|
+
Rows are colorized when stdout is a terminal: cyan parent header, magenta
|
|
214
|
+
epic, green story, blue subtask. Piping or redirecting produces plain output.
|
jirasify-2.0.0/README.md
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# jirasify
|
|
2
|
+
|
|
3
|
+
Terminal UI for logging work and moving Jira issues, built with [Textual](https://textual.textualize.io/).
|
|
4
|
+
|
|
5
|
+
The distribution name on PyPI is `jirasify`; the main CLI command is `jirasify-tui`; the importable Python module is `jiratui`.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install jirasify
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Configure
|
|
14
|
+
|
|
15
|
+
On first run, `jirasify-tui` writes a template config to `~/.local/etc/jiratui/config.yaml` and prints a usage page. Edit the file (or answer the interactive prompt to fill it) with your Jira URL, project key, and username.
|
|
16
|
+
|
|
17
|
+
Your Jira token is read from the environment variable named by `authentication.token_env` (default: `JIRATOKEN`):
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
export JIRATOKEN=your_token
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
- **Self-hosted Jira (Data Center / Server)**: use a **Personal Access Token**. It is sent as `Authorization: Bearer <token>`.
|
|
24
|
+
- **Atlassian Cloud** (URL contains `atlassian.net`): use an **API token** from your Atlassian account. It is combined with `assignee.username` (your account email) via HTTP Basic auth.
|
|
25
|
+
|
|
26
|
+
Verify connectivity before launching the TUI:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
jirasify-tui --test
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Prints the configured URL and token env var, then hits `/rest/api/2/myself`. Exits with a non-zero status on missing config, unset token, or auth failure.
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
jirasify-tui
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
| Key | Action |
|
|
41
|
+
| -------- | --------------------------------- |
|
|
42
|
+
| `l` | Log view |
|
|
43
|
+
| `o` | Overview |
|
|
44
|
+
| `r` | Monthly report |
|
|
45
|
+
| `t`/`w` | Today's / this week's report |
|
|
46
|
+
| `m` | This month's report |
|
|
47
|
+
| `s` | Change status of selected issue |
|
|
48
|
+
| `e` | Set estimate on selected issue |
|
|
49
|
+
| `h` | Help |
|
|
50
|
+
| `Ctrl+R` | Refresh |
|
|
51
|
+
| `q` | Quit |
|
|
52
|
+
|
|
53
|
+
Time input accepts `30m`, `1h`, `2h30m`, `1d` (a day = 8h).
|
|
54
|
+
|
|
55
|
+
## `jirasify`
|
|
56
|
+
|
|
57
|
+
`pip install jirasify` also installs a `jirasify` command that turns a
|
|
58
|
+
structured Markdown file into Jira user stories using the same
|
|
59
|
+
`~/.local/etc/jiratui/config.yaml` and `JIRATOKEN` env var as `jirasify-tui`.
|
|
60
|
+
|
|
61
|
+
Expected Markdown layout:
|
|
62
|
+
|
|
63
|
+
```markdown
|
|
64
|
+
# My Epic Name - User Stories
|
|
65
|
+
|
|
66
|
+
## 1. Project Overview
|
|
67
|
+
|
|
68
|
+
Free text.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
# Phase 1 - Foundations
|
|
73
|
+
|
|
74
|
+
## User Story 001 - Inventory Environment
|
|
75
|
+
|
|
76
|
+
**Estimate:** 15 h
|
|
77
|
+
|
|
78
|
+
> As a developer, I will document ... so that ...
|
|
79
|
+
|
|
80
|
+
### Scope
|
|
81
|
+
- Item A
|
|
82
|
+
- Item B
|
|
83
|
+
|
|
84
|
+
### Acceptance Criteria
|
|
85
|
+
- Criterion 1
|
|
86
|
+
- Criterion 2
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
- The top-level `# ...` heading (any H1 that is not a `# Phase ...`) is used
|
|
90
|
+
as the Epic name. A trailing ` - User Stories` suffix is stripped.
|
|
91
|
+
- Stories are only picked up under a `## User Story ...` heading inside a
|
|
92
|
+
`# Phase ...` section.
|
|
93
|
+
- Recognised subsections: `Scope`, `Validate`, `Include`, `Examples`,
|
|
94
|
+
`Acceptance Criteria`.
|
|
95
|
+
|
|
96
|
+
### Commands
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
jirasify --example # write ./example.md (plain markdown starter)
|
|
100
|
+
jirasify --template --file stories.md.j2 # write an Ansible/Jinja2 template
|
|
101
|
+
|
|
102
|
+
jirasify --file stories.md # parse + write .jirastories.state.json
|
|
103
|
+
jirasify --file stories.md --create # ensure Epic exists, create missing stories linked to it (idempotent)
|
|
104
|
+
jirasify --file stories.md --status # key/status/assignee/epic/estimate/used per story, grouped by phase with totals
|
|
105
|
+
jirasify --file stories.md --epic VOS-100 # relink stories to a specific epic
|
|
106
|
+
jirasify --file stories.md --epic VOS-100 --story VOS-142 # relink a single story
|
|
107
|
+
|
|
108
|
+
jirasify --reverse VOS-1234 # print a markdown reconstruction of an Epic and its stories
|
|
109
|
+
jirasify --reverse VOS-1234 --save # write it to <Epic_Name>.md (spaces -> _)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`--create` skips stories that already have an `issue_key` recorded in
|
|
113
|
+
`.jirastories.state.json`, and also skips summaries that already exist in the
|
|
114
|
+
target project, so re-runs won't duplicate issues. If no Epic with the H1's
|
|
115
|
+
name exists, one is created before the stories.
|
|
116
|
+
|
|
117
|
+
### Ansible / Jinja2 template
|
|
118
|
+
|
|
119
|
+
`--template` emits a `.j2` scaffold expecting these variables:
|
|
120
|
+
|
|
121
|
+
- `epic_name` (string)
|
|
122
|
+
- `project_overview` (string, optional)
|
|
123
|
+
- `phases`: list of `{ name, stories: [ { id, summary, estimate, description, scope?, acceptance_criteria? } ] }`
|
|
124
|
+
|
|
125
|
+
Render it with `ansible.builtin.template` (or `jinja2.Template`) to produce
|
|
126
|
+
a `stories.md`, then run `jirasify --file stories.md --create`.
|
|
127
|
+
|
|
128
|
+
## `jirasify-list`
|
|
129
|
+
|
|
130
|
+
`pip install jirasify` also installs a `jirasify-list` command for read-only
|
|
131
|
+
listing of Jira objects, using the same config as `jirasify-tui`.
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
jirasify-list --epics # Epics in jira.project (config), excluding Cancelled and Done
|
|
135
|
+
jirasify-list --epics --key VOS # override the project key
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Status filters (mutually exclusive)
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
jirasify-list --epics --active # status = Implementing
|
|
142
|
+
jirasify-list --epics --planning # status = Planning
|
|
143
|
+
jirasify-list --epics --done # status = Done
|
|
144
|
+
jirasify-list --epics --cancelled # status = Cancelled
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Without any of these flags, Cancelled and Done epics are hidden by default.
|
|
148
|
+
|
|
149
|
+
### Parent Link filter
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
jirasify-list --epics --parent VOS-100 # exact parent (JQL: "Parent Link" = VOS-100)
|
|
153
|
+
jirasify-list --epics --parent VOS # any parent whose key starts with VOS-
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Full hierarchy
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
jirasify-list --epics --full # also show stories under each epic and subtasks under each story
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
`--full` issues one extra batched JQL for all epic children (`"Epic Link" in (…)`)
|
|
163
|
+
and one for all subtasks (`parent in (…)`), regardless of item count.
|
|
164
|
+
|
|
165
|
+
### Output
|
|
166
|
+
|
|
167
|
+
Epics are grouped by their Parent Link and printed as a tree with box-drawing
|
|
168
|
+
branches (`├──`, `└──`, `│`):
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
VOS-100 Digital Platform Modernization
|
|
172
|
+
├── VOS-215 Implementing Jane Doe Artifactory PyPI
|
|
173
|
+
│ ├── VOS-231 In Progress Bob Provision PyPI local repo
|
|
174
|
+
│ │ └── VOS-232 To Do Alice Configure retention
|
|
175
|
+
│ └── VOS-233 To Do Bob Assemble virtual PyPI
|
|
176
|
+
└── VOS-220 To Do Bob Sisyphos Onboarding
|
|
177
|
+
|
|
178
|
+
VOS-105 Infrastructure 2026
|
|
179
|
+
└── VOS-311 Implementing Alice VMware 9.1 Enablement
|
|
180
|
+
|
|
181
|
+
(no parent)
|
|
182
|
+
└── VOS-999 Planning Unassigned Ad-hoc Epic
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Columns per row: issue key, status, assignee (truncated to 32 chars with `…`),
|
|
186
|
+
and the label — Epic Name for epics (falls back to summary when the Epic Name
|
|
187
|
+
custom field is absent), summary for stories and subtasks. Without `--full`
|
|
188
|
+
only the epic level is shown.
|
|
189
|
+
|
|
190
|
+
Rows are colorized when stdout is a terminal: cyan parent header, magenta
|
|
191
|
+
epic, green story, blue subtask. Piping or redirecting produces plain output.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "jirasify"
|
|
7
|
+
version = "2.0.0"
|
|
8
|
+
description = "Terminal UI for Jira worklogs and status transitions"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.14"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Jakob Holst", email = "code@jakobholst.dk" },
|
|
14
|
+
]
|
|
15
|
+
keywords = ["jira", "tui", "worklog", "textual", "terminal"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Environment :: Console",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"License :: OSI Approved :: MIT License",
|
|
21
|
+
"Operating System :: OS Independent",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Topic :: Software Development",
|
|
24
|
+
"Topic :: Utilities",
|
|
25
|
+
]
|
|
26
|
+
dependencies = [
|
|
27
|
+
"requests>=2.28",
|
|
28
|
+
"textual>=0.50",
|
|
29
|
+
"pyyaml>=6.0",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.scripts]
|
|
33
|
+
jirasify-tui = "jiratui.app:main"
|
|
34
|
+
jirasify = "jiratui.markdown2jira:main"
|
|
35
|
+
jirasify-list = "jiratui.jira_list:main"
|
|
36
|
+
|
|
37
|
+
[project.urls]
|
|
38
|
+
Homepage = "https://gitlab.com/jakobholst/jaho_local_bin"
|
|
39
|
+
Repository = "https://gitlab.com/jakobholst/jaho_local_bin"
|
|
40
|
+
|
|
41
|
+
[tool.setuptools.packages.find]
|
|
42
|
+
where = ["src"]
|
jirasify-2.0.0/setup.cfg
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jirasify
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Terminal UI for Jira worklogs and status transitions
|
|
5
|
+
Author-email: Jakob Holst <code@jakobholst.dk>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://gitlab.com/jakobholst/jaho_local_bin
|
|
8
|
+
Project-URL: Repository, https://gitlab.com/jakobholst/jaho_local_bin
|
|
9
|
+
Keywords: jira,tui,worklog,textual,terminal
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: Software Development
|
|
17
|
+
Classifier: Topic :: Utilities
|
|
18
|
+
Requires-Python: >=3.14
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
Requires-Dist: requests>=2.28
|
|
21
|
+
Requires-Dist: textual>=0.50
|
|
22
|
+
Requires-Dist: pyyaml>=6.0
|
|
23
|
+
|
|
24
|
+
# jirasify
|
|
25
|
+
|
|
26
|
+
Terminal UI for logging work and moving Jira issues, built with [Textual](https://textual.textualize.io/).
|
|
27
|
+
|
|
28
|
+
The distribution name on PyPI is `jirasify`; the main CLI command is `jirasify-tui`; the importable Python module is `jiratui`.
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install jirasify
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Configure
|
|
37
|
+
|
|
38
|
+
On first run, `jirasify-tui` writes a template config to `~/.local/etc/jiratui/config.yaml` and prints a usage page. Edit the file (or answer the interactive prompt to fill it) with your Jira URL, project key, and username.
|
|
39
|
+
|
|
40
|
+
Your Jira token is read from the environment variable named by `authentication.token_env` (default: `JIRATOKEN`):
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
export JIRATOKEN=your_token
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
- **Self-hosted Jira (Data Center / Server)**: use a **Personal Access Token**. It is sent as `Authorization: Bearer <token>`.
|
|
47
|
+
- **Atlassian Cloud** (URL contains `atlassian.net`): use an **API token** from your Atlassian account. It is combined with `assignee.username` (your account email) via HTTP Basic auth.
|
|
48
|
+
|
|
49
|
+
Verify connectivity before launching the TUI:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
jirasify-tui --test
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Prints the configured URL and token env var, then hits `/rest/api/2/myself`. Exits with a non-zero status on missing config, unset token, or auth failure.
|
|
56
|
+
|
|
57
|
+
## Usage
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
jirasify-tui
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
| Key | Action |
|
|
64
|
+
| -------- | --------------------------------- |
|
|
65
|
+
| `l` | Log view |
|
|
66
|
+
| `o` | Overview |
|
|
67
|
+
| `r` | Monthly report |
|
|
68
|
+
| `t`/`w` | Today's / this week's report |
|
|
69
|
+
| `m` | This month's report |
|
|
70
|
+
| `s` | Change status of selected issue |
|
|
71
|
+
| `e` | Set estimate on selected issue |
|
|
72
|
+
| `h` | Help |
|
|
73
|
+
| `Ctrl+R` | Refresh |
|
|
74
|
+
| `q` | Quit |
|
|
75
|
+
|
|
76
|
+
Time input accepts `30m`, `1h`, `2h30m`, `1d` (a day = 8h).
|
|
77
|
+
|
|
78
|
+
## `jirasify`
|
|
79
|
+
|
|
80
|
+
`pip install jirasify` also installs a `jirasify` command that turns a
|
|
81
|
+
structured Markdown file into Jira user stories using the same
|
|
82
|
+
`~/.local/etc/jiratui/config.yaml` and `JIRATOKEN` env var as `jirasify-tui`.
|
|
83
|
+
|
|
84
|
+
Expected Markdown layout:
|
|
85
|
+
|
|
86
|
+
```markdown
|
|
87
|
+
# My Epic Name - User Stories
|
|
88
|
+
|
|
89
|
+
## 1. Project Overview
|
|
90
|
+
|
|
91
|
+
Free text.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
# Phase 1 - Foundations
|
|
96
|
+
|
|
97
|
+
## User Story 001 - Inventory Environment
|
|
98
|
+
|
|
99
|
+
**Estimate:** 15 h
|
|
100
|
+
|
|
101
|
+
> As a developer, I will document ... so that ...
|
|
102
|
+
|
|
103
|
+
### Scope
|
|
104
|
+
- Item A
|
|
105
|
+
- Item B
|
|
106
|
+
|
|
107
|
+
### Acceptance Criteria
|
|
108
|
+
- Criterion 1
|
|
109
|
+
- Criterion 2
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
- The top-level `# ...` heading (any H1 that is not a `# Phase ...`) is used
|
|
113
|
+
as the Epic name. A trailing ` - User Stories` suffix is stripped.
|
|
114
|
+
- Stories are only picked up under a `## User Story ...` heading inside a
|
|
115
|
+
`# Phase ...` section.
|
|
116
|
+
- Recognised subsections: `Scope`, `Validate`, `Include`, `Examples`,
|
|
117
|
+
`Acceptance Criteria`.
|
|
118
|
+
|
|
119
|
+
### Commands
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
jirasify --example # write ./example.md (plain markdown starter)
|
|
123
|
+
jirasify --template --file stories.md.j2 # write an Ansible/Jinja2 template
|
|
124
|
+
|
|
125
|
+
jirasify --file stories.md # parse + write .jirastories.state.json
|
|
126
|
+
jirasify --file stories.md --create # ensure Epic exists, create missing stories linked to it (idempotent)
|
|
127
|
+
jirasify --file stories.md --status # key/status/assignee/epic/estimate/used per story, grouped by phase with totals
|
|
128
|
+
jirasify --file stories.md --epic VOS-100 # relink stories to a specific epic
|
|
129
|
+
jirasify --file stories.md --epic VOS-100 --story VOS-142 # relink a single story
|
|
130
|
+
|
|
131
|
+
jirasify --reverse VOS-1234 # print a markdown reconstruction of an Epic and its stories
|
|
132
|
+
jirasify --reverse VOS-1234 --save # write it to <Epic_Name>.md (spaces -> _)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
`--create` skips stories that already have an `issue_key` recorded in
|
|
136
|
+
`.jirastories.state.json`, and also skips summaries that already exist in the
|
|
137
|
+
target project, so re-runs won't duplicate issues. If no Epic with the H1's
|
|
138
|
+
name exists, one is created before the stories.
|
|
139
|
+
|
|
140
|
+
### Ansible / Jinja2 template
|
|
141
|
+
|
|
142
|
+
`--template` emits a `.j2` scaffold expecting these variables:
|
|
143
|
+
|
|
144
|
+
- `epic_name` (string)
|
|
145
|
+
- `project_overview` (string, optional)
|
|
146
|
+
- `phases`: list of `{ name, stories: [ { id, summary, estimate, description, scope?, acceptance_criteria? } ] }`
|
|
147
|
+
|
|
148
|
+
Render it with `ansible.builtin.template` (or `jinja2.Template`) to produce
|
|
149
|
+
a `stories.md`, then run `jirasify --file stories.md --create`.
|
|
150
|
+
|
|
151
|
+
## `jirasify-list`
|
|
152
|
+
|
|
153
|
+
`pip install jirasify` also installs a `jirasify-list` command for read-only
|
|
154
|
+
listing of Jira objects, using the same config as `jirasify-tui`.
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
jirasify-list --epics # Epics in jira.project (config), excluding Cancelled and Done
|
|
158
|
+
jirasify-list --epics --key VOS # override the project key
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Status filters (mutually exclusive)
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
jirasify-list --epics --active # status = Implementing
|
|
165
|
+
jirasify-list --epics --planning # status = Planning
|
|
166
|
+
jirasify-list --epics --done # status = Done
|
|
167
|
+
jirasify-list --epics --cancelled # status = Cancelled
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Without any of these flags, Cancelled and Done epics are hidden by default.
|
|
171
|
+
|
|
172
|
+
### Parent Link filter
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
jirasify-list --epics --parent VOS-100 # exact parent (JQL: "Parent Link" = VOS-100)
|
|
176
|
+
jirasify-list --epics --parent VOS # any parent whose key starts with VOS-
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Full hierarchy
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
jirasify-list --epics --full # also show stories under each epic and subtasks under each story
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
`--full` issues one extra batched JQL for all epic children (`"Epic Link" in (…)`)
|
|
186
|
+
and one for all subtasks (`parent in (…)`), regardless of item count.
|
|
187
|
+
|
|
188
|
+
### Output
|
|
189
|
+
|
|
190
|
+
Epics are grouped by their Parent Link and printed as a tree with box-drawing
|
|
191
|
+
branches (`├──`, `└──`, `│`):
|
|
192
|
+
|
|
193
|
+
```
|
|
194
|
+
VOS-100 Digital Platform Modernization
|
|
195
|
+
├── VOS-215 Implementing Jane Doe Artifactory PyPI
|
|
196
|
+
│ ├── VOS-231 In Progress Bob Provision PyPI local repo
|
|
197
|
+
│ │ └── VOS-232 To Do Alice Configure retention
|
|
198
|
+
│ └── VOS-233 To Do Bob Assemble virtual PyPI
|
|
199
|
+
└── VOS-220 To Do Bob Sisyphos Onboarding
|
|
200
|
+
|
|
201
|
+
VOS-105 Infrastructure 2026
|
|
202
|
+
└── VOS-311 Implementing Alice VMware 9.1 Enablement
|
|
203
|
+
|
|
204
|
+
(no parent)
|
|
205
|
+
└── VOS-999 Planning Unassigned Ad-hoc Epic
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Columns per row: issue key, status, assignee (truncated to 32 chars with `…`),
|
|
209
|
+
and the label — Epic Name for epics (falls back to summary when the Epic Name
|
|
210
|
+
custom field is absent), summary for stories and subtasks. Without `--full`
|
|
211
|
+
only the epic level is shown.
|
|
212
|
+
|
|
213
|
+
Rows are colorized when stdout is a terminal: cyan parent header, magenta
|
|
214
|
+
epic, green story, blue subtask. Piping or redirecting produces plain output.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/jirasify.egg-info/PKG-INFO
|
|
4
|
+
src/jirasify.egg-info/SOURCES.txt
|
|
5
|
+
src/jirasify.egg-info/dependency_links.txt
|
|
6
|
+
src/jirasify.egg-info/entry_points.txt
|
|
7
|
+
src/jirasify.egg-info/requires.txt
|
|
8
|
+
src/jirasify.egg-info/top_level.txt
|
|
9
|
+
src/jiratui/__init__.py
|
|
10
|
+
src/jiratui/__main__.py
|
|
11
|
+
src/jiratui/app.py
|
|
12
|
+
src/jiratui/client.py
|
|
13
|
+
src/jiratui/config.py
|
|
14
|
+
src/jiratui/dailywork.py
|
|
15
|
+
src/jiratui/jira_list.py
|
|
16
|
+
src/jiratui/markdown2jira.py
|
|
17
|
+
src/jiratui/screens.py
|
|
18
|
+
src/jiratui/utils.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|