{"id":51429975,"url":"https://github.com/msradam/fromjcl","last_synced_at":"2026-07-05T03:03:06.651Z","repository":{"id":358613827,"uuid":"1106115922","full_name":"msradam/fromjcl","owner":"msradam","description":"Parse IBM z/OS JCL and serialize it to JSON, YAML, CSV, or byte-exact JCL. Optional ZOAU shell emitter.","archived":false,"fork":false,"pushed_at":"2026-06-17T18:54:31.000Z","size":3199,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-17T20:27:54.404Z","etag":null,"topics":["cli","cobol-tools","ibm","jcl","mainframe","mvs","parser","python","zoau","zos"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/fromjcl/","language":"JCL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/msradam.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-28T16:51:04.000Z","updated_at":"2026-06-17T18:54:37.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/msradam/fromjcl","commit_stats":null,"previous_names":["msradam/fromjcl"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/msradam/fromjcl","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/msradam%2Ffromjcl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/msradam%2Ffromjcl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/msradam%2Ffromjcl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/msradam%2Ffromjcl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/msradam","download_url":"https://codeload.github.com/msradam/fromjcl/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/msradam%2Ffromjcl/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35141967,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-05T02:00:06.290Z","response_time":100,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","cobol-tools","ibm","jcl","mainframe","mvs","parser","python","zoau","zos"],"created_at":"2026-07-05T03:03:03.480Z","updated_at":"2026-07-05T03:03:06.617Z","avatar_url":"https://github.com/msradam.png","language":"JCL","funding_links":[],"categories":[],"sub_categories":[],"readme":"# fromjcl\n\n[![PyPI](https://img.shields.io/pypi/v/fromjcl.svg)](https://pypi.org/project/fromjcl/)\n[![Python](https://img.shields.io/pypi/pyversions/fromjcl.svg)](https://pypi.org/project/fromjcl/)\n[![License](https://img.shields.io/pypi/l/fromjcl.svg)](LICENSE)\n[![CI](https://github.com/msradam/fromjcl/actions/workflows/ci.yml/badge.svg)](https://github.com/msradam/fromjcl/actions/workflows/ci.yml)\n\nParse IBM z/OS JCL into a typed Python model, then serialize it to\nJSON, YAML, CSV, or back to byte-exact JCL.\n\n![demo](https://raw.githubusercontent.com/msradam/fromjcl/main/docs/demo.gif)\n\n```bash\nfromjcl job.jcl --to json\nfromjcl job.jcl --to yaml\nfromjcl job.jcl --to csv          # one row per (step, DD, dataset)\nfromjcl job.jcl --to jcl          # byte-exact roundtrip\nfromjcl job.jcl --to raw          # parse-tree dump\n```\n\nThe parser is a pure-Python port of [Mike Fulton's\nJCLParser](https://github.com/MikeFultonDev/JCLParser) (Apache 2.0).\nByte-exact roundtrip is enforced on every commit against an 83-sample\ncorpus pulled from `github.com/IBM/*`, `github.com/zowe/*`, and\nhand-authored paraphrases (see\n[`tests/jcl_samples/`](tests/jcl_samples/)).\n\n## Install\n\n```bash\npip install fromjcl\n```\n\nPython 3.12+. Runtime deps are `pyyaml` and `typer`. Pure Python, so\nthe same wheel installs under IBM Open Enterprise Python on z/OS as\nwell as Linux, macOS, and Windows.\n\n### Optional `[zoau]` extra (experimental)\n\n```bash\npip install 'fromjcl[zoau]'\n```\n\nEnables `--to zoau` and `--to mvscmd`, which translate each step into\nits closest ZOAU shell equivalent (or an `mvscmd`/`mvscmdauth`\ninvocation when no opinionated mapping exists). Pulls in\n[bashlex](https://github.com/idank/bashlex) to structurally check\nevery flag in the emitted script against a frozen ZOAU 1.x manpage\ntable.\n\nThe output is best-effort. Every generated script starts with an\n`EXPERIMENTAL` banner, and bashlex catches flag typos but does not\nverify semantic equivalence to the source JCL. Review before running\nagainst real datasets.\n\n## Quick start\n\nGiven `test.jcl`:\n\n```jcl\n//TESTJOB  JOB (ACCT),'TEST',CLASS=A\n//STEP01   EXEC PGM=IDCAMS\n//SYSPRINT DD SYSOUT=*\n//SYSIN    DD *\n /* HELLO */\n/*\n```\n\n`fromjcl test.jcl --to yaml`:\n\n```yaml\nname: TESTJOB\naccount: (ACCT)\nprogrammer: TEST\nclass_: A\nsteps:\n- name: STEP01\n  program: IDCAMS\n  dds:\n  - name: SYSPRINT\n    sysout: '*'\n  - name: SYSIN\n    instream: ' /* HELLO */'\n```\n\n## Querying with jq\n\n```bash\n# every dataset referenced\nfromjcl job.jcl --to json | jq -r '.steps[].dds[].datasets[]?.dsn'\n\n# datasets being created\nfromjcl job.jcl --to json | jq -r '.steps[].dds[].datasets[]? | select(.disposition.status==\"NEW\") | .dsn'\n\n# steps that run a specific program\nfromjcl job.jcl --to json | jq -r '.steps[] | select(.program==\"IDCAMS\") | .name'\n```\n\n## Python API\n\nThe public surface is what `fromjcl/__init__.py` re-exports. Anything\nunder `fromjcl._*` or `fromjcl.converters.*` is internal.\n\n```python\nfrom fromjcl import parse, Job, to_yaml, to_jcl, from_dump\n\n# Parse + walk the model.\njob = Job.from_parsed(parse(\"test.jcl\"))\nfor step in job.steps:\n    print(step.name, step.program, [dd.name for dd in step.dds])\n\n# Forward: serialize the Job to a human-readable format.\nprint(to_yaml(job))           # YAML\n# to_json(job), to_csv(job) also available\n\n# Byte-exact roundtrip preserves comments, column layout, blank lines.\n# Pass the parse tree, not the Job (the Job IR is lossy by design).\nprint(to_jcl(parse(\"test.jcl\")))\n\n# Reverse: take a JSON/YAML/CSV dump and emit JCL (functionally\n# equivalent, not byte-exact).\nprint(from_dump(open(\"job.yaml\").read(), \"yaml\"))\n```\n\nAvailable names:\n\n| Symbol | What it is |\n| --- | --- |\n| `parse(path)`, `parse_bytes(data)` | Parser. Returns a dict with full byte-level metadata. |\n| `Job`, `Step`, `DD`, `Dataset`, `Disposition`, `Space`, `DCB` | Dataclasses. Standard equality, `asdict()`, structural pattern matching. |\n| `Job.from_parsed(tree)` | Build the Job model from a parse tree. |\n| `to_json(job)`, `to_yaml(job)`, `to_csv(job)` | Serialize a Job. |\n| `to_jcl(tree)`, `to_raw(tree)` | Serialize a parse tree (byte-exact / raw dump). |\n| `from_dump(text, fmt)` | Reverse path: text dump back to JCL. |\n\n`[zoau]` extra adds two private modules accessible via the CLI's\n`--to zoau` / `--to mvscmd` paths. They are not part of the stable\nPython API.\n\n## Reverse: re-emit JCL from JSON, YAML, or CSV\n\n```bash\nfromjcl job.json --rejcl              # auto-detects input format\nfromjcl job.yaml --rejcl --from yaml\nfromjcl job.csv  --rejcl --from csv\n```\n\nThe reverse path produces *functionally equivalent* JCL, not\nbyte-exact. Comments, blank lines, and column layout are lost on the\nforward pass through the IR and cannot be reconstructed. The\ncombinatoric matrix in [`tests/test_rejcl_matrix.py`](tests/test_rejcl_matrix.py)\nasserts that `Job → format → JCL → Job` is a fixed point under\ndataclass equality across the entire IBM/community/ZOAU corpus.\n\n## z/OS notes\n\n`fromjcl` reads EBCDIC-encoded JCL natively. Binary-transferred JCL files\n(not auto-converted by the z/OS UNIX shell) are detected by their `//`\nbyte signature (EBCDIC `0x61 0x61`) and decoded as cp037 by default. All\ncharacters that appear in JCL syntax have identical byte values in cp037,\ncp500, and cp1047, so the choice of code page rarely matters for parsing.\n\nIf auto-detection picks the wrong code page, pass `--encoding` explicitly:\n\n```bash\nfromjcl job.jcl --encoding cp037   # EBCDIC US/Canada (default)\nfromjcl job.jcl --encoding cp500   # EBCDIC International\nfromjcl job.jcl --encoding cp1047  # z/OS Open Systems (remapped to cp037)\nfromjcl job.jcl --encoding ebcdic  # alias for cp037\n```\n\nIf `pip install` itself trips over EBCDIC tagging, set\n`_BPXK_AUTOCVT=ON` in the install shell.\n\n## Development\n\n```bash\nuv sync --all-groups\ntests/check.sh        # ruff format + check, mypy, vulture, pytest\n```\n\nThe `test` group is z/OS-installable (pytest + bashlex, both pure\nPython). The `dev` group adds workstation-only tooling (ruff, mypy,\nvulture, radon, interrogate); ruff and uv are Rust binaries with no\nz/OS build.\n\nCI runs the same `tests/check.sh` pipeline on every push and pull\nrequest (see [`.github/workflows/ci.yml`](.github/workflows/ci.yml)).\n\n## Docs\n\n- [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md): parser layout, IR\n  shape, and the byte-exact roundtrip contract\n- [CHANGELOG.md](CHANGELOG.md): release notes\n- [CONTRIBUTING.md](CONTRIBUTING.md): DCO sign-off, PR expectations\n- [SECURITY.md](SECURITY.md): private vulnerability reporting\n\n## License\n\nApache-2.0. See [LICENSE](LICENSE) and [NOTICE](NOTICE).\n\n## Trademarks\n\nIBM, the IBM logo, z/OS, MVS, and Z Open Automation Utilities (ZOAU)\nare trademarks or registered trademarks of International Business\nMachines Corporation, registered in many jurisdictions worldwide.\nOther product and service names might be trademarks of IBM or other\ncompanies. A current list of IBM trademarks is available at\n\u003chttps://www.ibm.com/legal/copytrade\u003e.\n\nThis project is an independent community effort. It is not affiliated\nwith, endorsed by, or sponsored by IBM.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmsradam%2Ffromjcl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmsradam%2Ffromjcl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmsradam%2Ffromjcl/lists"}