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
188 changes: 188 additions & 0 deletions .github/workflows/staging-conflicts.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
name: Resolve staging conflicts

# Reproduces a staging <- main merge inside the runner. If it conflicts, Claude
# resolves the conflicts and creates the merge commit. Nothing is commented,
# nothing is pushed unless the resolution passes every verification below.
#
# claude-code-action rejects `push` events, so detection runs on a schedule.
#
# Required secrets:
# STAGING_MERGE_KEY private half of a write-enabled deploy key
# CLAUDE_CODE_OAUTH_TOKEN Claude API key, despite the secret name

on:
schedule:
- cron: "*/10 * * * *"
workflow_dispatch:

concurrency:
group: staging-conflict-resolve
cancel-in-progress: false

jobs:
resolve:
if: github.repository == 'gitroomhq/postiz-app'
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: read

steps:
- name: Checkout staging
uses: actions/checkout@v6
with:
ref: staging
fetch-depth: 0
ssh-key: ${{ secrets.STAGING_MERGE_KEY }}
persist-credentials: true

# Runs before anything billable. If the deploy key cannot push, the job
# dies here at zero cost instead of after a paid resolution.
- name: Configure push credentials
env:
SSH_KEY: ${{ secrets.STAGING_MERGE_KEY }}
run: |
mkdir -p ~/.ssh
printf '%s\n' "$SSH_KEY" > ~/.ssh/staging_merge
chmod 600 ~/.ssh/staging_merge
ssh-keyscan -t ed25519 github.com >> ~/.ssh/known_hosts
echo "GIT_SSH_COMMAND=ssh -i $HOME/.ssh/staging_merge -o IdentitiesOnly=yes" >> "$GITHUB_ENV"

# `git push --dry-run` exercises the exact path the real push takes.
# A bare `ssh -T` does not, since GIT_SSH_COMMAND applies only to git.
- name: Preflight push
run: |
git remote set-url origin "git@github.com:${{ github.repository }}.git"
git push --dry-run origin HEAD:staging
echo "push path verified"

- name: Probe merge
id: probe
run: |
git config user.name "postiz-merge-bot"
git config user.email "bot@postiz.com"

if git merge --no-commit --no-ff origin/main; then
git merge --abort 2>/dev/null || git reset --hard HEAD
echo "conflicted=false" >> "$GITHUB_OUTPUT"
echo "staging merges cleanly into main, nothing to do"
else
echo "conflicted=true" >> "$GITHUB_OUTPUT"
echo "Conflicted paths:"
git diff --name-only --diff-filter=U
fi

# CI definitions always come from main, so conflicts under .github/ are
# settled here by taking main's side. This keeps Claude away from them
# and stops this workflow from deadlocking on edits to itself: it was
# added independently on both branches, so git sees add/add and every
# change to it on main conflicts no matter how staging's copy looks.
- name: Take main's CI definitions
if: steps.probe.outputs.conflicted == 'true'
run: |
git diff --name-only -z --diff-filter=U -- .github/ > /tmp/ci_paths
if [ ! -s /tmp/ci_paths ]; then
echo "no conflicts under .github/"
exit 0
fi

echo "taking main's copy of:"
tr '\0' '\n' < /tmp/ci_paths
xargs -0 git checkout --theirs -- < /tmp/ci_paths
xargs -0 git add -- < /tmp/ci_paths

- name: Resolve with Claude
if: steps.probe.outputs.conflicted == 'true'
uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
github_token: ${{ secrets.GITHUB_TOKEN }}
allowed_bots: "github-actions[bot]"
prompt: |
The repository is checked out on `staging`, part-way through
`git merge --no-ff origin/main`, and the merge has conflicts.

Resolve every conflicted file so the result preserves the intent of
both sides. Then `git add` the resolved paths and create the merge
commit with:

git commit --no-edit --trailer "Resolved-by: claude-code-action"

Constraints:
- Change nothing beyond what the conflict resolution requires.
- Do not push, switch branches, create branches, or amend history.
- Do not post comments, open issues, or touch any pull request.
- Never modify anything under `.github/`. Conflicts there are
already resolved and staged for you; leave them exactly as they
are and resolve only the remaining paths.
- If a conflict is ambiguous enough that you would be guessing at
the correct resolution, stop without committing and explain why.
claude_args: |
--model claude-sonnet-5
--allowedTools "Read,Glob,Grep,Edit,Write,Bash(git status:*),Bash(git diff:*),Bash(git log:*),Bash(git show:*),Bash(git ls-files:*),Bash(git add:*),Bash(git commit:*)"

- name: Verify resolution
if: steps.probe.outputs.conflicted == 'true'
run: |
if git ls-files -u | grep -q .; then
echo "::error::Unmerged paths remain in the index"
git ls-files -u
exit 1
fi

if git rev-parse -q --verify MERGE_HEAD >/dev/null; then
echo "::error::Merge was never committed"
exit 1
fi

if git grep -nI -e '^<<<<<<< ' -e '^=======$' -e '^>>>>>>> ' HEAD; then
echo "::error::Conflict markers present in the committed tree"
exit 1
fi

if [ -n "$(git status --porcelain)" ]; then
echo "::error::Working tree is dirty after the commit"
git status --porcelain
exit 1
fi

if ! git merge-base --is-ancestor origin/main HEAD; then
echo "::error::HEAD does not contain origin/main, wrong commit shape"
exit 1
fi

# Every .github/ path the merge touched must be either untouched by
# the merge or byte-identical to main's copy, so a resolution can
# never smuggle in a CI change of its own.
for path in $(git diff --name-only origin/staging..HEAD -- .github/); do
if ! git diff --quiet origin/main HEAD -- "$path"; then
echo "::error::$path differs from main's copy, refusing"
git diff origin/main HEAD -- "$path"
exit 1
fi
done

echo "Resolution commit:"
git log -1 --stat

# Saved before the push, so a push failure never costs a second run.
# Recover with: git fetch ./resolved.bundle HEAD
# git push origin FETCH_HEAD:staging
- name: Archive resolution
if: steps.probe.outputs.conflicted == 'true'
run: git bundle create /tmp/resolved.bundle HEAD ^origin/staging ^origin/main

- uses: actions/upload-artifact@v4
if: steps.probe.outputs.conflicted == 'true'
with:
name: staging-resolution-${{ github.run_id }}
path: /tmp/resolved.bundle
retention-days: 14

# The action rewrites `origin` to an HTTPS URL during its own git setup,
# so the SSH remote is reasserted here.
- name: Push staging
if: steps.probe.outputs.conflicted == 'true'
run: |
git remote set-url origin "git@github.com:${{ github.repository }}.git"
git push origin HEAD:staging
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- **MCP Client Icons & Onboarding Enhancements (Upstream Sync)**:
- Added Nanoclaw and other third-party MCP client icons support in Public API.
- Upgraded onboarding experience and interactive modal walkthroughs.
- **Post Workflow v1.1.2**:
- Enhanced background workflow with automatic retry on heartbeat timeouts when no heartbeat details are present.
- **Frontend & Media Modernization Roadmap**:
- Expanded `ROADMAP.md` with Crove OS visual design standards, workspace switcher overhaul, and R2 direct upload pipeline.

## [v2.24.0] - 2026-09-03

### Added
Expand Down
32 changes: 30 additions & 2 deletions ROADMAP.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
# Crove Roadmap
# Crove Post Roadmap

## Provider readiness
## 1. Frontend & UI/UX Modernization (Crove OS Standards)

- [ ] **Design System & Visual Refresh**:
- Migrate legacy Postiz purple/neon styles to the unified Crove OS Design System (modern dark mode, refined zinc neutrals, subtle glassmorphism).
- Standardize UI components with Tailwind and native primitives across Navigation, Modals, Forms, and Buttons.
- [ ] **Workspace & Organization Switcher Overhaul**:
- Replace stock dropdown with a sleek, multi-tenant Workspace Selector featuring avatar/initials, active checkmarks, and Super-Admin/Role badges.
- Optimize SWR cache invalidation for seamless zero-reload workspace switching.
- [ ] **Post Composer & Media Preview Rework**:
- Redesign the post creation modal with live multi-channel previews (X, LinkedIn, Facebook, Instagram, TikTok, Threads).
- Modernize character counters, hashtag generators, and AI assistant side panels.
- [ ] **Calendar & Analytics Experience**:
- Implement a modern responsive calendar grid with smooth drag-and-drop post scheduling.
- Redesign analytics dashboards with clean charts, engagement heatmaps, and exportable reports.

## 2. Provider Readiness & Integrations

### TikTok Content Posting API

Expand All @@ -11,3 +26,16 @@
- [ ] Resolve or document the stock Postiz defaults that preselect public visibility and enable comments before submitting the TikTok audit.
- [ ] Submit the Content Posting API audit only after the recorded behavior matches the requested products and scopes.

## 3. Media & Storage Architecture

- [ ] **Cloudflare R2 Direct Upload & Streaming**:
- Optimize multipart chunked uploads for large video files (Reels, TikTok, YouTube Shorts).
- Implement client-side video transcode checks and automatic thumbnail generation via Cloudflare CDN.

## 4. AI & Ecosystem Intelligence

- [ ] **Brand Voice & Copilot Enhancements**:
- Integrate brand voice guidelines and tone-of-voice presets into the OpenAI-compatible AI Copilot engine.
- Expand Mastra / MCP agent capabilities for autonomous multi-channel campaign scheduling.


Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading