In Spine, dependencies and CI configurations are shared among the subprojects.
The code of this repository should be added to a target project as a Git submodule.
To add a submodule:
git submodule add https://github.com/SpineEventEngine/config configThis will only add a submodule with the reference to the repository.
To get the actual code for the config submodule, run the following command:
git submodule update --init --recursiveRun the following command from the root of your project:
./config/pullpull updates config to the tip of origin/master, then floats the
config-managed submodules — the shared agents submodule and
any shared submodule added later (those declaring a tracked branch in
.gitmodules) — to their branch tip, leaving each on the branch rather than in
a detached HEAD. Submodules your repository owns (a Hugo theme, a vendored
library) declare no tracked branch and are left untouched. It then copies the
shared files into your project.
Use
./config/pull, notgit submodule update --recursive. A baregit submodule updaterestores the commit each submodule is pinned to in your superproject and will roll the shared submodules backward; onlypull(orgit submodule update --remote) advances them to the latestmaster.
The following files will be copied:
.idea— shared IntelliJ IDEA settings..codecov.yml.gitattributesand.gitignore(created on first run if absent;.gitignoreis overwritten on update)..github— created on first run if absent. GitHub workflows from.github-workflows/are then merged into.github/workflows/on every update.buildSrc— common build-time code, in Kotlin.module.gradle.ktsin the consuming repo is preserved.gradle/,gradlew,gradlew.bat— the Gradle Wrapper.gradle.properties— overwritten on update.CONTRIBUTING.md,CODE_OF_CONDUCT.md.
The pull script also wires up AI-agent configuration:
AGENTS.mdandCLAUDE.md— copied entry points that direct any agent to.agents/guidelines/_TOC.md.- Shared skills, scripts, and guidelines are not copied. They live in the
SpineEventEngine/agentsrepository, mounted as a floating Git submodule at.agents/shared(trackingmaster) and exposed through symlinks:.agents/skills,.agents/scripts,.agents/guidelines,.claude/commands, and.claude/agents— plus.claude/skillsand.junie/skills, which alias.agents/skills.pullruns the idempotentadopt-shared-agentsscript, which sets up the submodule on the first run and floats it to the latestagents@master(checked out onmaster, not a detachedHEAD) on every subsequent run — so shared skills update everywhere with no file churn in consumer pull requests. .claude/settings.json— the permission allowlist distributed byconfig(Hugo-only repos receive a Hugo-tuned variant). JVM and mixed repos also getsettings.local.json; Hugo-only repos do not..junie/guidelines.md— JetBrains Junie guidelines.
Per-repo content is never overwritten: docs/project.md (linked from .agents/project.md),
.agents/memory/, and .agents/tasks/.
The single source of truth for each workflow is its SKILL.md in the
agents repository; the Claude slash commands
and subagents are thin wrappers that point Claude Code at those files.
When changing the configuration (e.g. a version of a dependency, or adding a build script plugin),
it may be worth testing that the change does not break dependant projects. ConfigTester allows
automating this process. This tool serves to probe the Spine repositories for compatibility with
the local changes in the config repository. The usage looks like this:
// A reference to `config` to use along with the `ConfigTester`.
val config = Paths.get("./")
// A temp folder to use to check out the sources of other repositories with the `ConfigTester`.
val tempFolder = File("./tmp")
// Creates a Gradle task which checks out and builds the selected Spine repositories
// with the local version of `config` and `config/buildSrc`.
ConfigTester(config, tasks, tempFolder)
.addRepo(SpineRepos.baseTypes) // Builds `base-types` at `master`.
.addRepo(SpineRepos.base) // Builds `base` at `master`.
.addRepo(SpineRepos.coreJvm) // Builds `core-jvm` at `master`.
// This is how one builds a specific branch of some repository:
// .addRepo(SpineRepos.coreJvm, Branch("grpc-concurrency-fixes"))
// Register the produced task under the selected name to invoke manually upon need.
.registerUnder("buildDependants")The build.gradle.kts is already tuned to test changes
against these projects:
base,base-types, andcore-jvm.
This takes slightly over half an hour, depending on the local configuration.
If you need to change the list of repositories, please update addRepo() calls to ConifigTester.
The command to start the build process is:
./gradlew clean buildDependants This directory contains GitHub Workflow scripts that do not apply to the config repository, and
as such cannot be placed under .github/workflows.
These scripts are copied by the pull script when config is applied to a new repository.
Occasionally a repository needs a CI workflow that diverges from the uniform one
distributed here — for example, gcloud-java decrypts a
service-account key on Ubuntu and skips the Datastore-emulator suites on the
Windows runner. Keep the repo-specific variant under a distinct name (e.g.
build-on-ubuntu-gcloud.yml) and add a directive comment naming the distributed
file it stands in for:
# config:replaces build-on-ubuntu.yml
name: Ubuntu CI with Google Cloud SDKmigrate reads these config:replaces directives and will not copy the named
generic workflow into that repository, so only the repo-specific variant runs.
The directive is an ordinary YAML comment, so GitHub Actions ignores it.
migrate never deletes a generic workflow the repository already committed: when
you first introduce a variant, delete the generic file (e.g. build-on-ubuntu.yml)
by hand once. From then on, ./config/pull leaves the variant in place and does
not re-add the generic.