如何利用极狐GitLab 轻松管理NPM依赖发布与更新?

news2024/10/5 16:25:44

本文来自:

任治桐 极狐(GitLab)  前端工程师

NPM 是 Node.js 的包管理工具,用来安装各种 Node.js 的扩展。本文将分享如何通过极狐GitLab,让 NPM 依赖发布更新更加快速和自动化,让你轻松管理依赖,拥有更多时间专注于核心工作!

少年小明之烦恼


在开发团队日常工作中,不可避免的会依赖大量第三方模块;同时,团队内部也会发布一些公共模块到内部或外部源中,方便跨团队复用。但往往会遇到和小明一样的烦恼。

小明同学负责内部公共 NPM 模块发布和升级工作。他每天的工作是这样的:

1. 开发同学通知小明某个公共模块代码有更新;

2. 小明打包内部公共模块;

3. 发布到 NPM 源;

4. 在开发群里通知各个团队升级到最新版本。

渐渐的,每个团队都被繁琐小事缠身,低效:

  • 开发同学需要经常检查依赖的 NPM 模块是否有更新;

  • 公共模块的维护同学更新代码后需要决定是否发布版本;

  • 版本更新后还需要通知各个团队;

  • 各个团队还是容易出现更新不及时、容易遗漏等问题。

终于有一天,小明同学灵机一动:如果以后更新公共模块代码能够根据一定规则,自动更新版本号→自动发布日志→自动发布到 NPM 源→自动将公司内所有依赖该模块的代码库更新为最新版本,岂不乐哉?

经过一番探索,小明发现,通过极狐GitLab CI 和第三方工具结合,就可以达到目的。一起实践吧!

NPM 自动发布-操作指南


我们知道,NPM 包版本规范为 Semantic Versioning ,即为 major.minor.patch 格式数字组成。

那么,如果我们可以识别开发人员的 git commit message ,通过对提交信息进行形式化约定,就可以自动生成新的符合 Semantic Versioning 的版本号。然后,将新版本号更新到我们的 package.json 文件中,最后发布到 NPM 源中即可。

commitlint

首先,我们需要通过 commitlint 或类似工具,强制规范化团队的 git commit message ,通过如下命令将 commitlint 安装到项目中:

yarn add --dev @commitlint/cli @commitlint/config-conventional

# 设置 commitlint 配置文件使用 conventional config
echo "module.exports = { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js

我们采用 @commitlint/config-conventional 作为常规配置,它默认采用 Angular Commit Message 规范,也可自行修改。默认 git commit message 格式如下:

<type>(<scope>): <short summary> 
  │
  └─⫸ 可选类型: build|ci|docs|feat|fix|perf|refactor|test|chore

流水线配置

之后,需要在极狐GitLab CI 流水线中执行 commitlint ,在项目根目录新建一个 .gitlab-ci.yml 文件,添加如下代码:

# .gitlab-ci.yml

stages:
  - lint

workflow:
  rules:
    - if: $CI_MERGE_REQUEST_IID
    - if: $CI_COMMIT_TAG
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

default:
  image: node:16
  cache:
    paths:
      - node_modules/
      - .yarn

.yarn_install:
  before_script:
    - yarn install --frozen-lockfile --check-files --cache-folder .yarn

lint:commit:
  extends: .yarn_install
  stage: lint
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
  script:
    - yarn commitlint --from ${CI_MERGE_REQUEST_DIFF_BASE_SHA} --to HEAD --verbose

如果有不符合 commitlint 规范的 git commit message极狐GitLab CI 就会阻止该 MR 合入

这样,我们就拥有了符合约定的提交信息。

semantic-release

Semantic-release 是一个可以根据约定提交信息类型生成新版本号、极狐GitLab Changelog,还提供了大量插件用于发布 NPM 的工具。

首先安装 semantic-release 到我们的项目:

yarn add --dev semantic-release

与此同时,我们还想同步 publish 到 NPM 源,接着将更新的版本号推送到主分支,最后创建极狐GitLab Changelog。所以还需要安装如下 semantic-release plugins:

yarn add --dev @semantic-release/changelog @semantic-release/git @semantic-release/gitlab

由于 @semantic-release/npm 插件已经是 semantic-release 的一部分,所以不需要单独安装,参见文档 。

需要注意的是,通常情况下,我们只需针对主分支流水线执行:

  • 更新 package.json 版本号;

  • 推送 NPM 源;

  • 生成 changelog。

而在 MR 流水线中,我们只需确保能够正常生成 changelog 即可。为此,针对 MR 执行 --dry-run 模式,然后在 semantic-release 配置文件中区分执行步骤。

默认情况下,我们的提交信息会按照如下规则更新版本号:

完整的匹配规则参见 default-release-rules.js :

/**
 * Default `releaseRules` rules for common commit formats, following conventions.
 *
 * @type {Array}
 */
module.exports = [
  {breaking: true, release: 'major'},
  {revert: true, release: 'patch'},
  // Angular
  {type: 'feat', release: 'minor'},
  {type: 'fix', release: 'patch'},
  {type: 'perf', release: 'patch'},
  // Atom
  {emoji: ':racehorse:', release: 'patch'},
  {emoji: ':bug:', release: 'patch'},
  {emoji: ':penguin:', release: 'patch'},
  {emoji: ':apple:', release: 'patch'},
  {emoji: ':checkered_flag:', release: 'patch'},
  // Ember
  {tag: 'BUGFIX', release: 'patch'},
  {tag: 'FEATURE', release: 'minor'},
  {tag: 'SECURITY', release: 'patch'},
  // ESLint
  {tag: 'Breaking', release: 'major'},
  {tag: 'Fix', release: 'patch'},
  {tag: 'Update', release: 'minor'},
  {tag: 'New', release: 'minor'},
  // Express
  {component: 'perf', release: 'patch'},
  {component: 'deps', release: 'patch'},
  // JSHint
  {type: 'FEAT', release: 'minor'},
  {type: 'FIX', release: 'patch'},
];

如需修改上述规则,就需要添加 semantic-release 配置文件,新建 release.config.js 文件到项目根目录下:

const { execSync } = require('child_process');

const isDryRun = () => {
  return process.argv.includes('--dry-run'); // 通过命令行参数判断当前模式
};

const getCurrentBranch = () => {
  return execSync('git rev-parse --abbrev-ref HEAD').toString().trim();
};

// MR运行配置
const getDryRunConfig = () => {
  return {
    branches: getCurrentBranch(),
    plugins: [
      [
        '@semantic-release/commit-analyzer',
        {
          preset: 'conventionalCommits',
          releaseRules: [
            { type: 'feat', release: 'minor' },
            { type: 'revert', release: 'patch' },
            { type: 'docs', release: 'patch' },
            { type: 'style', release: 'patch' },
            { type: 'chore', release: 'patch' },
            { type: 'refactor', release: 'patch' },
            { type: 'test', release: 'patch' },
            { type: 'build', release: 'patch' },
            { type: 'ci', release: 'patch' },
            { type: 'improvement', release: 'patch' },
          ],
        },
      ],
      [
        '@semantic-release/release-notes-generator',
        {
          preset: 'conventionalCommits',
          presetConfig: {
            types: [
              { type: 'feat', section: 'Features' },
              { type: 'fix', section: 'Bug Fixes' },
              { type: 'perf', section: 'Performance Improvements' },
              { type: 'revert', section: 'Reverts' },
              { type: 'docs', section: 'Documentation', hidden: false },
              { type: 'style', section: 'Styles', hidden: true },
              { type: 'chore', section: 'Miscellaneous Chores' },
              { type: 'refactor', section: 'Code Refactors', hidden: false },
              { type: 'test', section: 'Tests', hidden: true },
              { type: 'build', section: 'Build System', hidden: false },
              { type: 'ci', section: 'CI/CD', hidden: false },
              { type: 'improvement', section: 'Improvements', hidden: false },
            ],
          },
        },
      ],
    ],
  };
};

// 主分支运行配置
const defaultConfig = {
  branches: ['main'],
  plugins: [
    [
      '@semantic-release/commit-analyzer',
      {
        preset: 'conventionalCommits',
        releaseRules: [
          { type: 'feat', release: 'minor' },
          { type: 'revert', release: 'patch' },
          { type: 'docs', release: 'patch' },
          { type: 'style', release: 'patch' },
          { type: 'chore', release: 'patch' },
          { type: 'refactor', release: 'patch' },
          { type: 'test', release: 'patch' },
          { type: 'build', release: 'patch' },
          { type: 'ci', release: 'patch' },
          { type: 'improvement', release: 'patch' },
        ],
      },
    ],
    [
      '@semantic-release/release-notes-generator',
      {
        preset: 'conventionalCommits',
        presetConfig: {
          types: [
            { type: 'feat', section: 'Features' },
            { type: 'fix', section: 'Bug Fixes' },
            { type: 'perf', section: 'Performance Improvements' },
            { type: 'revert', section: 'Reverts' },
            { type: 'docs', section: 'Documentation', hidden: false },
            { type: 'style', section: 'Styles', hidden: true },
            { type: 'chore', section: 'Miscellaneous Chores' },
            { type: 'refactor', section: 'Code Refactors', hidden: false },
            { type: 'test', section: 'Tests', hidden: true },
            { type: 'build', section: 'Build System', hidden: false },
            { type: 'ci', section: 'CI/CD', hidden: false },
            { type: 'improvement', section: 'Improvements', hidden: false },
          ],
        },
      },
    ],
    '@semantic-release/changelog', // 仅有主分支需要更新极狐GitLab changelog
    '@semantic-release/npm', // 仅有主分支需要npm publish
    '@semantic-release/git',
    ['@semantic-release/gitlab', { gitlabUrl: 'https://jihulab.com' }], // 需要指定gitlabUrl 为极狐GitLab地址
  ],
  success: false,
  fail: false,
};

module.exports = isDryRun() ? getDryRunConfig() : defaultConfig;

流水线配置

此时,就可以进入上文提到的 .gitlab-ci.yml 文件,添加 semantic-release 相关的 stage:

stages:
  - lint
  - test
  - release

workflow:
  rules:
    - if: $CI_MERGE_REQUEST_IID
    - if: $CI_COMMIT_TAG
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

default:
  image: node:16
  cache:
    paths:
      - node_modules/
      - .yarn

.yarn_install:
  before_script:
    - yarn install --frozen-lockfile --check-files --cache-folder .yarn

lint:commit:
  extends: .yarn_install
  stage: lint
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
  script:
    - yarn commitlint --from ${CI_MERGE_REQUEST_DIFF_BASE_SHA} --to HEAD --verbose

lint:prettier:
  extends: .yarn_install
  stage: lint
  script:
    - yarn lint:prettier

jest:test:
  extends: .yarn_install
  stage: test
  script:
    - yarn test

semantic-release:
  extends: .yarn_install
  stage: release
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
  script:
    - git config --global http.emptyAuth true
    - yarn semantic-release

# Run a dry run on Merge Requests
semantic-release-dry-run:
  needs: ['jest:test']
  script:
    - git config --global http.emptyAuth true
    - yarn semantic-release --branches $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME --dry-run --no-ci
  rules:
    - if: $CI_MERGE_REQUEST_IID

下面,我们就需要进入极狐GitLab CI 设置页面,添加对应的变量。

首先前往 https://www.npmjs.com/ 生成 access token 用来发布 NPM 模块:

然后,进入极狐GitLab 项目设置 → 访问令牌生成项目 access token,用来推送版本号更新,生成 release note 等:

最后,前往极狐GitLab 项目设置 → CI/CD → 变量新增  NPM_TOKEN,GITLAB_TOKEN 两个变量,分别输入刚才生成的 npm access token 和 项目 access token 。切记设为保护和隐藏变量,否则会存在 token 泄露的风险。

此时,我们所有准备工作都完成了。下面就提交一个 MR 试验一下:

该 MR 包含了一个 chore 类型的 commit message ,等待流水线通过后,合入该 MR。此时可以看到创建了一条主分支流水线,并且执行了 semantic-release 将版本号更新到 13.3.2 后 publish 到 NPM 源:

此时进入项目发布页面,可以看到 semantic-release 根据提交信息生成的极狐GitLab release-note:

🎉 至此,通过极狐GitLab CI 和第三方工具的结合,我们实现了:

  • 根据提交信息自动更新版本号;

  • 自动发布 NPM 模块;

  • 自动生成极狐GitLab release-note。

新的 NPM 模块发布流程,如下图所示:

下面让我们继续尝试,优化项目依赖 NPM 模块版本更新流程。

NPM 模块自动更新-操作指南


随着我们依赖的 NPM 模块增多,频繁手动更新版本也是一件麻烦事。下面就来介绍,如何通过极狐GitLab + renovate bot 自动更新第三方依赖版本号。

Renovate Bot

Renovate bot 是一款能够自动更新依赖版本的工具,不仅适用于 NPM 依赖,同样适用于 Docker、Ruby gem 等多种依赖。

Renovate bot 官方提供了一个 renovate-runner 项目,来帮助我们托管自己的 renovate bot。由于官方的 renovate-runner 位于 GitLab.com 。为了方便大家访问,我将这个项目镜像到了极狐GitLab 里,镜像项目地址位于:https://jihulab.com/gitlab-cn/frontend/renovate-runner

那么如何基于 renovate-runner 项目托管自己的 renovate 机器人呢?

1. 我们需要创建一个自己的 my-renovate-bot 项目。项目名字可自拟。

2. 在 my-renovate-bot 创建 .gitlab-ci.yml 文件:

include:
    - project: 'gitlab-cn/frontend/renovate-runner'
      file: '/templates/renovate-dind.gitlab-ci.yml'

variables:
  RENOVATE_ONBOARDING: 'false'
  RENOVATE_REQUIRE_CONFIG: 'ignored'

配置项含义如下:

  • project 配置为 renovate-runner 项目地址;

  • file 为我们需要使用流水线模板;

  • variables 可以对模板中变量的默认值进行覆盖。

3. 在 my-renovate-bot 下创建一个 renovate bot 配置文件 config.js :

module.exports = {
    endpoint: 'https://jihulab.com/api/v4/',
    platform: 'gitlab',
    labels: ['renovate', 'dependencies', 'automated'],
    includeForks: true,
    extends: ['config:base'],
    rangeStrategy: 'pin',
    enabledManagers: ["npm", "regex"],
    repositories: [{ 
        repository: 'your project path', 
        bumpVersion: true,
        internalChecksFilter: "strict",
        stabilityDays: 30,
        reviewersFromCodeOwners: true,
    }]
}

其中 repositories 的配置有两种方式,如上文所示,添加在 config.js 中可以针对不同项目使用不同的配置项。如果不需要针对各个项目单独定制,则可以在 my-renovate-bot 项目设置→ CI/CD 变量中添加 RENOVATE_EXTRA_FLAGS 配置项,以空格分隔多个项目名称即可:

4. 生成 RENOVATE_TOKEN 来方便 renovate bot 访问项目、创建 MR 。如果是个人使用,可以前往个人设置页面创建 Access Token 即可:

5. 为了方便 renovate bot 创建 MR 时携带上本次更新的 changelog,我们还需要前往 GitHub 生成一个 GITHUB_COM_TOKEN 。

设置定时任务

至此,我们的配置准备工作已经完成。下一步前往 my-renovate-bot 项目 → CI/CD → 计划 页面设置定时任务。我们可以选择每天凌晨自动执行 renovate bot ,它会自动扫描我们的 package.json 文件,获取最新依赖版本,然后创建更新版本号的 MR 。

注意:如果我们选择自定义执行时间,需要按照 Cron 语法 输入。大家可以使用这个工具 方便生成自己的 Cron 表达式。

到这里,我们所有的配置工作已经完成。下面就尝试触发 renovate bot 进行测试。前往我们在 config.js 或 RENOVATE_EXTRA_FLAGS 中配置的项目 MR 页面,可以看到 renovate-bot 已经帮助我们创建了三个 MR ,分别进行了固定版本号,升级 path 版本,升级 major 版本的操作。

进入 MR 详情,可以看到,renovate bot 帮助我们抓取了详细的 changelog 。表格中的字段含义分别是:

  • Age - 该版本发布至今的时间;

  • Adoption - 该版本在使用 renovate bot 的项目中接受安装的比例;

  • Passing - 该版本通过测试的更新的百分比;

  • Confidence - 该版本的可信度。

最后,我们只需要查看最新的 MR,选择是否需要合并即可。🎉

总结


如上图所示,我们已经实现了通过极狐GitLab 和 renovate bot 结合自动更新 NPM 依赖版本号的功能。

通过和第一部分所述 NPM 自动发布相结合,就可以实现不需要人工介入的 NPM 包自动发布 + 自动更新的工作流了,很大程度上节约开发时间,减少沟通成本,让 NPM 发布更新更及时,让研发工作更高效!爱钻研的小明,浅尝到了精英效能的奇妙滋味。😁

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/168792.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Java|golang】1814. 统计一个数组中好对子的数目

给你一个数组 nums &#xff0c;数组中只包含非负整数。定义 rev(x) 的值为将整数 x 各个数字位反转得到的结果。比方说 rev(123) 321 &#xff0c; rev(120) 21 。我们称满足下面条件的下标对 (i, j) 是 好的 &#xff1a; 0 < i < j < nums.length nums[i] rev(…

算法之美~时间复杂度

时间复杂度 时间复杂度的全称是渐进时间复杂度&#xff0c;表示算法的执行时间与数据规模之间的增长关系1、只关注循环执行次数最多的一段代码2、加法法则&#xff1a;总复杂度等于量级最大的那段代码的复杂度3、乘法法则&#xff1a;嵌套代码的复杂度等于嵌套内外代码复杂度的…

文末有福利 | 零代码连接邮箱腾讯云企业网盘,附件管理超轻松

在日常工作中&#xff0c;想必大家每天都会收到各种各样的工作邮件&#xff0c;并且很多重要的文件材料也是通过邮件附件的形式来传输的&#xff0c;那么如何一站式管理这些文件&#xff0c;对于提高办公效率就至关重要了。关于邮件附件&#xff0c;相信大家也都碰到过这样的困…

【软件相关】Typora配置图片上传

文章目录0 前言1 确定需求2 开始配置2.1 软件储备2.2 插件安装2.3 gitee配置3 其他配置3.1 获取SMMS token参考链接0 前言 对于喜欢写Markdown文档的人来说&#xff0c;Typora无疑是一个写作利器&#xff0c;它有别于其他的Markdown软件&#xff0c;不是一边编辑一边渲染&#…

excel区分工时制度:如何计算996与955的工时差异

什么是996呢&#xff1f;是指早上9点上班&#xff0c;晚上9点下班&#xff0c;中午和晚上各有1小时吃饭时间&#xff0c;每周工作6天的工作模式。而955&#xff0c;也就是朝九晚五&#xff0c;中午就餐在工作时间内&#xff0c;但不能休息&#xff0c;共计8小时工作时间&#x…

[FUNC]在AHK中实现Pyhton的range函数

本文是用AutoHotkey复刻Python的range函数函数语法range(start, stop, step)参数说明&#xff1a;参数描述start计数从 start 开始。默认是从 0 开始。例如&#xff1a;range&#xff08;6&#xff09;等价于range(0,6)。stop计数到 stop 结束&#xff0c;但不包括 stop。例如&…

5、Ubuntu20常用操作_进程管理重定向和管道常用命令网络管理

进程管理 进程的概念 大家比较熟悉 Windows 下的可执行文件&#xff0c;就是那些扩展名为exe的文件。 大家知道&#xff0c;只需要鼠标双击这些程序&#xff0c; 就可以运行了。 程序运行起来后&#xff0c;我们把这个程序正在运行的 实例 称之为 进程 。 操作系统对每个进…

acwing86场周赛题解(模拟,dp,数学推导式)

目录 第一题&#xff1a;AcWing 4794. 健身 思路 核心代码 完整代码 第二题&#xff1a;4795. 安全区域 思路 核心代码 完整代码 第三题&#xff1a;4796. 删除序列 思路 核心代码 完整代码 谢谢您的阅读 第一题&#xff1a;AcWing 4794. 健身 4794. 健身 李华…

配置与管理DNS服务器

DNS概念 DNS是一种新的主机名称和IP地址转换机制&#xff0c;使用分层的分布式数据库来处理Internet上众多的主机和IP地址转换。当一个应用需要将域名翻译成为IP地址时&#xff0c;这个应用便成为域名系统的一个客户。这个客户将待翻译的域名放在一个DNS请求信息中&#xff0c;…

CHAPTER 10 Web服务与应用(二)

Web服务与应用10.1 Tomcat10.1.1 准备工作10.1.2 编写dockerfile10.1.3 创建tomcat用户和密码脚本文件10.1.4 编写启动脚本10.1.5 创建和测试镜像10.1.6 相关资源10.2 Jetty10.2.1 使用官方镜像10.2.2 相关资料10.3 LAMP10.3.1 使用官方镜像10.4 持续开发与管理10.4.1 Jenkins及…

[前端笔记——HTML介绍] 5.文档与网站架构

[前端笔记——HTML介绍] 5.文档与网站架构1.文档的基本组成部分&#xff08;1&#xff09;页眉&#xff08;2&#xff09;导航栏&#xff08;3&#xff09;主内容&#xff08;4&#xff09;侧边栏&#xff08;5&#xff09;页脚2.用于构建内容的HTML3.HTML布局元素细节3.1无语义…

205:vue+openlayer: 点击某feature,列表滑动,定位到相应的点的列表位置

第205个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+openlayers做一个互动,点击某个feature,在左侧的列表中显示出来,滚动条滑动,能显示在视觉区内。 直接复制下面的 vue+openlayers源代码,操作2分钟即可运行实现效果; 注意如果OpenStreetMap无法加载,请加载其…

HTB打靶(Active Directory 101 Resolute)

nmap扫描 nmap -A -T4 10.10.10.169 Starting Nmap 7.93 ( https://nmap.org ) at 2023-01-16 01:30 EST Stats: 0:00:04 elapsed; 0 hosts completed (1 up), 1 undergoing SYN Stealth Scan SYN Stealth Scan Timing: About 74.65% done; ETC: 01:30 (0:00:01 remaining) St…

Maven学习(一):Maven简介及安装配置

Maven简介及安装配置一、Maven简介1.1、Maven是什么1.2、Maven的作用二、Maven安装配置2.1、大前提2.2、Maven下载2.3、windows版1、安装2、配置环境3、配置本地仓库2.4、mac版1、安装2、配置环境3、需要注意的点4、配置本地仓库一、Maven简介 1.1、Maven是什么 先对Maven做一…

从 Spectral Clustring 推导到 Regularized Diffusion Process

Spectral Clustring 参考&#xff1a;bilibili 机器学习-白板推导系列(二十二)-谱聚类&#xff08;Spectral Clustering&#xff09; Background 首先看一种数据分布&#xff1a; 对于以上分布的数据&#xff0c;可以直接利用K−meansK-meansK−means或者GMM&#xff08;高…

2、linux_CentOS_6_64位常用命令远程操作--yum云用不了_建议学习Ubuntu

Linux的概述 学习Linux之前先了解Unix Unix是一个强大的多用户、多任务操作系统。于1969年在AT&T的贝尔实验室开发。UNIX的商标权由国际开放标准组织&#xff08;The Open Group&#xff09;所拥有。UNIX操作系统是商业版&#xff0c;需要收费&#xff0c;价格比Microsof…

洞悉获客之道,林肯汽车开展高端社区精准营销俘获消费者芳心

一、出场即焦点 全新领航员诠释顶级美式豪华“强大的外部气场&#xff0c;肌肉与优雅完美结合”&#xff0c;一直以来&#xff0c;美式豪华汽车以沉稳、古典的高端奢华气质演绎“出场即焦点”的恢弘气场&#xff0c;吸引着无数精英人士为之着迷、追捧。2022 年&#xff0c;林肯…

C/C++宏定义注意事项

宏定义后不能加“&#xff1b;”&#xff0c;如果想查找宏可能带来的bug,可以增加编译选项&#xff1a;/P&#xff0c;然后选择仅编译&#xff0c; 这时会生成*.i的文件&#xff0c;打开后可以看到编译器替换宏以后的实际内容&#xff0c;然后再去查看相关的替换有没有错误。带…

夏普MX-M2658N复印机显示请放入载体组件

故障描述: 一台夏普MX-M2658N复印机一开机就显示请放入载体组件,重新再次开机有可能不显示但是复印或打印的时候一定会卡纸,卡纸有时候卡在硒鼓附近或者加热组件的位置; 故障处理: 1、碳粉质量差; 2

fiddler的自动响应器_小实验

目录 一、小实验介绍 二、fiddler的自动响应器的应用 1.找对自动响应器的位置&#xff0c;添加规则 2.编辑规则&#xff0c;将这个请求用fiddler的内置响应&#xff1b; 3.编辑规则&#xff0c;将这个请求重定向到本地资源&#xff1b; 4.编辑响应 一、小实验介绍 承接上…