cyberark/bash-lib: shared shell functions for pipeline-heavy repositories
Library for bash utility methods and tools
At a glance
- What is it?
- This is a small library of bash functions that belong in a shared place because several repositories run the same pipeline steps, with an init script, a directory per concern and a strict rule about pinning versions. It is useful, and the README rates it alpha with some libraries still placeholders.
- Who is it for?
- bash-lib fits a team whose CI pipelines share the same shell plumbing across several repositories and who want one reviewed copy of it, with the tooling to pin and update deliberately. It is the wrong choice for a single-repository project, and it is the wrong choice if you need a complete logging or Kubernetes helper today, since the README calls several libraries placeholders.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 78 days ago.
- What is it written in?
- Mainly Shell, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A shared toolbox for shell functions used in pipelines
The README states the purpose in one line: the place to store functions that are used in pipelines for multiple repos. That is the whole scope. This is not a framework, it is a drawer of shell helpers that several repositories would otherwise each write for themselves.
The origin is a pipeline problem. The repository comes from CyberArk, and the README links an introductory blog post on the Conjur site titled Stop Bashing Bash, which is the argument for moving shell logic out of ad-hoc scripts and into something shared and tested. The topic tags on the repository are bash, utility and conjbot-notify, and the top-level directories confirm the shape: filehandling, git, github, helpers, k8s, logging and ruby, each one a library directory.
The invitation at the top of the README is to add whatever is useful, with the condition that it be kept tidy so it stays useful to everyone else. That is a library rather than a personal toolbox, which is why the pinning rules below exist.
The scale is worth keeping in mind. This is a set of shell functions with a test harness, not a product, and the README is candid that it is at alpha status.
Pinning the version is the only stated requirement
The usage section gives one hard rule: you must pin the version of bash-lib that you use. The stated reason is that changes to bash-lib must not have the power to break all projects that use it, and the stated workflow is that your project can test updates to bash-lib and roll forward periodically.
The README then lays out three ways to bring the library into a project, and the differences between them are the real content of that section.
A submodule adds a pointer from a mount point in your repository to the external repository, so a single SHA is used until you update it manually. The cost is workflow changes, since the pointer has to be dereferenced during clone, checkout and other operations. A subtree copies the external repository into a subdirectory of the host repository, which needs no workflow changes and keeps a single version until explicitly updated, but the README warns that subtree merge commits do not rebase well and suggests keeping subtree updates in separate pull requests from normal commits. Cloning in the deployment process is the third option, and it is the one where the pinning rule bites hardest, because the README states that a clone tracks master by default, which is not an allowed use of bash-lib.
That is a sharp constraint for a CI tool. A pipeline that clones the library at run time gets whatever master points at, which is precisely the failure mode the pinning rule exists to prevent.
init sets the path, and you source one lib per library
Once the library is in place, two things are sourced. First `bash-lib/init`, which the README says ensures submodules are initialised and sets the `BASH_LIB_DIR` environment variable to the absolute path of the bash-lib directory, which makes it easy to source libraries from other scripts. Second, `${BASH_LIB_DIR}/lib-name/lib` for whichever libraries you want.
The structure section explains the convention the second step relies on. The repository is organised into libraries, each library being a directory that has a lib file, and sourcing the lib for a library exposes all the functions that library offers. The lib file may source or reference other supporting files within its own directory, so a library is self-contained rather than a single flat file of functions.
The tree in the README shows how small the unit of organisation is:
.
├── libname
│ ├── lib
│ └── supporting-file
├── init # init script, source this first
├── run-tests # top level test script, executes all tests
├── secrets.yml # secrets required for executing tests
├── test-utilsSo adding a library means adding a directory with a `lib` file, and consumers pick it up by name. The cost of that design is that there is no registry, no dependency resolution and no versioning per library; you take the whole library at the version you pinned.
The git library is where the real functions live
The contents table in the README lists the functions of each library, and the git library is the most detailed. It includes `bl_git_available`, which is true if a git binary or function is available, `bl_in_git_repo`, which is true if the current directory is a git working directory, `bl_repo_root`, which finds the root of the current repository, and `bl_github_owner_repo`, which returns the owner and repository extracted from the url of the origin remote.
The remote-related functions are the interesting cluster, because they exist to make pinning enforceable. `bl_remote_latest_tag` returns the symbolic name of the latest tag from a remote, `bl_remote_latest_tagged_commit` returns the SHA of the most recently tagged commit in a remote repository using the `tag^{}` form, `bl_remote_sha_for_ref` returns the SHA for a given ref from a named remote, and `bl_remote_tag_for_sha` goes the other way, returning the tag corresponding to a SHA from a named remote if there is one. A pipeline can therefore resolve the newest tag to a commit and then use that exact SHA.
There is also a subtree-aware pair. `bl_tracked_files_excluding_subtrees` lists files tracked by git but excluding any files in paths listed in a `.gittrees` file, and `bl_gittrees_present` succeeds if that file is present in the root of the repository. The `.gittrees` file is in the top-level entries of this repository, and the filehandling library separately provides `bl_abs_path`, which ensures a path is absolute.
The set is small, and it is small on purpose. These are the primitives a pipeline needs to know where it is and which commit it is running, and the alternative was every repository writing its own version.
Tests are bats, with a gitleaks pass and a python lint
The test setup is the part of the repository that says how seriously the functions are meant to be taken. The `test-utils` directory holds bats, bats-assert-1 and bats-support as git subtrees, alongside a lib directory and tap2junit, and the `tests-for-this-repo` directory holds the actual suites, with filehandling.bats as one of them, a fixtures directory holding a libname directory, and the scripts `run-bats-tests`, `run-gitleaks` and `run-python-lint`.
The gitleaks script is the interesting one. A library of shell functions that runs inside pipelines, with a `secrets.yml` file at the root for secrets required to execute the tests, is a natural place to leak credentials, and a scan for leaked secrets as part of the test run addresses that directly.
A top-level `run-tests` script executes all tests, and the CI setup is a Jenkinsfile at the root, with .github and CONTRIBUTING.md alongside it. The use of tap2junit suggests the bats output is converted into JUnit XML so that a CI system can display results per test rather than a single pass or fail line, which is what a shared library needs when many repositories consume it.
Alpha status with placeholder libraries
The README labels the project with a release status of Alpha, and the summary line under it is precise: ready for use, but needs expansion. The elaboration is that the functions in the repository are tested and ready for use, but certain libraries are pretty much place holders, for example logging, and those need further contributions before they provide a comprehensive solution.
That is a useful warning to read before picking a library directory. The git and filehandling libraries are described function by function, so you can see what they do. The logging library is described as a placeholder, which means a pipeline that needs structured logging from this library should expect to write that part itself.
The licensing is Apache 2.0, with the LICENSE file at the root, and the repository also carries a CyberArk_Open_Source_Contributor_Agreement.pdf alongside CONTRIBUTING.md, which is the agreement a contributor signs rather than a licence term. The SECURITY.md file suggests there is a reporting channel for security issues in the library.
For an organisation evaluating this, that combination is the deciding factor. Apache 2.0 is a permissive licence with patent grant and a clear notice requirement, and the contributor agreement is the part to read if you intend to send patches rather than only consume the library.
The last tagged release is from 2021 while the branch is newer
The maintenance picture needs two dates read together. The repository is not archived and the last push was on 2026-08-06, so there has been recent activity on master. The published releases are much older: v2.0.6 with the change to pass SHELLCHECK_OPTS to Shellcheck from 2021-08-23, v2.0.5 excluding submodules from git repo file lists from 2021-02-22, and v2.0.4 fixing automatic ticket creation from 2020-09-25.
So the last tagged release is more than five years old, while the branch has moved on. A CHANGELOG.md is in the repository, so the history between the tag and master is documented somewhere, but there is no GitHub release to point at.
That combination makes the pinning instruction harder to follow than it looks. The version you can pin from a tag is v2.0.6, which is old, and the code you would get from master is newer and unpinned by definition, since the README calls tracking master a disallowed use. Any team adopting this needs to decide which side of that gap it lives on, and the tag names suggest the library's own release process was not running at the end.
The shellcheck reference in the v2.0.6 release note also tells you the project was linted with Shellcheck, which is a reasonable expectation for a shell library that will be sourced into other people's pipelines.
Editorial conclusion
bash-lib fits a team whose CI pipelines share the same shell plumbing across several repositories and who want one reviewed copy of it, with the tooling to pin and update deliberately. It is the wrong choice for a single-repository project, and it is the wrong choice if you need a complete logging or Kubernetes helper today, since the README calls several libraries placeholders. Verify first that the functions you need exist in the library directory you expect, pin a tagged version rather than a branch, and read the Apache 2.0 licence and the CyberArk contributor agreement before contributing back.
Frequently asked questions
How do I add cyberark/bash-lib to my project?
The README lists three options: a git submodule, a git subtree, or cloning the library during the deployment process. In every case the requirement is the same, pinning the version of bash-lib that you use, and the README notes that a clone tracks master by default, which is not an allowed use of the library.
What does the bash-lib init script do?
Sourcing bash-lib/init ensures submodules are initialised and sets the BASH_LIB_DIR environment variable to the absolute path of the library root. You then source ${BASH_LIB_DIR}/lib-name/lib for each library you want, and sourcing a lib file exposes all the functions that library offers.
Which git helper functions does bash-lib provide?
The contents table lists bl_git_available, bl_in_git_repo, bl_repo_root, bl_github_owner_repo, bl_all_files_in_repo, bl_remote_latest_tag, bl_remote_latest_tagged_commit, bl_remote_sha_for_ref, bl_remote_tag_for_sha, bl_tracked_files_excluding_subtrees, bl_gittrees_present and bl_cat_gittrees. The filehandling library adds bl_abs_path.
Is bash-lib finished?
The README marks the release status as Alpha with the summary that it is ready for use but needs expansion, and it says certain libraries are pretty much place holders, naming logging as an example, and that those need further contributions before they provide a comprehensive solution.
How are bash-lib functions tested?
Tests use bats, with bats, bats-assert-1 and bats-support vendored as git subtrees under test-utils alongside tap2junit. The tests-for-this-repo directory holds the .bats files, fixtures and the run-bats-tests, run-gitleaks and run-python-lint scripts, and a top-level run-tests script executes the whole suite with a Jenkinsfile for CI.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/cyberark-bash-lib)Was this useful?