Стендап Сьогодні

Що я зробив, що я хочу зробити, і що це все значить.
Повсякденні здобутки в форматі стендапу.
Детальніше в статті

Підписатись на RSS · 📢 Канал в Telegram @stendap_sogodni

14.07.2024

Технічні проблеми з іграми на macOS

Я тимчасово відірваний від свого ігрового компʼютера (навіть Parsec не допоможе, бо він просто вимкнений.) Залишається в щось пограти на макбуці. Але, навіть коли він включений у монітор, клавіатуру та мишу, геймінг з маком залишається чимсь особливим… щоб його любити, доводиться йти на компроміси.


13.07.2024

Емулятор відповідей SMTP

Коли розробляєш клієнта для SMTP, виникає потреба відтворювати ту чи іншу статусну відповідь - щоб перевірити, як клієнт на неї відреагує. Тестових серверів SMTP вистачає, але здебільшого вони емулюють успішне доставлення. Як максимум, ситуації на кшталт “користувача не існує”. Також легко емулювати “сервер недоступний”. :)

Цього недостатньо для повноцінної розробки, бо, як я писав, відповіді не є стандартними, тому важливо, щоб клієнт підтримував різноманітні варіанти. В ідеалі, потрібний сервіс, який відповість буквально такою статусною відповіддю, як тобі потрібно.

Тому ми вбудували у Mailtrap емулятор відповідей. Достатньо надіслати листа, наприклад, на bounce+555+spam@inbox.mailtrap.io, та система відповість: 555 spam. Звісно, це працює навіть для користувацьких клієнтів, таких, як GMail - до речі, цікаво було подивитися, як він реагує на тимчасові помилки (тобто з кодами 4xx).

Зміст відповіді додається до адреси отримувача з URL-кодуванням. Чому обрали саме адресу? Бо це працює не тільки в будь-якому клієнті, а й навіть тоді, коли ти не контролюєш зміст листа (наприклад, тих листів, що надсилаються застосунком.) Адреса отримувача - найгнучкіший з усіх доступних носіїв.

Але, це змушує нас кодувати зміст (який може бути довільним рядком ASCII) в набір символів, доступний в імені скрині електронної пошти. Обрали кодування URL, бо воно залишає зміст читабельним. Нюанс: за стандартом поштові адреси зводяться до нижнього регістру, тому якщо у відповіді потрібні великі літери, їх також доведеться закодувати: Spam стане %53pam. На жаль, стандартні кодувальники цього не зроблять, хоча механізм кодування URL не забороняє будь-який символ замінити на його код. В майбутньому щось для цього придумаємо.

Таке рішення лягає в загальну тему тулінгу, тобто оснащення, про яку хочу потім поговорити детальніше.


12.07.2024

Хліб

🌡️ Літо — найкраща пора року, щоб почати пекти хліб. Тісто любить підвищену температуру — те, що взимку потребує часу або підігріву, влітку відбувається непомітно. Особливо коли йдеться про закваску; а на заквасці хліб найзапашніший та найхарактерніший.Наприклад, мій “звичайний” хліб взимку готується з обіду до наступного пізнього ранку, а влітку він готовий вже увечері.

Для мене домашній хліб почався з подорожі в Австрію, де я скуштував дуже простий житній хліб на заквасці. У нас такого практично не купиш; або суміш з пшеницею, або щось солодке. Знайшов рецепт. Сподобалось.

Головне, що такий хліб не потребує особливих навичок. Тільки вимірюй складники, перемішуй та чекай. Його виготовлення майже не потребує часу, особливо коли запамʼятаєш рецепт: хвилинку тут, хвилинку там. Але, важливо дотримуватись розкладу. Тому бачив, наприклад, рецепт, розкладений на робочий день

Наразі я печу хліб з цілозернової спельти. Рецепт тут. Спельта в нас почалася як “здорова альтернатива пшениці”, але як виявилось, вона просто смачна: духмяніша за пшеницю, легша в роботі ніж жито (яке сходить ну дуже важко.) Поки нічого кращого не треба.

Нарешті, можу порадити книгу Ібана Ярса “Домашній хліб”. Мені сподобалось, що рецепті в ній “відкриті”, а техніки добре проілюстровані. Також автор пояснює, як правильно використовувати дріжджі. Гарний посібник.


11.07.2024

Go-global та важливість правильного повідомлення про помилку

Несподівано з новим застосунком натрапили на таку помилку в go-global:

global: : cannot write param: config key is of unsupported type struct

(Go-global це наша бібліотека для завантаження структур параметрів. Останній раз оновлював її ще рік тому, втім, зазвичай вона свою функцію виконує та проблем не створює.)

По-перше, незрозуміло, чому struct не підтримується, бо звісно ж на них вся конфігурація й побудована. Справжній зміст помилки в тому, що ми примітивне значення намагаємося записати в поле структурного типу. Тому так і написав:

cannot write param: destination should be a primitive type, not a struct

По-друге, дивно: повідомлення про помилку мало б містити шлях, за яким вона відбулася. Переглянув код та ніби все так і працює… тоді роздивився уважніше помилку та зрозумів, що друга двокрапка global: : і є шляхом, точніше, шлях порожній. А порожній він не тому, що щось поламане, а тому, що помилка відбулася на верхньому рівні. Так не повинно бути, бо за логікою там не може бути параметра — завжди є хоч би один рівень вкладеності.

Виявив, що параметрів на цей раз зовсім не було. Це утворювало “порожній” верхній рівень… а далі, через збіг обставин, він розглядався як “листок”, та й призводив до помилки. Тобто що дійсно треба побачити користувачу, це попередження про відсутність параметрів:

no parameters in Parameter Store with prefix "/foo/"

Отакої! А без вірного повідомлення навіть я як автор бібліотеки не міг зрозуміти, де криється справжня помилка.


10.07.2024

Перегляд журналів до CodeDeploy

Ми використовуємо AWS CodeDeploy для розгортування сервісів. Якщо чесно, єдине що про нього можу сказати хороше — це те що він добре інтегрований в Amazon та дозволяє робити безпечне розгортування за декількома моделями на вибір. Але в користуванні він жахливий.

Зокрема, дуже складно шукати причину, чому розгортування не вдалося. (А на стейджингу це відбувається досить часто.) В CodeDeploy немає, як це не дивно, явного звʼязку з конкретними екземплярами сервісу та їхніми логами. А якщо до того згадати, що ECS “забуває” задачі десь через годину після зупинки, то відстежити відповідність розгортування до логів стає кропіткою та терміновою ручною роботою. Та це ми навіть не почали дивитися на саму проблему!

Отже, сьогодні з цим щось зробив. Написав скрипт, який знаходить ті самі журнали та зводить їх в зручну форму. Причому робить це автоматично, з посилання на розгортування.

Для того довелося: з API витягнути розгортування; потім з нього “специфікацію застосунку”; потім з неї “дефініцію задачі”, а також назви Lambda-функцій, що роблять перевірку на здоровʼя; нарешті по назвах знаходжу конфігурації функцій, та з них та дефініцій задач можна здобути групи журналів, які стосуються розгортування. Фух! Не тільки складно, але й досить спеціалізовано; мабуть, саме через це вони самі логи й не показують.

Але це ще не все… з груп залишилося знайти потоки. (Я колись писав, що в Cloudwatch “потік” відповідає конкретній задачі.) Для того я більш хитрим, ніж базові API, шляхом відтинаю всі потоки, які не збігаються за часом. (Як не дивно, такого API немає; максимум це перелічити потоки за зменшенням часу.)

А потім всі результати складаю в шаблон HTML та відкриваю в браузері. В терміналі теж гарно, але я може ще якийсь UI додам зверху.

….Альтернативним шляхом було б формулювання запиту до Cloudwatch Insights. Тоді можна було б зупинитися на групах + діапазоні часу розгортування. Так, може, було б і краще.


09.07.2024

MX записи — адресна книжка електронної пошти

Широко кажучи, електронна пошта набагато сильніше привʼязана до адресата, ніж до відправника. Відправником може бути буквально будь-хто в інтернеті, особливо якщо надасть ознаки довіри, але й це не обовʼязково. А от адресат обовʼязково сполучений з доменом своєї адреси, бо саме туди будуть надходити листи.

Для того в стандартах DNS передбачений особливий тип запису: MX. Він вказує з домену отримувача на домен поштової скриньки. Це практично єдиний сервіс, ушанований такого місця; так, пізніше зʼявилися записи SRV, які можуть вказувати на будь-який сервіс, але відокремлений тип є тільки для пошти.

Коли ми хочемо передивитись вебсайт, то знаходимо його IP адресу. А от якщо надіслати листа — то робимо окремий та особливий крок: шукаємо MX запис, а потім вже так само IP адресу, але вже скрині, на яку вказав MX-запис.

(Відзначу, що механізм CNAME, яким ми користуємось для делегації сайту на хостинг, працює кардинально інакше, але це варто окремого поста.)

Таким чином, отримувати пошту для домену може будь-який інший домен. Може, це GMail чи Outlook. Може, спам-фільтр. Може, сервіс “поштового API”. Ба більше, MX записів може бути навіть декілька, та це “не баг, а фіча”: так можна налаштувати балансування навантаження або додати резервний сервер на випадок відключення. Це є джерелом помилок, бо коли люди змінюють провайдера поштової скрині, то можуть не знати, що старий необхідно видалити з MX.

MX-записи не мають жодного відношення до надсилання пошти. Хоча, у форматі SPF є можливість “благословити” надсилання з MX-записів - але це тільки для зручності. Бо сервер для надсилання міг збігатися зі скринею — але це скоріше випадок маленьких, “гаражних” архітектур, якими вже ніхто майже не користується. Якщо у вас MX - це GMail, то надсилати GMail буде зовсім з інших вузлів.


08.07.2024

Помилка повтореного заголовку з ActionMailer

Сьогодні випустив нову версію бібліотеки mailtrap для Ruby. Це було вмотивовано виявленням цікавого збігу обставин, який псував, хоч і не суттєво, зміст листів, які надсилалися через Ruby on Rails.

Яку проблему помітили: в листах було два заголовки MIME-Version (а точніше, другий Mime-Version, тобто очевидно заголовки приходили з різних місць.) Це не призводить до помилки (головне, щоб такий заголовок був.) Але це один з заголовків, які будуть включені в підпис DKIM; та так складалося, що з подвійним заголовком підпис був не до кінця вірним (деякі сервери звертають на це увагу та знижують репутацію листів.) Тому необхідно було позбавитись того зайвого заголовку.

Що робити? Можна, звісно, вичищати той заголовок на виході, але тут треба обережно знайти системний підхід. Простіше спочатку зрозуміти, звідки він взявся — тим паче що очевидно, система-джерело робить зайве.

Причину я знайшов: ActionMailer до того, як передати листа на доставлення, викликає в нього метод encoded. А той метод зрештою додає той самий заголовок. Все було б більш-менш добре, якби лист надсилався за SMTP; але оскільки він йшов у наш API, то всі заголовки, які є, передавалися як “особливо замовлені”. А наш API, коли будує листа, теж додає MIME-Version за стандартом. Така вийшла плутанина з відповідальностями.

Цікаво також, що ми не знайшли цей збіг, бо не тестували повний цикл разом з застосунком Rails, а тільки починаючи з готового обʼєкту Mail::Message. Тобто все б нічого, але метод encoded ніхто не викликав. Власне, щоб виправити помилку, я спочатку його додав та зламав тести.

А взагалі, некрасиво в методі, який ніяк не натякає на мутації, змінювати обʼєкт та ще й таким суттєвим чином.


07.07.2024

Побудова плану квартири, або фотограмметрія в домашніх умовах

Була в мене задача: побудувати план квартири, до якої немає постійного доступу. Та ще й зі стінами, дверми, вікнами. Знайшов підхід, який може й непрофесійний, але результат дав.

До того думав креслити все вручну на папері; але у Pixelmator вийшло не тільки чіткіше, але ще й швидше. Та цей цифровий план легше буде використовувати повторно. Pixelmator 1 : папір 0.


06.07.2024

Стискання шрифтів для вебу

Закінчую роботу над значками для Obsidian. Як минулого разу казав, шрифт значків всім гарний, от тільки завеликий за розміром. Взявся обрізати шрифт, щоб залишилася тільки та купка значків, яку я використовую.

На то знайшов бібліотеку fontTools - до речі, ще один продукт на Python, який треба встановлювати на пайтонівський намір. Ну, зате, на щастя, brew install fonttools теж працює. Бо всі ці pip та virtualenv то просто жах.

FontTools вміє багато всього, а мені була потрібна команда subset - вона саме й обрізає шрифти під замовлення. Приклад можна побачити на знімку вище. Взагалі все дуже прямолінійно: даєш шрифт, даєш символи чи їх діапазон — та готово.

Працює навіть надто гарно, бо я довго не міг зрозуміти, чому ж на виході “порожній” файл у 1 кілобайт. Ну не може пʼять значків влазити в 1 Кб! А виходить, що можуть — переконався, коли нарешті наважився перевірити результат на ділі. Тепер мені рішення зі шрифтом для значків подобається ще більше.

Нарешті, тією ж командою можна перекодувати шрифт у WOFF2. Дописав також скрипт, який відразу загортає його в .scss, щоб не робити цього вручну кожного разу, як додам новий значок.


05.07.2024

Тредізація RSS Twitter

Трохи невдала сьогодні спроба. Хотілося у стрічці RSS для Twitter, яку генерує RSSHub, бачити треди в одному пості — бо як окремі пости, їх досить складно сприймати. Взагалі проблема витікає з того, що з API неможливо витягнути твіт, на який зроблена відповідь — тільки його URL. (Та й взагалі той API вже “не той”, та Twitter вже “не той”, та я б із задоволенням про нього забув, проте багато журналістів та дослідників більше нікуди не публікують.)

Ідея була проста: спід час генерації стрічки RSS виявляти ті твіти, що відповідають на інші твіти з того ж RSS, та “підшивати” один до одного. Тут не потрібні додаткові звернення до API - тільки додаткова обробка. Ніби все просто та прототип я навіть зробив, але на практичній перевірці ідея не запрацювала.

Річ у тім, що твітів в одній виборці близько 20, та вони навіть не впорядковані за часом — а, певно, за популярністю чи ще якимсь алгоритмом. Через це у вибірку може потрапити, наприклад, перший та останній твіт у треді — а решта ні. З такого вже кашу не звариш.

Та друга проблема: у вибірку може потрапити не весь тред. Спочатку я думав, що це не так погано — умовно, замість 40 постів в стрічці буде не 1, а 2. Але: при наступному оновленні межі вибірки зсуваються не повністю. Виходить, можна отримати хоч 10 напівдубльованих шматків. Щоб цього уникнути, можна залучити кеш та зберігати попередні пости. Але це вже ускладнення на майбутнє.

До речі, розробляти для RSSHub напрочуд просто; це застосунок Node.js, та запускається він за yarn install && yarn dev. Навіть TypeScript та Prettier в них є. Навіть Codespace підготований! Так що, можливо, власні RSS генератори теж перенесу у RSSHub.