Skip to content

beam-campus/bc-gitops

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

31 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

bc_gitops

Hex.pm Hex Docs License Buy Me A Coffee

BEAM-native GitOps reconciler for OTP applications.

bc_gitops brings the GitOps pattern to the BEAM ecosystem. It monitors a Git repository for application specifications and automatically reconciles the running system to match the desired state—deploying new applications, upgrading versions, and removing deprecated ones.

Why bc_gitops?

Traditional GitOps tools like Flux and ArgoCD are built for Kubernetes. But what if you're running a BEAM cluster without Kubernetes? Or you want tighter integration with OTP's powerful release and hot code upgrade capabilities?

bc_gitops provides:

  • Works out of the box - Default runtime fetches packages from hex.pm and git, compiles, and starts them
  • Native BEAM integration - Works directly with OTP applications, releases, and supervision trees
  • Hot code upgrades - Automatic module reloading with process suspension/resumption
  • Erlang & Elixir support - Fetches and compiles both Erlang (rebar3) and Elixir (mix) packages
  • Flexible runtimes - Pluggable backend for custom deployment strategies
  • Observable - Built-in telemetry events for monitoring and alerting
  • Minimal dependencies - Only requires telemetry, no external services needed

Installation

Add bc_gitops to your list of dependencies in rebar.config:

{deps, [
    {bc_gitops, "0.2.0"}
]}.

Or for Elixir projects in mix.exs:

def deps do
  [
    {:bc_gitops, "~> 0.2.0"}
  ]
end

Quick Start

1. Create a GitOps Repository

Create a git repository with your application specifications:

my-gitops-repo/
├── apps/
│   ├── my_web_app/
│   │   └── app.config
│   └── my_worker/
│       └── app.config
└── README.md

Each application has a configuration file:

%% apps/my_web_app/app.config
#{
    name => my_web_app,
    version => <<"1.0.0">>,
    source => #{
        type => hex
    },
    env => #{
        port => 8080,
        pool_size => 10
    },
    health => #{
        type => http,
        port => 8080,
        path => <<"/health">>
    },
    depends_on => []
}.

2. Configure bc_gitops

Add configuration to your sys.config:

{bc_gitops, [
    {repo_url, "https://github.com/myorg/my-gitops-repo.git"},
    {branch, "main"},
    {reconcile_interval, 60000},  %% 1 minute
    {runtime_module, my_app_runtime}
]}

3. Start the Application

The default runtime (bc_gitops_runtime_default) handles everything:

  • Fetches packages from hex.pm using rebar3 (Erlang) or mix (Elixir)
  • Clones and compiles git repositories
  • Performs hot code reloading during upgrades
  • Manages code paths automatically
application:start(bc_gitops).

bc_gitops will:

  1. Clone/pull the repository
  2. Parse application specifications
  3. Compare desired state with current state
  4. Deploy, upgrade, or remove applications as needed
  5. Repeat on the configured interval

Configuration Options

Option Type Default Description
repo_url string required Git repository URL
local_path string /var/lib/bc_gitops Local clone path
branch string "main" Git branch to track
apps_dir string "apps" Directory containing app specs
reconcile_interval integer 60000 Reconcile interval (ms)
runtime_module atom bc_gitops_runtime_default Runtime implementation

Application Specification Format

Erlang Format (.config)

#{
    name => my_app,
    version => <<"1.2.3">>,
    source => #{
        type => hex,          %% hex | git | release
        url => <<"...">>,     %% For git/release
        ref => <<"main">>,    %% For git (branch/tag/commit)
        sha256 => <<"...">>   %% For release (integrity check)
    },
    env => #{
        key => value
    },
    depends_on => [other_app],
    health => #{
        type => http,         %% http | tcp | custom
        port => 8080,
        path => <<"/health">>,
        interval => 30000,
        timeout => 5000
    }
}.

JSON Format (.json)

{
    "name": "my_app",
    "version": "1.2.3",
    "source": {
        "type": "hex"
    },
    "env": {
        "key": "value"
    },
    "depends_on": [],
    "health": {
        "type": "http",
        "port": 8080,
        "path": "/health"
    }
}

API

Manual Operations

%% Trigger immediate reconciliation
bc_gitops:reconcile().
bc_gitops:sync().  %% alias

%% Get status
{ok, Status} = bc_gitops:status().
%% #{status => synced, last_commit => <<"abc123">>, app_count => 5, healthy_count => 5}

%% Get state
{ok, DesiredState} = bc_gitops:get_desired_state().
{ok, CurrentState} = bc_gitops:get_current_state().

%% Check specific app
{ok, AppState} = bc_gitops:get_app_status(my_app).

Manual Deployment

%% Deploy manually (bypasses git)
AppSpec = #app_spec{name = my_app, version = <<"1.0.0">>, ...},
bc_gitops:deploy(AppSpec).

%% Remove an app
bc_gitops:remove(my_app).

%% Upgrade to specific version
bc_gitops:upgrade(my_app, <<"2.0.0">>).

Programmatic Start

%% Start reconciler with custom config
bc_gitops:start_reconciler(#{
    repo_url => <<"https://github.com/myorg/gitops.git">>,
    runtime_module => my_runtime
}).

%% Stop reconciler
bc_gitops:stop_reconciler().

Telemetry Events

bc_gitops emits the following telemetry events:

Event Measurements Metadata
[bc_gitops, reconcile, start] - -
[bc_gitops, reconcile, stop] duration status
[bc_gitops, reconcile, error] duration error
[bc_gitops, deploy, start] - app
[bc_gitops, deploy, stop] - app, result
[bc_gitops, upgrade, start] - app, from_version, to_version
[bc_gitops, upgrade, stop] - app, result
[bc_gitops, git, pull] - repo, branch

Example handler:

telemetry:attach(
    <<"gitops-logger">>,
    [bc_gitops, reconcile, stop],
    fun(_Event, Measurements, Metadata, _Config) ->
        logger:info("Reconcile completed in ~p ms: ~p",
                    [Measurements, Metadata])
    end,
    []
).

Default Runtime Features

The default runtime (bc_gitops_runtime_default) is fully functional out of the box:

Package Fetching

  • Hex packages: Automatically fetched via rebar3 or mix
  • Git repositories: Cloned, compiled, and loaded
  • Code path management: Adds compiled ebin directories to the VM

Hot Code Reloading

During upgrades, bc_gitops:

  1. Suspends processes using sys:suspend/1
  2. Reloads changed modules with code:soft_purge/1 + code:load_file/1
  3. Resumes processes (triggering code_change/3 callbacks)
  4. Falls back to restart if hot reload fails

Supported Project Types

  • rebar3 - Erlang projects with rebar.config
  • mix - Elixir projects with mix.exs
  • erlang.mk - Projects using Makefile

Implementing Custom Runtimes

For production deployments with specific requirements, implement the bc_gitops_runtime behaviour:

-module(my_app_runtime).
-behaviour(bc_gitops_runtime).

-export([deploy/1, remove/1, upgrade/2, reconfigure/1, get_current_state/0]).

deploy(AppSpec) ->
    %% Download from private artifact repository
    %% Integrate with service discovery
    %% Handle secrets injection
    {ok, AppState}.

remove(AppName) ->
    %% Deregister from service discovery
    %% Clean up resources
    ok.

upgrade(AppSpec, OldVersion) ->
    %% Use release_handler for OTP releases
    %% Or custom upgrade logic
    {ok, AppState}.

reconfigure(AppSpec) ->
    %% Hot config reload
    {ok, AppState}.

get_current_state() ->
    %% Return current state of all managed apps
    {ok, #{}}.

See the Runtime Guide for detailed examples.

Git Authentication

bc_gitops uses the system git command, so authentication works through standard git mechanisms:

  • SSH keys: Add your deploy key to ~/.ssh/ or use ssh-agent
  • HTTPS: Use git credential helpers or embed credentials in URL (not recommended)
  • GitHub Actions: Use $GITHUB_TOKEN with credential helper

For private repositories, we recommend SSH deploy keys with read-only access.

Architecture

bc_gitops Architecture

Hot Code Reload Flow

During upgrades, bc_gitops can perform hot code reloading:

Hot Reload Flow

The reconciler follows a continuous loop:

  1. Pull - Fetch latest changes from git repository
  2. Parse - Read application specifications from apps/ directory
  3. Diff - Compare desired state (git) with current state (runtime)
  4. Apply - Execute actions: deploy, upgrade, remove, or reconfigure

Contributing

Contributions are welcome! Please read our Contributing Guide before submitting a PR.

License

MIT License - see LICENSE for details.

Acknowledgments

  • Inspired by Flux and ArgoCD
  • Built on the rock-solid foundation of OTP
  • Thanks to the BEAM community for their continuous inspiration

About

No description, website, or topics provided.

Resources

License

Contributing

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •