Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 0 additions & 17 deletions .github/release.yml

This file was deleted.

19 changes: 19 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Publish
on:
push:
branches:
- main

permissions:
contents: write
packages: write

jobs:
publish:
name: Publish
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dylanvann/publish-github-action@v1.1.49
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
28 changes: 28 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Test
on:
push:
branches-ignore:
- main

jobs:
test:
name: Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
node-version: 24
cache: npm
- run: npm ci
# Optional integration test of the action using a dedicated GitHub App.
- id: create_token
if: ${{ vars.TEST_GITHUB_APP_ID != '' }}
uses: ./
with:
# The only required permission is `Repository permissions > Metadata: Read-only`.
app_id: ${{ vars.TEST_GITHUB_APP_ID }}
private_key: ${{ secrets.TEST_GITHUB_APP_PRIVATE_KEY }}
- if: ${{ steps.create_token.outcome != 'skipped' }}
run: node --eval "assert('${{ steps.create_token.outputs.token }}'.length > 0);"
- run: npm run prettier -- --check
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
43 changes: 24 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,76 +61,81 @@ Parent project uuid in Dependency-Track (available in DT v4.8 and later)
## Example usage

With project name and version:

```yml
uses: DependencyTrack/gh-upload-sbom@v3
with:
serverHostname: 'example.com'
serverHostname: "example.com"
apiKey: ${{ secrets.DEPENDENCYTRACK_APIKEY }}
projectName: 'Example Project'
projectVersion: 'master'
projectName: "Example Project"
projectVersion: "master"
bomFilename: "/path/to/bom.xml"
autoCreate: true
```

With project name, version and tags:

```yml
uses: DependencyTrack/gh-upload-sbom@v3
with:
serverHostname: 'example.com'
serverHostname: "example.com"
apiKey: ${{ secrets.DEPENDENCYTRACK_APIKEY }}
projectName: 'Example Project'
projectVersion: 'master'
projectTags: 'tag1,tag2'
projectName: "Example Project"
projectVersion: "master"
projectTags: "tag1,tag2"
bomFilename: "/path/to/bom.xml"
autoCreate: true
```

With protocol, port and project name:

```yml
uses: DependencyTrack/gh-upload-sbom@v3
with:
protocol: ${{ secrets.DEPENDENCYTRACK_PROTOCOL }}
serverHostname: ${{ secrets.DEPENDENCYTRACK_HOSTNAME }}
port: ${{ secrets.DEPENDENCYTRACK_PORT }}
apiKey: ${{ secrets.DEPENDENCYTRACK_APIKEY }}
projectName: 'Example Project'
projectVersion: 'master'
projectName: "Example Project"
projectVersion: "master"
bomFilename: "/path/to/bom.xml"
autoCreate: true
```

With project uuid:

```yml
uses: DependencyTrack/gh-upload-sbom@v3
with:
serverHostname: 'example.com'
serverHostname: "example.com"
apiKey: ${{ secrets.DEPENDENCYTRACK_APIKEY }}
project: 'dadec8ad-7053-4e8c-8044-7b6ef698e08d'
project: "dadec8ad-7053-4e8c-8044-7b6ef698e08d"
```

With protocol, port, project name and parent name:

```yml
uses: DependencyTrack/gh-upload-sbom@v3
with:
protocol: ${{ secrets.DEPENDENCYTRACK_PROTOCOL }}
serverHostname: ${{ secrets.DEPENDENCYTRACK_HOSTNAME }}
port: ${{ secrets.DEPENDENCYTRACK_PORT }}
apiKey: ${{ secrets.DEPENDENCYTRACK_APIKEY }}
projectName: 'Example Project'
projectVersion: 'master'
projectName: "Example Project"
projectVersion: "master"
bomFilename: "/path/to/bom.xml"
autoCreate: true
parentName: 'Example Parent'
parentVersion: 'master'
parentName: "Example Parent"
parentVersion: "master"
```

With parent uuid:

```yml
uses: DependencyTrack/gh-upload-sbom@v3
with:
serverHostname: 'example.com'
serverHostname: "example.com"
apiKey: ${{ secrets.DEPENDENCYTRACK_APIKEY }}
project: 'dadec8ad-7053-4e8c-8044-7b6ef698e08d'
parent: '6a5a3c33-3f8b-42ee-8d50-594bfd95dd32'
project: "dadec8ad-7053-4e8c-8044-7b6ef698e08d"
parent: "6a5a3c33-3f8b-42ee-8d50-594bfd95dd32"
```

42 changes: 21 additions & 21 deletions action.yml
Original file line number Diff line number Diff line change
@@ -1,51 +1,51 @@
name: 'Upload BOM to Dependency-Track'
name: "Upload BOM to Dependency-Track"
author: Patrick Dwyer
description: 'Uploads a CycloneDX software bill of materials to a Dependency-Track server'
description: "Uploads a CycloneDX software bill of materials to a Dependency-Track server"
inputs:
serverhostname:
description: 'Dependency-Track hostname'
description: "Dependency-Track hostname"
required: true
port:
description: 'Dependency-Track port'
description: "Dependency-Track port"
required: false
default: '443'
default: "443"
protocol:
description: 'Dependency-Track protocol'
description: "Dependency-Track protocol"
required: false
default: 'https'
default: "https"
apikey:
description: 'Dependency-Track API key'
description: "Dependency-Track API key"
required: true
project:
description: 'Project in Dependency-Track'
description: "Project in Dependency-Track"
required: false
projectname:
description: 'Project name in Dependency-Track'
description: "Project name in Dependency-Track"
required: false
projectversion:
description: 'Project version in Dependency-Track'
description: "Project version in Dependency-Track"
required: false
projecttags:
description: 'Comma-separated list of tags (available in DT v4.12 and later)'
default: ''
description: "Comma-separated list of tags (available in DT v4.12 and later)"
default: ""
required: false
autocreate:
description: "Automatically create the project in Dependency-Track if it doesn't exist"
default: 'false'
default: "false"
required: false
bomfilename:
description: 'Path and filename of the BOM'
default: 'bom.xml'
description: "Path and filename of the BOM"
default: "bom.xml"
required: false
parent:
description: 'Parent project UUID in Dependency-Track (available in DT v4.8 and later)'
description: "Parent project UUID in Dependency-Track (available in DT v4.8 and later)"
required: false
parentname:
description: 'Parent project name in Dependency-Track (available in DT v4.8 and later)'
description: "Parent project name in Dependency-Track (available in DT v4.8 and later)"
required: false
parentversion:
description: 'Parent project version in Dependency-Track (available in DT v4.8 and later)'
description: "Parent project version in Dependency-Track (available in DT v4.8 and later)"
required: false
runs:
using: 'node24'
main: 'index.js'
using: "node24"
main: "index.js"
85 changes: 48 additions & 37 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,46 +1,51 @@
const fs = require('fs');
const core = require('@actions/core');
const fs = require("fs");
const core = require("@actions/core");

async function run() {
try {
const serverHostname = core.getInput('serverhostname');
const port = core.getInput('port');
const protocol = core.getInput('protocol');
const apiKey = core.getInput('apikey');
const project = core.getInput('project');
const projectName = core.getInput('projectname');
const projectVersion = core.getInput('projectversion');
const projectTags = core.getInput('projecttags');
const autoCreate = core.getInput('autocreate') !== 'false';
const bomFilename = core.getInput('bomfilename');
const parent = core.getInput('parent');
const parentName = core.getInput('parentname');
const parentVersion = core.getInput('parentversion');
const serverHostname = core.getInput("serverhostname");
const port = core.getInput("port");
const protocol = core.getInput("protocol");
const apiKey = core.getInput("apikey");
const project = core.getInput("project");
const projectName = core.getInput("projectname");
const projectVersion = core.getInput("projectversion");
const projectTags = core.getInput("projecttags");
const autoCreate = core.getInput("autocreate") !== "false";
const bomFilename = core.getInput("bomfilename");
const parent = core.getInput("parent");
const parentName = core.getInput("parentname");
const parentVersion = core.getInput("parentversion");

if (protocol !== "http" && protocol !== "https") {
throw 'protocol "' + protocol + '" not supported, must be one of: https, http'
throw (
'protocol "' + protocol + '" not supported, must be one of: https, http'
);
}

if (project === "" && (projectName === "" || projectVersion === "")) {
throw 'project or projectName + projectVersion must be set'
throw "project or projectName + projectVersion must be set";
}

if (!autoCreate && project === "") {
throw 'project can\'t be empty if autoCreate is false'
throw "project can't be empty if autoCreate is false";
}

if (project === "" && (projectName === "" || projectVersion === "")) {
throw 'project or projectName + projectVersion must be set'
throw "project or projectName + projectVersion must be set";
}

if ((parentName === "" && parentVersion !== "") || (parentName !== "" && parentVersion === "")) {
throw 'parentName + parentVersion must both be set'
if (
(parentName === "" && parentVersion !== "") ||
(parentName !== "" && parentVersion === "")
) {
throw "parentName + parentVersion must both be set";
}

core.info(`Reading BOM: ${bomFilename}...`);
const bomContents = fs.readFileSync(bomFilename);
let encodedBomContents = Buffer.from(bomContents).toString('base64');
if (encodedBomContents.startsWith('77u/')) {
let encodedBomContents = Buffer.from(bomContents).toString("base64");
if (encodedBomContents.startsWith("77u/")) {
encodedBomContents = encodedBomContents.substring(4);
}

Expand All @@ -50,56 +55,62 @@ async function run() {
projectName: projectName,
projectVersion: projectVersion,
autoCreate: autoCreate,
bom: encodedBomContents
}
bom: encodedBomContents,
};
if (projectTags) {
bomPayload.projectTags = projectTags.split(',').map(tag => ({name: tag.trim()}));
bomPayload.projectTags = projectTags
.split(",")
.map((tag) => ({ name: tag.trim() }));
}
} else {
bomPayload = {
project: project,
bom: encodedBomContents
}
bom: encodedBomContents,
};
}

if (parent && parent.trim().length > 0) {
bomPayload.parentUUID = parent;
} else if (parentName && parentName.trim().length > 0 && parentVersion && parentVersion.trim().length > 0) {
} else if (
parentName &&
parentName.trim().length > 0 &&
parentVersion &&
parentVersion.trim().length > 0
) {
bomPayload.parentName = parentName;
bomPayload.parentVersion = parentVersion;
}

const postData = JSON.stringify(bomPayload);

const requestOptions = {
method: 'PUT',
method: "PUT",
headers: {
'X-API-Key': apiKey,
'Content-Type': 'application/json',
"X-API-Key": apiKey,
"Content-Type": "application/json",
},
body: postData
body: postData,
};

const url = new URL(`${protocol}://${serverHostname}`);
if (port) {
url.port = port;
}
url.pathname = '/api/v1/bom';
url.pathname = "/api/v1/bom";

core.info(`Uploading to Dependency-Track server ${serverHostname}...`);

const response = await fetch(url.toString(), requestOptions);

if (response.ok) {
core.info('Finished uploading BOM to Dependency-Track server.');
core.info("Finished uploading BOM to Dependency-Track server.");
} else {
const responseBody = await response.text();
if (responseBody) {
core.debug(responseBody);
}
core.setFailed('Failed response status code:' + response.status);
core.setFailed("Failed response status code:" + response.status);
}

} catch (error) {
core.setFailed(error.message);
}
Expand Down
1 change: 0 additions & 1 deletion node_modules/.bin/uuid

This file was deleted.

Loading
Loading