Cіпласпластик
رفتن به کانال در Telegram
🇺🇦 Про айті та дотичні теми загалом, ну й трохи про C++. Усі думки тут відображають виключно мій особистий погляд. Мої емоджі: https://t.me/addemoji/AdaptiveDevIcons https://t.me/addemoji/VehicleBrands
نمایش بیشتر672
مشترکین
اطلاعاتی وجود ندارد24 ساعت
+37 روز
+830 روز
آرشیو پست ها
Сідаєш такий разом з ШІ-шкою за прогу на Go (мову, котру я навіть ніколи окремо не вчив, але доволі успішно використовую), витрачаєш дві години, і гоп — готова тула. Все стабільно працює, всі потрібні фічі, які запланував, один бінарь і навіть вбудований Web UI!
Сідаєш з ШІ-шкою й усім своїм багатолітнім досвідом за прогу на C++, просираєш чотири години… Результат: вдалося нарешті підключити сторонню лібу для парсингу KDL так, щоб вона зібралася без помилок. Вельми очевидно й прямолінійно все: Xmake качає з ґітгаба архів релізу, розпаковує, знаходить там
CMakeLists.txt, накладає на нього щойно написану латку, без котрої ніц не працювало, збирає CMakeʼом і вуаля — магія!
Потім, правда, виявилося, що бібліотека для всіх рядків використовує виключно std::u8string — такий новий сучасний тип для UTF-8 з C++20. Чудово, що в C++ нарешті його додали, бо як же без UTF-8 у 20-х роках.
Щоправда… схоже, вони забули додати ще дещо. Наприклад, бодай якісь функції чи типи, які б з ним працювали‽!! Може std::u8stringstream? Ні, для потоків є тільки спеціалізації для char та wchar_t, а тут char8_t — він того ж розміру, вирівнювання й знаку, що й unsigned char, але все-таки окремий. Ну може тоді хоча б std::ifstream/std::ofstream? Нє, теж ні. Файли нам читати-писати не треба, як я бачу, ок. Ну, принаймні std::filesystem::path з такого можна сконструювати.
Доведеться, видно, рядки туди-сюди між типами ганяти. Яке щастя, що в Qt є QString, який всередині використовує char16_t, але дозволяє конвертувати з або в std::string. А для конвертації між std::string та std::u8string є чудові стандартні функції з красномовними назвами std::mbrtoc8 та std::c8rtomb (пропонуйте ваші варіанти, що це може значити).
Враховуючи, що в моїй програмі все одно є вбудований рушій 💻, я, якщо чесно, вже думав, чи не буде легше просто там той 🤗 парсити 😆
І наостанок тримайте ще історію про 🦶. Треба було мені того тижня зробити одну хєрню, і єдина тулза для цього, котру я знайшов, була написана якимсь нонеймом на колінці 10 років тому якраз мовою Go в ґіт-сховищі на декілька зірок. Тільки версія під macOS з релізів у мене не запрацювала, бо, підозрюю, що вона досі 32-бітна може бути абощо. То я стягнув собі репозиторій, щоб зібрати власноруч. Не знаю, яка там версія компілятора чи бібліотек була 10 років тому в чувака, але в мене-то найсвіжіша! Запускаю go build і… нуль помилок. Усе працює 🙂
Треба зменшувати використання сі-плас-пластика & Go touch grass.Кореш підкинув блог чувака, що робив дуже крутецькі інтерактивні статті. Обожнюю такі й навіть трохи колекціоную. Шкода, що їх доволі важко й довго робити, тому мало хто цим займається. От і цей автор навіть намагався якось монетизувати своє хобі, та, видно, не вийшло — з кінця 2024 року вже нічого нового не публікував.
Мені до вподоби прийшлися зокрема статті про те, як влаштований механічний годинник, як працює GPS або як працюють лінзи в камерах. Дуже цікаво, детально й загалом круто.
Доволі важко писати після такої перерви й таких подій. Але насправді я багато прикольного дізнався й зробив за останній час у спробах відірватися від реалій, тож варто розповісти.
Наприклад, як і в будь-якого поважного айтівця, є в мене такий собі home lab. Ви-то всі кубернетіси та кластери крутите, я знаю, але мені вистачає Docker 💻 + Tailscale 💠.
І щоб не тільки в чорну консоль витріщатися, я користуюся Portainer: там можна і контейнерами керувати, логи там почитати абощо, підняти стек з компоуза, навіть є кривенький механізм деплоя через Git 💻.
Так ось того тижня я викинув його на звалище, бо поставив собі Dockhand. Абсолютно випадково натрапив, і він мені одразу припав до душі, бо зроблений якось… правильно… хз. Він банально зручніший у всьому. Відкриті сирці, звісно (якийсь поляк педалить: Bun, TypeScript, Svelte — норм). Підіймається на раз-два.
На VPS поставив собі агента (Hawser), щоб можна було віддалено керувати. Він до речі на 🦶 написаний, тож можна прямо бінарем запускати без докера.
У Dockhand швидкий інтерфейс, усілякі приколи з переглядом логів одночасно з декількох контейнерів (типу як у Dozzle) тощо. А головне — значно зручніший GitOps, як на мене! Можна деплоїти купу всього з одного ґіт-сховища, як я й роблю, але при цьому не треба всюди окремо прописувати токени доступу — достатньо зробити це один раз. Ще він вміє о́брази на вразливості сканувати. Бекапів поки нема, але обіцяють найближчим часом додати. Ще б оце якось до нього 1Password прикрутити, щоб не палити паролі у змінних оточення. Треба глянути, наскільки складно буде підвайбкодити. А, ну й SSO налаштувати.
Отже, це рішення я прийняв дуже швидко: про Portainer можна забути — хоч якась радість серед усіх цих інфраструктурних питань.
+1
Як ніби всіх труднощів 2025-го було недостатньо, цей рік ударив дуже сильно з самого початку.
Увечері третього січня наш найкращий у цілому світі кіт Осінь був вʼялуватий, тож я повіз його вночі до клініки, де його геть неочікувано для нас залишили під наглядом в стаціонарі з діагнозом діабетичного кетоацидозу. Виявилося, що він діабетик. А за кілька днів ми дізналися, що в нього ще й з серцем проблеми, які вдалося виявити лише на УЗД. І після майже тижня тяжкої боротьби за життя наш кіцюнька врешті пішов з цього світу.
Немає в жодній мові слів, достатніх описати весь відчай, біль і горе, які звалилися на нас з дружиною. Але ось вже місяць, як Осені нема. Перші тижні я був абсолютно недієздатний: не працював, передивився всі улюблені «добрі» фільми й серіали, які вже знаю напамʼять, зустрівся разок з друзями, пройшов разом з дружиною Зельду — словом, робив усе, аби дати психіці зачепитися хоч за щось знайоме, стабільне й бодай трохи позитивне. Тоді ми плакали перманентно, а зараз у середньому лише раз на день. Я хз, яка це вже спроба написати цей текст, але я знову плачу. Абсолютно все нагадує про Осінь: предмети, звуки, усі наші звички — все якось повʼязано з ним. Інколи дуже раптово накриває прям, бо здалося, що він увійшов до кімнати абощо. У ті моменти, коли він зазвичай спав, якось полегше. Хоча в цілому я можу стверджувати на 200%, що це найтяжчий період у моєму житті.
Окремо варто сказати, наскільки давить менталка. Украй складно позбутися тривоги, почуття провини, думок штибу «а треба було…», «а от якби ж я тільки…» тощо. Багато чого миттєво втратило сенс. Якісь покупки, котрим міг радіти ще декілька тижнів тому, зненацька стали настільки незначним дрібʼязком, що аж гидко від себе. Лише підтримка один одного допомагає. І, як не дивно, розповіді інших людей теж. Декілька друзів розповіли власні аналогічні історії з минулого, а дружина знайшла цілий сабреддіт, де люди щодня пишуть свої. Розуміння, що буквально в цю секунду тисячі інших людей по всьому світові відчувають те саме, трохи врівноважує.
⁂
Наш Осінь — найкращий кіт. Він найдобріший, дуже розумний і, як, певно, і всі орієнтали, — надзвичайно балакучий. Протягом девʼяти років (це так мало!) ми завжди були разом, навіть у подорожах. І загалом робили все можливе, щоб він був по-котячому щасливий, віддавали йому всю любов і турботу, на які спроможні. А він у відповідь обожнював і дружину, і мене — по-різному, але однаково сильно. І я думаю, що нам вдалося забезпечити йому чудове життя.
Сказати, що Осінь був моїм другом, — замало. Я переконаний, що він забрав у нас з дружиною по частині душі з собою, але нам для нього і не шкода. Бо він по собі не лишив порожнечу. Зараз досі невимовно болісно, і серце крається, але з часом це, мабуть, вщухне, і з нами лишиться його любов і памʼять про всі ті чудові моменти, які він нам подарував. Я дуже вдячний дружині, яка колись вибрала саме Осінь, другану, котрий допоміг його привезти, усім, хто нас підтримав, і загалом життю за наданий шанс прожити ці коротенькі девʼять років з найкращим у світі котом.
Отож тепер потроху повертатимуся до тями й почну, мабуть, знову писати сюди. Навряд чи часто, втім.
2025 рік був складним у багатьох аспектах. Подекуди занадто складним. Але ми з вами досі тут, а це чогось та варте! Погляньмо на наші з вами результати в цифрах 👇
Враховуючи, що останні пару місяців я дуже багато займаюся тим, що перекладаю JSONʼи з місця на місце, колупаю CSV і тицяю патиком SQLite-базу, можу впевнено заявити: нічого краще за Nushell для цього ще не вигадали!
Усілякі там фільтрації, агрегації, щось звідкись дістати, транспонувати, перекрутити, згорнути список — це все робиться в декілька рядків легкого для сприйняття коду. Не треба вчити всратий синтаксис
jq, не треба писати скрипти на пайтоні, щоб перегнати один формат в інший — лише кілька одних і тих самих команд, і вуаля! З базових композитних типів там структури, списки й таблиці, причому останні еквівалентні першим, вкладеним у другі 🙂 А потім хоч читайте та пишість JSON 💻, хоч CSV, хоч TOML 💻 якийсь — вже без різниці.
Але інколи все ж буває зручніше зробити щось нашвидкоруч в UI. І якщо Excel вам для цього здається заважким, то я оце знайшов і спробував легшу прогу: SmoothCSV. Вона виявилася вельми непогана! Нічого надзвичайного, але під мій випадок вона якраз ідеальна. І там навіть є змога писати SQL-запити по CSV-шках, хоча я не пробував.Закладаюся, що більшість з вас не чула про мову Rye.
Я досі мрію, що для моєї улюбленої мови Red 🔺 колись вийде повноцінний реліз. А допоки цього не трапилося (і насправді вже не трапиться), звертаю увагу на будь-що інше, де REBOL 💻 вказаний як джерело натхнення. І якщо Red — це буквально той самий REBOL з додатковими фічами, то Rye зроблений скоріше «за мотивами», хоча спільних рис достатньо.
По-перше, він так само гомоіконний: між даними та кодом немає жодної різниці — це все лише блоки слів, які набувають якогось конкретного значення в контексті. Але якщо в ліспах списки на кшталт
(+ 100 500) обчислюються одразу, хоча цьому можна запобігти додавши якийсь апостроф або ще щось залежно від діалекту, то в Rye (як і в REBOL) навпаки — нічого само по собі не обчислюється, а треба робити do { _+ 100 500 }.
По-друге, у мові фактично нема якихось ключових слів або спеціальних конструкцій. Усі if, for, while і ще купа усіляких — це звичайні функції, які приймають або скалярні значення, або ті ж блоки. Виходить, що той do зазвичай вже є десь усередині них, тож на практиці у своєму коді бачиш його не часто.
Ну а ще виходить (по-третє), що можна створити контекст, в якому цих усіх функцій не буде взагалі, зате можна наповнити його власними штуками. Це активно використовується для створення діалектів. Останні є фактично eDSL, і вони дуже різноманітні, що як добре, так і погано з обʼєктивних причин.
Чисто з погляду на синтаксис Rye трохи сумнівний місцями. Не на мій смак. Трохи приємніше за ліспи, але досі гірше, ніж REBOL чи Red. Думаю, вся справа в дужках: в ліспах використовуються переважно круглі, і їх багато, тут же їх менше, але вони фігурні, а в REBOL дужки квадратні! Що це значить? Правильно — що не треба тиснути Shift! Оптимізація процесів 😀
Ще є всілякі приколи, як можна записувати одні й ті самі слова по-різному:
add: pfn { a b } { a + b } ; pure btw!
div: pfn { a b } { a / b }
div ( add 3 9 ) 6 ; 2.000000
( 3 .add 9 ) .div 6 ; 2.000000
3 .add 9 |div 6 ; 2.000000
Тулити щось прямо до дужок не можна — обовʼязково ставити пробіл. Хз, мені не дуже це подобається.
Тепер трохи про мінуси. Одним з помітних недоліків є те, що ніхєра не працює. Ну може не геть прям усе, але за той час, упродовж якого я намагався розвʼязати задачі, я бачив і функції, які працюють не так, як описано, і якісь внутрішні краші. Документації дуже мало, деяка взагалі застаріла. Врешті довелося плюнути на це діло й закрити третій день за допомогою 🆕, яка дивним чином до речі схожа синтаксично, але звісно не настільки гнучка. (А четвертого дня — учора — взагалі розвʼязував задачі на 💻. Кочуся потроху донизу).
Сам інтерпретатор до речі на 🦶 написаний, що потенційно дозволяє легко кроскомпілювати програми під будь-які системи, але я не перевіряв.
У підсумку скажу, що лишився трохи розчарований. Чувак-автор — молодець, але видно, що йому не вистачає чи то наснаги, чи то часу, щоб стабілізувати й розвивати мову швидше. Навіть логотип ШІ-шний. Тож лишаємося ми й надалі без сучаснішого нащадка REBOL.Сьогодні не про Advent of Code. Вчора часу на це не було — наклепав якесь сумнівне рішення на SWI-Prolog 🦉, про який вже раніше писав. Додати нема чого.
Натомість розповім про #тулзи для дво- та тристоронніх дифів. Спробував нещодавно Kaleidoscope.app: доволі красива програма, є прикольні фічі й усе таке, але хз — лишилося відчуття сируватості… Тож коли тріал закінчився, просто видалив її.
За ці роки пробував багато різних програм. Але досі не бачив нічого кращого за Araxis Merge, яким користувався вперше ще років 20 тому. Єдина проблема, що ліцуха коштує 270 баксів — якось забагато для інструмента, котрий не те щоб критично часто трапляється в пригоді. Тож порівняння всі робив просто у VS Code 💻.
А в неділю зайшов до них на сайт і побачив, що вони дають ліцензію тим, хто робить внески в open source. Тож написав їм на е-пошту, скинув приклади своїх PRʼів у той же Xmake, та й власні напрацювання деякі є — і шо ви думаєте? Наступного дня відповіли мені й дали Pro-ліцуху на рік 🥰
Якщо шукали собі щось подібне, можете спробувати. Програма трохи олдскульна й доволі аскетична, але справу свою робить добре!
Колись давно тимлід на роботі дав мені задачу зробити фічу на Perl 🧅, а сам пішов у відпустку. Я намагався розібратися з тим перлом, але мені настільки не зайшло, що не зміг себе змусити. Та й у щойно створеній команді ніхто мови знав. Тож я плюнув і переписав всі наявні на той момент напрацювання на Python 💻 + фічу, яку мав зробити. (Python насправді теж ніхто не знав, але всі хутко розібралися).
Тимлід, коли повернувся, дуже засмутився з цього приводу, бо не любив мови з відступами замість дужок, почав забивати на роботу, і врешті я став його тимлідом 👀 Та повернімося до програмування.
Perl, як відомо, це write-only мова. Я знаю людей, які добре в ній тямлять і роблять чудові речі, однак, я точно не серед них. Не люблю, коли в мові програмування забагато якихось мутних символів. Чого не можна написати нормальними словами‽
Взагалі хз, нащо я це все вам розповідаю. Ні, на перлі я не почав писати й не планую.
⁂
О, до речі ж розвʼязав учора задачі першого дня Advent of Code на Uiua 💻. Оцініть мій код 👇
Мова доволі нескладна, опановується швидко. А от концепції складнуваті — мозок вивертають місцями. Першу задачу я швидко накидав: там відносно проста згортка списку. У другій задачі в принципі теж вона, але вже довелося посидіти. Початкове рішення працювало чудово на тестових даних, але на реальних видавало некоректний результат, з чого стало очевидно, що там якесь виродження з нулями.
Обідня перерва на той час добігла кінця, тож дав розібратися з цим Claude Code. Він на диво непогано себе показав! Прям дуже довго щось дебажив та врешті досяг результата. Щоправда, розвʼязок був занадто не елегантний — з трьома додатковими функціями. Довелося викинути й написати своє рішення по-людськи. Я переконаний, що цією мовою можна написати ще краще, та я задоволений навіть цим результатом.
Цікаве спостереження про впровадження інструментів в європейському автомотіві (і, певно, ще багато де).
От робили колись всі дизайни для UI у фотошопі, і це здавалося зручно. А потім зʼявився Sketch, і всі дуже швидко перейшли на нього, бо він закривав цілу низку потреб: по-перше, це вектор, а не растр, по-друге, можна бібліотеку компонентів створювати було, перевикористовувати їх для різних скрінів тощо. Щоправда, тільки на macOS 🙁 Згодом зʼявилася Figma 💻, яка певні речі ще сильніше спрощувала, як то, наприклад, спільна робота над одним документом, можливість користуватися навіть у бравзері тощо.
Всі миттєво на неї перестрибнули… але не автомотів 🙁 Чому? Ну бо дані десь у хмарах, а Figma не запарювалася зробити варіант з виділеним сервером абощо. А раптом дизайн майбутньої системи кудись не в ті руки потрапить (промисловий шпіонаж досі існує, я думаю), раптом хвіґму ту вашу зламають — гігантські втрати грошей потенційно. Отак воно роками тягнулося, і все нарешті узгодили, домовилися й вирішили мігрувати в неї. Що ж змінилося для цього? А нічого! Дані досі в хмарах 😆 Просто криза підтискає, конкуренти в Китаї не сплять (а роблять чік-чік і в продакшн™), а німці досі скетч-файли пересилають імейлами.
Ну нічого… Краще пізно, ніж ніколи. Хоча деякі виробники тільки зараз оце поступово на фігму переходять. Та годі про неї.
Тепер же в нас всюди ШІ для програмування. Окрім автомотіва звісно. Чому? Ну бо дані в хмари кудись ідуть 😂 І я не про написання якихось критичних систем в автомобілях — там ясно, що є купа сертифікацій, які просто не пройдеш зі згенерованим ШІ кодом (а якщо пройдеш, і потім щось «вистрілить», то це ще гірше) — ні, я про внутрішні тулзи навіть. Спочатку було повністю заборонене будь-яке використання. Згодом почали потроху послаблювати: можна отримати ліцуху на умовний копайлот чи клод, щоб потестувати, але спочатку треба створити заявку з детальним описом, нащо воно тобі, підписатися кровʼю, що використовуватимеш ШІ максимум для прототипів, які йдуть у смітник, почекати на апрув членів спеціальної ради, й тоді можна буде навайбкодити свій перший hello world.
Чи вважаю я, що це погано? Ні, не вважаю, бо дійсно є ціла низка нюансів, яким творці генеративних ШІ не приділяли значної уваги. Наприклад, використання чужих робіт для навчання своїх ВММ-ок, захист даних досі не надто переконливий, захист від нових векторів атак через ШІ-агентів тощо.
Чи знаю я, як правильно варто було б розвʼязувати питання активнішого впровадження ШІ? Ні, на це в мене теж рішення нема. Всі занепокоєння дійсно актуальні, ризики реальні й далі по списку.
Але що я знаю, так це те, що років за 3–5 жодні з цих питань так само не будуть вирішені. Навіть доволі ймовірно, що ще гірше стане. Але хтось з босів в автомотіві скаже: «та пофіг! впроваджуймо всюди ШІ!» — бо ринок змусить.
От тоді покатаємося 😂 (А взагалі я б, мабуть, не радив купувати моделі автівок пізніше за 2025 рік, бо хто його зна 😂)
Прочитав десь фразу:
30+ років — це той вік, коли з'ясовується, що ти навіть дихав усе життя неправильно.Але сьогодні буде не про дихання, а про чистку зубів 🦷🪥 В дитинстві мені показали «алгоритм»: повозюкав туди-сюди щіткою, сполоснув рота від пасти, виплюнув — ну я так роками й робив, а шо. Деякі сумніви почали зʼявлятися з появою в мене іригатора. Це такий прикольний пристрій, який фігачить воду під тиском. Дуже раджу, до речі. Так от я просто додав його у свій алгоритм: якщо все одно споліскувати рота водою від пасти, то чого б не робити це іригатором? Логічно? Логічно. В який момент навіть додавав туди Listerine. Знаєте ж цього виробника? Вони роблять ополіскувач для ротової порожнини якраз — по всьому світу продається. Взагалі-то спочатку вони свою «формулу» намагалися впарювати людям як засіб для миття підлоги, як ліки проти гонореї тощо — але не пішло. Зате як ополіскувач для рота зайшло аж бігом. Можете віддячити їм за світову одержимість «свіжим подихом», бо до нього це не було прям такою проблемою серед людей. (Тобто, проблема-то насправді була, але люди це не сприймали як проблему). Але повернімося до теми. Прикол іригатора в тому, що як би ретельно і вправно ти не чистив зуби, щітка ніколи настільки добре не вичистить усі щілини від залишків їжі, як він. Після іригатора з рота летить усе шо можна, аж з-під ясен якісь рештки дістає, їй-богу. Топова штука. І якось воно дивно виходить… ну, тобто… ти чистиш зуби щіткою й пастою, а коли потім «споліскуєш» іригатором, то виявляється, що ще пів рота хавки. Дурня якась. Мене, щоправда, не одразу осяяло. Ще десь рік-другий я в такому режимі й продовжував користуватися. А потім я-я-як збагнув! Треба ж їх місцями поміняти! Спочатку іригатор, потім щітка з пастою, потім сполоснув, щоб паста ротову порожнину не розʼїдала своєю мʼятою. І прям усе на свої місця встало! Ну, майже все, точніше. Подумав я про це ще рік, і почав схилятися до того, що якось тупо наносити пасту й тут же її змивати. У чому сенс, себто? Ще й зуб один став трохи чутливіший до холодного, на що стоматологиня якраз порадила пасту лишати на зубах. Капе-е-ець! Тепер-то реально все на своїх місцях. Це ж як автомийка: навряд чи ви спочатку наносите всілякі захисні шари на тачку, а потім миєте її від бруду. І раптом виявилося, що усі ці написи на зубних пастах типу там sensitive repair або whitening — це не стовідсоткова маячня рекламна. Воно справді діє й робить краще, якщо тільки дати йому змогу. А деякі пасти навіть не настільки й мерзенні, щоб їх лишати на зубах. Закладаюся, що ви-то все життя правильно робите. І що все це давно відомі факти, які ґуґляться за 2 хвилини. Однак річ у тім, що ґуґлити-то й не спадало на думку ніколи: ти просто робиш те, що ніби знаєш, як робити. А деякі ще й примудряються інших навчати. Дивовижно все-таки, наскільки люди схильні за деревами лісу не бачити. І наскільки звичка впливає на наше сприйняття 😬
Якийсь чувак «зібрав докупи свої нотатки» про розробку UI-фреймворків. Вийшло понад 200 сторінок 😯 Доведеться полистати, бо це одна з моїх найулюбленіших тем.
(Посилання підрізав у пана Лютікова в «Шось про айтішку»).
Вкотре хочу зачепити тему систем побудови, але цього разу говоритиму не про якусь конкретну.
Прочитав пару тижнів назад у пана К. (в «Мамкіному Архітекторі») допис із закликом писати в коментарі, хто чим користувався. І там хто про шо — від
make до Jenkins. Воно й дійсно різниця не завжди очевидна, бо і те, й інше може виконувати приблизно ту саму роботу, а саме: запускати якісь скрипти, що викликають тули, туди-сюди перекладають файли, качають щось з інтернетів тощо.
Раніше я вже казав, що люблю, коли можна повністю зібрати й запустити проєкт однією командою, проте, так було не завжди. Користувався я й TeamCity, і Jenkins і ще бозна-чим. Коли GitHub Actions зʼявилися, я одразу на них перестрибнув через їхню (нібіто) простоту. Тож білд-система в мене щось компілювала базово, а потім вже Actions хоп-хоп файли кудись з теки у теку поклали, заархівували або викликали скрипт для інсталятора й оце все. Іншими словами, побудова проєкту була розмазана між білд-системою і CI.
Це незручно з низки причин.
По-перше, CI-ні скрипти дуже важко запускати локально: на вашому CI-сервері є власне оточення, нерідко доволі крихке 😆 гг, а також є купа додаткових штук для якоїсь там безпеки та ізоляції (щоб не вийшло, що дві джоби якось конфліктують) — це все важко, та й не сильно треба, повторити локально. І якщо частину важливої роботи робить саме CI, то в якийсь момент може виявитися, що ніхто не знає, як той проєкт взагалі зібрати без нього. Небезпечна залежність, ще й потенційно «вузьке місце».
По-друге, якщо щось піде не так на CI, а щось раз у раз йде не так, то полагодити це стає значно складніше. Навіть діагностувати не завжди легко, особливо якщо локально важко запустити. Підтримкою неперервної інтеграції найчастіше займається окрема команда, доступи до всього є тільки в них, специфіку й нюанси оточення розуміють тільки вони — інша залежність і вузьке місце ланцюжка.
Тобто є сенс якомога більше речей виводити на рівень нижче (що корелює з загальним розумінням і практиками) — у білд-систему. Чи можна було б наробити 🐈 Actions для виклику окремо компілятора, окремо компонувальника, окремо тулів для підписування бінарів ключем? Та можна, але чомусь ніхто так не робить. Беручи це до уваги, стає очевидним, що інші задачі також краще виконувати на рівні білд-системи: встановлювати залежності, пакувати документацію, збирати інсталятори тощо.
А чи є сенс іти ще на рівень нижче — у саму програму? Мабуть можна й без білд-системи обходитися, але тут вже треба оцінювати складність і вартість. Наразі не маю для цього хороших прикладів використання на думці.
Урешті в тому проєкті ми досягли відносного успіху. Головна частина нашої джоби на CI мала отакий вигляд:
- name: Create package
run: qbs build config:release profile:github-${{ matrix.os }}-ci qbs.architecture:${{ env.QBS_ARCH }} -p megaapp-${{ matrix.os }}-installer
Буквально одна команда, яка все збирає і пакує. Але необхідні залежності вже були в оточенні (компілятор + Conan + Qt). А зараз з Xmake навіть це не обовʼязково, бо він вміє все сам стягувати.
Розумію, що в декого «поцікавіше» ситуації бувають, коли локально в принципі важко щось запустити, бо треба мільйон контейнерів підняти в кластері. Можу хіба що поспівчувати, бо це дійсно складно без належної організації процесів 🙂 Утім навіть у таких умовах можна зробити зручно, я певен.Розкажу вам уберлайфхак для 💻 VS Code (як мінімум):
Виявляється, що та висота рядків, яку редактор якось обчислює «автоматично», — це повна хєрня. Не памʼятаю вже, чого я поліз дивитися налаштування, але якщо поставити для editor.lineHeight якесь адекватне значення, то раптом стає ЗНАЧНО ліпше 🤩
Першу хвилину я через звичку ще сумнівався, але виграш очевидний: щільність інформації вища, очі важливий контекст захоплюють краще — в результаті мозку легше 🧠
#TIL на ґітгабі 🐈 в налаштуваннях можна ввімкнути режим високої контрастності. Значно ліпше виглядає!
До речі про Steam 💨
Днями вирішив зробити собі невеличку базу ігор, яку заповнювати, звісно, руками не будеш. Так виявилося, що у стіма є відкрита API-шка!
Можна отримати основну інформацію про гру, знаючи її ідентифікатор. Останній можна подивитися, наприклад, в урлі сторінки в магазині. І потім хоп-хоп — і вся інформація легко дістається:
let gameId = '1328670'
let info = http get https://store.steampowered.com/api/appdetails?appids=($gameId)
| get $gameId
| get data
І далі вже можна дивитися, шо там:
> $info.name Mass Effect™ Legendary Edition > $info.genres ╭───┬────┬─────────────╮ │ # │ id │ description │ ├───┼────┼─────────────┤ │ 0 │ 1 │ Action │ │ 1 │ 3 │ RPG │ ╰───┴────┴─────────────╯ > $info.ratings.pegi ╭──────────────┬──────────────╮ │ rating │ 18 │ │ descriptors │ Violence │ │ │ Bad Language │ │ │ Gambling │ │ use_age_gate │ true │ │ required_age │ 18+ │ ╰──────────────┴──────────────╯Або навіть отак:
> $info.supported_languages | split words | 'Ukrainian' in $in false # 😭(Підіть, може, хоча б ШБТ 💙 попросіть у дискорді, щоб вони переклали… Ех.) Я собі так навіть знятки екрана всі стягнув. Важать вони, до речі, доволі дофіга: на ≈150 ігор вийшло близько 900 МБ 🤯 Треба, мабуть, у WebP конвертнути. Апішка має ліміти на кількість запитів за певний проміжок часу. Скільки точно, я не знаю. Якщо робити десь два запита на секунду, то наче не рубає зʼєднання, але якщо флудити частіше, то сервер починає кидати якусь з 500-х помилок, здається. За який час його попускає, я теж хз — просто VPN перемкнув собі, і далі запрацювало норм. Уважний читач (так, ти 🫵) міг побачити, що URL-параметр в запиті називається
appids, а не appid. Однак ні — одразу декілька передавати не можна, бо тоді їхній сервер повертає HTTP 400. Єдиний виняток — це комбінація з фільтром price_overview, яка працює:
> http get https://store.steampowered.com/api/appdetails?appids=1328670,1091500&filters=price_overview | transpose -d | rename id val | select id val.data.price_overview
╭───┬─────────┬────────────────────────────────╮
│ # │ id │ val.data.price_overview │
├───┼─────────┼────────────────────────────────┤
│ 0 │ 1328670 │ ╭───────────────────┬────────╮ │
│ │ │ │ currency │ EUR │ │
│ │ │ │ initial │ 5999 │ │
│ │ │ │ final │ 479 │ │
│ │ │ │ discount_percent │ 92 │ │
│ │ │ │ initial_formatted │ 59,99€ │ │
│ │ │ │ final_formatted │ 4,79€ │ │
│ │ │ ╰───────────────────┴────────╯ │
│ 1 │ 1091500 │ ╭───────────────────┬────────╮ │
│ │ │ │ currency │ EUR │ │
│ │ │ │ initial │ 5999 │ │
│ │ │ │ final │ 5999 │ │
│ │ │ │ discount_percent │ 0 │ │
│ │ │ │ initial_formatted │ │ │
│ │ │ │ final_formatted │ 59,99€ │ │
│ │ │ ╰───────────────────┴────────╯ │
╰───┴─────────┴────────────────────────────────╯
Тепер можна нагенерувати собі сторінок в обсідіані або кудись ще покласти 😎На торішньому Advent of Code я з певним успіхом намагався розвʼязувати задачі щодня різними мовами, зокрема на Haskell 💻, 💻, Nushell 🆕, Swift 🕊, Python 💻, Red 🔺, 💻, Nim 👑, 🦶, Elixir 💻, Dart 💻, SWI-Prolog 🦉, 💻, Janet 👩🦰, Crystal 🔮, 💻 й 🕸.
Не певен, чи цього року я робитиму так само, але накидайте мені, може, ще пропозицій? Напишіть, яку мову програмування на вашу думку варто спробувати й чому ✍️🧐
Колись вже писав був статтю про локалізацію текстур у грі. На той момент треба було чік-чік і в продакшн, тому ми там чимось щось кудись експортували, моєю тулою обробили, потім назад якось запхали — доволі незручний процес.
Згодом зʼявився настрій то все покращити. А для цього треба розібратися, як у ресурсах гри що зберігається. На щастя основну роботу зворотної розробки вже проробили модери й навіть документували структуру файлів, тож мені лишалося цим скористатися.
Отже, в старих іграх на рушії Infinity Engine — Baldurʼs Gate, Planescape: Torment тощо — структура приблизно така: є key-файл (зазвичай називається
chitin.key) і є тека data з низкою BIF-файлів (не дуже багато — штук 80, наприклад). Але фактично рушій працює з конкретними ресурсами: маю на увазі всілякі скрипти, зображення, описи анімацій, діалогів, звуки тощо. От їх якраз купа. Наприклад, у першій Baldurʼs Gate їх 37341 штука.
Але жорсткі диски в компах раніше були доволі повільні, а інколи й доволі малі, тому в певних випадках доводилося грати зі вставленим CD, який ще повільніший. Ну а файлові системи на вінді й зараз не фонтан у сенсі швидкодії. Тому читання такої кількості дрібних файлів напряму з жорсткого диска — це вирок. Натомість краще мати меншу кількість великих файлів. Саме так розробники й зробили (та й досі роблять).
Отож key-файл — це бінарний формат, який описує всі ресурси, а також де саме вони лежать. А лежать вони в BIF-файлах. Кожний BIF — теж бінарний: фактично архів, у який напаковані різні ресурси, тобто файли й тайлсети. Розробники їх згрупували якось для зручності.
Я вже мільйон разів писав, як мені подобається Kaitai Struct 🏗, тож не обійшлося без нього й цього разу. Писати бінарний парсер по готових спеках — якесь дивне задоволення, трохи медитативний процес навіть. Сидиш ото, пиришся в hex-редактор, щоб переконатися, що ніде не сплутав big-endian з little-endian — кайф! Хоча цього разу парсери конкретно для key та BIF сів писати мій дружбан.
А мені ж треба було вже працювати з конкретним типом ресурсів. В ідеалі я хотів би читати їх прямо з key+bif-файлів так само як це робить гра, але на той момент єдиним виходом було експортувати їх вручну іншим інструментом в локальну файлову систему. Постає питання: як написати код так, щоб потім не треба було його адаптувати?
Я пишу на 🦶 і спочатку думав абстрагувати якось процес читання в якийсь Reader абощо, а потім передавати його у свої обробники всюди. Але це якось кволо. До того ж якщо подумати, будь-який файл — це вже Reader, а точніше io.Reader. Тож гіпотетично можна написати свою імплементацію. Правда, виявилося, що для Kaitai одного io.Reader замало — треба ще io.Seeker, щоб можна було читати з будь-якого офсета.
Тут я натрапив на лібу spf13/afero, яка ніби трохи спрощувала все це. Тож я просто завʼязався на інтерфейс afero.Fs усюди у своєму коді, і на той момент працював з локальною файлухою. А згодом написав свою «віртуальну» файлову систему, яка дає змогу працювати з усіма запакованими ресурсами гри «прозоро» для користувача, хоча насправді я читаю їх прямо з BIF-файлів без проміжного видобування. І вуаля: підмінив одну фс на іншу, а воно досі працює 😎
Повільнувато трохи тільки 😅 Наприклад, видобування ~1200 файлів на 25 МБ загалом у мене на M1 Max ішло 2,5 хвилини 😂 Довелося зайнятися профілюванням, яке, маю зазначити, в Go дуже легко додати в програму! Посиділи трохи з тим же друганом, знайшли найболючіші місця, і тепер видобування всіх ресурсів на майже 2 ГБ загалом триває близько 12 секунд.
У підсумку скажу (знову) пару слів про Go 💻: я ще починаючи з торішнього Advent of Code писав, що мова мені не подобається. І в принципі це досі так. Вона якось деревʼяно відчувається, немає в ній наче фану зовсім. Але наскільки ж легко й зручно в ній доводити справу до кінця! От просто сідаєш і пишеш без виїбонів, а воно потім працює, так ще й компілюється в один бінарь для будь-якої системи. І тулінг зручний. Можливо, це наразі єдина розробка від ґуґла, яку я поважаю.Стикнулися тут з проблемою: текст для перекладу гри — це просто здоровезний список рядків у довільному порядку без жодного контексту. Виходить, що перша фраза в діалозі може бути з аідійшкою
#23456, а наступна вже #76543 (тобто між ними 50 тисяч інших текстових шматків). Отже, дуже складно зробити переклад узгодженим — купу енергії потребує.
Але ж у самій-то грі ці рядки якось повʼязані в суцільний діалог! А значить, є шанс цю інформацію звідти видобути. Цим я й зайнявся.
Про сам процес розкопування ресурсів гри я згодом ще розповім, а зараз скажу лише, що мені це вдалося. Половина справи зроблена.
А інша половина — це відображення цих даних. Тож сів і зробив інструмент для перегляду діалогів. Про фічі вже розповів у відосі, а тут напишу про технічну складову.
У вебі я не тямлю, але знайомий топовий чувак @marktanashchuk порадив мені SvelteKit, і мені норм зайшло. Замість ноди я взяв Bun, бо він принаймні швидко працює, до того ж підтримує 🕸 з коробки.
Майже все написав мені 🐈 Copilot (з Claude 4). За три дні на це пішла приблизно половина місячної норми токенів 😆 Закласти фундамент проєкту було найскладніше. ШІ-шка традиційно згенерувала дохуїльйон коду, зробивши низку некоректних припущень. Довелося це все видаляти й іти дрібнішими кроками. Згодом в пригоді став 💻-сервер для DevTools, завдяки якому можна ШІ-агенту прям сказати: «Піди й сам подивися, що за лайно ти наробив», — а він іде, дивиться й виправляє (а завдяки хот-релоаду й перевіряє одразу).
Всі діалоги насправді експортовані в JSON Canvas, який виявився відкритим форматом. Рендер графа робиться через D3 в SVG з домішками HTML через <foreignObject>. Потім з цього збирається статичний вебсайт, який я через GitHub Actions розгортаю на Cloudflare Pages.
Завдяки цьому всьому тулза не залежить від конкретної гри. На прикладі у відео діалоги з першої Baldur's Gate, яка звісно вже давно перекладена. З 18 МБ вхідних даних на виході отримав 56,5 МБ сайт, тобто роздуло його втричі. Зате не вимагає виконання жодного коду на сервері взагалі.
На перших порах розробки, коли мій рендер був ще такий собі, використовувати JSON Canvas було дуже зручно, адже будь-який експортований діалог можна було переглянути в тому ж Obsidian. Зараз вже бачу, наскільки їхня спека мене обмежує. Думаю, згодом, може, зроблю власну надбудову.
اکنون در دسترس! پژوهش تلگرام ۲۰۲۵ — مهمترین بینشهای سال 
