diff --git a/.changeset/sharp-pens-accept.md b/.changeset/sharp-pens-accept.md new file mode 100644 index 0000000..620e357 --- /dev/null +++ b/.changeset/sharp-pens-accept.md @@ -0,0 +1,5 @@ +--- +"@slack/slack-github-action": patch +--- + +feat: add instrumentation to address error rates diff --git a/package.json b/package.json index f3abdaa..2b11359 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "slack-github-action", + "name": "@slack/slack-github-action", "version": "3.0.2", "private": true, "description": "The official Slack GitHub Action. Use this to send data into your Slack workspace", diff --git a/src/config.js b/src/config.js index 2294447..9febb98 100644 --- a/src/config.js +++ b/src/config.js @@ -1,5 +1,7 @@ +import os from "node:os"; import webapi from "@slack/web-api"; import axios from "axios"; +import packageJson from "../package.json" with { type: "json" }; import Content from "./content.js"; import SlackError from "./errors.js"; import Logger from "./logger.js"; @@ -119,6 +121,7 @@ export default class Config { core.getInput("webhook") || process.env.SLACK_WEBHOOK_URL || null, webhookType: core.getInput("webhook-type"), }; + this.instrument(); this.mask(); this.validate(core); core.debug(`Gathered action inputs: ${JSON.stringify(this.inputs)}`); @@ -126,6 +129,21 @@ export default class Config { core.debug(`Parsed request content: ${JSON.stringify(this.content)}`); } + /** + * Add user agent metadata for instrumentation. + */ + instrument() { + this.webapi.addAppMetadata({ + name: packageJson.name, + version: packageJson.version, + }); + this.axios.defaults.headers.common["User-Agent"] = + `${packageJson.name.replace("/", ":")}/${packageJson.version} ` + + `axios/${this.axios.VERSION} ` + + `node/${process.version.replace("v", "")} ` + + `${os.platform()}/${os.release()}`; + } + /** * Hide secret values provided in the inputs from appearing. */ diff --git a/test/config.spec.js b/test/config.spec.js index db105c5..17c292f 100644 --- a/test/config.spec.js +++ b/test/config.spec.js @@ -1,5 +1,7 @@ import assert from "node:assert"; import { beforeEach, describe, it } from "node:test"; +import webapi from "@slack/web-api"; +import sinon from "sinon"; import Config from "../src/config.js"; import SlackError from "../src/errors.js"; import send from "../src/send.js"; @@ -158,6 +160,46 @@ describe("config", () => { }); }); + describe("instrument", () => { + it("adds metadata to webapi with package name and version", async () => { + const stub = sinon.stub(); + const original = Object.getOwnPropertyDescriptor( + webapi, + "addAppMetadata", + ); + Object.defineProperty(webapi, "addAppMetadata", { + value: stub, + configurable: true, + }); + try { + mocks.core.getInput.withArgs("method").returns("chat.postMessage"); + mocks.core.getInput.withArgs("token").returns("xoxb-example"); + new Config(mocks.core); + assert.ok(stub.calledOnce); + const { name, version } = stub.firstCall.args[0]; + assert.equal(name, "@slack/slack-github-action"); + assert.ok(version); + } finally { + Object.defineProperty(webapi, "addAppMetadata", original); + } + }); + + it("adds metadata to webhook with package name and version", async () => { + mocks.core.getInput.withArgs("method").returns("chat.postMessage"); + mocks.core.getInput.withArgs("token").returns("xoxb-example"); + const config = new Config(mocks.core); + assert.ok( + config.axios.defaults.headers.common["User-Agent"].startsWith( + "@slack:slack-github-action/", + ), + ); + assert.ok( + config.axios.defaults.headers.common["User-Agent"].length > + "@slack:slack-github-action/".length, + ); + }); + }); + describe("mask", async () => { it("treats the provided token as a secret", async () => { mocks.core.getInput.withArgs("token").returns("xoxb-example");