Skip to content

Implement module system for composable CI configuration

Summary

This MR implements a module system for gitlab.ci that enables composable CI configuration. Multiple modules can now contribute stages and jobs that are automatically merged together.

This is the first step toward the architecture described in issue #2 (closed), allowing us to extract common CI functionality (omnix, cachix, discover, etc.) into separate reusable modules.

Key Changes

  • Add gitlab.ci.modules option that accepts a list of modules
  • Each module can define stages (list) and jobs (attrset)
  • Module evaluation uses lib.evalModules with proper specialArgs
  • Stages are concatenated and deduplicated
  • Jobs are merged together (later modules override earlier ones)
  • Make stages and jobs options have sensible defaults

Example Usage

perSystem = { pkgs, ... }: {
  gitlab.ci = {
    enable = true;
    modules = [
      # Omnix devour module
      ({ pkgs, ... }: {
        stages = [ "build" ];
        jobs.omnix-devour = {
          stage = "build";
          script = [ "${pkgs.omnix}/bin/om ci run" ];
        };
      })
      
      # Cachix push module
      ({ pkgs, ... }: {
        stages = [ "publish" ];
        jobs.cachix-push = {
          stage = "publish";
          script = [ "cachix push horizon" ];
        };
      })
    ];
  };
};

Backward Compatibility

The existing direct stages and jobs options still work - they are merged with module-contributed stages and jobs.

Next Steps

This enables us to:

  1. Extract common patterns into standalone modules
  2. Allow projects to compose CI functionality as needed
  3. Make horizon-gen-nix and other tools ship their own CI modules
  4. Reduce duplication across projects

🤖 Generated with Claude Code

Merge request reports