mkloubert / vs-deploy

Visual Studio Code extension that provides commands to deploy files of a workspace to a destination.
https://marketplace.visualstudio.com/items?itemName=mkloubert.vs-deploy
MIT License
131 stars 24 forks source link

Help me with beforeDeploy #33

Closed LexRiver closed 7 years ago

LexRiver commented 7 years ago

I'm trying to compile .pug templates to .html before deploy. I have a script which I run like node build.js and it works good. But when I'm trying to use it in section beforeDeploy nothing happens.

here is my working script:

            var fs = require('fs');
            var pug = require('pug');
            var glob = require('glob');
            var path = require('path');

            console.log('building *.pug');

            glob('*.pug', {}, function(error, files){
                files.forEach(function(file, index){
                    var htmlFilename = path.basename(file, '.pug')+'.html';
                    console.log(file+' => '+htmlFilename);
                    fs.writeFile(htmlFilename, pug.renderFile(file, {pretty: true}));
                });
            });

and here is my beforeDeploy part:

                "beforeDeploy": [
                    {
                        "type": "script",
                        "script": "./build.js"
                    }
                ]

and here is my build.js script for vs-deploy:

console.log('test');

function execute(args) {
    console.log('execute starts');
    return new Promise(function(resolve, reject) {
        try {
            // do the MAGIC here

            // from the configuration example
            // above 'args.options' would contain
            // 5979 as value

            var fs = require('fs');
            var pug = require('pug');
            var glob = require('glob');
            var path = require('path');

            console.log('building *.pug');

            glob('*.pug', {}, function(error, files){
                files.forEach(function(file, index){
                    var htmlFilename = path.basename(file, '.pug')+'.html';
                    console.log(file+' => '+htmlFilename);
                    fs.writeFile(htmlFilename, pug.renderFile(file, {pretty: true}));
                });
                resolve();  // tell that anything went fine
            });

        }
        catch (e) {
            reject(e); // Oops, an error!
        }
    });
}
exports.execute = execute;

So I have no console outputs, no compilation of my *.pug and no errors:

[BEFORE DEPLOY #1] 
Deploying file ..... [OK]
Finished

So what I'm doing wrong?

mkloubert commented 7 years ago

@LexRiver

Try to wrap the code with try-catch-blocks and see if an error messages is shown as popup:

var vscode = require('vscode');

try {
    var fs = require('fs');
    var pug = require('pug');
    var glob = require('glob');
    var path = require('path');

    // console.log('building *.pug');
    vscode.window.showInformationMessage('build.js: building *.pug');

    glob('*.pug', {}, function(error, files){
        try {
            files.forEach(function(file, index) {
                var htmlFilename = path.basename(file, '.pug')+'.html';
                console.log(file+' => '+htmlFilename);
                fs.writeFile(htmlFilename, pug.renderFile(file, {pretty: true}));
            });

            resolve();  // tell that anything went fine
        }
        catch (e) {
            vscode.window.showErrorMessage('build.js.forEach(): ' + e);

            reject(e);
        }
    });
}
catch (e) {
    vscode.window.showErrorMessage('build.js: ' + e);

    reject(e);
}

I think the problem is that you have to link the pug module inside your project folder. It is not part of the extension.

For console.log you have to look at the debug console (CTRL + SHIFT + Y).

mkloubert commented 7 years ago

@LexRiver

I had the problem, after I removed the pug part, the script told me that glob could not be loaded.

The problem is that this is no NodeJS API / VSCode module.

After I replaced

var glob = require('glob');

with

var glob = args.require('glob');

it works.

The require method of args loads modules from the script / extension context, what means that you are also able to load modules that are part of the extension (and not only the VSCode / NodeJS ones).

These are the modules, the extension currently uses (s. package.json)

{
  "dependencies": {
    "aws-sdk": "^2.7.19",
    "azure-storage": "^1.4.0",
    "fs-extra": "^1.0.0",
    "ftp": "^0.3.10",
    "glob": "^7.1.1",
    "html-entities": "^1.2.0",
    "i18next": "^4.1.4",
    "isbinaryfile": "^3.0.2",
    "less": "^2.7.2",
    "mime": "^1.3.4",
    "moment": "^2.17.1",
    "mssql": "^3.3.0",
    "mysql": "^2.12.0",
    "node-uuid": "^1.4.7",
    "node-zip": "^1.1.1",
    "nodemailer": "^2.7.0",
    "ssh2-sftp-client": "^1.0.5",
    "typescript": "^2.1.5"
  }
}

BTW: If you want to include own modules, you have to the NodeJS way:

// relative to the 'build.js' path
var myModule = require('./my-module.js');
mkloubert commented 7 years ago

@LexRiver

Now it seems to work.

Open your terminal / command line, go to the folder where your build.js is stored and execute

npm link pug

It will create a node_modules subfolder with the module there and you can use it by

var pug = require('pug');
LexRiver commented 7 years ago

@mkloubert Thank you, compilation of *.pug files works now, but what are the correct settings to deploy these files to server?

Seems like they are deploying only on Ctrl+Alt+W, but not on Ctr+Alt+F

LexRiver commented 7 years ago

@mkloubert If you could add some option like

that will be really great.

mkloubert commented 7 years ago

@LexRiver

There is still a deploy on change feature :-)

But before we start: Upgrade at least to version 5.19.1

1. We have to compile the PUG files to HTML

Create a package that tracks the .pug files:

{
    "deploy": {
        // ...

        "packages": [
            // ...

            {
                "name": "PUG to HTML",
                "description": "The files that should be compiled to HTML files.",

                "files": [
                    "/**/*.pug"
                ],

                "deployOnSave": [ "PUG Compiler" ]
            }

            // ...
        ]

        // ...
    }
}

This will send all .pug files to the following target, called PUG Compiler, automatically if you save one (or more):

{
    "deploy": {
        // ...

        "targets": [
            // ...

            {
                "name": "PUG Compiler",
                "description": "Compiles an HTML file to PUG",

                "type": "script",

                "script": "./build.js"
            }

            // ...
        ]

        // ...
    }
}

This script target will use your ./build.js as target, but first you have to modify it, because it has another structure:


function deployFile(args) {
    return new Promise(function(resolve, reject) {
        var onCompleted = function(err) {
            if (err) {
                reject(err);
            }
            else {
                resolve(args);  // send input arguments
                                // back to the extension
            }
        };

        if (args.context.isCancelling()) {
            // cancellation request
            args.canceled = true;    // tell the extension
                                     // that we have cancelled

            onCompleted();
            return;
        }

        try {
            var fs = require('fs');
            var path = require('path');
            var pug = require('pug');

            var file = args.file;

            var htmlFilename = path.basename(file, '.pug') + '.html';
            var htmlFile = path.join(path.dirname(file),
                                     htmlFilename);  // I hope that HTML files
                                                     // should be created in
                                                     // same directory as PUGs

            if (args.deployOptions.onBeforeDeploy) {
                // use this to tell the extension that
                // we begin to deploy the file

                args.deployOptions.onBeforeDeploy(args.sender, {
                    destination: htmlFilename,  // this is only a string
                                                // to display in the output window
                    file: file,
                    target: args.target
                });
            }

            // render from PUG to HTML
            var pugData = pug.renderFile(file, {
                pretty: true
            });

            // write to HTML file async
            fs.writeFile(htmlFile, pugData, (err) => {
                onCompleted(err);  // tell extenstion that we have finished
            });
        }
        catch (e) {
            onCompleted(e);
        }
    });
}

exports.deployFile = deployFile;

Now, when you save PUG files, it automatically compiles then to HTML.

2. Automatically deploy HTML after compilation

When a HTML file has been generated / changed, you can use the deploy on change feature:

Add a second package that tracks changed HTML files:

{
    "deploy": {
        // ...

        "packages": [
            // ...

            {
                "name": "HTML from PUG",
                "description": "The files that should be deployed after compilation.",

                "files": [
                    "/**/*.html"
                ],

                "deployOnChange": true,

                "deployOnSave": [ "HTML target" ]
            }

            // ...
        ]

        // ...
    }
}

This will send all modifed / created HTML to the second target, called HTML target, automatically:

{
    "deploy": {
        // ...

        "targets": [
            // ...

            {
                "name": "HTML target",
                "description": "Deploys HTML files",

                "type": "test"
            }

            // ...
        ]

        // ...
    }
}

In my example I only used a mock target. Have a look at the wiki where all target types are listed.

LexRiver commented 7 years ago

@mkloubert Thank you very much, Marcel. That's works for me.

But small mistake in your code

           // render from PUG to HTML
            var pugData = pug.renderFile(htmlFilename, {
                pretty: true
            });

htmlFilename should be file

and that's is not obvious that it's possible to use targets in deployOnSave, I think you should mention this in wiki.

"deployOnSave": [ "HTML target" ]

and another proposal is to make this combination of two targets to work a little faster. When I save a .pug file it compiles it to .html and then waits for 1 or 2 seconds and only then deploying it. So maybe this delay could be reduced.

mkloubert commented 7 years ago

@LexRiver

Thats right, it needs a better documentation.

I described it here, but the better way is to write it down to a separate wiki article, were you find examples for such scenarios. I will do this the next days.

With the speed problem, you seem to have the same problem as @rshakespear

I realeased version 5.20.0 that should reduce the delay, as you can read here.

LexRiver commented 7 years ago

@mkloubert Another strange thing I've noticed. If I compile pug templates outside of vscode when this deployment is on I'm getting strange warning messages: Could not deploy '/file.html' to $'sftp' on save: Error: read ECONNRESET or Could not deploy '/file.html' to $'sftp' on save: Error: Timed out while waiting for handshake

sag3ll0 commented 7 years ago

Hi, before deploy a want call "gulp build" command for workspace, how i must write it in section "beforeDeploy": []

mkloubert commented 7 years ago

@sag3ll0

You can use the open operation type:

{
    "deploy": {
        // ...

        "targets": [
            {
                "name": "My target",
                "type": "test",

                "beforeDeploy": [
                    {
                        "type": "open",
                        "target": "gulp", "arguments": [ "build" ]
                    }
                ]
            }
        ]
    }
}

If the extension cannot execute your gulp, try to set the full path or create a npm link inside your workspace:

npm link gulp
sag3ll0 commented 7 years ago

it working, but it call new cmd window, that after operation not close. Can we use vs code terminal?

lvbeck commented 7 years ago

@LexRiver I had same Error: Timed out while waiting for handshake I found a solution by adding a config variable "readyTimeout" in connect() function of ssh2-sftp-client

in out\src\plugins\sftp.js, line 179, add readyTimeout: 99999

                    conn.connect({
                        host: host,
                        port: port,
                        username: user,
                        password: pwd,
                        privateKey: privateKey,
                        passphrase: target.privateKeyPassphrase,
                        hostHash: hashAlgo,
                        hostVerifier: (hashedKey, cb) => {
                            hashedKey = toHashSafe(hashedKey);
                            if (hashes.length < 1) {
                                return true;
                            }
                            return hashes.indexOf(hashedKey) > -1;
                        },
                        agent: agent,
                        agentForward: agentForward,
                        tryKeyboard: tryKeyboard,
            readyTimeout: 99999 // to fix handshake timeout issue
                    })
mkloubert commented 7 years ago

@lvbeck

Great! I will add and release a new version that can define that value in about 2 or 3 hours.

mkloubert commented 7 years ago

@lvbeck @LexRiver

Since version 5.32.0 there is now a readTimeout property:

{
    "deploy": {
        // ...

        "targets": [
            {
                "name": "My target",
                "type": "sftp",

                "readyTimeout": 99999
            }
        ]
    }
}
mkloubert commented 7 years ago

@sag3ll0

Sorry, when I answer so late ... since version 7.0.0 I have implemented the feature of running apps and "bash" scripts inside the integrated terminal (by setting runInTerminal property to (true) in the operation):

{
    "deploy": {
        // ...

        "targets": [
            {
                "name": "My target",
                "type": "test",

                "beforeDeploy": [
                    {
                        "type": "open",
                        "target": "gulp", "arguments": [ "build" ],

                        "runInTerminal": true
                    }
                ]
            }
        ]
    }
}