How to pass GitHub action event hook from a javascript to a bash file? - github

I want to create a GitHub action that is simple and only run a bash-script file, see previous
question: How to execute a bash-script from a java script
With this javascript action, I want to pass values to the bash-script from the JSON payload given by GitHub.
Can this be done with something as simple as an exec command?
...
exec.exec(`export FILEPATH=${filepath}`)
...

I wanted to do something like this, but found there to be much more code needed that I originally expected. So while this is not simple, it does work and will block the action script while the bash script runs:
const core = require('#actions/core');
function run() {
try {
// This is just a thin wrapper around bash, and runs a file called "script.sh"
//
// TODO: Change this to run your script instead
//
const script = require('path').resolve(__dirname, 'script.sh');
var child = require('child_process').execFile(script);
child.stdout.on('data', (data) => {
console.log(data.toString());
});
child.on('close', (code) => {
console.log(`child process exited with code ${code}`);
process.exit(code);
});
}
catch (error) {
core.setFailed(error.message);
}
}
run()
Much of the complication is handling output and error conditions.
You can see my debugger-action repo for an example.

Related

How can I prevent a Gulp task with a dynamic import from being asynchronous?

I want to use gulp-imagemin to minify images. The relevant part of my gulpfile.js looks like this:
const gulp = require('gulp');
// a couple more require('')s
function minifyImages(cb) {
import('gulp-imagemin')
.then(module => {
const imagemin = module.default;
gulp.src('src/img/**/*')
.pipe(imagemin())
.pipe(gulp.dest('img'));
cb();
})
.catch(err => {
console.log(err);
cb();
});
}
function buildCSS(cb) { /* ... */ }
exports.build = gulp.series(buildCSS, minifyImages);
The reason I'm using a dynamic import here is because I think I have to - gulp-imagemin doesn't support the require('') syntax, and when I say import imagemin from 'gulp-imagemin I get an error saying "Cannot use import statement outside a module".
I would expect the build task to only finish after minifyImages has finished. After all, I'm calling cb() only at the very end, at a point where the promise should be resolved.
However, build seems to finish early, while minifyImages is still running. This is the output I get:
[21:54:47] Finished 'buildCSS' after 6.82 ms
[21:54:47] Starting 'minifyImages'...
[21:54:47] Finished 'minifyImages' after 273 ms
[21:54:47] Finished 'build' after 282 ms
<one minute later>
[21:55:49] gulp-imagemin: Minified 46 images (saved 5.91 MB - 22.8%)
How can I make sure the task doesn't finish early, and all tasks are run in sequence?
Let me know if there's something wrong with my assumptions; I'm somewhat new to gulp and importing.
Streams are always asynchronous, so if the cb() callback is called just after creating the gulp stream as in your then handler, it's only obvious that the stream by that time has not finished yet (in fact, it hasn't even started).
The simplest solution to call a callback when the gulp.dest stream has finished is using stream.pipeline, i.e.:
function minifyImages(cb) {
const { pipeline } = require('stream');
return import('gulp-imagemin')
.then(module => {
const imagemin = module.default;
pipeline(
gulp.src('src/img/**/*'),
imagemin(),
gulp.dest('img'),
cb
);
})
.catch(cb);
}
Or similarly, with an async function.
async function minifyImages(cb) {
const { pipeline } = require('stream');
const { default: imagemin } = await import('gulp-imagemin');
return pipeline(
gulp.src('src/img/**/*'),
imagemin(),
gulp.dest('img'),
cb
);
}
Another approach I have seen is to split the task in two sequential sub-tasks: the first sub-task imports the plugin module and stores it in a variable, and the second sub-task uses the plugin already loaded by the previous sub-task to create and return the gulp stream in the usual way.
Then the two sub-tasks can be combined with gulp.series.

VSCode Extension executes local python (bash) code and append to output channel

In my VS Code extension, I'd like to invoke a python file and append standard output to VSCode's output channel. I'm aware that I can create an output channel via vscode.window.createOutputChannel("..."); and I'm also aware I can execute such python (or for this matter, any local bash) script via these APIs: https://nodejs.org/api/child_process.html. Would it be possible to append standard out information to this output channel in real-time (i.e., as the script runs)?
What I currently have is the following:
export function execute(cmd: string, callback: any, logging?: vscode.OutputChannel) {
const spwanedProcess = spawn(cmd, [], {shell: true, detached: true});
console.log(`spawned pid ${spwanedProcess.pid} with command ${cmd}`);
spwanedProcess.stdout.on('data', (data: any) => {
console.log(data);
logging?.appendLine("stdout:" + data);
});
spwanedProcess.stderr.on('data', (data: any) => {
console.error(`spawned pid ${spwanedProcess.pid} pushed something to stderr`);
logging?.appendLine(data);
});
spwanedProcess.on('exit', function(code: any) {
if (code !== 0) {
console.log('Failed: ' + code);
}
else {
console.log(`pid ${spwanedProcess.pid} finished`);
}
callback();
});
}
where callback() is something to be executed after the execution is done. I got this structure from here https://stackoverflow.com/a/32872753/14264786.
However, when I run a simple python code that sleeps for 3 seconds and prints something and does this again, the standard out information is still not displayed on the logging in real-time. Instead, they are outputted together after the script finishes.
Any idea on what's a potential solution?
After some more digging, I found out that one solution is to use python -u to invoke the python script to unbuffer the python output

How can I extend the window reuse code paths in vscode and Azure Data Studio?

I am working in the microsoft/azuredatastudio github repo, which is largely forked from vscode. I am trying to extend our command line processing to handle the window reuse parameter such that if we pass a server connection along with -r that we will open the specified connection. Our current command line processing service is loaded by src\vs\workbench\electron-browser\workbench.ts in Workbench.initServices.
Is there any platform-provided service that is visible to both electron-main and workbench\electron-browser that I could modify or leverage to be informed of the app being reused with new command line arguments?
I've found that the LaunchService defined in src\vs\code\electron-main\launch.ts appears to be responsible for capturing the arguments and opening or reusing the window, but it's not clear how I would marshal a notification from the LaunchService over to our services that are loaded by workbench.
2/12/2019 update:
It looks like I need to add an equivalent of this function in src\vs\code\electron-main\windows.ts
private doOpenFilesInExistingWindow(configuration: IOpenConfiguration, window: ICodeWindow, filesToOpen: IPath[], filesToCreate: IPath[], filesToDiff: IPath[], filesToWait: IPathsToWaitFor): ICodeWindow {
window.focus(); // make sure window has focus
window.ready().then(readyWindow => {
const termProgram = configuration.userEnv ? configuration.userEnv['TERM_PROGRAM'] : void 0;
readyWindow.send('vscode:openFiles', { filesToOpen, filesToCreate, filesToDiff, filesToWait, termProgram });
});
return window;
}
which has a new message like 'ads:openconnection' . Now to find out how to handle the message.
I ended up using ipcRenderer service and adding an ipc call to the launch service in main.
// {{SQL CARBON EDIT}}
// give the first used window a chance to process the other command line arguments
if (args['reuse-window'] && usedWindows.length > 0 && usedWindows[0])
{
let window = usedWindows[0];
usedWindows[0].ready().then(() => window.send('ads:processCommandLine', args));
}
// {{SQL CARBON EDIT}}

Nothing happens using spawn gulp task to execute commands into subfolder

Yesterday, I've been sent to a reference to use process_child.spawn for my need. I'd like gulp executing commands for me, to avoid typing commands concerning my dependency when I need to compile my main project.
I got something that seems ok, any error into logs, but nothing happened, the way my commands wouldn't been executed.
Any one with feedback about my code ? I got another task like this one to compile the dependency.
var spawn = require("child_process").spawn;
gulp.task("my-dependency-install", function(done) {
spawn("ft", ["install"], {
cwd: "node_modules/app/my-dependency/"
})
.on("error", function (err) {
throw err
})
.on("close", done);
});
Thanks
Here is the way I've fixed it :
var spawn = require("child_process").spawn;
spawn("ft.cmd", ["install"], {
cwd: "node_modules/app/my-dependency/"
})
.on("error", function (err) {
throw err
});
Anyone is able to explain why I had to add .cmd ? It's because of windows OS, isn'it ?

How to run ant from an Eclipse plugin, send output to an Eclipse console, and capture the build result (success/failure)?

From within an Eclipse plugin, I'd like to run an Ant build script. I also want to display the Ant output to the user, by displaying it in an Eclipse console. Finally, I also want to wait for the Ant build to be finished, and capture the result: did the build succeed or fail?
I found three ways to run an Ant script from eclipse:
Instantiate an org.eclipse.ant.core.AntRunner, call some setters and call run() or run(IProgressMonitor). The result is either normal termination (indicating success), or a CoreException with an IStatus containing a BuildException (indicating failure), or else something else went wrong. However, I don't see the Ant output anywhere.
Instantiate an org.eclipse.ant.core.AntRunner and call run(Object), passing a String[] containing the command line arguments. The result is either normal termination (indication success), or an InvocationTargetException (indicating failure), or else something else went wrong. The Ant output is sent to Eclipse's stdout, it seems; it is not visible in Eclipse itself.
Call DebugPlugin.getDefault().getLaunchManager(), then on that call getLaunchConfigurationType(IAntLaunchConfigurationConstants.ID_ANT_BUILDER_LAUNCH_CONFIGURATION_TYPE), then on that set attribute "org.eclipse.ui.externaltools.ATTR_LOCATION" to the build file name (and attribute DebugPlugin.ATTR_CAPTURE_OUTPUT to true) and finally call launch(). The Ant output is shown in an Eclipse console, but I have no idea how to capture the build result (success/failure) in my code. Or how to wait for termination of the launch, even.
Is there any way to have both console output and capture the result?
Edit 05/16/2016 #Lii alerted me to the fact that any output between the ILaunchConfigurationWorkingCopy#launch call and when the IStreamListener is appended will be lost. He made a contribution to this answer here.
Original Answer
I realize this is an old post, but I was able to do exactly what you want in one of my plugins. If it doesn't help you at this point, maybe it will help someone else. I originally did this in 3.2, but it has been updated for 3.6 API changes...
// show the console
final IWorkbenchPage activePage = PlatformUI.getWorkbench()
.getActiveWorkbenchWindow()
.getActivePage();
activePage.showView(IConsoleConstants.ID_CONSOLE_VIEW);
// let launch manager handle ant script so output is directed to Console view
final ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager();
ILaunchConfigurationType type = manager.getLaunchConfigurationType(IAntLaunchConstants.ID_ANT_LAUNCH_CONFIGURATION_TYPE);
final ILaunchConfigurationWorkingCopy workingCopy = type.newInstance(null, [*** GIVE YOUR LAUNCHER A NAME ***]);
workingCopy.setAttribute(ILaunchManager.ATTR_PRIVATE, true);
workingCopy.setAttribute(IExternalToolConstants.ATTR_LOCATION, [*** PATH TO ANT SCRIPT HERE ***]);
final ILaunch launch = workingCopy.launch(ILaunchManager.RUN_MODE, null);
// make sure the build doesnt fail
final boolean[] buildSucceeded = new boolean[] { true };
((AntProcess) launch.getProcesses()[0]).getStreamsProxy()
.getErrorStreamMonitor()
.addListener(new IStreamListener() {
#Override
public void streamAppended(String text, IStreamMonitor monitor) {
if (text.indexOf("BUILD FAILED") > -1) {
buildSucceeded[0] = false;
}
}
});
// wait for the launch (ant build) to complete
manager.addLaunchListener(new ILaunchesListener2() {
public void launchesTerminated(ILaunch[] launches) {
boolean patchSuccess = false;
try {
if (!buildSucceeded[0]) {
throw new Exception("Build FAILED!");
}
for (int i = 0; i < launches.length; i++) {
if (launches[i].equals(launch)
&& buildSucceeded[0]
&& !((IProgressMonitor) launches[i].getProcesses()[0]).isCanceled()) {
[*** DO YOUR THING... ***]
break;
}
}
} catch (Exception e) {
[*** DO YOUR THING... ***]
} finally {
// get rid of this listener
manager.removeLaunchListener(this);
[*** DO YOUR THING... ***]
}
}
public void launchesAdded(ILaunch[] launches) {
}
public void launchesChanged(ILaunch[] launches) {
}
public void launchesRemoved(ILaunch[] launches) {
}
});
I'd like to add one thing to happytime harry's answer.
Sometimes the first writes to the stream happens before the stream listener is added. Then streamAppended on the listener is never called for those writes so output is lost.
See for example this bug. I think happytime harry's solution might have this problem. I myself registered my stream listener in ILaunchListener.launchChanged and this happened 4/5 times.
If one wants to be sure to get all the output from a stream then the IStreamMonitor.getContents method can be used to fetch the output that happened before the listener got added.
The following is an attempt on a utility method that handles this. It is based on the code in ProcessConsole.
/**
* Adds listener to monitor, and calls listener with any content monitor already has.
* NOTE: This methods synchronises on monitor while listener is called. Listener may
* not wait on any thread that waits for monitors monitor, what would result in dead-lock.
*/
public static void addAndNotifyStreamListener(IStreamMonitor monitor, IStreamListener listener) {
// Synchronise on monitor to prevent writes to stream while we are adding listener.
// It's weird to synchronise on monitor because that's a shared object, but that's
// what ProcessConsole does.
synchronized (monitor) {
String contents = monitor.getContents();
if (!contents.isEmpty()) {
// Call to unknown code while synchronising on monitor. This is dead-lock prone!
// Listener must not wait for other threads that are waiting in line to
// synchronise on monitor.
listener.streamAppended(contents, monitor);
}
monitor.addListener(listener);
}
}
PS: There is some weird stuff going on in ProcessConsole.java. Why is the content buffering switched of from the ProcessConsole.StreamListener constructor?! If the ProcessConsole.StreamListener runs before this one maybe this solution doesn't work.