Repository navigation
Consolidate the SITMUN repositories into a new sitmun/sitmun monorepo #105
Description
In short
SITMUN lives in nine GitHub repositories today. This proposal creates one new repository, sitmun/sitmun, with one build and one version for everything that runs in Docker. The two mobile apps keep their own versions.
sitmun/sitmun is a brand new repository. It is not a rename of sitmun-application-stack. It starts from a snapshot import of all nine repositories, including this one.
The structural refactor starts in sitmun/sitmun right after the import. The old repositories stay writable for about one month. Work already in flight lands there and is ported into sitmun/sitmun. On the end date the old repositories get a final port, a pointer README and issue transfers, and then they are archived.
Why
If you install SITMUN
- Docker Compose compiles two Angular apps and three Java builds every time you deploy.
- The README says secrets are required, but Compose starts with built-in development secrets (#113).
- The PostgreSQL and Oracle Compose files in
profiles/do not build (#110).
If you want to contribute
- You have to understand eight builds and a hidden step that publishes a shared library to your machine before the backend or proxy builds.
- No repository has a contributing guide, a security policy, a code owners file, or a pull request template.
- It is not written down that pull requests go to
dev, while GitHub showsmainas the default branch. - Git hooks in different components accept different commit messages.
If you maintain or release SITMUN
- Four separate dependency catalogs drift apart. One build uses axion-release 1.18.0 and another uses 1.19.0.
- Every release bumps 26 version strings in five components, although all five already move together at 1.2.9.
- Tags clash.
v1.0.0exists in three repositories on different commits. - A change that touches two components needs two pull requests plus a pointer update here. Checks here cannot see files inside a component, so a backend database change does not run the database upgrade tests.
Target layout
sitmun/
sitmun-backend API (was sitmun-backend-core)
sitmun-proxy proxy (was sitmun-proxy-middleware)
sitmun-admin admin app (was sitmun-admin-app)
sitmun-viewer map viewer (was sitmun-viewer-app)
sitmun-addons/
sitmun-mbtiles map tiles service, off unless a deployment enables it
sitmun-common/ shared libraries (was sitmun-shared)
sitmun-spring-upstream
sitmun-proxy-contract
sitmun-mobile/ outside the default build
sitmun-edition (was edition-mobile-app)
sitmun-touristic (was touristic-mobile-app)
docker/ profiles/ e2e/ tools/ docs/ .github/
compose.yaml package.json scripts/
settings.gradle build.gradle gradle.properties gradle/libs.versions.toml
- Build. One root Gradle build.
sitmun-backend,sitmun-proxy,sitmun-mbtiles,sitmun-spring-upstreamandsitmun-proxy-contractare subprojects with one version catalog. Root Gradle tasks wrap thesitmun-adminandsitmun-viewernpm builds.sitmun-editionandsitmun-touristicstay outside the default build. - Removed.
sitmun-cataloghas no consumers and is deleted (sitmun-shared#5). The separatesitmun-sharedbuild disappears intositmun-common/. So do everymavenLocal()andpublishToMavenLocal, the three axion-release blocks and the threesetupGitHookstasks. - Versions. One platform version in
gradle.properties, taggedvX.Y.Z. The mobile apps tagsitmun-edition/X.Y.Zandsitmun-touristic/X.Y.Z, because app stores set their pace. - Images. A release publishes
ghcr.io/sitmun/backend,proxy,mbtilesandfront.compose.yamlpulls them for the chosenSITMUN_VERSION, with abuild:fallback (#107). - Developer entry point.
npm run devstarts the backend on H2 plus the admin, viewer and proxy dev servers with generated secrets. It is a Node script, so it runs on Windows, macOS and Linux. The Gradle toolchain pins Java..nvmrcandenginespin Node. - Contributor files.
CONTRIBUTING,SECURITY,CODE_OF_CONDUCT,CODEOWNERS, a pull request template, issue forms with a component field, a path labeler and DCO sign-off. - CI. One always-run
ci-gatejob is the only required check. It detects changed paths and waits on the matching workflows. CodeQL runs as one matrix with acategoryper component and language. Each E2E suite runs when its paths change, and all suites run on pushes todevandmain. The Liquibase immutability gate covers the backend changelog for real once it is in the tree.
What stays separate on purpose
| Idea | Verdict | Reason |
|---|---|---|
| Merge the proxy into the backend | No | The proxy fetches arbitrary upstream URLs and runs JDBC against outside databases. The backend holds the JWT signing secret and database credentials. Separate processes are the security boundary. |
| Merge MBTiles into the backend | No | It is unauthenticated inside the network, has its own 1G memory limit and runs Spring Batch on H2. |
| One Angular workspace for admin and viewer | Not now | They already ship in one image. One lockfile would tie admin library upgrades to the viewer's api-sitna. |
| Devcontainer or a required version manager | No | Gradle and npm already run on every OS with pinned versions. Liquibase, Spring Framework and DataHub do not ship a devcontainer either. |
| Split mobile into its own repository | No | Root E2E (npm run e2e:mobile:web) covers both apps. A split loses that coverage. |
| Keep per-module tags | No | Practice is already lockstep. Per-module tags only create collisions. |
A new repository, not a rename
A rename would keep GitHub redirects. A new repository does not.
- Stars, watchers, forks and old clone URLs stay with the old repositories. Today they hold 10 stars and 26 forks in total.
- Old clone URLs and
.gitmodulesentries keep working, because the old repositories are archived and never deleted. - Maven coordinates (
org.sitmun:*) do not change. Image names are new. - Issue transfers keep redirects for issue URLs. Pull requests, releases and commit pages stay readable in the archived repositories.
Day 0. Snapshot import
- Create
sitmun/sitmunempty, with no README, license or.gitignoreinit. - Import each old repository with
git filter-repo --to-subdirectory-filteronto its finalsitmun-*directory. The final directory names exist from the first commit. sitmun-application-stackis imported at the root, not into a subdirectory. Itse2e/,tools/,profiles/,scripts/,docker-compose.ymland.github/become the new root. The import commit removes its gitlinks and.gitmodules.- The same rewrite changes
#Nin commit messages tositmun/<oldrepo>#N. That link redirects to the issue once it is transferred. - Every old tag is imported as
legacy/<repo>/<tag>, including the stack's own tags. Partner tags such as1.2.7-DIPTAand the clashingv1.0.0tags get new names this way. WIP tags are dropped. The run fails on any tag the map does not cover. - The new repository starts with no tags.
- Only
mainanddevare imported. Feature branches stay in the old repositories. - Record the imported old SHA of
mainanddevper repository intools/monorepo/ported.json. That is the starting point for ports.
Verification. For every directory, git rev-parse dev:<dir> must equal git rev-parse <snapshot>^{tree} of the old dev. A rewrite into a subdirectory keeps tree hashes, so this is one hash comparison per directory. Nobody else gets write access until all nine pass.
The import is reproducible. git filter-repo is deterministic for unchanged input. Confirmed by experiment with filter-repo a40bce548d2c and git 2.56.0. Two independent runs over sitmun-proxy-middleware (143 commits, 13 refs) gave identical SHAs. A reviewer can re-run the pinned command from tools/monorepo/ and compare. Commit signatures are dropped by the rewrite.
Day 0 result. Repository: sitmun/sitmun. 63 legacy/ tags. Stack tags are legacy/sitmun-application-stack/sitmun-application-stack/X.Y.Z. ported.json matches the old main and dev tips. Both rulesets are active. ci-gate is green on dev and red on main (Spotless in sitmun-common and sitmun-backend, plus one MBTiles integration test). 30 Dependabot pull requests were open.
Weeks 1 to 4. Refactor plus ports
The refactor does not wait. It starts in sitmun/sitmun on day 1 and edits module files directly. There is no merge-based sync, because the refactored directories no longer match the old trees.
Port mechanism
Ports use patches from the old repository, applied into the module's directory.
git -C <old> format-patch -o <out> <last-ported>..dev
# add the trailer and rewrite #N to sitmun/<repo>#N in each patch
git am -3 --directory=<sitmun-dir> <out>/*.patch
Tested on a toy module after a simulated restructure. The restructure deleted settings.gradle, rewrote build.gradle and moved src/*.java to src/main/java/. Results, all confirmed.
- Source-only changes apply cleanly. An edit to a moved file follows the move through the 3-way fallback.
- New files land at the old path. A file added under
src/in the old repository arrives at<sitmun-dir>/src/, not at the moved location. The porter moves it by hand. - Build-file changes conflict. An edit to a rewritten
build.gradlestops with a content conflict. An edit to a deletedsettings.gradlestops with a modify/delete conflict. The source hunks of the same commit are already staged. The porter resolves the build files, runsgit am --continue, and the trailer stays. - Build files can be skipped.
git am -3 --directory=<sitmun-dir> --exclude=<sitmun-dir>/build.gradleapplies the rest of the commit. The path in--excludemust include the directory prefix. The bare file name does not match. - Merge commits are skipped.
format-patchexports the commits without merges. A conflict resolution made inside a merge in the old repository is ported by hand.
Fallback. When a patch does not apply, port the old PR by hand as one commit in sitmun/sitmun. Use the old PR as the reference and carry one trailer per old commit it contains.
Tracking
- Every port commit carries
Ported-from: sitmun/<repo>@<old-sha>. tools/monorepo/ported.jsonrecords the last ported old SHA ofmainanddevper repository.- The marker lives in a tracked file, not in a git ref. It changes in the same PR as the ports it covers, so review sees both. It survives every clone and falls under the branch rules. A ref would be invisible in PRs and is not fetched by default.
Proposed script tools/monorepo/unported.sh <repo> [branch].
- Fetch the old repository's branch.
- List
git rev-list --no-merges --reverse <marker>..<branch>. - Drop every SHA that already appears in a
Ported-fromtrailer onsitmun/sitmundev. This covers ports done out of order. - Print what is left and exit non-zero if anything is.
A scheduled workflow runs it for all nine repositories and posts the counts. The end-date exit check is zero unported commits on main and dev of every old repository. Admin main holds 3 commits that are not on dev today, so main is checked on its own.
Keeping the port load small
- Old-repository contributors keep build-file changes out of feature PRs. Build changes go to
sitmun/sitmun. - In-flight PRs land or rebase early in the month, not on the last days.
- Dependabot stays off in all nine old repositories for the month. Its PRs would only create port work.
- Close the 17 open Dependabot PRs on
sitmun-admin-app. The root Dependabot config insitmun/sitmunredoes them against the new layout. - New features freeze in the old repositories one week before the end date. After that only fixes for in-flight work land there.
- Work that is not already in flight goes to
sitmun/sitmunfrom Day 0. - Each old repository gets a pinned issue on Day 0 that states the end date, the freeze date and where new work goes.
Refactor order
- Layout. Moves are git-tracked, so
--directoryand rename detection handle ports into them. Thesitmun-*directories already exist from the import. Move the Dockerfiles todocker/, renamedocker-compose.ymltocompose.yamland cleanprofiles/. Avoid moving sources inside a module during the month, because new files ported into it land at the old path. - Root Gradle build and one catalog. Start with the modules that have the least in-flight work. That is
sitmun-common/,sitmun-mbtilesandsitmun-proxy.sitmun-backendgoes last. - Version. Remove axion,
setupGitHooks,mavenLocalandpublishToMavenLocal, and move to one version ingradle.properties. Backend goes last here too.
In-flight work found on 27 Oct 2026, over the last 30 days.
| Repository | Commits on dev |
Open PRs (not Dependabot) | Recent unmerged branches | Touch build files |
|---|---|---|---|---|
| sitmun-backend-core | 83 | #355 | 29 | #355, fix/292-style-name-unique, fix/294-literal-unique |
| sitmun-application-stack | 56 | #116, #118 | 1 | #118 (profile Compose) |
| sitmun-admin-app | 46 | #485 | 0 | none |
| sitmun-viewer-app | 38 | #204 | 1 | none |
| sitmun-proxy-middleware | 21 | none | 0 | none |
| sitmun-mbtiles | 11 | none | 0 | none |
| sitmun-shared | 6 | none | 1 | none |
| touristic-mobile-app | 6 | #16 | 1 | #16 (package.json) |
| edition-mobile-app | 3 | #26 | 1 | #26 (package.json) |
The backend carries most of the in-flight work. If its port load is high, its build-file refactor moves to the last week or to right after the end date.
Merged since that count: stack #122 and #133, shared #6. Still open against main: stack #116 and #118.
The stack itself
sitmun-application-stack is still active, with 56 commits on dev in 30 days and three open PRs. Its files become the root of sitmun/sitmun, and the root is refactored first. So the stack stops taking changes on Day 0. Merge #116, #118 and #122 before creation. Anything that misses Day 0 is ported like any other change, without --directory.
Issues and PRs
- New issues and PRs for in-flight work go to the old repository until its end date. Everything else goes to
sitmun/sitmunfrom Day 0. - Issues transfer per repository at the end date, open and closed. Archived repositories cannot transfer issues out, so every transfer finishes before archiving.
- The stack is the exception. All its issues, including this one and #106 to #115, move on Day 0, because its root files move then.
- Transfer one issue at a time with
gh issue transfer, oldest first, paced under GitHub's secondary rate limits. Record old URL, new number and component in a CSV. - Every participant gets a transfer notification. Announce this with the end date.
- Check org Projects before transfers. Transferred issues may need to be re-added to boards.
- PRs cannot be transferred. Every old PR is merged and ported, or closed, before the end date.
Issue counts on 27 Oct 2026.
| Repository | Open | Closed | Open PRs |
|---|---|---|---|
| sitmun-application-stack | 32 | 33 | 3 |
| sitmun-backend-core | 0 | 235 | 1 |
| sitmun-admin-app | 13 | 311 | 18 |
| sitmun-viewer-app | 22 | 124 | 1 |
| sitmun-proxy-middleware | 0 | 7 | 0 |
| sitmun-mbtiles | 0 | 0 | 0 |
| sitmun-shared | 3 | 0 | 1 |
| edition-mobile-app | 15 | 3 | 1 |
| touristic-mobile-app | 5 | 2 | 1 |
| Total | 90 | 715 | 26 |
805 issues transfer in total.
Labels must exist in sitmun/sitmun before any transfer, because a transfer keeps only labels that already exist by name. Create them on Day 0. The issues use bug, enhancement, documentation, duplicate, invalid, wontfix, question, help wanted, good first issue, planned, confirmed, someday, unconfirmed, stale, build, maintenance, verified, meta and dependencies. Add component:backend, component:proxy, component:admin, component:viewer, component:mbtiles, component:common, component:edition and component:touristic. Stack-wide issues stay without a component label.
Releases
- During the month the old repositories may cut patch releases with their existing tags.
sitmun/sitmuncuts no release until the end date.- Every old-repository release made during the month is ported before the first
vX.Y.Z. Its tag stays in the archived repository. - The first platform release is
sitmun/sitmun's ownvX.Y.Z, cut after the final port. It is the next platform version after 1.2.9. The number is a decision to take. - The 1.3.0 database baseline (#123) changes backend files. It is built in
sitmun/sitmun, not insitmun-backend-core.
Settings that do not carry over
A new repository starts with none of the old settings. This list covers only what exists today, checked with gh on 27 Oct 2026.
- Team access.
silmehas push on the stack, admin, viewer, proxy and both mobile apps.nexushas push on the stack and admin and pull on backend, viewer and proxy.guadaltelhas push on backend and proxy.atol-cdhas push on backend and viewer.avansighas push on viewer.rbejaris a direct admin collaborator on backend. In one repository, write access spans every directory, soCODEOWNERStakes over the per-component boundary. - Actions secrets. No repository-level secrets exist. Org secrets
HEROKU_EMAIL,HEROKU_TOKEN,SONAR_TOKEN,TOKEN_FOR_WORKFLOWSandUSERNAME_FOR_WORKFLOWSare visible tositmun/sitmun. - SonarCloud.
mainstill runs SonarCloud in backend, admin and proxy.devhas dropped it. Decide whether to rebind it tositmun/sitmunor drop it. - CodeQL. Six
Analyzechecks are green onsitmun/sitmundev. Alert history stays in the old repositories. - Dependabot. Only admin has a config. Add a root
.github/dependabot.ymlinsitmun/sitmunwith one entry per module directory. - Environments.
herokuin backend.github-pagesin viewer. - Pages. The viewer publishes
https://sitmun.github.io/sitmun-viewer-app/from a branch. Decide whether to keep it there or rebuild it fromsitmun/sitmununder a new URL. - Rules. No branch protection exists anywhere. Backend has one ruleset,
Sample, which is disabled. Create thedevandmainrulesets insitmun/sitmunwithci-gaterequired and code owner review. - GHCR. Images are new under
ghcr.io/sitmun/. Link each package tositmun/sitmunand grant it Actions write access when the release workflow lands. - Repository features. Wiki and Discussions are off on
sitmun/sitmun.
Repository secrets, Dependabot secrets, Actions variables, webhooks, deploy keys and autolinks are absent in all nine repositories. Nothing to recreate there.
Risks
- Port backlog at the end. Unported commits pile up in the last week. The scheduled
unported.shcounts, the feature freeze and early landing of in-flight PRs keep it small. - Build-file conflict with an in-flight branch. A branch that edits
build.gradleconflicts with the refactor. Three backend branches and #355 do today. They are ported by hand, or the backend build refactor waits until they land. - A contributor misses the announcement. The pinned issue in each old repository and the feature freeze date limit the damage. Late work is ported by hand.
- Force-push in an old repository. It rewrites the commits the marker points at, and
unported.shloses its base. Rulesets block force-push and deletion onmainanddevin all nine repositories before creation. None has branch protection today. - CI runs in both repositories for a month. A change runs CI in the old repository and again when it is ported. This is accepted. Scheduled workflows in the old repositories can be turned off to limit it.
- Downstream pins. Consumers pin clone URLs, submodule URLs and image names. Clone URLs keep working against archived repositories. Maven coordinates do not change. Image names are new, so deployers switch once.
Steps
The end date is a decision. Suggested Day 0 plus 30 days, for example Friday 27 Nov 2026, with the feature freeze one week earlier on Friday 20 Nov 2026.
Before creation (one or two days)
- Add rulesets blocking force-push and deletion on
mainanddevin all nine repositories. - Add the EUPL-1.2 LICENSE to
sitmun-shared(sitmun-shared#4). - Delete
sitmun-catalog(sitmun-shared#5, PR sitmun-shared#6). - Fix the profile Compose files (#110, PR #130). PR #118 remains open.
- Exclude
node_modulesfrom the root Docker context (#114, PR #131). PR #116 remains open. - Merge or close the stack's #122.
- Turn off Dependabot in all nine repositories and close the 17 admin Dependabot PRs.
- Pin
git-filter-repo, git, the tag map and the message rewrite undertools/monorepo/(#132). - Announce Day 0, the feature freeze date and the end date. Tell partners their tags are renamed under
legacy/.
Day 0
- Create
sitmun/sitmunempty. - Import the nine histories onto their final paths and push to
dev. - Pass the tree check for every directory, then open write access.
- Write
tools/monorepo/ported.jsonwith the imported SHAs. - Fix the root
.gitignore(it ignores*.propertiesand*.jar, which would drop every Gradle wrapper). - Add root workflows,
ci-gate,CODEOWNERS, the rulesets, the labels and the root Dependabot config. - Make
sitmun-application-stackread-only and transfer its issues. - Pin the announcement issue in the other eight repositories.
Weeks 1 to 4
- Layout refactor.
- 1. Root E2E paths. sitmun#43
- 2. Viewer E2E Node (#115). sitmun#44
- 3. Liquibase paths and legacy tags. sitmun#45
- 4. Dependabot batch. Superseded bumps are closed. Green minor and patch bumps stay open for review. Major bumps stay open.
- 5. Docker layout:
docker/andcompose.yaml. sitmun#48 - 6.
ci-gatebuilds images and runs E2E (#108). sitmun#49 - 7. Setup scripts. sitmun#46
- 8. Nested component GitHub files and root issue forms. sitmun#47
- 9. Contributor docs (#111, #112). sitmun#50
- 10. Contributor files on
main. sitmun#51
- Root Gradle build and one catalog, backend last.
- One version, backend last.
- Port in-flight work at least weekly with
Ported-fromtrailers. - Feature freeze in the old repositories one week before the end date.
End date
- Final port.
unported.shreports zero onmainanddevfor every old repository. - Merge and port, or close, every remaining old PR.
- Make each old repository read-only.
- Commit a pointer README to
sitmun/sitmunand the matching directory on each old default branch. - Transfer each repository's issues and add
component:<name>. - Archive all nine repositories.
- Cut the first
vX.Y.Zand publish the images.
How we know it worked
- No gitlinks and no
.gitmodulesremain insitmun/sitmun. - At import, each directory's tree hash equals the old
devroot tree hash. git logon a directory shows its old commits, andlegacy/tags check out.- Issue references in imported commit messages open the transferred issue.
unported.shreports zero for every old repository on the end date.- Every old repository is archived with a pointer README, and its issues are in
sitmun/sitmunwith labels. ./gradlew buildat the root is the only Gradle build, and nosettings.gradleremains below the root.docker compose buildsucceeds, and the E2E suites pass in CI.
Activity
[-]Move the stack and its eight component repositories into one Git repository[/-][+]Consolidate the SITMUN repositories into a new sitmun/sitmun monorepo[/+]