Coder Social home page Coder Social logo

microsoft / azure-devops-extension-hot-reload-and-debug Goto Github PK

View Code? Open in Web Editor NEW
85.0 9.0 28.0 1.37 MB

Azure DevOps extension sample that uses hot reloading and is debuggable directly in VS Code

License: MIT License

JavaScript 29.41% HTML 3.70% TypeScript 64.30% SCSS 2.59%

azure-devops-extension-hot-reload-and-debug's Introduction

Azure DevOps Extension Hot Reload and Debug

This repository demonstrates how to load an Azure DevOps extension's code directly from the dev machine rather than bundle all the code and deploy it through the marketplace. We will leverage the (somewhat hidden) capability in Azure DevOps to load content from localhost, which will enable us to use hot reload and debug in VS Code.

For more information about our motivation in developing this project, see our blog post.

Prerequisites

Download and install the following tools

  1. Visual Studio Code
  2. Firefox (because the VS Code Debugger for Chrome extension doesn't support iframes yet)
  3. The Debugger for Firefox VS Code extension
  4. The tfx-cli npm package
  5. The webpack npm package
  6. The webpack-dev-server npm package

If you would prefer not to install the npm packages globally, you can add them to devDependencies in your package.json file and invoke them with scripts. You can use the package.json in this repo as a template for scripts and to ensure you have the correct versions of packages in your extension.

Starting a new extension

If you're starting a new extension, you can either clone this repo or use our Yeoman generator to scaffold a new extension with support for hot reload and debugging. (The following sections provide information about how we configured the template.)

Clone

git clone https://github.com/microsoft/azure-devops-extension-hot-reload-and-debug.git

Yeoman

npm install -g yo
npm install -g @microsoft/generator-azure-devops-extension
yo @microsoft/azure-devops-extension

Enabling an existing extension

To reconfigure your existing extension, follow these steps to get everything working. This configuration assumes you know the basics of how to set up an Azure DevOps extension using Typescript and webpack. For more information, see the Azure DevOps extension docs or the azure-devops-extension-sample repo.

Extension manifest

The "trick" to get everything working is to set baseUri in the extension manifest file to https://localhost:3000. This setting tells the extension to load resources from your local dev server rather than the packaged extension.

Your port can be different than 3000. For the purposes of this document we will use 3000 consistently, but if you have a port conflict, you can change this to any port that works for your environment.

We recommend using an overrides JSON file to set this value only for your dev builds. The tfx command line utility supports overriding any values in your extension manifest with the values in this file while packaging by passing the --overrides-file parameter. We will use this feature to set the value of baseUri differently for dev vs. release builds.

Create an overrides JSON file with the following content for your dev build called dev.json:

{
  "id": "[extension-id]-dev",
  "public": false,
  "baseUri": "https://localhost:3000"
}

You can leave default values for these properties in your extension manifest or, for consistency, you can delete them from your manifest and create a release.json overrides file for release builds as well (in which case we suggest you create a configs folder to keep things organized):

{
  "id": "[extension-id]",
  "public": true
}

You can move additional settings you would like to override for specific build configurations from the extension manifest to the dev.json or release.json files if you choose. These values are just the recommended minimum settings.

Webpack config

You will need to enable source maps in webpack.config.js. Set the devtool property to inline-source-map. You will also want to set devServer.https to true and devServer.port to 3000.

module.exports = {
  devtool: "inline-source-map",
  devServer: {
    https: true,
    port: 3000,
  },
  // ...
};

For this example, we've validated that inline-source-map works well. However, you may find that another source map option works better for your project.

By default, webpack serves its compiled, in-memory files directly under localhost:3000 but the extension is looking for files in the dist path. To fix this discrepancy, set output.publicPath to /dist/ in the webpack config. Now webpack will serve files from localhost:3000/dist and your extension should load correctly.

module.exports = {
  output: {
    publicPath: "/dist/",
    // ...
  },
  // ...
};

Your extension may load fine without this change because webpack will serve files from the filesystem if it does not have an in-memory version to serve instead. Therefore, webpack may serve files from the dist folder if it exists, but hot reload will not work.

In order to make webpack copy HTML files from the src folder to the dist folder, you need to add the copy-webpack-plugin npm package to your project, and then add the following lines to your webpack.config.json file. These changes will configure webpack to copy all HTML files from src:

const CopyWebpackPlugin = require("copy-webpack-plugin");
// ...

module.exports = {
  plugins: [new CopyWebpackPlugin({ patterns: [{ from: "**/*.html", context: "src"}] })],
  // ...
};

VS Code's launch.json

The last configuration change we need to make is to set up a debug configuration for VS Code that launches Firefox with the correct path mappings. Add a path mapping with url set to webpack:/// and path set to ${workspaceFolder}/. To avoid restarting Firefox every time you debug, you can also set the reAttach property on the configuration to true.

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Launch Firefox",
      "type": "firefox",
      "request": "launch",
      "url": "https://localhost:3000/dist/hub/hub.html",
      "reAttach": true,
      "pathMappings": [
        {
          "url": "webpack:///",
          "path": "${workspaceFolder}/"
        }
      ]
    }
  ]
}

Using hot reload and debugging

Now that you have configured the extension, follow these steps to use hot reload and start debugging your code.

Install dependencies and build the extension

Before we deploy the extension, we need to install its dependencies and compile the code using the following commands:

npm install
webpack --mode development

Deploy your dev extension to Azure DevOps

You will need to deploy your extension to the marketplace at least once using the following command:

tfx extension publish --manifest-globs vss-extension.json --overrides-file configs/dev.json --token [token]

The [token] here is an Azure DevOps PAT (personal access token) with the Marketplace (Publish) scope and access set to All accessible organizations. For more information, see Authenticate access with personal access tokens.

After the extension is published, share it with your Azure DevOps organization and install it. Navigate to a project and you will find a new hub called "Hello World!" When you click it, you will notice that the hub won't load correctly. It isn't loading because the extension is configured to load all its resources (html, images, etc.) from localhost:3000, but the dev server isn't running yet.

Launch your dev server

Start the webpack-dev-server with:

webpack-dev-server --mode development

Now if you go to https://localhost:3000/dist/hub/hub.html in your browser, you should get an untrusted certificate error page. Select Advanced and then select Accept the Risk and Continue.

Go back to Azure DevOps and reload. Your extension should now load correctly and any changes to the source code will cause webpack to recompile and reload the extension automatically.

Although most code changes will be reflected immediately, you may still need to occasionally update your extension in the marketplace. The dev extension loads all its resources from the webpack-dev-server, but the manifest itself is being loaded from the published code. Therefore, any changes to the manifest file will not be properly reflected in Azure DevOps until the extension has been republished.

Launch your VS Code project to debug against Azure DevOps

In VS Code, press F5 to start debugging (making sure the webpack-dev-server is still running). The default launch configuration should be set to Launch Firefox.

Once Firefox starts up, you will have to go through the steps of allowing the localhost:3000 certificate again and log into your Azure DevOps account. From now on, if you leave this Firefox window open, the debugger will reattach instead of starting a clean Firefox instance each time.

Once you are logged in to Azure DevOps, your extension should be running. Set a breakpoint in a method in VS Code and you should see that breakpoint hit when that method executes.

Chrome configurations are included in the sample as well in case the Debugger for Chrome extension eventually supports iframes. However, debugging iframes is only supported in the Debugger for Firefox extension at this time.

Conclusion

We hope you enjoy the ability to iterate faster and debug right in VS Code!

If you want to start a new extension project, check out the Yeoman generator our team has built to get everything set up faster. Also, if you would prefer to debug your extension in Chrome, please upvote this GitHub issue and add in any relevant comments.

Contributing

This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.microsoft.com.

When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.

This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact [email protected] with any additional questions or comments.

azure-devops-extension-hot-reload-and-debug's People

Contributors

dependabot[bot] avatar dertosh avatar dwhathaway avatar ethanis avatar maciekwin3 avatar matissehack avatar microsoft-github-policy-service[bot] avatar microsoftopensource avatar msftgits avatar nathan-teoh avatar pabrams avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

azure-devops-extension-hot-reload-and-debug's Issues

Problem with new Extension

Hi!
I try to create new extension base on this repo.
My problem is during 'npm install'.
I have a problem with sass.
When I change version on: 7.0.1 I have another problems.
Has anybody tried to create azure-devops-extension (in the last few months) based on this repo?

Where is the default contribution visible?

The default contribution has a target of ms.vss-web.project-hub-groups-collection and type ms.vss-web.hub-group.

I'm trying to figure out where this contribution is actually made.

In old UI, it looks like it should show up here:
image

But in the new UI, I don't see an equivalent hub:
image

Looking at the extensibility points, I don't see the type or target listed there. Since the UI has changed, has this hub group been removed? Should this template be using a different hub as an example? Or am I missing something?

CopyWebpackPlugin error after package version updates

After updating package versions, the CopyWebpackPlugin fails.

Updating from the suggested:

plugins: [new CopyWebpackPlugin([{ from: "**/*.html", context: "src" }])]

To:

plugins: [new CopyWebpackPlugin({ patterns: [{ from: "**/*.html", context: "src" }] })]

seems to have fixed the problem.

Unverified Breakpoint message while trying to debug tsx file.

I am new to this, and have followed the steps. The debugging session starts fine, but if I try to add break points in my tsx file, it shows "Unverified Breakpoint" and the breakpoints are not hit.

image

The .vscode folder and launch.json are at the root, and I have a src folder inside it which had the code files.
Folder structure:
image

webpack.config.js:
image

launch.json is exactly as mentioned:
{ "version": "0.2.0", "configurations": [ { "name": "Launch Firefox", "type": "firefox", "request": "launch", "url": "https://localhost:3000/", "reAttach": true, "pathMappings": [ { "url": "webpack:///", "path": "${workspaceFolder}/" } ] } ] }

Any hep will be appreciated.

Icons not showing in released extension

I have noticed that after I released my extension, the icons are not appearing as demonstrated in the attached image. However, when I am debugging the application, everything appears to be functioning correctly.

image

Fonts and icons not loading after upgrading dependencies to latest versions

Fonts not loading after upgrading package versions:

image

package.json (updated dependencies

"dependencies": {
    "azure-devops-extension-api": "^1.158.0",
    "azure-devops-extension-sdk": "^2.0.11",
    "azure-devops-ui": "^2.167.46"
  },
  "devDependencies": {
    "@types/react": "~16.8.2",
    "@types/react-dom": "~16.8.0",
    "@typescript-eslint/eslint-plugin": "^5.18.0",
    "@typescript-eslint/parser": "^5.18.0",
    "base64-inline-loader": "^2.0.1",
    "copy-webpack-plugin": "^11.0.0",
    "css-loader": "^6.7.1",
    "eslint": "^8.12.0",
    "eslint-config-prettier": "^8.5.0",
    "eslint-plugin-prettier": "^4.0.0",
    "file-loader": "^6.2.0",
    "node-sass": "^7.0.3",
    "prettier": "^2.6.2",
    "react": "~16.13.1",
    "react-dom": "~16.13.1",
    "rimraf": "^3.0.2",
    "sass-loader": "^13.0.2",
    "style-loader": "^3.3.1",
    "tfx-cli": "^0.12.0",
    "ts-loader": "^9.2.8",
    "typescript": "^4.6.3",
    "webpack": "^5.74.0",
    "webpack-cli": "^4.10.0",
    "webpack-dev-server": "^3.11.2"
  }

webpack.config.json (only updated the breaking changes to CopyWebpackPlugin)

const path = require("path");
const fs = require("fs");
const CopyWebpackPlugin = require("copy-webpack-plugin");

const entries = {};
const srcDir = path.join(__dirname, "src");
fs.readdirSync(srcDir)
  .filter(dir => fs.statSync(path.join(srcDir, dir)).isDirectory())
  .forEach(dir => (entries[dir] = "./" + path.join("src", dir, dir)));

module.exports = {
  target: "web",
  entry: entries,
  output: {
    filename: "[name]/[name].js",
    publicPath: "/dist/",
  },
  devtool: "inline-source-map",
  devServer: {
    https: true,
    port: 3000,
  },
  resolve: {
    extensions: [".ts", ".tsx", ".js"],
    alias: {
      "azure-devops-extension-sdk": path.resolve(
        "node_modules/azure-devops-extension-sdk"
      )
    }
  },
  stats: {
    warnings: false
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: "ts-loader"
      },
      {
        test: /\.scss$/,
        use: [
          "style-loader",
          "css-loader",
          "azure-devops-ui/buildScripts/css-variables-loader",
          "sass-loader"
        ]
      },
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"]
      },
      {
        test: /\.woff$/,
        use: [
          {
            loader: "base64-inline-loader"
          }
        ]
      },
      {
        test: /\.html$/,
        use: "file-loader"
      }
    ]
  },
  plugins: [
    new CopyWebpackPlugin({
      //[{ from: "**/*.html", context: "src" }])
      patterns: [
        {
          from: "**/*.html",
          context: "src",
        }
      ]
    }),
  ]
};

Icons not showing

Hi there,

After downloading this project and testing it in an environment of mine, I noticed that the icons are not loading properly. Does anyone have a tip?

The error code in the console:

  1. Failed to decode downloaded font: https://{name}.gallery.vsassets.io/dist/acb0b44d710a15f95219.woff

  2. OTS parsing error: invalid sfntVersion: 218762506

Yeoman instructions need updating

I tried going down the route of using yo for scaffolding the project, and it uses an old template with 2 yo dependencies in package.json.
Here is a glimpse of npm outdated

Package Current Wanted Latest Location
@types/react 16.14.38 16.14.38 18.0.33 node_modules/@types/react
@types/react-dom 16.9.18 16.9.18 18.0.11 node_modules/@types/react-dom
@typescript-eslint/eslint-plugin 1.13.0 1.13.0 5.57.1 node_modules/@typescript-eslint/eslint-plugin
@typescript-eslint/parser 1.13.0 1.13.0 5.57.1 node_modules/@typescript-eslint/parser
azure-devops-ui 1.160.4 1.160.4 2.167.63 node_modules/azure-devops-ui
base64-inline-loader 1.1.1 1.1.1 2.0.1 node_modules/base64-inline-loader
copy-webpack-plugin 5.1.2 5.1.2 11.0.0 node_modules/copy-webpack-plugin
css-loader 2.1.1 2.1.1 6.7.3 node_modules/css-loader
eslint 5.16.0 5.16.0 8.37.0 node_modules/eslint
eslint-config-prettier 4.3.0 4.3.0 8.8.0 node_modules/eslint-config-prettier
eslint-plugin-prettier 3.4.1 3.4.1 4.2.1 node_modules/eslint-plugin-prettier
file-loader 3.0.1 3.0.1 6.2.0 node_modules/file-loader
prettier 1.19.1 1.19.1 2.8.7 node_modules/prettier
react 16.14.0 16.14.0 18.2.0 node_modules/react
react-dom 16.14.0 16.14.0 18.2.0 node_modules/react-dom
rimraf 2.7.1 2.7.1 4.4.1 node_modules/rimraf
style-loader 0.23.1 0.23.1 3.3.2 node_modules/style-loader
tfx-cli 0.7.11 0.7.11 0.14.0 node_modules/tfx-cli
ts-loader 6.2.2 6.2.2 9.4.2 node_modules/ts-loader
typescript 3.9.10 3.9.10 5.0.3 node_modules/typescript

.woff 404 Not Found in Production

When deploying my extension to production, icons from the azure-devops-ui library are not displaying. This issue does not occur in my development environment. In the browser console, I'm greeted with a 404 error pointing to:

https://[user].gallerycdn.vsassets.io/dist/[fileName].woff

However, accessing the resource directly via:

https://[user].gallerycdn.vsassets.io/extensions/[user]/[extensionName]/[Version]/[SomeNumber]/dist/[fileNmae].woff

shows that the file does indeed exist.

I'm using:
azure-devops-ui version: 2.167.75
Webpack version: 5.88.1

yo @microsoft/azure-devops-extension fails

When I run yo @microsoft/azure-devops-extension it fails with the following error:

AssertionError [ERR_ASSERTION]: Trying to copy from a source that does not exist: C:\Users\johndoe\AppData\Roaming\npm\node_modules\@microsoft\generator-azure-devops-extension\app\templates\.gitignore

After creating manually a .gitignore file in the C:\Users\johndoe\AppData\Roaming\npm\node_modules@microsoft\generator-azure-devops-extension\app\templates folder I could create the scaffolding for the Extension.

Stefan [msft]

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.