gojira

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 6, 2026 License: MIT Imports: 14 Imported by: 0

README

gojira

A Go CLI and library for working with Jira Cloud from the command line. The v0.1.0 release ships gojira crawl, which recursively mirrors a Jira issue graph into Markdown — including the issue's hierarchy, development metadata (branches, commits, pull requests, builds, repositories), and human-labelled custom fields. Future releases will add subcommands for targeted fetching, ticket creation and update, and other workflows that today require the Jira UI.

Pre-1.0 status. APIs may change between minor versions until v1.0 is tagged.

Install

Go install
go install github.com/neumachen/gojira/cmd/gojira@v0.1.0
Docker
# Pull (when published to a registry) or build locally:
docker build -t gojira:v0.1.0 .

A docker-compose.yml is provided for convenience; see "Docker Compose" below.

Quick start

# 1. Generate a Jira Cloud API token at:
#      https://id.atlassian.com/manage-profile/security/api-tokens

# 2. Configure credentials. The CLI reads env vars; either export
#    them or use a .env file.
cp .env.example .env
$EDITOR .env   # fill in GOJIRA_SITE, GOJIRA_USER, GOJIRA_TOKEN

# 3. Crawl a single Jira issue and its reachable graph.
gojira crawl PROJ-123

# 4. The Markdown output appears under ./out by default.
ls out/PROJ-123/
Docker Compose
# After ./out is created by your first run, this works as a
# convenient wrapper:
docker compose run --rm gojira crawl PROJ-123

What gojira crawl does

Starting from one Jira issue key, the crawler:

  1. Fetches the issue via the Jira Cloud REST API v3 (GET /rest/api/3/issue/{key}?expand=names).
  2. Parses the issue's description from Atlassian Document Format into Markdown.
  3. Renders an index.md for the issue under <output-dir>/<KEY>/index.md with metadata, description, relationships, the ## Development panel (pull requests, branches, commits, repositories, builds), and human-labelled custom fields.
  4. Recursively follows links: subtasks, parents, issue links, hierarchy children (via JQL parent = "KEY" plus Epic Link), and Jira-flavoured links inside the description. Each discovered key is fetched and rendered the same way.
  5. Recognizes GitHub pull request URLs as PR references even when no Jira key is present in the PR title or branch.
  6. Writes a per-issue references/outbound.md summarising every outbound reference the issue produced.
  7. Honours configurable caps on depth, issue count, time, and API concurrency.

CLI flags

The crawl subcommand accepts these flags. Each maps to an env var of the same name in uppercase with a GOJIRA_ prefix; the flag overrides the env var when both are set.

Flag Env var Default What it does
--site GOJIRA_SITE (required) Jira Cloud site URL, e.g. https://your-site.atlassian.net.
--user GOJIRA_USER (required) Atlassian account email.
--token GOJIRA_TOKEN (required) Atlassian API token.
--output-dir GOJIRA_OUTPUT_DIR ./out Output root directory.
--depth-limit GOJIRA_DEPTH_LIMIT 0 (no cap) Max crawl depth from the start issue.
--issue-cap GOJIRA_ISSUE_CAP 500 Max issues to fetch per run.
--time-cap GOJIRA_TIME_CAP_SECONDS 0 (no cap) Max wall-clock seconds per run.
--concurrency GOJIRA_CONCURRENCY 3 Concurrent Jira API requests.
--refetch GOJIRA_REFETCH false Re-fetch issues that already exist on disk.
--include-comments GOJIRA_INCLUDE_COMMENTS false Fetch issue comments (v0.1.0 ignores them; reserved for v0.2).
--include-children GOJIRA_INCLUDE_CHILDREN true Discover hierarchy children via JQL parent search.
--child-search-limit GOJIRA_CHILD_SEARCH_LIMIT 100 Max children to discover per parent.
--epic-link-field GOJIRA_EPIC_LINK_FIELD (auto-detect) Tenant's Epic Link custom-field ID.
--include-dev-status GOJIRA_INCLUDE_DEV_STATUS true Query the Jira Dev Status API for development metadata.
--dev-status-applications GOJIRA_DEV_STATUS_APPLICATIONS GitHub Comma-separated Dev Status integration types.
--dev-status-data-types GOJIRA_DEV_STATUS_DATA_TYPES pullrequest,branch,commit,repository,build Comma-separated dataType values to query.
--render-null-custom-fields GOJIRA_RENDER_NULL_CUSTOM_FIELDS false Include custom fields whose value is JSON null.
--log-level GOJIRA_LOG_LEVEL info One of: error, warn, info, debug.
--log-format GOJIRA_LOG_FORMAT text One of: text (human-readable), json (one JSON object per line).

Output layout

out/
└── PROJ-123/
    ├── index.md
    └── references/
        └── outbound.md

Each fetched issue lives at <KEY>/index.md. Outbound references discovered in that issue are summarised at <KEY>/references/outbound.md. The references/ directory keeps the per-issue reference index out of the issue's own rendered Markdown so a reader who wants the full graph view can find it, but a reader who just wants the issue content sees only index.md.

Library usage

The same engine is available as a Go library. Third-party programs can embed gojira in their pipelines without invoking the CLI binary:

package main

import (
    "context"
    "fmt"
    "os"

    "github.com/neumachen/gojira"
)

func main() {
    cfg, err := gojira.LoadConfig(map[string]string{
        "GOJIRA_SITE":       os.Getenv("GOJIRA_SITE"),
        "GOJIRA_USER":       os.Getenv("GOJIRA_USER"),
        "GOJIRA_TOKEN":      os.Getenv("GOJIRA_TOKEN"),
        "GOJIRA_OUTPUT_DIR": "./out",
    })
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    summary, err := gojira.Crawl(
        context.Background(), cfg, []string{"PROJ-123"}, nil,
    )
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
    fmt.Printf("fetched %d issues; %d PRs discovered\n",
        summary.Fetched, summary.PRsFound)
}

The four named library capabilities (Classify, LoadConfig, FetchAndRender, Crawl) plus the type aliases for Config, Summary, Sink, and Event are documented in gojira.go's package doc.

Known limitations (v0.1.0)

  • Jira Cloud only. Jira Server / Data Center is out of scope for the entire product.
  • Comments are not yet rendered (the field is fetched but the current renderer ignores it; landing in v0.2).
  • The Jira Dev Status API used for development metadata is semi-undocumented. It has been stable for ~10 years because Atlassian's UI uses it, but no SLA is offered. Disable with --include-dev-status=false to opt out.
  • The CLI ships only one subcommand: crawl. See the roadmap.

Roadmap

Future releases anticipated:

  • gojira fetch — targeted single-issue (or small-list) retrieval without recursive expansion. The same renderer; just no JQL parent search and no descent into linked issues.
  • Write operations: create issues, update fields, post comments.
  • JQL search: list issues matching a query and crawl the result set.
  • Improved customisation: per-field rendering options, per-tenant field-name overrides.

No timelines committed. Direction only.

Project documentation

License

MIT

Documentation

Overview

Package gojira is the public library facade for the gojira Jira-to-Markdown mirror tool. It exposes exactly four named capabilities committed in PRD §8:

  1. Classify — classify a URL or bare issue key.
  2. LoadConfig / Config — build and validate a runtime configuration.
  3. FetchAndRender — fetch one Jira issue and return rendered Markdown.
  4. Crawl / Summary — run a full recursive crawl to disk.

Public surface invariants

  • No flag parsing, CLI argument handling, or process-level signal handling.
  • No os.Exit calls.
  • No hard-coded credentials, Jira domains, or project keys.
  • All internal packages are hidden; only the four capabilities and their supporting types are exported.
  • The CLI binary (cmd/gojira) is a thin consumer of this package; it is never required to use the library.

Minimal usage example (third-party consumer workflow from PRD §8)

cfg, err := gojira.LoadConfig(map[string]string{
    "GOJIRA_SITE":       "https://mycompany.atlassian.net",
    "GOJIRA_USER":       "me@example.com",
    "GOJIRA_TOKEN":      os.Getenv("JIRA_TOKEN"),
    "GOJIRA_OUTPUT_DIR": "./jira-mirror",
})
if err != nil {
    log.Fatal(err)
}

summary, err := gojira.Crawl(ctx, cfg, []string{"PROJ-1"}, nil)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("fetched=%d stubbed=%d failed=%d\n",
    summary.Fetched, summary.Stubbed, summary.Failed)

Index

Constants

View Source
const (
	KindIssueFetched     = events.KindIssueFetched
	KindIssueStubbed     = events.KindIssueStubbed
	KindIssueFailed      = events.KindIssueFailed
	KindIssueCapReached  = events.KindIssueCapReached
	KindPRReferenceFound = events.KindPRReferenceFound
	KindCrawlSummary     = events.KindCrawlSummary
)
View Source
const Version = "v0.1.0"

Version is the current library version. The authoritative release tag is set in Phase 7; this constant is declared early so callers can read it.

Variables

View Source
var (
	// ErrUnauthorized is returned when Jira responds with 401.
	// This is a total-failure condition: credentials are invalid.
	ErrUnauthorized = client.ErrUnauthorized

	// ErrForbidden is returned when Jira responds with 403.
	// The crawl renders a permission-denied stub and continues.
	ErrForbidden = client.ErrForbidden

	// ErrNotFound is returned when Jira responds with 404.
	// The crawl renders a not-found stub and continues.
	ErrNotFound = client.ErrNotFound

	// ErrRateLimited is returned when Jira responds with 429 and all
	// retry attempts are exhausted.
	ErrRateLimited = client.ErrRateLimited
)

Functions

func Classify

func Classify(input, jiraSite string) classify.Result

Classify determines the kind of link represented by input.

It is a direct re-export of classify.Classify. The returned classify.Result carries the Kind and any extracted fields (IssueKey, Owner, Repo, PRNumber, URL). classify is a public package; callers may import it directly for the Kind constants.

jiraSite is the Jira Cloud base URL (e.g. "https://mycompany.atlassian.net"). Only the host portion is used for matching.

func FetchAndRender

func FetchAndRender(ctx context.Context, cfg Config, key string, opts ...client.Option) (indexMD, outboundMD string, discoveredKeys []string, err error)

FetchAndRender fetches a single Jira issue identified by key, parses its ADF description and relationships, and returns the rendered Markdown content for both the issue page and its outbound reference index.

It does NOT write anything to disk. The caller decides what to do with the returned strings (write them, embed them in a larger pipeline, etc.).

Parameters

  • ctx: controls the lifetime of the HTTP request.
  • cfg: validated runtime configuration (construct via LoadConfig).
  • key: Jira issue key, e.g. "PROJ-1".
  • opts: optional client.Option values (e.g. client.WithHTTPClient for tests). Pass nil or omit for production use.

Return values

  • indexMD: Markdown content for <KEY>/index.md.
  • outboundMD: Markdown content for <KEY>/references/outbound.md. Empty string when the issue has no outbound references.
  • discoveredKeys: Jira issue keys found in the issue's description and relationships, in extract's documented order (description → parent → subtasks → issuelinks → remotelinks). May contain duplicates; the caller is responsible for deduplication.
  • err: non-nil on fetch, parse, extract, or render failure.

Neighbour resolution

Because FetchAndRender fetches a single issue in isolation, the neighbours set passed to the renderer is always empty. Relationship links therefore render as absolute Jira browse URLs rather than relative Markdown paths. Use Crawl for a full recursive crawl where relative links are resolved.

Types

type Config

type Config = config.Config

Config is the validated runtime configuration for a gojira run. It is an alias for the internal config type; construct it via LoadConfig.

func LoadConfig

func LoadConfig(kv map[string]string) (Config, error)

LoadConfig validates the key-value pairs in kv against the canonical GOJIRA_* key set defined in PRD §6, applies defaults for optional keys, and returns a populated Config.

kv may come from any source: environment variables, CLI flags, a config file, or a test fixture. This package does not read environment variables itself.

On the first validation failure, LoadConfig returns a zero Config and a descriptive error. Use errors.Is with config.ErrMissingRequired or config.ErrInvalidValue to distinguish failure classes.

type Event

type Event = events.Event

Event is a single observable occurrence emitted by the library. It is an alias for the internal events.Event type.

type Sink

type Sink = events.Sink

Sink is the interface callers implement to receive structured events from the library. It is an alias for the internal events.Sink interface, so callers can implement their own sink without importing internal/events.

var NoopSink Sink = events.NoopSink{}

NoopSink discards every event. Pass it (or nil) to Crawl when you do not need to observe crawl progress.

func NewSlogSink

func NewSlogSink(logger *slog.Logger) Sink

NewSlogSink returns a Sink that emits each event as a structured slog record through logger. It is the recommended way for callers (including the gojira CLI) to bridge gojira's event stream onto the standard log/slog pipeline without importing internal/events directly.

A nil logger is replaced with slog.Default so the resulting sink is always safe to call. The Event-to-slog mapping (kind → level, attribute names) is documented on the underlying events.SlogSink.

type Summary

type Summary = crawl.Summary

Summary is the structured result returned by Crawl after a run completes. It is an alias for the internal crawl summary type.

func Crawl

func Crawl(ctx context.Context, cfg Config, startKeys []string, sink Sink) (Summary, error)

Crawl executes a full recursive Jira issue crawl starting from startKeys.

It constructs the real HTTP client and fetcher from cfg, then delegates to the internal crawl orchestrator. Output files are written to cfg.OutputDir.

sink receives structured events for every state transition (issue queued, fetched, skipped, stubbed, failed, cap reached, PR found, crawl summary). Pass nil to use NoopSink and discard all events.

Error handling

  • 401 Unauthorized: the crawl is aborted immediately. Crawl returns a partial Summary and an error wrapping ErrUnauthorized. Map to exit 1.
  • 403 / 404: a stub index.md is written; the crawl continues.
  • Rate limited (429, retries exhausted): counted in Summary.Failed.
  • Network/transport error: a stub is written; the crawl continues.
  • Context cancellation: in-flight fetches complete; no new fetches start.

Skip-if-exists

When cfg.Refetch is false (the default), issues whose index.md already exists on disk are skipped without making an API call. This makes repeated runs additive and fast.

Directories

Path Synopsis
Package classify provides pure, stateless classification of strings into one of four link kinds: a bare Jira issue key, a Jira issue URL, a GitHub pull request URL, or an unclassified external link.
Package classify provides pure, stateless classification of strings into one of four link kinds: a bare Jira issue key, a Jira issue URL, a GitHub pull request URL, or an unclassified external link.
Package client implements the Jira Cloud HTTP transport for gojira.
Package client implements the Jira Cloud HTTP transport for gojira.
cmd
gojira command
Command gojira is the CLI binary for the gojira Jira-to-Markdown mirror tool.
Command gojira is the CLI binary for the gojira Jira-to-Markdown mirror tool.
internal
adf
Package adf provides pure, stateless traversal and rendering of Atlassian Document Format (ADF) documents.
Package adf provides pure, stateless traversal and rendering of Atlassian Document Format (ADF) documents.
config
Package config validates and constructs the runtime configuration consumed by every other gojira package.
Package config validates and constructs the runtime configuration consumed by every other gojira package.
crawl
Package crawl is the recursive crawl orchestrator for gojira.
Package crawl is the recursive crawl orchestrator for gojira.
devstatus
Package devstatus enriches an already-fetched Jira issue with all the development metadata Jira's Dev Status API surfaces: pull requests, branches, commits, repositories, and builds.
Package devstatus enriches an already-fetched Jira issue with all the development metadata Jira's Dev Status API surfaces: pull requests, branches, commits, repositories, and builds.
events
Package events defines the structured event sink interface used by every gojira package to report progress, warnings, errors, and partial-success states.
Package events defines the structured event sink interface used by every gojira package to report progress, warnings, errors, and partial-success states.
extract
Package extract discovers all outbound references from a parsed Jira issue.
Package extract discovers all outbound references from a parsed Jira issue.
fetch
Package fetch provides a thin adapter between the crawl orchestrator and the Jira Cloud HTTP client.
Package fetch provides a thin adapter between the crawl orchestrator and the Jira Cloud HTTP client.
hierarchy
Package hierarchy discovers Jira hierarchy children for an already-fetched issue via JQL search.
Package hierarchy discovers Jira hierarchy children for an already-fetched issue via JQL search.
output
Package output is the filesystem writer for gojira.
Package output is the filesystem writer for gojira.
parse
Package parse converts raw Jira Cloud REST API v3 issue JSON into a typed Issue value.
Package parse converts raw Jira Cloud REST API v3 issue JSON into a typed Issue value.
render
Package render converts a parsed Jira issue into Markdown content.
Package render converts a parsed Jira issue into Markdown content.
Package log is gojira's small slog-compatible logging facade.
Package log is gojira's small slog-compatible logging facade.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL