Collect scattered config files from your monorepo and generate them where your systems expect.
Many tools require config files in specific locations:
- GitHub reads
CODEOWNERSfrom.github/CODEOWNERS - GitHub Actions workflows must live in
.github/workflows/ - And more...
In a monorepo, each package has its own context. But these systems only look at root-level paths. You end up with a single massive file that every team has to edit, leading to merge conflicts and unclear ownership.
pull-up lets you keep config files next to the code they describe, then collects and generates them to the locations your systems expect.
packages/
core/
CODEOWNERS # * @core-team
web/
CODEOWNERS # * @frontend-team
api/
CODEOWNERS # * @backend-team
↓ pullup sync
.github/
CODEOWNERS # All entries merged with correct paths
npm install -D @pull-up/cli
# or
yarn add -D @pull-up/cli
# or
pnpm add -D @pull-up/cliCreate a config file in your project root:
import { defineConfig, codeownersJob } from "@pull-up/cli";
export default defineConfig(codeownersJob());
// or export default defineConfig([codeownersJob()]);Collects CODEOWNERS files from your monorepo and merges them into a single file.
codeownersJob({
from: ["**/CODEOWNERS"], // default
output: ".github/CODEOWNERS", // default
});You can define custom jobs using defineJob:
import { defineConfig, defineJob } from "@pull-up/cli";
const myJob = defineJob({
name: "my-job",
from: ["packages/*/config.json"],
output: "merged-config.json",
transform: (sources, context) => {
// sources: array of { path, contents }
// context: { root, outputPath, existingContents }
return JSON.stringify(sources.map((s) => JSON.parse(s.contents)));
},
});
export default defineConfig([myJob()]);Generate files from scattered sources:
pullup syncPreview changes without writing:
pullup sync --dry-runVerify generated files are up to date (useful in CI):
pullup check| Option | Description |
|---|---|
--root <path> |
Repository root path |
--cwd <path> |
Working directory |
--dry-run |
Preview without writing (sync only) |