{"id":44511318,"url":"https://github.com/cloudogu/pipe-build-lib","last_synced_at":"2026-02-13T15:01:56.703Z","repository":{"id":298856724,"uuid":"1001343986","full_name":"cloudogu/pipe-build-lib","owner":"cloudogu","description":null,"archived":false,"fork":false,"pushed_at":"2026-02-05T12:12:15.000Z","size":427,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-05T23:49:31.063Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Groovy","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cloudogu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-06-13T08:11:19.000Z","updated_at":"2026-01-22T14:27:23.000Z","dependencies_parsed_at":"2025-11-30T09:07:46.895Z","dependency_job_id":null,"html_url":"https://github.com/cloudogu/pipe-build-lib","commit_stats":null,"previous_names":["cloudogu/pipe-build-lib"],"tags_count":26,"template":false,"template_full_name":null,"purl":"pkg:github/cloudogu/pipe-build-lib","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudogu%2Fpipe-build-lib","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudogu%2Fpipe-build-lib/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudogu%2Fpipe-build-lib/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudogu%2Fpipe-build-lib/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cloudogu","download_url":"https://codeload.github.com/cloudogu/pipe-build-lib/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudogu%2Fpipe-build-lib/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29411138,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-13T06:24:03.484Z","status":"ssl_error","status_checked_at":"2026-02-13T06:23:12.830Z","response_time":78,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-02-13T15:01:52.751Z","updated_at":"2026-02-13T15:01:54.698Z","avatar_url":"https://github.com/cloudogu.png","language":"Groovy","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PipeBuildLib - Jenkins Shared Library\n\nThis repository provides a modular and reusable Jenkins Shared Library to manage pipeline logic using Groovy and a class-based approach.\n\n## Contents\n\n- `BasePipe`: Abstract base pipeline class that defines the structure and behavior for stage management.\n- `DoguPipe`: A concrete implementation tailored for Dogu builds.\n- `StageDefinition`: Data class for holding individual stage metadata.\n\n---\n\n##  BasePipe\n\nLocated in `BasePipe.groovy`, this abstract class provides:\n\n### Constructor\n```groovy\nBasePipe(script)\n```\nInitializes the pipeline with a Jenkins `script` context.\n\n---\n\n###  Stage Management\n\n#### `addStage(String name, Closure block, String agentLabel = defaultAgent, boolean parallel = false)`\nAdds a new stage to the pipeline.\n- `agentLabel`: Optional agent label to execute the stage.\n- `parallel`: If true, this stage will be run in parallel with other stages on the same agent.\n\n#### `insertStageAfter(String afterName, String newName, Closure block, String agentLabel = defaultAgent, boolean parallel = false)`\nInserts a stage after a given one.\n\n#### `overrideStage(String name, Closure newBlock, String newAgentLabel = null, Boolean newParallel = null)`\nOverrides an existing stage's logic or properties.\n\n#### `removeStage(String name)`\nRemoves a stage by name.\n\n#### `moveStageAfter(String stageToMove, String targetStage)`\nReorders a stage after another stage.\n\n#### `assignAgentToStage(String name, String agentLabel)`\nAssigns or reassigns an agent to a stage.\n\n#### `assignAgents(Map\u003cString, String\u003e assignments)`\nAssigns multiple stages to different agents using a map.\n\n---\n\n### Execution\n\n#### `run()`\nGroups all added stages by their agent and:\n- Executes sequential and parallel stages on the appropriate agents.\n- Handles empty scripts or stage lists gracefully with debug logs.\n\n---\n\n##  StageDefinition\n\nFound in `StageDefinition.groovy`, used to represent each stage:\n\n```groovy\nclass StageDefinition {\n    String name\n    Closure block\n    String agentLabel\n    boolean parallel\n}\n```\n\n---\n\n### Default `PipelineMode.FULL` Behavior\n\nAll stages added via `StageGroup.stage(...)` are **implicitly executed in `PipelineMode.FULL`**.\n\nThis means:\n- Any stage registered with `stage(...)` will **always run** when the pipeline is executed in `FULL` mode\n- Even if you specify a different `PipelineMode`, `FULL` is automatically added\n\n```groovy\ngroup.stage(\"Build\", PipelineMode.INTEGRATION) { ... }\n```\n\nIf you don't want that behavior you can use:\n\n```groovy\ngroup.raw_stage(\"Build\", PipelineMode.RELEASE) { ... }\n```\n\n---\n\n##  DoguPipe\n\n`DoguPipe.groovy` is a concrete subclass of `BasePipe` tailored to build and release **Dogu** containers.\n\n### Constructor\n```groovy\nDoguPipe(script, Map config)\n```\n\n### Main Method\n\n#### `addDefaultStages()`\n\nRegisters all built-in **Dogu Stage Modules** into agent-scoped `StageGroup`s.\n\nInstead of hardcoding stages, `addDefaultStages()` composes the pipeline by loading\nindependent **stage modules** that each contribute their own stages:\n\n| Module | Responsibility |\n|-------|----------------|\n| `StaticStages` | Linting, shellcheck, markdown, Sonar, unit tests |\n| `IntegrationStages` | Provisioning, setup, build, Trivy, integration tests |\n| `MultinodeStages` | Multi-VM / multi-cluster integration tests |\n| `ReleaseStages` | Triggers the gitflow via pipeline when Integration Mode is RELEASE |\n\nInternally this looks like:\n\n```groovy\naddStageGroup(agentStatic)   { new StaticStages().register(this, it) }\naddStageGroup(agentVagrant) { new IntegrationStages().register(this, it) }\naddStageGroup(agentVagrant) { new ReleaseStages().register(this, it) }\naddStageGroup(agentMultinode){ new MultinodeStages().register(this, it) }\n```\n\n#### `DoguConfig`\n\n`DoguConfig` is the compiled, runtime-ready configuration for a Dogu pipeline.\nIt takes the raw Jenkinsfile config map, applies defaults and normalization,\ninstantiates all required build systems (Git, EcoSystem, Docker, Vagrant, etc.),\nand injects pipeline-specific helpers into them so that the pipeline runs in a\nfully initialized, self-contained build environment.\n\nIn addition, `DoguPipe` transparently exposes all properties of `DoguConfig`\nvia Groovy’s `propertyMissing` mechanism.\n\nThis means:\n\n```groovy\npipe.ecoSystem\npipe.git\npipe.cypressImage\npipe.runIntegrationTests\n```\n\n```groovy\npipe.config.ecoSystem\npipe.config.git\npipe.config.cypressImage\npipe.config.runIntegrationTests\n```\n\nbut without the caller needing to know or care that a DoguConfig object exists.\n\nThis makes DoguPipe behave like a live view of the pipeline configuration\nwhile still keeping all configuration, defaults, and runtime wiring isolated\ninside DoguConfig.\n\n### Additional Utilities\n\n- `executeShellTests()`: Runs Bats-based shell tests.\n- `runCypress()`: Runs Cypress integration tests.\n- `setBuildProperties(List customParams)`: Configures Jenkins parameters.\n- `checkout_updatemakefiles(boolean updateSubmodules)`: Checks out code and updates Makefiles with the latest version.\n\n---\n\n## Basic Usage in Jenkinsfile\n\n```groovy\n@Library('pipebuildlib') _\nimport com.cloudogu.sos.pipebuildlib.DoguPipe\n\ndef pipe = new DoguPipe(this, [\n    doguName: 'my-dogu',\n    runIntegrationTests: true,\n    shellScripts: 'scripts/*.sh'\n])\n\npipe.setBuildProperties()\npipe.addDefaultStages()\npipe.run()\n```\n\n---\n\n##  Notes\n\n- Default agent label is `\"sos\"` if not specified.\n\n\u003e **Library versioning \u0026 class resolution**\n\u003e\n\u003e The versions of `pipe-build-lib`, `ces-build-lib`, and `dogu-build-lib` are **not defined in the Jenkinsfile**.  \n\u003e They are resolved by Jenkins via **Manage Jenkins → Global Pipeline Libraries**, which pins each library to a specific Git ref (branch, tag, or commit).\n\u003e\n\u003e Jenkins loads each shared library into its **own classloader**.  \n\u003e Because of this, **classes from another library are *not visible* unless they are imported**.\n\u003e\n\u003e That means this will crash:\n\u003e\n\u003e ```groovy\n\u003e // ❌ Will fail if ces-build-lib is not imported\n\u003e new Docker(this)\n\u003e ```\n\u003e\n\u003e And this is the correct way:\n\u003e\n\u003e ```groovy\n\u003e @Library(['pipe-build-lib', 'ces-build-lib', 'dogu-build-lib']) _\n\u003e // full path required!\n\u003e new com.cloudogu.ces.cesbuildlib.Docker(this)\n\u003e\n\u003e ```\n\u003e\n\u003e Even though the class has a fully-qualified name, **Jenkins will not load it unless the library is explicitly imported**.\n\u003e Fully-qualified names avoid ambiguity, but they do not bypass Jenkins’ library isolation.\n\n---\n\n\n## 🖥 Agent-Based Stage Groups\n\nStages in PipeBuildLib are not executed individually --- they are\ngrouped into **StageGroups**, and each group is bound to a specific\n**Jenkins agent label**.\n\n``` groovy\naddStageGroup(agentStatic) { group -\u003e ... }\naddStageGroup(agentVagrant) { group -\u003e ... }\naddStageGroup(agentMultinode) { group -\u003e ... }\n```\n\nEach `StageGroup` represents **one execution lane on one Jenkins\nagent**.\n\n### Execution Model\n\n  -----------------------------------------------------------------------\n  Scenario                 What happens\n  ------------------------ ----------------------------------------------\n  Two groups with          Run in **parallel** on different machines\n  **different agent        \n  labels**                 \n\n  Two groups with the      Run **sequentially** on the same machine\n  **same agent label**     \n\n  Multiple stages inside   Run in the order they were registered\n  one group                \n  -----------------------------------------------------------------------\n\n### Why this exists\n\nSome parts of a Dogu build: - Must share a workspace (same agent) - Must\nnot run in parallel (race conditions) - Need more powerful machines (Vagrant,\nDocker, GCP)\n\nOther parts: - Are safe to run independently - Should run in parallel to\nsave time\n\n`StageGroup` encodes that **infrastructure intent** directly into the\npipeline.\n\n### Mental Model\n\nThink of a `StageGroup` as:\n\n\u003e \"A queue of stages that must run on the same machine.\"\n\nDifferent queues → different machines → parallel execution.\n\n## Usage Examples\n\n### Initialize and Run Custom Pipeline\n\n```groovy\nclass MyCustomPipe extends BasePipe {\n    MyCustomPipe(script) {\n        super(script)\n        addStage(\"Checkout\", {\n            script.echo \"Checking out source...\"\n        })\n\n        addStage(\"Build\", {\n            script.echo \"Building...\"\n        }, agentLabel: \"builder\", parallel: false)\n\n        addStage(\"Test\", {\n            script.echo \"Running tests...\"\n        }, parallel: true)\n    }\n}\n```\n\n### In Jenkinsfile\n\n```groovy\n@Library('your-shared-library') _\ndef pipe = new MyCustomPipe(this)\npipe.run()\n```\n\n### Dynamically Modify Stages\n\n```groovy\npipe.overrideStage(\"Build\", {\n    script.echo \"Overridden build step\"\n})\n\npipe.removeStage(\"Test\")\n\npipe.insertStageAfter(\"Checkout\", \"Lint\", {\n    script.echo \"Linting code...\"\n})\n```\n\n### Full Example Usage in Jenkins with custom stage\n```groovy\n@Library([\n  'pipe-build-lib',\n  'ces-build-lib',\n  'dogu-build-lib'\n]) _\n\n// Create instance of DoguPipe with configuration parameters\ndef pipe = new com.cloudogu.sos.pipebuildlib.DoguPipe(this, [\n    doguName           : \"jenkins\",\n\n    // Optional behavior settings\n    shellScripts       : \"resources/startup.sh resources/upgrade-notification.sh resources/pre-upgrade.sh\",\n    dependencies       : [\"cas\", \"usermgt\"],\n    checkMarkdown      : true,\n    runIntegrationTests: true,\n    cypressImage       : \"cypress/included:13.16.1\"\n])\n\n// Set default or custom build parameters (can also pass a list to override defaults)\npipe.setBuildProperties()\n// add default stages based on config map\npipe.addDefaultStages()\n\n// Insert a custom post-integration stage directly after the \"Integration Tests\" stage\npipe.insertStageAfter(\"Integration Tests\", \"Test: Change Global Admin Group\", {\n    def eco = pipe.ecoSystem\n    // Change the global admin group and restart jenkins\n    eco.changeGlobalAdminGroup(\"newAdminGroup\")\n    eco.restartDogu(\"jenkins\")\n    eco.waitForDogu(\"jenkins\")\n\n    // Run Cypress tests again without video/screenshot recording\n    eco.runCypressIntegrationTests([\n        cypressImage     : \"cypress/included:13.16.1\",\n        enableVideo      : false,\n        enableScreenshots: false\n    ])\n})\n\n\n// Run the pipeline – this will execute all previously added stages\npipe.run()\n```\n### Full Example Usage in Jenkins with custom stage and overriding and class usage of shared library\n```groovy\n@Library([\n  'pipe-build-lib',\n  'ces-build-lib',\n  'dogu-build-lib'\n]) _\n\ndef pipe = new com.cloudogu.sos.pipebuildlib.DoguPipe(this, [\n    doguName           : \"portainer\",\n    shellScripts       : \"./resources/startup.sh\",\n    checkMarkdown      : true,\n    doBatsTests        : true,\n    runIntegrationTests: true,\n    doSonarTests       : true\n\n])\n\npipe.setBuildProperties()\npipe.addDefaultStages()\n\n// Get ecosystem object from pipe-build-lib initiated by the pipe-build-lib \ncom.cloudogu.ces.dogubuildlib.EcoSystem eco = pipe.ecoSystem\n\npipe.insertStageAfter(\"Bats Tests\",\"build \u0026 test carp\") {\n    def ctx = pipe.script\n    new com.cloudogu.ces.cesbuildlib.Docker(ctx)\n        .image('golang:1.23')\n        .mountJenkinsUser()\n        .inside('-e ENVIRONMENT=ci')\n    {\n            ctx.sh 'make carp-clean'\n            ctx.sh 'make build-carp'\n            ctx.sh 'make carp-unit-test'\n    }\n}\n\npipe.overrideStage(\"Integration tests\")\n{\n    eco.runCypressIntegrationTests([        enableVideo      : params.EnableVideoRecording,\n                                            enableScreenshots: params.EnableScreenshotRecording,\n                                            cypressImage: pipe.cypressImage])\n    // Test special case with restricted access\n    eco.vagrant.ssh \"sudo etcdctl set /config/portainer/user_access_restricted true\"\n    eco.restartDogu(pipe.doguName)\n    eco.runCypressIntegrationTests([        enableVideo      : params.EnableVideoRecording,\n                                            enableScreenshots: params.EnableScreenshotRecording,\n                                            cypressImage: pipe.cypressImage,\n                                            additionalCypressArgs:\n                                            \"--config '{\\\"excludeSpecPattern\\\": [\\\"cypress/e2e/dogu_integration_test_lib/*\\\"]}'\"])\n}\n\npipe.run()\n```\n\n### Full Example Usage in Jenkins with overriding\n\n```groovy\n#!groovy\n@Library([\n  'pipe-build-lib',\n  'ces-build-lib',\n  'dogu-build-lib'\n]) _\n\ndef pipe = new com.cloudogu.sos.pipebuildlib.DoguPipe(this, [\n    doguName           : 'redmine',\n    shellScripts       : ['''\n                          resources/startup.sh\n                          resources/post-upgrade.sh\n                          resources/pre-upgrade.sh\n                          resources/util.sh\n                          resources/upgrade-notification.sh\n                          resources/default-config.sh\n                          resources/update-password-policy.sh\n                          resources/util.sh\n                          resources/delete-plugin.sh\n                          '''],\n    dependencies       : ['cas', 'usermgt', 'postgresql'],\n    doBatsTests        : true,\n    runIntegrationTests: true,\n    cypressImage       : \"cypress/included:13.14.2\"\n])\ncom.cloudogu.ces.dogubuildlib.EcoSystem ecoSystem = pipe.ecoSystem\n\npipe.setBuildProperties()\npipe.addDefaultStages()\npipe.overrideStage('Setup') {\n  ecoSystem.loginBackend('cesmarvin-setup')\n  ecoSystem.setup([additionalDependencies: ['official/postgresql']])\n}\n\npipe.run()\n```\n\n\n### Additional Dogu Dependencies Setup Stage\n\n\n```groovy\npipe.overrideStage('Setup') {\n    ecoSystem.loginBackend('cesmarvin-setup')\n    def settingsJson = '{\"custom_menu_entries\":[{\"name\":\"Handbuch \\uD83D\\uDD17\",\"url\":\"https://docs.cloudogu.com/de/usermanual/easyredmine/er12/1_administrators_checklist/\",\"icon\":\"icon-help\"}]}'\n    def escapedSettings = settingsJson.replaceAll('\"', '\\\\\\\\\"')\n\n    ecoSystem.setup([\n        additionalDependencies: ['official/mysql', 'official/redis'],\n        // setting custom menu entry for docs\n        registryConfig: \"\"\"\n            \"easyredmine\": {\n                \"logging\": {\n                    \"root\": \"ERROR\"\n                },\n                \"default_data\": {\n                    \"usertype_settings\": \"${escapedSettings}\"\n                }\n            },\n            \"_global\": {\n                \"password-policy\": {\n                    \"must_contain_capital_letter\": \"false\",\n                    \"must_contain_lower_case_letter\": \"true\",\n                    \"must_contain_digit\": \"true\",\n                    \"must_contain_special_character\": \"false\",\n                    \"min_length\": \"1\"\n                }\n            }\n        \"\"\"\n    ])\n}\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcloudogu%2Fpipe-build-lib","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcloudogu%2Fpipe-build-lib","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcloudogu%2Fpipe-build-lib/lists"}