Case study 03 / Automation / 2026
MyRain price sync
A weekly pricing chore across about 1,000 SKUs, handed over to Python.
A Python integration between a supplier portal and Shopify, built to replace a manual weekly pricing workflow across about 1,000 product SKUs.
~1,000product SKUs
Role, status and stack
- Role
- Sole developer (Software Developer, Volunteer)
- Status
- Engagement ran Apr 2026 to Aug 2026. Deployed and scheduled through GitHub Actions
- Made with
- Python / Playwright / openpyxl / Shopify GraphQL Admin API / GitHub Actions / pytest
The problem
The tedium was obvious. The real risk was a $3 part going live at $1,000.
MyRain is a small irrigation business on Shopify. Every week, about 1,000 prices got checked by hand: download the supplier's list, compare it with the store, key in the differences. Some supplier prices use a different unit or pack size than the store sells, so one bad match could reach the live store. I worked through the process and its risks with the owner, then built around them.
Output from the file-driven version's dry run on November 4, 2026. The file name is changed, and the per-product lines are left out.
The system
Download. Playwright logs in to the supplier portal and downloads the Excel price list.
What can go wrong: Retries up to three times, then stops with a clear error before anything touches Shopify.
Read all 6 steps at once
- Download. Playwright logs in to the supplier portal and downloads the Excel price list. What can go wrong: Retries up to three times, then stops with a clear error before anything touches Shopify.
- Parse. openpyxl finds the SKU and retail price columns and builds a clean price map. What can go wrong: No columns, no run. Unreadable prices are skipped and logged, and duplicate SKUs raise a warning.
- Match SKUs. Pulls every Shopify variant through GraphQL and matches it to the list by normalized SKU. What can go wrong: Store products that aren't on the price list are counted and left alone.
- Validate. Keeps only real changes. A cent or less counts as unchanged. What can go wrong: In test mode, the run stops here and logs what would change.
- Update Shopify. Sends changes in batches of 25, slowing down as the rate limit budget runs low. What can go wrong: A rejected update fails the whole run, so it can't go unnoticed.
- Report. Writes JSON logs and an execution report, kept as GitHub Actions artifacts. What can go wrong: A crashed run still writes a failure report, and GitHub emails the repository owner.
- Test mode
- --dry-run does the whole comparison and stops before writing anything. --local-file skips the portal login, so a price list can be tested on its own.
- A pytest suite
- 44 tests cover the parser, Shopify client and sync engine with mocked HTTP and fixture Excel files, so they need no live credentials.
- Scheduling
- A GitHub Actions workflow is scheduled for Mondays at 10:00 UTC, and can be started by hand, with or without test mode.
- Replaceable source
- Only the extractor knows where prices come from. If the supplier ever offers a direct feed, that one piece changes.
- An outlier guard
- A file-driven version flags and skips any change over 50%, with the percentage in its report. Those are usually unit mismatches, like per-foot against per-roll, and look exactly like real changes until a person checks.
- Credentials
- Credentials live in GitHub Actions secrets, never the source code. The Shopify client uses a short-lived token and refreshes it when needed.
- Operating procedures
- Step-by-step procedures for non-technical staff, so running it doesn't depend on me.
Results
- GitHub Actions schedule
- Weekly
- pytest tests
- 44
| Result | SKUs |
|---|---|
| Loaded from export | 525 |
| Would update | 229 |
| Unchanged | 71 |
| Not found in Shopify | 197 |
| Flagged as outliers | 28 |
| Errors | 0 |
What I learned
What I learned
I underestimated the run report. Nobody asks for it until the week a price looks wrong, then it's all anyone wants. Most of the work in a small automation is deciding what it should refuse to do.
What I'd do differently
Ask for a direct supplier feed early, since a browser login is the part most likely to break when a page changes. And build the outlier guard into the first version, not after seeing what the data could do.
Built with an AI coding assistant. I set the requirements with the owner, decided the safeguards and what counted as a bad update, and did the debugging and checking.
Properties
- Type
- Automation
- Role
- Sole developer (Software Developer, Volunteer)
- Year
- 2026
- Stack
- Python, Playwright, openpyxl, Shopify GraphQL Admin API, GitHub Actions, pytest
- Also shows
- CI/CD, REST and GraphQL APIs, Debugging, Requirements gathering, Testing, Technical documentation, Monitoring and logging
- Status
- Engagement ran Apr 2026 to Aug 2026. Deployed and scheduled through GitHub Actions
