Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make CI Configuration Branch-Agnostic and Reusable #31

Open
thorwhalen opened this issue Dec 3, 2024 · 1 comment
Open

Make CI Configuration Branch-Agnostic and Reusable #31

thorwhalen opened this issue Dec 3, 2024 · 1 comment

Comments

@thorwhalen
Copy link
Member

thorwhalen commented Dec 3, 2024

Summary

The current CI configuration (see actions and example CI hardcodes the master branch in several places, such as:

uses: i2mint/isee/actions/install-packages@master

and

if: "!contains(github.event.head_commit.message, '[skip ci]') && github.ref == 'refs/heads/master'"

This approach makes the CI non-reusable across projects where the default branch may be named differently (e.g., main, dev, etc.). To address this, we propose a solution that allows branch specification to be more flexible, enabling reusability and adaptability for different repository configurations.

Problem Statement

Hardcoding the branch name as master introduces the following limitations:
1. Incompatibility with projects using different default branch names, such as main or dev.
2. Difficulty in maintaining and reusing CI across multiple repositories without customization.
3. Inability to adapt to changes in the default branch of a repository over time.

Proposed Solutions

To address these limitations, we propose implementing a flexible system for specifying branches. This system can be a combination of the following solutions:

1.	Specify CI-triggering branch as a variable:

Allow the branch to be defined in the env section or as an input variable, making it easy to change across different repositories. Example:

env:
  DEFAULT_BRANCH: main
2.	Support multiple branch names:

Enable specifying an array of branch names, where the CI uses the first one found. This approach would allow fallbacks. Example:

env:
  TRIGGER_BRANCHES: ["main", "master"]
3.	Default to the repository’s default branch:

Detect the repository’s default branch dynamically during the CI run and use that as the trigger. This would make the CI even more robust. For example, a reserved variable like $default_branch could represent the repository’s default branch, and the CI would resolve it at runtime.

4.	Combine approaches for maximum flexibility:

Allow users to define branches in a hierarchical manner, e.g., supporting a mix of explicit branch names and the dynamic $default_branch. Example:

env:
  BRANCH_PREFERENCES: ["main", "master", "$default_branch"]

Implementation Considerations

•	Backward Compatibility: The default branch variable should have a sensible fallback (e.g., defaulting to master if unspecified).
•	Performance: Detecting the default branch dynamically should be efficient and not impact CI runtime significantly.
•	User Experience: Provide clear documentation and examples to make configuring branch preferences intuitive.

Benefits

•	Increased reusability of the CI configuration across repositories.
•	Easier adaptation to repositories with non-standard default branch names.
•	Improved maintainability by avoiding hardcoded branch names.
•	Future-proofing the CI to handle potential changes in branch naming conventions.

Next Steps

1.	Discuss and finalize the preferred implementation strategy (variable-based, detection-based, or a blend).
2.	Develop and test the changes to support branch-agnostic configurations.
3.	Update documentation with clear instructions and examples.

Let us know your thoughts and preferences on the proposed solutions!

This issue template lays out the problem clearly, proposes multiple solutions, and highlights the benefits.

@thorwhalen
Copy link
Member Author

thorwhalen commented Dec 3, 2024

Here are detailed solutions to resolve the issue, each addressing a specific aspect of branch flexibility and reusability:

Solution 1: Specify CI-Triggering Branch as a Variable

Approach:

Define the branch as a variable in the env or inputs section of the CI configuration. The branch name can then be used dynamically throughout the workflow.

Example:

env:
  DEFAULT_BRANCH: main

jobs:
  validation:
    if: "!contains(github.event.head_commit.message, '[skip ci]') && github.ref == 'refs/heads/${{ env.DEFAULT_BRANCH }}'"

Pros:

•	Simple and explicit.
•	Easy to modify for different repositories.
•	Minimal performance overhead.

Cons:

•	Requires manual updates if the default branch changes.
•	No fallback if the branch name is incorrect or not defined.

Solution 2: Support Multiple Branch Names

Approach:

Allow an array of branch names, and use the first one that matches the current repository setup. This can be implemented using GitHub Actions’ conditional logic.

Example:

env:
  TRIGGER_BRANCHES: ["main", "master"]

jobs:
  validation:
    if: "!contains(github.event.head_commit.message, '[skip ci]') && (contains(github.ref, 'refs/heads/${{ env.TRIGGER_BRANCHES[0] }}') || contains(github.ref, 'refs/heads/${{ env.TRIGGER_BRANCHES[1] }}'))"

Pros:

•	Provides fallback options if the first branch doesn’t match.
•	Accommodates repositories with varying branch naming conventions.

Cons:

•	Slightly more complex to configure and maintain.
•	Requires explicit enumeration of all possible branch names.

Solution 3: Detect the Default Branch Dynamically

Approach:

Use GitHub’s REST API to retrieve the repository’s default branch at runtime. This can be done via a step early in the workflow that sets the default branch as an environment variable.

Example:

1.	Add a Step to Detect Default Branch:
  • name: Get Default Branch
    id: get-default-branch
    run: echo "DEFAULT_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@')" >> $GITHUB_ENV

    1. Use the Default Branch Dynamically:
jobs:
  validation:
    if: "!contains(github.event.head_commit.message, '[skip ci]') && github.ref == 'refs/heads/${{ env.DEFAULT_BRANCH }}'"

Pros:

•	Automatically adapts to the repository’s default branch.
•	No manual updates needed for branch changes.

Cons:

•	Requires an additional step, increasing workflow complexity.
•	Slight performance overhead due to API call or Git command.

Solution 4: Combine Approaches

Approach:

Blend the previous solutions for maximum flexibility:
• Use a variable to specify preferred branches.
• Include $default_branch as a placeholder that dynamically resolves to the repository’s default branch.

Example:

1.	Define Branch Preferences:
env:
  BRANCH_PREFERENCES: ["main", "master", "$default_branch"]
2.	Resolve Branch Dynamically if Needed:
- name: Resolve Default Branch
  if: contains('${{ env.BRANCH_PREFERENCES }}', '$default_branch')
  run: echo "DEFAULT_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@')" >> $GITHUB_ENV
3.	Use the Resolved or Predefined Branch:
jobs:
  validation:
    if: "!contains(github.event.head_commit.message, '[skip ci]') && contains(github.ref, 'refs/heads/${{ env.DEFAULT_BRANCH }}')"

Pros:

•	Highly flexible and adaptable to varying setups.
•	Combines explicit control with dynamic resolution.

Cons:

•	Most complex to implement and maintain.

Implementation Recommendations

1.	Start Simple:

For immediate needs, implement Solution 1 or Solution 2 to allow basic customization with minimal changes.
2. Add Dynamic Detection:
Implement Solution 3 or a hybrid (Solution 4) when supporting a large number of repositories or needing adaptability for unknown setups.
3. Document Usage:
Clearly document how to use the chosen solution, including examples for defining variables and fallback strategies.

These solutions provide flexibility and scalability, making the CI reusable across various repositories while reducing the need for manual configuration updates.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant