You've got this awesome project you built internally. It started small, maybe lived in a giant monorepo, or perhaps its own repo that also accumulated some... internal peculiarities. Now, the time has come: you want to share it with the world! Open source it! 🎉
That initial excitement often hits a speed bump: how do you actually get just that project out without exposing the entire monorepo, confidential history, accidental secrets, or internal configurations? And maybe even trickier: once it's out, how do you keep the public version updated if development still primarily happens internally?
I've wrestled with this myself. Manually copying files is error-prone. git filter-repo
(or its predecessor filter-branch
) is powerful but feels like handling dynamite – one wrong move and you've either leaked history or permanently mangled the public repo. And keeping things synced later? Often involves complex scripts, manual cherry-picking marathons, or just giving up and letting the public version stagnate.
Wouldn't it be nice to have a tool that helps manage this process? Something that automates the tedious parts but keeps you firmly in control for the critical review steps?
That's why I built oss-porter
.
What is oss-porter
?
oss-porter
is a command-line tool written in Rust, designed specifically for this internal-to-public workflow. Its philosophy is:
- Configuration is Key: Define what needs to be ported and how in a simple
~/.oss-porter.toml
file. Manage multiple projects easily. - Safety First: Provide mechanisms for clean extraction and emphasize manual review. Help prevent accidental leaks.
- Sustainable Updates: Offer a guided, interactive workflow to sync ongoing internal development to the public repository after the initial release.
How Does It Work? (The Core Ideas)
Instead of just copying files or blindly filtering history, oss-porter
uses a more structured approach based on your configuration:
Configuration (
oss-porter config ...
): You start by tellingoss-porter
about your project(s) using commands likeconfig init
andconfig add
(or by editing~/.oss-porter.toml
). You define things like:internal_repo_path
: Where the private source lives.project_subdir
: Which specific subdirectory within that repo is the project you care about.output_path
: A separate, dedicated directory where the clean, public version will be managed locally.history_mode
:"clean_slate"
(default, copies files, no history) or"preserve"
(usesgit-filter-repo
, requires manual vigilance!).internal_branch
/public_branch
: Which branches to track and push to (defaults tomain
).public_repo_url
: The URL for the final GitHub/GitLab destination.
Initial Extraction (
oss-porter extract <id>
): Based on the config, this command creates theoutput_path
, populates it using the chosenhistory_mode
, initializes a fresh Git repo (if clean slate), adds basics like.gitignore
and a placeholderLICENSE
(if configured), and crucially excludes the internal state file. This step requires heavy manual review of theoutput_path
afterwards!Ongoing Updates (
oss-porter update <id>
): This is where it gets interesting for maintainers. It doesn't just re-copy. Instead:- It reads a state file (
.oss_porter_state.toml
, committed inside your internal repo's project subdir) to know the last internal commit that was synced. - It finds new commits on the configured
internal_branch
that touched theproject_subdir
. - It then interactively walks you through each commit:
- Shows you the
git diff
(relative to the project subdir). - Asks: Apply this commit?
[Y]es / [n]o / [s]kip / [A]ll remaining / [q]uit
- Shows you the
- It applies the approved commits as patches (
git am
) to youroutput_path
repository. - If conflicts occur, it pauses and tells you how to resolve them manually.
- When done, it updates the state file and prompts you to commit that state change back to your internal repo.
- It reads a state file (
Checks & Push (
oss-porter check <id>
,oss-porter push <id>
): Helper commands to run basic sanity checks (path deps, license) on theoutput_path
repo and push it to the configuredpublic_repo_url
.
Use Cases (Especially for Corporate OSS)
- Releasing a Library from a Monorepo: Define the library's subdir, use
extract clean_slate
, manually review theoutput_path
thoroughly, set up the internal state file,push
, and then useupdate
to sync future internal fixes/features. - Open Sourcing an Existing Internal Tool: If the tool had its own repo but with internal cruft,
extract clean_slate
provides a fresh start. If history is really valuable and you are confident you can clean it,extract preserve
is an option (review required!). Useupdate
for ongoing sync. - Maintaining Multiple OSS Projects: Define all your managed projects in the central
~/.oss-porter.toml
and useoss-porter update <project-id>
for each as needed. The shared state file (committed internally per project) ensures collaborators can pick up where others left off. - Controlled Syncing: The interactive
update
allows you to explicitly skip internal commits that shouldn't go public (e.g., internal refactoring, commits accidentally touching the subdir, temporary hacks) without complex Git gymnastics.
Example Quick Flow
# 1. One-time setup
oss-porter config init
oss-porter config add # Follow interactive prompts
# 2. Initial Extraction (creates /path/to/output defined in config)
oss-porter extract my-project
# 3. !!! CRITICAL MANUAL REVIEW in /path/to/output !!!
# (Clean secrets, fix deps, add LICENSE/README, git commit fixes)
# 4. Create & Commit State File internally
# (Create .oss_porter_state.toml in internal repo's subdir with extract hash)
# (git add .oss_porter_state.toml && git commit ...)
# 5. First Push
oss-porter push my-project
# --- Later ---
# 6. Make changes internally, commit & push internally
# 7. Sync updates interactively
oss-porter update my-project # Review diffs, select Y/n/s/A/q
# 8. If prompted, confirm commit of updated state file internally
# 9. !!! CRITICAL MANUAL REVIEW & TEST in /path/to/output !!!
# 10. Push updates
oss-porter push my-project
Safety First!
It's worth repeating: oss-porter
helps automate the mechanics, but it does not eliminate the need for careful manual review. Especially after extract
and after update
(particularly if you use the [A]ll
option), you must inspect the code and potentially the history in the output_path
directory before pushing publicly.
Try it Out!
This is still a relatively new tool, but it solves a problem I've personally encountered frequently.
- Find the code: https://github.com/normano/oss_porter
- Installation:
cargo install oss-porter
or build from source.
I'd love to hear feedback, suggestions, or contributions if this is a problem you face too! Let me know what you think.