I'm writing an API that's using django-rest-framework (but the framework, nor the language matter at this point). I've got a diagnostic API endpoint "/version" that reads some data from a file (the last git checkin ID) and then spits it back out. the code looks like this:
class Version(APIView):
permission_classes = (IsAdminUser,)
def get(self, request):
git_version_file = open('gitversion', 'r')
checkin_id = git_version_file.read().strip()
return Response({'version' : checkin_id})
Normally (on my dev machine) this is easy as I can call a git post-commit hook to create the file (the command would be git rev-parse --short HEAD > gitversion. Obviously, this file cannot be checked into git.
However, I want this to work on my instances that are deployed on heroku. Of course, I can use the "heroku releases" command, however I want this to be available programmatically (by people who don't have access to the "heroku" command line command).
I can't run this on a one-off dyno as the filesystems are not the same. I'm not sure how I can accomplish this at this point.
Related
I created an app on Heroku using the CLI:
heroku apps:create --region eu myapp
Then in my app's GitHub repository I created a GitHub action to deploy the app on Heroku by pushing a subtree of the repository:
git remote add heroku https://heroku:${{ secrets.HEROKU_API_KEY }}#git.heroku.com/myapp.git
git subtree push --prefix path/to/myapp heroku main
Then I triggered the action, and it worked perfectly just as intended; the app got deployed on Heroku.
Next, I edited a line of the app's source on GitHub, committed and triggered the pipeline just to test that the action works (as it was configured to trigger on pushes to main). The action triggered but then failed with a git error saying that the remote (Heroku remote repository) contains some work that I don't have "locally" (in the GitHub repository where the action was triggered from). I did not push anything to the Heroku remote repository in between, and nobody else has access to the remote either. I just created the app as described. How can the remote have something that I do not have locally?
EDIT1:
After further inspection it looks like the git subtree push command produces a different kind of git log when issued from the GitHub actions workflow agent vs. when issued from my local system. The former produces a git log where all of the commits up to that point are somehow squashed into one, whereas the latter produces a full git log including all the commits normally up to that point. Therefore the last commits' hashes are different even though the files are "in the same state" at that point.
Does the discrepancy come from differing git config --list or is there something more to this? And what would I need to change in the GitHub workflow's git config to make it not ruin the last commit's hash by doing this not-asked-for squashing? (Assuming the last commit's hash is the issue here.)
Here's the git config that the GitHub workflow agent appears to use:
$ git config --list
safe.directory=*
filter.lfs.clean=git-lfs clean -- %f
filter.lfs.smudge=git-lfs smudge -- %f
filter.lfs.process=git-lfs filter-process
filter.lfs.required=true
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
remote.origin.url=https://github.com/mynick/myreponame
remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*
gc.auto=0
http.https://github.com/.extraheader=AUTHORIZATION: basic ***
branch.main.remote=origin
branch.main.merge=refs/heads/main
I'm deploying an app to Heroku, which means pushing to their git repo. When do git push heroku master (or the equivalent remote alias) I get this warning:
WARNING: You're about to push to master, is that what you intended? [y|n]
Which is kinda annoying but not a big deal. However, I'm now scripting deployments so I don't want to have interact with the script - how I do get my bash script to answer y automatically?
I tried doing yes | git push heroku master but that doesn't work.
Agree with #bk2204, I would check if a git hook is being used that you are unaware of.
Git hooks are not version controlled, so make sure you are searching for this at the machine where you get this error.
The git hook may live elsewhere other than the .git directory. Check if git config core.hooksPath is set and if so, it will point to the directory where your git hook is.
The hook file that you are looking for will most likely have the name pre-push. The solution in this case is to remove or rename this hook file, and git won't run it before push.
Nope, no such file
And yet, this is exactly what a .git/hooks/pre-push would do, like this one.
To rule that out, activate (with your local Git 2.25+) trace2.
That will allow you to see what is used on the client side (your PC) by Git:
git config --global trace2.normalTarget ~/log.normal
Try your push, type 'n' (to abort), and check ~/log.normal for clues.
Just another way to solve the problem of automating responses to the prompts. You can use expect to take care of interactive warnings/ prompts.
After writing the deployment script deploy.sh, you can write a expect script and spawn the deploy script in it, for response to
WARNING: You're about to push to master, is that what you intended? [y|n]
message, you can send 'y' from expect.
The following snippet can be taken as example
#!/usr/bin/expect -f
spawn bash ./deploy.sh
expect "WARNING: You're about to push to master, is that what you intended? [y|n]\r"
send -- "y\r"
I am trying to use the nodejs module, gift to read the index of a Git repo. I chose 'gift' because it seemed the most promising on Windows. Does anyone know how to get the status of the Git repo using this module? I basically just want to perform a status command on my Git repo.
$ git status
Are there any other nodejs Git Modules that work well on Windows?
Thanks!
Solution
After looking through the source I found that Status is an object which is attached to Repo. You use it as described in my selected answer.
Windows caveat - I had to add git to my system path and then REBOOT. Gotta love windows :\ Even after adding git to my system path and testing it out in cmd.exe, gift kept throwing error:
command failed: 'git' is not recognized as an internal or external command, operable program or batch file.
Once I rebooted all was well.
You can get the status like this
git = require 'gift'
repo = git "path/to/repo"
repo.status(callback)
The callback receives (err, status). Status has properties
status.clean
boolean, true if working tree clean
status.files
Object - The keys are files, the values objects indicating whether or not the file is staged, tracked, etc.
Each file has the following properties:
type - "A" for added, "M" for modified, "D" for deleted.
staged - Boolean
tracked - Boolean
Source for this information: the documentation.
I'm trying to get a git post-receive hook working on Windows.
I'm using Git 1.7.9 (Msysgit) and have a repo locally and a bare repo on a remote server. I can fetch, commit, push etc. I've set up a post-receive hook that should checkout the files into a working folder (part of the deployment process) but it doesn't seem to work.
Here's what I do:
Change a file, stage and commit it
Push to remote server - successfully
Expect to see the echo - don't see the echo
Check working folder on server - latest files are not there
Log onto the server and run the hook script manually - latest files are checkout out into the working folder.
I changed the hook so it does nothing except echo a message and I've read that I should see this in my console after pushing. But this is not being displayed so I can only assume the hook is not being fired off.
I'm pushing over HTTP with git dot aspx on the server handling the request and pusing via the gui locally. After that failed I tried Bonobo and the hook doesn't work when pushing via the gui or a bash console.
I'm assuming someone has this working somewhere but after two days of searching all I have found are solutions that don't help or people with the same problem that has gone unanswered.
(I'm a git newbie btw).
Cheers.
Update
I'm starting to think it may be to do with permissions - but Unix permissions, rather than NTFS. When #eis mentioned permissions I had assumed NTFS. But after more digging it seems that Git on Windows still checks for Unix file perms.
So I suspect the issue is that the post-receive file is not executable as when I do a ls -o it's -rw-r--r-- (644 I believe). If I try and change this through bash and chmod 777 post-receive then do ls -o the permissions are the same.
The strange this is that as soon as I edited post-receive (with notepad++) the execute bit gets removed. (my test script that ends in .bat does retain its execute bits though...)
BTW, the user I'm logged on as is the owner of the files (according to ls -o) and yet I can't set the permissions.
Starting to get really confused now. Am I missing something really obvious?
Update 2
Neither chmod 777 post-receive nor chmod a+x post-receive work. I took a new, clean post-receive file, uploaded it the to the server and checked the permissions and it had execute. If I rename the file (to remove sample) in Windows then execute is removed. If I do it in bash with mv execute is retained.
But, whenever I edit the file (in Windows or in bash with vi) then execute gets removed.
So, the problem now is why does it remove the execute bits when I edit the file?
Hopefully this is the final hurdle and the cause of it not executing...
You are going to have to patch git to make this work. The checks in builtin/receive-pack.c are for access(path, X_OK). In msysgit this diverts to mingw_access which throws away the X_OK bit as it is simple not supported on Windows.
On windows, we have no flag to specify a file is executable. Systems often do some emulation of this. For instance, tcl will look for any extension in the PATHEXT environment variable to decide that a file is executable. We can't do that here as the hook names are hardcoded without any extensions.
Instead, I suggest changing the access test to just check the file exists and then call execv on the path. The mingw version of this (in compat/mingw.c) looks for script files and will read the shbang line and launch an appropriate interpreter (sh, perl etc). So modifying builtin/receive-pack.c:run_update_hook should let this work for you. Currently the hook running uses start_command and I think that should call down to execv for you.
In short, change the access test and it will probably work.
When using msysgit on the server and pushing via a file share hooks work without problem now. Maybe this was fixed in mysysgit since the answer was written. I didn't look into it.
I also noticed that the original question stated git dot aspx and Bonobo were being used which use GitSharp.dll. This would mean the application is not shelling out to the git.exe and hooks would not be handled the same way.
For example, the GitSharp.dll used in git dot aspx has it's own hook post-receive hook implementation which could be performed in C#:
public void Receive(Stream inputStream, Stream outputStream)
{
using (var repository = GetRepository())
{
var pack = new ReceivePack(repository);
pack.setBiDirectionalPipe(false);
//setup post receive hook here
pack.setPostReceiveHook(new PostRecieveHook());
pack.receive(inputStream, outputStream, outputStream);
}
}
public class PostRecieveHook : IPostReceiveHook
{
public void OnPostReceive(ReceivePack rp, ICollection<ReceiveCommand> commands)
{
//Do PostRecieve Hook Work Here
}
}
I hope to help others with confusion between libraries that are implementations of Git and applications that call out to the actual git.exe.
I'm using Heroku to host a Rails app, which means using Git to deploy to Heroku. Because of the "pure Git workflow" on Heroku, anything that needs to go upstream to the server has to be configured identically on my local box.
However I need to have certain configuration files be different depending on whether I'm in the local setup or deployed on Heroku. Again, because of the deployment method Heroku uses I can't use .gitignore and a template (as I have seen suggested many times, and have used in other projects).
What I need is for git to somehow track changes on a file, but selectively tell git not to override certain files when pulling from a particular repo -- basically to make certain changes one-way only.
Can this be done? I'd appreciate any suggestions!
You can have config vars persistently stored ON each heroku app's local setup so they do not have to be in your code at all! so the same code can run on multiple heroku sites but with different configuration. It very simple, easy, elegant...
It's the approach we used. (We used it for the SAME thing... we have multiple clones of the SAME app at Heroku, but we want only ONE source at github, in our dev local directory we do the PUSH to ORIGIN (github), then when we have it the way we like it, we CD to the prod local directory, which goes to the SAME github repository, and we ONLY PULL from GITHUB into this directory, never push (eg, all pushes to github come from our dev directory, the prod directory is just a staging area for the other heroku app.)
By having the different configs ON the different HEROKU sites (as explained below), the EXACT SAME CODE works on BOTH heroku sites.
So our workflow is: (the key is that BOTH directories point to SAME github repo)
cd myDEVdir
*....develop away....*
git add .
git commit -am "another day, another push"
git push origin *(to our SINGLE github repo)*
git push heroku *(test it out on heroku #1)*
cd ../myPRODdir
git pull *(grabs SAME code as used on other site *)
git push heroku *(now the SAME code runs on Heroku #2)*
that's it!
Now here's how you keep your site-specific config vars ON the heroku site:
http://docs.heroku.com/config-vars
on your local command line, for EACH of your two local directories, do:
$ heroku config:add FIRST_CONFIGVAR=fooheroku1
Adding config vars:
FIRST_CONFIGVAR => fooheroku1
$ heroku config:add SECOND_CONFIGVAR=barheroku1
Adding config vars:
SECOND_CONFIGVAR => barheroku1
to see the ones you have defined:
$ heroku config
FIRST_CONFIGVAR => fooheroku1
SECOND_CONFIGVAR => barheroku1
then cd to your other directory myPRODdir and do the SAME thing, only set the same remote heroku vars to fooheroku2 and barheroku2.
then in your rails app you simple refer to them like so:
a = ENV['FIRST_CONFIGVAR']
One app will read 'fooheroku1' the other app will read 'fooheroku2'
And finally, on your LOCAL directory myDEVdir, where you run in DEV mode, put the same config commands in your config/environment/development.rb file your 'dev' version of the config vars will be set to whatever they should be:
ENV['FIRST_CONFIGVAR'] = "foodev"
ENV['SECOND_CONFIGVAR'] = "bardev"
Easy, elegant. Thanks, Heroku!
Here are a few solutions:
1) If you want to ignore files only on heroku, use slugignore
2) If your changes are minor, stay DRY and use universal config files, inserting switches for server-specific behavior
if Rails.env == "production"
#production server code
elsif Rails.env == "development"
#development server code
else
#test server code
end
3) If your changes are major, write one config file and add a "smudge file" to config/initializers for each additional server. Basically, you would have separate files using the technique in (2).
4) If your changes are SWEEPING (unlikely), then maintain separate branches for each server.
5) This script can do exactly what you requested, but may be overkill.
I hope that helped.