Skip to content

Commit 1066e91

Browse files
authored
Branch was updated using the 'autoupdate branch' Actions workflow.
2 parents 0afe1a6 + ed53e2d commit 1066e91

5 files changed

Lines changed: 99 additions & 121 deletions

File tree

content/billing/index.md

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,46 @@
11
---
2-
title: Billing and payments for GitHub
2+
title: Billing and payments on GitHub
33
shortTitle: Billing and payments
4-
intro: '{% ifversion fpt %}{% data variables.product.product_name %} offers free and paid products for every account. You can upgrade, downgrade, and view pending changes to your account''s subscription at any time.{% elsif ghec or ghes or ghae %}{% data variables.product.company_short %} bills for your enterprise members'' {% ifversion ghec or ghae %}usage of {% data variables.product.product_name %}{% elsif ghes %} licence seats for {% data variables.product.product_name %}{% ifversion ghes > 3.0 %} and any additional services that you purchase{% endif %}{% endif %}.{% endif %}'
4+
intro: '{% ifversion fpt %}{% data variables.product.product_name %} offers free and paid products for every account. You can upgrade or downgrade your account''s subscription and manage your billing settings at any time.{% elsif ghec or ghes or ghae %}{% data variables.product.company_short %} bills for your enterprise members'' {% ifversion ghec or ghae %}usage of {% data variables.product.product_name %}{% elsif ghes %} licence seats for {% data variables.product.product_name %}{% ifversion ghes > 3.0 %} and any additional services that you purchase{% endif %}{% endif %}. {% endif %}{% ifversion ghec %} You can view your subscription and manage your billing settings at any time. {% endif %}{% ifversion fpt or ghec %} You can also view usage and manage spending limits for {% data variables.product.product_name %} features such as {% data variables.product.prodname_actions %}, {% data variables.product.prodname_registry %}, and {% data variables.product.prodname_codespaces %}.{% endif %}'
55
redirect_from:
66
- /github/setting-up-and-managing-billing-and-payments-on-github
77
- /categories/setting-up-and-managing-billing-and-payments-on-github
8+
introLinks:
9+
overview: '{% ifversion fpt or ghec %}/billing/managing-your-github-billing-settings/about-billing-on-github{% elsif ghes%}/billing/managing-billing-for-your-github-account/about-billing-for-your-enterprise{% endif %}'
10+
featuredLinks:
11+
guides:
12+
- '{% ifversion fpt or ghec %}/billing/managing-your-github-billing-settings/adding-or-editing-a-payment-method{% endif %}'
13+
- '{% ifversion fpt %}/billing/managing-billing-for-your-github-account/upgrading-your-github-subscription{% endif %}'
14+
- '{% ifversion ghec %}/billing/managing-billing-for-your-github-account/about-billing-for-your-enterprise{% endif %}'
15+
- '{% ifversion fpt or ghec %}/billing/managing-your-github-billing-settings/setting-your-billing-email{% endif %}'
16+
- '{% ifversion fpt or ghec %}/billing/managing-billing-for-your-github-account/about-per-user-pricing{% endif %}'
17+
- '{% ifversion ghes %}/billing/managing-billing-for-your-github-account/viewing-the-subscription-and-usage-for-your-enterprise-account{% endif %}'
18+
- '{% ifversion ghes %}/billing/managing-your-license-for-github-enterprise/about-licenses-for-github-enterprise{% endif %}'
19+
- '{% ifversion ghes %}/billing/managing-your-license-for-github-enterprise/viewing-license-usage-for-github-enterprise{% endif %}'
20+
- '{% ifversion ghae %}/billing/managing-billing-for-your-github-account/about-billing-for-your-enterprise{% endif %}'
21+
popular:
22+
- '{% ifversion ghec %}/billing/managing-billing-for-your-github-account/viewing-the-subscription-and-usage-for-your-enterprise-account{% endif %}'
23+
- '{% ifversion fpt or ghec %}/billing/managing-billing-for-your-github-account/downgrading-your-github-subscription{% endif %}'
24+
- '{% ifversion fpt or ghec %}/billing/managing-billing-for-github-actions/about-billing-for-github-actions{% endif %}'
25+
- '{% ifversion fpt or ghec %}/billing/managing-billing-for-github-codespaces/about-billing-for-codespaces{% endif %}'
26+
- '{% ifversion ghes %}/billing/managing-billing-for-github-advanced-security/about-billing-for-github-advanced-security{% endif %}'
27+
- '{% ifversion ghes %}/billing/managing-billing-for-github-advanced-security/viewing-your-github-advanced-security-usage{% endif %}'
28+
- '{% ifversion ghes %}/billing/managing-your-license-for-github-enterprise/uploading-a-new-license-to-github-enterprise-server{% endif %}'
29+
- '{% ifversion ghae %}/billing/managing-billing-for-your-github-account/about-billing-for-your-enterprise{% endif %}'
30+
guideCards:
31+
- /billing/managing-your-github-billing-settings/removing-a-payment-method
32+
- /billing/managing-billing-for-your-github-account/how-does-upgrading-or-downgrading-affect-the-billing-process
33+
- /billing/managing-billing-for-git-large-file-storage/upgrading-git-large-file-storage
34+
- '{% ifversion ghes %}/billing/managing-your-license-for-github-enterprise/downloading-your-license-for-github-enterprise{% endif %}'
35+
- '{% ifversion ghes %}/billing/managing-your-license-for-github-enterprise/syncing-license-usage-between-github-enterprise-server-and-github-enterprise-cloud{% endif %}'
36+
layout: product-landing
837
versions:
938
fpt: '*'
10-
ghec: '*'
1139
ghes: '*'
1240
ghae: '*'
41+
ghec: '*'
42+
topics:
43+
- Billing
1344
children:
1445
- /managing-your-github-billing-settings
1546
- /managing-billing-for-your-github-account
@@ -23,5 +54,4 @@ children:
2354
- /managing-billing-for-github-marketplace-apps
2455
- /managing-billing-for-git-large-file-storage
2556
- /setting-up-paid-organizations-for-procurement-companies
26-
---
27-
57+
---

lib/failbot.js

Lines changed: 27 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,112 +1,34 @@
1-
import fetch from 'node-fetch'
1+
import got from 'got'
2+
import { Failbot, HTTPBackend, LogBackend } from '@github/failbot'
23

3-
export default class FailBot {
4-
constructor({ app, haystackURL, headers }) {
5-
this.app = app
6-
this.headers = headers
4+
const HAYSTACK_APP = 'docs'
75

8-
// Since we're using `node-fetch` we can't rely on it deconstructing the
9-
// basic authentication credentials from the URL (e.g.
10-
// https://user:pass@failbotdomain/path) because `node-fetch` will always
11-
// strip it. See https://github.com/node-fetch/node-fetch/issues/1330
12-
// and it's not a bug.
13-
// The correct thing is to extract it manually and add an `Authorization`
14-
// header based on it from the URL.
15-
const url = new URL(haystackURL)
6+
export function report(error, metadata) {
7+
// If there's no HAYSTACK_URL set, bail early
8+
if (!process.env.HAYSTACK_URL) return
169

17-
// remove the basic auth portion of the url since it throws an error in node-fetch
18-
this.haystackURL = `${url.origin}${url.pathname}`
19-
20-
const { username, password } = url
21-
if (username || password) {
22-
this.headers.Authorization = `Basic ${Buffer.from(`${username}:${password}`).toString(
23-
'base64'
24-
)}`
25-
} else {
26-
console.warn(`The haystack URL does not contain authentication credentials`)
27-
}
28-
}
29-
30-
/**
31-
* Report an error to Sentry
32-
* @param {Error} error
33-
* @param {any} metadata
34-
* @param {any} [headers]
35-
*/
36-
static async report(error, metadata, headers = {}) {
37-
// If there's no HAYSTACK_URL set, bail early
38-
if (!process.env.HAYSTACK_URL) return
39-
40-
const failbot = new FailBot({
41-
app: 'docs',
10+
const backends = [
11+
new HTTPBackend({
4212
haystackURL: process.env.HAYSTACK_URL,
43-
headers,
44-
})
45-
46-
return failbot.sendException(error, metadata)
47-
}
48-
49-
/**
50-
* Create a rollup of this error by generating a base64 representation
51-
* @param {Error} error
52-
*/
53-
createRollup(error) {
54-
const stackLine = error.stack && error.stack.split('\n')[1]
55-
const str = `${error.name}:${stackLine}`.replace(/=/g, '')
56-
return Buffer.from(str).toString('base64')
57-
}
58-
59-
/**
60-
* Format the error to a plain JSON object with additional data
61-
* @param {Error} error
62-
* @param {any} metadata
63-
*/
64-
formatJSON(error, metadata) {
65-
return Object.assign({}, metadata, {
66-
/* eslint-disable camelcase */
67-
created_at: new Date().toISOString(),
68-
rollup: this.createRollup(error),
69-
class: error.name,
70-
message: error.message,
71-
backtrace: error.stack || '',
72-
js_environment: `Node.js ${process.version}`,
73-
/* eslint-enable camelcase */
74-
})
75-
}
76-
77-
/**
78-
* Populate default context from settings. Since settings commonly comes from
79-
* ENV, this allows setting defaults for the context via the environment.
80-
*/
81-
getFailbotContext() {
82-
const failbotKeys = {}
83-
84-
for (const key in process.env) {
85-
if (key.startsWith('FAILBOT_CONTEXT_')) {
86-
const formattedKey = key.replace(/^FAILBOT_CONTEXT_/, '').toLowerCase()
87-
failbotKeys[formattedKey] = process.env[key]
88-
}
89-
}
90-
91-
return failbotKeys
13+
fetchFn: got,
14+
}),
15+
]
16+
if (process.env.NODE_ENV !== 'test') {
17+
backends.push(new LogBackend({ log: console.log.bind(console) }))
9218
}
19+
const failbot = new Failbot({
20+
app: HAYSTACK_APP,
21+
backends: backends,
22+
})
23+
return failbot.report(error, metadata)
24+
}
9325

94-
/**
95-
* Send the error to Sentry
96-
* @param {Error} error
97-
* @param {any} metadata
98-
*/
99-
async sendException(error, metadata = {}) {
100-
const data = Object.assign({ app: this.app }, this.getFailbotContext(), metadata)
101-
const body = this.formatJSON(error, Object.assign({ app: this.app }, data))
102-
103-
return fetch(this.haystackURL, {
104-
method: 'POST',
105-
body: JSON.stringify(body),
106-
headers: {
107-
...this.headers,
108-
'Content-Type': 'application/json',
109-
},
110-
})
111-
}
26+
// Kept for legacy so you can continue to do:
27+
//
28+
// import FailBot from './lib/failbot.js'
29+
// ...
30+
// FailBot.report(myError)
31+
//
32+
export default {
33+
report,
11234
}

package-lock.json

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
],
1111
"dependencies": {
1212
"@alex_neo/jest-expect-message": "^1.0.5",
13+
"@github/failbot": "0.7.0",
1314
"@hapi/accept": "^5.0.2",
1415
"@primer/components": "^31.1.0",
1516
"@primer/css": "^18.2.0",

tests/unit/failbot.js

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,22 @@ import FailBot from '../../lib/failbot.js'
22
import nock from 'nock'
33

44
describe('FailBot', () => {
5+
const requestBodiesSent = []
6+
57
beforeEach(() => {
6-
nock('https://haystack.com')
8+
nock('https://haystack.example.com')
79
.post('/')
810
.reply(200, (uri, requestBody) => {
11+
requestBodiesSent.push(requestBody)
912
return requestBody
1013
})
1114
})
1215

1316
afterEach(() => {
1417
delete process.env.HAYSTACK_URL
18+
// Reset the array to an empty one between tests
19+
// so it doesn't intefere across tests.
20+
requestBodiesSent.length = 0
1521
})
1622

1723
describe('.report', () => {
@@ -21,19 +27,23 @@ describe('FailBot', () => {
2127
})
2228

2329
it('sends the expected report', async () => {
24-
process.env.HAYSTACK_URL = 'https://haystack.com'
30+
process.env.HAYSTACK_URL = 'https://haystack.example.com'
2531
const err = new Error('Kaboom')
26-
const result = await FailBot.report(err)
27-
28-
// Check that we made a request
29-
expect(result.status).toBe(200)
32+
const backendPromises = FailBot.report(err, { foo: 'bar' })
33+
// Note! You don't need to await the promises it returns to be
34+
// able to use `FailBot.report()`. It will send.
35+
// But here in the context of jest, we need to await *now*
36+
// so we can assert that it did make the relevant post requests.
37+
// Once we've done this, we can immediate check what it did.
38+
await Promise.all(await backendPromises)
3039

31-
// Verify the basic fetch params
32-
expect(result.headers.get('content-type')).toBe('application/json')
40+
// It's not interesting or relevant what the `.report()` static
41+
// method returns. All that matters is that it did a POST
42+
// request.
43+
expect(requestBodiesSent.length).toBe(1)
3344

34-
// Check that we send the expected body
35-
const body = await result.json()
36-
expect(body).toMatchObject({
45+
// Verify what was sent in that POST request.
46+
expect(requestBodiesSent[0]).toMatchObject({
3747
app: 'docs',
3848
backtrace: expect.stringContaining('Error: Kaboom'),
3949
class: 'Error',

0 commit comments

Comments
 (0)