# oils: the readme tells you not to clone this repository

> Oils is a bash upgrade path written in Python and mechanically translated to C++, and its readme opens by steering users away from the source tree entirely. What the repository is instead is a development environment, and it is candid about its own seams: contributors edit Python and hope the translation works, failing spec tests are merged on purpose, and the tree carries a vendored CPython 2.7.13.

**oils-for-unix/oils** — Oils is our upgrade path from bash to a better language and runtime.  It's also for Python and JavaScript users who avoid shell!

- Repository: https://github.com/oils-for-unix/oils
- Website: https://oils.pub/
- Stars: 3,397 · Forks: 195
- Language: Python
- License: NOASSERTION
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/oils-for-unix-oils

## The first instruction is to use a different distribution channel

The readme contains an important notice early, and it is not about the code. It says that if you want to use Oils, do not clone this repository, and to visit the project's release page instead.

That is reinforced twice more. There is a wiki page with a name that states the whole distinction, the repository is different from the tarball releases, and the same reminder reappears near the development build instructions.

The reason follows from the build model. The project is written in Python and translated to C++ with custom tools so the result is fast and small, and the deployed executable does not depend on Python. What is in the repository is the source of that process, not the product of it. There are no GitHub releases, and the version lives in a plain text file at the root rather than in a tag, which is consistent with releases being built as tarballs from somewhere else.

So there are two artefacts with two lifecycles. A user installs a tarball from the project site. A contributor clones the repository and gets a Python program that runs immediately and can be edited without a compile step.

That second artefact is the one this article is about, and it is genuinely unusual to receive that much documentation. Most projects would not think to tell users to go elsewhere.

## You write Python and hope the translation to C++ keeps up

The build model is stated plainly: the code is Python so it is short and easy to change, and it is translated to C++ with custom tools so the deployed executable is fast and small.

The translation is described as semi-automated and separate, with the reassuring aside that it often just works. And the contribution bar is stated in the same terms: you only have to make your code work in Python.

That is a real trade and it is worth being clear about which side of it you are on. A contributor is not responsible for the native build, and does not need a C++ toolchain to make a change. In exchange, a change can fail after translation in ways nothing in the Python test suite would have caught, and the person who finds out is not the person who wrote it.

The tree explains how much machinery sits behind that translation. There is a directory for the translation itself, a directory named for a custom garbage-collected runtime, a directory for a parser generator, a directory for an abstract syntax description language, a directory for the C++ output, and a separate one for the C++ runtime support. There is also a Python extension module directory and a directory for a statically linked interpreter.

The readme's contribution guidance reflects the same pragmatism from the other direction. It notes the project is full of many ideas and that this may be intimidating, then says the program is basically a medium-sized Python codebase with many tests and that many programmers know how to change such programs. It calls that a good ground for prototyping.

## Failing tests are merged on purpose, as documentation

The most unusual paragraph in the readme concerns tests that do not pass.

For compatibility with the shell it replaces, the maintainer says failing specification tests are often merged. The reason given is that the tests alone are useful, and the sentence about it is that you do not even have to write code for that contribution to count.

The framing is worth pausing on, because it inverts the usual rule. A failing test is normally a gate, something that blocks a merge until it is fixed. Here a failing test is an accepted artefact: it records a known divergence between this implementation and the one it is measured against, and the search for related cases is done with a plain grep over the spec directory.

For OSH, the compatibility shell whose entire purpose is running scripts written for the original, that is coherent. A test that fails because the original behaves differently is a specification of a difference, and merging it makes the difference visible instead of leaving it as an unwritten caveat. For anyone reading the suite, a red test is therefore not automatically a bug in the change that introduced it.

The suite itself is shell-based, with files named by extension in the spec directory, and the search command given in the readme treats a shell tracing feature as the filter. There is also a separate regression test directory, so there are two distinct layers of testing with different purposes.

Continuous integration runs the whole thing at every commit, which is what makes a large permanently-red suite tolerable: the failures are known, classified and stable rather than intermittent.

## A vendored CPython 2.7.13 is part of the native build

There is a directory at the root of the repository named after a specific CPython release, 2.7.13. That interpreter version dates from 2010.

The build file is where its purpose becomes clear. The native build produces app bundles, described as Python code with a statically linked CPython interpreter, and it also produces a tarball that lets an end user build an app bundle themselves, which requires GNU Make, bash and a C compiler. The comment block documents that tarball's layout in detail, and the interpreter directory is one of its required entries, described as containing a frozen configuration header.

So this is not a stray leftover. The release path depends on a vendored interpreter, and the layout is specified well enough that a third party could rebuild a bundle from the tarball.

That is worth understanding plainly rather than reacting to. A project whose stated goal is to replace a long-lived shell, and whose compatibility promise is measured against decades of accumulated scripts, is building a native artefact that must behave identically regardless of what interpreter a user has installed. Vendoring the interpreter removes that variable. The cost is that a 2010-era interpreter is now part of the shipped surface, and anyone auditing the supply chain of the resulting binary is entitled to ask about it.

Two other build entry points sit alongside the interpreter directory: a hand-written configure script with its own test script, and a shell fragment for configuring the Ninja build. So the build system is bespoke rather than generated by a standard tool.

## Six ways to define the development environment, one of them generated

Count the mechanisms in the tree that describe how to build and run this project, and the number is surprising.

There is a Travis configuration and, beside it, a template file that the configuration appears to be generated from. There is a GitHub Actions workflow referenced by the badge. There is a Gitpod configuration. There is a Vagrantfile. There is a Nix shell definition. There is a directory-initialiser file for a directory-activation tool, and submodules for vendored dependencies.

Six definitions of the same environment, maintained side by side. Each solves a slightly different problem, and a project with this many contributors and this many hosting options will accumulate them rather than choose. The cost is that a contributor does not know which one is authoritative, and that a change to the build has to be made in several places or the definitions drift apart.

The Travis pair is the sharpest example. A generated file that is committed alongside its template is a deliberate choice, and it is defensible when a workflow has many matrix entries, because hand-editing a large generated file is worse. It also means the committed file is not the source of truth, and anyone changing it by hand will find their edit overwritten.

Formatters are configured three times over as well, once each for the languages involved: a style file for Python, a C++ style file, and a configuration for a third formatter. And there is a file listing revisions to be ignored by version control's blame, which is the standard companion to a project that reformats its own code in bulk. The three together suggest a codebase that gets normalised periodically and has arranged for that to be invisible in its history.

## A named maintainer promises a 24 hour response and admits he travels

There is a section with a heading that is a service commitment: a target response time of 24 hours.

The mechanism for invoking it is a single named person and two platforms. If you are waiting for a pull request review, or have a question, you are asked to ping that handle on the project's chat server or on the code host. The readme suggests you might have been missed, and that it does not hurt to ping again.

Then the paragraph qualifies itself, and the qualification is what makes the section trustworthy rather than promotional. The maintainer says he usually responds within 24 hours, and might be travelling, in which case the response may be something like an undertaking to look at it by a named day. An acknowledgement of a day is not a 24 hour response, and the readme says so.

This is a single-maintainer project. One person holds the merge rights, the review queue and the design decisions, and the same person is the one who promises the response time. That is true of a great many successful infrastructure projects, and it is also the project's main concentration of risk, which the readme does not discuss.

What it does offer instead is a low threshold. Issues are labelled for newcomers to pick up, with an explicit request to say what you are thinking before getting too far. The documentation lives in a wiki that anyone may edit. And the design of the newer language is open to influence, with the readme telling would-be contributors to be ambitious and giving a concrete example of a feature to attempt.

The old and new domains are both still in use, which is the small fingerprint of a project that renamed itself and kept its links working.

## Conclusion

Use the tarball releases for anything you depend on, and treat this repository as the place the work happens rather than as a product. That distinction is the readme's own, and it is the first thing to internalise. Three things to check. Which artefact you actually have, because the development build is a Python program you run from the source tree and the released one is a translated C++ binary with no interpreter dependency. Whether a change you make only needs to work in Python to be acceptable, which is the stated bar and which surprises people used to end-to-end native builds. And what the licence terms are, since the repository records none in its metadata while a licence file sits at the root.

## FAQ

### What is oils-for-unix/oils?

It is the source repository for Oils, a project describing itself as an upgrade path from bash to a better language and runtime. It provides two shells from one codebase: one that runs existing shell scripts for compatibility, and one aimed at Python and JavaScript users who avoid shell.

### How do I get Oils if not from the GitHub repository?

The readme says explicitly not to clone the repository to use Oils, and points to the latest release page on the project site instead. There are no GitHub releases, and a wiki page explains the difference between the repository and the tarball releases.

### Does Oils need Python installed to run?

Not the deployed executable. The code is written in Python and translated to C++ so the released program is fast and small and does not depend on Python. The development build in the repository is a Python program, run through bin/osh for the compatibility shell or bin/ysh for the other one.

### What licence is Oils released under?

The repository index records the licence field as not determined, and a licence file is present at the root of the tree. The readme does not summarise the terms, so they are not available from the metadata alone.

### How do I contribute to Oils?

The readme asks you to make a development build first, which it says should take one to five minutes on a Linux machine, and reports problems on the chat channel or as an issue. It also notes that failing specification tests are sometimes merged on purpose for compatibility, so contributing tests without writing code counts.

## Sources

- [Issues](https://github.com/oils-for-unix/oils/issues)
- [oils-for-unix/oils on GitHub](https://github.com/oils-for-unix/oils)
- [Project website](https://oils.pub/)
- [README](https://github.com/oils-for-unix/oils/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/oils-for-unix-oils
