# WooCommerce Headless Blueprint

## Purpose

Use this SOP when a human needs to create a new headless WooCommerce project for a client using the GETQUICK three-repository pattern.

The workflow creates:

- A parent workspace repository under `Quick-Release/<client-slug>`.
- A WordPress admin repository under `Quick-Release/<client-slug>-admin`.
- An Astro frontend repository under `Quick-Release/<client-slug>-fe`.

The parent repository records the admin and frontend repositories as Git submodules. WordPress deploys to Ploi at `admin.<client-domain>`, and Astro deploys to Cloudflare Workers.

## Context

This blueprint follows the existing GETQUICK headless WordPress/WooCommerce workspace shape:

- The parent repo coordinates local development and submodule versions.
- The admin repo contains Bedrock WordPress, DDEV local development, WooCommerce, GraphQL/API configuration, and Ploi deployment configuration.
- The frontend repo contains the Astro storefront, Cloudflare Workers deployment configuration, and WordPress/WooCommerce data integration.

Use the current internal example repositories as the source pattern, but rename everything to generic client values before committing the new project.

Supporting SOPs:

- Cloudflare domain registration: `/cloudflare/register-a-domain-with-cloudflare/`
- Ploi API reference: `/ploi-io/ploi-api-reference/`
- Ploi auth config: `/ploi-io/auth-config/`
- Agent domain registration policy: `/agents/allow-agents-to-register-domains/`

Official references:

- DDEV installation: `https://docs.ddev.com/en/stable/users/install/ddev-installation/`
- GitHub CLI: `https://github.com/cli/cli`
- Cloudflare Wrangler: `https://developers.cloudflare.com/workers/wrangler/commands/general/`

## Outcome

After the procedure is complete:

- Three private GitHub repositories exist under `Quick-Release`.
- The parent repository includes the admin and frontend repositories as submodules.
- The admin repository can run WordPress locally with DDEV and can deploy to Ploi.
- The frontend repository can run Astro locally and deploy to Cloudflare Workers.
- Required GitHub secrets and variables are configured for Ploi and Cloudflare deployments.
- The production admin hostname is `admin.<client-domain>`.
- The production storefront hostname is ready for the chosen client domain.

## Steps

### Set project variables

Run this first and edit only the values on the right side:

```bash
export GITHUB_ORG="Quick-Release"
export CLIENT_SLUG="clientname"
export CLIENT_DOMAIN="clientdomain.tld"
export MAIN_REPO="${CLIENT_SLUG}"
export ADMIN_REPO="${CLIENT_SLUG}-admin"
export FE_REPO="${CLIENT_SLUG}-fe"
export ADMIN_HOST="admin.${CLIENT_DOMAIN}"
export WP_ADMIN_USER="admin"
export WP_ADMIN_PASSWORD="change-this-password"
export WP_ADMIN_EMAIL="admin@${CLIENT_DOMAIN}"
export WORKDIR="${HOME}/workspaces/${CLIENT_SLUG}"
export SOURCE_MAIN_REPO="<approved-parent-example>"
export SOURCE_ADMIN_REPO="<approved-admin-example>"
export SOURCE_FE_REPO="<approved-frontend-example>"
```

Use lowercase kebab-case for `CLIENT_SLUG`. Do not include the customer legal name if it should not appear in GitHub URLs.

Set the `SOURCE_*` variables to the current approved internal example repositories before running the copy step.

### Install or update required CLIs on macOS

Use this path for macOS workstations with Homebrew:

```bash
brew update
brew install git gh curl jq node composer ddev/ddev/ddev mkcert
brew install --cask docker
mkcert -install
corepack enable
corepack prepare pnpm@latest --activate
npm install -g wrangler@latest

git --version
gh --version
curl --version
jq --version
docker version
ddev --version
mkcert -help >/dev/null
node --version
pnpm --version
composer --version
wrangler --version
```

Start Docker Desktop before running DDEV commands.

### Install or update required CLIs on Ubuntu or Debian

Use this path for Ubuntu or Debian workstations:

```bash
sudo apt-get update
sudo apt-get install -y git curl jq ca-certificates gnupg lsb-release unzip

# Docker provider
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list >/dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo usermod -aG docker "$USER"

# GitHub CLI
sudo mkdir -p -m 755 /etc/apt/keyrings
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | \
  sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg >/dev/null
sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | \
  sudo tee /etc/apt/sources.list.d/github-cli.list >/dev/null
sudo apt-get update
sudo apt-get install -y gh

# DDEV and mkcert
curl -fsSL https://ddev.com/install.sh | bash
mkcert -install

# Node, pnpm, Composer, Wrangler
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt-get install -y nodejs composer
corepack enable
corepack prepare pnpm@latest --activate
sudo npm install -g wrangler@latest

git --version
gh --version
curl --version
jq --version
docker version
ddev --version
mkcert -help >/dev/null
node --version
pnpm --version
composer --version
wrangler --version
```

Log out and back in if Docker reports permission errors after adding the user to the `docker` group.

### Authenticate CLIs

Authenticate GitHub and Cloudflare:

```bash
gh auth login
gh auth status

wrangler login
wrangler whoami
```

Create the Ploi API token and GitHub secrets using the Ploi Auth Config SOP:

```text
https://sop.getquick.io/ploi-io/auth-config/
```

At minimum, plan for:

```text
PLOI_API_TOKEN
PLOI_SERVER_ID
PLOI_SITE_ID
PLOI_USER_AGENT
```

### Create the private GitHub repositories

Create all three repositories under `Quick-Release`:

```bash
gh repo create "${GITHUB_ORG}/${MAIN_REPO}" --private --confirm
gh repo create "${GITHUB_ORG}/${ADMIN_REPO}" --private --confirm
gh repo create "${GITHUB_ORG}/${FE_REPO}" --private --confirm
```

Confirm they exist:

```bash
gh repo view "${GITHUB_ORG}/${MAIN_REPO}" --json name,visibility,url
gh repo view "${GITHUB_ORG}/${ADMIN_REPO}" --json name,visibility,url
gh repo view "${GITHUB_ORG}/${FE_REPO}" --json name,visibility,url
```

### Create local working copies from the approved examples

Use the current approved internal examples as source workspaces. Keep the source repositories unchanged and copy into fresh target folders.

```bash
mkdir -p "$WORKDIR"
cd "$WORKDIR"

gh repo clone "${GITHUB_ORG}/${MAIN_REPO}" "${MAIN_REPO}"
gh repo clone "${GITHUB_ORG}/${ADMIN_REPO}" "${ADMIN_REPO}"
gh repo clone "${GITHUB_ORG}/${FE_REPO}" "${FE_REPO}"

mkdir -p "${WORKDIR}/_sources"
cd "${WORKDIR}/_sources"
gh repo clone "${GITHUB_ORG}/${SOURCE_MAIN_REPO}" parent-example
gh repo clone "${GITHUB_ORG}/${SOURCE_ADMIN_REPO}" admin-example
gh repo clone "${GITHUB_ORG}/${SOURCE_FE_REPO}" frontend-example
```

Copy source files into the new repositories without copying `.git` directories:

```bash
rsync -a --exclude ".git" --exclude "node_modules" --exclude "vendor" --exclude ".ddev/.global_commands" \
  "${WORKDIR}/_sources/parent-example/" "${WORKDIR}/${MAIN_REPO}/"

rsync -a --exclude ".git" --exclude "node_modules" --exclude "vendor" \
  "${WORKDIR}/_sources/admin-example/" "${WORKDIR}/${ADMIN_REPO}/"

rsync -a --exclude ".git" --exclude "node_modules" --exclude "dist" --exclude ".wrangler" \
  "${WORKDIR}/_sources/frontend-example/" "${WORKDIR}/${FE_REPO}/"
```

Replace example names with client names before committing:

```bash
cd "$WORKDIR"
grep -R "<example-client>" "${MAIN_REPO}" "${ADMIN_REPO}" "${FE_REPO}" || true

find "${MAIN_REPO}" "${ADMIN_REPO}" "${FE_REPO}" -type f \
  -not -path "*/.git/*" \
  -not -path "*/node_modules/*" \
  -not -path "*/vendor/*" \
  -print0 | \
  xargs -0 perl -pi -e "s/<example-client>/${CLIENT_SLUG}/g; s/<example-domain>/${CLIENT_DOMAIN}/g; s/admin\\.<example-domain>/${ADMIN_HOST}/g"
```

If source examples contain project-specific names that are not covered by these placeholders, replace them manually before the first commit.

### Configure the parent workspace repository

Add the admin and frontend repositories as submodules:

```bash
cd "${WORKDIR}/${MAIN_REPO}"
rm -rf "${ADMIN_REPO}" "${FE_REPO}"

git submodule add "git@github.com:${GITHUB_ORG}/${ADMIN_REPO}.git" "${ADMIN_REPO}"
git submodule add "git@github.com:${GITHUB_ORG}/${FE_REPO}.git" "${FE_REPO}"

git submodule status
```

Update parent workspace references so scripts point at the generic submodule names:

```bash
grep -R "<admin-submodule>" . || true
grep -R "<frontend-submodule>" . || true
```

Expected final parent layout:

```text
<client-slug>/
├── AGENTS.md
├── README.md
├── package.json
├── .gitmodules
├── <client-slug>-admin/
└── <client-slug>-fe/
```

Commit the parent workspace:

```bash
git add -A
git commit -m "Initial headless WooCommerce workspace"
git push -u origin main
```

### Configure WordPress admin

Set up the Bedrock/DDEV admin repository:

```bash
cd "${WORKDIR}/${ADMIN_REPO}"

cp .env.example .env 2>/dev/null || true
perl -pi -e "s|^WP_HOME=.*|WP_HOME=https://${ADMIN_HOST}|; s|^WP_SITEURL=.*|WP_SITEURL=https://${ADMIN_HOST}/wp|" .env

ddev config --project-name "${ADMIN_REPO}" --project-type wordpress --docroot web --php-version 8.3
ddev start
ddev composer install
```

Install WordPress locally if the Bedrock setup does not already include a database:

```bash
ddev wp core install \
  --url="https://${ADMIN_REPO}.ddev.site" \
  --title="${CLIENT_SLUG} Admin" \
  --admin_user="${WP_ADMIN_USER}" \
  --admin_password="${WP_ADMIN_PASSWORD}" \
  --admin_email="${WP_ADMIN_EMAIL}" \
  --skip-email
```

Install or activate required WordPress components for headless WooCommerce:

```bash
ddev wp plugin activate woocommerce || ddev wp plugin install woocommerce --activate
ddev wp plugin activate wp-graphql || ddev wp plugin install wp-graphql --activate
ddev wp rewrite structure '/%postname%/' --hard
ddev wp rewrite flush --hard
```

Commit and push the admin repo:

```bash
git add -A
git commit -m "Initial Bedrock WooCommerce admin"
git push -u origin main
```

### Configure Ploi for WordPress admin

In Ploi:

1. Create or choose the server.
2. Create a site for `admin.${CLIENT_DOMAIN}`.
3. Set the repository to `git@github.com:${GITHUB_ORG}/${ADMIN_REPO}.git`.
4. Set the deploy branch to `main`.
5. Configure the site as a Bedrock WordPress project.
6. Add environment variables from the admin `.env`, replacing local values with production values.
7. Add the database credentials Ploi created for the site.
8. Configure SSL for `admin.${CLIENT_DOMAIN}`.
9. Deploy once from Ploi.

Add GitHub secrets and variables for automation:

```bash
cd "${WORKDIR}/${ADMIN_REPO}"

gh secret set PLOI_API_TOKEN --repo "${GITHUB_ORG}/${ADMIN_REPO}"
gh variable set PLOI_SERVER_ID --repo "${GITHUB_ORG}/${ADMIN_REPO}" --body "<ploi-server-id>"
gh variable set PLOI_SITE_ID --repo "${GITHUB_ORG}/${ADMIN_REPO}" --body "<ploi-site-id>"
gh variable set PLOI_USER_AGENT --repo "${GITHUB_ORG}/${ADMIN_REPO}" --body "${ADMIN_REPO}-github-actions"
```

### Configure Astro frontend

Set up the frontend repository:

```bash
cd "${WORKDIR}/${FE_REPO}"

pnpm install
pnpm exec wrangler init --from-dash "${FE_REPO}" || true
pnpm exec wrangler types
```

Create or update the frontend environment file:

```bash
cat > .env.example <<EOF
PUBLIC_SITE_URL=https://${CLIENT_DOMAIN}
WORDPRESS_URL=https://${ADMIN_HOST}
WORDPRESS_GRAPHQL_URL=https://${ADMIN_HOST}/graphql
WOOCOMMERCE_STORE_URL=https://${ADMIN_HOST}
EOF

cp .env.example .env
```

Confirm the frontend can build:

```bash
pnpm build
```

Commit and push the frontend repo:

```bash
git add -A
git commit -m "Initial Astro WooCommerce frontend"
git push -u origin main
```

### Configure Cloudflare Workers for Astro

Register or connect the domain with the Cloudflare domain SOP if needed:

```text
https://sop.getquick.io/cloudflare/register-a-domain-with-cloudflare/
```

Deploy the frontend:

```bash
cd "${WORKDIR}/${FE_REPO}"
pnpm build
pnpm exec wrangler deploy
```

If the Worker uses secrets, set them with Wrangler:

```bash
pnpm exec wrangler secret put WORDPRESS_GRAPHQL_URL
pnpm exec wrangler secret put WOOCOMMERCE_CONSUMER_KEY
pnpm exec wrangler secret put WOOCOMMERCE_CONSUMER_SECRET
```

Only set WooCommerce API credentials if the frontend requires authenticated server-side WooCommerce API access. Prefer public GraphQL or Store API data when possible.

### Update parent submodule pointers

After the admin and frontend repos are pushed, update the parent submodule SHAs:

```bash
cd "${WORKDIR}/${MAIN_REPO}"
git submodule update --remote --merge
git status
git add "${ADMIN_REPO}" "${FE_REPO}" .gitmodules
git commit -m "Bump initial admin and frontend submodules"
git push
```

## Validation

Validate GitHub repositories:

```bash
gh repo view "${GITHUB_ORG}/${MAIN_REPO}" --json name,visibility,url
gh repo view "${GITHUB_ORG}/${ADMIN_REPO}" --json name,visibility,url
gh repo view "${GITHUB_ORG}/${FE_REPO}" --json name,visibility,url
```

Validate submodules:

```bash
cd "${WORKDIR}/${MAIN_REPO}"
git submodule status
git config --file .gitmodules --get-regexp path
```

Expected result: the admin and frontend submodules are present and have pinned SHAs.

Validate WordPress locally:

```bash
cd "${WORKDIR}/${ADMIN_REPO}"
ddev describe
ddev wp option get home
ddev wp plugin status woocommerce
ddev wp plugin status wp-graphql
curl -I "https://${ADMIN_REPO}.ddev.site"
```

Validate Ploi readiness:

```bash
curl -fsS https://ploi.io/api/servers \
  -H "Authorization: Bearer $PLOI_API_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -H "User-Agent: ${ADMIN_REPO}-smoke-test"
```

Validate Astro and Cloudflare:

```bash
cd "${WORKDIR}/${FE_REPO}"
pnpm build
pnpm exec wrangler whoami
pnpm exec wrangler deploy --dry-run
```

Validate final DNS and HTTPS:

```bash
dig A "${CLIENT_DOMAIN}"
dig CNAME "www.${CLIENT_DOMAIN}"
dig A "${ADMIN_HOST}"
curl -I "https://${CLIENT_DOMAIN}"
curl -I "https://${ADMIN_HOST}"
```

Expected result:

- `https://${ADMIN_HOST}` reaches the Ploi WordPress admin site.
- `https://${CLIENT_DOMAIN}` reaches the Astro frontend on Cloudflare Workers.
- The parent repo records the exact admin and frontend submodule commits.

## Operational notes for agents

Use this article when a user asks for a new WooCommerce headless stack, a three-repository WordPress/Astro workspace, or a GETQUICK storefront blueprint.

Do not place this workflow under the `Agents` category. It is a human operational blueprint.

Retrieve current docs before executing provider-specific steps:

- GitHub CLI: `https://github.com/cli/cli`
- DDEV: `https://docs.ddev.com/en/stable/users/install/ddev-installation/`
- Cloudflare Wrangler: `https://developers.cloudflare.com/workers/wrangler/commands/general/`
- Ploi API: `https://developers.ploi.io/`

Never commit secrets. Use GitHub Secrets, Wrangler secrets, Ploi environment variables, or local untracked `.env` files.

Important search terms: WooCommerce headless, Bedrock, DDEV, Ploi, Cloudflare Workers, Astro frontend, GitHub submodules, WordPress admin, storefront.

## Troubleshooting

- If `gh repo create` fails, run `gh auth status` and confirm the user has permission to create repositories under `Quick-Release`.
- If submodule clone fails, confirm SSH access to all private repositories.
- If DDEV fails to start, confirm Docker is running and the current user can access Docker.
- If `ddev composer install` fails, check Composer authentication for private packages.
- If WordPress install fails, confirm the Bedrock `.env` database values match DDEV.
- If WooCommerce or WPGraphQL activation fails, confirm the packages are installed or add them through Composer/WP-CLI according to the admin repo pattern.
- If Ploi deployment fails, confirm repository access, deploy branch, production `.env`, database credentials, and Ploi token scopes.
- If Wrangler deployment fails, run `wrangler whoami`, confirm the Cloudflare account, and check `wrangler.jsonc`.
- If the frontend cannot fetch WordPress data, verify `WORDPRESS_GRAPHQL_URL`, CORS, SSL, and that the admin host is reachable.