Skip to content

fix(ci): handle empty target repo in sync-starter workflow (#5) #2

fix(ci): handle empty target repo in sync-starter workflow (#5)

fix(ci): handle empty target repo in sync-starter workflow (#5) #2

Workflow file for this run

name: Sync starter to flat repo
# Mirrors `packages/starter/` into `emdashCommerce/starter` as a flat, cloneable
# template. Content-mirror approach (not `git subtree split`) — gives us a
# clean history in the target and room for the `workspace:*` → `^x.y.z`
# rewrite without fighting rebases.
#
# Fires on pushes to main that touch the starter or core's version. Also
# triggerable manually via the Actions UI.
on:
push:
branches: [main]
paths:
- "packages/starter/**"
- "packages/core/package.json"
- ".github/workflows/sync-starter.yml"
workflow_dispatch:
concurrency: sync-starter
permissions:
contents: read
env:
TARGET_REPO: emdashCommerce/starter
TARGET_BRANCH: main
jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Checkout monorepo
uses: actions/checkout@v4
with:
fetch-depth: 1
# Handle both "target repo has commits" and "target repo is brand-new and
# empty" cases without failing. actions/checkout refuses empty repos
# because there's no branch to resolve yet, so manage target's .git
# directly instead.
- name: Initialize or fetch target repo
run: |
mkdir -p target
cd target
git init --initial-branch="${TARGET_BRANCH}" --quiet
git remote add origin "https://x-access-token:${TOKEN}@github.com/${TARGET_REPO}.git"
if git fetch --depth=1 origin "${TARGET_BRANCH}" 2>/dev/null; then
echo "::notice::Fetched existing ${TARGET_REPO}@${TARGET_BRANCH}"
git reset --hard "origin/${TARGET_BRANCH}"
else
echo "::notice::${TARGET_REPO} is empty — this run will seed it"
fi
env:
TOKEN: ${{ secrets.STARTER_REPO_TOKEN }}
TARGET_REPO: ${{ env.TARGET_REPO }}
TARGET_BRANCH: ${{ env.TARGET_BRANCH }}
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "22"
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Mirror starter contents into target
run: |
# Keep target's .git, wipe everything else, copy starter/ in.
find target -mindepth 1 -maxdepth 1 -not -name '.git' -exec rm -rf {} +
cp -R packages/starter/. target/
# Strip local-dev artifacts that shouldn't ship to the flat repo
rm -rf target/node_modules target/dist target/.astro target/.wrangler \
target/data.db target/data.db-shm target/data.db-wal target/uploads
mkdir -p target/uploads && touch target/uploads/.gitkeep
- name: Rewrite workspace:* → actual core version
run: |
CORE_VERSION=$(node -p "require('./packages/core/package.json').version")
echo "Pinning @dashcommerce/core to ^${CORE_VERSION}"
node - "$CORE_VERSION" <<'NODE'
const fs = require('fs');
const [, , version] = process.argv;
const path = 'target/package.json';
const pkg = JSON.parse(fs.readFileSync(path, 'utf8'));
for (const field of ['dependencies', 'devDependencies', 'peerDependencies']) {
if (!pkg[field]) continue;
for (const dep of Object.keys(pkg[field])) {
if (pkg[field][dep] === 'workspace:*') {
pkg[field][dep] = `^${version}`;
}
}
}
fs.writeFileSync(path, JSON.stringify(pkg, null, '\t') + '\n');
NODE
- name: Regenerate standalone lockfile
working-directory: target
run: |
rm -f bun.lock bun.lockb
bun install --ignore-scripts
- name: Commit + push to target
working-directory: target
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add -A
if git diff --cached --quiet; then
echo "::notice::No starter changes to sync"
exit 0
fi
SHORT_SHA="${GITHUB_SHA:0:7}"
git commit -m "sync: ${GITHUB_REPOSITORY}@${SHORT_SHA}" \
-m "Source: https://github.com/${GITHUB_REPOSITORY}/commit/${GITHUB_SHA}"
git push origin "${{ env.TARGET_BRANCH }}"
echo "::notice::Synced ${GITHUB_REPOSITORY}@${SHORT_SHA} → ${TARGET_REPO}"