@4cloudguru/pipeline-task-core

中文文档
0.9.3发布于 2026/09/09Apache-2.0TypeScriptESM + CJS零依赖构建来源已验证近 30 天下载 4330

Shared HTTP, retry, egress and redaction primitives for the Azure Pipelines task extensions.

原始使用文档
文档目录7 节
  1. Why this exists
  2. Constraints that shaped the build
  3. Entrypoints
  4. Proxy support
  5. The credential-bearing transport
  6. Why it is not fetch
  7. Development

pipeline-task-core

@4cloudguru/pipeline-task-core — shared, versioned security primitives (HTTP client, retry, egress allowlisting, URL secret redaction, proxy configuration) consumed by every Azure DevOps pipeline-task extension in the estate:

Status: scaffold. The toolchain, CI and governance are in place, and one primitive (parseRetryAfterMs) is implemented to prove the build end to end. The remaining module inventory is Phase 0 work. The plan lives in docs/initiatives/initiative-1-shared-task-core.md and covers both this package and the new extension that motivates it.

Why this exists

The Terraform and Packer extensions each carry their own copy of the same HTTP/retry/egress primitives — 33 files and 8 files respectively — kept in step by a provenance-comment convention rather than by a compiler. That convention only checks the markers exist, not that their claims are true, so status prose rots while CI stays green. This package replaces the convention with a dependency.

Constraints that shaped the build

These differ deliberately from cloud-suite-ui, which is a browser/React package:

ConstraintValueWhy
Module formatdual CJS + ESMADO tasks are CommonJS and use import tl = require(...). CI asserts require() resolves.
Node floor20Every task declares a Node20_1 fallback handler for agents without the Node 24 runner.
Runtime dependenciesnoneopenpgp is an optional peer, reachable only via the ./gpg subpath.
Registrypublic npmjs, with provenanceNo consumer needs a token to install, and --provenance publishes a verifiable package → commit → workflow link.

Entrypoints

import { parseRetryAfterMs } from '@4cloudguru/pipeline-task-core'
import type { VerifyDetached } from '@4cloudguru/pipeline-task-core/gpg'

./gpg is separate so tasks that never verify release signatures do not vendor openpgp into their .vsix. It deliberately ships no signing key — callers pass their own armoured key in and keep their own key-freshness checks, because a trust root belongs in the repository that relies on it.

Proxy support

resolveProxy reads the configuration an Azure DevOps agent hands out. resolveEnvProxy reads the HTTPS_PROXY / HTTP_PROXY / NO_PROXY variables a GitHub self-hosted runner sets — in both spellings, lowercase winning — which Node's fetch honours on its own for none of them. Neither builds a dispatcher: undici stays a consumer dependency.

import { ProxyAgent } from 'undici'
import { createHttpClient, resolveEnvProxy } from '@4cloudguru/pipeline-task-core'

const client = createHttpClient({
  // Called for every attempt AND every redirect hop, with the URL about to be
  // issued — the answer depends on the destination, so a chain that redirects
  // off the origin has to be resolved again.
  fetchOptions: (url) => {
    const proxy = resolveEnvProxy(url)
    if (!proxy) return {}
    proxy.secrets.forEach((secret) => core.setSecret(secret)) // credentials in the URL are secrets
    return { dispatcher: new ProxyAgent(proxy.proxyUrl) } as RequestInit
  },
})

A proxy changes which socket carries the request, never which destination is permitted. assertEgressHostAllowed still runs against the destination host — the initial one and every redirect hop — and its subject is never the proxy: a CONNECT tunnel to an unauthorized host is still unauthorized egress.

The credential-bearing transport

createHttpClient is for downloading public artifacts over fetch. httpsRequest is the other transport: raw node:https, for the requests that carry a bearer token, an API key or a basic credential. That is why its refusal is phrased as refusing to send credentials over a non-https URL rather than refusing to fetch one.

createProxyTunnelAgent is the raw-https counterpart of resolveProxy: node:https honours no proxy setting unless handed an agent, so this builds one that opens an HTTP CONNECT tunnel and upgrades it to TLS. registerSecret is required, not optional — the derived base64 Basic credential is a byte sequence the caller never constructs and so could never think to mask.

import { createProxyTunnelAgent, httpsRequest, truncateBody } from '@4cloudguru/pipeline-task-core'

const { status, body } = await httpsRequest({
  method: 'POST',
  url: new URL(registryUrl), // parsed by the caller, which owns the "bad URL" message
  headers: { Authorization: `Bearer ${apiKey}` },
  body: Buffer.from(JSON.stringify(payload), 'utf8'),
  agent: createProxyTunnelAgent(tl.getHttpProxyConfiguration(), {
    tunnelTimeoutMs: DEFAULT_REQUEST_TIMEOUT_MS,
    registerSecret: (secret) => tl.setSecret(secret),
  }),
})
if (status >= 400) throw new Error(`Publish failed: ${truncateBody(body)}`)

Why it is not fetch

Two independent reasons, and both still hold:

  • undici stays out. This package has no runtime dependencies and is not growing one; consumers pin undici themselves, currently to 7.x because 8 breaks against the Node 24 bundles.
  • The consumers mock with nock and inject an agent. fetch has no agent option at all, so a port would silently drop agent-proxy support — the tunnel above — and every test that drives a request through a real CONNECT proxy with it.

A status is returned, never thrown on: callers differ on what a non-2xx means, so the decision stays with them. truncateBody is the bound that keeps a remote body from choosing how much of the job log a failure occupies; truncateForLog is the stronger helper (it also strips the C0 characters that forge a logging command) and is where a new caller should start.

Development

npm ci
npm run typecheck
npm test
npm run build

Commits follow Conventional Commits (enforced on PR titles); releases are cut by release-please.

版本与发布记录

@4cloudguru/pipeline-task-core 共发布 20 个版本,首次发布于 2026/08/12;过去 12 个月发布了 20 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.9.3latest2026/09/091 个月前
0.9.12026/09/081 个月前
0.9.02026/09/072 个月前
0.8.02026/09/012 个月前
0.7.22026/08/312 个月前
0.7.12026/08/252 个月前

关于 @4cloudguru/pipeline-task-core 的常见问题

@4cloudguru/pipeline-task-core 是什么?

Shared HTTP, retry, egress and redaction primitives for the Azure Pipelines task extensions.

如何安装 @4cloudguru/pipeline-task-core?

在项目目录运行 npm install @4cloudguru/pipeline-task-core,或使用 pnpm add / yarn add / bun add。

@4cloudguru/pipeline-task-core 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@4cloudguru/pipeline-task-core 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@4cloudguru/pipeline-task-core 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@4cloudguru/pipeline-task-core 可以商用吗?

@4cloudguru/pipeline-task-core 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@4cloudguru/pipeline-task-core 的下载量有多少?

根据 npm 官方下载统计,@4cloudguru/pipeline-task-core 在 2026/10/05 – 2026/11/03 这 30 天内被下载 4,330 次,日均约 144 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@4cloudguru/pipeline-task-core 还在维护吗?

最新版本 0.9.3 发布于 2026/09/09(1 个月前);至今共发布 20 个版本,其中过去 12 个月发布了 20 个;首次发布于 2026/08/12。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。