Skip to content

Commit

Permalink
Adding some minor tests, and updating docs.
Browse files Browse the repository at this point in the history
- Also added `ci-pipeline-fingerprints` support in `action.yml`
- Prepping to release.
  • Loading branch information
manchicken committed Dec 21, 2023
1 parent e8fedb9 commit 63f2d8e
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 134 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning].

- Added support for schema version v2.2
- Added the `uuid` dependency since `@actions/core` insists
- Added some more edge case tests

### Changed

Expand Down
44 changes: 24 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ At this time, this GitHub Action supports the following versions of the Service

- `v2`
- `v2.1`
- `v2.2`

## Wait, but why?

Expand Down Expand Up @@ -69,35 +70,38 @@ Using a field which is not supported in the schema version you've selected will

| Field | Description | Required | Default | Schema Versions |
| --- | --- | --- | --- | --- |
| `service-name` | The name of the service. This must be unique across all services. | Yes | | `v2`, `v2.1` |
| `team` | The team that owns the service. | Yes | | `v2`, `v2.1` |
| `description` | A description of the service. | No | | `v2.1` |
| `application` | The application that the service belongs to. | No | | `v2.1` |
| `tier` | The importance tier of the service. This is an unconstrained text field where you can use your own tiering definitions. Examples would be `High`, `Critical`, or however you or your organization classify criticality tiers. | No | | `v2.1` |
| `lifecycle` | This is the lifecycle of the service. This text field is unconstrained, and some examples are `production`, `development`, `staging`. | No | | `v2.1` |
| `contacts` | The list of contacts for the service. Each of these contacts is an object. Keep in mind that `email` and `slack-support-channel` are already included as contacts. This list should be in addition to that. These values are supplied as objects, but due to the limitations of GitHub Actions, please supply these object properties as a multi-line string. | No | `[]` | `v2`, `v2.1` |
| `contacts[].name` | The name of the contact. | Yes | | `v2`, `v2.1` |
| `contacts[].type` | The type of the contact. Acceptable values are: `email`, `slack`, and `microsoft-teams` | Yes | | `v2`, `v2.1` |
| `contacts[].contact` | The actual contact information for the contact. For example, if the type is `email`, this would be the email address. | Yes | | `v2`, `v2.1` |
| `service-name` | The name of the service. This must be unique across all services. | Yes | | `v2`, `v2.1`, `v2.2` |
| `team` | The team that owns the service. | Yes | | `v2`, `v2.1`, `v2.2` |
| `description` | A description of the service. | No | | `v2.1`, `v2.2` |
| `application` | The application that the service belongs to. | No | | `v2.1`, `v2.2` |
| `type` | The type of resource that this service constitutes. Values are constrained to one of "web", "db", "cache", "function", "browser", "mobile", or "custom". | No | | `v2.2` |
| `languages` | This is a list of the languages used in this service. This is an array, so you may supply multiple values. | No | `[]` | `v2.2` |
| `tier` | The importance tier of the service. This is an unconstrained text field where you can use your own tiering definitions. Examples would be `High`, `Critical`, or however you or your organization classify criticality tiers. | No | | `v2.1`, `v2.2` |
| `lifecycle` | This is the lifecycle of the service. This text field is unconstrained, and some examples are `production`, `development`, `staging`. | No | | `v2.1`, `v2.2` |
| `contacts` | The list of contacts for the service. Each of these contacts is an object. Keep in mind that `email` and `slack-support-channel` are already included as contacts. This list should be in addition to that. These values are supplied as objects, but due to the limitations of GitHub Actions, please supply these object properties as a multi-line string. | No | `[]` | `v2`, `v2.1`, `v2.2` |
| `contacts[].name` | The name of the contact. | Yes | | `v2`, `v2.1`, `v2.2` |
| `contacts[].type` | The type of the contact. Acceptable values are: `email`, `slack`, and `microsoft-teams` | Yes | | `v2`, `v2.1`, `v2.2` |
| `contacts[].contact` | The actual contact information for the contact. For example, if the type is `email`, this would be the email address. | Yes | | `v2`, `v2.1`, `v2.2` |
| `repos` | The list of GitHub repositories that are part of the service. You must supply at least one repository. The repositories are supplied as objects, but due to the limitations of GitHub Actions, please supply these object properties as a multi-line string. In `v2.1`, this field is moved under `links`. | Yes | `[]` | `v2` |
| `repos[].name` | The name of the repository. | Yes | | `v2` |
| `repos[].url` | The URL of the repository. | Yes | | `v2` |
| `repos[].provider` | The provider of the repository. Acceptable values are: `Github`. | No | | `v2` |
| `tags` | The list of tags that are associated with the service. This should be a list of key-value pairs separated by colons. | No | |
| `links` | A list of links associated with the service. These links are objects with a variety of properties, but due to the limitations of GitHub Actions, please supply these object properties as a multi-line string. | No | `[]` | `v2`, `v2.1` |
| `links[].name` | The name of the link. | Yes | | `v2`, `v2.1` |
| `links[].url` | The URL of the link. | Yes | | `v2`, `v2.1` |
| `links[].type` | The type for the link. Acceptable values for the `v2` API are: `doc`, `wiki`, `runbook`, `url`, `repo`, `dashboard`, `oncall`, `code`, and `link`. Acceptable values for the `v2.1` API are: `doc`, `runbook`, `repo`, `dashboard`, and `other`. | Yes | | `v2`, `v2.1` |
| `links` | A list of links associated with the service. These links are objects with a variety of properties, but due to the limitations of GitHub Actions, please supply these object properties as a multi-line string. | No | `[]` | `v2`, `v2.1`, `v2.2` |
| `links[].name` | The name of the link. | Yes | | `v2`, `v2.1`, `v2.2` |
| `links[].url` | The URL of the link. | Yes | | `v2`, `v2.1`, `v2.2` |
| `links[].type` | The type for the link. Acceptable values for the `v2` API are: `doc`, `wiki`, `runbook`, `url`, `repo`, `dashboard`, `oncall`, `code`, and `link`. Acceptable values for the `v2.1` API are: `doc`, `runbook`, `repo`, `dashboard`, and `other`. | Yes | | `v2`, `v2.1`, `v2.2` |
| `docs` | A list of documentation links associated with the service. These links are objects with a variety of properties, but due to the limitations of GitHub Actions, please supply these object properties as a multi-line string. In `v2.1`, this field moved under `links`. | No | `[]` | `v2` |
| `docs[].name` | The name of the document. | Yes | | `v2` |
| `docs[].url` | The URL of the document. | Yes | | `v2` |
| `docs[].provider` | The provider for where the documentation lives. Acceptable values are: `Confluence`, `GoogleDocs`, `Github`, `Jira`, `OneNote`, `SharePoint`, and `Dropbox` | No | | `v2` |
| `integrations` | Integrations associated with the service. These integrations are objects with a variety of properties, but due to the limitations of GitHub Actions, please supply these object properties as a multi-line string. | No | `{}` | `v2`, `v2.1` |
| `integrations.opsgenie` | The OpsGenie details for the service. | No | | `v2`, `v2.1` |
| `integrations.opsgenie.service-url` | The service URL for the OpsGenie integration. A team URL will work, but if you want on-call metadata then make sure that this URL is to a service, not a team. | Yes | | `v2`, `v2.1` |
| `integrations.opsgenie.region` | The region for the OpsGenie integration. Acceptable values are `US` and `EU`. | No | | `v2`, `v2.1` |
| `integrations.pagerduty` | The PagerDuty URL for the service. **Important:** In `v2`, this field is just a URL. In `v2.1` this field is a dictionary with a `service-url` property. | No | | `v2`, `v2.1` |
| `integrations.pagerduty.service-url` | The PagerDuty URL for the service. | Yes | | `v2.1` |
| `integrations` | Integrations associated with the service. These integrations are objects with a variety of properties, but due to the limitations of GitHub Actions, please supply these object properties as a multi-line string. | No | `{}` | `v2`, `v2.1`, `v2.2` |
| `integrations.opsgenie` | The OpsGenie details for the service. | No | | `v2`, `v2.1`, `v2.2` |
| `integrations.opsgenie.service-url` | The service URL for the OpsGenie integration. A team URL will work, but if you want on-call metadata then make sure that this URL is to a service, not a team. | Yes | | `v2`, `v2.1`, `v2.2` |
| `integrations.opsgenie.region` | The region for the OpsGenie integration. Acceptable values are `US` and `EU`. | No | | `v2`, `v2.1`, `v2.2` |
| `integrations.pagerduty` | The PagerDuty URL for the service. **Important:** In `v2`, this field is just a URL. In `v2.1` this field is a dictionary with a `service-url` property. | No | | `v2`, `v2.1`, `v2.2` |
| `integrations.pagerduty.service-url` | The PagerDuty URL for the service. | Yes | | `v2.1`, `v2.2` |
| `ci-pipeline-fingerprints` | A set of CI pipeline fingerprints related to the service. | No | `[]` | `v2.2` |

### Convenience Fields

Expand Down
3 changes: 3 additions & 0 deletions __tests__/lib/__snapshots__/input-validation.test.cjs.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`validateDatadogHostname() invalid hostnames 1`] = `"Invalid DataDog host: somethingcompletelydifferent.com. See here for more details: https://docs.datadoghq.com/getting_started/site/"`;
8 changes: 7 additions & 1 deletion __tests__/lib/input-validation.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ const { validateDatadogHostname } = require('../../lib/input-validation')
describe('validateDatadogHostname()', () => {
test('valid hostname', () => {
expect(validateDatadogHostname('app.datadoghq.com')).toBe(
'app.datadoghq.com',
'app.datadoghq.com'
)
})

test('invalid hostnames', () => {
expect(() =>
validateDatadogHostname('somethingcompletelydifferent.com')
).toThrowErrorMatchingSnapshot()
})
})
60 changes: 46 additions & 14 deletions __tests__/lib/org-rules.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const path = require('path')
const YAML = require('yaml')

// Pulling this in here activates the mocking of the github module
const github = require('@actions/github')
require('@actions/github')

// Need to use inputs for some of our parameters
const core = require('@actions/core')
Expand All @@ -18,20 +18,20 @@ const testLocallyOnly = require('../test-locally-only')

// This is our test subject
const {
fetchAndApplyOrgRules,
// fetchAndApplyOrgRules,
_test: {
fetchRemoteRules,
ghHandle,
currentOrg,
determineApplicabilityOfRule,
determineRuleCompliance,
// determineApplicabilityOfRule,
// determineRuleCompliance,
applyOrgRules,
},
} = require('../../lib/org-rules')

process.env.GITHUB_EVENT_PATH = path.join(
__dirname,
'../data/github-context-payload.json',
'../data/github-context-payload.json'
)
process.env.GITHUB_REPOSITORY =
'arcxp/datadog-service-catalog-metadata-provider'
Expand All @@ -53,7 +53,7 @@ describe('org-rules.js Org Rules the basics', () => {
const gh = await ghHandle()

expect(core.warning).toHaveBeenCalledWith(
'No GitHub token found, org rules cannot be applied.',
'No GitHub token found, org rules cannot be applied.'
)
expect(gh).toBeUndefined()
process.env.GITHUB_TOKEN = GH_TOKEN
Expand All @@ -70,7 +70,7 @@ describe('org-rules.js Org Rules the basics', () => {
delete process.env.GITHUB_REPOSITORY
const result = currentOrg()
expect(core.setFailed).toHaveBeenCalledWith(
'This GitHub Actions environment does not have a valid context.',
'This GitHub Actions environment does not have a valid context.'
)
expect(result).toBeUndefined()

Expand Down Expand Up @@ -118,7 +118,7 @@ integrations: |
opsgenie:
service_url: https://example.com
region: US
`),
`)
)
const serviceDefinition = await inputsToRegistryDocument()

Expand Down Expand Up @@ -146,7 +146,7 @@ service-name: test1
team: Team Name Here
email: '[email protected]'
repo: foo
`),
`)
)
const serviceDefinition = await inputsToRegistryDocument()

Expand Down Expand Up @@ -186,7 +186,7 @@ integrations: |
opsgenie:
service_url: https://example.com
region: US
`),
`)
)
const serviceDefinition = await inputsToRegistryDocument()

Expand Down Expand Up @@ -222,7 +222,7 @@ email: '[email protected]'
repo: foo
tags: |
- data-sensitivity:critical
`),
`)
)
const serviceDefinition = await inputsToRegistryDocument()

Expand Down Expand Up @@ -262,7 +262,7 @@ integrations: |
opsgenie:
service_url: https://example.com
region: US
`),
`)
)
const serviceDefinition = await inputsToRegistryDocument()

Expand Down Expand Up @@ -300,7 +300,7 @@ integrations: |
opsgenie:
service_url: https://example.com
region: US
`),
`)
)
const serviceDefinition = await inputsToRegistryDocument()

Expand Down Expand Up @@ -340,11 +340,43 @@ integrations: |
opsgenie:
service_url: https://example.com
region: US
`),
`)
)
const serviceDefinition = await inputsToRegistryDocument()

const result = await applyOrgRules(serviceDefinition, ruleDefinition)
expect(result).toBeTruthy()
})
})

describe('Edge cases', () => {
const OLD_ENV = process.env

beforeEach(() => {
jest.resetModules() // Most important - it clears the cache
process.env = { ...OLD_ENV } // Make a copy
})

afterAll(() => {
process.env = OLD_ENV // Restore old environment
jest.clearAllMocks()
})

test('should return undefined without gh', () => {
process.env['GITHUB_TOKEN'] = undefined

expect(fetchRemoteRules()).resolves.toBeUndefined()
})

test('should respect runner debug', async () => {
core.debug = jest.fn()

expect(await fetchRemoteRules()).toBeTruthy()
expect(core.debug).toHaveBeenCalledTimes(1)

process.env['RUNNER_DEBUG'] = 1

expect(await fetchRemoteRules()).toBeTruthy()
expect(core.debug).toHaveBeenCalledTimes(3)
})
})
17 changes: 11 additions & 6 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,6 @@ inputs:
service-name:
description: 'The name of the service'
required: true
type:
description: 'Type of the service. Examples: "web", "db", "cache", "function", "browser", "mobile", "custom"'
required: false
languages:
description: 'A list of programming languages used.'
required: false
team:
description: 'The team responsible for the service'
required: false
Expand Down Expand Up @@ -81,6 +75,17 @@ inputs:
description: '(v2.1 only) The current life cycle phase of the service. For example: sandbox, staging, production, deprecated'
required: false

# Fields which were added in v2.2
type:
description: 'Type of the service. Examples: "web", "db", "cache", "function", "browser", "mobile", "custom"'
required: false
languages:
description: 'A list of programming languages used.'
required: false
ci-pipeline-fingerprints:
description: 'A set of CI pipeline fingerprints related to the service'
required: false

# These are convenience inputs which are unique to this action.
email:
description: 'The email address of the team responsible for the service'
Expand Down
92 changes: 0 additions & 92 deletions dist/index.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -34782,104 +34782,12 @@ var require_github = __commonJS({
}
});

// node_modules/@octokit/plugin-request-log/dist-node/index.js
var require_dist_node11 = __commonJS({
"node_modules/@octokit/plugin-request-log/dist-node/index.js"(exports2, module2) {
"use strict";
var __defProp2 = Object.defineProperty;
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
var __getOwnPropNames2 = Object.getOwnPropertyNames;
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
var __export2 = (target, all) => {
for (var name in all)
__defProp2(target, name, { get: all[name], enumerable: true });
};
var __copyProps2 = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames2(from))
if (!__hasOwnProp2.call(to, key) && key !== except)
__defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS2 = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
var dist_src_exports = {};
__export2(dist_src_exports, {
requestLog: () => requestLog
});
module2.exports = __toCommonJS2(dist_src_exports);
var VERSION = "4.0.0";
function requestLog(octokit) {
octokit.hook.wrap("request", (request, options) => {
octokit.log.debug("request", options);
const start = Date.now();
const requestOptions = octokit.request.endpoint.parse(options);
const path = requestOptions.url.replace(options.baseUrl, "");
return request(options).then((response) => {
octokit.log.info(
`${requestOptions.method} ${path} - ${response.status} in ${Date.now() - start}ms`
);
return response;
}).catch((error) => {
octokit.log.info(
`${requestOptions.method} ${path} - ${error.status} in ${Date.now() - start}ms`
);
throw error;
});
});
}
requestLog.VERSION = VERSION;
}
});

// node_modules/@octokit/rest/dist-node/index.js
var require_dist_node12 = __commonJS({
"node_modules/@octokit/rest/dist-node/index.js"(exports2, module2) {
"use strict";
var __defProp2 = Object.defineProperty;
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
var __getOwnPropNames2 = Object.getOwnPropertyNames;
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
var __export2 = (target, all) => {
for (var name in all)
__defProp2(target, name, { get: all[name], enumerable: true });
};
var __copyProps2 = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames2(from))
if (!__hasOwnProp2.call(to, key) && key !== except)
__defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS2 = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
var dist_src_exports = {};
__export2(dist_src_exports, {
Octokit: () => Octokit
});
module2.exports = __toCommonJS2(dist_src_exports);
var import_core = require_dist_node8();
var import_plugin_request_log = require_dist_node11();
var import_plugin_paginate_rest = require_dist_node10();
var import_plugin_rest_endpoint_methods = require_dist_node9();
var VERSION = "20.0.2";
var Octokit = import_core.Octokit.plugin(
import_plugin_request_log.requestLog,
import_plugin_rest_endpoint_methods.legacyRestEndpointMethods,
import_plugin_paginate_rest.paginateRest
).defaults({
userAgent: `octokit-rest.js/${VERSION}`
});
}
});

// lib/org-rules.cjs
var require_org_rules = __commonJS({
"lib/org-rules.cjs"(exports2, module2) {
var YAML = require_dist();
var core2 = require_core();
var github = require_github();
var ghRestPlugin = require_dist_node12();
var _ = require_lodash();
var DEFAULT_RULES_NAME = "service-catalog-rules.yml";
var ghHandle = async (token = void 0) => Promise.resolve().then(
Expand Down
2 changes: 1 addition & 1 deletion lib/org-rules.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
const YAML = require('yaml')
const core = require('@actions/core')
const github = require('@actions/github')
const ghRestPlugin = require('@octokit/rest')
// const ghRestPlugin = require('@octokit/rest')
const _ = require('lodash')

/**
Expand Down

0 comments on commit 63f2d8e

Please sign in to comment.