Azure Devops (VSTS) Extensions 开发小记
我在使用tfx-cli打包Azure Devops插件时,输出了很黄很黄很亮瞎眼的(尤其是在Visual Studio Code采用了Dark Black Theme的情况下)警告warning:
PS D:\Works\boards-extensions> npm run package
> vsts-widgets@1.0.234 package D:\Works\boards-extensions
tfx extension create --manifest-globs vss-extension.json
TFS Cross Platform Command Line Interface v0.7.9
Copyright Microsoft Corporation
warning: Could not determine content type for extension .woff2. Defaulting to application/octet-stream. To override this, add a contentType property to this file entry in the manifest.
warning: Could not determine content type for extension .ttf. Defaulting to application/octet-stream. To override this, add a contentType property to this file entry in the manifest.
warning: Could not determine content type for extension .otf. Defaulting to application/octet-stream. To override this, add a contentType property to this file entry in the manifest.
这让我有股消灭警告的冲动,
于是经过一番google后,我找到了一段打包代码,如下:
/**
* Generates the required [Content_Types].xml file for the vsix package.
* This xml contains a <Default> entry for each different file extension
* found in the package, mapping it to the appropriate MIME type.
*/
private genContentTypesXml(fileNames: string[], overrides: {[partName: string]: PackagePart}): Q.Promise<string> {
log.debug("Generating [Content_Types].xml");
let contentTypes: any = {
Types: {
$: {
xmlns: "http://schemas.openxmlformats.org/package/2006/content-types"
},
Default: [],
Override: []
}
};
let windows = /^win/.test(process.platform);
let contentTypePromise;
if (windows) {
// On windows, check HKCR to get the content type of the file based on the extension
let contentTypePromises: Q.Promise<any>[] = [];
let extensionlessFiles = [];
let uniqueExtensions = _.unique<string>(fileNames.map((f) => {
let extName = path.extname(f);
if (!extName && !overrides[f]) {
log.warn("File %s does not have an extension, and its content-type is not declared. Defaulting to application/octet-stream.", path.resolve(f));
overrides[f] = {partName: f, contentType: "application/octet-stream"};
}
if (overrides[f]) {
// If there is an override for this file, ignore its extension
return "";
}
return extName;
}));
uniqueExtensions.forEach((ext) => {
if (!ext.trim()) {
return;
}
if (!ext) {
return;
}
if (VsixWriter.CONTENT_TYPE_MAP[ext.toLowerCase()]) {
contentTypes.Types.Default.push({
$: {
Extension: ext,
ContentType: VsixWriter.CONTENT_TYPE_MAP[ext.toLowerCase()]
}
});
return;
}
let hkcrKey = new winreg({
hive: winreg.HKCR,
key: "\\" + ext.toLowerCase()
});
let regPromise = Q.ninvoke(hkcrKey, "get", "Content Type").then((type: WinregValue) => {
log.debug("Found content type for %s: %s.", ext, type.value);
let contentType = "application/octet-stream";
if (type) {
contentType = type.value;
}
return contentType;
}).catch((err) => {
log.warn("Could not determine content type for extension %s. Defaulting to application/octet-stream. To override this, add a contentType property to this file entry in the manifest.", ext);
return "application/octet-stream";
}).then((contentType) => {
contentTypes.Types.Default.push({
$: {
Extension: ext,
ContentType: contentType
}
});
});
contentTypePromises.push(regPromise);
});
contentTypePromise = Q.all(contentTypePromises);
} else {
// If not on windows, run the file --mime-type command to use magic to get the content type.
// If the file has an extension, rev a hit counter for that extension and the extension
// If there is no extension, create an <Override> element for the element
// For each file with an extension that doesn't match the most common type for that extension
// (tracked by the hit counter), create an <Override> element.
// Finally, add a <Default> element for each extension mapped to the most common type.
let contentTypePromises: Q.Promise<any>[] = [];
let extTypeCounter: {[ext: string]: {[type: string]: string[]}} = {};
fileNames.forEach((fileName) => {
let extension = path.extname(fileName);
let mimePromise;
if (VsixWriter.CONTENT_TYPE_MAP[extension]) {
if (!extTypeCounter[extension]) {
extTypeCounter[extension] = {};
}
if (!extTypeCounter[extension][VsixWriter.CONTENT_TYPE_MAP[extension]]) {
extTypeCounter[extension][VsixWriter.CONTENT_TYPE_MAP[extension]] = [];
}
extTypeCounter[extension][VsixWriter.CONTENT_TYPE_MAP[extension]].push(fileName);
mimePromise = Q.resolve(null);
return;
}
mimePromise = Q.Promise((resolve, reject, notify) => {
let child = childProcess.exec("file --mime-type \"" + fileName + "\"", (err, stdout, stderr) => {
try {
if (err) {
reject(err);
}
let stdoutStr = stdout.toString("utf8");
let magicMime = _.trimRight(stdoutStr.substr(stdoutStr.lastIndexOf(" ") + 1), "\n");
log.debug("Magic mime type for %s is %s.", fileName, magicMime);
if (magicMime) {
if (extension) {
if (!extTypeCounter[extension]) {
extTypeCounter[extension] = {};
}
let hitCounters = extTypeCounter[extension];
if (!hitCounters[magicMime]) {
hitCounters[magicMime] = [];
}
hitCounters[magicMime].push(fileName);
} else {
if (!overrides[fileName]) {
overrides[fileName].contentType = magicMime;
}
}
} else {
if (stderr) {
reject(stderr.toString("utf8"));
} else {
log.warn("Could not determine content type for %s. Defaulting to application/octet-stream. To override this, add a contentType property to this file entry in the manifest.", fileName);
overrides[fileName].contentType = "application/octet-stream";
}
}
resolve(null);
} catch (e) {
reject(e);
}
});
});
contentTypePromises.push(mimePromise);
});
contentTypePromise = Q.all(contentTypePromises).then(() => {
Object.keys(extTypeCounter).forEach((ext) => {
let hitCounts = extTypeCounter[ext];
let bestMatch = this.maxKey<string[]>(hitCounts, (i => i.length));
Object.keys(hitCounts).forEach((type) => {
if (type === bestMatch) {
return;
}
hitCounts[type].forEach((fileName) => {
overrides[fileName].contentType = type;
});
});
contentTypes.Types.Default.push({
$: {
Extension: ext,
ContentType: bestMatch
}
});
});
});
}
return contentTypePromise.then(() => {
Object.keys(overrides).forEach((partName) => {
contentTypes.Types.Override.push({
$: {
ContentType: overrides[partName].contentType,
PartName: "/" + _.trimLeft(partName, "/")
}
})
});
let builder = new xml.Builder(VsixWriter.DEFAULT_XML_BUILDER_SETTINGS);
return builder.buildObject(contentTypes).replace(/\n/g, os.EOL);
});
}
其中有一小段很有意思的,它尝试从当前电脑的注册表中获取扩展名对应的Content Type值,用以填充它的[Content_Types].xml:
let hkcrKey = new winreg({
hive: winreg.HKCR,
key: "\\" + ext.toLowerCase()
});
let regPromise = Q.ninvoke(hkcrKey, "get", "Content Type").then((type: WinregValue) => {
log.debug("Found content type for %s: %s.", ext, type.value);
let contentType = "application/octet-stream";
if (type) {
contentType = type.value;
}
return contentType;
})
根据这段代码我找到了注册表的以下项:
- HKEY_CLASSES_ROOT\.otf
- HKEY_CLASSES_ROOT\.woff2
- HKEY_CLASSES_ROOT\.ttf
这些项里均没有Content Type键值,所以我写了个reg文件进行注册:
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\.otf]
"Content Type"="application/x-font-opentype"
[HKEY_CLASSES_ROOT\.ttf]
"Content Type"="application/x-font-truetype"
[HKEY_CLASSES_ROOT\.woff2]
"Content Type"="application/x-font-woff2"
重新打包,警告不再出现了....
解决前的[Content_types].xml
<?xml version="1.0" encoding="utf-8"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension=".html" ContentType="text/html"/>
<Default Extension=".js" ContentType="application/javascript"/>
<Default Extension=".ts" ContentType="text/plain"/>
<Default Extension=".css" ContentType="text/css"/>
<Default Extension=".png" ContentType="image/png"/>
<Default Extension=".eot" ContentType="application/vnd.ms-fontobject"/>
<Default Extension=".svg" ContentType="image/svg+xml"/>
<Default Extension=".woff" ContentType="application/font-woff"/>
<Default Extension=".map" ContentType="application/json"/>
<Default Extension=".woff2" ContentType="application/octet-stream"/>
<Default Extension=".ttf" ContentType="application/octet-stream"/>
<Default Extension=".otf" ContentType="application/octet-stream"/>
<Default Extension=".vsixmanifest" ContentType="text/xml"/>
<Default Extension=".vsomanifest" ContentType="application/json"/>
<Override ContentType="application/x-font-woff2" PartName="/node_modules/font-awesome/fonts/fontawesome-webfont.woff2"/>
<Override ContentType="application/x-font-truetype" PartName="/node_modules/font-awesome/fonts/fontawesome-webfont.ttf"/>
<Override ContentType="application/x-font-opentype" PartName="/node_modules/font-awesome/fonts/FontAwesome.otf"/>
</Types>
解决后的[Content_types].xml
<?xml version="1.0" encoding="utf-8"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension=".html" ContentType="text/html"/>
<Default Extension=".js" ContentType="application/javascript"/>
<Default Extension=".ts" ContentType="text/plain"/>
<Default Extension=".css" ContentType="text/css"/>
<Default Extension=".png" ContentType="image/png"/>
<Default Extension=".eot" ContentType="application/vnd.ms-fontobject"/>
<Default Extension=".svg" ContentType="image/svg+xml"/>
<Default Extension=".woff" ContentType="application/font-woff"/>
<Default Extension=".map" ContentType="application/json"/>
<Default Extension=".woff2" ContentType="application/x-font-woff2"/>
<Default Extension=".ttf" ContentType="application/x-font-truetype"/>
<Default Extension=".otf" ContentType="application/x-font-opentype"/>
<Default Extension=".vsixmanifest" ContentType="text/xml"/>
<Default Extension=".vsomanifest" ContentType="application/json"/>
<Override ContentType="application/x-font-woff2" PartName="/node_modules/font-awesome/fonts/fontawesome-webfont.woff2"/>
<Override ContentType="application/x-font-truetype" PartName="/node_modules/font-awesome/fonts/fontawesome-webfont.ttf"/>
<Override ContentType="application/x-font-opentype" PartName="/node_modules/font-awesome/fonts/FontAwesome.otf"/>
</Types>
Azure Devops (VSTS) Extensions 开发小记的更多相关文章
- Azure DevOps(一)利用Azure DevOps Pipeline 构建应用程序镜像到AWS ECR
一,引言 最近项目上让开始学习AWS,作为一名合格的开发人员,当然也是学会利用Azure DevOps Pipeline 将应用程序部署到 AWS ECS(完全托管的容器编排服务).我们要学会将应用程 ...
- 微软改名部又出动啦!微软宣布VSTS改名为Azure DevOps
本篇为翻译,原文地址:https://azure.microsoft.com/en-us/blog/introducing-azure-devops/ 作者:Jamie Cool,Azure DevO ...
- VSTS 更名为 Azure DevOps
微软正式对外宣布Azure DevOps,其实就是原来的VSTS,我们来看一下Azure DevOps的介绍: 今天我们宣布Azure DevOps.与世界各地的客户和开发人员合作,很明显,DevOp ...
- [转贴]infoQ VSTS被拆成5个部分,以Azure DevOps服务形式推出
VSTS被拆成5个部分,以Azure DevOps服务形式推出 http://www.infoq.com/cn/news/2018/09/vsts-divide5parts-azuredevops?u ...
- 使用ML.NET + Azure DevOps + Azure Container Instances打造机器学习生产化
介绍 Azure DevOps,以前称为Visual Studio Team Services(VSTS),可帮助个人和组织更快地规划,协作和发布产品.其中一项值得注意的服务是Azure Pipeli ...
- 你好,Azure DevOps Server 2019;再见,Team Foundation Server
微软正式发布Azure DevOps Server 2019的第一个版本,作为Team Foundation Server (TFS)2018的升级版本和替代产品. 这是目前市面上唯一一款将产品名称冠 ...
- Azure DevOps Server(TFS) 客户端分析
Azure DevOps Server(TFS) 是微软公司的软件协作开发管理平台产品,为软件研发.测试.实施提供全流程的服务.作为一款应用服务器产品,他的客户端是什么,在哪里下载客户端?我们在项目实 ...
- Azure Devops测试管理(上)
因为最近测试人员合并到我这边开发组,对于如何能更好管理测试流程和测试与开发能更高效的完成任务,通俗的说如何能更敏捷,深入思考,然后就开始琢磨起TFS(也称之为VSTS/Azure Devops,因为我 ...
- 【Azure DevOps系列】什么是Azure DevOps
DevOps DevOps是一种重视"软件开发人员(Dev)"和"IT运维技术人员(Ops)"之间沟通合作的文化,它促进开发和运营团队之间的协作,以自动化和可重 ...
随机推荐
- JVM的监控工具之jps
jps的功能和ps命令相似:可列出正在运行的虚拟机进程,并显示虚拟机执行主类(Main Class,main()函数所在的类)名称以及这些进程的本地虚拟机唯一ID(Local Virtual Mach ...
- 『月之谜 数位dp』
月之谜 Description 打败了Lord lsp 之后,由 于lqr 是一个心地善良的女孩 子,她想净化Lord lsp 黑化的 心,使他变回到原来那个天然 呆的lsp--在倒霉的光之英 雄ap ...
- HashHelper
在C#中,数据的Hash以MD5或SHA-1的方式实现,MD5与SHA1都是Hash算法,MD5输出是128位的,SHA1输出是160位的,MD5比SHA1快,SHA1比MD5强度高. MD5与SHA ...
- C# - VS2019WinFrm程序通过SMTP方式实现邮件发送
前言 本篇主要记录:VS2019 WinFrm桌面应用程序通过SMTP方式实现邮件发送.作为Delphi转C#的关键一步,接下来将逐步实现Delphi中常用到的功能. 准备工作 搭建WinFrm前台界 ...
- Python脚本1
[轮子]P123. 求最大约数,并鉴别是否为素数
- TensorFlow、numpy、matplotlib、基本操作
一.常量的定义 import tensorflow as tf #类比 语法 api 原理 #基础数据类型 运算符 流程 字典 数组 data1 = tf.constant(2,dtype=tf.in ...
- php取整的几种方式,四舍五入,舍去法取整,进一法取整
php取整的几种方式,四舍五入,舍去法取整,进一法取整方式一:round 对浮点数进行四舍五入语法:float round ( float val [, int precision] ) echo r ...
- Windows+Qt使用gRPC
上篇文章<Windows+VS2017使用gRPC>编译出了Windows下可用的gRPC静态lib库文件,在此基础上要想在Qt上使用,需要使用MSVC2017 64bit构建组件进行构建 ...
- Arduino控制舵机
一.接线 舵机 Arduino GND GND +5V 5V PWN 10 其中信号线PWN接arduino上任意带波浪号的引脚都可,我这里选择的是10号引脚,注意在程序中绑定的引脚要和连接的引脚相同 ...
- GCN
REFERENCE: https://www.jianshu.com/p/ad528c40a08f https://www.zhihu.com/question/54504471 图有两个基本的特性: ...