CI-তে JSON ভ্যালিডেশন: jsonlint, ajv ও jq দিয়ে একটি পাইপলাইন
সংক্ষেপে: সিনট্যাক্স ভ্যালিডেশন প্রমাণ করে ফাইল পার্স হয়; স্কিমা ভ্যালিডেশন প্রমাণ করে ফিল্ড চুক্তি মানে। CI-তে গুরুত্বপূর্ণ হলো দ্ব্যর্থহীন exit code, এরর লোকেশন ও মেরামতের পথ।

মূল কথা: CI-তে jq empty-কে সিনট্যাক্স, ajv-cli-কে কাঠামো (check-jsonschema বহু-ফরম্যাট দলের জন্য) আর Prettier-কে স্টাইল হ্যান্ডল করতে দিন; স্তর অনুযায়ী চেইন করুন এবং প্রথম ব্যর্থতায় থামুন।
স্থানীয় এডিটরে পার্সার পাস করা মানে ফাইলটি CI-র যোগ্য নয়; CI পাস করা মানে ফিল্ড চুক্তি মেনেছে-ও নয়।
১. সিনট্যাক্স ভ্যালিডেশন ও স্কিমা ভ্যালিডেশন আলাদা করুন
"JSON ভ্যালিডেশন" বাক্যটি অনেক বেশি বিস্তৃতভাবে ব্যবহৃত হয়, এ কারণেই দলগুলো প্রায়ই দুই আলাদা সমস্যা সমাধানে এক টুল বেছে নেয়:
| স্তর | যে প্রশ্নের উত্তর দেয় | সাধারণ টুল | ব্যর্থতার অর্থ |
|---|---|---|---|
| সিনট্যাক্স | এই ফাইল বৈধ JSON কি? | jq empty, jsonlint, python -m json.tool |
ব্র্যাকেট, কমা, কোট বা এনকোডিং ভাঙা |
| স্কিমা | ফিল্ড, টাইপ ও required বৈশিষ্ট্য চুক্তি মানে কি? | ajv-cli, check-jsonschema |
ফাইল পার্স হয়, কিন্তু ব্যবসায়িক কাঠামো ভুল |
| স্টাইল | ইনডেন্টেশন ও কি-ক্রম সামঞ্জস্যপূর্ণ কি? | Prettier, jq -S |
ফরম্যাটিং অসামঞ্জস্যপূর্ণ, diff কোলাহলপূর্ণ |
এই স্তরগুলো একে অপরের বিকল্প নয়। যে ফাইল সিনট্যাক্স পাস করে সে স্কিমায় ব্যর্থ হতে পারে: {"price": "12.5"} বৈধ JSON, কিন্তু স্কিমা price-কে সংখ্যা দাবি করলে তা গ্রহণযোগ্য নয়। উল্টোদিকে স্কিমা যতই পূর্ণ হোক, ক্লোজিং ব্রেস বাদ পড়া ফাইল কখনো তার বিরুদ্ধে কম্পাইল হবে না।
তাই পাইপলাইনে আগে সিনট্যাক্স, তারপর কাঠামো, শেষে স্টাইল চালানো উচিত। কোনো স্তর ব্যর্থ হলে সেখানেই থামুন, নাহলে পার্স এরর নিজেকে ফিল্ড এরর হিসেবে ছদ্মবেশ দেয়।
২. সিনট্যাক্স স্তর: jq empty সর্বনিম্ন-নির্ভরতার বিকল্প
CI ইমেজে jq আগেই থাকলে সিনট্যাক্স ভ্যালিডেশন প্রায় বিনামূল্যে:
jq empty data.json
jq empty কোনো আউটপুট দেয় না; কেবল ফাইল পার্স করে। সফল হলে ০ এবং ব্যর্থ হলে অ-শূন্য কোড প্লাস লাইন/কলাম তথ্য দিয়ে exit করে। এতে এটি পূর্ণ Node নির্ভরতা-গাছ টানার চেয়ে হালকা, আর ভালো প্রথম গেট।
রিপোজিটরির JSON ফাইল ব্যাচে যাচাইয়ের সময় node_modules, বিল্ড আউটপুট ও তৃতীয়-পক্ষের ক্যাশে বাদ দিন:
find . -name '*.json' \
-not -path './node_modules/*' \
-not -path './dist/*' \
-print0 | xargs -0 -n1 jq empty
-print0 ও xargs -0 ফাইল নামে স্পেস হ্যান্ডল করে, আর -n1 প্রতি আহ্বানে এক ফাইল নিশ্চিত করে যাতে এরর মেসেজ অপরাধী ফাইল চিহ্নিত করে। শত শত ফাইল এক প্রসেসে দেবেন না; এরর মেসেজ একটিকে আরেকটি ঢেকে দেবে।
jsonlint আরেকটি প্রচলিত পছন্দ, সাধারণত npx jsonlint -q data.json হিসেবে চালানো হয়, যেখানে -q কেবল এরর ছাপে। এটি লাইন ও কলাম নম্বর রিপোর্ট করে, তবে রক্ষণাবেক্ষণ jq-এর চেয়ে কম সক্রিয়, আর ভিন্ন সংস্করণ BOM ও কমেন্ট ভিন্নভাবে সহ্য করে। এটিকে বেশি পাঠযোগ্য সিনট্যাক্স চেকার ভাবুন, স্কিমা ভ্যালিডেটর নয়।
৩. স্কিমা স্তর: ajv-cli validate-এর মূল কথা
Ajv JavaScript ইকোসিস্টেমের সবচেয়ে ব্যবহৃত স্কিমা ভ্যালিডেটর, আর ajv-cli এটিকে কমান্ড-লাইন টুল হিসেবে প্রকাশ করে:
ajv validate -s schema.json -d data.json
-s স্কিমা ফাইল আর -d যাচাই করা ডেটা নির্দেশ করে। ব্যর্থ হলে প্রসেস অ-শূন্য exit করে, তাই CI সঙ্গে সঙ্গে ব্যর্থ হয়। বাস্তব প্রকল্প সাধারণত দুইটি ফ্ল্যাগ যোগ করে:
npx ajv-cli validate \
-s schemas/order.json \
-d 'fixtures/orders/*.json' \
--strict=false \
--errors=text
--strict=false পুরোনো স্কিমায় সাহায্য করে: strict মোড অজানা কীওয়ার্ড ও সন্দেহজনক required ঘোষণাকে এরর ধরে, যা অনেক লেগ্যাসি স্কিমা ব্যর্থ করে। --errors=text মানুষের-পাঠযোগ্য আউটপুট দেয়, অথচ ডিফল্ট JSON কাঠামো মেশিনের জন্য ভালো। ফলাফল কে ব্যবহার করবে তার ভিত্তিতে বাছুন।
স্কিমার draft সংস্করণও মিলতে হবে। Ajv-এর প্রধান সংস্করণ ডিফল্টে ভিন্ন draft সমর্থন করে, আর ajv-cli --spec=draft7, --spec=draft2019 বা --spec=draft2020 দিয়ে একটি বেছে নিতে পারে। স্কিমা যদি $schema: "https://json-schema.org/draft/2020-12/schema" ঘোষণা করে কিন্তু CLI একে draft-07 হিসেবে কম্পাইল করে, কিছু কীওয়ার্ড নীরবে উপেক্ষিত হয়। স্কিমা লেখা ও সংস্করণ-পার্থক্যের জন্য দেখুন JSON Schema শুরু করা।
ফরম্যাট ভ্যালিডেশন আরেকটি ঘন ঘন প্রয়োজন। format কীওয়ার্ড ডিফল্টে কেবল টীকা; email, date-time ও uri সত্যিই কিছু enforce করতে হলে ajv-formats লোড করতে হবে:
npx ajv-cli validate -c ajv-formats -s schema.json -d data.json
এই ধাপ সহজে বাদ পড়ে, ফলে একটি ইমেইল ফিল্ড not-an-email গ্রহণ করে। শিপিংয়ের আগে পাইপলাইনে স্পষ্টভাবে একটি অবৈধ রেকর্ড দিন; এটি দশবার ডকুমেন্টেশন পড়ার চেয়ে ভালোভাবে প্রমাণ করে নিয়মটি সক্রিয়।
check-jsonschema: একাধিক ফরম্যাট ও বন্ধুত্বপূর্ণ এরর
দল JSON ছাড়াও YAML, TOML বা GitHub Actions কনফিগারেশন হ্যান্ডল করলে check-jsonschema বেশি সুবিধাজনক:
check-jsonschema --schemafile schemas/config.json config/app.json
check-jsonschema --schemafile schemas/pipeline.json .github/workflows/ci.yml
এর ব্যবহারিক সুবিধা:
- নেটিভ YAML সমর্থন, যা "JSON-আকৃতির কিন্তু YAML-এ লেখা" ফাইলে মানায়, যেমন Kubernetes ম্যানিফেস্ট ও CI কনফিগারেশন।
- এররে ফাইল পাথ, লাইন নম্বর ও ফিল্ড পাথ থাকে, লোকেশন খরচ কম রাখে।
- একটি pre-commit হুক দেওয়া হয়, তাই সংযোগ সস্তা।
- SchemaStore থেকে সাধারণ স্কিমা আনতে পারে, আবার স্থানীয় ফাইলও নেয়, নেটওয়ার্ক নির্ভরতা এড়ায়।
ক্রস-ভাষা দলে Python প্রায়ই আগেই থাকে, তাই check-jsonschema ইনস্টল ও আপগ্রেড করা Node টুলচেইন রক্ষণাবেক্ষণের চেয়ে হালকা। jq-কে সিনট্যাক্স আর এই টুলকে কাঠামো হ্যান্ডল করতে দিন; সমন্বয়ের রক্ষণাবেক্ষণ খরচ খুব কম।
৪. Exit code-ই CI-র একমাত্র বিশ্বস্ত সংকেত
আউটপুট যতই পালিশ হোক, CI কেবল exit code পড়ে। তিনটি ফাঁদ প্রচলিত:
| লক্ষণ | কারণ | সমাধান |
|---|---|---|
| এরর আছে কিন্তু CI সবুজ | কমান্ডের শেষে ` | |
| এরর নেই কিন্তু CI লাল | diff "পার্থক্য পাওয়া" কে ব্যর্থতা ধরে |
"পার্থক্য" ও "এক্সিকিউশন এরর" আলাদা করুন |
| এরর আউটপুট কাটা | সমান্তরাল বহু-ফাইল আউটপুট নিজেকে ঢেকে দেয় | ক্রমান্বয়ে চালান বা প্রতি ফাইলে ছাপুন |
jq-এর exit-code সেমান্টিক্স মনে রাখুন: সাধারণ পার্সিং ০ দেয়; -e সহ এক্সপ্রেশনের ফল false বা null হলে ১ দেয়; ব্যবহারের এরর ও অনুপস্থিত ফাইল ২ দেয়; স্ক্রিপ্ট কম্পাইল এরর ৩ দেয়। স্কিমা টুল সাধারণত সফলতায় ০, ভ্যালিডেশন ব্যর্থতায় ১, আর টুল বা আর্গুমেন্ট এররে ২ ব্যবহার করে। র্যাপার লেখার সময় প্রতিটি অ-শূন্য কোডকে "ডেটা ভুল" ভেঙে ফেলবেন না, নাহলে পরিবেশের ব্যর্থতা বিষয়বস্তুর সমস্যা ভুল নির্ণয় হবে।
৫. pre-commit: commit-এর আগেই সমস্যা ধরুন
pre-commit-এর মূল্য "আরেকটি চেক" নয়; বরং দশ মিনিটের CI রানের পরে নয়, এক মিনিটে ডেভেলপারকে ফিডব্যাক দেওয়া। ন্যূনতম কনফিগারেশন এমন:
repos:
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.33.0
hooks:
- id: check-jsonschema
files: ^configs/.*\.json$
args:
- --schemafile
- schemas/config.json
- repo: local
hooks:
- id: jq-syntax
name: jq syntax check
entry: jq empty
language: system
files: ^configs/.*\.json$
দুটি খুঁটিনাটি গুরুত্বপূর্ণ:
filesকেবল যেসব ডিরেক্টরিতে ভ্যালিডেশন দরকার সেগুলোই মিলুক।node_modulesবা জেনারেটেড ফাইলের বিরুদ্ধে হুক চালাবেন না।- স্থানীয় হুক ধরে নেয় ডেভেলপার মেশিনে
jqইনস্টল আছে। এই দাবি নথিভুক্ত করুন, বা কন্টেইনারাইজড হুকে যান।
প্রকল্প আগেই Husky ও lint-staged ব্যবহার করলে সরাসরি মিলিয়ে দিন:
{
"lint-staged": {
"configs/*.json": ["jq empty", "prettier --check"]
}
}
মনে রাখুন lint-staged staged ফাইলের তালিকা পাঠায়, তাই কমান্ডকে একাধিক পাথ গ্রহণ করতে হবে। jq empty ও prettier --check দুটোই করে। হুককে পুরো রিপোজিটরি স্ক্যান করতে দেবেন না; commit লক্ষণীয়ভাবে ধীর হবে।
৬. GitHub Actions: কপি করার মতো একটি পাইপলাইন
নিচের ওয়ার্কফ্লো সিনট্যাক্স ভ্যালিডেশন, স্কিমা ভ্যালিডেশন করে এবং ব্যর্থতায় রিপোর্ট সংরক্ষণ করে:
name: json-validate
on:
push:
paths:
- 'schemas/**'
- 'fixtures/**'
pull_request:
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Syntax check
run: |
find fixtures -name '*.json' -print0 | xargs -0 -n1 jq empty
- name: Schema check
run: |
npx --yes ajv-cli validate \
-s schemas/order.json \
-d 'fixtures/orders/*.json' \
--strict=false \
--errors=text
paths ফিল্টার কেবল ডকুমেন্টেশন বদলালে ভ্যালিডেশন ট্রিগার হওয়া আটকায়। npx --yes পিন করা নির্ভরতাহীন রিপোজিটরিতে মানায়; প্রোডাকশন প্রকল্পে ajv-cli devDependencies-এ যোগ করে সংস্করণ পিন করা উচিত। লক না থাকলে ডিফল্ট draft বা strict মোড বদলানো একটি আপস্ট্রিম রিলিজ রাতারাতি পাইপলাইন লাল করতে পারে।
৭. এরর লোকেশন: লাইন নম্বর থেকে ফিল্ড পাথে
ভ্যালিডেটরের অর্ধেক মূল্য সনাক্তকরণ; বাকি অর্ধেক লোকেশন। তিন ধরনের আউটপুট পড়তে শিখুন:
- সিনট্যাক্স এরর:
jqলাইন ও কলাম রিপোর্ট করে, প্রায়ই কাছের টুকরোসহ। আগে ব্র্যাকেট ও কমা ঠিক করুন; সঙ্গে সঙ্গে এনকোডিং সন্দেহ করবেন না। - স্কিমা এরর: Ajv-এর
instancePathডেটার লোকেশন দেখায় (যেমন/items/0/price),schemaPathনিয়ম দেখায় (যেমন#/properties/items/items/properties/price/type), আরkeywordভাঙা বাধ্যবাধকতার নাম দেয়। - ফরম্যাট এরর:
formatচেক ব্যর্থ হলে মেসেজ প্রায়ই কেবল বলে "must match format "email"". ফিল্ড চিহ্নিত করতেinstancePath-এর সঙ্গে মেলান।
instancePath তদন্ত-রেকর্ডে পেস্ট করুন; JSON-এর দেয়ালের চেয়ে এটি অনেক বেশি কাজে লাগে। অ্যারেতে সংখ্যাযুক্ত ইনডেক্সও বলে দেয় কোন রেকর্ড ব্যর্থ হয়েছে। ব্যবসায়িক কি বেশি সহজবোধ্য হলে পরে একটি ম্যাপিং লাইন যোগ করুন, যেমন items[0].id = ORD-042।
সাধারণ স্কিমা ভুল, যেমন additionalProperties: false অতিরিক্ত ব্যবহার বা আসলে ওভারল্যাপ করা oneOf শাখা লেখা, আলোচনা করা হয়েছে JSON Schema জেনারেশনের সাধারণ ফাঁদ-এ। ভুল ভ্যালিডেশন নিয়ম ভ্যালিডেশনহীনতার চেয়ে বেশি বিপজ্জনক, কারণ এটি নিরাপত্তার মিথ্যা অনুভূতি তৈরি করে।
৮. টুল বাছাইয়ের টেবিল
| প্রয়োজন | প্রস্তাবিত টুল | কেন | সতর্কতা |
|---|---|---|---|
| কেবল সিনট্যাক্স | jq empty |
শূন্য নির্ভরতা, দ্রুত, স্পষ্ট exit code | ব্যাচ রানে ফাইল নামে স্পেস |
| বন্ধুত্বপূর্ণ লাইন/কলাম এরর | jsonlint |
পাঠযোগ্য আউটপুট | রক্ষণাবেক্ষণ সক্রিয়তা সীমিত |
| JSON Schema ভ্যালিডেশন | ajv-cli |
পরিপক্ব ইকোসিস্টেম, কাঠামোবদ্ধ এরর | draft সংস্করণ মিলান ও format সমর্থন লোড করুন |
| মিশ্র JSON ও YAML রিপোজিটরি | check-jsonschema |
বহু-ফরম্যাট, pre-commit বান্ধব | স্কিমার উৎস ও নেটওয়ার্ক অ্যাক্সেস |
| commit-এর আগে দ্রুত ফিডব্যাক | pre-commit / lint-staged | কেবল staged ফাইল প্রসেস করে | হুককে একাধিক পাথ গ্রহণ করতে হবে |
| সামঞ্জস্যপূর্ণ ফরম্যাটিং | Prettier | ব্যাপকভাবে গৃহীত | ফাইল আবার লিখতে না চাইলে --check ব্যবহার করুন |
শেষ একটি সুপারিশ: আগে স্পষ্টভাবে একটি অবৈধ রেকর্ড দিয়ে পাইপলাইন সত্যিই ব্যর্থ হয় কি না যাচাই করুন। সবচেয়ে বড় ঝুঁকি জটিল কনফিগারেশন নয়; এমন কনফিগারেশন যা কখনো কার্যকর হয় না। "এটি ব্যর্থ হতে পারে" কে গ্রহণযোগ্যতার মানদণ্ড ধরা "এটি পাস করে" ধরার চেয়ে বেশি নির্ভরযোগ্য। বিস্তৃত কমান্ড-লাইন টুলচেইনের জন্য এগিয়ে যান এডিটর, কমান্ড লাইন ও অটোমেশন।
সাধারণ প্রশ্ন
jq empty নাকি jsonlint ব্যবহার করব?
CI-তে jq empty প্রাধান্য দিন: শূন্য নির্ভরতা, দ্রুত স্টার্টআপ, স্পষ্ট exit-code সেমান্টিক্স। বন্ধুত্বপূর্ণ লাইন/কলাম মেসেজ দরকার বা স্থানীয়ভাবে তদন্ত করলে jsonlint ব্যবহার করুন। দুটোই কেবল সিনট্যাক্স ভ্যালিডেশন করে; কোনোটিই ফিল্ডের কাঠামো চেক করে না।
ajv অজানা কীওয়ার্ড রিপোর্ট করে কেন?
আগে নিশ্চিত করুন স্কিমার $schema draft CLI-র ব্যবহৃত সংস্করণের সঙ্গে মেলে। অনেক "unknown keyword" এরর আসলে পুরোনো কম্পাইলারের সামনে পড়া নতুন-ড্রাফট কীওয়ার্ড। সংস্করণ মিললে বানান ভুল দেখুন। --strict=false লেগ্যাসি স্কিমা চালু রাখতে পারে, তবে আসল সমস্যা লুকাতে দীর্ঘমেয়াদে এর উপর নির্ভর করবেন না।
স্কিমা ভ্যালিডেশন পাস করে, তবু API ব্যর্থ হয় কেন?
স্কিমা কাঠামো যাচাই করে, ব্যবসায়িক সেমান্টিক্স নয়। যেমন status বৈধ এনামসহ স্ট্রিং হতে পারে, অথচ স্টেট মেশিন paid থেকে সরাসরি refunded-এ যাওয়া নিষিদ্ধ করে। এ ধরনের নিয়ম কোড বা ডেটাবেস বাধ্যবাধকতায় থাকে, JSON Schema-তে নয়।
ব্যর্থ ফিল্ড দ্রুত কীভাবে খুঁজব?
টুল আউটপুট থেকে instancePath বা লাইন নম্বর পড়ুন। Ajv-এর instancePath JSON Pointer যা সরাসরি ডেটা দেখায়; check-jsonschema ফাইল ও লাইন রিপোর্ট করে। পাথটি রেকর্ড করে ব্যবসায়িক কি-তে ম্যাপ করুন; সমস্যা খুঁজতে সাধারণত কয়েক মিনিট লাগে।
pre-commit কি পূর্ণ ভ্যালিডেশন চালানো উচিত?
না। pre-commit কেবল staged ফাইল প্রসেস করুক যাতে commit দ্রুত থাকে। পূর্ণ স্ক্যান CI বা নির্ধারিত জবের কাজ। বিভাজন সরল: স্থানীয় রান গতিকে প্রাধান্য দেয়, CI সম্পূর্ণতাকে।