团队项目利用Msbuild自定义Task实现增量发布

  最近一直在做自动部署工具,主要利用到了Msbuild的自定义Task,通过Task我们可以自定义编译、部署过程减少人工直接干预。Msbuild的详细用法,可以去园子里搜一下,有很多的基础教程,这里就不赘述了,还是集中说一下增量发布的问题。

  增量主要涉及到三部分内容,程序、配置和静态文件(例如CSS、JS等),程序的增量比较简单,通过版本对比或者TFS的修改记录便可以查询出被修改过的程序集。配置文件增量大致有两种,全增量部分增量。全增量也很简单,直接把修改过的配置文件复制到发布包就OK了;部分增量需要我们比较这个配置里所有修改过的节点、属性,并且只输出这些改动。之前做发布包均是通过VS人工比较两个版本的配置文件,手动COPY出来,再放入到发布包。这样做在配置文件比较少的时候没有问题,但是整个项目的工程较多,配置文件非常多就很麻烦了,每次单独做增量配置文件就要花费很长时间。我们可以利用Task来完成整个项目的增量部署包制作。静态文件的处理通常只是将开发版本进行压缩输出,这个园子里已经有成熟实例了,我们这里就不单独写出来了。

目录

  1. Hello Task
  2. TFS 基本操作
  3. Start 增量发布
  4. DLL 和 配置文件 增量发布

1. Hello Task

  新建一个Library项目,命名为HelloTask,同时新建一个HelloTask类,添加如下引用

using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;

  添加如下代码

1     public class HelloTask : Microsoft.Build.Utilities.Task
2     {
3         public override bool Execute()
4         {
5             Log.LogMessage("Hello Task!");
6             return true;
7         }
8     }

  这是再在Solution里添加一个名为HelloTask.Web的空Web项目,用来做实验(其它随便什么类型项目都行,WEB项目主要是后面做增量实验的时候有用),右键单击Publish,新建一个名为HelloTask的部署配置文件,Publish Method选File System,选好发布路径。这时,在Properties会多一个PublishProfiles\HelloTask.pubxml文件,这便是发布的配置,这个也可以直接写入到msbuild的配置里去。修改为如下内容

<?xml version="1.0" encoding="utf-8"?>
<!--
This file is used by the publish/package process of your Web project. You can customize the behavior of this process
by editing this MSBuild file. In order to learn more about this please visit http://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <UsingTask AssemblyFile="$(MSBuildProjectDirectory)\..\HelloTask.Lib\HelloTask.dll" TaskName="HelloTask"></UsingTask>

  <PropertyGroup>
    <WebPublishMethod>FileSystem</WebPublishMethod>
    <LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
    <LastUsedPlatform>Any CPU</LastUsedPlatform>
    <SiteUrlToLaunchAfterPublish />
    <LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
    <ExcludeApp_Data>False</ExcludeApp_Data>
    <publishUrl>Publish</publishUrl>
    <DeleteExistingFiles>False</DeleteExistingFiles>
  </PropertyGroup>

  <Target Name="HelloTask" AfterTargets="GatherAllFilesToPublish">
    <HelloTask></HelloTask>
  </Target>
</Project>

  注意AssemblyFile的路径,需要指向HelloTask.dll的输出目录,这里我在Output里修改过。然后右键选中Web项目,选择Publish, 输出窗口便可以看到输出,同时在设置的发布路径下可以看到整个Web项目的输出。  

  要实现自定义Task,我们主要需要实现Microsoft.Build.Framework.dll里面ITask接口,其定义如下

namespace Microsoft.Build.Framework
{
    public interface ITask : object
    {
        bool Execute();

        IBuildEngine BuildEngine { get; set; }

        ITaskHost HostObject { get; set; }
    }
}

  BuildEngine定义了编译引擎接口,HostObject定义了编译的宿主信息,这里Microsoft.Build.Utilities里为我们实现了这个接口,我们只需要重写Execute这个方法就行。上面就实现了一个非常简单的输出Hello Task的功能。

2. TFS 基本操作

  虽然现在Git满天飞,但是在做.Net项目是我们还是依然再用TFS,毕竟和VS集成度最好。这里需要使用TFS API来对项目和配置文件的修改状态进行判断,从而实现增量输出。在Task项目里再添加如下引用

using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;

  然后我们分别介绍下后面需要用到的操作,其他TFS的API童鞋们自行研究吧,

        public static VersionControlServer Open(string path)
        {
            var info = Workstation.Current.GetLocalWorkspaceInfo(path);
            var uri = info.ServerUri;
            //var uri = "..."
            var tfsCollection = new TfsTeamProjectCollection(uri);
            return tfsCollection.GetService<VersionControlServer>();
        }

  这里在获取VersionControlServer实例的时候转了下弯,其实可以直接输入服务器的uri,但是这里首先获取了本地的代码仓库,从本地的代码仓库再获取到了服务器的uri;同时TfsTeamProjectCollection有多个重载,可以显示的提供登陆凭据,这里我们偷懒,直接利用系统里已经存好的凭据登陆,具体可以查看

  返回的VersionControlServer实例是后续操作的基础,因此在这个静态类的我们将它放入

public static VersionControlServer SourceControl { get; set; }

  TFS里,每一次Check-In,会提交一个Changeset,里面包含了本次改动的所有Change,这些Change都对应到Item的,即我们版本管理的每一个文件。我们发布时,通常的做法是在当前版本的代码上打上相应的标注Label,使其成为一个特定的版本。那么,在增量发布中,我们就可以通过这些特性的标注来获取版本间的差别。创建标注的代码如下

public static void CreateLabel(string scope, string label)
        {
            var itemSpec = new ItemSpec(scope, RecursionType.Full);
            var labelItemSpec = new LabelItemSpec(itemSpec, VersionSpec.Latest, false);
            var vslabel = new VersionControlLabel(SourceControl, label, SourceControl.AuthorizedUser, scope, label);
            SourceControl.CreateLabel(vslabel, new[] { labelItemSpec }, LabelChildOption.Replace);
        }

  scope为这个标注的范围,通常我们以解决方案为发布的基本单元,那么传入这个解决方案的目录就可以了。查询标注的代码如下

        public static VersionControlLabel QueryLabel(string scope, string label)
        {
            return SourceControl.QueryLabels(label, null, null, true, scope, VersionSpec.Latest).FirstOrDefault();
        }

 

  如果要查询所有标注,第一个参数可以传入null。有了这些标注的操作,我们就可以获取这些特定版本之间的的Changeset

public static IEnumerable<Changeset> Changes(string scope, string label1, string label2 )
        {
            var vsLabel1 = QueryLabel(scope,label1);
            var vsLabel2 = QueryLabel(scope, label2);
            var vsLabelSpec1 = new LabelVersionSpec(vsLabel1.Name, vsLabel1.Scope);
            var vsLabelSpec2 = new LabelVersionSpec(vsLabel2.Name, vsLabel2.Scope);
            return SourceControl.QueryHistory(vsLabelSpec1.Scope,
                VersionSpec.Latest,
                0,
                RecursionType.Full,
                null,
                vsLabelSpec1,
                vsLabelSpec2,
                int.MaxValue,
                true,
                false)
                .Cast<Changeset>();
        }

  这里需要注意的是label1的版本一定要比label2的版本新,否则在调用QueryHistory的时候是没有返回的。我们还要用到的一个方法是获取特定标注下的某个文件,

        public static Item GetSpecVersion(string scope, string label, Item item)
        {
            var vslabel = QueryLabel(scope,label);
            return SourceControl.GetItem(item.ServerItem, new LabelVersionSpec(vslabel.Name, vslabel.Scope));
        }

  有了这些方法,我们就可以开始实现增量发布了。

3. Start增量发布

  正经Coding之前我们做点准备工作,方便调试。在Solution里在添加一个HelloTask.Task.DebugConsole项目,加入一个批处理文件debug.bat,内容如下

@echo off
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\msbuild "D:\HelloTask\HelloTask.Web\HelloTask.Web.csproj" /t:GatherAllFilesToPublish /p:PublishProfile=HelloTask;SolutionDir=D:\HelloTask\ /v:q

  然后修改其属性Copy to Output Direcotry -> Coy always,同时修改Debug项目的输出目录到HelloTask.dll的目录。然后修改在Execute()加入调试代码

#if DEBUG
            System.Diagnostics.Debugger.Launch();
#endif

  还需要修改整个Solution的启动项目为HelloTask.Task.Debug,这样使用F5我们就可以直接调试程序了。批处理是执行IDE里面右键Publish的功能,Console是为了给Solution提供一个启动的项目,这里用Process去调用批处理主要是为了瞬间让IDE退出HelloTask.Task.Console的执行,从而准备好进入到HelloTask的调试,这样不用开多个IDE了。

  同时为了配合增量功能的实现,我们继续修改发布的配置文件HelloTask.pubxml,增加修改如下内容

<ItemGroup>
    <Label Include="label1">
      <From>lable2</From>
    </Label>
</ItemGroup>
<Target Name="HelloTask" AfterTargets="GatherAllFilesToPublish">
    <HelloTask SolutionDir="$(MSBuildProjectDirectory)\..\" Version="@(Label)"></HelloTask>
</Target>

View Cod

  再定义一些其他必要的结构如 ProjectItem 和 ChangedItem

  public class ProjectItem
    {
        public static List<ProjectItem> ProjectCollection { get; set; }

        public static void GetAll(string solutionDir)
        {
            ProjectCollection = new List<ProjectItem>();
            Directory.GetFiles(solutionDir, "*.csproj", SearchOption.AllDirectories)
                .ToList()
                .ForEach(t => ProjectCollection.Add(new ProjectItem(t)));
        }

        public static ProjectItem Find(Item item)
        {
            return ProjectCollection.ToList()
                .FirstOrDefault(t => item.ServerItem.IndexOf(t.Name) >= 0);
        }

        public string Name { get; set; }

        public string Path { get; set; }

        public string AssemblyName { get; set; }

        public bool Changed { get; set; }

        public string OutputType { get; set; }

        public ProjectItem(string path)
        {
            Path = path;
            Name = System.IO.Path.GetFileNameWithoutExtension(path);
            var doc = new XmlDocument();
            doc.Load(path);
            var ns = new XmlNamespaceManager(doc.NameTable);
            ns.AddNamespace("ns", "http://schemas.microsoft.com/developer/msbuild/2003");
            var node = doc.SelectSingleNode("//ns:PropertyGroup//ns:OutputType", ns);
            OutputType = node!=null? node.InnerText :string.Empty;
            node=doc.SelectSingleNode("//ns:PropertyGroup//ns:AssemblyName", ns);
            AssemblyName = node != null ? node.InnerText : string.Empty;
            this.Changed =false;
        }
    }

 public class ChangedItem
    {
        public ChangedItem(ChangeType changeType, Microsoft.TeamFoundation.VersionControl.Client.Change change)
        {
            ChangeType = changeType;
            Change = change;
        }

        public ChangeType ChangeType { get; set; }
        public Microsoft.TeamFoundation.VersionControl.Client.Change Change { get; set; }

        public static IEnumerable<ChangedItem> Build(IEnumerable<Changeset> changesets)
        {
            var list = new List<ChangedItem>();
            changesets = changesets.OrderBy(t => t.CreationDate);
            var changes = new List<Microsoft.TeamFoundation.VersionControl.Client.Change>();
            changesets.ToList().ForEach(changeset => changes.AddRange(changeset.Changes));
            changes.Distinct(new ChangeComparer())
                .ToList()
                .ForEach(change =>{
                        var changeType = ChangeType.None;
                        changes.Where(t => t.Item.ItemId == change.Item.ItemId)
                            .ToList()
                            .ForEach(t => changeType = changeType | t.ChangeType);
                        list.Add(new ChangedItem(changeType, change));
                });
            return list;
        }
    }

    public class ChangeComparer : IEqualityComparer<Microsoft.TeamFoundation.VersionControl.Client.Change>
    {
        public bool Equals(Microsoft.TeamFoundation.VersionControl.Client.Change x, Microsoft.TeamFoundation.VersionControl.Client.Change y)
        {
            return x.Item.ItemId == y.Item.ItemId;
        }

        public int GetHashCode(Microsoft.TeamFoundation.VersionControl.Client.Change obj)
        {
            return obj.Item.ItemId.GetHashCode();
        }
    }

  主要说一下ChangedItem吧,因为我们在label之间查询返回的是多个Changeset,因此可能会返回一个文件的多次修改状态,因此,我们需要将这些状态组合在一起来判断两个label之间这些文件的最终状态,MS刚好提供了ChangeType这个这个Flags的枚举,我要做的只用将同一个文件的多个ChangeType进行或操作。

  然后定义了接口IAdditionable

    public interface IAdditionable
    {
        void Republish(string publishFolder, string tempFolder);
    }

  同时有一个默认的实现DefaultAddition

 public class DefaultAddition : IAdditionable
    {
        public ChangedItem ChangedItem { get; set; }

        public ProjectItem ProjectItem { get; set; }

        public DefaultAddition(ChangedItem changedItem)
        {
            this.ChangedItem = changedItem;
            this.ProjectItem = ProjectItem.Find(changedItem.Change.Item);
        }

        public virtual void Republish(string publishFolder,string tempFolder)
        {
            FileUtility.CopyTo(GetAbsolutePath(publishFolder), GetAbsolutePath(tempFolder));
        }

        protected string GetRelativePath()
        {
            var start = ChangedItem.Change.Item.ServerItem.IndexOf(ProjectItem.Name) + ProjectItem.Name.Length;
            return ChangedItem.Change.Item.ServerItem.Substring(start).Trim(‘/‘).Trim(‘\\‘);
        }

        protected string GetAbsolutePath(string dir)
        {
            return Path.Combine(dir, GetRelativePath());
        }
    }

  默认的增量发布直接将文件Copy过去,这里实现的Republish标明为virtual类型,因为后面针对不同的的类型,我们将会直接继承DefaultAddition这个,同时重写Republish这个方法。FileUtility里提供了一些安全的文件操作方法,这个就懒得贴了。

4. DLL 和 配置文件 增量发布

  然后首先实现一个个简单的DLL增量,DLL增量无非是查找哪些项目里的*.cs文件被修改过(这里我们不考虑极端情况),改过我们就像这个项目的DLL添加到增量发布包里面。因此新建一个类ProjectAddition,具体实现如下,

public class ProjectAddition : DefaultAddition
    {
        public ProjectAddition(ChangedItem changedItem)
            : base(changedItem)
        {
            this.ChangedItem = changedItem;
        }

        public override void Republish(string publishFolder, string tempFolder)
        {
            var bin = "bin";
            var assembly = string.Format("{0}.{1}", ProjectItem.AssemblyName, ProjectItem.OutputType == "Library" ? "dll" : "exe");
            var pdb = string.Format("{0}.pdb", ProjectItem.AssemblyName);

            var assemblyFrom = Path.Combine(publishFolder, bin, assembly);
            var assemblyTo = Path.Combine(tempFolder, bin, assembly);
            var pdbFrom = Path.Combine(publishFolder, bin, pdb);
            var pdbTo = Path.Combine(tempFolder, bin, pdb);

            FileUtility.CopyTo(assemblyFrom,assemblyTo);
            FileUtility.CopyTo(pdbFrom,pdbTo);
        }
    }

  因为发布的是Web项目,所以所有的dll都放在bin目录下,这里为了方便路径直接写在了里方法里,其实应该写在配置,同时如果输出的时候有pdb调试文件,也会自动复制。然后我们需要一个工厂方法来对增量的类型进行构造,再添加AdditionManger   

public class ChangeManger
    {
        static ChangeManger()
        {
            ChangeCollection = new List<IAdditionable>();
        }

        public static List<IAdditionable> ChangeCollection { get; set; }

        public static void AddChange(ChangedItem changeItem)
        {
            var ext = Path.GetExtension(changeItem.Change.Item.ServerItem).ToLower();
            IAdditionable changed = null;
            switch (ext)
            {
                case ".cs":
                    changed = new ProjectAddition(changeItem);
                    break;
                case ".config":
                    changed = new ConfigurationAddition(changeItem);
                    break;
                default:
                    changed = new DefaultAddition(changeItem);
                    break;
            }
            if (!ChangeCollection.Exists(t => t.Equals(changed))) ChangeCollection.Add(changed);
        }
        public static void AdditionChange()
        {
            var tempFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
            var publishFolder = @"D:\HelloTask\HelloTask.Web\Publish";
            ChangeCollection.ForEach(t => t.Republish(publishFolder, tempFolder));
        }
    }

  这里一并给出了后面将要实现的ConfigurationAddition,这里.cs文件会根据它的项目信息返回前面提到的 ProjectItem实例,同时后面保证了在ChangeCollection里每个项目是唯一的。 AdditionChange这个方法则批量调动了增量接口。其中,里面的publishFolder也应该写入配置,这里偷懒了下。

  配置的增量发布实现起来稍微麻烦点,同时要向完全自动化还有点距离,因为在XML里面,对于一个List对象的修改操作本身存在二义性,我们无法准确的获知这个节点的的修改状态,因此我们这里对配置增量的策略是将所有的有修改的节点增量输出,同时将List类型的对象整体输出。

   一个测试例子如下,

<Configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <singleNode>1</singleNode>
  <complexNode>
    <unchange>content</unchange>
    <changed>11</changed>
  </complexNode>
  <links>
    <link>
        <name>百度</name>
        <url>www.baidu.com</url>
    </link>
    <link>
        <name>Google1</name>
        <url>http://www.google.com</url>
    </link>
    <link>
        <name>Bing</name>
        <url>http://www.bing.com</url>
    </link>
  </links>
</Configuration>

  修改后的配置如下,

<Configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <singleNode>1</singleNode>
  <complexNode>
    <unchange>content</unchange>
    <changed>22</changed>
  </complexNode>
  <links>
    <link>
        <name>百度</name>
        <url>http://www.baidu.com</url>
    </link>
    <link>
        <name>Google</name>
        <url>http://www.google.com</url>
    </link>
        </link>
    <link>
        <name>Bing</name>
        <url>http://www.bing.com</url>
    </link>
  </links>
</Configuration>

  期待的增量配置如下,

<Configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <complexNode>
    <changed>22</changed>
  </complexNode>
  <links>
    <link>
        <name>百度</name>
        <url>http://www.baidu.com</url>
    </link>
    <link>
        <name>Google</name>
        <url>http://www.google.com</url>
    </link>
  </links>
</Configuration>

  这里的link对象虽然分别只修改了一个元素的内容,但是我们将整个link全部输出,是为了排除可能存在的二义性,带来的问题是如果这样的link节点较大,输出的增量配置文件也会较大,但是毕竟作为增量配置,需要修改的内容明确。

  然后具体实现如下,简单说来通过对配置元素层级递归,每一层对两个配置之间相互求差集,在将差集进行增量输出

public class ConfigurationAddition : DefaultAddition
    {

        public ConfigurationAddition(ChangedItem changedItem)
            : base(changedItem)
        {
            this.ChangedItem = changedItem;

        }
        public override void Republish(string publishFolder, string tempFolder)
        {
            if (ChangedItem.ChangeType.HasFlag(ChangeType.Add))
            {
                base.Republish(publishFolder, tempFolder);
                return;
            }
            var docAddit = new XmlDocument();
            var docThis = new XmlDocument();
            docThis.Load(GetAbsolutePath(publishFolder));
            var itemSpec = TfsUtility.GetSpecVersion(this.Scope,this.Label,this.ChangedItem.Change.Item);
            var docSpec = new XmlDocument();
            docSpec.Load(itemSpec.DownloadFile());
            //导入XML Declaration
            if (docThis.FirstChild.NodeType == XmlNodeType.XmlDeclaration)
                docAddit.AppendChild(docAddit.ImportNode(docThis.FirstChild, true));
            //设置DOcumentElement为增量文档的第一个节点
            var nodeThis = (XmlNode)docThis.DocumentElement;
            var nodeSpec = (XmlNode)docSpec.DocumentElement;
            var nodeAddit = docAddit.ImportNode(nodeThis, true);
            RecursiveCompareChildNode(nodeThis, nodeSpec, nodeAddit, docAddit);
            docAddit.AppendChild(nodeAddit);
            var path = Path.ChangeExtension(GetAbsolutePath(tempFolder), ".addition.config");
            FileUtility.CreateIfNotExists(path);
            docAddit.Save(path);
        }
        private void RecursiveCompareChildNode(XmlNode nodeThis, XmlNode nodeSpec, XmlNode nodeAddit, XmlDocument docAddit)
        {
            //如果当前节点是LIST对象,直接输出整个节点
            if (nodeThis.ParentNode != null &&
                nodeThis.ParentNode.ChildNodes.Cast<XmlNode>()
                    .Count(t => t.Name == nodeThis.Name && t.NodeType == XmlNodeType.Element) > 1)
                return;
            nodeAddit.InnerXml = string.Empty;

            var listThis = nodeThis.ChildNodes.Cast<XmlNode>().ToList();
            var listSpec = nodeSpec.ChildNodes.Cast<XmlNode>().ToList();
            //*完全一样的elements会被忽略掉
            var comparer = new XmlNodeComparer();
            var exceptsThis = listThis.Except(listSpec, comparer).ToList();
            var exceptsSpec = listSpec.Except(listThis, comparer).ToList();
            foreach (var exceptNode in exceptsThis)
            {
                var childAddit = docAddit.ImportNode(exceptNode, true);
                //如没有element子节点,直接加入到增量文档
                if (exceptNode.ChildNodes.Cast<XmlNode>().All(t => t.NodeType != XmlNodeType.Element))
                {
                    nodeAddit.AppendChild(childAddit);
                    continue;
                }
                var childSpec = nodeSpec.ChildNodes
                    .Cast<XmlNode>()
                    .ToList()
                    .FirstOrDefault(t => NodePathCompare(t, exceptNode));

                nodeAddit.AppendChild(childAddit);
            }
            foreach (var exceptNode in exceptsSpec)
            {
                if (!exceptsThis.Exists(t => t.Name == exceptNode.Name))
                    nodeAddit.AppendChild(docAddit.ImportNode(exceptNode, true));
            }
        }
        private bool NodePathCompare(XmlNode node1, XmlNode node2)
        {
            var node1path = node1.Name.GetHashCode();
            var node2path = node2.Name.GetHashCode();
            var node = node1;
            while (node.ParentNode != null)
            {
                node = node.ParentNode;
                node1path += node.Name.GetHashCode();
            }
            node = node2;
            while (node.ParentNode != null)
            {
                node = node.ParentNode;
                node2path += node.Name.GetHashCode();
            }
            return node1path == node2path;
        }

    }//class 

  最后在我们的HelloTask里对这个AdditionManger进行调用就行了。目前方法可以输出两个配置之间的差异,但是还不能确定对这个节点具体的操作,后续如果研究出来将会补上。

团队项目利用Msbuild自定义Task实现增量发布,布布扣,bubuko.com

时间: 2024-10-16 00:53:59

团队项目利用Msbuild自定义Task实现增量发布的相关文章

安卓项目-利用Sqlite数据库,开发新闻发布系统

本教程致力于程序员可以快速的学习安卓移动端手机开发. 适合于已经习得一种编程语言的同仁. 更多志同道合,想要学习更多编程技术的大神们. 小弟不才,麻烦关注一下我的今日头条号-做全栈攻城狮. 本文章是基于上篇文章基础之上进行深入学习的.程序员带你学习安卓开发-XML文档的创建与解析 Sqlite数据库: Sqlite数据库是在安卓中使用较广泛的数据库.其为简单.轻巧的Sql类文件型数据库.因以简单的文本形式保存,所以安全性不是很高.只要拿到sqlite数据库文件就可以得到数据.所以这就决定了sql

vue cli脚手架项目利用webpack给生产环境和发布环境配置不同的接口地址或者不同的变量值。

废话不多说,直接进入正题,此文以配置不同的接口域名地址为例子 项目根目录下有一个config文件夹,基础项目的话里面至少包括三个文件, 1.dev.env.js 2.index.js 3.prod.env.js 我们需要做配置的就是第一个和第三个. 其实这两个文件内容就是针对生产环境和发布环境设置不同的参数的文件,那么打开dev.en.js,开发环境.原本代码如下: 'use strict' const merge = require('webpack-merge') const prodEnv

基于JAXB的Ant自定义Task

官方Manual链接: http://ant.apache.org/manual/index.html Ant.XSD.JAXB.XML的基本概念这里就不介绍,网上随便搜搜都有一大把,本文主要讲解利用XSD生成JAXB类来自定义Ant Task,自动完成XML的解析工作,提高开发效率. 开发工具采用Eclipse. 第一步,在Eclipse中创建Java项目,这里取名为WritingAntTaskDemo. 第二步,创建xsd文件,这里我在项目中jet.demo.xsd的包下创建了名为MyTas

10.2: 现代软件工程这门课已经上了好几年了,以前有很多学生做过团队项目(说不定包括本校的学生),请你们找一个以前的团队采访一下-------------答题者:徐潇瑞

10.2: 现代软件工程这门课已经上了好几年了,以前有很多学生做过团队项目(说不定包括本校的学生),请你们找一个以前的团队采访一下 - 当时的项目有多少用户,给用户多少价值? 现在还有人用吗? - 这个项目能否给我们团队继续开发,源代码/文档还有么? - 项目开发有什么经验和教训 - 对学好软件工程有什么建议 写成一个博客   #团队博客作业2 根据老师的作业要求,我们采访了以前本科认识的一个同学,他在读本科的时候出去实习,参与了一些项目.他参与了手机外卖app的开发,根据他的回答,当时用户有1

使用axshare创建axure团队项目

产品经理在设计原型时,面对一些规模较大的项目,可能需要多人合作.axure也提供了多人合作的机制--创建团队项目.axure软件里集成了两种平台:svn和axshare.今天我们主要介绍利用axure官方平台axshare创建团队项目的方法. 首先我们了解一下什么是axshare.axshare是axure提供的官方发布平台,你可以把制作好的原型发布到axshare上生成url链接,其他人可以在线访问,并可以发布讨论内容.当然在使用axshare服务时必须要注册账号,官方网址是 https://

HeyWeGo小组《Java程序设计》 2015—2016年学期团队项目总结

HeyWeGo小组<Java程序设计> 2015—2016年学期团队项目总结 题目简介 一个简单的扫雷小游戏,在12*12的方格盘上,首先可以设定雷的个数,然后点击开始程序就会随机布雷,开始游戏后如果点到雷就会显示游戏结束,如果没有,会出现数字表示周围一圈雷的个数,以此推理当扫出所有雷将显示游戏胜利. 游戏项目规划: 确定游戏中方块格子的个数 确定游戏中地雷的个数(初始10个),完成布雷 计算每个方块周围的雷数,在方块周围本身没有地雷的情况下,统计周围地雷的个数 若周围没有地雷则翻开周围的空白

团队项目:开发模式及代码管理

本次团队项目我们有意无意地使用了主治医师模式,即由一到两个主程序员进行游戏风格,整体框架的设计,实现较为核心的内容,其他人的工作都围绕主程序员展开.说是有意无意是因为该项目利用Unity游戏引擎进行开发,而当时组里只有两人对Unity引擎较为熟悉,其他人在一边学习的同时先进行外围的工作,之后逐渐加入核心的实现. 比较遗憾的是目前我们的项目缺乏代码管理.由于采用Unity引擎开发,无法上传github进行托管,我们寻找了其他的管理工具但由于付费等原因效果不太理想.我们的代码都保存在每个人的电脑中,

Java团队项目总结

Java团队项目总结 1.项目实现情况 项目概述: 我们团队项目准备实现一个有关于大富翁有的游戏程序. 大富翁游戏,以经营权为主要的游戏方式,通过购买经营权与架构经营的星级服务来获得最大的利益,当其他玩家破产后,最后一个玩家取得胜利来结束游戏.在百度词条上示意为,默认多幅地图,以掷骰点数前进,并有多种道具.卡片使用,另外触发一些“特别事件”.主要通过购买房产,收取对方的路费.租金来导致对手的破产. 项目预期: 鉴于对java学习的程度和掌握内容,基本实现大富翁的游戏内容我们的项目打算实现以下目标

使用Team Explorer Everywhere (TEE) 2015 SDK获取团队项目的签入策略

TFS的代码签入策略与IDE工具紧密相关,例如Visual Studio中设置的签入策略,只会影响Visual Studio的团队资源管理器:如果需要在Eclipse的TEE中启用签入策略,你还需要在TEE中单独设置.(不仅如此,在数据存储上,二者也不一样,通过查询数控,我们发现TEE的签入策略存储在Collection数据库的表tbl_PropertyValue中,VS则不是). 如果需要统计或查询哪些团队项目启用了什么签入策略,我们可以通过报表或者API的方式编写脚本输出TFS的签入策略设置