Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .copier-answers.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# WARNING: Do not edit this file manually.
# Any changes will be overwritten by Copier.
_commit: v0.4.7
_commit: v0.5.0-11-g88c9a0e
_src_path: gh:easyscience/templates
lib_docs_url: https://easyscience.github.io/utils
lib_doi: 10.5281/zenodo.18163581
Expand All @@ -10,8 +10,8 @@ lib_python_min: '3.11'
lib_repo_name: utils
project_contact_email: support@easyscience.org
project_copyright_years: '2026'
project_extended_description: For sharing utilities, including reusable classes and
functions, to be used across EasyScience modules
project_extended_description: A shared library of utility classes and helper functions
used across the EasyScience framework
project_homepage_url: https://easyscience.github.io/utils
project_name: EasyUtilities
project_repo_name: utils
Expand Down
1 change: 0 additions & 1 deletion .github/actions/publish-to-pypi/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,3 @@ runs:
- uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: ${{ inputs.packages_dir }}
attestations: false
6 changes: 6 additions & 0 deletions .github/configs/pages-deployment.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"source": {
"branch": "gh-pages",
"path": "/"
}
}
37 changes: 37 additions & 0 deletions .github/configs/rulesets-develop.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "develop branch",
"target": "branch",
"enforcement": "active",
"conditions": {
"ref_name": {
"include": ["refs/heads/develop"],
"exclude": []
}
},
"bypass_actors": [
{
"actor_id": 2476259,
"actor_type": "Integration",
"bypass_mode": "always"
}
],
"rules": [
{
"type": "non_fast_forward"
},
{
"type": "deletion"
},
{
"type": "pull_request",
"parameters": {
"allowed_merge_methods": ["squash"],
"dismiss_stale_reviews_on_push": false,
"require_code_owner_review": false,
"require_last_push_approval": false,
"required_approving_review_count": 0,
"required_review_thread_resolution": false
}
}
]
}
19 changes: 19 additions & 0 deletions .github/configs/rulesets-gh-pages.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "gh-pages branch",
"target": "branch",
"enforcement": "active",
"conditions": {
"ref_name": {
"include": ["refs/heads/gh-pages"],
"exclude": []
}
},
"rules": [
{
"type": "non_fast_forward"
},
{
"type": "deletion"
}
]
}
30 changes: 30 additions & 0 deletions .github/configs/rulesets-master.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "master branch",
"target": "branch",
"enforcement": "active",
"conditions": {
"ref_name": {
"include": ["~DEFAULT_BRANCH"],
"exclude": []
}
},
"rules": [
{
"type": "non_fast_forward"
},
{
"type": "deletion"
},
{
"type": "pull_request",
"parameters": {
"allowed_merge_methods": ["merge"],
"dismiss_stale_reviews_on_push": false,
"require_code_owner_review": false,
"require_last_push_approval": false,
"required_approving_review_count": 0,
"required_review_thread_resolution": false
}
}
]
}
69 changes: 69 additions & 0 deletions .github/scripts/backmerge-conflict-issue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
module.exports = async ({ github, context, core }) => {
// Repo context
const owner = context.repo.owner
const repo = context.repo.repo

// Link to the exact workflow run that detected the conflict
const runUrl = `${context.serverUrl}/${owner}/${repo}/actions/runs/${context.runId}`

// We use a *stable title* so we can find/reuse the same "conflict tracker" issue
// instead of creating a new issue on every failed run.
const title = 'Backmerge conflict: master → develop'

// Comment/issue body includes the run URL so maintainers can jump straight to logs.
const body = [
'Automatic backmerge failed due to merge conflicts.',
'',
`Workflow run: ${runUrl}`,
'',
'Manual resolution required.',
].join('\n')

// Label applied to the tracker issue (assumed to already exist in the repo).
const label = '[bot] backmerge'

// Search issues by title across *open and closed* issues.
// Why: if the conflict was resolved previously and the issue was closed,
// we prefer to reopen it and append a new comment instead of creating duplicates.
const q = `repo:${owner}/${repo} is:issue in:title "${title}"`
const search = await github.rest.search.issuesAndPullRequests({
q,
per_page: 10,
})

// Pick the first exact-title match (search can return partial matches).
const existing = search.data.items.find((i) => i.title === title)

if (existing) {
// If a tracker issue exists, reuse it:
// - reopen it if needed
// - add a comment with the new run URL
if (existing.state === 'closed') {
await github.rest.issues.update({
owner,
repo,
issue_number: existing.number,
state: 'open',
})
}

await github.rest.issues.createComment({
owner,
repo,
issue_number: existing.number,
body,
})

core.notice(`Conflict issue updated: #${existing.number}`)
return
}

// No tracker issue exists yet -> create the first one.
await github.rest.issues.create({
owner,
repo,
title,
body,
labels: [label],
})
}
101 changes: 62 additions & 39 deletions .github/workflows/backmerge.yml
Original file line number Diff line number Diff line change
@@ -1,34 +1,29 @@
# This workflow automatically merges `master` into `develop` whenever a
# new version tag is pushed (v*).
#
# Key points:
# - Directly merges master into develop without creating a PR.
# - Skips CI on the merge commit using [skip ci] in the commit message.
# - The code being merged has already been tested as part of the release process.
# - This ensures develop stays up-to-date with release changes (version bumps, etc.).
#
# Required organization config:
# https://github.com/organizations/easyscience/settings/secrets/actions
# https://github.com/organizations/easyscience/settings/variables/actions
# - Actions secret: EASYSCIENCE_APP_KEY (GitHub App private key PEM)
# - Actions variable: EASYSCIENCE_APP_ID (GitHub App ID)
#
# IMPORTANT:
# The GitHub App must be added to the develop branch ruleset bypass list.

name: Backmerge (master -> develop)
# new version release with a tag is published. It can also be triggered
# manually via workflow_dispatch for cases where an automatic backmerge
# is needed outside of the standard release process.
# If a merge conflict occurs, the workflow creates an issue to notify
# maintainers for manual resolution.

name: Backmerge (master → develop)

on:
push:
tags: ['v*']
release:
types: [published, prereleased]
workflow_dispatch:

permissions:
contents: write
issues: write

concurrency:
group: backmerge-master-into-develop
cancel-in-progress: false

jobs:
backmerge:
runs-on: ubuntu-latest
timeout-minutes: 10

steps:
- name: Checkout repository (for local actions)
Expand All @@ -53,34 +48,62 @@ jobs:
git config user.name "easyscience[bot]"
git config user.email "${{ vars.EASYSCIENCE_APP_ID }}+easyscience[bot]@users.noreply.github.com"

- name: Merge master into develop
- name: Set merge message
run: |
set -euo pipefail
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
MESSAGE="Backmerge: master into develop (manual) [skip ci]"
else
TAG="${{ github.event.release.tag_name }}"
MESSAGE="Backmerge: master (${TAG}) into develop [skip ci]"
fi

echo "Fetching develop branch"
git fetch origin develop:develop
echo "MESSAGE=$MESSAGE" >> "$GITHUB_ENV"
echo "message=$MESSAGE" >> "$GITHUB_OUTPUT"
echo "📝 Merge message: $MESSAGE" | tee -a "$GITHUB_STEP_SUMMARY"

echo "Switching to develop branch"
git checkout develop
- name: Prepare branches
run: |
git fetch origin master develop
git checkout -B develop origin/develop

echo "Checking if already up-to-date"
- name: Check if develop is already up-to-date
id: up_to_date
run: |
if git merge-base --is-ancestor origin/master develop; then
echo "ℹ️ Develop is already up-to-date with master"
exit 0
echo "value=true" >> "$GITHUB_OUTPUT"
echo "ℹ️ Develop is already up-to-date with master" | tee -a "$GITHUB_STEP_SUMMARY"
else
echo "value=false" >> "$GITHUB_OUTPUT"
fi

echo "Preparing merge message"
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
MESSAGE="Backmerge: master into develop (manual) [skip ci]"
else
TAG="${{ github.ref_name }}"
MESSAGE="Backmerge: ${TAG} from master into develop [skip ci]"
- name: Try merge master into develop
id: merge
if: steps.up_to_date.outputs.value == 'false'
continue-on-error: true
run: |
if ! git merge origin/master --no-ff -m "${MESSAGE}"; then
echo "conflict=true" >> "$GITHUB_OUTPUT"
echo "❌ Backmerge conflict detected." | tee -a "$GITHUB_STEP_SUMMARY"
git status --porcelain || true
exit 0
fi

echo "Merging master into develop"
git merge origin/master --no-ff -m "${MESSAGE}"
echo "conflict=false" >> "$GITHUB_OUTPUT"
echo "✅ Merge commit created." | tee -a "$GITHUB_STEP_SUMMARY"

echo "Pushing to develop"
- name: Push to develop (if merge succeeded)
if:
steps.up_to_date.outputs.value == 'false' && steps.merge.outputs.conflict ==
'false'
run: |
git push origin develop
echo "🚀 Backmerge successful: master → develop" | tee -a "$GITHUB_STEP_SUMMARY"

echo "✅ Successfully merged master into develop"
- name: Create issue (if merge failed with conflicts)
if: steps.merge.outputs.conflict == 'true'
uses: ./.github/actions/github-script
with:
github-token: ${{ steps.bot.outputs.token }}
script: |
const run = require('./.github/scripts/backmerge-conflict-issue.js')
await run({ github, context, core })
39 changes: 39 additions & 0 deletions .github/workflows/issues-labels.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Verifies if an issue has at least one of the `[scope]` and one of the
# `[priority]` labels. If not, the bot adds labels with a warning emoji
# to indicate that those labels need to be added.

name: Issue labels check

on:
issues:
types: [opened, labeled, unlabeled]

jobs:
check-labels:
runs-on: ubuntu-latest

steps:
- name: Setup easyscience[bot]
id: bot
uses: ./.github/actions/setup-easyscience-bot
with:
app-id: ${{ vars.EASYSCIENCE_APP_ID }}
private-key: ${{ secrets.EASYSCIENCE_APP_KEY }}

- name: Check for required [scope] label
uses: trstringer/require-label-prefix@v1
with:
secret: ${{ steps.bot.outputs.token }}
prefix: '[scope]'
labelSeparator: ' '
addLabel: true
defaultLabel: '[scope] ⚠️ label needed'

- name: Check for required [priority] label
uses: trstringer/require-label-prefix@v1
with:
secret: ${{ steps.bot.outputs.token }}
prefix: '[priority]'
labelSeparator: ' '
addLabel: true
defaultLabel: '[priority] ⚠️ label needed'
Loading
Loading