-
-
Notifications
You must be signed in to change notification settings - Fork 101
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How can I vendor dependencies (shards)? #611
Comments
It should be as simple as commiting By default, this folder is excluded via |
@straight-shoota are there any potential downsides to doing this? I know the |
I don't see any downsides. The lib folder should only contain checked-out worktrees, not the entire repo history. There's no If you see something else, that would be odd. More details, please =) |
Thanks! That should be enough info for me to use for vendoring. I'll re-open this issue if I have any troubles. |
👋 Hey again @straight-shoota! I had an additional question around vendoring dependencies. So to truly "vendor" a dependency, it would mean that everything related to that dependency lives within the repo. For example, if I had a So my question is, in addition to vendoring (checking into git) the Example: If I can safely check in the Is this a safe thing to do though (I'm still new to crystal )? I do my development on linux locally and my target systems that I deploy to are also linux in production. Would this potentially cause any conflict if I had a team member that was doing development on a MacOS machine? I want to effectively remove all occurrences of: Fetching https://github.com/kemalcr/kemal.git
Fetching https://github.com/stefanwille/crystal-redis.git
Fetching https://github.com/crystal-ameba/ameba.git
Fetching https://github.com/luislavena/radix.git
Fetching https://github.com/crystal-loot/exception_page.git
Fetching https://github.com/sija/backtracer.cr.git
Fetching https://github.com/ysbaddaden/pool.git
... and instead have them all be: Using radix (0.4.1)
Using backtracer (1.2.2)
Using exception_page (0.4.1)
Using kemal (1.5.0)
Using pool (0.2.4)
Using redis (2.9.1)
Using ameba (1.6.1)
Dependencies are satisfied
... when running My overall goal is to have builds be extremely repeatable, reliable, not rely on the network, not fail if a rogue shard maintainer deletes their repo, and work well in CI systems across multiple platforms. Thanks! 🙇 |
Yah I think this should technically be fine. The shards cache just contains bare repositories which should be Cross-Platform compatible. This of course depends on the resolver (such as git). All current resolvers should work that way. I don't see the point of this though. Why do you want to run a cached The only reason I could think of where this might perhaps be useful ist to seamlessly switch between different dependency versions during development. |
Even when I have everything in
Does this all make sense and would it be fairly safe to do @straight-shoota? Thanks again for helping out a new user here! 🙇 |
I don't get why you even want to run I figure it should be fine to do, but I don't see any sense in it. |
Locally, (on my dev box) this would be fine. But in CI systems (like GitHub Actions) I'm not committing my binaries so they need to be built during a deployment or for testing. For example, during my Vendoring is what I want here, because if the maintainers of So I'm trying to iron out the best way to fully bootstrap, install dependencies, test, lint, and then deploy my service with zero reliance on the network or 3rd party shards existing. If I vendor a dependency even a single time, I expect it to be usable in my project forever, regardless if the source repo even exists or not. This is following the pattern of "who owns my availability?" and the answer is always I'm trying to figure out how to build extremely reliable and fault tolerant systems and I feel so close with crystal but not fully there yet. |
If your primary concern around all this is the small chance your dependencies are deleted upstream, might just be easier to fork them and update your |
For reference, here is the repository that I am working on which will be a "base" repo template for many of my projects going forward and hopefully for others to use as well with crystal |
Yep that is absolutely an option. I know many large organizations do this anyways with mission critical dependencies to avoid them bringing down their build systems. However, that requires a lot of maintenance and certainly isn't for everyone. What I was ultimately hoping for with crystal was the phenomenal dependency management that comes with Ruby. I mean its just outstanding and works so well. You can vendor all of your Ruby Gems into the In my mind, this is the absolute dream of dependency management and I am really hoping I can nail down something similar here with crystal. ⭐ |
I understand the reasoning: Committing Shards' cache means committing a git mirror of each dependency, some may not even be needed anymore (oops). Committing the I suppose there could be a pair of commands (or one command with two subcommands) that would:
|
I was not asking for reasons for vendoring dependencies. I get that you want that. If I understand correctly, you rely on the postinstall hook of However, it's also pretty easy to explicitly build the binaries you need. Alternatively, it should be pretty straightforward to implement the behaviour of triggering the postinstall hooks as a script outside of #! /bin/bash
O=${O:-"$(pwd)/bin"}
for spec in lib/*/shard.yml; do
cd $(dirname $spec)
eval $(yq '.scripts.postinstall' shard.yml || continue)
yq -e '.executables[]' shard.yml | while read -r line; do ln -s bin/$line $O; done
done |
@ysbaddaden |
Pro: that would just work today (nice). Con: Upgrading dependencies becomes tedious. You can't just upgrade, try things out, and commit when it's working. You must destroy your lib folder, reinstall the shards with the above command, commit, then rebuild everything, update your gitignore if needed, ... You also have to copy (& maintain) the script to each and every project where you want to vendor your dependencies. Shards doesn't do it (yet?) but postinstall scripts may have to be built in a specific order, which a script won't be able to notice. Prior art
|
This seems like it might be overcomplicating. If the dependencies have proper ignore patterns setup and maybe a recipe to clean up the build directory, upgrading should be pretty simple. I wouldn't worry too much about postinstalls. Their purpose can easily be factored out of the shards install process. IMO it's a better approach to build dependencies in your build system anyway. |
@straight-shoota To me what you're proposing is overcomplicated solution to a problem that other PMs are already handling since yrs back... I cannot imagine telling people that they have to cook up a Makefile recipe along with the custom process just to vendor the dependencies. |
Yep so in the perfect work, I wouldn't really need to run |
The thing that I am still struggling with the most is that even when doing hacky things, writing scripts, setting custom shards env vars, etc... I still cannot get
This behavior can be easily replicated by cloning my crystal-base-template repo and running I know many other languages have vendoring totally dialed in and what I'm catching here is that its either like 95% implemented in crystal/shards or I'm doing something slightly wrong |
@straight-shoota I half agree with you, there shouldn't be much need to care about the
|
No you shouldn't need to setup a build system just for this purpose. But you'll probably need it anyway (more likely with increasing level of project complexity) at which point it's no extra step because it's already there.
In an ideal world, maybe. But if you build C libraries, it's quite likely you'll need a way to configure them, and make this available as part of your own build step. Maybe simple shim libs or the like can get away with a basic default configuration. |
@straight-shoota this might be asking a lot of you and if you just don't have the time I totally understand. I'm wondering if it would be possible for you to check out this repo -> https://github.com/GrantBirki/crystal-base-template and either open a PR demonstrating how I could tweak something to vendor shards, or comment on what I'm doing wrong here that is preventing a fully network isolated vendoring strategy. Once I finally nail down a solution (if possible), I would be more than willing to open a PR some where to document this type of vendoring strategy in detail to help out other folks! If larger organizations, enterprises, or critical services are looking to adopt crystal, having a rock-solid vendoring strategy is 100% going to be a requirement. I would be happy to help out with making sure this is a paved path for the next person to come along 🙇 |
@GrantBirki This template doesn't have many dependencies, with this scope it's quite simple. With this change, Of course this is only a blank template. If you want to make any use of it, you'll need some shard dependencies and at some point they might need some platform-specific build artifacts. What exactly to do about them depends on the individual circumstances.
|
@straight-shoota I have added a simple dependency and I'm now working on trying to get vendoring working properly. I still can't quite figure out where "cached shards" are installed. The command that I am running ( So when I run a subsequent
|
However for the setup which I'm suggesting, you don't need to vendor the entire shards cache. Just |
I understand the dependency you added and all of its transitive dependencies are pure Crystal sources. They produce no platform-specific artifacts. So no additional steps are necessary. You can simply install them via |
So if I don't commit the $ script/bootstrap
I: Resolving dependencies
E: Locked version 0.1.0+git.commit.eb37b8129dbcf3638[5](https://github.com/GrantBirki/crystal-base-template/actions/runs/8807823800/job/24175736167#step:5:6)50cf8fce705aa9533598fd for octokit was not found in git: https://github.com/grantbirki/octokit.cr.git. But even if I do commit the How in the heck do I get my CI system to just run reference PR where I'm working: GrantBirki/crystal-base-template#2 I feel like I'm going nuts lol 😂 |
So in CI it fails with or without the |
So there has got to be a third component somewhere. If I vendor Perhaps there is another hidden directory somewhere that |
Hm, yeah I tested this locally. There seems to be something going wrong in I staged
The repos for And yes running
Well done, shards. First delete the repos and than complain that they don't exist 🤦 |
Looks like |
Ah ha! This is great news! That is totally the issue I bet since the true git repos are being nuked then. I see the exact same thing locally and in CI with the Great find! ❤️ So what do we do now to fix this issue? |
I figured out how to do this and have a project here -> https://github.com/grantbirki/crystal-base-template The TL;DR of how it works, is that I create a tarball named This allows for shards to be vendored, and cached on a per-project basis. This works in a similar manner to What I have created in the repo linked above is certainly a work-in-progress but it helps guard against network problems, repos being deleted, etc. A shard could be deleted, and I could be in the middle of a desert without internet and I would still be able to run I'll go ahead and close this out, thanks! |
I believe this can still be implemented given the valid use-cases already stated above. This shouldn't conflict with Shards' supposed state as a dependency manager, it's still doing the same thing just for a different destination — I don't believe there's a real reason not to support this. |
A common design pattern for production applications is the ability to "vendor" dependencies. This means committing them to version control (git). You cannot have truly reliable builds without vendoring dependencies because a GitHub repository could be deleted at any point in time.
If a repo is deleted that contains a shard, then I can no longer pull that dependency to build an executable and thus my entire build process has fallen apart.
How can
shards
be used to vendor and check-in all dependencies?I know that once could do something like this:
but this doesn't do exactly what I'm after. It does a great job at caching locally, but when I run this in my GitHub Actions CI flow, it breaks.
I come from the land of Ruby where vendoring dependencies (Gem) is quite common and it is done so with ease through
bundler
. Here is a dead simple Ruby project that is open source where I vendor my dependencies with bundler.I'm hoping the same can be accomplished with Crystal and Shards! 🙇
The text was updated successfully, but these errors were encountered: