Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 162 additions & 31 deletions Build/gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,85 @@ const jeditor = require('gulp-json-editor');
const fs = require('fs');
const path = require('path');
const del = require('del');
const decompress = require('gulp-decompress');
const { exec } = require('child_process');
const zip = require('gulp-zip');
const request = require('request');
const https = require('https');
const nuget = require('gulp-nuget');
const { spawn, execSync } = require('child_process');

buildVersion = '1';

if (process.env.devops_buildNumber) {
buildVersion = process.env.devops_buildNumber;
}

gulp.task('nuget-pack', function () {
// Helper function to find available NuGet executable
function findNugetExecutable() {
// On Windows, prefer local nuget.exe first
if (process.platform === 'win32') {
if (fs.existsSync('./nuget.exe')) {
return './nuget.exe';
}
}

// Check for system-installed nuget
try {
const command = process.platform === 'win32' ? 'where nuget' : 'which nuget';
execSync(command, { stdio: 'ignore' });
return 'nuget';
} catch (e) {
// nuget not found in PATH
}

// Check for dotnet (can be used as alternative)
try {
const command = process.platform === 'win32' ? 'where dotnet' : 'which dotnet';
execSync(command, { stdio: 'ignore' });
return 'dotnet';
} catch (e) {
// dotnet not found in PATH
}

return null;
}

// Helper function to check if NuGet packaging is available
function isNugetAvailable() {
const nugetExe = findNugetExecutable();
if (!nugetExe) {
return false;
}

// For dotnet, we need to check if pack command is available
if (nugetExe === 'dotnet') {
try {
execSync('dotnet pack --help', { stdio: 'ignore' });
return true;
} catch (e) {
return false;
}
}

return true;
}

gulp.task('nuget-pack', function (done) {
// Check if NuGet is available
if (!isNugetAvailable()) {
console.log('NuGet not available on this system. Skipping nuget-pack task.');
console.log('To enable NuGet packaging, install:');
console.log(' - Windows: nuget.exe will be downloaded automatically');
console.log(' - macOS: brew install nuget');
console.log(' - Linux: Install .NET SDK (dotnet command)');
return done();
}

let streams = [];
var nugetPath = './nuget.exe';
var nugetPath = findNugetExecutable();
var patchVersion = 'patchVersion=' + buildVersion

console.log(`Using NuGet executable: ${nugetPath}`);

let files = getFiles('./PackageFiles/Dotnet_precompiled');
for (let i = 0; i < files.length; i++) {
let filePath = './PackageFiles/Dotnet_precompiled/' + files[i];
Expand All @@ -42,6 +105,10 @@ gulp.task('nuget-pack', function () {
)
}

if (streams.length === 0) {
return done();
}

return gulpMerge(streams);
});

Expand All @@ -50,13 +117,26 @@ gulp.copy = function (src, dest) {
};

gulp.task('nuget-download', function (done) {
// Skip download on non-Windows platforms - rely on system nuget
if (process.platform !== 'win32') {
console.log('Skipping nuget.exe download on non-Windows platform');
return done();
}

if (fs.existsSync('nuget.exe')) {
return done();
}

request.get('https://dist.nuget.org/win-x86-commandline/v6.0.0/nuget.exe')
.pipe(fs.createWriteStream('nuget.exe'))
.on('close', done);
const file = fs.createWriteStream('nuget.exe');
https.get('https://dist.nuget.org/win-x86-commandline/v6.0.0/nuget.exe', (response) => {
response.pipe(file);
file.on('finish', () => {
file.close(done);
});
}).on('error', (err) => {
fs.unlink('nuget.exe', () => {}); // Delete the file on error
done(err);
});
});

gulp.task('clean-output', function (cb) {
Expand All @@ -77,30 +157,56 @@ gulp.task('clean-temp', function (cb) {
], { force: true });
});

gulp.task('unzip-templates', function () {
let streams = [];

gulp.task('unzip-templates', function (done) {
let files = getFiles('../bin/Temp/ExtensionBundle');
for (let i = 0; i < files.length; i++) {
let filePath = '../bin/Temp/Temp-' + files[i].replace(".nupkg", "")

streams.push(
gulp
.src(path.join('../bin/Temp/ExtensionBundle', files[i]))
.pipe(decompress())
.pipe(gulp.dest(filePath))
);
console.log('Found ExtensionBundle files:', files);

if (files.length === 0) {
console.log('No files to extract');
return done();
}

return gulpMerge(streams);
let completedFiles = 0;
const totalFiles = files.length;

files.forEach((file) => {
const sourceFile = path.resolve('../bin/Temp/ExtensionBundle', file);
const targetDir = path.resolve('../bin/Temp/Temp-' + file.replace('.nupkg', ''));

// Create target directory if it doesn't exist
if (!fs.existsSync(targetDir)) {
fs.mkdirSync(targetDir, { recursive: true });
}

console.log(`Extracting ${file} to ${targetDir}`);

// Use system unzip command which is more reliable for nupkg files
const unzipCmd = process.platform === 'win32'
? `powershell -command "Expand-Archive -Path '${sourceFile}' -DestinationPath '${targetDir}' -Force"`
: `unzip -q -o "${sourceFile}" -d "${targetDir}"`;

exec(unzipCmd, (error, stdout, stderr) => {
if (error) {
console.error(`Error extracting ${file}:`, error.message);
} else {
console.log(`Successfully extracted ${file}`);
}

completedFiles++;
if (completedFiles === totalFiles) {
console.log('All extractions completed');
done();
}
});
});
});

/********
* This task takes the Resource Resx files from both templates folder and Portal Resources Folder and converts them to json, it drops them into a intermediate 'convert' folder.
* Also it will change the file name format to Resources.<language code>.json
*/

gulp.task('resources-convert', function () {
gulp.task('resources-convert', function (done) {
const streams = [];

let files = getFiles('../bin/Temp/ExtensionBundle');
Expand Down Expand Up @@ -128,13 +234,18 @@ gulp.task('resources-convert', function () {
}))
.pipe(gulp.dest(convertPath)));
}

if (streams.length === 0) {
return done();
}

return gulpMerge(streams);
});

/********
* This is the task takes the output of the convert task and formats the json to be in the format that gets sent back to the client by the API, it's easier to do this here than at the end
*/
gulp.task('resources-build', function () {
gulp.task('resources-build', function (done) {
const streams = [];
let files = getFiles('../bin/Temp/ExtensionBundle');
for (let i = 0; i < files.length; i++) {
Expand Down Expand Up @@ -210,10 +321,15 @@ gulp.task('resources-build', function () {
.pipe(gulp.dest('../bin/Temp/out/' + fileName + '/resources-v2'))
);
}

if (streams.length === 0) {
return done();
}

return gulpMerge(streams);
});

gulp.task('resources-copy', function () {
gulp.task('resources-copy', function (done) {
const streams = [];
let files = getFiles('../bin/Temp/ExtensionBundle');
for (let i = 0; i < files.length; i++) {
Expand All @@ -226,21 +342,26 @@ gulp.task('resources-copy', function () {
}

streams.push(
gulp.src('../bin/Temp/out/' + fileName + '/resources/Resources.json')
gulp.src('../bin/Temp/out/' + fileName + '/resources/Resources.json', { allowEmpty: true })
.pipe(rename('Resources.en-US.json'))
.pipe(gulp.dest('../bin/Temp/out/' + fileName + '/resources'))
);

streams.push(
gulp.src('../bin/Temp/out/' + fileName + '/resources/Resources.json')
gulp.src('../bin/Temp/out/' + fileName + '/resources/Resources.json', { allowEmpty: true })
.pipe(rename('Resources.en-US.json'))
.pipe(gulp.dest('../bin/Temp/out/' + fileName + '/resources-v2'))
);
}

if (streams.length === 0) {
return done();
}

return gulpMerge(streams);
});

gulp.task('userprompt-copy', function () {
gulp.task('userprompt-copy', function (done) {
const streams = [];
let files = getFiles('../bin/Temp/ExtensionBundle');
for (let i = 0; i < files.length; i++) {
Expand All @@ -258,6 +379,11 @@ gulp.task('userprompt-copy', function () {
.pipe(gulp.dest('../bin/Temp/out/' + fileName + '/bindings-v2'))
);
}

if (streams.length === 0) {
return done();
}

return gulpMerge(streams);
});

Expand Down Expand Up @@ -306,9 +432,9 @@ gulp.task('build-templates', function (cb) {
}
writePath = path.join(writePath, 'templates.json');
fs.writeFileSync(writePath, Buffer.from(JSON.stringify(templateListJson, null, 2)));
cb();

}
cb();
});

gulp.task('build-templates-v2', function (cb) {
Expand Down Expand Up @@ -347,9 +473,9 @@ gulp.task('build-templates-v2', function (cb) {
}
writePath = path.join(writePath, 'templates.json');
fs.writeFileSync(writePath, Buffer.from(JSON.stringify(templateListJson, null, 2)));
cb();

}
cb();
});

/********
Expand Down Expand Up @@ -393,7 +519,7 @@ gulp.task('build-bindings', function (cb) {
cb();
});

gulp.task('zip-output', function () {
gulp.task('zip-output', function (done) {
let dirs = getSubDirectories("../bin/Temp/out")
let streams = [];

Expand All @@ -404,6 +530,11 @@ gulp.task('zip-output', function () {
.pipe(gulp.dest('../bin/'))
);
}

if (streams.length === 0) {
return done();
}

return gulpMerge(streams);
});

Expand Down Expand Up @@ -457,7 +588,7 @@ function getFilesWithContent(folder, filesToIgnore) {

function getFiles(folder) {
if (!fs.existsSync(folder)) {
return {};
return [];
}
return fileNames = fs.readdirSync(folder).filter(f => fs.statSync(path.join(folder, f)).isFile());
return fs.readdirSync(folder).filter(f => fs.statSync(path.join(folder, f)).isFile());
}
Loading