Posts in Ruby (20 found)
Farid Zakaria 1 weeks ago

Nix derivation madness

I’ve written a bit about Nix and I still face moments where foundational aspects of the package system confounds and surprises me. Recently I hit an issue that stumped me as it break some basic comprehension I had on how Nix works. I wanted to produce the build and runtime graph for the Ruby interpreter. I have Ruby but I don’t seem to have the derivation, , file present on my machine. No worries, I think I can it and download it from the NixOS cache. I guess the NixOS cache doesn’t seem to have it. 🤷 This was actually perplexing me at this moment. In fact there are multiple discourse posts about it. My mental model however of Nix though is that I must have first evaluated the derivation (drv) in order to determine the output path to even substitute. How could the NixOS cache not have it present? Is this derivation wrong somehow? Nope. This is the derivation Nix believes that produced this Ruby binary from the database. 🤨 What does the binary cache itself say? Even the cache itself thinks this particular derivation, , produced this particular Ruby output. What if I try a different command? So I seem to have a completely different derivation, , that resulted in the same output which is not what the binary cache announces. WTF? 🫠 Thinking back to a previous post, I remember touching on modulo fixed-output derivations . Is that what’s going on? Let’s investigate from first principles. 🤓 Let’s first create which is our fixed-output derivation . ☝️ Since this is a fixed-output derivation (FOD) the produced path will not be affected to changes to the derivation beyond the contents of . Now we will create a derivation that uses this FOD. The for the output for this derivation will change on changes to the derivation except if the derivation path for the FOD changes. This is in fact what makes it “modulo” the fixed-output derivations. Let’s test this all out by changing our derivation. Let’s do this by just adding some garbage attribute to the derivation. What happens now? The path of the derivation itself, , has changed but the output path remains consistent. What about the derivation that leverages it? It also got a new derivation path but the output path remained unchanged. 😮 That means changes to fixed-output-derivations didn’t cause new outputs in either derivation but it did create a complete new tree of files. 🤯 That means in nixpkgs changes to fixed-output derivations can cause them to have new store paths for their but result in dependent derivations to have the same output path. If the output path had already been stored in the NixOS cache, then we lose the link between the new and this output path. 💥 The amount of churn that we are creating in derivations was unbeknownst to me. It can get even weirder! This example came from @ericson2314 . We will duplicate the to another file whose only difference is the value of the garbage. Let’s now use both of these in our derivation. We can now instantiate and build this as normal. What is weird about that? Well, let’s take the JSON representation of the derivation and remove one of the inputs. We can do this because although there are two input derivations, we know they both produce the same output! Let’s load this modified derivation back into our and build it again! We got the same output . Not only do we have a trait for our output paths to derivations but we can also take certain derivations and completely change them by removing inputs and still get the same output! 😹 The road to Nix enlightenment is no joke and full of dragons.

2 views
André Arko 1 weeks ago

We want to move Ruby forward

On September 9, without warning, Ruby Central kicked out the maintainers who have cared for Bundler and RubyGems for over a decade. Ruby Central made these changes against the established project policies , while ignoring all objections from the maintainers’ team . At the time, Ruby Central claimed these changes were “temporary". However, While we know that Ruby Central had no right to act the way they did, it is nevertheless clear to us that the Ruby community will be better off if the codebase, maintenance, and legal rights to RubyGems and Bundler are all together in the same place. To bring this about, we are prepared to transfer our interests in RubyGems and Bundler to Matz , end the dispute over the GitHub enterprise account, 2 GitHub organizations, and 70 repositories, and hand over all rights in the Bundler logo and Bundler name, including the trademark applications in the US, EU, and Japan. Once we have entered into a legal agreement to settle any legal claims with Ruby Central and transfer all rights to Matz, the former maintainers will step back entirely from the RubyGems and Bundler projects, leaving them fully and completely to Matz, and by extension to the entire Ruby community. Although Ruby Central’s actions were not legitimate, our commitment to the Ruby community remains strong. We’re choosing to focus our energy on projects to improve Ruby for everyone, including rv , Ruby Butler , jim , and gem.coop . Signed, The former maintainers: André , David , Ellen , Josef , Martin , and Samuel None of the “temporary” changes made by Ruby Central have been undone, more than six weeks later. Ruby Central still has not communicated with the removed maintainers about restoring any permissions. Ruby Central still has not offered “operator agreements” or “contributor agreements” to any of the removed maintainers. The Ruby Together merger agreement plainly states that it is the maintainers who will decide what is best for their projects, not Ruby Central. Last week, Matz stepped in to assume control of RubyGems and Bundler himself. His announcement states that the Ruby core team will assume control and responsibility for the primary RubyGems and Bundler GitHub repository. Ruby Central did not communicate with any removed maintainers before transferring control of the rubygems/rubygems GitHub repo to the Ruby core team. On October 24th, Shan publicly confirmed she does not believe the maintainers need to be told why they were removed .

0 views
DHH 1 weeks ago

Success always spawns haters

As Omarchy was taking off this summer, and thousands of happy users started expressing their delight with the system, I kept waiting for the universe to balance the scales of passion. Nothing of note in this world is allowed to succeed without spawning a counteracting force of haters. And now they're finally here. The same happened twenty years ago with Ruby on Rails, but back then I still thought you could argue your way to understanding. That if you just made a logical case to counter whatever objections were raised, you'd be able to persuade most haters to change their perspective. How naive. It was Kathy Sierra who changed my perspective on this. From being annoyed by straw men and non sequiturs to accepting them and the haters as a natural consequence of success. That if you want people to love your creation, you have to accept the opposing force. Yin and yang. Here's how Kathy presented the choice: It's safe there in the gray middle. Nobody is mad at you, nobody is making any bad-faith arguments, but also, nobody cares. Lots of work exists in this zone. And that's fine. We don't need every project to reach the moon! But when escape velocity is achieved, you can't avoid drawing energy from both sides. All this isn't to say that all objections, skepticism, or criticisms come from haters. Far from it. But once sufficient success is secured, a large portion will. It's just that kind of planet, as Jim Rohn would say. The trick is to see this in aggregate as a necessary milestone. One that's even worth celebrating! Have you even made something worth cheering for, if there isn't a contingent there to boo at it too? Probably not. So embrace the boos as you embrace the cheers. They come as a pair.

0 views
Kix Panganiban 2 weeks ago

Dumb Cursor is the best Cursor

I previously wrote about how I believe Cursor peaked with Cursor Compose , and that since introducing Agent mode and letting the LLM make decisions, its user experience and quality of output has subjectively gotten worse. So I tried to force Cursor to go back to its roots -- just a simple "dumb" LLM tool that can do edits and nothing else. Enter dumb mode: Essentially -- a new custom "agent" that has no access to any tools apart from edit and delete. This completely prevents it from going off the rails and diving through random areas of your codebase (or godforbid, the web) and wasting tokens and time. Using it feels like the natural extension of Cmd + K -- I choose what context to give it by manually specifying which files to edit/look at each time (just like Cursor Compose!), and because it's just Auto mode, it's quick to run and very controlled. Works exactly like the surgical tool that I was looking for. Amazing for quick or tedious edits that require no thinking or decision making -- just something that takes natural language in and code out based on what exists on the files you expose it to.

0 views
Chris Coyier 2 weeks ago

Everything is Broken

Over in the ol’ ShopTalk Discord (that’s what our Patreon thingy unlocks) our editor Chris Enns was venting about some streaming gear woes. And I said: Nothing Ever Works Chris ultimately blogged the situation and used my reply as part of the title of the blog post. Then shortly after, Jason Rodriguez’s post made the rounds in my circles: Why doesn’t anything work anymore? I’ve officially reached “old man yells at cloud” age. Same, Jason. I feel like this should be one of those viral blog post topics! Like the “good newsletters” one that went around or “why I started blogging” before that or whatever those were. Let’s make it happen people. Here’s my list from the last week or so. I was trying to log into Paramount+ on my AppleTV, but was getting some kind of unclear error. I wasn’t even sure if I had an account or not, so I tried the signup flow from my laptop. Another unclear error. Tried a different browser and the same. I just wanted to watch the Packer game and this service I either already pay or wanted to pay just wouldn’t let me. And it wasn’t the only reason I was annoyed at Paramount+ that day. I bought a Gandalf costume for Halloween for like $50. The picture has a guy, ya know, dressed up as Gandalf on the cover of the package and it looks fine. Big grey beard. Small text on the package: beard not included . What the what. I use TablePlus (which I get through SetApp ) for local database spelunking. I had some data I was trying to get at that I knew was going to be a fairly complicated query to write. It was a count of entries on a column that wasn’t the index but then I needed the index to join onto another table while having where filter and also filtering on that final count as well. I could probably reason it out, but it would probably take me an hour. So I was like: AI! Turns out TablePlus does have an AI assistant built in, so I tossed in my OpenAI API key and… You exceeded your current quota, please check your plan and billing details. Fair enough. Figure out where I can put a few bucks into my account and… I get some “unknown” error. WHY WILL NOBODY TAKE MY MONEY. I tried another browser and another credit card and turned off any “blocker” extensions I had in case of interference, but nothing worked. So I tried to use the Anthropic integration instead, and it was behaving the same. (In retrospect, it was probably the us-east-1 downtime period.) I tried the Gemini integration last, and it worked and I got my API key properly. I got my prompt together explaining exactly what I needed to do and… I am sorry, I cannot fulfill this request. The available tools lack the ability to query data or cross-reference tables. I can only retrieve metadata such as database lists, schema lists, table lists, and table creation statements. What in the what. The AI tool built into TablePlus can’t… query data? Like, wouldn’t that be the entire point of an AI assistant in a tool like this? I tried using the built-in tool rather than just going to an AI tool because I figured it would be all extra-smart, having access to the actual local database structure and stuff to use as context. I get that it might be a saftey concern (you don’t want a tool like this sending actual data over to an LLM) but that wasn’t a concern here and I didn’t need that anyway, I just needed a query that I’d run myself. Anyway I just Zoomed Marie and she helped me write the query in like 2 minutes. We brain coded it. I bought a little cheap remote control car the other day from Fred Meyer, for me and Ruby to drive around and torment her new puppy. The car took 6 AA batteries. The package came with 4 AA batteries. What in the what. Can you imagine being in the meeting where this is decided? Everybody at that table was either stupid or mean. I can’t even say greedy because someone greedy would just advocate for no batteries at all which at least is understandable. (As a consumer you’d just assume they adjust the price accordingly and you don’t have to worry about cheap junk batteries that have lost their power after sitting in a warehouse for 3 years. How far we’ve fallen.) I needed one of those like 4-cup measuring cups the other day, so grabbed a GoodCook brand one from the grocery store. After one usage and trip through the dishwasher, the markings on the side are unreadable. What in the what. Just complete garbage. Not sure why I would forsaken Pyrex , I just assumed the competition would have caught up, but apparently they have not.

1 views
Steve Klabnik 2 weeks ago

I see a future in jj

In December of 2012, I was home for Christmas, reading Hacker News. And that’s when I saw “ Rust 0.5 released ."" I’m a big fan of programming languages, so I decided to check it out. At the time, I was working on Ruby and Rails, but in college, I had wanted to focus on compilers, and my friends were all very much into systems stuff. So I decided to give Rust a try. And I liked it! But, for other reasons I won’t get into here, I was thinking about a lot of things in that moment. I was looking to shake things up a bit. So I asked myself: is Rust going to be A Thing? So, I thought about it. What does a programming language need to be successful? It needs some sort of market fit. It needs to have people willing to work on it, as bringing a new language into the world is a lot of work. And it needs users. When I considered all of these things, here’s what I saw with Rust: Market fit: there was basically no credible alternatives to C and C++. I had been involved in the D community a bit, but it was clear that it wasn’t going to take off. Go was a few years old, and hit 1.0 earlier that year, but for the kinds of work that C and C++ are uniquely able to do, I saw the same problem that I did with D: garbage collection. This doesn’t mean Go isn’t a good language, or that it’s not popular, but I didn’t see it as being able to credibly challenge C and C++ in their strongholds. Rust, on the other hand, had a novel approach to these problems: memory safety without garbage collection. Now, I also need to mention that Rust back in those days was much closer to Go than it even is today, but again, I had just learned about it for a few hours, I didn’t really have a deep understanding of it yet. If I had, I actually might have also dismissed it as well, as it wasn’t really GC that was the issue, but a significant runtime. But again: I hadn’t really come to that understanding yet. Point is: low-level programming was a space where there hadn’t been much innovation in a very long time, and I thought that meant that Rust had a chance. Check. For a team: well, Mozilla was backing it. This is a big deal. It meant that there were folks whose job it was to work on the language. There’s so much that you need to do to make a new language, and that means a ton of work, which means that if you’re going to be able to get it done in a reasonable amount of time, having paid folks working on it is certainly better than the alternative. Check. And finally, how does this translate into users? Well, Mozilla was planning on using it in Firefox. This is huge. Firefox is a major project, and if they could manage to use Rust in it, that would prove that Rust was capable of doing real work. And, more importantly, it would mean that there would be a lot of folks who would need to learn Rust to work on Firefox. This would create a base of users, which would help the language grow. Check. Finally, even though it wasn’t part of my initial assessment, I just really liked the Rust folks. I had joined IRC and chatted with people, and unlike many IRC rooms, they were actually really nice. I wanted to be around them more. And if I did, other people probably would too. So that was also a plus. So, I started learning Rust. I decided to write a tutorial for it, “Rust for Rubyists,” because I’m a sucker for alliteration. And I eventually joined the team, co-authored The Book, and if you’re reading this post, you probably know the rest of the story. For some background, jj is a new version control system (VCS), not a programming language. It is written in Rust though! While I talked about how I decided to get involved with Rust above, my approach here generalizes to other kinds of software projects, not just programming languages. I have a rule of thumb: if Rain likes something, I will probably like that thing, as we have similar technical tastes. So when I heard her talk about jj, I put that on my list of things to spend some time with at some point. I was especially intrigued because Rain had worked at Meta on their source control team. So if she’s recommending something related to source control, that’s a huge green flag. It took me a while, but one Saturday morning, I woke up a bit early, and thought to myself, “I have nothing to do today. Let’s take a look at jj.” So I did . You’ll note that link goes to a commit starting a book about jj. Since it worked for me with Rust, it probably would work for me for jj as well. Writing about something really helps clarify my thinking about it, and what better time to write something for a beginner than when you’re also a beginner? Anyway, people seem to really like my tutorial, and I’m thankful for that. So, what do I see in jj? Well, a lot of it kind of eerily mirrors what I saw in Rust: a good market fit, a solid team, and a potential user base. But the market fit is interesting. Git has clearly won, it has all of the mindshare, but since you can use jj to work on Git repositories, it can be adopted incrementally. At Oxide , Rain started using jj, and more of us did, and now we’ve got a chat channel dedicated to it. This is, in my opinion, the only viable way to introduce a new VCS: it has to be able to be partially adopted. Google is using jj, and so that is a bit different than Mozilla, but the same basic idea. I have more to say about Google’s relationship to jj, but that’s going to be a follow-up blog post. What I will say in this post is that at the first ever jj conference a few weeks ago, Martin (the creator of jj) said that internal adoption is going really well. I’m burying the lede a bit here, because the video isn’t up yet, and I don’t want to get the details of some of the more exciting news incorrect in this post. I also don’t mean to imply that everyone at Google is using jj, but the contingent feels significant to me, given how hard it is to introduce a new VCS inside a company of that size. Well, in this case, it’s using Piper as the backend, so you could argue about some of the details here, but the point is: jj is being used in projects as small as individual developers and as large as one of the largest monorepos in the world. That’s a big deal. It can show the social proof needed for others to give jj a chance. Outside of Google, a lot of people say that there’s a bit of a learning curve, but once you get over that, people really like it. Sound familiar? I think jj is different from Rust in this regard in that it’s also very easy to learn if you aren’t someone who really knows a ton about Git. It’s folks that really know Git internals and have put time and care into their workflows that can struggle a bit with jj, because jj is different. But for people who just want to get work done, jj is really easy to pick up. And when people do, they often tend to like it. jj has developed a bit of a reputation for having a passionate fanbase. People are adopting it in a skunkworks way. This is a great sign for a new tool. And finally, the team. Martin is very dedicated to jj, and has been working on it for a long time. There’s also a small group of folks working on it with him. It recently moved out from his personal GitHub account to its own organization, and has started a more formal governance. The team is full of people who have a deep history of working on source control tools, and they know what they’re doing. The burgeoning jj community reminds me of that early Rust community: a bunch of nice folks who are excited about something and eager to help it grow. Basically, to me, jj’s future looks very bright. It reminds me of Rust in all of the best ways. Speaking of burying the lede… I’ve decided to leave Oxide. Oxide is the best job I’ve ever had, and I love the people I work with. I was employee 17. I think the business will do fantastic in the future, and honestly it’s a bit of a strange time to decide to leave, since things are going so well. But at the same time, some of my friends have started a new company, ERSC , which is going to be building a new platform for developer collaboration on top of jj. Don’t worry, “errssk” isn’t going to be the name of the product. It’s kind of like how GitHub was incorporated as Logical Awesome , but nobody calls it that. This won’t be happening until next month, I have some stuff to wrap up at Oxide, and I’m going to take a week off before starting. But as sad as I am to be leaving Oxide, I’m also really excited to be able to spend more time working in the jj community, and helping build out this new platform. For those of you who’ve been asking me to finish my tutorial, well, now I’ll have the time to actually do that! I’m sorry it’s taken so long! You’ll see me talking about jj even more, spending even more time in the Discord , and generally being more involved in the community. And I’ll be writing more posts about it here as well, of course. I’m really excited about this next chapter. 2025 has been a very good year for me so far, for a number of reasons, and I am grateful to be able to take a chance on something that I’m truly passionate about. Here’s my post about this post on BlueSky: I see a future in #jj-vcs : steveklabnik.com/writing/i-se... I see a future in jj Blog post: I see a future in jj by Steve Klabnik

0 views
ptrchm 2 weeks ago

How to Accept Crypto Payments in Rails

I wanted to see what it would take to implement crypto payments in PixelPeeper . With Coinbase Commerce, it’s surprisingly easy. To accept crypto, you’ll need an ETH wallet address and a Coinbase Commerce account. Coinbase provides a nice hosted checkout page, where users have multiple options to pay (the funds will be converted to USDC). Upon successful payment, Coinbase will collect a small fee and the funds will be sent to your ETH address (there’s a caveat, see the last section). How does it work? Create a Charge Rails Integration The Woes of Crypto UX

0 views
DHH 3 weeks ago

A petabyte worth of Omarchy in a month

Omarchy didn't even exist before this summer. I did much of the pre-release work during the downtime between sessions at the 24 Hours of Le Mans in June. And now, just a few months later, we've delivered a petabyte of ISOs in the past thirty days alone. That's about 150,000 installs of the Omarchy Linux distribution! I've been involved with a lot of successful open-source projects in the past quarter of a century or so. Ruby on Rails, first and foremost. But nothing, not even Rails, grew as quickly as Omarchy has been growing in the first few months of its life. It's rather remarkable. This is what product-market fit looks like. Doesn't matter if the product is free or not. The fit is obvious. The stream of people who don't just enjoy Omarchy but love it is seemingly endless. The passion is palpable. But why? And why now? As per usual, there are a lot of contributing factors, but key is how Apple and Microsoft have been fumbling their relationship with people who love computers in general and developers in particular. Microsoft is killing off Windows 10, which in turn cuts off a whole slew of perfectly fine computers made prior to around 2017–2018. They also seem intent on shoving AI into everything, and wavering on whether that might be optional or not. Oh, and Windows is still Windows: decades of patching cracks in a foundation that just never was all that solid to begin with. Apple too has turned a ton of people off with macOS 26 Tahoe, liquid glass, and faltering software quality. They're also cutting off all Intel-based Macs from future updates. A Mac Mini sold as recently as 2023 is now end-of-life! This is before we even talk about how poorly the company has been treating developers depending on the App Store bureaucracy. Meanwhile, Linux has never looked better. Hyprland, the tiling window manager at the heart of Omarchy, is a sensation. It's brought an incredible level of finesse, detail, and style to the tiling window management space: superb animations, lightning-fast execution, and super-light resource consumption. The historic gap in native GUI apps has never mattered less either. The web has conquered all as the dominant computing platform. In the past, missing, say, Photoshop was a big deal. Now it's Figma — a web app! — that's driving designers. Same too with tools like Microsoft Office or Outlook, which are all available on the web. I'm not saying there aren't specialized apps that some people simply can't do without, that keep them trapped on Windows or Mac. But I am saying that they've never been fewer. Almost everything has a great web alternative. And for developers, the fact is that Linux was always a superior platform in terms of performance and tooling for most programming environments. With 95% of the web running on Linux servers, all optimization and tuning needed to get the most out of the hardware was done with Linux in mind. This is why even a $500 Beelink Mini PC is competitive with an M4 Max machine costing thousands of dollars for things like our HEY test suite, which runs on Ruby and MySQL. Linux is just really efficient and really fast. Finally, I think the argument that owning your computer, fully and deeply, is starting to resonate. The Free Software crowd has been making the argument since the 90s, if not before, but it's taken Apple's and Microsoft's recent tightening of the reins on our everyday operating systems to make it relevant for most. Omarchy is a beautiful, modern, and opinionated Linux distribution, but it's also yours. Everything is preconfigured, sure, but every configuration is also changeable. Don't like how something works? Change it. Don't like the apps I use? Change them. Don't like how something looks? Redesign it. The level of agency is off the charts. Turns out that plenty of people were starved for just this. All it took was someone to actually put all the pieces together, ignore the Linux neckbeards who insist you aren't worthy to run Arch or Hyprland without spending a hundred hours setting it up from scratch, and invite everyone to the party!

1 views
André Arko 4 weeks ago

Announcing <code>rv</code> 0.2

With the help of many new contributors, and after many late nights wrestling with make, we are happy to (slightly belatedly) announce the 0.2 release of rv ! This version dramatically expands support for Rubies, shells, and architectures. Rubies: we have added Ruby 3.3, as well as re-compiled all Ruby 3.3 and 3.4 versions with YJIT. On Linux, YJIT increases our glibc minimum version to 2.35 or higher. That means most distro releases from 2022 or later should work, but please let us know if you run into any problems. Shells: we have added support for bash, fish, and nushell in addition to zsh. Architectures: we have added Ruby compiled for macOS on x86, in addition to Apple Silicon, and added Ruby compiled for Linux on ARM, in addition to x86. Special thanks to newest member of the maintainers’ team @adamchalmers for improving code and tests, adding code coverage and fuzzing, heroic amounts of issue triage, and nushell support. Additional thanks are due to all the new contributors in version 0.2, including @Thomascountz , @lgarron , @coezbek , and @renatolond . To upgrade, run , or check the release notes for other options.

0 views
André Arko 4 weeks ago

The RubyGems “security incident”

Ruby Central posted an extremely concerning “ Incident Response Timeline ” today, in which they make a number of exaggerated or purely misleading claims. Here’s my effort to set the record straight. First, and most importantly: I was a primary operator of RubyGems.org, securely and successfully, for over ten years. Ruby Central does not accuse me of any harms or damages in their post, in fact stating “we have no evidence to indicate that any RubyGems.org data was copied or retained by unauthorized parties, including Mr. Arko.” The actions I took during a time of great confusion and uncertainty (created by Ruby Central!) were careful, specific, and aimed to defend both Ruby Central the organization and RubyGems.org the service from potential threats. The majority of the team, including developers in the middle of paid full-time work for Ruby Central, had just had all of their permissions on GitHub revoked. And then restored six days later. And then revoked again the next day. Even after the second mass-deletion of team permissions, Marty Haught sent an email to the team within minutes, at 12:47pm PDT, saying he was (direct quote) “terribly sorry” and “I messed up”. Update : Added email timestamp. The erratic and contradictory communication supplied by Marty Haught, and the complete silence from Shan and the board, made it impossible to tell exactly who had been authorized to take what actions. As this situation occurred, I was the primary on-call. My contractual, paid responsibility to Ruby Central was to defend the RubyGems.org service against potential threats. Marty’s final email clearly stated “I’ll follow up more on this and engage with the governance rfc in good faith.”. Just a few minutes after that email, at 1:01pm PDT, Marty also posted a public GitHub comment , where he agreed to participate in the proposed governance process and stated “I’m committed to find the right governance model that works for us all. More to come.” Update : screenshot of comment removed and replaced with link, since the comment appears to still be visible (at least to logged out users) on GitHub. Given Marty’s claims, the sudden permission deletions made no sense. Worried about the possibility of hacked accounts or some sort of social engineering, I took action as the primary on-call engineer to lock down the AWS account and prevent any actions by possible attackers. I did not change the email addresses on any accounts, leaving them all owned by a team-shared email at rubycentral.org, to ensure the organization retained overall control of the accounts, even if individuals were somehow taking unauthorized actions. Within a couple of days, Ruby Central made an (unsigned) public statement, and various board members agreed to talk directly to maintainers. At that point, I realized that what I thought might have been a malicious takeover was both legitimate and deliberate, and Marty would never “fix the permissions structure”, or “follow up more” as he said. Once I understood the situation, I backed off to let Ruby Central take care of their “security audit”. I left all accounts in a state where they could recover access. I did not alter, or try to alter, anything in the Ruby Central systems or GitHub repository after that. I was confident, at the time, that Ruby Central’s security experts would quickly remove all outside access. My confidence was sorely misplaced. Almost two weeks later, someone asked if I still had access and I discovered (to my great alarm), that Ruby Central’s “security audit” had failed. Ruby Central also had not removed me as an “owner” of the Ruby Central GitHub Organization. They also had not rotated any of the credentials shared across the operational team using the RubyGems 1Password account. I believe Ruby Central confused themselves into thinking the “Ruby Central” 1Password account was used by operators, and they did revoke my access there. However, that 1Password account was not used by the open source team of RubyGems.org service operators. Instead, we used the “RubyGems” 1Password account, which was full of operational credentials. Ruby Central did not remove me from the “RubyGems” 1Password account, even as of today. Aware that I needed to disclose this surprising access, but also aware that it was impossible for anyone except former operators to exploit this security failure, I immediately wrote an email to Ruby Central to disclose the problem. Here is a copy of my disclosure email, in full. Ruby Central did not reply to this email for over three days. When they finally did reply, they seem to have developed some sort of theory that I was interested in “access to PII”, which is entirely false. I have no interest in any PII, commercially or otherwise . As my private email published by Ruby Central demonstrates, my entire proposal was based solely on company-level information, with no information about individuals included in any way. Here’s their response, over three days later. In addition to ignoring the (huge) question of how Ruby Central failed to secure their AWS Root credentials for almost two weeks, and appearing to only be aware of it because I reported it to them , their reply also failed to ask whether any other shared credentials might still be valid. There were more. Unbeknownst to me, while I was answering Marty’s email in good faith, Ruby Central’s attorney was sending my lawyer a letter alleging I had committed a federal crime, on the theory that I had “hacked” Ruby Central’s AWS account. On the contrary, my actions were taken in defense of the service that Ruby Central was paying me to support and defend. With my side of the story told, I’ll leave it to you to decide whether you think it’s true that “Ruby Central remains committed to transparent, responsible stewardship of the RubyGems infrastructure and to maintaining the security and trust that the Ruby ecosystem depends on.”

1 views
マリウス 1 months ago

Alpine Linux on a Bare Metal Server

When I began work on 📨🚕 ( MSG.TAXI ) , I kept things deliberately low-key, since I didn’t want it turning into a playground for architecture astronauts . For the web console’s tech stack, I went with the most boring yet easy-to-master CRUD stack I could find , that doesn’t depend on JavaScript . And while deploying Rails in a sane way (without resorting to cOnTaInErS ) is a massive PITA, thanks to the Rails author’s cargo-cult mentality and his followers latching onto every half-baked wannabe-revolutionary idea, like Kamal and more recently Omarchy , as if it were a philosopher’s stone, from a development perspective it’s still the most effective getting-shit-done framework I’ve used to date. Best of all, it doesn’t rely on JavaScript (aside from actions, which can be avoided with a little extra effort). Similarly, on the infrastructure side, I wanted a foundation that was as lightweight as possible and wouldn’t get in my way. And while I’m absolutely the type of person who would run a Gentoo server, I ultimately went with Alpine Linux due to its easier installation, relatively sane defaults (with a few exceptions, more on that later ), and its preference for straightforward, no-nonsense tooling that doesn’t try to hide magic behind the scenes. “Why not NixOS?” you might ask. Since I’m deploying a lightweight, home-brewed Ruby/Rails setup alongside a few other components, I didn’t see the point of wrapping everything as Nix packages just to gain the theoretical benefits of NixOS. In particular, the CI would have taken significantly longer, while the actual payoff in my case would have been negligible. Since I’m paying for 📨🚕 out of my own pocket, I wanted infrastructure that’s cheap yet reliable. With plenty of people on the internet praising Hetzner , I ended up renting AMD hardware in one of their Finnish datacenters. Hetzner doesn’t offer as many Linux deployment options as cloud providers like Vultr , so I had to set up Alpine myself, which was pretty straightforward. To kickstart an Alpine installation on a Hetzner system, you just need to access the server’s iPXE console, either by renting a Hetzner KVM for an hour or by using their free vKVM feature. From there, you can launch the Alpine Linux by initializing the network interface and chain-loading the file: From that point on setup should be easy thanks to Alpine’s installer routine. If you’re using Hetzner’s vKVM feature to install Alpine, this chapter is for you. Otherwise, feel free to skip ahead. vKVM is a somewhat hacky yet ingenious trick Hetzner came up with, and it deserves a round of applause. If you’re curious about how it works under the hood, rent a real KVM once and reboot your server into vKVM mode. What you’ll see is that after enabling vKVM in Hetzner’s Robot , iPXE loads a network image, which boots a custom Linux OS. Within that OS, Hetzner launches a QEMU VM that uses your server’s drives to boot whatever you have installed. It’s basically Inception at the OS level. As long as vKVM is active (meaning the iPXE image stays loaded), your server is actually running inside this virtualized environment, with display output forwarded to your browser. Run while in vKVM mode and you’ll see, for example, your NIC showing up as a VirtIO device. Here’s the catch: When you install Alpine through this virtualized KVM environment, it won’t generate the your physical server actually needs. For instance, if your server uses an NVMe drive, you may discover that doesn’t include the module, causing the OS to fail on boot. Hetzner’s documentation doesn’t mention this, and it can easily bite you later. Tl;dr: If you installed your system via vKVM , make sure your includes all necessary modules. After updating , regenerate the . There are several ways to do this, but I prefer . Always double-check that the regenerated really contains everything you need. Unfortunately Alpine doesn’t provide tools for this, so here’s a .tar.gz with Debian’s and . Extract it into , and note that you may need to for them to work properly, due to Alpine’s somewhat annoying defaults (more on that later ). Finally, after rebooting, make sure you’ve actually left the vKVM session. You can double check by running . If the session is still active (default: 1h), your system may have just booted back into the VM, which you can identify by its Virt-devices. As soon as your Alpine Linux system is up and running there are a couple of things that I found important to change right off the bat. Alpine’s default boot timeout is just 1 second, set in ( ). If you ever need to debug a boot-related issue over a high-latency KVM connection, you will dread that 1-second window. I recommend increasing it to 5 seconds and running to apply the change. In practice, you hopefully won’t be rebooting the server that often, so the extra four seconds won’t matter day-to-day. Alpine uses the classic to configure network settings. On Hetzner’s dedicated servers, you can either continue using DHCP for IPv4 or set the assigned IP address statically. For IPv6, you’ll be given a subnet from which you can choose your own address. Keep in mind that the first usable IPv6 on Hetzner’s dedicated servers is : Amongst the first things you do should be disabling root login and password authentication via SSH: Apart from that you might want to limit the type of key exchange methods and algorithms that your SSH server allows, depending on the type of keys that you’re using. Security by obscurity: Move your SSH server from its default port (22) to something higher up and more random to make it harder for port-scanners to hit it. Finicky but more secure: Implement port knocking and use a handy client to open the SSH port for you only, for a limited time only. Secure: Set up a small cloud instance to act as Wireguard peer and configure your server’s SSH port to only accept connections from the cloud instance using a firewall rule . Use Tailscale if a dedicated Wireguard instance is beyond your expertise. You will likely want to have proper (GNU) tools around, over the defaults that Alpine comes with ( see below ). Some of the obvious choices include the following: In addition, I also like to keep a handful of convenience tools around: This is a tricky part because everyone’s monitoring setup looks different. However, there are a few things that make sense in general. Regardless what you do with your logs it’s generally a good idea to switch from BusyBox to something that allows for more advanced configurations, like syslog-ng : You probably should have an overview of how your hardware is doing. Depending on what type of hard drives your server has, you might want to install the or packages. UFW is generally considered an uncomplicated way to implement firewalling without having to complete a CCNP Security certification beforehand: Depending on your SSH setup and whether you are running any other services that could benefit from it, installing Fail2Ban might make sense: The configuration files are located at and you should normally only create/edit the files. The easiest way to backup all the changes that you’ve made to the general configuration is by using , the integrated Alpine local backup solution that was originally intended as a tool to manage diskless mode installations. I would, however, recommend to manually back up installed packages ( ) and use Restic for the rest of the system, including configuration files and important data, e.g.: However, backups depend on the data that your system produces and your desired backup target. If you’re looking for an easy to use, hosted but not-too-expensive one-off option, then Tarsnap might be for you. You should as well look into topics like local mail delivery, system integrity checks (e.g. AIDE ) and intrusion detection/prevention (e.g. CrowdSec ). Also, if you would like to get notified for various server events, check 📨🚕 ( MSG.TAXI ) ! :-) One of the biggest annoyances with Alpine is BusyBox : You need SSH? That’s BusyBox. The logs? Yeah, BusyBox. Mail? That’s BusyBox, too. You want to untar an archive? BusyBox. What? It’s gzipped? Guess what, you son of a gun, gzip is also BusyBox. I understand why Alpine chose BusyBox for pretty much everything, given the context that Alpine is most often used in ( cOnTaInErS ). Unfortunately, most BusyBox implementations are incomplete or incompatible with their full GNU counterparts, leaving you wondering why something that worked flawlessly on your desktop Linux fails on the Alpine box. By the time I finished setting up the server, there was barely any BusyBox tooling left. However, I occasionally had to resort to some odd trickery to get things working. You now have a good basis to set up whatever it is that you’re planning to use the machine for. Have fun! Footnote: The artwork was generated using AI and further botched by me using the greatest image manipulation program .

0 views
ptrchm 1 months ago

Event-driven Modular Monolith

The main Rails app I currently work on has just turned eight. It’s not a huge app. It doesn’t deal with web-scale traffic or large volumes of data. Only six people working on it now. But eight years of pushing new code adds up. This is a quick overview of some of the strategies we use to keep the codebase maintainable. After the first few years, our codebase suffered from typical ailments: tight coupling between domains, complex database queries spread across various parts of the app, overgrown models, a maze of side effects triggered by ActiveRecord callbacks , endlessly chained associations (e.g. ) – with an all-encompassing model sitting on top of the pile. Modular Monolith Pub/Sub (Events) Patterns Service Objects Repositories for Database Queries Slim and Dumb Models Bonus: A Separate Frontend App How Do I Start?

0 views
André Arko 1 months ago

Announcing gem.coop, a community gem server

The team behind the last ten years of rubygems.org, including @deivid-rodriguez, @duckinator, @martinemde, @segiddins, @simi, and myself, is very pleased to announce a new gem server for the Ruby community: gem.coop . The new server’s governance policies are being prepared in coordination with Mike McQuaid of Homebrew, and will be released later this week. The current versions of RubyGems and Bundler work with this new server already, and any Ruby developer is welcome to switch to using this new server immediately. We have exciting plans to add new features and functionality in the coming days. Join us!

1 views
Robin Moffatt 1 months ago

Stumbling into AI: Part 5—Agents

A short series of notes for myself as I learn more about the AI ecosystem as of Autumn [Fall] 2025. The driver for all this is understanding more about Apache Flink’s Flink Agents project, and Confluent’s Streaming Agents . I started off this series —somewhat randomly, with hindsight—looking at Model Context Protocol ( MCP ) . It’s a helper technology to make things easier to use and provide a richer experience. Next I tried to wrap my head around Models —mostly LLMs, but also with an addendum discussing other types of model too. Along the lines of MCP, Retrieval Augmented Generation ( RAG ) is another helper technology that on its own doesn’t do anything but combined with an LLM gives it added smarts. I took a brief moment in part 4 to try and build a clearer understanding of the difference between ML and AI . So whilst RAG and MCP combined make for a bunch of nice capabilities beyond models such as LLMs alone, what I’m really circling around here is what we can do when we combine all these things: Agents ! But…what is an Agent, both conceptually and in practice? Let’s try and figure it out. Let’s begin with Wikipedia’s definition : In computer science, a software agent is a computer program that acts for a user or another program in a relationship of agency . We can get more specialised if we look at Wikipedia’s entry for an Intelligent Agent : In artificial intelligence, an intelligent agent is an entity that perceives its environment, takes actions autonomously to achieve goals , and may improve its performance through machine learning or by acquiring knowledge . Citing Wikipedia is perhaps the laziest ever blog author’s trick, but I offer no apologies 😜. Behind all the noise and fuss, this is what we’re talking about: a bit of software that’s going to go and do something for you (or your company) autonomously . LangChain have their own definition of an Agent, explicitly identifying the use of an LLM: An AI agent is a system that uses an LLM to decide the control flow of an application. The blog post from LangChain as a whole gives more useful grounding in this area and is worth a read. In fact, if you want to really get into it, the LangChain Academy is free and the Introduction to LangGraph course gives a really good primer on Agents and more. Meanwhile, the Anthropic team have a chat about their definition of an Agent . In a blog post Anthropic differentiates between Workflows (that use LLMs) and Agents: Workflows are systems where LLMs and tools are orchestrated through predefined code paths. Agents, on the other hand, are systems where LLMs dynamically direct their own processes and tool usage, maintaining control over how they accomplish tasks. Independent researcher Simon Willison also uses the LLM word in his definition: An LLM agent runs tools in a loop to achieve a goal. He explores the definition in a recent blog post: I think “agent” may finally have a widely enough agreed upon definition to be useful jargon now , in which Josh Bickett’s meme demonstrates how much of a journey this definition has been on: That there’s still discussion and ambiguity nearly two years after this meme was created is telling. My colleague Sean Falconer knows a lot more this than I do. He was a guest on a recent podcast episode in which he spells things out: [Agentic AI] involves AI systems that can reason, dynamically choose tasks, gather information, and perform actions as a more complete software system. [ 1 ] [Agents] are software that can dynamically decide its own control flow: choosing tasks, workflows, and gathering context as needed. Realistically, current enterprise agents have limited agency[…]. They’re mostly workflow automations rather than fully autonomous systems . [ 2 ] In many ways […] an agent [is] just a microservice . [ 3 ] A straightforward software Agent might do something like: Order more biscuits when there are only two left The pseudo-code looks like this: We take this code, stick it on a server and leave it to run. One happy Agent, done. An AI Agent could look more like this: Other examples of AI Agents include: Coding Agents . Everyone’s favourite tool (when used right). It can reason about code, it can write code, it can review PRs. One of the trends that I’ve noticed recently (October 2025) is the use of Agents to help with some of the up-front jobs in software engineering (such as data modelling and writing tests ), rather than full-blown code that’s going to ship to production. That’s not to say that coding Agents aren’t being used for that, but by using AI to accelerate certain tasks whilst retaining human oversight (a.k.a. HITL ) it makes it easier to review the output rather than just trusting to luck that reams and reams of code are correct. There’s a good talk from Uber on how they’re using AI in the development process, including code conversion, and testing. Travel booking . Perhaps you tell it when you want to go, the kind of vacation you like, and what your budget is; it then goes and finds where it’s nice at that time of year, figures out travel plans within your budget, and either proposes an itinerary or even books it for you. Another variation could be you tell it where , and then it integrates with your calendar to figure out the when . This is a canonical example that is oft-cited; I’d be interested if anyone can point me to an actual implementation of it, even if just a toy one . I saw this in a blog post from Simon Willison that made me wince, but am leaving the above in anyway just to serve as an example of the confusion/hype that exists in this space: comes from plus , the latter meaning of, relating to, or characterised by . So is simply AI that is characterised by an Agent, or Agency. Contrast that to AI that’s you sat at the ChatGPT prompt asking it to draw pictures of a duck dressed as a clown . Nothing Agentic about that—just a human-led and human-driven interaction. "AI Agents" becomes a bit of a mouthful with the qualifier, so much of the current industry noise is simply around "Agents". That said, "Agentic AI" sounds cool, so gets used as the marketing term in place of "AI" alone. So we’ve muddled our way through to some kind of understanding of what an Agent is, and what we mean by Agentic AI. But how do we actually build one? All we need is an LLM (such as access to the API for OpenAI or Claude ), something to call that API (there are worse choices than !), and a way to call external services (e.g. MCP servers) if the LLM determines that it needs to use them. So in theory we could build an Agent with some lines of bash, some API calls, and a bunch of sticky-backed plastic . This is a grossly oversimplified example (and is missing elements such as memory)—but it hopefully illustrates what we’re building at the core of an Agent. On top of this goes all the general software engineering requirements of any system that gets built (suitable programming language and framework, error handling, LLM output validation, guard rails, observability, tests, etc etc). The other nuance that I’ve noticed is that whilst the above simplistic diagram is 100% driven by an LLM (it decides what tools to call, it decides when to iterate) there are plenty of cases where an Agent is to some degree rules-driven. So perhaps the LLM does some of the autonomous work, but then there’s a bunch of good ol' statements in there too. This is also borne out by the notion of "Workflows" when people talk about Agents. An Agent doesn’t wake up in the morning and set out on its day serving only to fulfill its own goals and enrichment. More often than not an Agent is going to be tightly bound into a pre-defined path with a limited range of autonomy. What if you want to actually build this kind of thing for real? That’s where tools like LangGraph and LangChain come in. Here’s a notebook with an example of an actual Agent built with these tools. LlamaIndex is another framework, with details of building an Agent in their docs. As we build up from the so-simple-it-is-laughable strawman example of an Agent above, one of the features we’ll soon encounter is the concept of memory. The difference between a crappy response and a holy-shit-that’s-magic response from an LLM is often down to context . The richer the context, the better a chance it has at generating a more accurate output. So if an Agent can look back on what it did previously, determining what worked well and what didn’t, perhaps even taking into account human feedback, it can then generate a more successful response the next time. You can read a lot more about memory in this chapter of Agentic Design Patterns by Antonio Gulli . This blog post from "The BIG DATA guy" is also useful: Agentic AI, Agent Memory, & Context Engineering This diagram from Generative Agents: Interactive Simulacra of Human Behavior (J.S. Park, J.C. O’Brien, C.J. Cai, M.R. Morris, P. Liang, M.S. Bernstein) gives a good overview of a much richer definition of an Agent’s implementation. The additional concepts include memory (discussed briefly above), planning, and reflection: Also check out Paul Iusztin’s talk from QCon London 2025 on The Data Backbone of LLM Systems . Around the 35-minute mark he goes into some depth around Agent architectures. Just as you can build computer systems as monoliths (everything done in one place) or microservices (multiple programs, each responsible for a discrete operation or domain), you can also have one big Agent trying to do everything (probably not such a good idea) or individual Agents each good at their particular thing that are then hooked together into what’s known as a Multi-Agent System (MAS). Sean Falconer’s family meal planning demo is a good example of a MAS. One Agent plans the kids' meals, one the adults' meals, another combines the two into a single plan, and so on. This is a term you’ll come across referring to the fact that Agents might be pretty good, but they’re not infallible. In the travel booking example above, do we really trust the Agent to book the best holiday for us? Almost certainly we’d want—at a minimum—the option to sign off on the booking before it goes ahead and sinks £10k on an all-inclusive trip to Bognor Regis. Then again, we’re probably happy enough for an Agent to access our calendars without asking permission, and as to whether they need permission or not to create a meeting is up to us and how much we trust them. When it comes to coding, having an Agent write code, test it, fix the broken tests, compare it to a spec, and iterate is really neat. On the other hand, letting it decide to run …less so 😅. Every time an Agent requires HITL, it reduces its autonomy and/or responsiveness to situations. As well as simply using smarter models that make fewer mistakes, there are other things that an Agent can do to reduce the need for HITL such as using guardrails to define acceptable parameters. For example, an Agent is allowed to book travel but only up to a defined threshold. That way the user gets to trade off convenience (no HITL) with risk (unintended first-class flight to Hawaii). 📃 Generative Agents: Interactive Simulacra of Human Behavior 🎥 Paul Iusztin - The Data Backbone of LLM Systems - QCon London 2025 📖 Antonio Gulli - Agentic Design Patterns 📖 Sean Falconer - https://seanfalconer.medium.com/ Coding Agents . Everyone’s favourite tool (when used right). It can reason about code, it can write code, it can review PRs. One of the trends that I’ve noticed recently (October 2025) is the use of Agents to help with some of the up-front jobs in software engineering (such as data modelling and writing tests ), rather than full-blown code that’s going to ship to production. That’s not to say that coding Agents aren’t being used for that, but by using AI to accelerate certain tasks whilst retaining human oversight (a.k.a. HITL ) it makes it easier to review the output rather than just trusting to luck that reams and reams of code are correct. There’s a good talk from Uber on how they’re using AI in the development process, including code conversion, and testing. Travel booking . Perhaps you tell it when you want to go, the kind of vacation you like, and what your budget is; it then goes and finds where it’s nice at that time of year, figures out travel plans within your budget, and either proposes an itinerary or even books it for you. Another variation could be you tell it where , and then it integrates with your calendar to figure out the when . This is a canonical example that is oft-cited; I’d be interested if anyone can point me to an actual implementation of it, even if just a toy one . I saw this in a blog post from Simon Willison that made me wince, but am leaving the above in anyway just to serve as an example of the confusion/hype that exists in this space: 📃 Generative Agents: Interactive Simulacra of Human Behavior 🎥 Paul Iusztin - The Data Backbone of LLM Systems - QCon London 2025 📖 Antonio Gulli - Agentic Design Patterns 📖 Sean Falconer - https://seanfalconer.medium.com/

0 views
David Bushell 1 months ago

Not My Cup of Tea

As blog topics go, last week’s Next.js clownshow was a freebie. A bit of front-end dev and a middle finger to fascism. I had it drafted before my tea went cold. I wasn’t expecting round two to hit harder. This week I momentarily lost the will to blog. Ok, I’m being very dramatic. It wasn’t writer’s block or imposter syndrome. Not the usual suspects. I was just stun-locked for a couple of days. Like any self-respecting person with an ounce of sanity, I’ve been off Twitter X since it got all fashy. Nevertheless, it’s impossible to avoid the crazy stuff. And this week’s crazy was another level. Enjoyed my discussion with PM Netanyahu on how AI education and literacy will keep our free societies ahead. We spoke about AI empowering everyone to build software and the importance of ensuring it serves quality and progress. Optimistic for peace, safety, and greatness for Israel and its neighbors. @rauchg Sep 29, 2025 (xcancel) - Guillermo Rauch On seeing this I noted in anger followed by a hastily worded social post: I wonder if @svelte.dev is onboard with this? The obvious answer is: ‘no’. Everything I already know suggests the Svelte maintainers are antithetical to Rauch. My words were clumsy not malicious. I was merely wondering what on earth does Svelte do? WTF does anyone do when a major funding source does… that? A quick catch-up for those unaware: In the wake of this mess some people were keen to remind everyone of Svelte’s independence. Rich Harris and others were lost for words. Can you blame them? I called out Svelte specifically because it’s the one project with ties to Vercel I care about. Svelte is a shining light in a rather bleak JavaScript ecosystem . I try to avoid political discussion online. I’m a little ham-fisted in questioning the ethics of my tech stack. Some argue that taking Vercel’s money has moral baggage. That it makes the recipient complicit in brand-washing. Personally I’m not sure what to think. To cut ties with Vercel would be morally courageous. Then what? There’s no easy alternative to keep the lights on. The day after Rauch’s infamous selfie Vercel announced $300M in series F funding . The “F” stands for “f*ck you” I’m told. Vercel’s pivot to AI banked them one third of a billy in a world where profit doesn’t matter . Until the “AI” bubble bursts Vercel are untouchable. Is it wrong to siphon off a little cheddar for better use? Let’s be honest, who else is funding open source software? Few users are willing to pay for it, developers included. The world revolves around load-bearing Nebraskans . So what can projects like Svelte do? Does it matter? Nothing matters anymore. You can just say things these days. Make up your own truths. Re-roll the chatbot until it agrees. And if your own chatbot continues to fact-check you just rewrite history . We live in a reality where you can spew white supremacy fan fiction for the boys on X and Cloudflare will sponsor your side hustle a week later. Moral bankruptcy is a desirable trait in this economy. Is it any wonder open source maintainers with a backbone are shell-shocked? I’ll leave Svelte to figure out how to navigate impossible waters. Let’s hope that open governance remains intact, lest it go off the rails . For the rest of us: Taking action and Doing The Right Thing is often difficult, always exhausting, but it is what we must do, together. We all deserve better. The world deserves better. It’ll take a little work to get there, but there is hope. We all have a choice - Salma Alam-Naylor Amen to that. Thanks for reading! Follow me on Mastodon and Bluesky . Subscribe to my Blog and Notes or Combined feeds. Guillermo Rauch is CEO of Vercel Netanyahu is a wanted war criminal Vercel funds Svelte and employs creator Rich Harris Svelte remains open governance; not owned by Vercel Cut ties and take the moral victory for a day and then be amazed by the magical vanishing act of an entire dev community when asked for spare change tomorrow? Continue discarding skeets until the drama blows over? Pivot to “AI”?

0 views
Joel Drapper 1 months ago

Ruby Central’s “security measures” leave front door wide open

Despite locking maintainers out of

1 views
Joel Drapper 1 months ago

The RubyGems takeover from the perspective of an open source developer

We must demand that Ruby Central make this right.

0 views
Joel Drapper 1 months ago

Yippee: Our vision for modern full-stack Ruby framework

What would it look like to build a new web application framework from a blank sheet of paper today?

15 views
André Arko 1 months ago

stupid jj tricks

This post was originally given as a talk for JJ Con . The slides are also available. Welcome to “stupid jj tricks”. Today, I’ll be taking you on a tour through many different jj configurations that I have collected while scouring the internet. Some of what I’ll show is original research or construction created by me personally, but a lot of these things are sourced from blog post, gists, GitHub issues, Reddit posts, Discord messages, and more. To kick things off, let me introduce myself. My name is André Arko, and I’m probably best known for spending the last 15 years maintaining the Ruby language dependency manager, Bundler. In the world, though, my claim to fame is completely different: Steve Klabnik once lived in my apartment for about a year, so I’m definitely an authority on everything about . Thanks in advance for putting into the official tutorial that whatever I say here is now authoritative and how things should be done by everyone using , Steve. The first jj tricks that I’d like to quickly cover are some of the most basic, just to make sure that we’re all on the same page before we move on to more complicated stuff. To start with, did you know that you can globally configure jj to change your name and email based on a path prefix? You don’t have to remember to set your work email separately in each work repo anymore. I also highly recommend trying out multiple options for formatting your diffs, so you can find the one that is most helpful to you. A very popular diff formatter is , which provides syntax aware diffs for many languages. I personally use , and the configuration to format diffs with delta looks like this: Another very impactful configuration is which tool jj uses to handle interactive diff editing, such as in the or commands. While the default terminal UI is pretty good, make sure to also try out Meld, an open source GUI. In addition to changing the diff editor, you can also change the merge editor, which is the program that is used to resolve conflicts. Meld can again be a good option, as well as any of several other merging tools. Tools like mergiraf provide a way to attempt syntax-aware automated conflict resolution before handing off any remaining conflicts to a human to resolve. That approach can dramatically reduce the amount of time you spend manually handling conflicts. You might even want to try FileMerge, the macOS developer tools built-in merge tool. It supports both interactive diff editing and conflict resolution. Just two more configurations before we move on to templates. First, the default subcommand, which controls what gets run if you just type and hit return. The default is to run , but my own personal obsessive twitch is to run constantly, and so I have changed my default subcommand to , like so: The last significant configuration is the default revset used by . Depending on your work patterns, the multi-page history of commits in your current repo might not be helpful to you. In that case, you can change the default revset shown by the log command to one that’s more helpful. My own default revset shows only one change from my origin. If I want to see more than the newest change from my origin I use to get the longer log, using the original default revset. I’ll show that off later. Okay, enough of plain configuration. Now let’s talk about templates! Templates make it possible to do many, many things with jj that were not originally planned or built in, and I think that’s beautiful. First, if you haven’t tried this yet, please do yourself a favor and go try every builtin jj template style for the command. You can list them all with , and you can try them each out with . If you find a builtin log style that you especially like, maybe you should set it as your default template style and skip the rest of this section. For the rest of you sickos, let’s see some more options. The first thing that I want to show you all is the draft commit description. When you run , this is the template that gets generated and sent to your editor for you to complete. Since I am the kind of person who always sets git commit to verbose mode, I wanted to keep being able to see the diff of what I was committing in my editor when using jj. Here’s what that looks like: If you’re not already familiar with the jj template functions, this uses to combine strings, to choose the first value that isn’t empty, to add before+after if the middle isn’t empty, and to make sure the diff status is fully aligned. With this template, you get a preview of the diff you are committing directly inside your editor, underneath the commit message you are writing. Now let’s look at the overridable subtemplates. The default templates are made of many repeated pieces, including IDs, timestamps, ascii art symbols to show the commit graph visually, and more. Each of those pieces can be overrides, giving you custom formats without having to change the default template that you use. For example, if you are a UTC sicko, you can change all timestamps to render in UTC like , with this configuration: Or alternatively, you can force all timestamps to print out in full, like (which is similar to the default, but includes the time zone) by returning just the timestamp itself: And finally you can set all timestamps to show a “relative” distance, like , rather than a direct timestamp: Another interesting example of a template fragment is supplied by on GitHub, who changes the node icon specifically to show which commits might be pushed on the next command. This override of the template returns a hollow diamond if the change meets some pushable criteria, and otherwise returns the , which is the regular icon. It’s not a fragment, but I once spent a good two hours trying to figure out how to get a template to render just a commit message body, without the “title” line at the top. Searching through all of the built-in jj templates finally revealed the secret to me, which is a template function named . With that knowledge, it becomes possible to write a template that returns only the body of a commit message: We first extract the title line, remove that from the front, and then trim any whitespace from the start of the string, leaving just the description body. Finally, I’d like to briefly look at the possibility of machine-readable templates. Attempting to produce JSON from a jj template string can be somewhat fraught, since it’s hard to tell if there are quotes or newlines inside any particular value that would need to be escaped for a JSON object to be valid when it is printed. Fortunately, about 6 months ago, jj merged an function, which makes it possible to generate valid JSON with a little bit of template trickery. For example, we could create a output of a JSON stream document including one JSON object per commit, with a template like this one: This template produces valid JSON that can then be read and processed by other tools, looks like this. Templates have vast possibilities that have not yet been touched on, and I encourage you to investigate and experiment yourself. Now let’s look at some revsets. The biggest source of revset aliases that I have seen online is from @thoughtpolice’s jjconfig gist, but I will consolidate across several different config files here to demonstrate some options. The first group of revsets roughly corresponds to “who made it”, and composes well with other revsets in the future. For example, it’s common to see a type alias, and a type alias to let the current user easily identify any commits that they were either author or committer on, even if they used multiple different email addresses. Another group uses description prefixes to identify commits that have some property, like WIP or “private”. It’s then possible to use these in other revsets to exclude these commits, or even to configure jj to refuse to push them. Thoughtpolice seems to have invented the idea of a , which is a group of commits on top of some parent: Building on top of the stack, it’s possible to construct a set of commits that are “open”, meaning any stack reachable from the current commit or other commits authored by the user. By setting the stack value to 1, nothing from trunk or other remote commits is included, so every open commit is mutable, and could be changed or pushed. Finally, building on top of the open revset, it’s possible to define a “ready” revset that is every open change that isn’t a child of wip or private change: It’s also possible to create a revset of “interesting” commits by using the opposite kind of logic, as in this chain of revsets composed by . You take remote commits and tags, then subtract those from our own commits, and then show anything that is either local-only, tracking the remote, or close to the current commit. Now let’s talk about jj commands. You probably think I mean creating jj commands by writing our own aliases, but I don’t! That’s the next section. This section is about the jj commands that it took me weeks or months to realize existed, and understand how powerful they are. First up: . When I first read about absorb, I thought it was the exact inverse of squash, allowing you to choose a diff that you would bring into the current commit rather than eject out of the current commit. That is wildly wrong, and so I want to make sure that no one else falls victim to this misconception. The absorb command iterates over every diff in the current commit, finds the previous commit that changed those lines, and squashes just that section of the diff back to that commit. So if you make changes in four places, impacting four previous commits, you can to squash all four sections back into all four commits with no further input whatsoever. Then, . If you’re taking advantage of jj’s amazing ability to not need branches, and just making commits and squashing bits around as needed until you have each diff combined into one change per thing you need to submit… you can break out the entire chain of separate changes into one commit on top of trunk for each one by just running and letting jj do all the work for you. Last command, and most recent one: . You can use fix to run a linter or formatter on every commit in your history before you push, making sure both that you won’t have any failures and that you won’t have any conflicts if you try to reorder any of the commits later. To configure the fix command, add a tool and a glob in your config file, like this: Now you can just and know that all of your commits are possible to reorder without causing linter fix conflicts. It’s great. Okay. Now we can talk about command aliases. First up, the venerable . In the simplest possible form, it takes the closest bookmark, and moves that bookmark to , the parent of the current commit. What if you want it to be smarter, though? It could find the closest bookmark, and then move it to the closest pushable commit, whether that commit was , or , or . For that, you can create a revset for , and then tug from the closest bookmark to the closest pushable, like this: Now your bookmark jumps up to the change that you can actually push, by excluding immutable, empty, or descriptionless commits. What if you wanted to allow tug to take arguments, for those times when two bookmarks are on the same change, or when you actually want to tug a different bookmark than the closest one? That’s also pretty easy, by adding a second variant of the tug command that takes an argument: This version of tug works just like the previous one if no argument is given. But if you do pass an argument, it will move the bookmark with the name that you passed instead of the closest one. How about if you’ve just pushed to GitHub, and you want to create a pull request from that pushed bookmark? The command isn’t smart enough to figure that out automatically, but you can tell it which bookmark to use: Just grab the list of bookmarks attached to the closest bookmark, take the first one, pass it to , and you’re all set. What if you just want single commands that let you work against a git remote, with defaults tuned for automatic tugging, pushing, and tracking? I’ve also got you covered. Use to colocate jj into this git repo, and then track any branches from upstream, like you would get from a git clone. Then, you can to find the closest bookmark to , do a git fetch, rebase your current local commits on top of whatever just got pulled, and then show your new stack. When you’re done, just . This push handles looking for a huggable bookmark, tugging it, doing a git push, and making sure that you’re tracking the origin copy of whatever you just pushed, in case you created a new branch. Last, but definitely most stupid, I want to show off a few combo tricks that manage to deliver some things I think are genuinely useful, but in a sort of cursed way. First, we have counting commits. In git, you can pass an option to log that simply returns a number rather than a log output. Since jj doesn’t have anything like that, I was forced to build my own when I wanted my shell prompt to show how many commits beyond trunk I had committed locally. In the end, I landed on a template consisting of a single character per commit, which I then counted with . That’s the best anyone on GitHub could come up with, too . See? I warned you it was stupid. Next, via on Discord, I present: except for the closest three commits it also shows at the same time. Simply create a new template that copies the regular log template, while inserting a single conditional line that adds if the current commit is inside your new revset that covers the newest 3 commits. Easy. And now you know how to create the alias I promised to explain earlier. Last, but definitely most stupid, I have ported my previous melding of and over to , as the subcommand , which I alias to because it’s inspired by , the shell cd fuzzy matcher with the command . This means you can to see a list of local bookmarks, or to see a list of all bookmarks including remote branches. Then, you can to do a fuzzy match on , and execute . Jump to work on top of any named commit trivially by typing a few characters from its name. I would love to also talk about all the stupid shell prompt tricks that I was forced to develop while setting up a zsh prompt that includes lots of useful jj information without slowing down prompt rendering, but I’m already out of time. Instead, I will refer you to my blog post about a jj prompt for powerlevel10k , and you can spend another 30 minutes going down that rabbit hole whenever you want. Finally, I want to thank some people. Most of all, I want to thank everyone who has worked on creating jj, because it is so good. I also want to thank everyone who has posted their configurations online, inspiring this talk. All the people whose names I was able to find in my notes include @martinvonz, @thoughtpolice, @pksunkara, @scott2000, @avamsi, @simonmichael, and @sunshowers. If I missed you, I am very sorry, and I am still very grateful that you posted your configuration. Last, I need to thank @steveklabnik and @endsofthreads for being jj-pilled enough that I finally tried it out and ended up here as a result. Thank you so much, to all of you.

2 views