Hacker Newsnew | past | comments | ask | show | jobs | submit | ufo's commentslogin

How do you handle persistent state in your actions?

For my actions, the part that takes the longest to run is installing all the dependencies from scratch. I'd like to speed that up but I could never figure it out. All the options I could find for caching deps sounded so complicated.


> How do you handle persistent state in your actions?

You shouldn't. Besides caching that is.

> All the options I could find for caching deps sounded so complicated.

In reality, it's fairly simple, as long as you leverage content-hashing. First, take your lock file, compute the sha256sum. Then check if the cache has an artifact with that hash as the ID. If it's found, download and extract, those are your dependencies. If not, you run the installation of the dependencies, then archive the results, with the ID set to the hash.

It really isn't more to it. I'm sure there are helpers/sub-actions/whatever Microsoft calls it, for doing all of this with 1-3 lines or something.


The tricky bit for me was figuring out which cache to use, and how to use and test it locally. Do you use the proprietary github actions stuff? If the installation process inside the actions runner is different from what we use in the developer machines, now we have two sets of scripts and it's harder to test and debug...

> Do you use the proprietary github actions stuff?

If I can avoid it, no. Almost everything I can control is outside of the Microsoft ecosystem. But as a freelancer, I have to deal a bunch with GitHub and Microsoft anyways, so in many of those cases, yes.

Many times, I end up using https://github.com/actions/cache for the clients who already use Actions, and none of that runs in the local machines at all.

Typically I use a single Makefile/Justfile, that sometimes have most of the logic inside of it for running tests and what not, sometimes shell out to "proper" scripts.

But that's disconnected from the required "setup", so Make/Just doesn't actually download dependencies, that's outside of the responsibilities of whatever runs the test.

And also, with a lot of languages, it doesn't matter if you run an extra "npm install" over already existing node_modules/, it'll figure out what's missing/there already, so you could in theory still have "make test" do absolute everything locally, including installing dependencies (if you now wish this), and still do the whole "hash > find cache > extract > continue" thing before running "make test", and it'll skip the dependencies part if it's there already.


If you are able to use the large runners, custom images are a recent addition to what Github offers.

https://docs.github.com/en/actions/how-tos/manage-runners/la...


Depends on the build toolchain but usually you'd hash the dependency file and that hash is your cache key for a folder in which you keep your dependencies. You can also make a Docker image containing all your dependencies but usually downloading and spinning that up will take as long as installing the dependencies.

For caching you use GitHubs own cache action.


You don't.

For things like installing deps, you can use GitHub Actions or several third party runners have their own caching capabilities that are more mature than what GHA offers.


Not at all; the charge happens at the end of the proccess, after the article was reviewed and accepted for publication.

They charge that much because they can.


They charge a substantial premium for that service. The open access publication fees are typically hundreds or even thousands of dollars per article.

There are other platforms that can offer a similar service for much cheaper, but scientists incentivised to publish on established journals that have a higher impact metrics.


A big footgun with strncpy is that the output string may not be null terminated.


Yeah but fixed width strings don’t need null termination. You know exactly how long the string is. No need to find that null byte.


Until you pass them as a `char *` by accident and it eventually makes its way to some code that does expect null termination.

There’s languages where you can be quite confident your string will never need null termination… but C is not one of them.


You don’t do that by accident. Fixed-width strings are thoroughly outdated and unusual. Your mental model of them is very different from regular C strings.


Sadly, all the bug trackers are full of bugs relating to char*. So you very much do those by accident. And in C, fixed width strings are not in any way rare or unusual. Go to any c codebase you will find stuff like:

   char buf[12];
   sprintf(buf, "%s%s", this, that); // or
   strcat(buf, ...) // or
   strncpy(buf, ...) // and so on..


Thats only really a problem if this and that are coming from an external source and have not been truncated. I really don't see this as any more significant of a problem than all the many high level scripting languages where you can potentially inject code into a variable and interpret it.

There are certainly ways in which the c library could've been better (eg making strncpy handle the case where the source string is longer than n) but ultimately it will always need to operate under the assumption that the people using it are both competent and acting in good faith.


When you write such code your mental model is C strings, not fixed-width strings, the intended use case for strncpy.


The mental model doesn’t matter, it’s the compiler’s model that is going to bite you. If the compiler doesn’t reject it, it will happen eventually.


Good luck though remembering not to pass one to any function that does expect to find a null terminator.


Ignore the prefix and always treat strncpy() as a special binary data operation for an era where shaving bytes on storage was important. It's for copying into a struct with array fields or direct to an encoded block of memory. In that context you will never be dependent on the presence of NUL. The only safe usage with strings is to check for NUL on every use or wrap it. At that point you may as well switch to a new function with better semantics.


> an era where shaving bytes on storage was important

Fixed size strings don’t save bytes on storage tho, when the bank reserves 20 bytes for first name and you’re called Jon that’s 17 bytes doing fuckall.

What they do is make the entire record fixed size and give every field a fixed relative position so it’s very easy to access items, move record around, reuse allocations (or use static allocation), … cycles is what they save.


> Fixed size strings don’t save bytes on storage tho

I have seen plenty of fixed strings in the 8 to 20 byte range, not much, but often enough for a passable identifier. The memory management overhead for a simple dynamically allocated string is probably larger than that even on a 32 bit system.


That's not a problem with strncpy, right? Fixed width records are a thing of the past, and even then it was only used for on-disk storage.


Seriously. We have type systems and compilers that help us to not forget these things. It's not the 70s anymore!


I think what the other poster was trying to say is that the other AI chatbots would be more subtle and their bias would be harder to detect.


That was already the case in previous versions of Lua. You could assign to the loop variable but the assignment would be overwritten in the next loop iteration.

https://www.lua.org/manual/5.3/manual.html#3.3.5

The loop count was fixed at the start of the loop. One of the reasons for this was performance. For loops behave differently if the step count is positive or negative, and it's a bit faster to compute that once, before the loop, than to repeat it every iteration.


I wonder if there would be a way to piggyback on top of GCC's nested function extension. It does something a little bit similar, with the dynamically generated functions.


Indeed. Unification-based type inference doesn't work great when the type constraints are inequalities.


Languages with local type inference can sometimes omit type annotations from lambdas, if that lambda is being returned or passed as an argument to another function. In those situations we know what the expected type of the argument should be and can omit it.


Yeah, that's true and that's a good convenience even if it's not full inference. In the case of Scala, the parameter types may often be required, but at least the return type can be omitted, so there's that.


In Haskell, type error messages are always like "types A and B should be equal, but they are not". The problem is that, without type annotations, the compiler cannot know if it is A or B that is wrong, which can result in confusing error messages.

For example, suppose that you have a bug in the body of a function, but did not provide a type annotation for it. The function might still compile but not with the type you want. The compiler will only notice something is amiss when you try to call the function and it turns out that the function's inferred type doesn't fit the call site.

Basically, global type inference in the absence of type annotations means that changes in one part of the file can affect inferred types very far away. In practice it's best to use type annotations to limit inference to small sections, so that type errors are reported close to what caused them.


Since Haskell is statically compiled, wouldn't it not compile at all?


That's all happening at compile time. I only meant to say that the function's inferred type isn't what you'd expect.


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: