Vendoring Golang Shared Repository - go

Trying to move to the officially supported Golang vendoring solution from legacy Godeps workflow.
Scenario:
Repo A===
\
========> Repo C (shared library code)
/
Repo B===
What is the best workflow I can choose for a mid-size (roughly 5-10 member) team of engineers to vendor Repo C for both Repo A and Repo B? Engineers of varying abilities, most of which probably shouldn't need to know the details of this at all?
I'm currently using govendor for this. I'd prefer not to switch but would if there is a tool that provides a better workflow.
This needs to integrate with a CI server running the builds. I can think of 3 scenarios:
Vendor Repo C into A & B:
Pros:
Reproducible Builds
Easy integration with CI
Cons:
Manual and Error Prone - Can easily vendor incorrect code
Engineers need decent knowledge of vendor tool and methodology
Symlink Repo C trunk branch into vendor folders of A & B:
Pros:
Engineers need no knowledge of vendor tool
Low Developer Maintenance
Cons:
Builds not (easily) reproducible
Possibility of including code in build that shouldn't be released
Less Flexible (Repo A and Repo B can't have differing versions of C)
Include Repo C as a git submodule or subtree in Repo A and Repo B (either utilizing vendor or not):
Pros:
Engineers need no knowledge of vendor tool
Easy Setup
Less Maintenance
Reproducible Builds
Cons:
Having to use git submodule or subtree
Finding surprisingly little about this question on the internet. Is there some idiomatic way of doing this? I'm sure there are other ways of doing this; what am I missing?

i suggest to use a manifest based approach with version constraint.
Project A == Manifest
|- Repo A#~1.0.1
|- Repo B#~1.0.1
Repo A == Manifest
|- Repo C#~1.0.1
Repo B == Manifest
|- Repo C#~1.0.5
Repo C == Manifest empty
Which will resolve into
Project A == Resolved Manifest
|- Repo A#1.0.1
|- Repo B#1.0.1
|- Repo C#1.0.5
where ~1.0.1 means >=1.0.1 <1.1.0.
As you see B and A dependency to C are independent, yet within project they are resolved correctly.
In the event A and B would define incompatible dependency to C, an error should occur as the project should not be build-able.
You may prefer to use caret ^ rather than tilde ~, ^1.0.1 -> >=1.0.1 < 2.0.0.
Note that you are not forced to use those 'helpers' such tilde and caret, you may define explicit version range.
You shall decide which constraint to apply given the level of confidence you give to the remote author to correctly upgrade its version number.
Finally,you can use glide to solve that for you.
Starting with Repo C, assuming you already tagged the repos, run glide init, git commit -am 'glide init', git push
Repo A, glide init, glide get git#repo.com/repoc, git commit -am 'glide init', git push
Repo B, glide init, glide get git#repo.com/repoc, git commit -am 'glide init', git push
Finally, Project A, glide init, glide get git#repo.com/repoa, glide get git#repo.com/repob, git commit -am 'glide init', git push
To re install the project, glide install, go build.
Nothing prevent you to tarball ProjectA with its vendor folder, in order to skip the glide install command when you execute the remote installation.
But you normally don t want to commit the vendor folder for a development environment. You d usually add vendor/ to your .gitignore file and run glide install or glide update.
Expect some difficulties in the begin, passed that step, things will work.
Once you jumped to that workflow, note that you ll have to bump every changes on your repos.
That is bloatware when you work both project A and repo B to reach a viable change, so in that case, rather than vendoring repoB into project A (you can leave the manifest definition, but get ride of the repoB folder into vendor/), install repoB as a go module with the go get command.
Doing so the changes are taken in effect immediately on re-build. Once the change set is completed, browse into each repos and bump them appropriately.
Finally you may want to use a version bumper to help you to get it done quick and fast, it happens i did one for my personal usage.
hope this helps.

Related

How to use git in vendor folder of fork?

I always use composer packages in Laravel but I never changed one. This is my first time and I don't want to do it incorrect.
I need to use and change a packages foo/bar. Everything that follows now is just guessed:
I forked the repo
I created a develop branch
I added a vcs to my composer.json
"require": {
//...
"foo/bar": "dev-develop",
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/thisisme/bar"
}
],
composer update
Now I have the thisisme/bar fork in my vendor folder in foo.
So far so good. Now I can use my own fork.
But currently, as I don't know what is good practice to modify the repo, I cloned the repo to a completely different location. Then I push my changes there and run composer update in my project to get the changes. But this is a pain.
Do I need to have a sub git in my project in vendor/foo with
git remote add origin https://github.com/thisisme/bar.git. Because "git in git" feels wrong and finally is not really working as git commands seem to interact with the "parent git".
While VonCs answer is correct regarding git, I'm not certainly sure that git submodule support is well aligned with composer(1) vendor dir for packages from a VCS repository. At least I have not experimented much with it and when I use a composer configuration with a VCS git repository, I normally don't need that1.
While composer(1) has support for git for vendor packages, it is on repository level, that is, you can have your own repository for your package (as you have configured it shown in your question) and then composer takes care of updating (or giving a warnings about local changes).
composer(1) supports this with its own remote for the packages (non-bare) clone (in the source install, read on).
So yes, what you describe ("But this is a pain."), is as long as you don't use it to your benefit. While you develop your (cloned) package, you don't need to run composer update all the time.
.git
composer.json
vendor/foo/bar/.git
A Composer project with two Git repositories
This is why IMHO "git in git" must not feel wrong. Similar to git sub-modules, git supports this very well. By default it even keeps track in the parent project of the current revision (changes) of the sub-project but without having the information of the remote - as it is local (gitlink).
You won't see this thought as within the tree, the gitlink would be at vendor/foo/bar and commonly (& given that) vendor is git ignored, no version tracking in the main project for vendor/foo/bar/.git - but there in the sub-project.
This is not a problem as Composer manages that git sub-project for you (the initial clone and further checkouts) in terms of your main project.
And git realizes it is a different project.
You should be able to cd into the package directory within the vendor folder (vendor/foo/bar) and configure your remote(s) there. You can then work within that project and git(1) will work there and not within the parent repository.
To have this work with composer(1) it is important that you configure composer to prefer the source install variant for that repository. This is the preferred-install option and you can configure it for your repository specifically.
{
"config": {
"preferred-install": {
"foo/bar": "source"
}
}
}
From the wording in your question, I assume that you have not yet configured it.
And this is somewhat important as only with the source install, there will be a (non-bare) git clone in vendor/foo/bar and therefore the git checkout with the overall git configuration within the packages folder in the vendor directory (as you have Github configured as the repository source and composer optimizes to take the dist version by default IIRC).
After you changed your configuration to the source install and updated it, cd into vendor/foo/bar and then run git remote -v. It now should show you the "composer" remote(s) for that package.
As you use the develop branch, you can add changes locally but mind the gap that you would also need to push them to the remote repository (Github) before you use composer again to update (at least) that foo/bar package - as while you use git for the development of the foo/bar package now, in your main project you use composer to manage the dependency.
This is the price you have on the payroll using Github instead of a configuration that is more near to the place of work, but at least locally, you can handle the package with "git in git".
This is normally straight forward. One overall price remains thought, due to managing two instead of one repository but that you can't prevent with this kind of composer project [composer only versioned vendor folder]).
Note: If development takes longer than a few hours, it may also make sense to include the new Git sub-project in the backup routine of your parenting project, so that when you remove the folder vendor/foo/bar you have a backup of the (local) Git repository and working tree in it. However, this depends on the project configuration and is your own responsibility.
A bit of a workflow with some hints is also outlined in the composer documentation in Loading a package from a VCS repository.
1 There is a type of setup for a composer project where vendor itself is under git version control, with that git sub-modules can work (very well), but this is most likely not the kind of setup you have for your project, so I skip it for this answer.
If you're working with sail or docker-compose and linking the foo/bar project in the vendor dir is only a temporary until 'it works' solution you could just add it as a volume link. This is what I usually do.
Eg: I'm working on my-project in ~/projects/my-project, I clone the foo/bar repo to ~/projects/bar
Then in the docker-compose.yml I can add the volume:
volumes:
- .:/var/www/html
- ../bar:/var/www/html/vendor/foo/bar
Again, this has a huge assumption on docker being used, but I like to think that everybody is using it these days.
Do I need to have a sub git in my project in vendor/foo with git remote add origin https://github.com/thisisme/bar.git.
That could be achieved with a submodule which allows for your parent Git repository to only store a reference to another repository.
You would use git submodule add for that.
A git clone --recurse-submodule would therefore clone your project with the submodule Git repository in it cloned as well, and checked out to the exact reference you previously committed.

Organize git repo to avoid running into 'filename too long' issue

I use git submodules in my repositories often. These submodules can themselves be several levels deep with other submodules. This often results in the filename too long issue on Windows. I have enabled long path support both on Windows and on git (as suggested in this question). i.e. if I run the following commands:
git config --global --get core.longpaths
git config --system --get core.longpaths
I get true.
LongPathsEnabled is also set to 1 under HKLM\SYSTEM\CurrentControlSet\Control\FileSystem
After scouring the internet, I've learned long paths should be avoided in the first place (e.g. see this link).
So my main question is: what is the "correct way" of organizing repositories that depend on other repositories without running into the long path issue?
In my case, the repositories are usually .NET projects that depend on other libraries (included as submodules), these libraries further depend on other libraries and so forth.
Like so:
Project A --> submodule A --> submodule B --> submodule C --> submodule D...
I was also wondering whether I'm enabling the long path support correctly?

Is there an easier way to keep local Go packages updated

I am using multiple packages that I import into different projects, these range from custom adapters for my business logic that are shared by lambda and google cloud functions and other public packages. The way I do this right now is that I vendor them and include them for cloud functions. For applications that can be compiled and deployed on a VM, I compile them separately. This works fine for me, however, its a pain developing these modules.
If I update the method signature and names in the package, I have to push my changes to github / gitlab (my package path is something like gitlab.com/groupName/projectName/pkg/packageName) and then do a go get -u <pacakgeName> to update the package.
This also, does not really update it, sometimes am stuck with an older version with no idea on how to update it. Is there an easier way of working with this I wonder.
For sake of clarity:
Exported package 1
Path: gitlab.com/some/name/group/pkg/clients/psql
psql-client
|
|_ pkg
|
|_psql.go
Application 1 uses psql-client
Path: gitlab.com/some/name/app1
Application 2 uses psql-client
Path: gitlab.com/some/name/app2
My understanding is that (a) you are using the new Go modules system, and that (b) part of the problem is that you don't want to keep pushing changes to github or gitlab across different repositories when you are doing local development.
In other words, if you have your changes locally, it sounds like you don't want to round-trip those changes through github/gitlab in order for those changes to be visible across your related repositories that you are working on locally.
Most important advice
It is greatly complicating your workflow to have > 1 module in a single repository.
As is illustrated by your example, in general it is almost always more work on an on-going basis to have > 1 module in a single repository. It is also very hard to get right. For most people, the cost is almost always not worth it. Also, often the benefit is not what people expect, or in some cases, there is no practical benefit to having > 1 module in a repo.
I would definitely recommend you follow the commonly followed rule of "1 repo == 1 module", at least for now. This answer has more details about why.
Working with multiple repos
Given you are using Go modules, one approach is you can add a replace directive to a module's go.mod file that informs that Go module about the on-disk location of other Go modules.
Example structure
For example, if you had three repos repo1, repo2, repo3, you could clone them so that they all sit next to each other on your local disk:
myproject/
├── repo1
├── repo2
└── repo3
Then, if repo1 depends on repo2 and repo3, you could set the go.mod file for repo1 to know the relative on-disk location of the other two modules:
repo1 go.mod:
replace github.com/me/repo2 => ../repo2
replace github.com/me/repo3 => ../repo3
When you are inside the repo1 directory or any of its child directories, a go command like go build or go test ./.... will use the on-disk versions of repo2 and repo3.
repo2 go.mod:
If repo2 depends on repo3, you could also set:
replace github.com/me/repo3 => ../repo3
repo3 go.mod:
If for example repo3 does not depend on either of repo1 or repo2, then you would not need to add a replace to its go.mod.
Additional details
The replace directive is covered in more detail in the replace FAQ on the Modules wiki.
Finally, it depends on your exact use case, but a common solution at this point is to use gohack, which automates some of this process. In particular, it creates a mutable copy of a dependency (by default in $HOME/gohack, but the location is controlled by $GOHACK variable). gohackalso sets your current go.mod file to have a replace directive to point to that mutable copy.
go get is transitive, so you can just add it to your build process. A typical Go project build is basically:
go get -u ./... && go test ./... && go build ./cmd/myapp
Which gets & updates dependencies, runs all project tests, then builds the binary.

Building dependent packages with BitBake separately

We have a collection of related, and partly interdependent, packages. They live in separate Git repositories. Like:
git#bitbucket.company:common
git#bitbucket.company:libfoo
depends on common
git#bitbucket.company:libbar
depends on common
git#bitbucket.compnay:libbarex
depends on libbar
git#bitbucket.company:daemon
depends on common
final-image (just an image recipe)
depends on libfoo, libbar, libbarex, daemon
The packages are currently built by setting up the Yocto environment, checking out all of them and running bitbake with the image target that depends on all of them. The checkout is done with logic containing special logic. That is
git clone https://git.yoctoproject.org/git/poky
git clone -b $BRANCH git#bitbucket.company:common
git clone -b $BRANCH git#bitbucket.company:libfoo
git clone -b $BRANCH git#bitbucket.company:libbar
git clone -b $BRANCH git#bitbucket.company:libbarex
git clone -b $BRANCH git#bitbucket.company:daemon
. poky/oe-init-build-env
../common/add-layers.sh
bitbake final-image
The disadvantage is that the CI server does not understand the configuration management side of things, so it can't do things like building each pull-request, not to mention reporting it back to the Git repository manager to gate the pull-requests on it.
So I would like to start splitting the build to build each package separately, pulling the dependencies according to some specification that shall be updated as needed.
But how do I do that?
I can add
bitbake -c populate_sdk final-image
to the main build, and then use the SDK to build individual components. However that means that any changes in common have to be merged, and the SDK rebuilt, before I can build libfoo, libbar or daemon and additionally changes to libbar have to be merged before I can build libbarex.
Can I push the results of these incremental builds to some kind of repository and pull them back to build the dependent component without having to rebuild the SDK?

VS Team Services git Dependency Build

I am new to Team Services (web based) build process. Previously I asked a different question for a solution with using TF for version control. Thanks to a great individual that problem was solved. I am now experimenting with git for version control and ran into a similar situation.
I have a Solution 1 with Project A and B where B is dependent on A (common code). Project A is under a different Solution 2 but was added to Solution 1 via "Add...Existing Project". Both solutions and projects are under different git repo. I use submodule for Project B to pull in Project A and that part works. However, Project A is pulled in under "d:\a\1\s\ProjectA" and
when the build agent attempts to build Project B, it is looking at "d:\a\1\s\ProjectB....\ProjectA\" for the dependency. How do I change this part to tell the build agent the location of Project A? Or how can I tell the submodule to pull Project A to "d:\a\1\s\ProjectB....\ProjectA\"?
Thank you!
Since you added ProjectA as submodule for the ProjectB, so you should add dependent for ProjectB by selecting ProjectA in submodule (not solution 2). So you should remove the projectA you added, and then use Add -> Existing Project -> select projectA from submodule -> OK.
Then commit/push changes to VSTS git repo. And build your ProjectB, it should find the dependent in the submodule path.
Or if you want the submodule located in root/ProjectB/.../ProjectA, in your local repo, you should go to the directory root/ProjectB/..., and use git submodule add <URL for the repo including ProjectA>. Then the submodule will located in root/ProjectB/.../ProjectA.

Resources