Reputation: 53
We're setting up a project in Rails and the fact is we have two git repos for that purpose, one is bitbucket (Were we keep track of the project), and the other is Heroku (Production). We have to upload a credentials file into Heroku so the app can work properly but we don't want that file to be uploaded to Bitbucket for security issues so our question is: Can we set some sort of option into gitignore file to upload the credentials file JUST TO Heroku AND NO to bitbucket? Thanks in advance.
We've tried to upload the file via transfer.sh and gpg but we'd rather no use it since this file is very delicate in terms of security. Also creating the file using heroku bash is not possible since Heroku automatically deletes it.
Upvotes: 0
Views: 1987
Reputation: 45799
You can sort of make that happen, but maybe not exactly the way you describe.
When git pushes (or pulls), it communicates in terms of "refs" and their history. The history of a ref is made up of commits. In this context, a commit is immutable and can be thought of as atomic - you either send a commit, or you don't. Within a commit is the structure of directories and files (roughly) that make up a snapshot of the project's content.
So in order to include the file when you send to heroku, but not when you send to bitbucket, you need to send different commits to each repo. Which, in practice, means you should send different refs. And that's fine - you can have the file on a "production branch", and not on any other branches. You can omit the production branch from your pushes to bitbucket, and include it in your pushes to heroku.
In theory it's as simple as that. You start a repo without the sensitive file in it. You map to remotes - say, origin
maps to bitbucket, and heroku
maps to your prod heroku repo.
You let the software grow, and eventually you have a version you want to publish to production.
O -- ... -- A <--(master)(origin/master)
(Here the O
is your initial commit; ...
represents any kind of history whatsoever - maybe with branches and merges, etc.; and A
is a commit you want to publish to prod. I've shown it being on the master
branch, which would be consistent with models like GitFlow, but this isn't strictly necessary.)
So you check out that version, then
git checkout -b production
# create/copy the sensitive file into your work tree
git add path/to/sensitive file
git commit -m "production release 1"
Now you have
O -- ... -- A <--(master)(origin/master)
\
P1 <--(production)
You don't want to push production
to bitbucket, but you do want to push it to heroku. Note that your production branch in the heroku repo must be known asmaster
, so you might say something like
git push heroku production:master
and you have
O -- ... -- A <--(master)(origin/master)
\
P1 <--(production)(heroku/master)
Then more commits come in on your local branches, and eventually you want to publish again.
O -- ... -- A -- ... -- B <--(master)(origin/master)
\
P1 <--(production)(heroku/master)
In this scenario, you can just
git checkout production
git merge master
The merge base will be A
. The only change from A
to production
is adding the sensitive file, and master
doesn't have the sensitive file (so can't have made conflicting changes to it); so the merge should go smoothly and you get
O -- ... -- A -- ... -- B <--(master)(origin/master)
\ \
P1 --------- P2 <--(production)
^(heroku/master)
So again you push to heroku
git push heroku production:master
ending at
O -- ... -- A -- ... -- B <--(master)(origin/master)
\ \
P1 --------- P2 <--(production)(heroku/master)
To keep the merges into the production
branch simple, each time you're releasing a new version (i.e. B
) you'd want the previous releasable version (i.e. A
) to be "reachable" from B
. Most branching models will give you that. But you could for some reason do something like
x -- x -- C <--(special-release)
/
O -- ... -- A -- ... -- B <--(master)(origin/master)
\ \
P1 --------- P2 <--(production)(heroku/master)
In this case, a simple merge wouldn't work to update production
so that it "looks like" C
. If you feel that you would absolutely need to support this case, I can add some thoughts on how to do it, but the best advice is to keep the releasable history moving in a single direction (which, again, is often what your master
branch would be doing for you anyway).
You can see that there's a lot of room for human error if you just do exactly what I've described above. As a fail-safe, you might want to only create one special local repo in which you build the production branch. (If your other local repos don't contain production
, you can't accidentally push production
from them to bitbucket.) You also might want to tweak that special repo's configuration, so that push by default will send local production
to heroku master
. (See the git config docs for more info on options for doing that.)
Upvotes: 0
Reputation: 489588
Can we set some sort of option into gitignore file to upload the credentials file JUST TO Heroku AND NO to bitbucket?
No. There are a few things you might be able to do, although storing credentials in a repository is generally the wrong idea anyway.
Git is not about files. Git is about commits. Commits contain files, so by getting and using a commit, you can get those files, but the fundamental unit of Git is the commit.
Meanwhile, .gitignore
is about files—specifically, files that are in your work-tree that are not going to be in future commits. There's a stumbling block that people run into here, because you build new commits in a thing that Git calls the index. In the end, though, if an ignore entry is effective, that is because it keeps the file from going into the next commit. The mechanism here—the .gitignore
file—is entirely irrelevant.
If you keep a file from going into your next commit, which eventually goes to Bitbucket, then, if and when the same commit also goes to Heroku, that commit will also not have that file.
Likewise, if and when you put a file into a commit that eventually goes to your Heroku system, then, if and when the same commit also goes to Bitbucket, that commit will continue to have the file.
This makes the situation clear: If a commit has a file, whoever has the commit has the file. It's possible to send commits to Heroku that you never send to Bitbucket, but because Git in general is designed around a philosophy where it tends to send every commit to everyone, think very hard about how you intend to make sure that security data never leak, before you attempt this.
If you're dead set on doing this anyway, note that .gitignore
is probably going to be the wrong mechanism for sometimes putting files into, and sometimes keeping files out of, particular commits. (This is because a .gitignore
entry takes effect only if the file is not already in the index, and checking out any commit that has the file, puts the file into the index, so that the next commit will also have the file, regardless of any .gitignore
entry.)
Upvotes: 1