Skip to content
This repository was archived by the owner on Mar 5, 2025. It is now read-only.

Is it possible to apply a custom drupal/core .htaccess patch after composer update? How? #1135

Closed
justinlevi opened this issue Feb 24, 2017 · 14 comments
Labels
Support A support request

Comments

@justinlevi
Copy link
Contributor

justinlevi commented Feb 24, 2017

We have a situation where we need to apply a custom local htaccess patch after running composer update. The patch applies some custom url redirects but doesn't seem to run as expected when I add it to our drupal/core patches in composer.json.

Here is my patch:

Index: docroot/.htaccess
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- docroot/.htaccess (date 1487969309000)
+++ docroot/.htaccess (revision )
@@ -69,6 +69,28 @@
 <IfModule mod_rewrite.c>
   RewriteEngine on
 
+  # Rewrite all requests to the old inspector general website to the new domain.
+  RewriteCond ^owig.state.ny.us [NC]
+  RewriteRule ^(.*)$ https://<<***MY-DOMAIN-HERE***>>/$1 [L,R=301,NC]
+
+  # redirects for .com and .org domains
+  RewriteCond %{HTTP_HOST} ^<<***MY-DOMAIN-HERE***>> [OR]
+  RewriteCond %{HTTP_HOST} ^www.<<***MY-DOMAIN-HERE***>> [OR]
+  RewriteCond %{HTTP_HOST} ^<<***MY-DOMAIN-HERE***>> [OR]
+  RewriteCond %{HTTP_HOST} ^www.<<***MY-DOMAIN-HERE***>> [OR]
+  RewriteCond %{HTTP_HOST} ^<<***MY-DOMAIN-HERE***>> [OR]
+  RewriteCond %{HTTP_HOST} ^www.<<***MY-DOMAIN-HERE***>> [OR]
+  RewriteCond %{HTTP_HOST} ^<<***MY-DOMAIN-HERE***>> [OR]
+  RewriteCond %{HTTP_HOST} ^www.<<***MY-DOMAIN-HERE***>> [OR]
+  RewriteCond %{HTTP_HOST} ^www.<<***MY-DOMAIN-HERE***>>.gov
+  RewriteRule ^(.*)$ https://<<***MY-DOMAIN-HERE***>>.gov/$1 [R=permanent,L]
+  # Redirect to HTTPS for Production Sites
+  RewriteCond %{HTTPS} off
+  RewriteCond %{HTTP_HOST} ^<<***MY-DOMAIN-HERE***>>.gov$ [NC]
+  RewriteCond %{HTTP:X-Forwarded-Proto} !https
+  RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
+
   # Set "protossl" to "s" if we were accessed via https://.  This is used later
   # if you enable "www." stripping or enforcement, in order to ensure that
   # you don't bounce between http and https.

Here is the composer.json drupal/core patch lines:

"patches": {
            "drupal/core": {
                "Image check for the presence of the array before using it in a function call": "https://www.drupal.org/files/issues/2745491-15.patch",
                "Redirects": "./patches/redirects.patch"
            }
        },

If I add -vvv to my $ composer update command I'm seeing the following error:

    ./patches/redirects.patch (Redirects)
cd 'docroot/core' && GIT_DIR=. git apply --check '-p1' '/Users/justinwinter/Sites/wny/patches/redirects.patch'
Executing command (CWD): cd 'docroot/core' && GIT_DIR=. git apply --check '-p1' '/Users/justinwinter/Sites/wny/patches/redirects.patch'
fatal: patch with only garbage at line 5

cd 'docroot/core' && GIT_DIR=. git apply --check '-p0' '/Users/justinwinter/Sites/wny/patches/redirects.patch'
Executing command (CWD): cd 'docroot/core' && GIT_DIR=. git apply --check '-p0' '/Users/justinwinter/Sites/wny/patches/redirects.patch'
fatal: patch with only garbage at line 5

cd 'docroot/core' && GIT_DIR=. git apply --check '-p2' '/Users/justinwinter/Sites/wny/patches/redirects.patch'
Executing command (CWD): cd 'docroot/core' && GIT_DIR=. git apply --check '-p2' '/Users/justinwinter/Sites/wny/patches/redirects.patch'
fatal: patch with only garbage at line 5

patch '-p1' --no-backup-if-mismatch -d 'docroot/core' < '/Users/justinwinter/Sites/wny/patches/redirects.patch'
Executing command (CWD): patch '-p1' --no-backup-if-mismatch -d 'docroot/core' < '/Users/justinwinter/Sites/wny/patches/redirects.patch'
can't find file to patch at input line 6
Perhaps you used the wrong -p or --strip option?
The text leading up to this was:
--------------------------
|diff --git b/docroot/.htaccess a/docroot/.htaccess
|index 4716fa1..bf0b596 100644
|--- b/docroot/.htaccess
|+++ a/docroot/.htaccess
|
--------------------------
File to patch: 
Skip this patch? [y] 
Skipping patch.

1 out of 1 hunk ignored

patch '-p0' --no-backup-if-mismatch -d 'docroot/core' < '/Users/justinwinter/Sites/wny/patches/redirects.patch'
Executing command (CWD): patch '-p0' --no-backup-if-mismatch -d 'docroot/core' < '/Users/justinwinter/Sites/wny/patches/redirects.patch'
can't find file to patch at input line 6
Perhaps you used the wrong -p or --strip option?
The text leading up to this was:
--------------------------
|diff --git b/docroot/.htaccess a/docroot/.htaccess
|index 4716fa1..bf0b596 100644
|--- b/docroot/.htaccess
|+++ a/docroot/.htaccess
|
--------------------------
File to patch: 
Skip this patch? [y] 
Skipping patch.

1 out of 1 hunk ignored

patch '-p2' --no-backup-if-mismatch -d 'docroot/core' < '/Users/justinwinter/Sites/wny/patches/redirects.patch'
Executing command (CWD): patch '-p2' --no-backup-if-mismatch -d 'docroot/core' < '/Users/justinwinter/Sites/wny/patches/redirects.patch'
can't find file to patch at input line 6
Perhaps you used the wrong -p or --strip option?
The text leading up to this was:
--------------------------
|diff --git b/docroot/.htaccess a/docroot/.htaccess
|index 4716fa1..bf0b596 100644
|--- b/docroot/.htaccess
|+++ a/docroot/.htaccess
|
--------------------------
File to patch: 
Skip this patch? [y] 
Skipping patch.

1 out of 1 hunk ignored

   Could not apply patch! Skipping. The error was: Cannot apply patch ./patches/redirects.patch

I'm assuming this has to do with how the drupal-scaffolding gets built but I'm unsure how to solve this issue given the BLT ecosystem.

Seems like a similar issue:
#608

@justinlevi
Copy link
Contributor Author

The only thing I can think of that might get this to work is adding a post-update-cmd, but that feels hacky.

        "post-update-cmd": [
            "patch -p1 < patches/redirects.patch "
        ]

@justinlevi justinlevi changed the title Apply Custom .htaccess patch after composer update Is it possible to apply a custom drupal/core .htaccess patch after composer update? How? Feb 28, 2017
@grasmash grasmash added the Support A support request label Feb 28, 2017
@bkosborne
Copy link
Contributor

I'm guessing that doesn't work because the .htaccess file is moved out of the /docroot/core/ folder (where composer downloads all of Drupal core to) and into /docroot instead.

This one of the the drupal "scaffolding" files that the drupal-composer/drupal-scaffold project handles for you.

I'm not sure if there's a way to do this w/ composer patches for that reason.

That said, updates to drupal core are pretty infrequent and you could revert the changes in git pretty easily after the update, right?

@justinlevi
Copy link
Contributor Author

@bkosborne Yeah, this happens pretty infrequently so we could definitely revert the changes manually. That being said, I work on a team so it just adds a bit more overhead to keep track of. Stuff like this really needs to be automated otherwise it's likely to get missed at some point down the road.

Is there a way to apply the patch via the post-update-cmd hook in a way that wouldn't throw an error and wouldn't add the same patch updates each time it's executed? I've done a bit of googling on this but haven't been able to come up with anything and my command line patch skills are not so good.

@bkosborne
Copy link
Contributor

Agreed it adds some overhead, but it's always been the case with Drupal core updates that you have to manually check any overrides you made to scaffolding files like robots.txt and .htaccess.

That said, being able to automate it is a great idea anyway. I'm not sure I can help you though, sorry.

@justinlevi
Copy link
Contributor Author

Something like this might work:

if ! patch -R -p0 --dry-run <patchfile; then
  patch -p0 <patchfile
fi

@mike-potter
Copy link

I couldn't find any way to patch the .htaccess file. The docs in patches/README are very misleading on how to apply patches.

As mentioned here, you cannot patch .htaccess because it doesn't get moved to the docroot until drupal-scaffold runs. So trying to patch it in post-update-cmd doesn't work.

The only "solution" I have found is to just add .htaccess to the excludes section of drupal-scaffolding, like this:

        "drupal-scaffold": {
            "initial": {
                "sites/default/default.services.yml": "sites/default/services.yml",
                "sites/default/default.settings.php": "sites/default/settings.php"
            },
            "excludes": [
                "sites/development.services.yml",
                ".htaccess"
            ]
        }

This at least keeps your changes intact during updates, but if Drupal core ever changed its .htaccess file you'd need to manually merge that with your patched version.

I posted here: drupal-composer/drupal-scaffold#45 to suggest that drupal-scaffold add a better way to patch files, or at least runs a "post scaffold" script that we could hook into.

@grasmash
Copy link
Contributor

grasmash commented Mar 9, 2017

I think @mike-potter is correct, there isn't currently any way to do this. You must commit the modified file. Documentation for this has been added in #1157. This should be pursued in drupal-composer/drupal-scaffold#45.

@grasmash grasmash closed this as completed Mar 9, 2017
@mike-potter
Copy link

mike-potter commented Mar 9, 2017

For others having this issue, I did find a fix from the drupal-scaffold process. You can run a post-drupal-scaffold-cmd in the scripts section of your composer.json, like this:

        "post-drupal-scaffold-cmd": [
            "cd docroot && patch -p1 <../patches/htaccess-ssl.patch"
        ]

@justinlevi
Copy link
Contributor Author

Here's the bash script I ended up creating at script/wny/update-htaccess.sh

patchfile=$1  # The patch to apply.

DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )

patch -p1 -N --dry-run --silent < $patchfile 2>/dev/null
#If the patch has not been applied then the $? which is the exit status
#for last command would have a success status code = 0
if [ $? -eq 0 ];
then
    #apply the patch
    patch -p1 -N < $patchfile
fi

I then call my patches in the scripts section of the composer.json.

"scripts": {
        "post-drupal-scaffold-cmd": [
            "cd docroot && ../scripts/wny/update-htaccess.sh ../patches/htaccess-redirects.patch && cd -",
            "cd docroot && ../scripts/wny/update-htaccess.sh ../patches/htaccess-ssl.patch && cd -",
            "rm -f docroot/.htaccess.orig"
        ]
},

Here's what my patches/htaccess-redirects.patch looks like:

diff --git a/.htaccess b/.htaccess
index 4716fa1..14014ce 100644
--- a/.htaccess
+++ b/.htaccess
@@ -69,6 +69,28 @@
 <IfModule mod_rewrite.c>
   RewriteEngine on
 
+  # Rewrite all requests to the old domains to the new domain.
+  RewriteCond ^<<<MY DOMAIN HERE>>> [NC]
+  RewriteRule ^(.*)$ https://<<<MY NEW DOMAIN HERE>>>/$1 [L,R=301,NC]
+
+  # redirects for .com and .org domains
+  RewriteCond %{HTTP_HOST} ^<<<MY DOMAIN HERE>>> [OR]
+  RewriteCond %{HTTP_HOST} ^<<<MY DOMAIN HERE>>>[OR]
+  RewriteCond %{HTTP_HOST} ^<<<MY DOMAIN HERE>>> [OR]
+  RewriteCond %{HTTP_HOST} ^<<<MY DOMAIN HERE>>> [OR]
+  RewriteCond %{HTTP_HOST} ^<<<MY DOMAIN HERE>>> [OR]
+  RewriteCond %{HTTP_HOST} ^<<<MY DOMAIN HERE>>> [OR]
+  RewriteCond %{HTTP_HOST} ^<<<MY DOMAIN HERE>>> [OR]
+  RewriteCond %{HTTP_HOST} ^<<<MY DOMAIN HERE>>> [OR]
+  RewriteCond %{HTTP_HOST} ^<<<MY DOMAIN HERE>>>
+  RewriteRule ^(.*)$ https://<<<MY NEW DOMAIN HERE>>>/$1 [R=permanent,L]
+  # Redirect to HTTPS for Production Sites
+  RewriteCond %{HTTPS} off
+  RewriteCond %{HTTP_HOST} ^<<<MY DOMAIN HERE>>>$ [NC]
+  RewriteCond %{HTTP:X-Forwarded-Proto} !https
+  RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
+
   # Set "protossl" to "s" if we were accessed via https://.  This is used later
   # if you enable "www." stripping or enforcement, in order to ensure that
   # you don't bounce between http and https.

@grasmash I'm going to try and add this to the documentation somewhere but do you think it's worth adding a version of my shell script above to the project?

@snize
Copy link
Contributor

snize commented Apr 14, 2017

May be needed with no prefix option for generating patch . git diff --no-prefix > patches/htaccess-redirects.patch :)

@damienmckenna
Copy link

I'm guessing that doesn't work because the .htaccess file is moved out of the /docroot/core/ folder (where composer downloads all of Drupal core to) and into /docroot instead.

This one of the the drupal "scaffolding" files that the drupal-composer/drupal-scaffold project handles for you.

This appears to be the root of the issue. When you run "composer update drupal/core" and attempt to patch the .htaccess file, that file doesn't exist yet as Composer actually copies the .htaccess file from core/assets/scaffold/files/htaccess. So what you have to do is create the patch against core/assets/scaffold/files/htaccess.

Put together, you end up with something like this in the composer.json file:

        "patches": {
            "drupal/core": {
                "Changes for .htaccess.": "patches/core_htaccess.patch"
            }
        }

Then the patches/core_htaccess.patch file looks like this:

diff --git a/core/assets/scaffold/files/htaccess b/core/assets/scaffold/files/htaccess
index 9a73a3d3a..5275068f6 100644
--- a/core/assets/scaffold/files/htaccess
+++ b/core/assets/scaffold/files/htaccess
@@ -109,8 +109,8 @@ AddEncoding gzip svgz
   # To redirect all users to access the site WITHOUT the 'www.' prefix,
   # (http://www.example.com/foo will be redirected to http://example.com/foo)
   # uncomment the following:
-  # RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
-  # RewriteRule ^ http%{ENV:protossl}://%1%{REQUEST_URI} [L,R=301]
+    RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
+    RewriteRule ^ http%{ENV:protossl}://%1%{REQUEST_URI} [L,R=301]
 
   # Modify the RewriteBase if you are using Drupal in a subdirectory or in a
   # VirtualDocumentRoot and the rewrite rules are not working properly.

When you next run composer update drupal/core it will download core, apply the patch, then copy the newly patched core/assets/scaffold/files/htaccess to .htaccess, and life will be good.

@philltran
Copy link
Contributor

philltran commented Jan 14, 2021

After I applied the post-drupal-scaffold-cmd patch method above, a co-worker pointed out the append feature in drupal scaffold. So, if you just need to add come rules to the end of the .htaccess this may be an easier method than managing a patch on an gitignored file.

  "extra": {
    "drupal-scaffold": {
      "file-mapping": {
        "[web-root]/.htaccess": {
          "append": "htaccess-additions.txt"
        }
      },
      ...
    }
  }

@pf-tech
Copy link

pf-tech commented Jan 18, 2024

I know this is an old post, but my Googling hasn't revealed any answers about the syntax of a htaccess.patch

how much of

diff --git a/.htaccess b/.htaccess
index 4716fa1..14014ce 100644
--- a/.htaccess
+++ b/.htaccess
@@ -69,6 +69,28 @@
<IfModule mod_rewrite.c>
RewriteEngine on

+ # Rewrite all requests to the old domains to the new domain.
+ RewriteCond ^<<<MY DOMAIN HERE>>> [NC]
+ RewriteRule ^(.*)$ https://<<<MY NEW DOMAIN HERE>>>/$1 [L,R=301,NC]
+
+ # redirects for .com and .org domains
+ RewriteCond %{HTTP_HOST} ^<<<MY DOMAIN HERE>>> [OR]
+ RewriteCond %{HTTP_HOST} ^<<<MY DOMAIN HERE>>>[OR]
+ RewriteCond %{HTTP_HOST} ^<<<MY DOMAIN HERE>>> [OR]
+ RewriteCond %{HTTP_HOST} ^<<<MY DOMAIN HERE>>> [OR]
+ RewriteCond %{HTTP_HOST} ^<<<MY DOMAIN HERE>>> [OR]
+ RewriteCond %{HTTP_HOST} ^<<<MY DOMAIN HERE>>> [OR]
+ RewriteCond %{HTTP_HOST} ^<<<MY DOMAIN HERE>>> [OR]
+ RewriteCond %{HTTP_HOST} ^<<<MY DOMAIN HERE>>> [OR]
+ RewriteCond %{HTTP_HOST} ^<<<MY DOMAIN HERE>>>
+ RewriteRule ^(.*)$ https://<<<MY NEW DOMAIN HERE>>>/$1 [R=permanent,L]
+ # Redirect to HTTPS for Production Sites
+ RewriteCond %{HTTPS} off
+ RewriteCond %{HTTP_HOST} ^<<<MY DOMAIN HERE>>>$ [NC]
+ RewriteCond %{HTTP:X-Forwarded-Proto} !https
+ RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
+
# Set "protossl" to "s" if we were accessed via https://. This is used later
# if you enable "www." stripping or enforcement, in order to ensure that
# you don't bounce between http and https.

Is needed if you wanted to append

<IfModule mod_headers.c>
AddType application/javascript .mjs

<FilesMatch "\.(?i:pdf)$>
ForceType application/octet-stream
Header set Content-Disposition "attachment"
</FilesMatch>
</IfModule>

at the end of your .htaccess file?

My composer.json sees my patch, and the scaffold command runs fine, but I get an error saying the text is garbage, and I know that is because I don't have the correct syntax. I've never built a .htaccess patch before. Any help would be appreciated.

@philltran
Copy link
Contributor

For appending, the text file contains only the text that gets added. So in your case the file would just have

<IfModule mod_headers.c>
  AddType application/javascript .mjs
 
  <FilesMatch "\.(?i:pdf)$>
    ForceType application/octet-stream
    Header set Content-Disposition "attachment"
  </FilesMatch>
</IfModule>

and in your composer.json

 "extra": {
        "drupal-scaffold": {
            "gitignore": true,
            "locations": {
                "web-root": "docroot/"
            },
                "[web-root]/.htaccess": {
                    "append": "my-htaccess-additions.txt"
                },
            }
        },

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Support A support request
Projects
None yet
Development

No branches or pull requests

8 participants