Simple Timing in plpgsql
June 18, 2025
There are numerous ways to get the current date/time in Postgres (see https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT). However, to do some simple timing of operations, you want clock_timestamp(). Here's an example in plpgsql:
do $body$
declare
op_start time;
op_end time;
begin
op_start = (select clock_timestamp);
for i in 1..1000000 loop
perform (select 1 + 1);
end loop;
op_end = (select clock_timestamp);
raise info '%', (op_end - op_start);
end;
$body$
Name it "timing.sql" and run with psql -f timing.sql.
Share colorized diffs
June 6, 2025
-
Install difftastic:
cargo install difftastic -
Install ansifilter:
sudo apt install ansifilter -
diff two files and turn the output into html:
difft --color always a.txt b.txt | ansifilter --html > colorized_diff.html. -
Share the file.
Make curl use stderr properly
May 30, 2025
daybook, shell, curl | permalink
At work, there was a cronjob that used curl, and it was sending an email every time it ran, which in theory it shouldn't have because I was redirecting the stdout from the curl call to /dev/null with > /dev/null. But it did, so after the internet failed to deliver me a good answer, I took a peek into man curl.
What was happening is that curl reports the progress of a request to stderr. Why? I don't know. Stderr is weird. I wanted only actual errors to be emailed to me. So I turned off progress reporting with --no-progress-meter. But if I tried a url that returned a 404, that was also getting binned into /dev/null, e.g. curl --no-progress-meter https://kdwarn.net/api/no-api-here > /dev/null returned nothing, meaning there was nothing getting reported to stderr. Then I found the --fail option, which makes curl report error 22 (and the http error) to stderr, so now I get only the error reported to stderr with curl --fail --no-progress-meter https://kdwarn.net/api/no-api-here > /dev/null. I tested binning stderr to make sure I wasn't insane (because the curl documentation on this isn't very clear), and in fact curl --fail --no-progress-meter https://kdwarn.net/api/no-api-here 2> /dev/null prints no output.
Postgres Domain v. Constraint
May 28, 2025
I'm working on cleaning some data, and I'm identifying the bad data by trying to enter it into tables with constraints and domains. I have not used domains until today. Postgres's glossary defines a domain as "A user-defined data type that is based on another underlying data type. It acts the same as the underlying type except for possibly restricting the set of allowed values" and then points you to section 8.18 for more information.
I've found three situations where domains are more useful than constraints:
- Since they are types, they can be defined once and used in multiple places. You cannot do this with constraints. If you want to use the same constraint on multiple columns, you have to write out the exact same text on each of them.
- When a value violates the constraint on a domain - at least using Postgres's COPY function - it tells you the specific value (and only that value) that violated it. If a constraint on a regular type is violated, the error is less specific and prints out the entire row (or part of it) where the violation occurred. If there are many columns in the row, it makes it difficult to identify which value caused the issue.
- Domains can be redefined. In my work, I first defined a domain with a constraint that was supposed to match the data. If there was a violation, I redefined the domain with a less restrictive constraint until there was no violation. My work in identifying issues is thus shown in the code. You cannot do this with constraints, at least not in the same way. Constraints don't replace each other; you'd have to erase or comment out a more restrictive one to get to a less restrictive one.
rsync with --update
February 8, 2025
Calling rsync with the --update flag will "skip files that are newer on the receiver", according to the man page. That is, it will only copy the file over if it is newer than the destination file. scp can't do this, and so it's a nice convenience even if rsync needs to be installed. I discovered this as I am trying to rely more on just and the shell than Ansible, which is good but also kind of gets you out of practice.
at
December 17, 2024
Want to run some program, but perhaps not right at the moment? at to the rescue.
See:
jujutsu
November 10, 2024
EDIT, 2025-12-25: I've gone back to straight git. I think jj will probably be a passing fad that will never have the full functionality as git, nor the documentation/support/community.
This is probably going to be something of an evergreen post, updated as necessary. Unless I forget it exists.
I heard about jujutsu sometime within the last year. I have to admit my initial thought was something along the lines of "why make something else when there's git?" but then I saw that Steve Klabnik (co-author of the Rust book) is writing a tutorial on it. So, as I said on Mastodon, I guess I'm going to have to check it out. (EDIT, 2025-07-23: "Jujustsu for busy devs" is a good intro.)
And so I've been using it on personal projects for the last couple weeks and I quite like it.
Anyway, I wanted to note some things I'm getting used to/figuring out.
Workflow:
- I'm mostly using the "edit" workflow Steve describes, but not quite as he describes it.
- The happy path: When I'm done with some set of changes, I'll update the description if necessary. I'm finding that writing the description before doing any work is helpful in thinking about it before doing it, so that's why I say "if necessary". But half the time it is, and if so it's done with
jj describe(to open an editor to write the subject and optionally body of the message) or withjj describe -m <message>to write it on the command line. Push to remote, possibly. Then runjj newto start a new revision. I don't often use the-m <message>option withjj new, as I'm just trying to finish up the one I've been working on and leave things in a fresh state, but you can do that. (There's also one command that will replacedescribeand thennew:jj commit, orjj cifor short.) - Aw fuck I forgot something:
- if I've already started a new revision with
jj new, just make whatever changes are necessary and then runjj squash. This will push the changes in the working copy into the previous revision, and the working copy will be empty. If you already added a description, an editor will pop up allow you to edit the commit, very much like in rebasing ingit. - if not, run
jj newand thenjj squash. If you just runjj squashwithout starting a new revision, you'll be pushing all your changes both now and what you previously did into the the revision before the one you're attempting to add to. - if you already pushed to a remote, you can do it again, just specify the revision:
jj git push -r <rev> --remote <remote-name>. There's no need (or option) for--force. Just push it. - it's also easy to do this with only some of the changes. I'll add that later.
- if I've already started a new revision with
Pushing to remote: If finished with a revision and want to push it somewhere, don't start a new one (because you can just work on the working copy without having to specify a revision). Update the bookmark with jj bookmark set main to move the main bookmark/branch to the working copy. Then do jj git push --remote <remote-name> to push it there. Then a new revision, to start further work, can be started with jj new.
Various things:
@is the working copy and@-is used for the revision before the working copy. You can pass in the revision on most commands --r <rev>. So-r @-is the one before the working. I'm not sure how far out it goes, but tacking on additional-will go one further.jj showwill show commit description (the full one, not just the subject likejj logdoes) and revision changes. Handy asjj show @-to see previous one from working copy.jj undois pretty great. I fucked up some things and it made them go away.jj abandonis both useful and good naming. Wrote some code that's actually not worth saving?jj abandon.- to use jujutsu out of the gate with a new Rust project (rather than "colocate" it with git), pass
--vcs=nonetocargo newand then runjj git initin the project's directory. - the "builtin_log_compact_full_description" template is the one that feels most like what I expect from
git log. So I've added an alias for it, to "v", which means it can be called withjj v. The new part of my ~/.config/jj/config.toml file looks like this:[aliases] v = ["log", "-T", "builtin_log_compact_full_description"] # v for verbose - Start a branch awhile ago and then just kind of forget about it? And then you're like 30 commits from where you diverged but you want to pick up the old branch again? I'd have to probably read several blog posts and forum threads for git, but for jujutsu it took me just
jj helpand a couple minutes to figure out that the answer is justjj rebase -r [revision] -d @and everything seems ... like I wanted it to be? (-bor-smay be a better choice than-r.jj help rebaseprovides clear explanation and graphs to make the decision easy.)
It's Not REST
August 28, 2024
daybook, http, rpc | permalink
I've been building "REST" APIs for years that are not actually REST. (See "How Did REST Come To Mean The Opposite of REST?" as a starting point). I've kind of known this for a while and I've always meant to get back to and try to fully understand HATEOAS and other similar concepts. But for whatever reason - mainly, the things I built did what they were supposed to do (deliver data) - I never have. And I'm completely fine with that. REST seems overly complex and rigid. I don't need the reponse to be "self-describing" or contain the possible actions from there, and I don't really want to read yet another thing about the nuances of hypertext or hypermedia. I just need the thing to provide data. I think external, user-facing and higher-level documentation is useful.
So, what is it instead? I think HTTP-based RPC. And that's all that's really needed.
Code for Yourself
August 25, 2024
Writers have "write for yourself, not your audience" and probably a bunch of variations and maybe there's the same thing in other creative endeavors. I want to try to apply "code for yourself" when thinking about new side projects to work on. Sometimes I'll get into a "what would be a useful project?" mode of thinking when I want to do something new, and it's not productive because I just end of wasting time trying to thinking of possibilties and write nothing. I'll end up having more fun and writing more code when I just scratch my own itch.
Don't Use Serial
August 12, 2024
On one episode of Postgres.fm, the hosts discuss some "Don't Do This" lists, one of which is hosted by Postgres itself. I hadn't heard of this before, and so checked it out and discovered that using the serial type (as an auto-generated number, typically for primary keys) is not recommended, unless you're supporting a version older than Postgres 10. I didn't start using Postgres until version 12, so I'm definitely not supporting any database on version 10.
Since reading that page some time ago, I've had replacing my usage of serial in my veganbot database on my task list, and didn't get to it until today because I thought it was going to be a bit of a pain in the ass. Turns out, it was super simple! For example, turn this:
(
id serial PRIMARY KEY,
);
into this:
(
id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
);
See the Postgres documentation here and here for more information.
Helix * and gr
June 14, 2024
* and gr
I learned two useful things in Helix today:
- You can use
*thennto do a search for the current selection. Say you're in the middle of some word. Usemiwto select that word,*to put it into the default register (/), and thennto find the next occurrence of it. gris "go to reference". It pulls up a list of all the occurrences of whatever you're referring to. So, as in above you can select a word withmiwand then typegrto open the picker where you can navigate through the list, previewing it. Pressing enter on one will take you to that position in the code.
Diffing
April 15, 2024
There are (at least) two ways to view the diff of both an untracked file and tracked-but-changed/not-staged files in git.
-
Add the untracked file, run
git diff HEAD. If it turns out you don't want that file added, just follow the command shown on withgit status:git restore --staged <file>. -
Use the
-N(--intent-to-add) flag ingit add, which "record[s] only the fact that the path will be added later" (fromgit add --help). As the help continues, "This is useful for, among other things, showing the unstaged content of such files withgit diffand committing them withgit commit -a."
Not sure which way I'll settle on, but I'm glad I finally looked into it. I generally do the first version, but only because I didn't know any other way. And before I end up doing that, I usually think there is some simple flag I'm missing with git diff that'll do what I want, before realizing - once again - that there's not.
Associated Functions
April 8, 2024
Associated functions in traits can be very useful.
git show
April 1, 2024
Occasionally I'm looking at the current code for a project, and want to see some previous iteration of it, often because I want to bring back some part of it. I'll typically go to the code in the software forge, find the commit I think I'm looking for, click the link to view the source at the point in the commit history, and then navigate to the file.
There's an easier way:
git logto find the commit- copy first bunch of characters (6) of the hash for the commit you want to view code from
git show [hash]:relative/path/to/file.
That goes to stdout. Pipe it to an editor, e.g.: git show 3a53e76:src/main.rs | hx. I haven't yet figured out how to set the language of the file when opening with Helix in this way, but that can be solved by setting it once the file is open with :set-language rust.
just
February 14, 2024
I've never used Make before (as someone creating the Makefile), but I just tried out just and found it to be fairly intuitive/well documented. There were a couple things I struggled with, but nothing too serious: composing recipes has some nuance that I didn't pick up on at first, silencing output of commands/recipes I got to work how I wanted but mostly by luck. The end result is that the next time the work needs to be done, using just will be a far better experience than the step-by-step directions it is replacing.
I feel like Just is a good sibling tool to Ansible. Ansible would have been overkill for what I did with Just, as well as harder to set up for a new person. just is a simple binary, no minimal boilerplate to speak of, simple syntax. I'm looking forward to seeing where else I can use it. I expect that the next justfile I create will come along much faster than this first one did.
--show-output with cargo test
February 2, 2024
daybook, rust, testing | permalink
cargo test does not display any standard output from successful tests, just that the results were successful. To get around this and see the output of dbg! or println! statements, I usually make a false assertion so that a test fails and these get shown. However, an easier way is to use the --show-output flag: cargo test -- --show-output, as mentioned in the Book. The cargo test section of the Cargo Book does not list this (for some reason unclear to me), but instead cargo test -- --nocapture, which has the same effect. I'm pretty sure I've come across --nocapture before, but I think --show-output will be easier to remember. Well, maybe. I've definitely read the book and forgot about --show-output. Noting here should help.
Capturing Catchall Test Value
January 18, 2024
I seem to have forgotten that when pattern matching in Rust, you can capture the value of a catchall test (left side of arm). I typically just use _, which will match anything, but isn't captured for further use. By creating a variable instead, it can be used on the right side of the arm, for whatever purpose. Realized this from watching Zoo's Rust Club: Error-handling.
Science, Engineering, Construction, Gardening?
January 8, 2024
I've just been reviewing notes I had taken two years ago, within a month of each other, on two different sources. I'm not sure if I thought about it much then, if at all, but I thought they were interesting reading together today.
The first is from Brian Harvey's first lecture of Computer Science 61A UC Berkeley in 2011. In it, he says, "Computer science is a bad name for what we do. It's not science and it's not about computers. Scientists ask questions about how the world works. For the most part, what we do is more like engineering - we build stuff. That's not entirely true, because of theoretical computer science, but mostly it's engineering. And it's not about computers - that's electrical engineering. We build software. Our field should be called software engineering."
A month prior, I had quoted a sentence from David Thomas and Andrew Hunt's The Pragmatic Programmer, noting that it was a good point: "Unfortunately, the most common metaphor for software development is building construction. Rather than construction, software is more like gardening — it is more organic than concrete."
At first the two quotes seem to be in opposition, but I think the broader context from Thomas and Hunt shows they aren't that different, but they are starting from different positions:
Unfortunately, the most common metaphor for software development is building construction. Bertrand Meyer’s classic work Object-Oriented Software Construction uses the term “Software Construction,” and even your humble authors edited the Software Construction column for IEEE Software in the early 2000s.
But using construction as the guiding metaphor implies the following steps:
- An architect draws up blueprints.
- Contractors dig the foundation, build the superstructure, wire and plumb, and apply finishing touches.
- The tenants move in and live happily ever after, calling building maintenance to fix any problems.
Well, software doesn’t quite work that way. Rather than construction, software is more like gardening — it is more organic than concrete. You plant many things in a garden according to an initial plan and conditions. Some thrive, others are destined to end up as compost. You may move plantings relative to each other to take advantage of the interplay of light and shadow, wind and rain. Overgrown plants get split or pruned, and colors that clash may get moved to more aesthetically pleasing locations. You pull weeds, and you fertilize plantings that are in need of some extra help. You constantly monitor the health of the garden, and make adjustments (to the soil, the plants, the layout) as needed.
Business people are comfortable with the metaphor of building construction: it is more scientific than gardening, it’s repeatable, there’s a rigid reporting hierarchy for management, and so on. But we’re not building skyscrapers — we aren’t as constrained by the boundaries of physics and the real world.
The gardening metaphor is much closer to the realities of software development. Perhaps a certain routine has grown too large, or is trying to accomplish too much — it needs to be split into two. Things that don’t work out as planned need to be weeded or pruned.
I don't know that Harvey would disagree with this much; it's an extension of what he said, in a way.
'static for error types
January 6, 2024
daybook, rust, error-handling | permalink
A suggestion from Jon Gjengset in the chapter on error handling in Rust for Rustaceans:
Finally, where possible, your error type should be
'static. The most immediate benefit of this is that it allows the caller to more easily propagate your error up the call stack without running into lifetime issues. It also enables your error type to be used more easily with type-erased error types, as we’ll see shortly.
Variable Number of SQL Clause Predicates
January 4, 2024
daybook, rust, sql | permalink
From reviewing my notes on sqlx from a couple years ago, it seems I came up with a way to handle a variable number of SQL WHERE clause predicates, since there's no built-in way to do this. Looking at it now, it seems a little convoluted. It seems I also got a couple of suggestions from the maintainer, which I unfortunately only wrote down but didn't actually use, because they would have been a good foundation to start from, and are similar to what I did this time around. Anyway, I'm glad I forgot to check my notes before starting this little endeavor, because I think my old method would have thrown me off track for a bit. This time around, my solution is more robust, capable of handling any number of predicates. Here it is in an API I'm building with Dropshot.
/// Get union membership by union.
async
cal
December 5, 2023
cal is a useful command; I've often used it to get a visualization of where we're at in the week or month. Since I'm often in the terminal or a couple keystrokes away from one, it's the quickest way to do this. However, just cal doesn't highlight the day, which would be useful. A quick web search revealed that its partner program ncal, does this with ncal -b.
Instrumentation
November 8, 2023
daybook, instrumentation | permalink
I've heard the term "instrumentation" used - I think first and probably most on the Oxide and Friends podcast (which makes sense now that I've learned a little bit about what it is) - but have never understood what is meant by it. Well, I spent five minutes and did a web search to find out, prompted by a blog post in my RSS reader using it. It's basically a system for discovering what's going on in software and getting a better view of state/performance/errors. So I think DTrace could be considered in this realm - and hence why I've heard about it on Oxide and Friends. Originally I thought it was more about deployment or infrastructure creation.
systemctl cat
August 18, 2023
systemctl cat <service-name> will print the path and contents of the unit file for a service. I sometimes know the filename and so it's easy enough to just cat the path, but sometimes I don't and I guess several times until I give up and look it up. Just using this by default would probably end up saving some time/make things smoother.
Broken Pipe
May 11, 2023
I went down a little rabbit hole after getting a "broken pipe" error when trying to pipe the output of ts-cli to another program (head/tail). The short version of what I've learned is that repeatedly calling println! is not very performant and can cause this error. So what I'm going to do is stick all output into a String, locking a BufWriter on io::stdout, and writing to it with writeln! (and calling .ok() on it to ignore any errors). (I also tried using writeln! every time something needed to be printed, but this slows this down significantly. So both are needed - a single string and the lock/writeln! once. This is now done on the Summary command and pipes are working.
let if
October 13, 2022
I've used the let if pattern before, but not enough to remember how it works, so had to refresh myself on it. It's very useful. Here's a sample from tp-updater:
let updated_indicators = if self.updated_indicators.is_empty else ;
Looping with modulus
July 27, 2022
daybook, rust, embedded | permalink
You can use a modulus operator to loop around the end of a range quite efficiently, which would come in handy in an infinite loop. I discovered this in Section 5.6 of the embedded Rust Discovery book. It was used there for turning LEDs on and off; here is just printing out the vars:
Use format! to turn chars to string
June 30, 2022
While working on a Rust exercise, at first I did some convoluted way of turning two chars into a string. But it's quite simple with the format! macro: format!("{ch}{next_ch}").
Generic and traits
April 18, 2022
I finally have a decent understanding of generics and traits. Something clicked when I was reading about them in Ch 2 of Programming Rust. I imagine that I came across similar descriptions of these before, but it really helped this time around having some more experience with the language. I don't think I knew that generic types were called "type parameters" when you put them in function signatures. So, just having a term for that helps. That's fn<T>(t: T) {}. And you can specify not just any type, but a type that implements a specific trait: fn<T: FromStr>(t: T) {}, which can be read as "For any type that implements the FromStr trait." When writing this up in my notes, I also came across returning a type that implements at trait, which makes more sense now, although I just came up with a few questions to dig deeper into the whole subject that I need to look into sometime.
fold()
February 20, 2022
I think I finally get fold(). In Rust at least, this is a method on an iterator. Its form is:
.fold;
1..=4 is a range, so this will iterate from 1 to 4.
The 1 immediately following fold( is the initial value. So this could be anything, and will typically come from whatever you're iterating over. The next part - |acc, x| acc + x is the closure that .fold() takes - this consists of two parameters (the "accumulator", acc, and current element of the iteration, x) and the operation of the closure (acc + x). So with each iteration, the operation is performed, and the acc parameter holds the result, which then gets used in the next iteration.
The above example is a factorial, and so multiples every number for 1 to 4 (inclusive) together, resulting in 24.
Client Interface First
January 19, 2022
Interesting bit from the Rust book, Ch. 20:
When you’re trying to design code, writing the client interface first can help guide your design. Write the API of the code so it’s structured in the way you want to call it; then implement the functionality within that structure rather than implementing the functionality and then designing the public API.
Similar to how we used test-driven development in the project in Chapter 12, we’ll use compiler-driven development here. We’ll write the code that calls the functions we want, and then we’ll look at errors from the compiler to determine what we should change next to get the code to work.
Transforming programming
January 17, 2022
"Transforming programming" is the title of topic 30 of The Pragmatic Programmer (though a better title would probably be "transformational programming".) The gist is "we need to get back to thinking of programs as being something that transforms inputs from outputs". Uses example of Unix philosophy and piping. This makes a lot of sense to me, and in Rust it is essentially how iterators work - you keep chaining methods onto the iterator to do some work on each element in the collection, and then pass the outputs to the next method. This section also mentioned an and_then function, which Rust has, though they weren't talking about it in a Rust context.
Refactoring in Rust
January 9, 2022
I posted about broken-links to r/commandline the other day, and someone suggested that I use a HEAD rather than GET request to check link validity. A fair point, although what it made me realize was that I was making a GET request to each URL twice, and I could eliminate the second one if I just grabbed the html the first time and passed that along. It took about an hour and a half, and it seems to have improved performance by about 25% (depending on mix of URLs checked). So I published another version of it - 0.2.1. The refactoring wasn't the simplest thing to do, but I found that Rust's type system and the compiler errors made it pretty smooth and almost guided me in the right direction.
Iterators and collect()
January 8, 2022
Figured out rustlings/iterators2. Now the solutions seem obvious. I think what I was doing wrong was thinking that .collect() created a vector, whereas it creates whatever kind of collection you tell it to or that it infers. In this exercise, there were two functions requiring you to have a different result type, but the code was the same for both. I was making it much harder than it was, not taking advantage of .collect() and Rust's ability to infer the type.
Before solving the exercise correctly, I spent about two hours reading through the documentation on iterators as well as part of "A Journey into Iterators" by Ana Hobden. Both were very helpful. I discovered the .inspect() method in the Hobden piece, and it seems like it will really be useful in debugging/figuring out iterator chains.
Trait function signatures
January 7, 2022
I thought when you implemented a trait, the function had to have the exact same signature as the trait, but apparently you can (at least) change the parameters from being immutable to mutable. This is from rustlings/traits/traits2, the instructions of which said to implement AppendBar on a vector of strings, such that you appended "Bar" to it:
Braces
October 2, 2021
daybook, rust, bash | permalink
Rust and Bash have a very similar construct for iterating over ranges: x..y. Bash uses it like for i in {1..9} while Rust doesn't use the braces (though seems like parentheses may be used - but not required).
I spent some time reworking my bash note on parameter expansion (actually calling the value of a parameter by using the $ symbol), and think I have further strengthened my understanding of it, using doubles quotes in conjunction with it, and using braces in conjunction with it.
One important reason to use braces around a parameter's name (but after the $, like this: "${somevar}"), is that you can make a default value, either for immediate expansion or to assign the default value to the parameter. Immediate expansion (won't be used again if you reuse the parameter): ${somevar:-this}". Assignment (assigned here, will be used as value of the parameter in subsequent uses): "${somevar:=this}".
The Secret Life of Programs
September 21, 2021
I started reading the book, The Secret Life of Programs, Understand Computers - Craft Better Code, by Jonathan E. Steinhart. So far it seems like what I've been after in terms of a more general book on computer science and programming. And the author's got a sense of humor, which is nice. I think it has convinced me to really try to focus on the lower level stuff, much more so than the first couple of chapters of the first volume of Randall Hyde's Write Great Code series.
One thing I learned - or at least deepened my understanding of - is what "system programming" is. He describes it as: "systems programming is at the bottom of the software hierarchy. It's similar to infrastructure [...]. Being a good programmer always matters, but it matters more if you're a system programmer, because others rely on your infrastructure." Says you need to learn about application programming and computer hardware to be a system programmer.
There was also a decent discussion of the differences between coding, programming, engineering, and computer science.
Something that also resonated with me was this statement: "But the strange thing about computer programming is that unlike in medicine, in programming you can become a specialist without ever being a generalist." That's kind of where I'm at/heading (web development at the backend) unless I purposefully steer towards a more general level, which I want to do, and I think I'll be a better overall programmer for it.
Various items
September 14, 2021
daybook, bash, database, python, ansible | permalink
Bash: I think I already knew this, or at least had a general feeling that it was true, but now I read explicitly (in Miell's Learn Bash the Hard Way) that wildcards are not the same as regular expressions.
Web apps/postgres: connection pools could have a significant effect on performance when there's a fair amount of traffic.
Python: you can include on requirements file inside another. Useful for a base/production file and then a separate dev reqs file.
Ansible: set_fact allows you to create variables at runtime (most variables are created at beginning of play).
Background and Foreground
August 29, 2021
This is more something I relearned than learned for the first time.
Use Ctrl-Z to put a program you are running from a terminal into the background. You will be taken back to the terminal. Can do other stuff. Then use fg to bring the program you put into the background back into the foreground - back to the active program in the terminal. I think this will be useful in Vim when I don't need to have a dedicated terminal running in a window.
Host-dependent variables in Ansible
July 24, 2021
Ansible: I don't know if I learned this or re-learned this, but it's quite easy to set up host-dependent variables.
Here's one example of a file structure:
project/
|--inventories/
|--local/
| |--host_vars/
| |--[name of host].yaml <-- where the variables are for this host
|--production/
|--host_vars/
|--[name of host].yaml <-- where the variables are for this host
"group_vars" directories (at same level as "host_vars") would also work on the group level, if you have groups of hosts defined.
Various items
July 12, 2021
daybook, bash, python, ansible | permalink
Bash: easy way to test out conditions is to do it right on the command line, and then echo out the result of the last command, e.g.:
That will result in "1" being printed to the terminal, since == does string evaluation.
Python: pypandoc (wrapper around program pandoc) is super easy to use to convert strings and files from one format to another.
Also, not something I learned but a good practice I've developed: anytime I want to install some new program, I do it via the Ansible playbook I set up for my computer. This way, I get more experience with ansible, and I can reproduce my computer if it dies unexpectedly or I get a new one or even if I just switch distros.
Rebase and Merge
July 5, 2021
daybook, vcs, python | permalink
Rebasing in git is similar to merging. You do it when you want to combine the changes from one branch into another. (Although you can also do it interactively within one branch to squash commits, which I've done a fair amount since figuring out how that works.) I don't understand the differences between the two strategies, but at least rebase is a little less mysterious. I learned this from Git Gud, a Python program that interactively teaches you git by having you try to do various tasks with it. The explanations of how things work are great (which you can view with git gud explain). (Side note: I don't know how this CLI is able to use git gud as its command - shouldn't git be immediately invoked and tell you it doesn't know wtf the gud subcommand is?) Anyway, also learned from that program what ~ and ^ do (at the end of a branch name or hash). I've used HEAD~ and HEAD~2 or similar before (again, with interactive rebasing), but now I understand what's going on. (~ is for the previous commit, ~N is for the nth previous commit; ^ is similar but for parent branches.)