332 строки
12 KiB
JavaScript
332 строки
12 KiB
JavaScript
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
/* eslint-disable no-undef */
|
|
"use strict";
|
|
const util = require('util');
|
|
const nls = require('vscode-nls-dev');
|
|
const exec = util.promisify(require('child_process').exec);
|
|
const gulp = require('gulp');
|
|
const filter = require('gulp-filter');
|
|
const eslint = require('gulp-eslint');
|
|
const replace = require('gulp-replace');
|
|
const mocha = require('gulp-mocha');
|
|
const moment = require('moment');
|
|
const gulpWebpack = require('webpack-stream');
|
|
const webpack = require('webpack');
|
|
const vsce = require('vsce');
|
|
const argv = require('yargs').argv;
|
|
|
|
const fetch = require('node-fetch');
|
|
const fs = require('fs-extra');
|
|
const log = require('fancy-log');
|
|
const path = require('path');
|
|
const pslist = require('ps-list');
|
|
|
|
const webPackConfig = require('./webpack.config');
|
|
const distdir = path.resolve('./dist');
|
|
const outdir = path.resolve('./out');
|
|
const packagedir = path.resolve('./package');
|
|
const feedPAT = argv.feedPAT || process.env['AZ_DevOps_Read_PAT'];
|
|
|
|
async function clean() {
|
|
(await pslist())
|
|
.filter((info) => info.name.startsWith('pacTelemetryUpload'))
|
|
.forEach(info => {
|
|
log.info(`Terminating: ${info.name} - ${info.pid}...`)
|
|
process.kill(info.pid);
|
|
});
|
|
|
|
fs.emptyDirSync(outdir);
|
|
return fs.emptyDir(distdir);
|
|
}
|
|
|
|
function compile() {
|
|
return gulp
|
|
.src('src/**/*.ts')
|
|
.pipe(gulpWebpack(webPackConfig, webpack))
|
|
.pipe(replace("src\\\\client\\\\lib\\\\", "src/client/lib/")) // Hacky fix: vscode-nls-dev/lib/webpack-loader uses Windows style paths when built on Windows, breaking localization on Linux & Mac
|
|
.pipe(gulp.dest(distdir));
|
|
}
|
|
|
|
async function nugetInstall(nugetSource, packageName, version, targetDir) {
|
|
// https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource
|
|
const feeds = {
|
|
'nuget.org': {
|
|
authenticated: false,
|
|
baseUrl: 'https://api.nuget.org/v3-flatcontainer/'
|
|
},
|
|
'CAP_ISVExp_Tools_Daily': {
|
|
authenticated: true,
|
|
// https://dev.azure.com/msazure/One/_packaging?_a=feed&feed=CAP_ISVExp_Tools_Daily
|
|
baseUrl: 'https://pkgs.dev.azure.com/msazure/_packaging/d3fb5788-d047-47f9-9aba-76890f5cecf0/nuget/v3/flat2/'
|
|
},
|
|
'CAP_ISVExp_Tools_Stable': {
|
|
authenticated: true,
|
|
// https://dev.azure.com/msazure/One/_packaging?_a=feed&feed=CAP_ISVExp_Tools_Stable
|
|
baseUrl: 'https://pkgs.dev.azure.com/msazure/_packaging/b0441cf8-0bc8-4fad-b126-841a6184e784/nuget/v3/flat2/'
|
|
},
|
|
}
|
|
|
|
const selectedFeed = feeds[nugetSource];
|
|
const baseUrl = selectedFeed.baseUrl;
|
|
|
|
packageName = packageName.toLowerCase();
|
|
version = version.toLowerCase();
|
|
const packagePath = `${packageName}/${version}/${packageName}.${version}.nupkg`;
|
|
|
|
const nupkgUrl = new URL(packagePath, baseUrl);
|
|
const reqInit = {
|
|
headers: {
|
|
'User-Agent': 'gulpfile-DPX-team/0.1',
|
|
'Accept': '*/*'
|
|
},
|
|
redirect: 'manual'
|
|
};
|
|
if (selectedFeed.authenticated) {
|
|
if (!feedPAT) {
|
|
throw new Error(`nuget feed ${nugetSource} requires authN but neither '--feedToken' argument nor env var 'AZ_DevOps_Read_PAT' was defined!`);
|
|
}
|
|
reqInit.headers['Authorization'] = `Basic ${Buffer.from('PAT:' + feedPAT).toString('base64')}`;
|
|
}
|
|
|
|
log.info(`Downloading package: ${nupkgUrl}...`);
|
|
let res = await fetch(nupkgUrl, reqInit);
|
|
if (res.status === 303) {
|
|
const location = res.headers.get('location');
|
|
const url = new URL(location);
|
|
log.info(` ... redirecting to: ${url.origin}${url.pathname}}...`);
|
|
// AzDevOps feeds will redirect to Azure storage with location url w/ SAS token: on 2nd request drop authZ header
|
|
delete reqInit.headers['Authorization'];
|
|
res = await fetch(location, reqInit);
|
|
}
|
|
if (!res.ok) {
|
|
const body = res.body.read();
|
|
throw new Error(`Cannot download ${res.url}, status: ${res.statusText} (${res.status}), body: ${body ? body.toString('ascii') : '<empty>'}`);
|
|
}
|
|
|
|
const localNupkg = path.join(targetDir, `${packageName}.${version}.nupkg`);
|
|
fs.ensureDirSync(targetDir);
|
|
return new Promise((resolve, reject) => {
|
|
res.body.pipe(fs.createWriteStream(localNupkg))
|
|
.on('close', () => {
|
|
resolve();
|
|
}).on('error', err => {
|
|
reject(err);
|
|
})
|
|
});
|
|
}
|
|
|
|
function lint() {
|
|
return gulp
|
|
.src(['src/**/*.ts', __filename])
|
|
.pipe(eslint({
|
|
formatter: 'verbose',
|
|
configuration: '.eslintrc.js'
|
|
}))
|
|
.pipe(eslint.format())
|
|
.pipe(eslint.results(results => {
|
|
if (results.warningCount > 0){
|
|
throw new Error(`Found ${results.warningCount} eslint errors.`)
|
|
}
|
|
}))
|
|
.pipe(eslint.failAfterError());
|
|
}
|
|
|
|
function test() {
|
|
return gulp
|
|
.src(['src/server/test/unit/**/*.ts','src/client/test/unit/**/*.ts'], { read: false })
|
|
.pipe(mocha({
|
|
require: [ "ts-node/register" ],
|
|
ui: 'bdd'
|
|
}));
|
|
}
|
|
|
|
async function packageVsix() {
|
|
fs.emptyDirSync(packagedir);
|
|
return vsce.createVSIX({
|
|
packagePath: packagedir,
|
|
})
|
|
}
|
|
|
|
async function git(args) {
|
|
args.unshift('git');
|
|
const {stdout, stderr } = await exec(args.join(' '));
|
|
return {stdout: stdout, stderr: stderr};
|
|
}
|
|
|
|
async function setGitAuthN() {
|
|
const repoUrl = 'https://github.com';
|
|
const repoToken = argv.repoToken;
|
|
if (!repoToken) {
|
|
throw new Error(`Must specify parameter --repoToken with read and push rights to ${repoUrl}!`);
|
|
}
|
|
const bearer = `AUTHORIZATION: basic ${Buffer.from(`PAT:${repoToken}`).toString('base64')}`;
|
|
await git(['config', '--local', `http.${repoUrl}/.extraheader`, `"${bearer}"`]);
|
|
await git(['config', '--local', 'user.email', 'capisvaatdev@microsoft.com' ]);
|
|
await git(['config', '--local', 'user.name', '"DPT Tools Dev Team"' ]);
|
|
}
|
|
|
|
async function snapshot() {
|
|
const targetBranch = argv.targetBranch || 'release/daily';
|
|
const sourceSpecParam = argv.sourceSpec;
|
|
|
|
const tmpRepo = path.resolve('./out/tmpRepo');
|
|
fs.emptyDirSync(tmpRepo);
|
|
|
|
const repoUrl = (await git(['remote', 'get-url', '--all', 'origin'])).stdout.trim();
|
|
log.info(`snapshot: remote repoUrl: ${repoUrl}`);
|
|
const orgDir = process.cwd();
|
|
process.chdir(tmpRepo);
|
|
try
|
|
{
|
|
await git(['init']);
|
|
await git(['remote', 'add', 'origin', repoUrl]);
|
|
await setGitAuthN();
|
|
await git(['fetch', 'origin']);
|
|
const remotes = (await git(['remote', 'show', 'origin'])).stdout;
|
|
const head = remotes
|
|
.split('\n')
|
|
.map(line => {
|
|
const branch = line.match(/HEAD branch:\s*(\S+)/);
|
|
if (branch && branch.length >= 2) {
|
|
return branch[1];
|
|
}
|
|
})
|
|
.filter(b => !!b);
|
|
if (!head || head.length < 1 || head.length > 1 || !head[0]) {
|
|
throw new Error(`Cannot determine HEAD from remote: ${repoUrl}`);
|
|
}
|
|
const headBranch = head[0];
|
|
if (headBranch == targetBranch) {
|
|
throw new Error(`Cannot snapshot into default HEAD branch: ${headBranch}`);
|
|
}
|
|
const sourceSpec = sourceSpecParam || `origin/${headBranch}`;
|
|
log.info(` > snap shotting '${sourceSpec}' into branch: ${targetBranch}...`);
|
|
await git(['checkout', headBranch]);
|
|
const snapshotTag = `snapshot-${targetBranch.replace('/', '_').replace(' ', '_')}-${moment.utc().format('YYMMDD[Z]HHmmss')}`;
|
|
// TODO: setting this tag can interfere with the versioning tool, release-it; for now, don't set this tag
|
|
// await git(['tag', snapshotTag, sourceSpec]);
|
|
await git(['checkout', '--force', '-B', targetBranch]);
|
|
const resetMsg = (await git(['reset', '--hard', `"${sourceSpec}"`])).stdout.trim();
|
|
log.info(` > snapshot (${snapshotTag}): ${resetMsg}`);
|
|
log.info(` > pushing snapshot branch '${targetBranch} to origin...`);
|
|
const pushMsg = (await git(['push', '--force', '--tags', 'origin', targetBranch])).stderr.trim();
|
|
log.info(` > ${pushMsg}`)
|
|
}
|
|
finally {
|
|
process.chdir(orgDir);
|
|
}
|
|
}
|
|
const cliVersion = '1.12.2';
|
|
|
|
const recompile = gulp.series(
|
|
clean,
|
|
async () => nugetInstall('CAP_ISVExp_Tools_Stable', 'Microsoft.PowerApps.CLI',cliVersion, path.resolve(distdir, 'pac')),
|
|
async () => nugetInstall('CAP_ISVExp_Tools_Stable', 'Microsoft.PowerApps.CLI.Core.osx-x64', cliVersion, path.resolve(distdir, 'pac')),
|
|
async () => nugetInstall('CAP_ISVExp_Tools_Stable', 'Microsoft.PowerApps.CLI.Core.linux-x64', cliVersion, path.resolve(distdir, 'pac')),
|
|
translationsExport,
|
|
translationsImport,
|
|
translationsGenerate,
|
|
compile,
|
|
);
|
|
|
|
const dist = gulp.series(
|
|
recompile,
|
|
packageVsix,
|
|
lint,
|
|
test
|
|
);
|
|
|
|
const translationExtensionName = "vscode-powerplatform";
|
|
|
|
// Extract all the localizable strings from TS and package.nls.json, and package into
|
|
// an XLF for the localization team
|
|
async function translationsExport() {
|
|
return gulp
|
|
.src('src/**/*.ts')
|
|
.pipe(nls.createMetaDataFiles())
|
|
.pipe(filter(['**/*.nls.json', '**/*.nls.metadata.json']))
|
|
.pipe(nls.bundleMetaDataFiles('ms-vscode.powerplatform', '.'))
|
|
.pipe(filter(['**/nls.metadata.header.json', '**/nls.metadata.json']))
|
|
.pipe(gulp.src(["package.nls.json"]))
|
|
.pipe(nls.createXlfFiles("translations-export", translationExtensionName))
|
|
.pipe(gulp.dest(path.join("loc")));
|
|
}
|
|
|
|
const languages = [
|
|
//{ id: "bg", folderName: "bul" },
|
|
//{ id: "hu", folderName: "hun" },
|
|
//{ id: "pl", folderName: "plk" },
|
|
{ id: "cs", folderName: "csy" },
|
|
{ id: "de", folderName: "deu" },
|
|
{ id: "es", folderName: "esn" },
|
|
{ id: "fr", folderName: "fra" },
|
|
{ id: "it", folderName: "ita" },
|
|
{ id: "ja", folderName: "jpn" },
|
|
{ id: "ko", folderName: "kor" },
|
|
{ id: "pt-BR", folderName: "ptb"},
|
|
{ id: "ru", folderName: "rus" },
|
|
{ id: "tr", folderName: "trk" },
|
|
{ id: "zh-CN", folderName: "chs" },
|
|
{ id: "zh-TW", folderName: "cht" },
|
|
];
|
|
|
|
async function translationsImport(done) {
|
|
const tasks = languages.map((language) => {
|
|
const importTask = async () => gulp.src(path.join("loc", "translations-import", `vscode-powerplatform.${language.id}.xlf`))
|
|
.pipe(nls.prepareJsonFiles())
|
|
.pipe(replace("\\r\\n", "\\n"))
|
|
.pipe(gulp.dest(path.join("./i18n", language.folderName)));
|
|
importTask.displayName = `Importing localization - ${language.id}`;
|
|
return importTask;
|
|
});
|
|
|
|
return gulp.parallel(...tasks, (seriesDone) => {
|
|
seriesDone();
|
|
done();
|
|
})();
|
|
}
|
|
|
|
function translationsGeneratePackage() {
|
|
return gulp.src(['package.nls.json'])
|
|
.pipe(nls.createAdditionalLanguageFiles(languages, "i18n"))
|
|
.pipe(gulp.dest('.'));
|
|
}
|
|
function translationsGenerate(done) {
|
|
return gulp.series(
|
|
async() => translationsGeneratePackage(),
|
|
async() => translationsGenerateSrcLocBundles(),
|
|
(seriesDone) => {
|
|
seriesDone();
|
|
done();
|
|
}
|
|
)();
|
|
}
|
|
|
|
function translationsGenerateSrcLocBundles() {
|
|
return gulp.src('src/**/*.ts')
|
|
.pipe(nls.createMetaDataFiles())
|
|
.pipe(nls.createAdditionalLanguageFiles(languages, "i18n"))
|
|
.pipe(nls.bundleMetaDataFiles('ms-vscode.powerplatform', path.join('dist', 'src')))
|
|
.pipe(nls.bundleLanguageFiles())
|
|
.pipe(filter(['**/nls.bundle.*.json', '**/nls.metadata.header.json', '**/nls.metadata.json']))
|
|
.pipe(filter(['**/nls.*.json']))
|
|
.pipe(gulp.dest(path.join('dist', 'src')));
|
|
}
|
|
|
|
exports.clean = clean;
|
|
exports.compile = compile;
|
|
exports.recompile = recompile;
|
|
exports.snapshot = snapshot;
|
|
exports.lint = lint;
|
|
exports.test = test;
|
|
exports.package = packageVsix;
|
|
exports.ci = dist;
|
|
exports.dist = dist;
|
|
exports.translationsExport = translationsExport;
|
|
exports.translationsImport = translationsImport;
|
|
exports.translationsGenerate = translationsGenerate;
|
|
exports.setGitAuthN = setGitAuthN;
|
|
exports.default = compile;
|