Skip to content

Commit

Permalink
Add sub-action to download Grype (#152)
Browse files Browse the repository at this point in the history
  • Loading branch information
kzantow authored Mar 31, 2022
1 parent 4a24b5a commit 637a129
Show file tree
Hide file tree
Showing 8 changed files with 229 additions and 161 deletions.
1 change: 1 addition & 0 deletions GrypeVersion.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
exports.GRYPE_VERSION = "v0.34.4";
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,33 @@ You may add a `.grype.yaml` file at your repository root
for more [Grype configuration](https://github.com/anchore/grype#configuration)
such as [ignoring certain matches](https://github.com/anchore/grype#specifying-matches-to-ignore).

## anchore/scan-action/download-grype

A sub-action to [download Grype](download-grype/action.yml).

Input parameters:

| Parameter | Description | Default |
| --------------- | ------------------------------------------------------------------------------------------------------------ | ------- |
| `grype-version` | An optional Grype version to download, defaults to the pinned version in [GrypeVersion.js](GrypeVersion.js). | |

Output parameters:

| Parameter | Description |
| --------- | -------------------------------------------------------------------- |
| `cmd` | a reference to the [Grype](https://github.com/anchore/grype) binary. |

`cmd` can be referenced in a workflow like other output parameters:
`${{ steps.<step-id>.outputs.cmd }}`

Example usage:

```yaml
- uses: anchore/scan-action/download-grype@v3
id: grype
- run: ${{steps.grype.outputs.cmd}} dir:.
```

## Contributing

We love contributions, feedback, and bug reports. For issues with the invocation of this action, file [issues](https://github.com/anchore/scan-action/issues) in this repository.
Expand Down
66 changes: 25 additions & 41 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@ module.exports =
/******/ (() => { // webpackBootstrap
/******/ var __webpack_modules__ = ({

/***/ 244:
/***/ ((__unused_webpack_module, exports) => {

exports.GRYPE_VERSION = "v0.34.4";


/***/ }),

/***/ 932:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {

Expand All @@ -10,42 +18,10 @@ const core = __webpack_require__(186);
const { exec } = __webpack_require__(514);
const fs = __webpack_require__(747);
const stream = __webpack_require__(413);
const { GRYPE_VERSION } = __webpack_require__(244);

const grypeBinary = "grype";
const grypeVersion = "0.34.4";

// Find all 'content-*.json' files in the directory. dirname should include the full path
function findContent(searchDir) {
let contentFiles = [];
let match = /content-.*\.json/;
var dirItems = fs.readdirSync(searchDir);
if (dirItems) {
for (let i = 0; i < dirItems.length; i++) {
if (match.test(dirItems[i])) {
contentFiles.push(`${searchDir}/${dirItems[i]}`);
}
}
} else {
core.debug("no dir content found");
}

core.debug(contentFiles.toString());
return contentFiles;
}

// Load the json content of each file in a list and return them as a list
function loadContent(files) {
let contents = [];
if (files) {
files.forEach((item) => contents.push(JSON.parse(fs.readFileSync(item))));
}
return contents;
}

// Merge the multiple content output types into a single array
function mergeResults(contentArray) {
return contentArray.reduce((merged, n) => merged.concat(n.content), []);
}
const grypeVersion = core.getInput("grype-version") || GRYPE_VERSION;

async function downloadGrype(version) {
let url = `https://raw.githubusercontent.com/anchore/grype/main/install.sh`;
Expand All @@ -58,7 +34,7 @@ async function downloadGrype(version) {
// Make sure the tool's executable bit is set
await exec(`chmod +x ${installPath}`);

let cmd = `${installPath} -b ${installPath}_grype v${version}`;
let cmd = `${installPath} -b ${installPath}_grype ${version}`;
await exec(cmd);
let grypePath = `${installPath}_grype/grype`;

Expand All @@ -75,6 +51,7 @@ async function installGrype(version) {

// Add tool to path for this and future actions to use
core.addPath(grypePath);
return `${grypePath}/${grypeBinary}`;
}

function sourceInput() {
Expand Down Expand Up @@ -255,15 +232,22 @@ module.exports = {
run,
runScan,
installGrype,
mergeResults,
findContent,
loadContent,
};

if (require.main === require.cache[eval('__filename')]) {
run().catch((err) => {
throw new Error(err);
});
const entrypoint = core.getInput("run");
switch (entrypoint) {
case "download-grype": {
installGrype(grypeVersion).then((path) => {
core.info(`Downloaded Grype to: ${path}`);
core.setOutput("cmd", path);
});
break;
}
default: {
run().then();
}
}
}


Expand Down
20 changes: 20 additions & 0 deletions download-grype/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: "Download Grype"
author: "Anchore"
description: "Downloads the Grype binary and provides a path to execute it"
branding:
color: blue
icon: check-circle
inputs:
grype-version:
description: "A specific version of Grype to install"
required: false
run:
description: "Flag to indicate which sub-action to run"
required: false
default: "download-grype"
outputs:
cmd:
description: "An absolute path to the Grype executable"
runs:
using: "node12"
main: "../dist/index.js"
58 changes: 17 additions & 41 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,42 +3,10 @@ const core = require("@actions/core");
const { exec } = require("@actions/exec");
const fs = require("fs");
const stream = require("stream");
const { GRYPE_VERSION } = require("./GrypeVersion");

const grypeBinary = "grype";
const grypeVersion = "0.34.4";

// Find all 'content-*.json' files in the directory. dirname should include the full path
function findContent(searchDir) {
let contentFiles = [];
let match = /content-.*\.json/;
var dirItems = fs.readdirSync(searchDir);
if (dirItems) {
for (let i = 0; i < dirItems.length; i++) {
if (match.test(dirItems[i])) {
contentFiles.push(`${searchDir}/${dirItems[i]}`);
}
}
} else {
core.debug("no dir content found");
}

core.debug(contentFiles.toString());
return contentFiles;
}

// Load the json content of each file in a list and return them as a list
function loadContent(files) {
let contents = [];
if (files) {
files.forEach((item) => contents.push(JSON.parse(fs.readFileSync(item))));
}
return contents;
}

// Merge the multiple content output types into a single array
function mergeResults(contentArray) {
return contentArray.reduce((merged, n) => merged.concat(n.content), []);
}
const grypeVersion = core.getInput("grype-version") || GRYPE_VERSION;

async function downloadGrype(version) {
let url = `https://raw.githubusercontent.com/anchore/grype/main/install.sh`;
Expand All @@ -51,7 +19,7 @@ async function downloadGrype(version) {
// Make sure the tool's executable bit is set
await exec(`chmod +x ${installPath}`);

let cmd = `${installPath} -b ${installPath}_grype v${version}`;
let cmd = `${installPath} -b ${installPath}_grype ${version}`;
await exec(cmd);
let grypePath = `${installPath}_grype/grype`;

Expand All @@ -68,6 +36,7 @@ async function installGrype(version) {

// Add tool to path for this and future actions to use
core.addPath(grypePath);
return `${grypePath}/${grypeBinary}`;
}

function sourceInput() {
Expand Down Expand Up @@ -248,13 +217,20 @@ module.exports = {
run,
runScan,
installGrype,
mergeResults,
findContent,
loadContent,
};

if (require.main === module) {
run().catch((err) => {
throw new Error(err);
});
const entrypoint = core.getInput("run");
switch (entrypoint) {
case "download-grype": {
installGrype(grypeVersion).then((path) => {
core.info(`Downloaded Grype to: ${path}`);
core.setOutput("cmd", path);
});
break;
}
default: {
run().then();
}
}
}
1 change: 0 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
module.exports = {
setupFiles: ["<rootDir>/.jest/setEnvVars.js"],
verbose: true,
testPathIgnorePatterns: ["action.test.js"],
};
48 changes: 38 additions & 10 deletions tests/action.test.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,39 @@
const child_process = require('child_process');
const path = require('path');
const process = require('process');
const child_process = require("child_process");
const os = require("os");
const path = require("path");
const process = require("process");

// shows how the runner will run a javascript action with env / stdout protocol
test('test runs', () => {
process.env['INPUT_DEBUG'] = 'true';
process.env['INPUT_IMAGE-REFERENCE'] = 'docker.io/alpine:latest';
const index_path = path.join(__dirname, '../index.js');
console.log(child_process.execSync(`node ${index_path}`, {env: process.env}).toString());
});
const actionPath = path.join(__dirname, "../index.js");

// Execute the action, and return any outputs
function runAction(inputs) {
// reverse core.js: const val = process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || '';
for (const k in inputs) {
process.env[`INPUT_${k}`.toUpperCase()] = inputs[k];
}
// capture stdout
const stdout = child_process
.execSync(`node ${actionPath}`, {
env: process.env,
})
.toString("utf8");
const outputs = {};
// reverse setOutput command calls like:
// ::set-output name=cmd::/tmp/actions/cache/grype/0.34.4/x64/grype
for (const line of stdout.split(os.EOL)) {
const groups = line.match(/::set-output name=(\w+)::(.*)$/);
if (groups && groups.length > 2) {
outputs[groups[1]] = groups[2];
}
}
return outputs;
}

describe("sbom-action", () => {
it("runs download-grype", () => {
const outputs = runAction({
run: "download-grype",
});
expect(outputs.cmd).toBeDefined();
});
});
Loading

0 comments on commit 637a129

Please sign in to comment.