npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tahminator/pipeline

v1.0.59

Published

A collection of Bun shell scripts that can be re-used in various CICD pipelines.

Readme

A collection of APIs built around Bun Shell that can be re-used in various CICD pipelines.

Examples

Setup

To install dependencies:

bun install

To run:

# note not all dependencies may run directly in a local environment
bun run src/index.ts

[!WARNING] This repository is iterating quickly & as such may have rough edges. I will always be happy to respond to & fix any issues anyone may have :)

Available Clients

import {
  DockerClient,
  EnvClient,
  GitHubClient,
  NPMClient,
  PulumiClient,
  SonarScannerClient,
  Utils,
  VersioningClient,
} from "@tahminator/pipeline";

[!NOTE] While there is documentation below, each API should be relatively well commented and be more up to date than what is seen below.

GitHubClient

Interface with GitHub / GitHub Actions API

This client has two auth strategies: createWithGithubAppToken() and createWithDefaultCiToken()

NOTE: If you want to automate pull requests, tags, or anything else that may trigger GitHub Actions, you must use createWithGithubAppToken(); there is no exception

It is strongly recommended that you basically always use createWithGithubAppToken()

const client = await GitHubClient.createWithGithubAppToken({
  appId: process.env.GH_APP_ID!,
  privateKey: await Utils.decodeBase64EncodedString(
    process.env.GH_PRIVATE_KEY_B64!,
  ),
  installationId: process.env.GH_INSTALLATION_ID!,
});

// output to env.GITHUB_OUTPUT to re-use outputs across steps, jobs, outputs, etc.
// Hover over type in IDE to see more details
await client.outputToGithubOutput({
  ctx: {
    imageTag: "1.2.3",
    deploy: { env: "production", healthy: true },
  },
});

// updates kubernetes manifest repo. Hover over type of function and each key in argument in IDE to see more details
await client.updateK8sTagWithPR({
  newTag: "1.2.3",
  imageName: "tahminator/my-service",
  kustomizationFilePath: "apps/prod/kustomization.yaml",
  environment: "production",
  originRepo: ["tahminator", "pipeline"],
  manifestRepo: ["tahminator", "infra"],
});

await client.sendPrMessage({
  owner: "tahminator",
  repository: "pipeline",
  prId: 123,
  message: "Deployed and healthy.",
});

// use VersioningClient to compute your next semver tag.
// look for `VersioningClient` section for how to configure / use
const versioning = new VersioningClient(client, VersionUpdatingStrategy.JSTS);

const nextTag =
  // requires gh app
  await client.createTag({
    nextTag: await versioning.next("1.5.0", {
      repositoryOverride: ["tahminator", "my-service"],
    }),
    repositoryOverride: ["tahminator", "my-service"],
    onPreTagCreate: async (tag) => {
      // update versions across your repo before tagging (strategy dependent)
      await versioning.update(tag);
      // you can write you own logic here if you would like
    },
  });
// client cannot do most automations without getting skipped by CICD, but it can do many read operations and all CI operations just fine
// uses GH_TOKEN from env
const client = await GitHubClient.createWithDefaultCiToken();

await client.outputToGithubOutput({
  ctx: { imageTag: "1.2.3" },
});

DockerClient

Interface with Docker to build & deploy images

// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#using-declarations-and-explicit-resource-management
await using client = await DockerClient.create(
  process.env.DOCKER_USERNAME!,
  process.env.DOCKER_PAT!,
);

// `buildImage` will build AND deploy
await client.buildImage({
  dockerFileLocation: "./Dockerfile",
  dockerRepository: "my-service",
  tags: ["latest", "sha-abc123"], // define as many as you would like
  platforms: ["linux/amd64", "linux/arm64"], // build for multiple platforms
  buildArgs: { NODE_ENV: "production" }, // pass in build args
});

await client.buildImage({
  dockerFileLocation: "./Dockerfile",
  dockerRepository: "my-service",
  tags: ["local-dev"],
  shouldUpload: false, // dry run, do not actually upload
});

// find tag by given name,
await client.promoteDockerImage({
  repository: "my-service",
  originalTag: "sha-abc123",
  newGithubTags: ["staging", "1.2.3"],
});

NPMClient

Interface with NPM registry in order to publish packages to npmjs.com

// uses trusted publishing, token based uploads are not supported
const npm = await NPMClient.create();

await npm.publish();

// dry run
await npm.publish(true);

// publish to the beta dist-tag instead of latest
// validate versions with `Utils.SemVer.validate(...)`
// and update versions with `VersioningClient.update(...)` if desired
await npm.publish(false, true);

SonarScannerClient

Interface with SonarQube and upload test coverage for basically any language / framework that is supported by Sonar Scanner.

import { $ } from "bun";

const backendClient = new SonarScannerClient({
  auth: {
    token: process.env.SONAR_TOKEN!,
  },
  run: {
    runTestsCmd: $`./mvnw clean verify -Dspring.profiles.active=ci`,
  },
  scan: {
    projectKey: "my-org_my-java-service",
    organization: "my-org",
    sourceCodeDir: "src/main/java",
    additionalArgs: {
      // all args are automatically wrapped in `-Dsonar.${key}=${value}`
      "java.binaries": "target/classes",
      "coverage.jacoco.xmlReportPaths": "target/site/jacoco/jacoco.xml",
    },
  },
});

await backendClient.runTests();
await backendClient.uploadTestCoverage();

const frontendClient = new SonarScannerClient({
  auth: {
    token: process.env.SONAR_TOKEN!,
  },
  run: {
    runTestsCmd: $`pnpm run --dir=js test`,
  },
  scan: {
    projectKey: "my-org_my-ts-service",
    organization: "my-org",
    sourceCodeDir: "js/src",
    additionalArgs: {
      "javascript.lcov.reportPaths": "js/coverage/lcov.info",
    },
  },
});

await frontendClient.runTests();
await frontendClient.uploadTestCoverage();

Utils

Assorted helper class used by clients and useful as an exported class to pipelines.

const githubPrivateKey = await Utils.decodeBase64EncodedString(
  process.env.GH_PRIVATE_KEY_B64!,
);

const shortId = Utils.generateShortId();

if (await Utils.isCmdAvailable("gh")) {
  console.log(Utils.Colors.green(`gh is installed (${shortId})`));
}

if (!Utils.SemVer.validate("1.2.3")) throw new Error("invalid version");

EnvClient

Load environment variables from encrypted files and automatically mask them in GitHub Actions logs.

Supports:

  • EnvClientStrategy.SOPS (YAML files decrypted via sops)
  • EnvClientStrategy.GIT_CRYPT (.env style files via git-crypt unlock)
const envClient = EnvClient.create(EnvClientStrategy.SOPS);

// YAML only
const env = await envClient.readFromEnv("production.yaml", {
  baseDir: "apps/backend",
});

// `env` is a Record<string, string>
console.log(env.DATABASE_URL);
// will automatically decrypt files via `git-crypt unlock`
const envClient = EnvClient.create(EnvClientStrategy.GIT_CRYPT);
const env = await envClient.readFromEnv(".env.ci");

PulumiClient

Interface with Pulumi Automation API (local workspace strategy).

const client = await PulumiClient.create({
  strategy: PulumiClientStrategy.AZURE,
  stackName: "production",
  workDir: "./infra",
  envs: {
    PULUMI_BACKEND_URL: process.env.PULUMI_BACKEND_URL!,
    ARM_CLIENT_ID: process.env.ARM_CLIENT_ID,
    ARM_CLIENT_SECRET: process.env.ARM_CLIENT_SECRET,
    ARM_TENANT_ID: process.env.ARM_TENANT_ID,
    ARM_SUBSCRIPTION_ID: process.env.ARM_SUBSCRIPTION_ID,
  },
});

const preview = await client.preview({
  diff: true,
  rewriteStdoutToDiffFriendly: true,
});

console.log(preview.stdout);
console.log(PulumiClient.parseChangeSumaryToPrettyTable(preview.changeSummary));

VersioningClient

Utilities to compute the next semver tag from GitHub and update versions in codebases.

const gh = await GitHubClient.createWithDefaultCiToken();

const versioning = new VersioningClient(gh, VersionUpdatingStrategy.JSTS);

// if your repo has no tags yet, this will return `1.0.0`
// if `baseVersion` is provided, its patch number must be `0` (e.g. `1.5.0`)
const next = await versioning.next("1.5.0");

// update versions across your repo (strategy dependent)
await versioning.update(next);
// beta tags
const gh = await GitHubClient.createWithDefaultCiToken();
const versioning = new VersioningClient(gh, VersionUpdatingStrategy.JSTS);

const sha = process.env.GITHUB_SHA!;
const shortSha = sha.slice(0, 8);

const beta = await versioning.nextBeta(shortSha);
if (!Utils.SemVer.validate(beta)) throw new Error("invalid beta version");

await versioning.update(beta);