{"id":15140250,"url":"https://github.com/mdolinin/gha-conductor","last_synced_at":"2025-10-23T15:31:54.524Z","repository":{"id":228102025,"uuid":"720504773","full_name":"mdolinin/gha-conductor","owner":"mdolinin","description":null,"archived":false,"fork":false,"pushed_at":"2024-10-29T03:07:52.000Z","size":2783,"stargazers_count":2,"open_issues_count":3,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-10-29T21:10:37.981Z","etag":null,"topics":["github-actions","github-app","monorepo","probot"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mdolinin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","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}},"created_at":"2023-11-18T17:31:51.000Z","updated_at":"2024-10-28T13:07:46.000Z","dependencies_parsed_at":"2024-09-05T17:15:36.694Z","dependency_job_id":"5476c781-bdf9-4ccf-8120-529c8e5d296e","html_url":"https://github.com/mdolinin/gha-conductor","commit_stats":{"total_commits":285,"total_committers":3,"mean_commits":95.0,"dds":0.4736842105263158,"last_synced_commit":"98ae55fc8939947d29a13231305af84cecb364ab"},"previous_names":["mdolinin/gha-conductor"],"tags_count":37,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdolinin%2Fgha-conductor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdolinin%2Fgha-conductor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdolinin%2Fgha-conductor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdolinin%2Fgha-conductor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mdolinin","download_url":"https://codeload.github.com/mdolinin/gha-conductor/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":237852484,"owners_count":19376692,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["github-actions","github-app","monorepo","probot"],"created_at":"2024-09-26T08:01:46.225Z","updated_at":"2025-10-23T15:31:54.518Z","avatar_url":"https://github.com/mdolinin.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# gha-conductor\n\n\u003e A GitHub App built with [Probot](https://github.com/probot/probot) that provide more flexible GitHub Actions workflow for monorepo repositories.\n\n## Why gha-conductor exists\n\nCurrently, GitHub Actions does not support monorepo repositories natively.\nYou can define workflows in `.github/workflows` directory, but if you have a monorepo repository, you might want to run different workflows for different subdirectories.\nThis can be achieved by using `paths` filter in the workflow definition, but it is not very flexible and can be hard to maintain.\n\n## Features\n\n- Define CI/CD workflows closer to the project code (any directory in the repository)\n- Reuse common workflows across multiple projects\n- Trigger workflows based on PR open/updated, closed or merged events\n- Trigger workflows based on what files changed in the pull request\n- Trigger workflows based on what branch the pull request is merged into\n- Trigger workflows based on /slash commands in PR comments (e.g. `/validate param=value`)\n  ![pr-comment-command](./docs/pr-comment-command.png)\n- Reload all hooks from `.gha.yaml` files by adding label `gha-conductor:load` to PR\n- Run multiple workflows in parallel for the same event\n- Report status of the workflow run as GitHub checks\n  ![pr-status-check](./docs/green-pr-checks.png)\n- Aggregate status of the all triggered workflows for the same event and report it as `pr-satus` GitHub check\n  ![pr-status-multiple-workflows](./docs/pr-status-multiple-workflows.png)\n- Provide ability to require all workflows to pass before merging PR (just add `pr-status` check to branch protection rules)\n- Verify changes in `.gha.yaml` files w/o merge into main branch by open PR with changes\n- Re-run all workflows from PR checks page\n- Re-run specific workflow from PR checks page\n- Re-run only failed workflows from PR checks page (save time and resources)\n  ![checks-re-run-options](./docs/checks-re-run-options.png)\n- Sync status of executed workflows with GitHub checks manually and automatically\n  ![pr-status-sync](./docs/pr-status-sync.png)\n- Show workflow logs in PR checks page\n- Link to the workflow run from PR checks page\n- Validate `.gha.yaml` files against JSON schema and check name uniqueness with error messages in PR diff\n  ![pr-diff-yaml-errors](./docs/pr-diff-yaml-errors.png)\n- Define repo specific configuration using probot configuration file `.github/gha-conductor-config.yaml`\n\n## What gha-conductor does\n\nThis app provides a way to define which workflows should be run for each event.\nDuring the workflow run, the app will create corresponding GitHub checks.\n\nCurrently, it supports the following events:\n\n| Event                | GitHub check name  | Description                                                                                                                                              |\n|----------------------|--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `onPullRequest`      | `pr-status`        | `opened`, `rereopened`, `synchronize`, `edited` - when a pull request is opened, reopened, synchronized or the base branch of a pull request was changed |\n| `onBranchMerge`      | `pr-merge`         | `merged` - when a branch is merged into another branch                                                                                                   |\n| `onPullRequestClose` | `pr-close`         | `closed` - when a pull request is closed and not merged                                                                                                  |\n| `onSlashCommand`     | `pr-slash-command` | `issue_comment.created`, `issue_comment.edited` - when a comment with slash command is created or edited                                                 |\n\nIt uses `.gha.yaml` files to define which workflows should be run for each event.\nJson schema for `.gha.yaml` files can be found in `schemas/gha_yaml_schema.json` directory.\n\nExample of `.gha.yaml` file:\n\n```yaml\nmoduleName: example-c\nteamNamespace: domain-b\n\nsharedParams:\n  ROOT_DIR: \"namespaces/domain-b/projects/example-c\"\n\ndefaultFileChangeTrigger: \u0026defaultFileChangeTrigger\n  - \"namespaces/domain-b/projects/example-c/**\"\n\nonPullRequest:\n  - name: build\n    pipelineRef:\n      name: common-job\n    pipelineRunValues:\n      params:\n        COMMAND: make build\n    triggerConditions:\n      fileChangesMatchAny: *defaultFileChangeTrigger\n\n  - name: test\n    pipelineRef:\n      name: common-job\n    pipelineRunValues:\n      params:\n        COMMAND: make test\n    triggerConditions:\n      fileChangesMatchAny:\n        - \"namespaces/domain-b/projects/example-c/tests/test.sh\"\n\nonBranchMerge:\n  - name: release\n    pipelineRef:\n      name: common-job\n    pipelineRunValues:\n      params:\n        COMMAND: \u003e-\n          make release\n    triggerConditions:\n      destinationBranchMatchesAny:\n        - 'main'\n      fileChangesMatchAny: *defaultFileChangeTrigger\n\nonPullRequestClose:\n  - name: cleanup\n    pipelineRef:\n      name: common-job\n    pipelineRunValues:\n      params:\n        COMMAND: \u003e-\n          make clean\n    triggerConditions:\n      destinationBranchMatchesAny:\n        - 'main'\n      fileChangesMatchAny: *defaultFileChangeTrigger\n\nonSlashCommand:\n  - name: validate-before-merge\n    pipelineRef:\n      name: generic-job\n    pipelineRunValues:\n      params:\n        COMMAND: make ${command} ${args} # ${command} and ${args} will be replaced with values from the comment slash command\n    triggerConditions:\n      slashCommands:\n        - \"validate\"\n      fileChangesMatchAny: *defaultFileChangeTrigger\n```\n\nFiles can be places in any directory in the repository.\nApp uses `worfklow_dispatch` event to trigger GitHub Actions workflows.\n\n`fileChangesMatchAny` uses [minimatch](https://github.com/isaacs/minimatch) to match file changes.\nUses default configuration for minimatch.\nIgnores filenames starting with a period. (e.g. `.gha.yaml`)\nIf pattern starts with `!`, it will be ignored.\n\nGitHub Actions workflows should be defined in `.github/workflows` directory and should have `workflow_dispatch` trigger.\nExample of GitHub Actions workflow:\n\n```yaml\nname: \"Common job\"\nrun-name: \"${{ inputs.PIPELINE_NAME }}\"\non:\n  workflow_dispatch: # This is required to be able to trigger the workflow from the app\n    inputs: # extra inputs can be added to the workflow and will be provided by the app from context, params or sharedParams\n      PIPELINE_NAME: # Required to be able to differentiate between jobs\n        required: true\n      SERIALIZED_VARIABLES: # workaround the 10 input limit by serializing the variables into a JSON string\n        required: true\n\npermissions:\n  id-token: write   # This is required for requesting the JWT\n  contents: read    # This is required for actions/checkout\n\njobs:\n  Execute_Task:\n    name: \"${{ github.event.inputs.PIPELINE_NAME }}\" # Required to be able to differentiate between jobs\n    runs-on: ubuntu-latest\n    timeout-minutes: 5 # This is the maximum time the job can run for\n    env:\n      SERIALIZED_VARIABLES: ${{ github.event.inputs.SERIALIZED_VARIABLES }}\n    steps:\n      - name: Load Serialized Variables\n        run: |\n          variables=$(echo $SERIALIZED_VARIABLES | jq -r 'to_entries|map(\"\\(.key)=\\(.value|tostring)\")|.[]')\n          while IFS= read -r line; do\n              echo \"$line\" \u003e\u003e $GITHUB_ENV\n          done \u003c\u003c\u003c \"$variables\"\n      - name: Check out Code\n        uses: actions/checkout@v4\n        with:\n          fetch-depth: 0 # We need to fetch all history so that we can checkout the PR merge commit\n          # We check out github.event.pull_request.merge_commit_sha\n          # to ensure we are testing the exact code that will be merged into the base branch\n          ref: ${{ env.PR_MERGE_SHA }} # Provided via SERIALIZED_VARIABLES\n      - name: Execute Task\n        env:\n          USER_HOME: ${{ github.workspace }}\n        working-directory: ${{ env.ROOT_DIR }}\n        run: ${{ env.COMMAND }}\n```\n\n## Usage\n\n* Install the app on your GitHub account or organization\n* Create `.gha.yaml` files in your repository\n* Define GitHub Actions workflows in `.github/workflows` directory\n* Run the app\n* (First time only) Open PR and add label `gha-conductor:load` to trigger the app to load all `.gha.yaml` files and create corresponding hooks in the database\n* When you create a pull request, merge a branch or close a pull request, the app will trigger the workflows defined in `.gha.yaml` files\n* During the workflow run, the app will create corresponding GitHub checks for each job defined in `.gha.yaml` file\n* (Optional) Update branch protection rules to require successful `pr-status` check before merging\n\n## Examples\n- Example of monorepo setup that uses `gha-conductor` can be found in [mdolinin/mono-repo-example](https://github.com/mdolinin/mono-repo-example) repository.\n\n## How it works\n\nSequence diagram of how the app works for PR event:\n\n```mermaid\nsequenceDiagram\n    participant GitHub\n    participant App\n    participant Workflows\n\n    GitHub-\u003e\u003eApp: Send pull request event\n    App-\u003e\u003eApp: Check what files changed in PR\n    App-\u003e\u003eApp: Find what workflows needs to be triggered\n    App-\u003e\u003eWorkflows: Trigger all requested workflows jobs\n    App-\u003e\u003eGitHub: Create pr-status check\n    loop until all triggered jobs completed\n        Workflows-\u003e\u003eApp: Notify workflow job queued\n        App-\u003e\u003eGitHub: Create check for triggered workflow job\n        Workflows-\u003e\u003eApp: Notify workflow job in_progress\n        App-\u003e\u003eGitHub: Update workflow job check in_progress\n        App-\u003e\u003eGitHub: Update pr-status check in_progress\n        Workflows-\u003e\u003eApp: Notify workflow job completed\n        App-\u003e\u003eGitHub: Update workflow run check completed\n    end\n    App-\u003e\u003eApp: Calculate conclusion\n    App-\u003e\u003eGitHub: Update pr-status check with conclusion\n```    \n\n### When app will not trigger workflows on PR event\n\n- if PR is opened from fork\n- if PR is not mergeable\n- if PR has no files changed\n\n### When app will not trigger workflow on /slash command in PR comment\n\n- if comment made by bot\n- if comment made PR is closed\n- if comment made by user that has no write access to the repository\n- if PR is opened from fork\n- if comment does not contain any slash command in the first line\n\n## Persistence\nApp uses PostgreSQL database to store information about which workflows should be triggered for each event and workflow executions that were triggered.\n- `@databases/pg` is used to interact with the database.\n- `pg-migrations` is used to manage database schema. Migrations are located in `migrations` directory.\n- `@databases/pg-typed` and `@databases/pg-schema-cli` are used to generate TypeScript types and JSON schemas from the database schema. Generated types and schema are located in `src/__generated__` directory.\n\n## Configuration\n\n- App leverages [Probot configuration plugin](https://github.com/probot/octokit-plugin-config) to provide a way to define repo specific configuration or organization wide configuration.\n- App uses `.github/gha-conductor-config.yaml` file to define repo specific configuration.\n- Available configuration options(default values are shown):\n\n```yaml\ngha_hooks_file: .gha.yaml\nworkflow_file_extension: .yaml\n```\n\n- Environment variables can be used to override app configuration options.\n- Available environment variables:\n  - `APP_CONFIG_FILE` - name of the configuration file (default: `gha-conductor-config.yaml`)\n  - `DEFAULT_GHA_HOOKS_FILE_NAME` - name of the file that contains hooks configurations file (default: `.gha.yaml`)\n  - `DEFAULT_WORKFLOW_FILE_EXTENSION` - extension of the GitHub Actions workflow files inside `.github/workflows` folder (default: `.yaml`)\n\n## Setup\n\n```sh\n# Install dependencies\nyarn\n# Generate schemas\nyarn generate\n# Apply db migrations\nyarn db:migrate\n# Generate db schema\nyarn db:generate\n# Run the bot\nyarn start\n# Run tests\nyarn test\n```\n\n## Docker\n\n```sh\n# 1. Build container\ndocker build -t gha-conductor .\n\n# 2. Start container\ndocker run -e APP_ID=\u003capp-id\u003e -e PRIVATE_KEY=\u003cpem-value\u003e -e WEBHOOK_SECRET=\u003csecret-value\u003e -e LOG_LEVEL=info -e DATABASE_URL=\u003cpostgres://user:pass@localhost:5432/postgres\u003e gha-conductor\n```\n\n## Self-hosting\n\n1. [Register a new GitHub App](https://probot.github.io/docs/deployment/#register-the-github-app)\n2. Install the app on your account or organization\n3. Create a new PostgreSQL database\n4. Apply database migrations\n    ```sh\n    export DATABASE_URL=\u003cyour-database-url\u003e\n    yarn db:migrate\n    ```\n5. Set the following environment variables:\n   - `APP_ID` - the ID of the GitHub App\n   - `PRIVATE_KEY` - the private key of the GitHub App, base64 encoded\n   - `WEBHOOK_SECRET` - the secret used to secure webhooks\n   - `LOG_LEVEL` - the log level (default: `info`)\n   - `DATABASE_URL` - the URL of the PostgreSQL database (format: `postgres://user:pass@localhost:5432/postgres`)\n6. Run the app as a Docker container\n    ```sh\n    docker run -e APP_ID=\u003capp-id\u003e -e PRIVATE_KEY=\u003cpem-value\u003e -e WEBHOOK_SECRET=\u003csecret-value\u003e -e LOG_LEVEL=info -e DATABASE_URL=\u003cdb-url\u003e ghcr.io/mdolinin/gha-conductor:latest\n    ```\n7. Update the webhook URL in the GitHub App settings to point to your server\n8. (Optional) Update branch protection rules to require successful `pr-status` check before merging\n\n## Contributing\n\nIf you have suggestions for how gha-conductor could be improved, or want to report a bug, open an issue! We'd love all and any contributions.\n\nFor more, check out the [Contributing Guide](CONTRIBUTING.md).\n\n## License\n\n[ISC](LICENSE) © 2024 mdolinin\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmdolinin%2Fgha-conductor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmdolinin%2Fgha-conductor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmdolinin%2Fgha-conductor/lists"}