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
20 changes: 13 additions & 7 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ jobs:
build_related: ${{ steps.filter.outputs.build_related }}
template_assets_related: ${{ steps.filter.outputs.template_assets_related }}
renovate_related: ${{ steps.filter.outputs.renovate_related }}
tooling_related: ${{ steps.filter.outputs.tooling_related }}
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
Expand All @@ -75,6 +76,7 @@ jobs:
echo "build_related=true" >> "${GITHUB_OUTPUT}"
echo "template_assets_related=true" >> "${GITHUB_OUTPUT}"
echo "renovate_related=true" >> "${GITHUB_OUTPUT}"
echo "tooling_related=true" >> "${GITHUB_OUTPUT}"
exit 0
fi

Expand All @@ -91,6 +93,7 @@ jobs:
echo "build_related=true" >> "${GITHUB_OUTPUT}"
echo "template_assets_related=true" >> "${GITHUB_OUTPUT}"
echo "renovate_related=true" >> "${GITHUB_OUTPUT}"
echo "tooling_related=true" >> "${GITHUB_OUTPUT}"
exit 0
fi

Expand All @@ -100,6 +103,7 @@ jobs:
echo "build_related=true" >> "${GITHUB_OUTPUT}"
echo "template_assets_related=true" >> "${GITHUB_OUTPUT}"
echo "renovate_related=true" >> "${GITHUB_OUTPUT}"
echo "tooling_related=true" >> "${GITHUB_OUTPUT}"
exit 0
fi
else
Expand All @@ -108,6 +112,7 @@ jobs:
echo "build_related=true" >> "${GITHUB_OUTPUT}"
echo "template_assets_related=true" >> "${GITHUB_OUTPUT}"
echo "renovate_related=true" >> "${GITHUB_OUTPUT}"
echo "tooling_related=true" >> "${GITHUB_OUTPUT}"
exit 0
fi
fi
Expand All @@ -117,21 +122,21 @@ jobs:
build_related=false
template_assets_related=false
renovate_related=false
tooling_related=false

while IFS= read -r path; do
[[ -z "${path}" ]] && continue
case "${path}" in
Dockerfile|upstream.toml|rootfs/*|scripts/*|openmemory/*)
Dockerfile|upstream.toml|rootfs/*|openmemory/*)
build_related=true
;;
*.xml|assets/*)
template_assets_related=true
;;
renovate.json)
renovate_related=true
scripts/*|.github/workflows/*)
tooling_related=true
;;
.github/workflows/*)
build_related=true
renovate.json)
renovate_related=true
;;
esac
Expand All @@ -140,10 +145,11 @@ jobs:
echo "build_related=${build_related}" >> "${GITHUB_OUTPUT}"
echo "template_assets_related=${template_assets_related}" >> "${GITHUB_OUTPUT}"
echo "renovate_related=${renovate_related}" >> "${GITHUB_OUTPUT}"
echo "tooling_related=${tooling_related}" >> "${GITHUB_OUTPUT}"

validate-repo:
needs: detect-changes
if: ${{ needs.detect-changes.outputs.build_related == 'true' || needs.detect-changes.outputs.template_assets_related == 'true' || needs.detect-changes.outputs.renovate_related == 'true' }}
if: ${{ needs.detect-changes.outputs.build_related == 'true' || needs.detect-changes.outputs.template_assets_related == 'true' || needs.detect-changes.outputs.renovate_related == 'true' || needs.detect-changes.outputs.tooling_related == 'true' }}
runs-on: ubuntu-latest
permissions:
contents: read
Expand All @@ -154,7 +160,7 @@ jobs:
submodules: recursive

- name: Validate shell and python scripts
if: ${{ needs.detect-changes.outputs.build_related == 'true' }}
if: ${{ needs.detect-changes.outputs.build_related == 'true' || needs.detect-changes.outputs.tooling_related == 'true' }}
run: |
bash -n scripts/smoke-test.sh
find rootfs -type f -name '*.sh' -print0 | xargs -0 -n1 bash -n
Expand Down
76 changes: 56 additions & 20 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
name: Release / mem0-aio
name: Release / Mem0-AIO

on:
workflow_dispatch:
pull_request_target:
types: [closed]
inputs:
action:
description: "Release action to run"
required: true
default: prepare
type: choice
options:
- prepare
- publish

jobs:
prepare-release:
if: ${{ github.ref == 'refs/heads/main' }}
if: ${{ github.event.inputs.action == 'prepare' && github.ref == 'refs/heads/main' }}
runs-on: ubuntu-latest
permissions:
contents: write
Expand All @@ -17,6 +24,7 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0

- name: Install git-cliff
env:
GIT_CLIFF_VERSION: 2.12.0
Expand All @@ -25,44 +33,66 @@ jobs:
curl -fsSL -o "/tmp/${archive}" "https://github.com/orhun/git-cliff/releases/download/v${GIT_CLIFF_VERSION}/${archive}"
tar -xzf "/tmp/${archive}" -C /tmp
install "/tmp/git-cliff-${GIT_CLIFF_VERSION}/git-cliff" /usr/local/bin/git-cliff

- name: Check for unreleased changes
id: changes
run: |
echo "has_changes=$(python3 scripts/release.py has-unreleased-changes)" >> "${GITHUB_OUTPUT}"

- name: Compute release version
id: version
run: echo "release_version=$(python3 scripts/release.py next-version)" >> "${GITHUB_OUTPUT}"
if: steps.changes.outputs.has_changes == 'true'
run: |
release_version="$(python3 scripts/release.py next-version)"
echo "release_version=${release_version}" >> "${GITHUB_OUTPUT}"

- name: Generate changelog
if: steps.changes.outputs.has_changes == 'true'
env:
GITHUB_REPO: ${{ github.repository }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_VERSION: ${{ steps.version.outputs.release_version }}
run: git-cliff --config cliff.toml --tag "${RELEASE_VERSION}" --output CHANGELOG.md
run: |
git-cliff --config cliff.toml --tag "${RELEASE_VERSION}" --output CHANGELOG.md

- name: Create release PR
if: steps.changes.outputs.has_changes == 'true'
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
with:
commit-message: "chore(release): ${{ steps.version.outputs.release_version }}"
title: "chore(release): ${{ steps.version.outputs.release_version }}"
body: |
This PR prepares `${{ steps.version.outputs.release_version }}`.

- updates `CHANGELOG.md` with `git-cliff`
- is intended to be merged to `main`
- requires a separate manual `publish` run after merge
branch: "release/${{ steps.version.outputs.release_version }}"
delete-branch: true

publish-release-on-merge:
if: "${{ github.event_name == 'pull_request_target' && github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'main' && startsWith(github.event.pull_request.title, 'chore(release): ') }}"
publish-release:
if: ${{ github.event.inputs.action == 'publish' && github.ref == 'refs/heads/main' }}
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout merge commit
- name: Checkout main
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.event.pull_request.merge_commit_sha }}
fetch-depth: 0

- name: Determine release version
id: version
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
release_version="${PR_TITLE#chore(release): }"
release_version="$(python3 scripts/release.py latest-changelog-version)"
echo "release_version=${release_version}" >> "${GITHUB_OUTPUT}"
test "$(python3 scripts/release.py latest-changelog-version)" = "${release_version}"
release_commit="$(git log --format='%H%x09%s' HEAD | awk -F '\t' -v title="chore(release): ${release_version}" '$2 == title {print $1; exit}')"
if [[ -z "${release_commit}" ]]; then
echo "Unable to find a merged release commit for ${release_version} on main." >&2
exit 1
fi
echo "release_commit=${release_commit}" >> "${GITHUB_OUTPUT}"

- name: Extract release notes
id: notes
env:
Expand All @@ -73,24 +103,30 @@ jobs:
python3 scripts/release.py extract-release-notes "${RELEASE_VERSION}"
echo "EOF"
} >> "${GITHUB_OUTPUT}"

- name: Create Git tag if missing
env:
RELEASE_VERSION: ${{ steps.version.outputs.release_version }}
MERGE_SHA: ${{ github.event.pull_request.merge_commit_sha }}
RELEASE_COMMIT: ${{ steps.version.outputs.release_commit }}
run: |
if git rev-parse "${RELEASE_VERSION}" >/dev/null 2>&1; then exit 0; fi
if git rev-parse "${RELEASE_VERSION}" >/dev/null 2>&1; then
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag "${RELEASE_VERSION}" "${MERGE_SHA}"
git tag "${RELEASE_VERSION}" "${RELEASE_COMMIT}"
git push origin "${RELEASE_VERSION}"

- name: Publish GitHub release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_VERSION: ${{ steps.version.outputs.release_version }}
RELEASE_NOTES: ${{ steps.notes.outputs.release_notes }}
MERGE_SHA: ${{ github.event.pull_request.merge_commit_sha }}
RELEASE_COMMIT: ${{ steps.version.outputs.release_commit }}
run: |
if gh release view "${RELEASE_VERSION}" >/dev/null 2>&1; then exit 0; fi
if gh release view "${RELEASE_VERSION}" >/dev/null 2>&1; then
exit 0
fi
notes_file="$(mktemp)"
printf '%s\n' "${RELEASE_NOTES}" > "${notes_file}"
gh release create "${RELEASE_VERSION}" --title "${RELEASE_VERSION}" --notes-file "${notes_file}" --target "${MERGE_SHA}"
gh release create "${RELEASE_VERSION}" --title "${RELEASE_VERSION}" --notes-file "${notes_file}" --target "${RELEASE_COMMIT}"
13 changes: 10 additions & 3 deletions docs/releases.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

`mem0-aio` uses upstream-version-plus-AIO-revision releases such as `v1.0.9-aio.1`.

## Version format

- first wrapper release for upstream `v1.0.9`: `v1.0.9-aio.1`
- second wrapper-only release on the same upstream: `v1.0.9-aio.2`
- first wrapper release after upgrading upstream: `v1.0.10-aio.1`

## Published image tags

Every `main` build publishes:
Expand All @@ -13,7 +19,8 @@ Every `main` build publishes:

## Release flow

1. Trigger **Release / mem0-aio** from `main`.
1. Trigger **Release / Mem0-AIO** from `main` with `action=prepare`.
2. The workflow computes the next `upstream-aio.N` version and opens a release PR.
3. Merge that PR into `main`.
4. After merge, the workflow creates the Git tag and GitHub Release automatically.
3. Review and merge that PR into `main`.
4. Trigger **Release / Mem0-AIO** from `main` again with `action=publish`.
5. The workflow reads the merged `CHANGELOG.md` entry, creates the Git tag, and publishes the GitHub Release.
27 changes: 27 additions & 0 deletions scripts/release.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,28 @@ def git_tags() -> list[str]:
return [line.strip() for line in output.splitlines() if line.strip()]


def latest_release_tag(dockerfile: pathlib.Path, upstream: pathlib.Path) -> str | None:
upstream_version = read_upstream_version(dockerfile, upstream)
pattern = re.compile(rf"^{re.escape(upstream_version)}-aio\.(\d+)$")
matches: list[tuple[int, str]] = []
for tag in git_tags():
match = pattern.match(tag)
if match:
matches.append((int(match.group(1)), tag))
if not matches:
return None
matches.sort(key=lambda item: item[0])
return matches[-1][1]


def has_unreleased_changes(dockerfile: pathlib.Path, upstream: pathlib.Path) -> bool:
latest_tag = latest_release_tag(dockerfile, upstream)
if latest_tag is None:
return True
output = subprocess.check_output(["git", "log", "--format=%s", f"{latest_tag}..HEAD"], cwd=ROOT, text=True)
return any(line.strip() for line in output.splitlines())


def next_release_version(dockerfile: pathlib.Path, upstream: pathlib.Path) -> str:
upstream_version = read_upstream_version(dockerfile, upstream)
pattern = re.compile(rf"^{re.escape(upstream_version)}-aio\.(\d+)$")
Expand Down Expand Up @@ -98,6 +120,9 @@ def main() -> None:
next_parser = subparsers.add_parser("next-version")
next_parser.add_argument("--dockerfile", type=pathlib.Path, default=DEFAULT_DOCKERFILE)
next_parser.add_argument("--upstream-config", type=pathlib.Path, default=DEFAULT_UPSTREAM)
changes_parser = subparsers.add_parser("has-unreleased-changes")
changes_parser.add_argument("--dockerfile", type=pathlib.Path, default=DEFAULT_DOCKERFILE)
changes_parser.add_argument("--upstream-config", type=pathlib.Path, default=DEFAULT_UPSTREAM)
latest_parser = subparsers.add_parser("latest-changelog-version")
latest_parser.add_argument("--changelog", type=pathlib.Path, default=DEFAULT_CHANGELOG)
notes_parser = subparsers.add_parser("extract-release-notes")
Expand All @@ -108,6 +133,8 @@ def main() -> None:
print(read_upstream_version(args.dockerfile, args.upstream_config))
elif args.command == "next-version":
print(next_release_version(args.dockerfile, args.upstream_config))
elif args.command == "has-unreleased-changes":
print("true" if has_unreleased_changes(args.dockerfile, args.upstream_config) else "false")
elif args.command == "latest-changelog-version":
print(latest_changelog_version(args.changelog))
else:
Expand Down
Loading