Skip to content

Commit

Permalink
feat: prep dist (#98)
Browse files Browse the repository at this point in the history
- feat: improve 3rd-party license gathering 
- feat: add CLI wrapper
- tests: improve tests
- docs: improve docs
- build: add dist-build

closes #55 - since the installable package includes a CLI wrapper

---------

Signed-off-by: Jan Kowalleck <jan.kowalleck@gmail.com>
  • Loading branch information
jkowalleck committed May 17, 2024
1 parent 272e2ce commit 088301f
Show file tree
Hide file tree
Showing 11 changed files with 330 additions and 189 deletions.
2 changes: 1 addition & 1 deletion .c8rc.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"all": true,
"src": ["src"],
"src": ["src", "bin"],
"exclude": [
"**/*.{spec,test}.{js,cjs,mjs}",
"**/*.cache/**",
Expand Down
9 changes: 9 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,15 @@ module.exports = {
/* see https://www.npmjs.com/package/eslint-config-standard */
'standard'
]
},
{
files: ['bin/*.js'],
rules: {
// region license-header
/* see https://github.com/Stuk/eslint-plugin-header#readme */
'header/header': 'off'
// endregion license-header
}
}
]
}
8 changes: 4 additions & 4 deletions .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ jobs:
path: ${{ env.BUNDLES_DIR }}
retention-days: 5
if-no-files-found: error
- name: build:dist
run: yarn run build:dist
- name: make-dist
run: yarn run make-dist
- name: artifact build result
# see https://github.com/actions/upload-artifact
uses: actions/upload-artifact@v4
Expand Down Expand Up @@ -234,8 +234,8 @@ jobs:
# see https://github.com/actions/download-artifact
uses: actions/download-artifact@v4
with:
name: ${{ env.DIST_DIR }}
path: ${{ env.DIST_DIR }}
name: ${{ env.BUILD_DIR }}
path: ${{ env.BUILD_DIR }}
- name: dogfooding
run: >
yarn run dogfooding
Expand Down
21 changes: 13 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,25 @@ Please read the [CONTRIBUTING][contributing_file] file first.
Currently, there are no releases nor pre-builds. This means, the only way to test this tool is by building it from source:
1. Clone this repository and change dir into the clone
1. Install the project dependencies - run: `yarn install`
1. Create the bundle - run: `yarn build`
1. Create the bundle - run: `yarn run build`
1. Create the dist - run: `yarn run make-dist`

Then,
import the plugin bundle into your project — like so:
Then, you could import the plugin into your project — like so:
```shell
yarn plugin import {pathToYourClone}/dist/yarn-plugin-cyclonedx.js`,
```
or reference it in environment variable [`YARN_PLUGINS`](https://yarnpkg.com/advanced/plugin-tutorial#dynamically-loading-plugins-using-the-yarn_plugins-environment-variable) — like so:
```shell
YARN_PLUGINS={pathToYourClone}/dist/yarn-plugin-cyclonedx.js yarn sbom --help
yarn plugin import {pathToYourClone}/dist/yarn-plugin-cyclonedx.js
```

## Usage

* After plugin installation
```shell
yarn cyclonedx --help
```
* Zero-install via dlx-wrapper
```shell
yarn dlx -p {pathToYourClone}/dist cyclonedx-yarn --help
```

The help page:

```text
Expand Down
33 changes: 33 additions & 0 deletions bin/cyclonedx-yarn-cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/usr/bin/env node
/* !!! do not remove/rename this file, it is public CLI in replacement for an API !!! */

const { spawn } = require('child_process')
const { realpathSync } = require('fs')
const { join } = require('path')

let pp
for (const p of [
['yarn-plugin-cyclonedx.cjs'], // running in pack
['dist', 'yarn-plugin-cyclonedx.cjs'], // running in dist
['bundles', '@yarnpkg', 'plugin-cyclonedx.js'] // running in build
]) {
try {
pp = realpathSync(join(__dirname, '..', ...p))
break
} catch {
/* pass */
}
}
if (!pp) {
throw Error('missing plugin')
}

const YARN_PLUGINS = `${pp}${process.env.YARN_PLUGINS ? ';' + process.env.YARN_PLUGINS : ''}`
const args = ['cyclonedx', ...process.argv.splice(2)]

process.stderr.write(`\n> YARN_PLUGINS='${YARN_PLUGINS}' yarn ${args.join(' ')}\n\n`)
spawn('yarn', args, {
stdio: 'inherit',
env: { ...process.env, YARN_PLUGINS },
shell: process.platform === 'win32'
}).once('exit', code => { process.exitCode = code })
23 changes: 12 additions & 11 deletions index.cjs → index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,19 @@ There is no public API. Instead, there is a well-thought, stable CLI.
Call it programmatically like so:
const { execFileSync } = require('child_process')
const { constants: { MAX_LENGTH: BUFFER_MAX_LENGTH } } = require('buffer')
const sbom = JSON.parse(execFileSync('yarn', [
'cyclonedx',
'--output-format', 'JSON',
'--output-file', '-'
// additional CLI args
], {
env: { 'YARN_PLUGINS': '{.../path/to/this/package}/dist/yarn-plugin-cyclonedx.js' },
stdio: ['ignore', 'pipe', 'ignore'], encoding: 'buffer', maxBuffer: BUFFER_MAX_LENGTH
}))
const sbom = JSON.parse(execFileSync(process.execPath, [
'.../path/to/this/package/bin/cyclonedx-yarn-cli.js',
'--output-format', 'JSON',
'--output-file', '-'
// additional CLI args
], {
stdio: ['ignore', 'pipe', 'ignore'],
encoding: 'buffer',
maxBuffer: BUFFER_MAX_LENGTH
}))
`)

/*
module.exports = {/*
Intentionally, here are no exports.
See above!
*/
*/}
43 changes: 24 additions & 19 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,38 +93,43 @@
"src": "src",
"test": "tests"
},
"bin": {
"cyclonedx-yarn": "./bin/cyclonedx-yarn-cli.js"
},
"main": "./src/plugin.ts",
"files": [
"/README.md",
"/LICENSE",
"/NOTICE",
"/index.cjs",
"/dist/**"
],
"exports": "./index.cjs",
"exports": "./index.js",
"files": ["!/**"],
"publishConfig": {
"main": "./dist/yarn-plugin-cyclonedx.js"
"$comment": "this section defines how `tools/make-dist-package.cjs` works",
"main": "./yarn-plugin-cyclonedx.cjs",
"files": [
"/README.md",
"/LICENSE",
"/NOTICE",
"/index.js"
],
"directories": {},
"scripts": {}
},
"scripts": {
"xprepack": "run-s clean build",
"xpostpack": "run-s test:node",
"build": "run-s -n build:gbti build:bundle build:dist",
"build": "run-s -n build:gbti build:bundle",
"build:gbti": "node $PROJECT_CWD/tools/gather-buildtime-info.cjs",
"build:bundle": "builder build plugin --metafile",
"build:bundle-dev": "yarn build:bundle --source-map",
"build:dist": "run-s -n build:dist:mkdir build:dist:license build:dist:cp",
"build:dist:mkdir": "mkdirp $PROJECT_CWD/dist",
"build:dist:cp": "cp $PROJECT_CWD/bundles/@yarnpkg/plugin-cyclonedx.js $PROJECT_CWD/dist/yarn-plugin-cyclonedx.js",
"build:dist:license": "node $PROJECT_CWD/tools/write-3rd-party-licenses.cjs",
"make-dist": "run-s -n make-dist:mkdir make-dist:cp-\\* make-dist:make-\\*",
"make-dist:mkdir": "mkdirp $PROJECT_CWD/dist",
"make-dist:cp-distributable": "cp -t $PROJECT_CWD/dist -r $PROJECT_CWD/README.md $PROJECT_CWD/LICENSE $PROJECT_CWD/index.js $PROJECT_CWD/bin",
"make-dist:cp-plugin": "cp $PROJECT_CWD/bundles/@yarnpkg/plugin-cyclonedx.js $PROJECT_CWD/dist/yarn-plugin-cyclonedx.cjs",
"make-dist:make-notice": "node $PROJECT_CWD/tools/write-3rd-party-licenses.cjs $PROJECT_CWD/dist/NOTICE",
"make-dist:make-manifest": "node $PROJECT_CWD/tools/write-dist-manifest.cjs $PROJECT_CWD/dist/package.json",
"clean": "rimraf bundles dist",
"setup-tests": "node tests/integration/setup.js",
"test": "run-p --aggregate-output -lc test:\\*",
"test:standard": "eslint .",
"test:lint": "tsc --noEmit",
"test:node": "c8 mocha -p",
"cs-fix": "eslint --fix .",
"dogfooding": "YARN_PLUGINS=$PROJECT_CWD/dist/yarn-plugin-cyclonedx.js yarn cyclonedx"
"dogfooding": "node $PROJECT_CWD/bin/cyclonedx-yarn-cli.js"
},
"packageManager": "yarn@4.1.1",
"preferUnplugged": true
"packageManager": "yarn@4.1.1"
}
53 changes: 16 additions & 37 deletions tests/integration/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,55 +57,48 @@ const testbedsPath = path.join(testRootPath, '_data', 'testbeds')
suite('integration', () => {
const UPDATE_SNAPSHOTS = !!process.env.CYARN_TEST_UPDATE_SNAPSHOTS

const thisYarnPlugin = path.join(projectRootPath, 'bundles', '@yarnpkg', 'plugin-cyclonedx.js')
const thisCLI = path.join(projectRootPath, 'bin', 'cyclonedx-yarn-cli.js')

// testing complex setups - this may take some time
const longTestTimeout = 120000

/**
* @param {string} cwd
* @param {string[]} [additionalArgs]
* @param {Object<string, string>} [additionalEnv]
* @return {string} the SBOM
* @param {string[]} [args]
* @param {Object.<string, string>} [env]
* @returns {string} the SBOM
*/
function makeSBOM (
cwd,
additionalArgs = [], additionalEnv = {}
) {
function runClI (cwd, args = [], env = {}) {
const res = spawnSync(
'yarn', ['cyclonedx',
...additionalArgs
], {
process.execPath, [thisCLI, ...args], {
cwd,
stdio: ['ignore', 'pipe', 'pipe'],
encoding: 'utf8',
maxBuffer: BUFFER_MAX_LENGTH,
shell: true,
env: {
...additionalEnv,
PATH: process.env.PATH,
CI: '1',
YARN_PLUGINS: thisYarnPlugin
...process.env,
...env,
CI: '1'
}
})
assert.strictEqual(res.error, undefined)
assert.strictEqual(res.status, 0, makeSBOM.output)
assert.strictEqual(res.error, undefined, res.output)
assert.strictEqual(res.status, 0, res.output)
return res.stdout.toString()
}

/**
* @param {string} purpose
* @param {string} testSetup
* @param {string[]} [additionalArgs]
* @param {Object<string, string>} [additionalEnv]
* @param {Object.<string, string>} [additionalEnv]
*/
async function runTest (
purpose, testSetup,
additionalArgs = [], additionalEnv = {}
) {
const expectedOutSnap = path.join(snapshotsPath, `${purpose}_${testSetup}.json.bin`)

let sbom = makeSBOM(
let sbom = runClI(
path.join(testbedsPath, testSetup),
[
'-vvv',
Expand Down Expand Up @@ -160,26 +153,12 @@ suite('integration', () => {
})

test('version', () => {
const res = spawnSync(
'yarn', ['cyclonedx', '--version'], {
cwd: projectRootPath,
stdio: ['ignore', 'pipe', 'pipe'],
encoding: 'utf8',
shell: true,
env: {
PATH: process.env.PATH,
CI: '1',
YARN_PLUGINS: thisYarnPlugin
}
})
assert.strictEqual(res.error, undefined)
assert.strictEqual(res.status, 0, res.output)
assert.ok(res.stdout.startsWith(`${thisName} v${thisVersion}`), res.stdout)
const res = runClI(projectRootPath, ['--version'])
assert.ok(res.startsWith(`${thisName} v${thisVersion}`), res)
})

test('dogfooding', async () => {
const sbom = makeSBOM(projectRootPath)

const sbom = runClI(projectRootPath)
const validationErrors = await validate('json', sbom, latestCdxSpecVersion)
assert.strictEqual(validationErrors, null)
}).timeout(longTestTimeout)
Expand Down
Loading

0 comments on commit 088301f

Please sign in to comment.