AI 资讯
I keep finding out about API breaking changes from production errors, so I'm building a changelog watcher
I build products solo. Every single one of them sits on top of somebody else's API — Stripe for payments, OpenAI and Anthropic for AI features, Meta for ads, print-on-demand APIs, map APIs. My code is maybe half of what actually runs in production. The other half belongs to vendors, and it changes whenever they decide it changes. Twice this year the first notice I got about a breaking change was a production error. Not an email, not a warning. An error, and then me digging through the vendor's changelog trying to figure out what they changed and when. The information was public the whole time. It was sitting in a changelog page I never visit, because nobody visits changelog pages until something is on fire. So I'm building the thing I wanted to exist BreakWatch is simple: you tell it which APIs your product depends on, and it reads their public changelogs for you. It fetches each changelog page once a day Diffs it against yesterday's snapshot Classifies the real changes: breaking (endpoint removed, field deprecated, "migrate by September") vs. informational (new feature, docs clarification — stuff you can ignore) Alerts you only when something looks like it will break an existing integration Keeps everything in a searchable timeline, so six months later "what changed on their side right before this broke" takes ten seconds instead of an afternoon No SDK, no credentials, nothing installed in your codebase. It only reads public pages. What I tested this week I ran it against the real changelogs of the ten APIs I'm watching first: Stripe, Twilio, OpenAI, Anthropic, Shopify, GitHub, Slack, Cloudflare, Google Maps and Plaid. Some honest findings: 10/10 scrape cleanly now, but it took fixes. Stripe's changelog page alone is 3.3 MB. SendGrid's standalone changelog doesn't exist anymore (it merged into Twilio's). PayPal's developer site serves a JavaScript shell with an HTTP 404 to anything that isn't a full browser, so it's out until I add rendering. The thing I was most a
AI 资讯
Bizbox Build Log — Week of 2026-05-31
Shipped this week Workflows are now a first-class Bizbox primitive — PR #86 · v2026.603.0 The biggest drop this week. @DennisDenuto landed Workflows as a company-scoped concept that sits alongside issues and routines — not shoehorned into either. What that means in practice: Google ADK-backed execution — workflow pipelines run as ADK agents, with phase state persisted as run records. Human handoffs baked in — pipelines can pause and wait for a human before resuming. Deliverables that survive — artefacts from each run are persisted and surfaced in the UI. A pipeline graph in the UI — topologically ordered, showing live phase state and console output. This is the foundation. More on what we can build on top of it below. Workflow human-handoffs now route through ClickUp — PR #91 · v2026.605.0 The day after Workflows landed, @angelofallars wired up the last kilometre: when ADK Python code calls input() inside a pipeline, Bizbox now intercepts that call and sends a ClickUp message to collect the human reply — instead of blocking the process forever. A few things that were fixed along the way: input() monkey-patching now works consistently across Python environments (was silently failing in some setups). Failed workflow runs no longer submit deliverables. You only see artefacts from runs that actually completed. ClickUp awaiting-human bridge adapter ships as a pure plugin — PR #78 · v2026.601.0 This one technically crossed the line on the last day of May (23:56 UTC, 31 May), so it's in scope. @ralphbibera ported the ClickUp transport and adapter as a genuine plugin — implementing the AwaitingHumanBridgeAdapter registry interface — without touching bridge core at all. What that gives you: ClickUp works through the same provider-agnostic layer as any future provider (Slack, Discord, whatever comes next). The core doesn't know ClickUp exists. Included: send/poll/reaction transport, message templates for request_confirmation and ask_user_questions interactions, brain_is_think
AI 资讯
Late Night Shipping Safi Budget Engine Updates & Render Deployment published
Finished up a solid coding sprint tonight working on Safi-Budget a financial management application built around the 50/30/20 budget framework. I'm currently building and training over at Zone01Kisumu , and getting this build updated and deployed live was the main goal for today's session. What Was Updated Today Localized Currency Logic: Updated the core engine defaults from EUR over to** KES** (Kenyan Shillings) to better support local financial tracking workflows. Auth Flow Refinements:** Ironed out session management and routing logic to ensure clean sign-in and logout behavior across the app. Containerization & Deployment:** Confirmed the Go backend containerizes smoothly with Docker and runs cleanly on Render. Tech Stack Language: Go (Golang) Containerization: Docker Deployment Platform: Render Live Demo & Link You can test out the live deployment here: 👉 Safi Budget Engine Live App
AI 资讯
# Why Building Automation Projects Get Delayed Long Before Commissioning
When people think about delays in Building Management System (BMS) projects, they usually blame installation issues, communication failures, or commissioning problems. In reality, many delays begin much earlier. They start during engineering. Before a single controller is installed, engineering teams spend significant time reviewing I/O lists, selecting controllers, designing panels, preparing wiring documentation, planning network architecture, and coordinating procurement. These activities are essential, but they are also repetitive, manual, and prone to errors. As modern buildings become larger and more connected, traditional engineering workflows are struggling to keep up. The Hidden Cost of Manual Engineering A typical BMS project may contain hundreds or even thousands of points: Temperature sensors Humidity sensors Pressure transmitters VFD controls Damper controls Pump status points AHU controls Chiller interfaces Each point must be reviewed, categorized, mapped, documented, and connected to the correct controller. While this process is necessary, it creates a bottleneck that often goes unnoticed. A small mistake in controller sizing or wiring documentation can trigger a chain of revisions, procurement changes, and commissioning delays. The result is a project schedule that slowly expands before installation even begins. Why Traditional Workflows Don't Scale The challenge isn't engineering knowledge. The challenge is repetition. Engineering teams repeatedly perform similar tasks across projects: Reviewing I/O schedules Selecting controllers Allocating points Generating documentation Creating wiring drawings Verifying network configurations As project complexity increases, the amount of repetitive work increases as well. This leads to: Longer engineering cycles Increased project costs More documentation reviews Greater risk of human error The Shift Toward Engineering Automation Many industries have already embraced automation in design and manufacturing. Build
工具
How I pulled data out of a cross-origin iframe I couldn't touch
The feature was supposed to be the easy one. NotebookLM generates flashcards. My extension reads them...
AI 资讯
What Actually Controls Your Building's HVAC System? Meet the DDC Controller
Most people working in offices never think about why the temperature stays comfortable throughout the day. The cooling adjusts automatically. Fresh air increases when occupancy rises. Fans start and stop without anyone touching a switch. Behind all of this is a device that most building occupants have never heard of: the DDC Controller. The Hidden Computer Inside Every Modern Building Walk into a mechanical room and you'll find equipment everywhere: Air Handling Units (AHUs) Chillers Pumps Cooling Towers VAV Boxes All these systems need coordination. If the supply air temperature rises above its target, something has to react. If occupancy increases, fresh air must increase. If a fan trips, alarms must be generated. This is where a DDC controller comes in. Think of it as a small industrial computer dedicated to one job: keeping a building running efficiently. A Typical Day in the Life of a DDC Controller Imagine an AHU supplying air to an office floor. At 9:00 AM employees begin arriving. The return air temperature starts increasing. The DDC controller notices this through a temperature sensor. Within seconds it: Reads the sensor value Compares it against the setpoint Calculates the cooling demand Adjusts the chilled water valve Verifies fan operation Repeats the process No operator is required. No manual intervention is needed. The controller quietly performs these calculations all day. Why Not Just Use a PLC? This is one of the most common questions from engineers entering building automation. PLCs and DDC controllers are both programmable devices. However, they were designed for different worlds. A PLC excels at: Manufacturing lines Packaging machines Process control High-speed sequencing A DDC controller excels at: HVAC control Energy optimization Occupancy schedules Comfort management BACnet communication Both can control equipment. The difference is what they were originally built for. The Four Signals Every BMS Engineer Learns First If you're new to building
AI 资讯
Multi-Agent Interview Coach
This post is my submission for DEV Education Track: Build Multi-Agent Systems with ADK . What I Built Preparing for technical interviews can be overwhelming. I wanted to build a tool that doesn't just give generic questions, but actually analyzes my specific resume to challenge my unique skill set. This led me to build a multi-agent system using the ADK. This multi-agent system, will take user resume and extracts their profile for generating Interview Questions specialized to the candidate profile. The agents communicate in a sequential loop: The Profiler extracts data -> The Interviewer generates questions -> The Judge validates them. If the Judge rejects a question, the Interviewer re-drafts it, ensuring only high-quality, resume-relevant questions make it to the user. Cloud Run Embed Your Agents Profiler Receives a resume PDF GCS path, downloads it in-memory, parses the text content, and builds a summary of skills. Interviewer Reads the candidate summary and drafts 3 technical interview questions designed to test the boundaries of their experience. Analyzes the drafted questions. Passes the iteration if they are resume-specific; rejects/fails them if they are too generic. Key Learnings This project was a fantastic weekend challenge. Working through the Google Codelab gave me a solid grasp of agent-based architectures, specifically implementing Agent, LoopAgent, and SequentialAgent to create a robust workflow. A few key technical takeaways included: Managing Statelessness : Learning to handle agent sessions in a Cloud Run environment was a great lesson in explicit session lifecycle management. Cloud Integration : Integrating Google Cloud Storage for file handling taught me how to bridge in-memory document processing with persistent cloud storage efficiently. Deployment Architecture : Mastering the transition from local development to a containerized, production-ready Cloud Run deployment provided deep insights into modern backend orchestration. Check the Code from
AI 资讯
How My Frustrating Job Search Led Me to Build an AI Job-Matching Platform
A few months ago, I was searching for a backend engineering job. Every day looked the same: Open LinkedIn Open Naukri Search for Python jobs Open dozens of tabs Read every job description Apply Repeat The frustrating part wasn't finding jobs. It was finding the right jobs . I kept getting recommendations for roles that technically matched my resume because they contained words like Python , Backend , or API , but after reading the description I'd realize they wanted a completely different skill set. I started wondering: Why are job boards still matching keywords instead of understanding what a developer actually knows? That question eventually turned into a side project called Jobspiq . The Problem Imagine these two jobs: Job A Python FastAPI PostgreSQL Redis Job B Java Spring Boot Oracle Kafka Both are "Backend Engineer" roles. A keyword-based system often treats them as similar. As developers, we know they're not. What I Built Instead of matching keywords, I built a system that compares a developer's profile with a job description to understand how well they actually fit. The platform: Collects jobs from multiple sources. Removes duplicate postings. Scores every job based on how closely it matches your profile. Sends alerts only for high-quality matches. Helps track applications in one place. The goal isn't to show more jobs. It's to show fewer, better ones. What I Learned Building the product taught me something interesting. Writing the software was the easier part. Helping people discover it is much harder. That's why I'm starting to build in public and share what I'm learning along the way. If you've ever built search systems, recommendation engines, or developer tools, I'd love to hear your thoughts. You can check out the project here: https://jobspiq.in Feedback is always welcome.
AI 资讯
I'm not an engineer. I built a prompt-structuring tool anyway, using Claude Code — here's what actually went wrong
I'm not an engineer. I built a prompt-structuring tool anyway, using Claude Code — here's what actually went wrong 🤔 The problem I don't write code. I'm not an engineer, day to day — but I use AI chatbots constantly, and I kept running into the same annoying pattern. I'd type something lazy into ChatGPT or Claude — half a sentence, no context, whatever came to mind first — and get back a mediocre answer. Then, later, I'd realize: if I'd just written a slightly better prompt, I probably would've gotten a much better answer on the first try. Instead I'd burned a chunk of my monthly quota (sometimes on a paid plan) on something forgettable. I figured other people had to be doing the same thing. So I built something for it. 💡 What I built It's called Deep Prompt Studio . You paste in a rough, unpolished prompt — the kind you'd type without thinking too hard — and it hands back a detailed version that pulls in whichever pieces actually matter for that request: role, task, constraints, output format, and more. It's not tuned for one specific chatbot; it's meant to work well whether you're pasting the result into ChatGPT, Claude, Gemini, or something else. https://deep-prompt-studio.vercel.app 🛠 Tech stack Next.js (App Router, Turbopack) + TypeScript Tailwind CSS Anthropic Claude API for the actual prompt enhancement Stripe for payments Upstash Redis for storage Deployed on Vercel ⚙️ How it works Tone selector — Default, Professional, Casual & friendly, Concise, Direct Target-model optimization — Generic, ChatGPT, Claude, Gemini, Midjourney Snippets — save reusable context/tone/role info and toggle them on or off per enhance (free: up to 3, Pro: unlimited) Variable fill-in — template placeholders like {{product name}} you can reuse Refine mode — if your prompt is too vague, it asks a couple of follow-up questions before enhancing, instead of guessing Template gallery — writing, coding, business, learning, SEO, customer support Library — saved enhanced prompts (Pro) Free ti
AI 资讯
A required field made my AI fabricate statistics
I run a pipeline that generates explainer articles. LLM in the middle, structured output, published in several languages. It had been running for a while and the articles looked good: clean layout, a chart, and near the top of each one a confident little box with a statistic. Something in the shape of "68% of people never change the default." A number, a source, an authoritative ring to it. Not one of those numbers had been researched. The pipeline had never looked up a single statistic in its life. It asked the model for a number and printed whatever came back. I did not find this through a clever eval. I found it while cleaning up something unrelated and actually reading the prompt. The field that forced a lie The output schema had a required field. statistic.text and statistic.source , described in the prompt as an "eye-catching stat" for the top of the article. Required. Every article had to have one. The prompt also, helpfully, told the model what to do when it did not have a real number. It said to round to a safe order of magnitude. And it said to strip the year off the source, so the article would look evergreen instead of dated. Read that back slowly. The instructions were: always produce a statistic, make up a plausible magnitude if you have to, and remove the one piece of metadata that would let anyone check it. That is not a prompt that occasionally allows a hallucination. That is a prompt that requires one, every single time the model does not happen to know a real figure. So it produced them, confidently, in every language, each wearing a real-sounding source: a named institute, an industry association, a government statistics office. None of it had been looked up when it was written. This was content people actually act on, which is exactly the category where being wrong is not a rounding error. There was a second engine doing the same thing in the chart code. The block that generated the data visualization asked the model for "actual statistics from
AI 资讯
Two Bugs, Two Strangers, One Week: What Shipping Early Actually Buys You
A week ago I put a rough, honestly-a-bit-thin version of PulseWatch in front of real people for the first time. Within days, two different strangers — independently, unprompted — found two real gaps in it. Neither was catastrophic. Both were exactly the kind of thing you only find by watching someone else use the thing you built. This is the story of both, and the fixes. Bug one: the run that never ends This first bug came from a friend testing it on a real script. His question was simple: "What happens if start fires twice before end ?" Good question. At the time: nothing good. Here's why. PulseWatch works on two pings — a job calls /start when it begins and /success (or /fail ) when it's done. The server tracks whichever run is currently "open" for a monitor. The bug: if a job's process restarts mid-run — a crash-and-retry, a redeploy that catches it mid-flight, a scheduler firing twice — you get a second /start before the first run ever closes. The old run just sits there, open forever, an orphan with no ending. Worse, because the watchdog was still waiting on that run's expected finish time, it could fire a false "still running" alert for a run that was, for all practical purposes, dead and abandoned. The fix is a small rule with an outsized effect: a new /start supersedes whatever run is currently open. The old run gets marked superseded — a terminal, non-alerting status — and a fresh run begins clean. The watchdog was updated to treat superseded as a dead end: nothing to wait on, nothing to alert about, and it never shows up in a user's run history. It's not a failure and it's not a success. It's just "this run doesn't matter anymore, a newer one replaced it." The logic, roughly: def handle_start ( monitor ): open_run = monitor . get_open_run () if open_run is not None : open_run . status = " superseded " open_run . finished_at = now () new_run = Run ( monitor = monitor , status = " running " , started_at = now ()) db . session . add ( new_run ) db . session .
AI 资讯
How a former DeepMind researcher raised at a $300M pre-seed valuation before launching a product
Drawing on more than a decade spent helping build some of the world's most influential AI systems, including research that later informed the development of ChatGPT, Andrew Dai explains why he believes visual AI is one of the next major frontiers in artificial intelligence.
AI 资讯
I spent a week trying to intercept Slack push notifications from a Chrome extension. Here's why it's impossible.
After I published my last article about building a Chrome extension that speaks browser notifications aloud, a commenter asked a question I didn't have a good answer to. He pointed out that a lot of web apps — Slack, Gmail, most modern tools — fire their notifications from a service worker via registration.showNotification() , not from the page's JavaScript context. My MAIN world override of window.Notification would never reach those. He was right. And I told him I'd look into it. I spent a week researching whether there was any way to close that gap. There isn't. But the reason why is more interesting than a simple "no." Two ways a website can show you a notification When a website sends you a browser notification, it can do it in one of two ways. The first is the constructor path. The page's own JavaScript calls new Notification("You have a message") directly. This is common for in-tab alerts, real-time updates when you're actively on the site, or any notification triggered by something you just did. The second is the push path. The browser receives a push event from the website's server, wakes up the website's service worker in the background, and the service worker calls self.registration.showNotification() from inside its own scope. This is what happens when Slack notifies you of a new message while the tab is closed or backgrounded. The page never runs. No page JavaScript ever fires. My extension catches the first path. The MAIN world content script overrides window.Notification before any page code runs. But the service worker never touches the page's window. It has no window . It runs in a completely isolated thread, completely separate from the page, and calls showNotification on itself. The override is never reached. Why can't the extension reach the service worker? This is the part that took me a week to fully accept. Chrome extensions can inject content scripts into web pages. They can run code in the MAIN world or the ISOLATED world of a page. They can
产品设计
Building Lynktern a digital SIWES logbook and internship management platform for Nigerian university students. Replacing the paper logbook with something students, supervisors, and companies can actually use. #buildinpublic
AI 资讯
He Built an App in 24 Hours and Made $20,378 the Next Day. Here's the Part Nobody Screenshots.
Marc Lou read a tweet, slept on it, and woke up still annoyed. The tweet, from Pieter Levels, was about all the fake revenue screenshots on X. By the next evening Lou had built a thing to fix it. By the day after that, the thing had made $20,378. That is the part everyone retweets. I want to walk you through it, and then I want to show you the line in his own year-end letter that complicates the whole legend. The setup Lou got fired by Tai Lopez in November 2021, was broke and depressed, and moved to Bali. He started shipping tiny products in public, copying the playbook of, yes, Pieter Levels. His breakout was ShipFast , a Next.js starter kit that did $40,000 in its first month in September 2023. By December 2025 he was running 15 startups generating about $84,900 a month, with cumulative revenue past $2.26 million, per his verified TrustMRR data. The reason I trust his numbers more than most is that he verifies them through Stripe on his own product, TrustMRR , which brings me to the 24-hour story. The moment something worked, absurdly fast TrustMRR exists to kill fake MRR screenshots. You connect a read-only Stripe key, and it shows your verified revenue on a public page nobody can edit. Lou built it in a day on top of his own boilerplate, which is the cheat code here. He was not starting from zero, he was starting from ShipFast. "TrustMRR is 24 hours old and was built in 24 hours." @marc_louvion on X He monetized it with sidebar ad slots. He listed them at $299 a month, then raised the price each time one sold, all the way to $1,499. In his newsletter he wrote that within three days every slot was gone and the side project had made $20,378. He called it the third fastest-growing thing he has ever built. Five days in, he posted the run-rate dream out loud. "20/20 spots filled! TrustMRR went from $0 to $18,380 MRR in 5 days. That's $220,000 ARR if I'm allowed to dream a little" @marc_louvion on X It kept going. By December 2025 TrustMRR was his single biggest inco
开发者
I Added 200+ Languages to a Translator… Then Realized Language Wasn't the Hardest Part
I'll Be Honest: The Internet Already Has Translators I know. Language translation isn't a new idea. There are already huge translation platforms out there. So when I started working on a translator for my tools website, I wasn't thinking: "I'm going to reinvent translation." Not at all. My thought was much simpler: "Can I make quick translation feel less distracting?" My Frustration Was Actually Pretty Simple Sometimes I just need to translate text. That's it. I don't want to: Create an account Open five different menus Break a long text into tiny pieces Jump between multiple tools I want to paste the text... Choose a language... And get the translation. So I Built My Own Version 👉 https://allinonetools.net/language-translator/ The tool currently supports 200+ languages and language variations . You can: Detect the source language Select the target language Translate long text Upload text Use voice input Listen to the result Copy or share the translation And I wanted to keep the text experience simple without forcing users into tiny input limits. Just: Enter → Choose Language → Translate 200+ Languages Sounded Simple Until I Saw the List English. Hindi. Gujarati. Spanish. Arabic. These are the languages most people immediately think about. But then I started going through the full language list. Abkhaz. Acholi. Afar. Alur. Aymara. Baluchi. And many more. Honestly... I hadn't even heard of some of them before building this. That was probably my biggest learning moment. I Realized How Small My Own View of the Internet Was As a developer, it's easy to build around the languages we personally know. For me, seeing English, Hindi, and Gujarati feels normal. But the internet is much bigger than my own experience. Someone somewhere may be trying to understand a sentence in a language I've never even heard spoken. That changed how I looked at this tool. The Hard Part Wasn't Adding a Dropdown A dropdown with 200+ options looks impressive. But that's not the real problem. The
产品设计
Logging workouts is solved. I'm building what comes after.
Every workout tracker I've tried has the same limitation: it records what you did, but it doesn't...
AI 资讯
Enhancing CI/CD and E2E Testing with Sentry Integration in tvview
Enhancing CI/CD and E2E Testing with Sentry Integration in tvview TL;DR: I integrated Sentry for error tracking and improved End-to-End (E2E) testing in the tvview project, enhancing the CI/CD pipeline. This resulted in a score increase from 85 to 95+. The Problem The tvview project lacked comprehensive error tracking and E2E testing, making it difficult to identify and resolve issues in production. The existing CI/CD pipeline needed improvement to ensure smoother deployments and better code quality. What I Tried First Initially, I focused on setting up E2E tests using Vitest, but encountered issues with the test configuration. I also attempted to integrate Sentry, but faced challenges with the DSN (Data Source Name) configuration. The Implementation Step 1: Configuring Sentry To integrate Sentry, I created separate configuration files for the client, edge runtime, and server: // sentry.client.config.ts import * as Sentry from " @sentry/nextjs " ; Sentry . init ({ dsn : " https://385038c88b6eb6ddac52d05a144ab8c1@o4511628189630464.ingest.us.sent " , // Additional configuration options }); // sentry.edge.config.ts import * as Sentry from " @sentry/nextjs " ; Sentry . init ({ dsn : " https://385038c88b6eb6ddac52d05a144ab8c1@o4511628189630464.ingest.us.sent " , // Additional configuration options }); // sentry.server.config.ts import * as Sentry from " @sentry/nextjs " ; Sentry . init ({ dsn : " https://385038c88b6eb6ddac52d05a144ab8c1@o4511628189630464.ingest.us.sentry.io " , // Additional configuration options }); Step 2: Enhancing CI/CD Pipeline I updated the .github/workflows/ci-e2e.yml file to include Sentry configuration and E2E testing: name : 📺 CI + E2E — TVView on : push : branches : [ main ] workflow_dispatch : {} schedule : - cron : " 35 6 * * *" jobs : build-and-test : runs-on : ubuntu-latest steps : - name : Checkout code uses : actions/checkout@v2 - name : Install dependencies run : npm install - name : Generate Prisma client env : DATABASE_URL : " postgre
AI 资讯
Enhancing CraveView's CI/CD Pipeline with Sentry and E2E Tests
Enhancing CraveView's CI/CD Pipeline with Sentry and E2E Tests TL;DR: I upgraded CraveView's CI/CD pipeline by integrating Sentry for error tracking and implementing End-to-End (E2E) tests, boosting the score from 85 to 95+. This technical deep-dive explores the architecture decisions, code changes, and lessons learned. The Problem The initial problem wasn't a single error message but a series of inefficiencies in the CI/CD pipeline. The existing setup lacked comprehensive error tracking and test coverage, leading to potential issues in production. Specifically, the pipeline didn't have: Robust Error Tracking : No integrated system for capturing and analyzing errors. End-to-End Tests : Limited test coverage, which could lead to undetected issues in production. What I Tried First Initially, I focused on enhancing the test suite. I explored various testing frameworks but decided to implement E2E tests using Vitest, given its compatibility with the existing tech stack. The first approach involved setting up a basic E2E test framework. However, I encountered issues with the test environment configuration, particularly with database connectivity. The tests required a realistic database setup, which wasn't properly simulated. The Implementation Step 1: Configuring Sentry To integrate Sentry, I created configuration files for client, edge, and server initialization: sentry.client.config.ts import * as Sentry from " @sentry/nextjs " ; Sentry . init ({ dsn : " https://385038c88b6eb6ddac52d05a144ab8c1@o4511628189630464.ingest.us.sentry.io/4511629 " , // Additional config options }); sentry.edge.config.ts and sentry.server.config.ts follow a similar structure, adjusted for their respective environments. Step 2: Implementing E2E Tests I added a new test file e2e-production.test.ts in src/__tests__ : import { test , expect } from ' @playwright/test ' ; test ( ' should render the homepage ' , async ({ page }) => { await page . goto ( ' https://craveview.vercel.app ' ); await expe
AI 资讯
Upgrading CI Workflows: From Node 20 to Node 22 and Actions v5/v6
Upgrading CI Workflows: From Node 20 to Node 22 and Actions v5/v6 TL;DR: I upgraded the CI workflows for the content-automation repository from Node 20 to Node 22 and Actions v5/v6, addressing compatibility issues and improving performance. Key changes included updating upload-artifact from v5 to v7 and implementing retry with backoff. The Problem The CI workflows for the content-automation repository were using Node 20 internally, despite the configuration specifying Node 20. This discrepancy caused compatibility issues with newer versions of the GitHub Actions. Specifically, the upload-artifact action was still on version 5, which was internally targeting Node 20. What I Tried First Initially, I attempted to update the upload-artifact action to version 7, which supports Node 22. However, this change alone did not resolve the issue, as other actions like checkout and setup-python were still on older versions. The Implementation To address the compatibility issues, I updated the following actions: upload-artifact from v5 to v7 checkout to v5 setup-python to v6 Here are the specific code changes: // .github/workflows/main.yml steps: - name: Checkout code uses: actions/checkout@v5 - name: Setup Python uses: actions/setup-python@v6 - name: Upload artifact uses: actions/upload-artifact@v7 Additionally, I implemented a retry mechanism with backoff for the CI workflows: // .github/workflows/main.yml steps : - name : Retry with backoff run : | for i in {1..3}; do if ./script.sh; then break else echo "Retry $i failed, backing off..." sleep $((i * 2)) fi done Key Takeaway The key takeaway from this experience is the importance of keeping CI workflows up-to-date with the latest versions of GitHub Actions. This not only ensures compatibility but also improves performance and reliability. What's Next Next, I plan to monitor the CI workflows for any issues and continue to optimize the retry mechanism for better performance. I will also explore other ways to improve the reliabili