Skip to content

Write Package

Kyle T edited this page Aug 31, 2022 · 3 revisions

Centrally Manage Config With Packages

Utilizing the Config SDK package as a dependency of your own published package, you can modularize any customizable components and share them across multiple projects.

For example, if you manage a large number of _Node_ projects on CircleCI which all follow a similar workflow pattern, such as running a `test` and `deploy` job, with the `deploy` job only running on a given tag, this can be packaged and utilized in multiple projects.

Package

Start first by creating a new Javascript or Typescript Project, and import the CircleCI SDK package as a dependency

npm init
npm i --save @circleci/circleci-config-sdk

In this example, our package will export a single function which will take in a tag parameter for our deployment, and a path parameter for where to save the config file that will be generated.

const CircleCI = require("@circleci/circleci-config-sdk");
const fs = require('fs');

// Node executor
const dockerNode = new CircleCI.executors.DockerExecutor(
  "cimg/node:lts"
);

// Test Job
const testJob = new CircleCI.Job("test", dockerNode);
testJob.addStep(new CircleCI.commands.Checkout());
testJob.addStep(new CircleCI.commands.Run({ command: "npm install && npm run test" }));

//Deploy Job
const deployJob = new CircleCI.Job("deploy", dockerNode);
deployJob.addStep(new CircleCI.commands.Checkout());
deployJob.addStep(new CircleCI.commands.Run({ command: "npm run deploy" }));

//Instantiate Config and Workflow
const nodeConfig = new CircleCI.Config();
const nodeWorkflow = new CircleCI.Workflow("node-test-deploy");
nodeConfig.addWorkflow(nodeWorkflow);

//Add Jobs. Add filters to deploy job
nodeWorkflow.addJob(testJob);
const wfDeployJob = new CircleCI.workflow.WorkflowJob(deployJob, {requires: ["test"], filters: {branches: {ignore: ".*"}}});
nodeWorkflow.jobs.push(wfDeployJob);

/**
* Exports a CircleCI config for a node project
*/
export default function writeNodeConfig(deployTag, configPath) {
  wfDeployJob.parameters.filters.tags = {only: deployTag};
  fs.writeFile(configPath, nodeConfig.generate(), (err) => {
    if (err) {
      console.error(err);
      return
    }
  });
}

Publish the package

Modify the package.json file to give your package a semantic version and publish the package to NPM, GitHub, or another repository. https://docs.npmjs.com/creating-and-publishing-unscoped-public-packages

Import Package into projects

Check out the guide for using the Config SDK with Dynamic Config if you haven't already. We will use CircleCI's Dynamic Config feature to run the function in the package we just created and then execute the generated config.yml.

Within your .circleci directory, create a new directory to house the code for your "config application". The name used here is dynamic

└── .circleci/
    ├── dynamic/
    └── config.yml

Copy the config file from the Dynamic Workflow Example into your own .circleci/config.yml

Inside the dynamic folder, you will create a new Node project again using npm init -y and install the package you created in the last section npm i <your package>

In your package.json file, will now be an entry for the NodeConfig package you created.

"dependencies": {
  "my-circleci-node-config": "^1.0.0",
},

You can modify the semantic version string to dictate what version of your package should be pulled at run time. This means, you could update your my-circleci-node-config package, and if you choose, have all of your CircleCI projects which utilize this package, immediately pick up these changes the next time your CI pipeline is triggered.

https://docs.npmjs.com/about-semantic-versioning#using-semantic-versioning-to-specify-update-types-your-package-can-accept

DANGEROUS Say you always wanted to pull the latest version of the custom dependency you have created, you could use:

"dependencies": {
  "my-circleci-node-config": "x",
},

To be safer, pull in only minor and patch updates, not major releases.

"dependencies": {
  "my-circleci-node-config": "1.x",
},

Utilize

Finally, utilize your custom package in the .circleci/dynamic.index.js file. For our example, we could simply use it like so

import writeNodeConfig from '<your/package>';

writeNodeConfig("v.*", "../dynamicConfig.yml")