How can I deploy/push only a subdirectory of my git repo to Heroku?
Asked Answered
E

5

143

I have a project that uses Serve and is version controlled using Git. Serve creates an output folder with static files that I want to deploy to Heroku.

I don't want to deploy the Serve project itself since the Heroku Cedar stack doesn't seem too fond of it, but most importantly I want to take advantage of Heroku's great support for static websites.

Is there a way to deploy a subfolder to a git remote? Should I create a Git repo in the output folder (that sounds wrong) and push that to Heroku?

Etam answered 24/9, 2011 at 13:39 Comment(1)
You might be looking for submodules : book.git-scm.com/5_submodules.htmlNightclub
R
253

There's an even easier way via git-subtree. Assuming you want to push your folder 'output' as the root to Heroku, you can do:

git subtree push --prefix output heroku master

It appears currently that git-subtree is being included into git-core, but I don't know if that version of git-core has been released yet.

Rhpositive answered 18/5, 2012 at 7:41 Comment(11)
It is available in git 1.7.11 and higher.Migrant
Yep, but subtree is still (as of 1.8.0.2) not included via the git installer. Luckily installing from source is quick and straightforward, this page worked for me on mac.Plash
I installed git subtree from this fork github.com/jroper/git-subtree since it includes support for --force pushRhpositive
Looks like it's in OS X Mavericks (10.9.2). My version is 1.8.3.4.Bromism
If you need --force, use git push heroku `git subtree split --prefix output master`:master --force. See https://mcmap.net/q/161340/-why-can-39-t-i-push-this-up-to-date-git-subtree.Leprechaun
But what is the correct way to push a specific tag. I thought it should be git subtree push --prefix output heroku +refs/tags/v1.0.0:refs/heads/master. But this doesn't work and comes back with +refs/tags/v1.0.0:refs/heads/master does not look like a ref. I need this kind of functionality to be able to roleback to specific tags later on. What is the correct way of doing that?Cornwell
Updated link to git subtree docsMaskanonge
I get the error 'Updates were rejected because a pushed branch tip is behind its remote'Briar
Hi, I never managed to push a specific branch with this command (eg develop instead of master).Senghor
@and-dev @Eric Burel I successfully pushed the output folder that was only present on my develop branch to the heroku master branch without the need of specifying develop:master, so apparently it pushes to the target branch you specify from your currently checked out branch.Bollworm
doesn't seem to work anymore I get a message instead: git subtree push --prefix server-heroku heroku master git push using: heroku master No new revisions were foundObservatory
L
11

I started with what John Berryman put, but actually it can be simpler if you don't care at all about the heroku git history.

cd bin
git init
git add .
git commit -m"deploy"
git push [email protected]:your-project-name.git -f
rm -fr .git

I guess official git subtree is the best answer, but i had issue getting subtree to work on my mac.

Levanter answered 12/12, 2013 at 22:50 Comment(1)
In this case 'bin' is your target subfolder?Tremor
F
9

I had a similar issue. In my case it was never a problem to blow away everything in the heroku repository and replace it with whatever is in my subdirectory. If this is your case you can use the following bash script. Just put it in your Rails app directory.

#!/bin/bash

#change to whichever directory this lives in
cd "$( dirname "$0" )"

#create new git repository and add everything
git init
git add .
git commit -m"init"
git remote add heroku [email protected]:young-rain-5086.git

#pull heroku but then checkback out our current local master and mark everything as merged
git pull heroku master
git checkout --ours .
git add -u
git commit -m"merged"

#push back to heroku, open web browser, and remove git repository
git push heroku master
heroku open
rm -fr .git

#go back to wherever we started.
cd -

I'm sure there are plenty of ways to improve upon this - so feel free to tell me how!

Foetor answered 10/10, 2011 at 18:11 Comment(2)
+1 Thanks. This solution works great if you don't care about git logs on Heroku. One can tweak above script in case there are some folders you want to ignore, within the application sub path to be deployed. For example I did not want spec folder on heroku. Example GistHydraulics
+1 but you can simplify by not pulling and merging into heroku master and instead simply git push --force heroku masterTheorist
B
4

After a long and hard month of trying different things and getting bitten every time I realized,

just because Heroku uses a git repository as a deployment mechanism, you should not treat it as a git repository

it could have been rsync just as well, they went for git, don't get distracted because of this

if you do so, you open up yourself to all kinds of hurt. All of the aforementioned solutions fail miserably somewhere:

  1. it requires something to be done every time, or periodically, or unexpected things happen (pushing submodules, syncing subtrees, ...)
  2. if you use an engine for example to modularize your code, Bundler will eat you alive, it's impossible to describe the amount of frustration I've had with that project during the quest to find a good solution for this
    • you try to add the engine as git repo link + bundle deploy - fail, you need to bundle update every time
    • you try to add the engine as a :path + bundle deploy - fail, the dev team considers :path option as "you're not using Bundler with this gem option" so it'll not bundle for production
    • also, every refresh of the engine wants to update your rails stack -_-
  3. only solution I've found is to use the engine as a /vendor symlink in development, and actually copy the files for production

The solution

The app in question has 4 projects in git root:

  1. api - depending on the profile will run on 2 different heroku hosts - upload and api
  2. web - the website
  3. web-old - the old website, still in migration
  4. common - the common components extracted in an engine

All of the projects have a vendor/common symlink looking at the root of the common engine. When compiling source code for deployment to heroku we need to remove the symlink and rsync it's code to physically be in the vendor folder of each separate host.

  1. accepts a list of hostnames as arguments
  2. runs a git push in your development repo and then runs a clean git pull in a separate folder, making sure no dirty (uncommited) changes are pushed to the hosts automatically
  3. deploys the hosts in parallel - every heroku git repo is pulled, new code is rsynced into the right places, commited with basic push information in the git commit comment,
  4. in the end, we send a ping with curl to tell the hobby hosts to wake up and tail the logs to see if all went wine
  5. plays nice with jenkins too :D (automatic code push to test servers after successful tests)

Works very very nice in the wild with minimal (no?) problems 6 months now

Here's the script https://gist.github.com/bbozo/fafa2bbbf8c7b12d923f

Update 1

@AdamBuczynski, it's never so straightforward.

1st you will always have a production and test environment at the least - and a bunch of function specific clusters at the worse - suddenly 1 folder needs to map to n heroku projects as a pretty basic requirement and it all needs to be organized somehow so that the script "knows" what source you want to deploy where,

2nd you will want to share code between projects - now comes the sync_common part, the shennanigans with symlinks in development being replaced by actual rsynced code on Heroku because Heroku requires a certain folder structure and bundler and rubygems really really really make things ugly very badly if you want to extract the common threads into a gem

3rd you will want to plug in CI and it will change a bit how subfolders and git repo need to be organized, in the end in the simplest possible use case you end up with the aforementioned gist.

In other projects I need to plug in Java builds, when selling software to multiple clients you will need to filter modules that get installed depending on the installation requirements and whatnot,

I should really consider exploring bundling things into a Rakefile or something and do everything that way...

Baltoslavic answered 26/1, 2016 at 9:34 Comment(3)
Hi @bbozo, would you mind condensing your solution a bit and making it specific to the use case of deploying one specific sub folder to one specific heroku project and take out all the stuff that's not needed/specific to Heroku?Sextans
Thanks for updating your answer. I think I'll just bit the bullet and split my client and server side code into separate repositories. Not ideal for our situation, but it will beat the forced subtree pushes we have to do now, and from what I gather, will also be a lot simpler than trying to use symlinks.Sextans
Don't be afraid of a "deploy script", it pays offBaltoslavic
C
0

Alternatively, you can use git subtree to create a heroku branch on GitHub which you can then deploy to Heroku using a Heroku Button:

Deploy

  1. Add an app.json to your server directory, as explained here.

  2. Add the following markdown to README.md:

    [![Deploy](https://www.herokucdn.com/deploy/button.png)](https://heroku.com/deploy?template=https://github.com/username/repository/tree/heroku)
    
  3. Push your changes to the heroku branch:

    git subtree push --prefix server origin heroku
    
Coenosarc answered 17/1, 2021 at 21:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.