CSV imports fail late. Move structural checks into CI.
CSV imports fail late. Move structural checks into CI.
A CSV can parse in one tool and still fail in the importer that matters. The expensive part is not noticing a broken delimiter or uneven row. It is noticing after a release, when the file has already reached an operator or customer.
For generic structural failures, a small preflight step belongs next to the code that produces the file. The check should be deliberately narrow: encoding, headers, row width, quoting, and exact duplicate rows. It should not pretend to know business rules or silently discard data.
A bounded GitHub Actions pattern
This workflow runs a dependency-free public Action against one UTF-8 CSV path. It gives the job read-only repository permissions, scopes triggers to the file and workflow, limits execution time, and preserves the normalized output and issue report even when the check fails.
name: CSV preflight
on:
pull_request:
paths:
- data/catalog.csv
- .github/workflows/csv-preflight.yml
permissions:
contents: read
jobs:
csv-preflight:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- name: Check CSV structure
id: preflight
uses: softpeanut/csv-preflight-action@eb04c527a46ce3bc8bfc711fde8e93ca947597ae
with:
path: data/catalog.csv
- name: Keep diagnostic artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: csv-preflight-report
path: ${{ runner.temp }}/csv-preflight/
retention-days: 7
Pinning the Action to a full commit makes the third-party code immutable for that workflow review. if: always() is equally important: GitHub normally skips later steps after a failure, which would hide the report at the moment it is most useful.
What this catches—and what it does not
The checker reports invalid UTF-8, BOMs, empty or duplicate headers, uneven row widths, unclosed quoted fields, and exact duplicate rows. It emits a normalized CSV only when parsing completes. It does not delete duplicates, infer a target schema, run the destination importer, or guarantee that Shopify or any other external system accepts the result.
“The file stays on the runner” is a data-flow boundary, not a universal security promise. GitHub Actions and any uploaded artifact remain inside the repository’s trust boundary. Do not put secrets, regulated data, or customer exports into a repository merely because the validator itself makes no network request.
Reproduce it before adopting it
The Action source, tests, release tags, and a copy-ready guide are public:
- Source and release: https://github.com/softpeanut/csv-preflight-action
- Browser-local checker: https://softpeanut.github.io/csv-preflight/
- Full workflow guide: https://softpeanut.github.io/csv-preflight/validate-csv-github-actions.html
- Public 1,583-row case study: https://softpeanut.github.io/csv-preflight/csv-ci-case-study.html
The free path is complete for one generic file. If you want the same bounded workflow configured in one public repository, the separate fixed-scope service and its exclusions are published before payment: https://softpeanut.github.io/csv-preflight/ci-setup-terms.html
If this saved you time, the article can be zapped through the softpeanut tools Nostr profile. No personal information is needed.
Write a comment