{"id":50496805,"url":"https://github.com/hubzero/hzforge","last_synced_at":"2026-06-02T08:03:51.476Z","repository":{"id":360080545,"uuid":"1248616899","full_name":"hubzero/hzforge","owner":"hubzero","description":"Install, diagnose, and repair HUBzero Forge services (Subversion, Git, gitExternal, Trac) as self-contained Apache drop-ins — independent of the m4 template and hzcms.","archived":false,"fork":false,"pushed_at":"2026-05-24T22:47:17.000Z","size":62,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-24T23:29:42.954Z","etag":null,"topics":["apache","git","hubzero","mod-python","mod-wsgi","python","subversion","trac"],"latest_commit_sha":null,"homepage":"https://hubzero.github.io/hzforge/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hubzero.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-24T21:51:05.000Z","updated_at":"2026-05-24T22:47:21.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/hubzero/hzforge","commit_stats":null,"previous_names":["hubzero/hzforge"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/hubzero/hzforge","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hubzero%2Fhzforge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hubzero%2Fhzforge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hubzero%2Fhzforge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hubzero%2Fhzforge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hubzero","download_url":"https://codeload.github.com/hubzero/hzforge/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hubzero%2Fhzforge/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33812205,"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-06-02T02:00:07.132Z","response_time":109,"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":["apache","git","hubzero","mod-python","mod-wsgi","python","subversion","trac"],"created_at":"2026-06-02T08:03:50.427Z","updated_at":"2026-06-02T08:03:51.466Z","avatar_url":"https://github.com/hubzero.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"gh-pages/assets/logo.svg\" alt=\"\" width=\"120\" height=\"120\"\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003ehzforge\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cem\u003eInstall, diagnose \u0026amp; repair HUBzero Forge services \u0026mdash; Apache drop-ins, no m4.\u003c/em\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/hubzero/hzforge/actions/workflows/tests.yml\"\u003e\u003cimg alt=\"tests\" src=\"https://github.com/hubzero/hzforge/actions/workflows/tests.yml/badge.svg\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/hubzero/hzforge/actions/workflows/integration.yml\"\u003e\u003cimg alt=\"integration (EL8)\" src=\"https://github.com/hubzero/hzforge/actions/workflows/integration.yml/badge.svg\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/hubzero/hzforge/actions/workflows/docs.yml\"\u003e\u003cimg alt=\"docs CI\" src=\"https://github.com/hubzero/hzforge/actions/workflows/docs.yml/badge.svg\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://hubzero.github.io/hzforge/\"\u003e\u003cimg alt=\"documentation\" src=\"https://img.shields.io/badge/docs-hubzero.github.io%2Fhzforge-2456c2?logo=github\u0026logoColor=white\"\u003e\u003c/a\u003e\n  \u003cimg alt=\"Python 3.6+\" src=\"https://img.shields.io/badge/python-3.6%2B-3776ab?logo=python\u0026logoColor=white\"\u003e\n  \u003ca href=\"LICENSE.md\"\u003e\u003cimg alt=\"license: MIT\" src=\"https://img.shields.io/badge/license-MIT-007ec6\"\u003e\u003c/a\u003e\n  \u003cimg alt=\"status: beta\" src=\"https://img.shields.io/badge/status-beta-d54a3c\"\u003e\n\u003c/p\u003e\n\n---\n\n`hzforge` installs, uninstalls, diagnoses, and repairs **HUBzero Forge\nservices** \u0026mdash; Subversion, Git, gitExternal, and Trac \u0026mdash; as\nself-contained Apache **drop-ins**, independent of the m4 vhost template.\n\nOn a HUBzero hub each tool gets a project area under `/tools/\u003cname\u003e/…`:\n\n| Service       | URL space                          | Apache mechanism |\n|---------------|------------------------------------|------------------|\n| `svn`         | `/tools/\u003cname\u003e/svn`                | `mod_dav_svn` (`\u003cLocation\u003e`) |\n| `git`         | `/tools/\u003cname\u003e/git/\u003cname\u003e`         | `git-http-backend` (`ScriptAliasMatch`) |\n| `gitExternal` | `/tools/\u003cname\u003e/gitExternal/\u003cname\u003e` | `git-http-backend` (`ScriptAliasMatch`) |\n| `trac`        | `/tools/\u003cname\u003e/{wiki,timeline,browser,ticket,…}` | mod_wsgi (default) or mod_python |\n\nhzforge writes **one config file per service** at\n`/etc/httpd/\u003chub\u003e.conf.d/00-forge-\u003csvc\u003e.conf`, picked up by the vhost's existing\n`IncludeOptional \u003chub\u003e.conf.d/*.conf` \u0026mdash; so it never edits the m4-generated\nvhost or requires regenerating the vhost. The per-tool `svn.conf` / `git.conf` blocks\nstill come from the hub's existing MySQL-driven generator; hzforge only *includes*\nthem and shields them from the CMS catch-all rewrite.\n\n## Documentation\n\nFull docs: **\u003chttps://hubzero.github.io/hzforge/\u003e** (sources under [`docs/`](docs/)).\nHUBzero Trac plugins being ported alongside hzforge live under [`plugins/`](plugins/).\n\n- [Summary](https://hubzero.github.io/hzforge/overview/summary/) \u0026mdash; what it is, at a glance\n- [Motivations](https://hubzero.github.io/hzforge/overview/motivations/) \u0026mdash; why it bypasses the m4 vhost\n- [Architecture](https://hubzero.github.io/hzforge/reference/architecture/) \u0026mdash; how the drop-ins are wired\n- [Services](https://hubzero.github.io/hzforge/reference/services/) \u0026mdash; the four services in detail\n- [Usage](https://hubzero.github.io/hzforge/operations/usage/) \u0026mdash; full command reference\n- [Manual installation](https://hubzero.github.io/hzforge/operations/manual-install/) \u0026mdash; by-hand steps for each handler/service set\n- [Migration](https://hubzero.github.io/hzforge/operations/migration/) \u0026mdash; mod_python → mod_wsgi\n\n## Quickstart\n\nRun as **root**. Services are **positional** (space- or comma-separated); a bare\n`hzforge` prints help. Preview anything with `--dry-run`.\n\n```sh\nsudo python3 hzforge.py install                 # all services\nsudo python3 hzforge.py install trac            # one service\nsudo python3 hzforge.py install svn git trac\nsudo python3 hzforge.py uninstall git           # stop serving git (data kept)\nsudo python3 hzforge.py doctor                  # diagnose (exit 1 on FAIL)\nsudo python3 hzforge.py doctor git              # diagnose one service\nsudo python3 hzforge.py repair                  # fix drift\n```\n\n## Commands\n\n| Command | What it does |\n|---|---|\n| `install [services]` | Install packages, create `/opt/\u003csvc\u003e/tools` dirs (conventional perms), create the `hzsvn`/`hzgit` groups, write the drop-in(s). No services = all. After `install trac` it runs `test` automatically (skip with `--no-test`). |\n| `uninstall \u003cservices\u003e` | Remove a service's drop-in (and, for trac, unload its interpreter) plus hzforge's own files for it (shim, egg cache, wandisco repo file). **Never** removes packages, the `hzsvn`/`hzgit` groups, or repo data. |\n| `doctor [services]` | Read-only diagnosis; exits non-zero on FAIL. Service checks scope to the request; global checks (`configtest`, interpreter state) always run. |\n| `repair [services]` | Diagnose, then re-assert the requested services and fix drift, then validate + reload/restart. |\n| `test [services]` | For each configured service, create a throwaway, uniquely-named project, verify it serves over HTTP (trac `…/wiki`, svn `…/svn/`, git `…/info/refs`) → 200, then remove it. Self-contained (no MySQL/forge provisioning; svn/git get a temporary self-test route). Exits non-zero on failure. |\n\n### Install options\n\n| Option | Default | Notes |\n|---|---|---|\n| `--trac-handler {mod_wsgi,mod_python}` | `mod_wsgi` | Exactly one interpreter is loaded. |\n| `--svn-source {wandisco,appstream}` | `wandisco` | `subversion`+`mod_dav_svn` source; `subversion-python` always from hubzero. |\n| `--ldap-url/--ldap-binddn/--ldap-bindpw` | auto-detect | Trac `/login` auth; read from `svn.conf` if omitted. |\n| `--ldap-bindpw-file` | — | Read the bind password from a root-only file (avoids exposing it in the process list). |\n| `--force-pip` | off | Reinstall Trac even if importable. |\n\nCommon: `--hub \u003cname\u003e` (auto-detected), `--dry-run`, `--no-restart`.\n\n## How it works\n\n- **No carve-out for alias handlers.** `WSGIScriptAliasMatch` (mod_wsgi Trac) and\n  `ScriptAliasMatch` (git) self-divert at translate-name, before the CMS rewrite.\n  `mod_dav_svn` and mod_python Trac use `\u003cLocation\u003e`, so the drop-in shields those\n  paths with a vhost-scope `RewriteRule … - [END]`.\n- **WSGI shim** (`/opt/trac/wsgi/hubtrac.wsgi`) re-splits `SCRIPT_NAME`/`PATH_INFO`\n  and selects the env \u0026mdash; the mod_wsgi replacement for `PythonOption\n  TracUriRoot`.\n- **Restart vs reload** is decided by comparing the running httpd's loaded modules\n  (`/proc/\u003cpid\u003e/maps`) against what's enabled on disk; full restart only when the\n  interpreter set must change. With systemd it uses `systemctl`; without it (a\n  container/chroot) it drives `httpd -k` directly — EL8's `apachectl` always defers\n  `start`/`restart`/`graceful` to systemctl — and first creates the `/run/httpd`\n  runtime dir that `systemd-tmpfiles` would otherwise provide.\n- **Trac ↔ Subversion are decoupled** \u0026mdash; Trac runs without the repo browser;\n  the browser is auto-enabled only when the svn service is installed.\n\nSee [architecture](https://hubzero.github.io/hzforge/reference/architecture/) for\nthe full picture.\n\n## Requirements\n\n- Rocky/RHEL 8, Apache 2.4, Python 2.7 (for the Trac stack) and Python 3.6+ (to run\n  hzforge — it targets RHEL 8's stock `python3`, 3.6). Run as root.\n- The `hubzero` yum repo (`hubzero-trac-mysqlauthz`, `subversion-python`);\n  for `--svn-source wandisco`, access to\n  `opensource.wandisco.com`; for the mod_wsgi handler, PyPI access.\n\n## License\n\nMIT \u0026mdash; see [LICENSE.md](LICENSE.md). Copyright \u0026copy; 2026 Purdue University.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhubzero%2Fhzforge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhubzero%2Fhzforge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhubzero%2Fhzforge/lists"}