This is an automated email from the ASF dual-hosted git repository.
wu-sheng pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking-nodejs.git
The following commit(s) were added to refs/heads/master by this push:
new 211d976 chore: add release automation scripts (release.sh +
release-finalize.sh) (#133)
211d976 is described below
commit 211d976da221be4db18e56f63d39c81188acbac9
Author: 吴晟 Wu Sheng <[email protected]>
AuthorDate: Tue Jun 23 21:06:33 2026 +0800
chore: add release automation scripts (release.sh + release-finalize.sh)
(#133)
---
.gitignore | 7 +
docs/How-to-release.md | 26 ++-
package.json | 6 +-
scripts/release-finalize.sh | 256 ++++++++++++++++++++++
scripts/release.sh | 358 +++++++++++++++++++++++++++++++
tests/plugins/axios/docker-compose.yml | 9 +
tests/plugins/axios/expected.data.yaml | 4 +-
tests/plugins/axios/server.ts | 2 +-
tests/plugins/common/base-compose.yml | 8 +
tests/plugins/express/docker-compose.yml | 9 +
tests/plugins/express/expected.data.yaml | 4 +-
tests/plugins/express/server.ts | 12 +-
tests/plugins/http/docker-compose.yml | 9 +
tests/plugins/http/expected.data.yaml | 4 +-
tests/plugins/http/server.ts | 12 +-
15 files changed, 700 insertions(+), 26 deletions(-)
diff --git a/.gitignore b/.gitignore
index d6ef811..e9bf187 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,10 @@ lib
src/proto/
*.log
tsconfig.tsbuildinfo
+
+# Release tooling working dirs + generated source artifacts
(scripts/release*.sh)
+scripts/.release-work/
+scripts/.finalize-work/
+skywalking-nodejs-src-*.tgz
+skywalking-nodejs-src-*.tgz.asc
+skywalking-nodejs-src-*.tgz.sha512
diff --git a/docs/How-to-release.md b/docs/How-to-release.md
index 3214fb6..fabe4be 100644
--- a/docs/How-to-release.md
+++ b/docs/How-to-release.md
@@ -2,10 +2,26 @@
This documentation guides the release manager to release the SkyWalking NodeJS
in the Apache Way, and also helps people to check the release for voting.
+## Automated release (recommended)
+
+Most of the steps below are automated by two scripts. Run them on a
single-user trusted host, with your Apache GPG key (an `@apache.org` uid,
already in the
[KEYS](https://dist.apache.org/repos/dist/release/skywalking/KEYS) file)
configured and Node >= 20:
+
+```shell
+# 1. Bump `version` in package.json to the release version and merge that PR
first.
+npm run release # GPG/preflight checks, fresh recursive clone,
build + sign the source
+ # release, verify it, push the tag, upload the RC
to svn dev, print the [VOTE] email
+# 2. ... after the vote passes (open >= 72h, >= 3 binding +1, more +1 than -1)
...
+npm run release:finalize # svn move dev -> release, publish the GitHub
release, optionally publish to npm
+```
+
+If you intend to publish to npm in `release:finalize`, run `npm login` first
(you must be a maintainer of `skywalking-backend-js`). The script verifies npm
auth **up front** — before the irreversible svn move — and auto-skips the npm
step if that version is already published.
+
+The rest of this guide is the reference those scripts implement, and the
fallback for running a step by hand.
+
## Prerequisites
1. Close (if finished, or move to next milestone otherwise) all issues in the
current milestone from
[skywalking-nodejs](https://github.com/apache/skywalking-nodejs/milestones) and
[skywalking](https://github.com/apache/skywalking/milestones), create a new
milestone for the next release.
-1. Update [CHANGELOG.md](../CHANGELOG.md) and `version` in
[package.json](../package.json).
+1. Update `version` in [package.json](../package.json). (CHANGELOG.md is a
stub — release notes are the auto-generated [GitHub
Release](https://github.com/apache/skywalking-nodejs/releases) notes.)
## Add your GPG public key to Apache svn
@@ -55,7 +71,7 @@ This is a call for vote to release Apache SkyWalking NodeJS
version $VERSION.
Release notes:
- * https://github.com/apache/skywalking-nodejs/blob/v$VERSION/CHANGELOG.md
+ * https://github.com/apache/skywalking-nodejs/releases/tag/v$VERSION
Release Candidate:
@@ -95,14 +111,14 @@ Thanks.
All PMC members and committers should check these before voting +1:
1. Features test.
-1. All artifacts in staging repository are published with `.asc`, `.md5`, and
`sha` files.
+1. All artifacts in staging repository are published with `.asc` and `.sha512`
files.
1. Source codes and distribution packages
(`skywalking-nodejs-src-$VERSION.tgz`)
are in `https://dist.apache.org/repos/dist/dev/skywalking/node-js/$VERSION`
with `.asc`, `.sha512`.
1. `LICENSE` and `NOTICE` are in source codes and distribution package.
1. Check `shasum -c skywalking-nodejs-src-$VERSION.tgz.sha512`.
1. Check `gpg --verify skywalking-nodejs-src-$VERSION.tgz.asc
skywalking-nodejs-src-$VERSION.tgz`.
1. Build distribution from source code package by following this [the build
guide](#build-and-sign-the-source-code-package).
-1. Licenses check, `make license`.
+1. License-header check via license-eye (`apache/skywalking-eyes`, as run by
`.github/workflows/license.yaml`); style lint via `npm run lint`.
Vote result should follow these:
@@ -166,7 +182,7 @@ Vote result should follow these:
Download Links: http://skywalking.apache.org/downloads/
- Release Notes :
https://github.com/apache/skywalking-nodejs/blob/v$VERSION/CHANGELOG.md
+ Release Notes :
https://github.com/apache/skywalking-nodejs/releases/tag/v$VERSION
Website: http://skywalking.apache.org/
diff --git a/package.json b/package.json
index 36f0289..2ee556e 100644
--- a/package.json
+++ b/package.json
@@ -19,8 +19,10 @@
"test": "DEBUG=testcontainers* jest",
"format": "prettier --write \"src/**/*.ts\"",
"clean": "(rm -rf src/proto || true) && (rm -rf src/proto || true) && (rm
-rf lib || true)",
- "package-src": "touch skywalking-nodejs-src-$npm_package_version.tgz &&
tar -zcvf skywalking-nodejs-src-$npm_package_version.tgz --exclude bin
--exclude .git --exclude .idea --exclude .DS_Store --exclude .github --exclude
node_modules --exclude skywalking-nodejs-src-$npm_package_version.tgz .",
- "release-src": "npm run prepare && npm run package-src && gpg --batch
--yes --armor --detach-sig skywalking-nodejs-src-$npm_package_version.tgz &&
shasum -a 512 skywalking-nodejs-src-$npm_package_version.tgz >
skywalking-nodejs-src-$npm_package_version.tgz.sha512"
+ "package-src": "touch skywalking-nodejs-src-$npm_package_version.tgz &&
tar -zcvf skywalking-nodejs-src-$npm_package_version.tgz --exclude bin
--exclude .git --exclude .idea --exclude .DS_Store --exclude .github --exclude
.claude --exclude node_modules --exclude
skywalking-nodejs-src-$npm_package_version.tgz .",
+ "release-src": "npm run prepare && npm run package-src && gpg --batch
--yes ${SW_GPG_KEY:+-u $SW_GPG_KEY} --armor --detach-sig
skywalking-nodejs-src-$npm_package_version.tgz && shasum -a 512
skywalking-nodejs-src-$npm_package_version.tgz >
skywalking-nodejs-src-$npm_package_version.tgz.sha512",
+ "release": "bash scripts/release.sh",
+ "release:finalize": "bash scripts/release-finalize.sh"
},
"files": [
"lib/**/*"
diff --git a/scripts/release-finalize.sh b/scripts/release-finalize.sh
new file mode 100755
index 0000000..7cef472
--- /dev/null
+++ b/scripts/release-finalize.sh
@@ -0,0 +1,256 @@
+#!/usr/bin/env bash
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Apache SkyWalking NodeJS — POST-VOTE release finalization.
+#
+# Run AFTER the [VOTE] on [email protected] passes (>=72h, >=3
+# binding +1, more +1 than -1). Second half of the flow; scripts/release.sh
+# is the first half.
+#
+# In order:
+# 1. Promote on svn: server-side move dev/<v>/ -> release/<v>/, then remove
+# the PREVIOUS (strictly-older) release (auto-archived to
archive.apache.org).
+# 2. Cut/publish the GitHub release on tag v<v> with auto-generated notes,
+# attaching the SAME voted bytes fetched back from svn release (checksum
+# AND signature verified before attaching).
+# 3. (optional, IRREVERSIBLE) publish skywalking-backend-js@<v> to npm,
+# built from a fresh clone of the tag.
+#
+# Every irreversible step confirms first. Run on a single-user trusted host.
+#
+# Usage: bash scripts/release-finalize.sh
+
+set -e -o pipefail
+
+SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
+PROJECT_DIR=$(cd "${SCRIPT_DIR}/.." && pwd)
+GH_REPO="apache/skywalking-nodejs"
+NPM_PACKAGE="skywalking-backend-js"
+REPO_URL="${SW_RELEASE_REPO_URL:-https://github.com/apache/skywalking-nodejs.git}"
+SVN_DEV_URL="https://dist.apache.org/repos/dist/dev/skywalking/node-js"
+SVN_RELEASE_URL="https://dist.apache.org/repos/dist/release/skywalking/node-js"
+KEYS_URL="https://dist.apache.org/repos/dist/release/skywalking/KEYS"
+WORK_DIR="${SCRIPT_DIR}/.finalize-work"
+
+# ========================== Helpers ==========================
+
+err() { echo "ERROR: $*" >&2; }
+note() { echo ""; echo "=== $* ==="; }
+
+confirm() {
+ local prompt="$1" ans
+ read -r -p "${prompt} [y/N] " ans || { err "No input (no TTY?)."; exit 1; }
+ [[ "$ans" == "y" || "$ans" == "Y" ]]
+}
+
+ask() {
+ local prompt="$1" val
+ read -r -p "${prompt}: " val || { err "No input for '${prompt}' (no
TTY?)."; exit 1; }
+ [ -n "$val" ] || { err "'${prompt}' must not be empty."; exit 1; }
+ printf '%s' "$val"
+}
+
+svn_exists() { svn ls "$1" >/dev/null 2>&1; }
+
+# ========================== Step 1: Preflight ==========================
+note "Step 1 — Tool + auth preflight"
+
+MISSING=()
+for t in svn gh git node npm gpg shasum curl; do
+ command -v "$t" >/dev/null || MISSING+=("$t")
+done
+if [ ${#MISSING[@]} -gt 0 ]; then err "Missing required tools: ${MISSING[*]}";
exit 1; fi
+gh auth status >/dev/null 2>&1 || { err "gh is not authenticated. Run: gh auth
login"; exit 1; }
+# Fetch tags up front so version auto-detect (Step 2) sees the RC tag even on
+# a checkout that never pulled it.
+(cd "${PROJECT_DIR}" && git fetch --tags --quiet origin) || err "git fetch
--tags failed (continuing with local tags)."
+echo "gh authenticated; tools present."
+
+# ========================== Step 2: Detect version ==========================
+note "Step 2 — Detect release version"
+
+DETECTED=$(cd "${PROJECT_DIR}" && git tag --list 'v*' --sort=-version:refname
| head -1 | sed 's/^v//')
+echo "Most recent git tag: v${DETECTED:-<none>}"
+read -r -p "Release version to finalize [${DETECTED}]: " RELEASE_VERSION || {
err "No input (no TTY?)."; exit 1; }
+RELEASE_VERSION="${RELEASE_VERSION:-${DETECTED}}"
+[ -n "${RELEASE_VERSION}" ] || { err "No release version provided."; exit 1; }
+TAG="v${RELEASE_VERSION}"
+SRC_TGZ="skywalking-nodejs-src-${RELEASE_VERSION}.tgz"
+
+# The tag MUST exist locally (we fetched in Step 1) — fail loudly, not later
+# inside an opaque gh error.
+(cd "${PROJECT_DIR}" && git rev-parse "${TAG}" >/dev/null 2>&1) || {
+ err "Tag ${TAG} not found locally or on origin. Did scripts/release.sh
push it?"; exit 1; }
+echo "Finalizing ${RELEASE_VERSION} (tag ${TAG})."
+confirm "Proceed?" || { echo "Aborted."; exit 1; }
+
+rm -rf "${WORK_DIR}"; mkdir -p "${WORK_DIR}"
+
+# ---- npm-publish intent + auth, decided NOW (before any irreversible step)
----
+# The npm publish is the last step (Step 5), but we decide whether you'll do it
+# and verify `npm login` HERE, so a missing login fails fast — BEFORE the svn
+# move (Step 3) and the GitHub release (Step 4), not after them.
+WILL_PUBLISH_NPM=false
+if npm view "${NPM_PACKAGE}@${RELEASE_VERSION}" version >/dev/null 2>&1; then
+ echo "npm: ${NPM_PACKAGE}@${RELEASE_VERSION} is already published — the
npm step will be skipped."
+elif confirm "Will you publish ${NPM_PACKAGE}@${RELEASE_VERSION} to npm in
this run? (the binding artifact is the svn tarball; npm is a convenience)"; then
+ NPM_USER=$(npm whoami 2>/dev/null || true)
+ [ -n "${NPM_USER}" ] || { err "You chose to publish to npm but are not
logged in. Run 'npm login' first, then re-run."; exit 1; }
+ echo "npm user: ${NPM_USER} — will publish after the GitHub release."
+ WILL_PUBLISH_NPM=true
+else
+ echo "npm publish will be skipped this run."
+fi
+
+# ========================== Step 3: svn move dev -> release
==========================
+note "Step 3 — Promote on svn: dev (RC) -> release (official)"
+
+echo " FROM (release candidate): ${SVN_DEV_URL}/${RELEASE_VERSION}/"
+echo " TO (official release): ${SVN_RELEASE_URL}/${RELEASE_VERSION}/"
+
+# NOTE: svn takes the password on argv; run only on a trusted host, never with
set -x.
+SVN_USER=$(ask "Apache SVN username")
+read -r -s -p "Apache SVN password: " SVN_PASS || { err "No SVN password (no
TTY?)."; exit 1; }
+echo ""
+[ -n "$SVN_PASS" ] || { err "SVN password must not be empty."; exit 1; }
+SVN_AUTH=(--username "${SVN_USER}" --password "${SVN_PASS}" --non-interactive
--no-auth-cache)
+
+if ! svn ls "${SVN_AUTH[@]}" "${SVN_DEV_URL}/${RELEASE_VERSION}" >/dev/null
2>&1; then
+ err "Release candidate not found at ${SVN_DEV_URL}/${RELEASE_VERSION}/.
Did scripts/release.sh upload it?"
+ exit 1
+fi
+
+if svn ls "${SVN_AUTH[@]}" "${SVN_RELEASE_URL}/${RELEASE_VERSION}" >/dev/null
2>&1; then
+ echo "Already present at release/${RELEASE_VERSION} — skipping the move
(idempotent)."
+else
+ if ! svn ls "${SVN_AUTH[@]}" "${SVN_RELEASE_URL}" >/dev/null 2>&1; then
+ echo "Creating ${SVN_RELEASE_URL}/ …"
+ svn mkdir --parents "${SVN_AUTH[@]}" -m "Create SkyWalking NodeJS
release directory" "${SVN_RELEASE_URL}"
+ fi
+ if confirm "Run the server-side svn mv now? (PMC-only action)"; then
+ svn mv "${SVN_AUTH[@]}" -m "Release Apache SkyWalking-NodeJS
${RELEASE_VERSION}" \
+ "${SVN_DEV_URL}/${RELEASE_VERSION}"
"${SVN_RELEASE_URL}/${RELEASE_VERSION}"
+ echo "Moved to ${SVN_RELEASE_URL}/${RELEASE_VERSION}/"
+ else
+ err "svn move skipped — cannot continue without the official
artifacts."
+ exit 1
+ fi
+fi
+
+# Remove ONLY a strictly-older previous release (never the one being released,
+# never a newer one). ASF keeps only the current release live; older versions
+# stay downloadable from archive.apache.org.
+PREV_RELEASE=$(svn ls "${SVN_AUTH[@]}" "${SVN_RELEASE_URL}/" 2>/dev/null \
+ | sed 's,/$,,' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | grep -vx
"${RELEASE_VERSION}" \
+ | sort -t. -k1,1n -k2,2n -k3,3n | tail -1 || true)
+if [ -n "${PREV_RELEASE}" ]; then
+ if printf '%s\n%s\n' "${PREV_RELEASE}" "${RELEASE_VERSION}" \
+ | sort -t. -k1,1n -k2,2n -k3,3n -C 2>/dev/null && [ "${PREV_RELEASE}"
!= "${RELEASE_VERSION}" ]; then
+ echo "Previous release to retire: ${SVN_RELEASE_URL}/${PREV_RELEASE}/"
+ read -r -p "To remove it, type the version '${PREV_RELEASE}' exactly
(blank = keep): " TYPED || TYPED=""
+ if [ "${TYPED}" = "${PREV_RELEASE}" ]; then
+ svn rm "${SVN_AUTH[@]}" -m "Remove superseded release
${PREV_RELEASE} (archived)" \
+ "${SVN_RELEASE_URL}/${PREV_RELEASE}"
+ echo "Removed release/${PREV_RELEASE}/."
+ else
+ echo "Kept release/${PREV_RELEASE}/."
+ fi
+ else
+ echo "WARN: latest other release ${PREV_RELEASE} is not strictly older
than ${RELEASE_VERSION}; not removing anything."
+ fi
+fi
+unset SVN_PASS; unset SVN_AUTH
+echo "Allow ~a few minutes for mirror propagation to downloads.apache.org
before updating website links."
+
+# ========================== Step 4: GitHub release ==========================
+note "Step 4 — GitHub release ${TAG}"
+
+# Fetch the VOTED bytes back from svn release so the GitHub release attaches
+# byte-identical files to what the PMC voted on (not a fresh rebuild).
+ART_DIR="${WORK_DIR}/artifacts"; mkdir -p "${ART_DIR}"
+for f in "${SRC_TGZ}" "${SRC_TGZ}.asc" "${SRC_TGZ}.sha512"; do
+ echo "Fetching ${f}…"
+ curl -fSL -o "${ART_DIR}/${f}" "${SVN_RELEASE_URL}/${RELEASE_VERSION}/${f}"
+done
+(cd "${ART_DIR}" && shasum -a 512 -c "${SRC_TGZ}.sha512")
+# Re-verify the binding signature too (not just the checksum).
+(cd "${ART_DIR}" && gpg --verify "${SRC_TGZ}.asc" "${SRC_TGZ}") \
+ || { err "GPG signature verify failed on the fetched release artifact. Is
the signing key in ${KEYS_URL}?"; exit 1; }
+echo "Checksum + signature verified."
+
+if gh release view "${TAG}" --repo "${GH_REPO}" >/dev/null 2>&1; then
+ echo "GitHub release ${TAG} already exists."
+ if confirm "Publish it (clear draft, mark latest) and (re)upload the voted
artifacts?"; then
+ gh release edit "${TAG}" --repo "${GH_REPO}" --draft=false --latest
+ gh release upload "${TAG}" --repo "${GH_REPO}" --clobber \
+ "${ART_DIR}/${SRC_TGZ}" "${ART_DIR}/${SRC_TGZ}.asc"
"${ART_DIR}/${SRC_TGZ}.sha512"
+ echo "GitHub release published."
+ fi
+else
+ PREV_TAG=$(cd "${PROJECT_DIR}" && git tag --list 'v*'
--sort=-version:refname | grep -vx "${TAG}" | head -1)
+ if confirm "Create GitHub release ${TAG} (auto-notes since
${PREV_TAG:-<none>}) and attach the artifacts?"; then
+ gh release create "${TAG}" --repo "${GH_REPO}" \
+ --title "Apache SkyWalking NodeJS ${RELEASE_VERSION}" \
+ --generate-notes ${PREV_TAG:+--notes-start-tag "${PREV_TAG}"}
--latest \
+ "${ART_DIR}/${SRC_TGZ}" "${ART_DIR}/${SRC_TGZ}.asc"
"${ART_DIR}/${SRC_TGZ}.sha512"
+ echo "GitHub release created."
+ fi
+fi
+
+# ========================== Step 5: npm publish (optional, IRREVERSIBLE)
==========================
+note "Step 5 — npm publish ${NPM_PACKAGE}@${RELEASE_VERSION} (optional,
IRREVERSIBLE)"
+
+# Intent + npm auth were already decided/verified in the preflight above.
+if ! $WILL_PUBLISH_NPM; then
+ echo "Skipping npm publish (decided in preflight)."
+else
+ echo "npm user: ${NPM_USER}"
+ # Build + publish from a FRESH clone of the tag so the published bytes
+ # match the released tag exactly (not your working tree).
+ PUB_DIR="${WORK_DIR}/publish-clone"
+ git clone --recurse-submodules --branch "${TAG}" "${REPO_URL}" "${PUB_DIR}"
+ cd "${PUB_DIR}"
+ npm install
+ npm run build
+ [ -f lib/index.js ] || { err "npm run build did not produce lib/index.js —
aborting publish."; exit 1; }
+ PUB_VERSION=$(node -e
"process.stdout.write(require('./package.json').version)")
+ [ "${PUB_VERSION}" = "${RELEASE_VERSION}" ] || { err "Clone version
${PUB_VERSION} != ${RELEASE_VERSION}."; exit 1; }
+ npm publish --dry-run
+ if confirm "Dry-run looks correct — run the REAL npm publish?"; then
+ if [ -n "${NPM_OTP:-}" ]; then npm publish --otp="${NPM_OTP}"; else
npm publish; fi
+ echo "Published ${NPM_PACKAGE}@${RELEASE_VERSION}."
+ else
+ echo "Skipped real npm publish."
+ fi
+ cd "${PROJECT_DIR}"
+fi
+
+# ========================== Done ==========================
+note "Done — ${RELEASE_VERSION} finalized"
+echo " svn release: ${SVN_RELEASE_URL}/${RELEASE_VERSION}/"
+echo " GitHub: https://github.com/${GH_REPO}/releases/tag/${TAG}"
+echo " npm: https://www.npmjs.com/package/${NPM_PACKAGE}"
+echo ""
+echo "Remaining MANUAL steps:"
+echo " 1. Website PR (apache/skywalking-website): add the release event, bump
the"
+echo " NodeJS Agent block in data/releases.yml and the docs pointer in
data/docs.yml."
+echo " 2. Send the [ANNOUNCE] email from your @apache.org address to"
+echo " [email protected] and [email protected]."
+echo ""
+echo "Working files left in ${WORK_DIR}/ (safe to delete)."
diff --git a/scripts/release.sh b/scripts/release.sh
new file mode 100755
index 0000000..4b99df4
--- /dev/null
+++ b/scripts/release.sh
@@ -0,0 +1,358 @@
+#!/usr/bin/env bash
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Apache SkyWalking NodeJS — release-candidate automation.
+#
+# Adapted from apache/skywalking-horizon-ui scripts/release.sh for the
+# single-package npm layout of skywalking-nodejs. Produces the Apache source
+# release, stages it for the vote, and prints the [VOTE] email:
+#
+# skywalking-nodejs-src-<v>.tgz {.asc,.sha512}
+#
+# The post-vote half lives in scripts/release-finalize.sh.
+#
+# PRECONDITION: the version bump to <v> is ALREADY merged on master
+# (package.json "version" == <v>). This script does NOT bump the version.
+# It builds the tarball from a FRESH recursive clone of master so the voted
+# bytes always match the tag, and — importantly — pushes the git tag ONLY
+# AFTER the artifacts are built, signed, and self-verified, so a build
+# failure never leaves a public, immutable release tag behind.
+#
+# Run interactively on a single-user trusted host (it reads your SVN
+# password). Requires bash, but is written to work on macOS bash 3.2.
+#
+# Usage: bash scripts/release.sh
+
+set -e -o pipefail
+
+SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
+PROJECT_DIR=$(cd "${SCRIPT_DIR}/.." && pwd)
+REPO_URL="${SW_RELEASE_REPO_URL:-https://github.com/apache/skywalking-nodejs.git}"
+REPO_BRANCH="${SW_RELEASE_BRANCH:-master}"
+SVN_DEV_URL="https://dist.apache.org/repos/dist/dev/skywalking/node-js"
+KEYS_URL="https://dist.apache.org/repos/dist/release/skywalking/KEYS"
+WORK_DIR="${SCRIPT_DIR}/.release-work"
+CLONE_DIR="${WORK_DIR}/skywalking-nodejs"
+
+TEST_FILE=""
+# Always clean up the throwaway GPG test file, even on Ctrl-C / early exit.
+trap 'rm -f "${TEST_FILE:-}" "${TEST_FILE:-}.asc"' EXIT
+
+# ========================== Helpers ==========================
+
+err() { echo "ERROR: $*" >&2; }
+note() { echo ""; echo "=== $* ==="; }
+
+confirm() {
+ local prompt="$1" ans
+ read -r -p "${prompt} [y/N] " ans || { err "No input (no TTY?)."; exit 1; }
+ [[ "$ans" == "y" || "$ans" == "Y" ]]
+}
+
+# Prompt for a required, non-empty value. $1=prompt var-name is echoed back
via stdout.
+ask() {
+ local prompt="$1" val
+ read -r -p "${prompt}: " val || { err "No input for '${prompt}' (no
TTY?)."; exit 1; }
+ [ -n "$val" ] || { err "'${prompt}' must not be empty."; exit 1; }
+ printf '%s' "$val"
+}
+
+# Read the package.json "version" without jq (runs on stock macOS / Alpine).
+read_version() {
+ node -e
"process.stdout.write(JSON.parse(require('fs').readFileSync('$1','utf8')).version)"
+}
+
+# ========================== Step 1: GPG signer ==========================
+note "Step 1 — GPG signer check"
+
+GPG_KEY_ID=$(git config user.signingkey 2>/dev/null || true)
+if [ -z "$GPG_KEY_ID" ]; then
+ GPG_KEY_ID=$(gpg --list-secret-keys --keyid-format LONG 2>/dev/null | grep
-A1 '^sec' | tail -1 | awk '{print $1}' || true)
+fi
+if [ -z "$GPG_KEY_ID" ]; then
+ err "No GPG secret key found. Configure your Apache GPG key first."
+ exit 1
+fi
+
+# Match @apache.org against ANY uid of the SELECTED key (not just the first
+# uid of a blind dump), and tolerate an empty result without aborting.
+GPG_EMAILS=$(gpg --list-keys --with-colons "${GPG_KEY_ID}" 2>/dev/null | awk
-F: '/^uid:/{print $10}' || true)
+if ! printf '%s\n' "${GPG_EMAILS}" | grep -q '@apache\.org'; then
+ err "Key ${GPG_KEY_ID} has no @apache.org uid — Apache releases must be
signed with an @apache.org key."
+ err "uids found: ${GPG_EMAILS:-<none>}"
+ exit 1
+fi
+echo "GPG Key: ${GPG_KEY_ID}"
+gpg --list-keys --keyid-format LONG "${GPG_KEY_ID}" | grep -E '^(pub|uid)' ||
true
+echo "Reminder: this key MUST already be in ${KEYS_URL} — every voter verifies
against it."
+confirm "Is this the correct signer?" || { echo "Aborted."; exit 1; }
+
+# Pin the signer end-to-end: package.json's release-src honors SW_GPG_KEY
+# (gpg -u), so the tarball is signed by THIS key, not gpg's default key.
+export SW_GPG_KEY="${GPG_KEY_ID}"
+
+export GPG_TTY=$(tty || true)
+echo "Verifying GPG signing works (you may be prompted for the passphrase)…"
+TEST_FILE=$(mktemp); echo "test" > "${TEST_FILE}"
+if ! gpg --batch --yes -u "${GPG_KEY_ID}" --armor --detach-sig "${TEST_FILE}"
2>/dev/null; then
+ err "GPG signing with ${GPG_KEY_ID} failed. Try: export GPG_TTY=\$(tty)
/ gpgconf --launch gpg-agent"
+ exit 1
+fi
+rm -f "${TEST_FILE}" "${TEST_FILE}.asc"; TEST_FILE=""
+echo "GPG signing OK (signer pinned to ${GPG_KEY_ID})."
+
+# ========================== Step 2: Required tools ==========================
+note "Step 2 — Tool check"
+
+MISSING=()
+for t in gpg svn shasum git gh node npm tar; do
+ command -v "$t" >/dev/null || MISSING+=("$t")
+done
+if [ ${#MISSING[@]} -gt 0 ]; then
+ err "Missing required tools: ${MISSING[*]}"
+ exit 1
+fi
+HAVE_LICENSE_EYE=true
+command -v license-eye >/dev/null || HAVE_LICENSE_EYE=false
+echo "All required tools present. node: $(node --version) npm: $(npm
--version)"
+$HAVE_LICENSE_EYE || echo "NOTE: license-eye not installed — header check will
be skipped (CI still enforces it)."
+
+# Node baseline: engines.node >=20, and grpc-tools' node-pre-gyp needs >=18.17.
+NODE_MAJOR=$(node -e
"process.stdout.write(String(process.versions.node.split('.')[0]))")
+[[ "${NODE_MAJOR}" =~ ^[0-9]+$ ]] || { err "Could not parse Node major version
('${NODE_MAJOR}')."; exit 1; }
+if [ "${NODE_MAJOR}" -lt 20 ]; then
+ err "Node ${NODE_MAJOR}.x is below the >=20 baseline; grpc-tools install
may also fail. Use Node 20/22/24."
+ exit 1
+fi
+
+# ========================== Step 3: Detect version ==========================
+note "Step 3 — Detect version"
+
+# skywalking-nodejs does NOT use a -dev suffix; master carries the release
+# number, bumped in a prior PR.
+RELEASE_VERSION=$(read_version "${PROJECT_DIR}/package.json")
+[ -n "$RELEASE_VERSION" ] || { err "Could not read version from
package.json."; exit 1; }
+echo "Release version (from package.json): ${RELEASE_VERSION}"
+confirm "Release this version?" || RELEASE_VERSION=$(ask "Enter the release
version to cut")
+TAG="v${RELEASE_VERSION}"
+
+# ========================== Step 4: Consistency check
==========================
+note "Step 4 — Consistency check"
+
+for f in LICENSE NOTICE package.json; do
+ [ -f "${PROJECT_DIR}/${f}" ] || { err "${f} missing at repo root."; exit
1; }
+done
+echo "LICENSE / NOTICE / package.json present."
+# Soft check: README's advertised baseline should not contradict engines.node.
+if grep -qiE 'NodeJS *>= *([0-9]|1[0-9])\b' "${PROJECT_DIR}/README.md"
2>/dev/null &&
+ ! grep -qiE 'NodeJS *>= *2[0-9]' "${PROJECT_DIR}/README.md" 2>/dev/null;
then
+ echo "WARN: README.md advertises a Node baseline below 20 while
engines.node is >=20. Fix before tagging."
+fi
+
+# ========================== Step 5: License-header check
==========================
+if $HAVE_LICENSE_EYE; then
+ note "Step 5 — License-header check (license-eye)"
+ (cd "${PROJECT_DIR}" && license-eye -c .licenserc.yaml header check)
+ echo "License headers OK."
+else
+ note "Step 5 — License-header check SKIPPED (license-eye absent; CI
enforces it)"
+fi
+
+# ========================== Step 6: Clone fresh + tag LOCALLY
==========================
+note "Step 6 — Fresh recursive clone + local tag ${TAG}"
+
+rm -rf "${WORK_DIR}"; mkdir -p "${WORK_DIR}"
+# --recurse-submodules is MANDATORY: protocol/ holds the .proto sources;
+# without it prepare->protoc.sh codegens nothing (Step 8 guards against this).
+echo "Cloning ${REPO_URL} (branch ${REPO_BRANCH}) with submodules…"
+git clone --recurse-submodules --branch "${REPO_BRANCH}" "${REPO_URL}"
"${CLONE_DIR}"
+
+CLONE_VERSION=$(read_version "${CLONE_DIR}/package.json")
+if [ "${CLONE_VERSION}" != "${RELEASE_VERSION}" ]; then
+ err "Fresh clone of ${REPO_BRANCH} has version ${CLONE_VERSION}, but you
are releasing ${RELEASE_VERSION}."
+ err "Merge the version-bump PR (package.json -> ${RELEASE_VERSION}) into
${REPO_BRANCH} first."
+ exit 1
+fi
+
+cd "${CLONE_DIR}"
+# Capture ls-remote success explicitly: a FAILED ls-remote must not be read
+# as "tag absent" (which would let us re-create an existing release tag).
+REMOTE_TAGS=$(git ls-remote --tags origin) || { err "git ls-remote origin
failed; cannot verify ${TAG} is unused."; exit 1; }
+if printf '%s\n' "${REMOTE_TAGS}" | grep -q "refs/tags/${TAG}$"; then
+ err "Tag ${TAG} already exists on origin. Delete it first to re-cut, or
pick a new version."
+ exit 1
+fi
+# Create the annotated tag LOCALLY only. It is pushed in Step 9, AFTER the
+# artifacts are built + verified — never before.
+git tag -a "${TAG}" -m "Release Apache SkyWalking-NodeJS ${RELEASE_VERSION}"
+RELEASE_COMMIT=$(git rev-parse "${TAG}")
+echo "Local tag ${TAG} created at ${RELEASE_COMMIT} (NOT pushed yet)."
+
+# ========================== Step 7: Build + sign source release
==========================
+note "Step 7 — Build + sign source tarball (npm run release-src)"
+
+# `npm install` runs prepare->scripts/protoc.sh (grpc-tools protoc; on Apple
+# Silicon under Rosetta). release-src then runs prepare again + package-src
+# (tar) + gpg detached sig (signer pinned via SW_GPG_KEY) + sha512.
+npm install
+npm run release-src
+
+SRC_TGZ="skywalking-nodejs-src-${RELEASE_VERSION}.tgz"
+for f in "${SRC_TGZ}" "${SRC_TGZ}.asc" "${SRC_TGZ}.sha512"; do
+ [ -f "${CLONE_DIR}/${f}" ] || { err "Expected artifact ${f} not produced
by release-src."; exit 1; }
+ cp "${CLONE_DIR}/${f}" "${WORK_DIR}/"
+done
+
+# ========================== Step 8: Verify the tarball
==========================
+note "Step 8 — Verify artifact contents + signature"
+
+cd "${WORK_DIR}"
+PROTO_COUNT=$(tar -tzf "${SRC_TGZ}" | grep -cE 'protocol/.*\.proto' || true)
+[ "${PROTO_COUNT}" -gt 0 ] || { err "Tarball contains 0 protocol/*.proto —
submodule was empty. Aborting."; exit 1; }
+if ! tar -tzf "${SRC_TGZ}" | grep -qE '(^|/)LICENSE$'; then err "Tarball
missing LICENSE."; exit 1; fi
+if ! tar -tzf "${SRC_TGZ}" | grep -qE '(^|/)NOTICE$'; then err "Tarball
missing NOTICE."; exit 1; fi
+if tar -tzf "${SRC_TGZ}" | grep -q 'node_modules/'; then err "Tarball
unexpectedly contains node_modules/."; exit 1; fi
+echo "Contents OK: ${PROTO_COUNT} .proto files, LICENSE + NOTICE present, no
node_modules."
+
+shasum -a 512 -c "${SRC_TGZ}.sha512"
+gpg --verify "${SRC_TGZ}.asc" "${SRC_TGZ}"
+echo "Checksum + signature self-verify OK."
+echo "Artifacts:"; ls -lh "${WORK_DIR}/${SRC_TGZ}"
"${WORK_DIR}/${SRC_TGZ}.asc" "${WORK_DIR}/${SRC_TGZ}.sha512"
+
+# ========================== Step 9: Push the tag (artifacts are good now)
==========================
+note "Step 9 — Push tag ${TAG}"
+
+TAG_PUSHED=false
+if confirm "Artifacts built + verified. Push tag ${TAG} to origin now? (needed
before the vote)"; then
+ (cd "${CLONE_DIR}" && git push origin "${TAG}")
+ TAG_PUSHED=true
+ echo "Pushed ${TAG}."
+else
+ echo "Tag ${TAG} NOT pushed. Push it (from ${CLONE_DIR}) before sending
the vote email:"
+ echo " git -C ${CLONE_DIR} push origin ${TAG}"
+fi
+
+# ========================== Step 10: Upload RC to svn dev
==========================
+note "Step 10 — Upload RC to ${SVN_DEV_URL}/${RELEASE_VERSION}"
+
+UPLOADED=false
+if confirm "Upload the release candidate to svn dev now?"; then
+ # NOTE: svn takes the password on argv (--password), so it is briefly
+ # visible in `ps`. Run this only on a single-user trusted host and never
+ # with `set -x`. The password is cleared from the environment below.
+ SVN_USER=$(ask "Apache SVN username")
+ read -r -s -p "Apache SVN password: " SVN_PASS || { err "No SVN password
(no TTY?)."; exit 1; }
+ echo ""
+ [ -n "$SVN_PASS" ] || { err "SVN password must not be empty."; exit 1; }
+ SVN_AUTH=(--username "${SVN_USER}" --password "${SVN_PASS}"
--non-interactive --no-auth-cache)
+
+ SVN_STAGE="${WORK_DIR}/svn-staging"
+ rm -rf "${SVN_STAGE}"
+ svn co --depth empty "${SVN_AUTH[@]}" "${SVN_DEV_URL}" "${SVN_STAGE}"
+ SVN_VERSION_DIR="${SVN_STAGE}/${RELEASE_VERSION}"
+ if svn ls "${SVN_AUTH[@]}" "${SVN_DEV_URL}/${RELEASE_VERSION}" >/dev/null
2>&1; then
+ echo "Version folder exists on svn — updating in place."
+ svn update "${SVN_AUTH[@]}" --set-depth infinity "${SVN_VERSION_DIR}"
+ else
+ mkdir -p "${SVN_VERSION_DIR}"
+ fi
+ cp "${WORK_DIR}/${SRC_TGZ}" "${WORK_DIR}/${SRC_TGZ}.asc"
"${WORK_DIR}/${SRC_TGZ}.sha512" "${SVN_VERSION_DIR}/"
+ (cd "${SVN_STAGE}" && svn add --force "${RELEASE_VERSION}" || true)
+ (cd "${SVN_STAGE}" && svn commit "${SVN_AUTH[@]}" -m "Draft Apache
SkyWalking-NodeJS release ${RELEASE_VERSION}")
+ UPLOADED=true
+ echo "Uploaded: ${SVN_DEV_URL}/${RELEASE_VERSION}"
+ unset SVN_PASS; unset SVN_AUTH
+else
+ echo "Skipped svn upload. Artifacts are in ${WORK_DIR}/."
+fi
+
+# ========================== Step 11: Vote email ==========================
+note "Step 11 — Vote email"
+
+if ! $TAG_PUSHED || ! $UPLOADED; then
+ echo "WARNING: tag pushed=${TAG_PUSHED}, RC uploaded=${UPLOADED}."
+ echo " Some links in the email below are DEAD until you push the
tag and/or upload the RC."
+ echo ""
+fi
+
+SRC_SHA512=$(cat "${WORK_DIR}/${SRC_TGZ}.sha512")
+VOTE_DATE=$(LC_ALL=C date +"%B %d, %Y")
+
+cat <<EOF
+
+========================================================================
+Vote Email — copy and send to [email protected]
+========================================================================
+
+Subject: [VOTE] Release Apache SkyWalking NodeJS version ${RELEASE_VERSION}
+
+Hi the SkyWalking Community,
+
+This is a call for vote to release Apache SkyWalking NodeJS version
${RELEASE_VERSION}.
+
+Release notes:
+
+ * https://github.com/apache/skywalking-nodejs/releases/tag/${TAG}
+
+Release Candidate:
+
+ * ${SVN_DEV_URL}/${RELEASE_VERSION}
+ * sha512 checksums
+ - ${SRC_SHA512}
+
+Release Tag :
+
+ * (Git Tag) ${TAG}
+
+Release Commit Hash :
+
+ * https://github.com/apache/skywalking-nodejs/tree/${RELEASE_COMMIT}
+
+Keys to verify the Release Candidate :
+
+ * ${KEYS_URL}
+
+Guide to build the release from source :
+
+ *
https://github.com/apache/skywalking-nodejs/blob/${TAG}/CONTRIBUTING.md#compiling-and-building
+
+Voting will start now (${VOTE_DATE}) and will remain open for at least 72
hours.
+A release passes with at least 3 binding +1 (PMC) votes and more +1 than -1.
+
+[ ] +1 Release this package.
+[ ] +0 No opinion.
+[ ] -1 Do not release this package because....
+
+Thanks.
+
+[1]
https://github.com/apache/skywalking-nodejs/blob/master/docs/How-to-release.md#vote-check
+========================================================================
+EOF
+
+note "Done — release candidate ${RELEASE_VERSION} staged"
+echo " Tag: ${TAG} ($($TAG_PUSHED && echo pushed || echo 'NOT
pushed — push it before voting'))"
+echo " Artifacts: ${WORK_DIR}/${SRC_TGZ}{,.asc,.sha512}"
+echo " svn dev staging: $($UPLOADED && echo
"${SVN_DEV_URL}/${RELEASE_VERSION}" || echo 'NOT uploaded')"
+echo ""
+echo "Next steps:"
+echo " 1. Draft the GitHub release notes (auto-generated; CHANGELOG.md is a
stub):"
+echo " gh release create ${TAG} --draft --generate-notes --verify-tag \\"
+echo " --notes-start-tag <previous tag> --title \"Apache SkyWalking
NodeJS ${RELEASE_VERSION}\""
+echo " 2. Send the [VOTE] email above to [email protected] (>=72h)."
+echo " 3. After the vote passes, run: bash scripts/release-finalize.sh"
diff --git a/tests/plugins/axios/docker-compose.yml
b/tests/plugins/axios/docker-compose.yml
index 0216550..c48ba93 100644
--- a/tests/plugins/axios/docker-compose.yml
+++ b/tests/plugins/axios/docker-compose.yml
@@ -25,6 +25,13 @@ services:
networks:
- traveling-light
+ httpbin:
+ extends:
+ file: ../common/base-compose.yml
+ service: httpbin
+ networks:
+ - traveling-light
+
server:
extends:
file: ../common/base-compose.yml
@@ -42,6 +49,8 @@ services:
depends_on:
collector:
condition: service_healthy
+ httpbin:
+ condition: service_started
client:
extends:
diff --git a/tests/plugins/axios/expected.data.yaml
b/tests/plugins/axios/expected.data.yaml
index 61e897e..c14df75 100644
--- a/tests/plugins/axios/expected.data.yaml
+++ b/tests/plugins/axios/expected.data.yaml
@@ -30,11 +30,11 @@ segmentItems:
endTime: gt 0
componentId: 4005
spanType: Exit
- peer: httpbin.org
+ peer: httpbin:8080
skipAnalysis: false
tags:
- key: http.url
- value: http://httpbin.org/json
+ value: http://httpbin:8080/json
- key: http.method
value: GET
- key: http.status_code
diff --git a/tests/plugins/axios/server.ts b/tests/plugins/axios/server.ts
index bbca28d..5233e5e 100644
--- a/tests/plugins/axios/server.ts
+++ b/tests/plugins/axios/server.ts
@@ -27,7 +27,7 @@ agent.start({
});
const server = http.createServer(async (req, res) => {
- const r = await axios.get('http://httpbin.org/json');
+ const r = await axios.get('http://httpbin:8080/json');
res.end(JSON.stringify(r.data));
});
diff --git a/tests/plugins/common/base-compose.yml
b/tests/plugins/common/base-compose.yml
index a74b56c..504dfa9 100644
--- a/tests/plugins/common/base-compose.yml
+++ b/tests/plugins/common/base-compose.yml
@@ -41,5 +41,13 @@ services:
networks:
- traveling-light
+ # Local httpbin so the HTTP-family plugin tests (http/axios/express) do not
depend
+ # on the flaky public httpbin.org. go-httpbin serves an identical /json
(HTTP 200),
+ # listening on container port 8080.
+ httpbin:
+ image: mccutchen/go-httpbin:2.23.1
+ networks:
+ - traveling-light
+
networks:
traveling-light:
diff --git a/tests/plugins/express/docker-compose.yml
b/tests/plugins/express/docker-compose.yml
index 69c9e35..bad6f4a 100644
--- a/tests/plugins/express/docker-compose.yml
+++ b/tests/plugins/express/docker-compose.yml
@@ -25,6 +25,13 @@ services:
networks:
- traveling-light
+ httpbin:
+ extends:
+ file: ../common/base-compose.yml
+ service: httpbin
+ networks:
+ - traveling-light
+
server:
extends:
file: ../common/base-compose.yml
@@ -42,6 +49,8 @@ services:
depends_on:
collector:
condition: service_healthy
+ httpbin:
+ condition: service_started
client:
extends:
diff --git a/tests/plugins/express/expected.data.yaml
b/tests/plugins/express/expected.data.yaml
index 51b0981..891f130 100644
--- a/tests/plugins/express/expected.data.yaml
+++ b/tests/plugins/express/expected.data.yaml
@@ -61,11 +61,11 @@ segmentItems:
endTime: gt 0
componentId: 2
spanType: Exit
- peer: httpbin.org
+ peer: httpbin:8080
skipAnalysis: false
tags:
- key: http.url
- value: http://httpbin.org/json
+ value: http://httpbin:8080/json
- key: http.method
value: GET
- key: http.status_code
diff --git a/tests/plugins/express/server.ts b/tests/plugins/express/server.ts
index 9ce3346..4e1dc42 100644
--- a/tests/plugins/express/server.ts
+++ b/tests/plugins/express/server.ts
@@ -30,12 +30,12 @@ const app = express();
app.get('/express', (req, res) => {
http
- .request('http://httpbin.org/json', (r) => {
- let data = '';
- r.on('data', (chunk) => (data += chunk));
- r.on('end', () => res.send(data));
- })
- .end();
+ .request('http://httpbin:8080/json', (r) => {
+ let data = '';
+ r.on('data', (chunk) => (data += chunk));
+ r.on('end', () => res.send(data));
+ })
+ .end();
});
app.listen(5000, () => console.info('Listening on port 5000...'));
diff --git a/tests/plugins/http/docker-compose.yml
b/tests/plugins/http/docker-compose.yml
index e5d8ca7..565d6dc 100644
--- a/tests/plugins/http/docker-compose.yml
+++ b/tests/plugins/http/docker-compose.yml
@@ -25,6 +25,13 @@ services:
networks:
- traveling-light
+ httpbin:
+ extends:
+ file: ../common/base-compose.yml
+ service: httpbin
+ networks:
+ - traveling-light
+
server:
extends:
file: ../common/base-compose.yml
@@ -42,6 +49,8 @@ services:
depends_on:
collector:
condition: service_healthy
+ httpbin:
+ condition: service_started
client:
extends:
diff --git a/tests/plugins/http/expected.data.yaml
b/tests/plugins/http/expected.data.yaml
index 22fdad8..370aae0 100644
--- a/tests/plugins/http/expected.data.yaml
+++ b/tests/plugins/http/expected.data.yaml
@@ -61,11 +61,11 @@ segmentItems:
endTime: gt 0
componentId: 2
spanType: Exit
- peer: httpbin.org
+ peer: httpbin:8080
skipAnalysis: false
tags:
- key: http.url
- value: http://httpbin.org/json
+ value: http://httpbin:8080/json
- key: http.method
value: GET
- key: http.status_code
diff --git a/tests/plugins/http/server.ts b/tests/plugins/http/server.ts
index bf362c7..629c1c7 100644
--- a/tests/plugins/http/server.ts
+++ b/tests/plugins/http/server.ts
@@ -27,12 +27,12 @@ agent.start({
const server = http.createServer((req, res) => {
http
- .request('http://httpbin.org/json', (r) => {
- let data = '';
- r.on('data', (chunk) => (data += chunk));
- r.on('end', () => res.end(data));
- })
- .end();
+ .request('http://httpbin:8080/json', (r) => {
+ let data = '';
+ r.on('data', (chunk) => (data += chunk));
+ r.on('end', () => res.end(data));
+ })
+ .end();
});
server.listen(5000, () => console.info('Listening on port 5000...'));