Skip to content

Drift detection

Drift is when a resource exists but its value no longer matches iap.yaml — typically because someone edited it by hand in a store console. storectl separates this from a first-time create:

  • create (+) — the item is missing and would be created.
  • drift (~) — the item exists but differs; current → desired is reported.
Terminal window
iapctl plan -config iap.yaml
# ~ price sonir.pro.yearly JPY 5800 → JPY 5400 (someone changed it in App Store Connect)
Detected (current → desired) Not yet monitored
Subscription & one-time prices availability / territories
Store listings / localizations (name, description) billing period, base-plan config
App Store / Play state where applicable RevenueCat wiring, display names

storectl reports only what it checks — it does not silently claim coverage it doesn’t have. (App Store non-consumable price drift currently reports desired only; subscriptions report both current and desired.)

-detailed-exitcode makes plan terraform-style, so CI can branch on it:

Code Meaning
0 no changes (everything matches)
2 pending changes / drift
1 error

Add -drift-only so only drift counts as pending (a first-time create is ignored) — exactly what a scheduled drift monitor wants.

Run plan -drift-only on a schedule; if it exits 2, open/update a GitHub Issue (and/or ping Slack). No hosted service required.

name: drift-cron
on:
schedule: [{ cron: "17 */6 * * *" }] # every 6h
workflow_dispatch: {}
permissions: { contents: read, issues: write }
jobs:
drift:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: curl -fsSL https://storectl.dev/install.sh | sh
- id: drift
uses: storectl-dev/storectl/.github/actions/storectl-comment@v1
with: { command: plan, config: iap.yaml, drift-only: "true", post-comment: "false" }
env: { /* your store secrets */ }
# if steps.drift.outputs.exit-code == '2' → open/update an Issue (see the sample)

A complete drift-cron.yml (Issue upsert + optional Slack) is in the storectl repo under examples/github-actions/.

-o json emits a versioned, machine-readable report for dashboards and notifications (see the CLI reference):

{
"schemaVersion": 1,
"app": "Sonir",
"command": "plan",
"summary": { "create": 0, "created": 0, "drift": 1, "exists": 12, "skip": 1, "error": 0 },
"actions": [
{ "provider": "appstore", "op": "price", "target": "sonir.pro.yearly",
"status": "drift", "current": "JPY 5800", "desired": "JPY 5400" }
]
}