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 🙏

© 2024 – Pkg Stats / Ryan Hefner

@octotask/octokit-plugin-config

v2.0.1

Published

Get/set persisted configuration using YAML/JSON files in repositories

Downloads

430

Readme

octokit-plugin-config

Get/set persisted configuration using YAML/JSON files in repositories

@latest Build Status Dependabot Status

By default, this plugin loads configuration from a given repository file. If the file doesn't exist, it loads configuration from the same path in the same owner's .github repository.

Configuration can be extended across multiple files using the _extends key.

Usage

Browsers

Load @octotask/octokit-plugin-config and @octokit/core (or core-compatible module) directly from cdn.pika.dev

<script type="module">
  import { Octokit } from "https://cdn.pika.dev/@octokit/core";
  import {
    config,
    composeConfigGet,
  } from "https://cdn.pika.dev/@octotask/octokit-plugin-config";
</script>

Node

Install with npm install @octokit/core @octotask/octokit-plugin-config. Optionally replace @octokit/core with a compatible module

const { Octokit } = require("@octokit/core");
const { config, composeConfigGet } = require("@octotask/octokit-plugin-config");
// given that `.github/my-app.yml` in `octocat/hello-world` has the following ocntent
//
// comment: 'Thank you for creating the issue!'
//
const { config } = await octokit.config.get({
  owner: "octocat",
  repo: "hello-world",
  path: ".github/my-app.yml",
});
// config is now { comment: "Thank you for creating the issue!" }

// all options and returns
const { config, files } = await octokit.config.get({
  owner: "octocat",
  repo: "hello-world",
  path: ".github/my-app.yml",
  defaults: {
    comment: "Thank you for creating the issue!",
  },
  branch: "develop",
});
// files is an array of { owner, repo, path, config } objects

Options

The _extends key

octokit.config.get() supports sharing configs between repositories. If configuration for your app is not available in the target repository, it will be loaded from the .github directory of the same owner's .github repository.

You can choose own shared location. Use the _extends option in the configuration file to extend settings from another repository.

For example, given .github/test.yml:

_extends: github-settings
# Override values from the extended config or define new values
name: myrepo

This configuration will be merged with the .github/test.yml file from the github-settings repository, which might look like this:

shared1: will be merged
shared2: will also be merged

Just put common configuration keys in a repository within your organization. Then reference this repository from config files with the same name.

You can also reference configurations from other owners:

_extends: other/octotask-settings
other: DDD

Additionally, you can specify a specific path for the configuration by appending a colon after the project.

_extends: octotask-settings:.github/other_test.yml
other: FFF

Merging configuration

Given .github/test.yml:

settings:
  one: value from configuration

And

const { config } = await octokit.config.get({
  owner,
  repo,
  path: ".github/test.yml",
  defaults: {
    settings: {
      one: "default value",
      two: "default value",
    },
  },
});

The resulting config object is

{
  settings: {
    one: "value from configuration";
  }
}

And not as you might expect

{
  settings: {
    one: "value from configuration";
    two: "default value";
  }
}

The reason for that behavior is that merging objects deeply is not supported in JavaScript by default, and there are different strategies and many pitfals. There are many libraries that support deep merging in different ways, but instead making that decision for and significantly increasing the bundle size of this plugin, we let you pass a custom merge strategy instead.

In order to achive the deeply merged configuration, the defaults option can be set to a function. The function receives one configs argument, which is an array of configurations loaded from files in reverse order, so that the latter items should take precedence over the former items. The configs array can have more than one object if the _extends key is used.

const defaults = {
  settings: {
    one: "default value",
    two: "default value",
  },
};
const { config } = await octokit.config.get({
  owner,
  repo,
  path: ".github/test.yml",
  defaults(configs) {
    const allConfigs = [defaults, ...configs];
    const fileSettingsConfigs = allConfigs.map(
      (config: Configuration) => config.settings
    );
    return Object.assign({}, ...allConfigs, {
      settings: Object.assign({}, ...fileSettingsConfigs),
    });
  },
});

Or simpler, using a library such as deepmerge

const { config } = await octokit.config.get({
  owner,
  repo,
  path: ".github/test.yml",
  defaults: (configs) => deepmerge.all([defaults, ...configs]),
});

Testing

Writing tests for your app's usage of octokit.config.get can be tricky. It's tempting to just mock the method directly, e.g. using a Jest mock function

octokit.config.get = jest.fn().mockResolvedValue({
  comment: "Thank you for creating the issue!",
});

The problem with this approach is that in future releases of @octotask/octokit-plugin-config, the method name or parameters might change. Before that happens, we will log a deprecation message, to make the upgrade to the next breaking version easier. If all your tests mock the .config.get() method, then you won't see this deprecation message. Even worse, your tests will continue to pass, but fail in production, because the mock will revert any future changes to .config.get().

We recommend you have at least one test that does not mock the method, but instead mocks the http responses. You can achiev that with nock or fetch-mock

Testing with nock

With configuration

async function myTest() {
  nock("https://api.github.com")
    .get("/repos/octocat/hello-world/contents/.github%2Fmy-app.yml")
    .reply(200, "comment: Thank you for creating the issue");

  const octokit = new Octokit();

  const { config } = await octokit.config.get({
    owner: "octocat",
    repo: "hello-world",
    path: ".github/my-app.yml",
  });

  assert.deepStrictEqual(config, {
    comment: "Thank you for creating the issue!",
  });
}

Without configuration

async function myTest() {
  nock("https://api.github.com")
    .get("/repos/octocat/hello-world/contents/.github%2Fmy-app.yml")
    .reply(404)
    .get("/repos/octocat/.github/contents/.github%2Fmy-app.yml")
    .reply(404);

  const octokit = new Octokit();

  const { config } = await octokit.config.get({
    owner: "octocat",
    repo: "hello-world",
    path: ".github/my-app.yml",
  });

  assert.deepStrictEqual(config, {});
}

Testing with fetch-mock

With configuration

async function myTest() {
  const fetch = fetchMock
    .sandbox()
    .getOnce(
      "https://api.github.com/repos/octocat/hello-world/contents/.github%2Fmy-app.yml",
      "comment: 'Thank you for creating the issue!'",
    );
  const octokit = new TestOctokit({
    request: { fetch },
  });

  const { config } = await octokit.config.get({
    owner: "octocat",
    repo: "hello-world",
    path: ".github/my-app.yml",
  });

  assert.deepStrictEqual(config, {
    comment: "Thank you for creating the issue!",
  });
}

Without configuration

async function myTest() {
  const fetch = fetchMock
    .sandbox()
    .getOnce(
      "https://api.github.com/repos/octocat/hello-world/contents/.github%2Fmy-app.yml",
      404,
    )
    .getOnce(
      "https://api.github.com/repos/octocat/.github/contents/.github%2Fmy-app.yml",
      404,
    );
  const octokit = new TestOctokit({
    request: { fetch },
  });

  const { config } = await octokit.config.get({
    owner: "octocat",
    repo: "hello-world",
    path: ".github/my-app.yml",
  });

  assert.deepStrictEqual(config, {});
}

Contributing

See CONTRIBUTING.md

Credits

The idea for this plugin and some of its code was extracted from Octotask. It originated as octotask-config, created by Jan Michael Auer and was later merged into octotask.

License

ISC