Merge branch 'main' into video-uploads-ga
4
.babelrc
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"presets": ["next/babel"],
|
||||||
|
"plugins": [["styled-components", { "ssr": true }]]
|
||||||
|
}
|
||||||
33
.github/ISSUE_COMMENT_TEMPLATE/batch-status.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
---
|
||||||
|
name: Batch Status Update
|
||||||
|
description: Batch Status Update
|
||||||
|
body:
|
||||||
|
- type: dropdown
|
||||||
|
attributes:
|
||||||
|
label: Status
|
||||||
|
options:
|
||||||
|
- label: "GREEN \U0001F7E2 (All good, smooth sailing)"
|
||||||
|
value: 'Status: GREEN'
|
||||||
|
- label:
|
||||||
|
"YELLOW \U0001F7E1 (We've identified areas of concern, but have them under
|
||||||
|
control at this time)"
|
||||||
|
value: 'Status: YELLOW'
|
||||||
|
- label: "RED \U0001F534 (We need help, there are blockers beyond our control)"
|
||||||
|
value: 'Status: RED'
|
||||||
|
- label: GREY ⚪️ (Not started, paused, not currently being worked on)
|
||||||
|
value: 'Status: GREY'
|
||||||
|
- label: "BLACK ⚫️ (We shipped it \U0001F389)"
|
||||||
|
value: 'Status: BLACK'
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Target date
|
||||||
|
format: date
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Update
|
||||||
|
placeholder: A few words on how it's going
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: 'Attribution'
|
||||||
|
value: '_created with :heart: by typing_ `/status`'
|
||||||
|
format: text
|
||||||
29
.github/ISSUE_COMMENT_TEMPLATE/quick-status.yml
vendored
@@ -1,29 +0,0 @@
|
|||||||
---
|
|
||||||
name: A brief status update
|
|
||||||
description: A brief status update.
|
|
||||||
body:
|
|
||||||
- type: dropdown
|
|
||||||
attributes:
|
|
||||||
label: Status
|
|
||||||
options:
|
|
||||||
- label: 'GREY ⚪️ (Not started, paused, not currently being worked on)'
|
|
||||||
value: 'Status: GREY'
|
|
||||||
- label: 'GREEN 🟢 (All good, smooth sailing)'
|
|
||||||
value: 'Status: GREEN'
|
|
||||||
- label: "YELLOW \U0001F7E1 (On track, with hurdles to work through)"
|
|
||||||
value: 'Status: YELLOW'
|
|
||||||
- label: "RED \U0001F534 (BLOCKED)"
|
|
||||||
value: 'Status: RED'
|
|
||||||
- label: "BLACK ⚫️ (We shipped it \U0001F389)"
|
|
||||||
value: 'Status: BLACK'
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Update Summary
|
|
||||||
placeholder:
|
|
||||||
Brief summary of the status and next steps. Any blockers should be
|
|
||||||
called out specifically.
|
|
||||||
- type: input
|
|
||||||
attributes:
|
|
||||||
label: 'Attribution'
|
|
||||||
value: '_created with :heart: by typing_ `/status`'
|
|
||||||
format: text
|
|
||||||
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -12,19 +12,20 @@ Thanks again!
|
|||||||
|
|
||||||
### Why:
|
### Why:
|
||||||
|
|
||||||
|
Closes [issue link]
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
- If there's an existing issue for your change, please link to it.
|
- If there's an existing issue for your change, please link to it.
|
||||||
- If there's _not_ an existing issue, please open one first to make it more likely that this update will be accepted: https://github.com/github/docs/issues/new/choose. -->
|
- If there's _not_ an existing issue, please open one first to make it more likely that this update will be accepted: https://github.com/github/docs/issues/new/choose. -->
|
||||||
**Closes [issue link]**
|
|
||||||
|
|
||||||
### What's being changed:
|
### What's being changed:
|
||||||
|
|
||||||
<!-- Share artifacts of the changes, be they code snippets, GIFs or screenshots; whatever shares the most context. -->
|
<!-- Share artifacts of the changes, be they code snippets, GIFs or screenshots; whatever shares the most context. -->
|
||||||
|
|
||||||
### Check off the following:
|
### Check off the following:
|
||||||
- [ ] I have reviewed my changes in staging. (look for the **deploy-to-heroku** link in your pull request, then click **View deployment**)
|
|
||||||
- [ ] For content changes, I have reviewed the [localization checklist](https://github.com/github/docs/blob/main/contributing/localization-checklist.md)
|
- [ ] I have reviewed my changes in staging (look for the **deploy-to-heroku** link in your pull request, then click **View deployment**).
|
||||||
- [ ] For content changes, I have reviewed the [Content style guide for GitHub Docs](https://github.com/github/docs/blob/main/contributing/content-style-guide.md).
|
- [ ] For content changes, I have completed the [self-review checklist](https://github.com/github/docs/blob/main/CONTRIBUTING.md#self-review).
|
||||||
|
|
||||||
### Writer impact (This section is for GitHub staff members only):
|
### Writer impact (This section is for GitHub staff members only):
|
||||||
|
|
||||||
|
|||||||
232
.github/actions-scripts/fr-add-docs-reviewers-requests.py
vendored
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
# TODO: Convert to JavaScript for language consistency
|
||||||
|
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import requests
|
||||||
|
|
||||||
|
# Constants
|
||||||
|
endpoint = 'https://api.github.com/graphql'
|
||||||
|
|
||||||
|
# ID of the github/github repo
|
||||||
|
github_repo_id = "MDEwOlJlcG9zaXRvcnkz"
|
||||||
|
|
||||||
|
# ID of the docs-reviewers team
|
||||||
|
docs_reviewers_id = "MDQ6VGVhbTQzMDMxMzk="
|
||||||
|
|
||||||
|
# ID of the "Docs content first responder" board
|
||||||
|
docs_project_id = "MDc6UHJvamVjdDQ1NzI0ODI="
|
||||||
|
|
||||||
|
# ID of the "OpenAPI review requests" column on the "Docs content first responder" board
|
||||||
|
docs_column_id = "PC_lAPNJr_OAEXFQs4A2OFq"
|
||||||
|
|
||||||
|
# 100 is an educated guess of how many PRs are opened in a day on the github/github repo
|
||||||
|
# If we are missing PRs, either increase this number or increase the frequency at which this script is run
|
||||||
|
num_prs_to_search = 100
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def find_open_prs_for_repo(repo_id: str, num_prs: int):
|
||||||
|
"""Return data about a specified number of open PRs for a specified repo
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
repo_id: The node ID of the repo to search
|
||||||
|
num_prs: The max number of PRs to return
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Returns a JSON object of this structure:
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"node": {
|
||||||
|
"pullRequests": {
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": str,
|
||||||
|
"isDraft": bool,
|
||||||
|
"reviewRequests": {
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"requestedReviewer": {
|
||||||
|
"id": str
|
||||||
|
}
|
||||||
|
}...
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"projectCards": {
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"project": {
|
||||||
|
"id": str
|
||||||
|
}
|
||||||
|
}...
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}...
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
query = """query ($repo_id: ID!, $num_prs: Int!) {
|
||||||
|
node(id: $repo_id) {
|
||||||
|
... on Repository {
|
||||||
|
pullRequests(last: $num_prs, states: OPEN) {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
isDraft
|
||||||
|
reviewRequests(first: 10) {
|
||||||
|
nodes {
|
||||||
|
requestedReviewer {
|
||||||
|
... on Team {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
projectCards(first: 10) {
|
||||||
|
nodes {
|
||||||
|
project {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
variables = {
|
||||||
|
"repo_id": github_repo_id,
|
||||||
|
"num_prs": num_prs
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.post(
|
||||||
|
endpoint,
|
||||||
|
json={'query': query, 'variables': variables},
|
||||||
|
headers = {'Authorization': f"bearer {os.environ['TOKEN']}"}
|
||||||
|
)
|
||||||
|
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
json_response = json.loads(response.text)
|
||||||
|
|
||||||
|
if 'errors' in json_response:
|
||||||
|
raise RuntimeError(f'Error in GraphQL response: {json_response}')
|
||||||
|
|
||||||
|
return json_response
|
||||||
|
|
||||||
|
def add_prs_to_board(prs_to_add: list, column_id: str):
|
||||||
|
"""Adds PRs to a column of a project board
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
prs_to_add: A list of PR node IDs
|
||||||
|
column_id: The node ID of the column to add the PRs to
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Nothing
|
||||||
|
"""
|
||||||
|
|
||||||
|
logger.info(f"adding: {prs_to_add}")
|
||||||
|
|
||||||
|
mutation = """mutation($pr_id: ID!, $column_id: ID!) {
|
||||||
|
addProjectCard(input:{contentId: $pr_id, projectColumnId: $column_id}) {
|
||||||
|
projectColumn {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
|
||||||
|
for pr_id in prs_to_add:
|
||||||
|
logger.info(f"Attempting to add {pr_id} to board")
|
||||||
|
|
||||||
|
variables = {
|
||||||
|
"pr_id": pr_id,
|
||||||
|
"column_id": column_id
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.post(
|
||||||
|
endpoint,
|
||||||
|
json={'query': mutation, 'variables': variables},
|
||||||
|
headers = {'Authorization': f"bearer {os.environ['TOKEN']}"}
|
||||||
|
)
|
||||||
|
|
||||||
|
json_response = json.loads(response.text)
|
||||||
|
|
||||||
|
if 'errors' in json_response:
|
||||||
|
logger.info(f"GraphQL error when adding {pr_id}: {json_response}")
|
||||||
|
|
||||||
|
def filter_prs(data, reviewer_id: str, project_id):
|
||||||
|
"""Given data about the draft state, reviewers, and project boards for PRs,
|
||||||
|
return just the PRs that are:
|
||||||
|
- not draft
|
||||||
|
- are requesting a review for the specified team
|
||||||
|
- are not already on the specified project board
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
data: A JSON object of this structure:
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"node": {
|
||||||
|
"pullRequests": {
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": str,
|
||||||
|
"isDraft": bool,
|
||||||
|
"reviewRequests": {
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"requestedReviewer": {
|
||||||
|
"id": str
|
||||||
|
}
|
||||||
|
}...
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"projectCards": {
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"project": {
|
||||||
|
"id": str
|
||||||
|
}
|
||||||
|
}...
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}...
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reviewer_id: The node ID of the reviewer to filter for
|
||||||
|
project_id: The project ID of the project to filter against
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A list of node IDs of the PRs that met the requirements
|
||||||
|
"""
|
||||||
|
|
||||||
|
pr_data = data['data']['node']['pullRequests']['nodes']
|
||||||
|
|
||||||
|
prs_to_add = []
|
||||||
|
|
||||||
|
for pr in pr_data:
|
||||||
|
if (
|
||||||
|
not pr['isDraft'] and
|
||||||
|
reviewer_id in [req_rev['requestedReviewer']['id'] for req_rev in pr['reviewRequests']['nodes'] if req_rev['requestedReviewer']] and
|
||||||
|
project_id not in [proj_card['project']['id'] for proj_card in pr['projectCards']['nodes']]
|
||||||
|
):
|
||||||
|
prs_to_add.append(pr['id'])
|
||||||
|
|
||||||
|
return prs_to_add
|
||||||
|
|
||||||
|
def main():
|
||||||
|
query_data = find_open_prs_for_repo(github_repo_id, num_prs_to_search)
|
||||||
|
prs_to_add = filter_prs(query_data, docs_reviewers_id, docs_project_id)
|
||||||
|
add_prs_to_board(prs_to_add, docs_column_id)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
1
.github/allowed-actions.js
vendored
@@ -9,6 +9,7 @@ module.exports = [
|
|||||||
"actions/github-script@626af12fe9a53dc2972b48385e7fe7dec79145c9", // v3.0.0
|
"actions/github-script@626af12fe9a53dc2972b48385e7fe7dec79145c9", // v3.0.0
|
||||||
"actions/labeler@5f867a63be70efff62b767459b009290364495eb", // v2.2.0
|
"actions/labeler@5f867a63be70efff62b767459b009290364495eb", // v2.2.0
|
||||||
"actions/setup-node@c46424eee26de4078d34105d3de3cc4992202b1e", // v2.1.4
|
"actions/setup-node@c46424eee26de4078d34105d3de3cc4992202b1e", // v2.1.4
|
||||||
|
"actions/setup-python@dc73133d4da04e56a135ae2246682783cc7c7cb6", // v2.2.2
|
||||||
"ruby/setup-ruby@fdcfbcf14ec9672f6f615cb9589a1bc5dd69d262", // v1.64.1
|
"ruby/setup-ruby@fdcfbcf14ec9672f6f615cb9589a1bc5dd69d262", // v1.64.1
|
||||||
"actions/stale@9d6f46564a515a9ea11e7762ab3957ee58ca50da", // v3.0.16
|
"actions/stale@9d6f46564a515a9ea11e7762ab3957ee58ca50da", // v3.0.16
|
||||||
"alex-page/github-project-automation-plus@fdb7991b72040d611e1123d2b75ff10eda9372c9",
|
"alex-page/github-project-automation-plus@fdb7991b72040d611e1123d2b75ff10eda9372c9",
|
||||||
|
|||||||
34
.github/review-template.md
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
## Author self-review
|
||||||
|
|
||||||
|
- [ ] The changes in this PR meet the user experience and goals outlined in the content design plan.
|
||||||
|
- [ ] I've compared my PR's source changes to staging and reviewed for versioning issues, redirects, the [style guide](https://github.com/github/docs/blob/main/contributing/content-style-guide.md), [content model](https://github.com/github/docs-content-strategy/blob/main/content-design/models.md), or [localization checklist](https://github.com/github/docs/blob/main/contributing/localization-checklist.md) rendering problems, typos, and wonky screenshots.
|
||||||
|
- [ ] I've worked through build failures and tests are passing.
|
||||||
|
- [ ] For REST API content, I've verified that endpoints, parameters, and responses are correct and work as expected and provided curl samples below.
|
||||||
|
|
||||||
|
For more information, check out our [full review guidelines and checklist](https://github.com/github/docs-content/blob/main/docs-content-docs/docs-content-workflows/reviews-and-feedback/review-process.md).
|
||||||
|
|
||||||
|
## Review request
|
||||||
|
|
||||||
|
### Summary
|
||||||
|
|
||||||
|
_Help reviewers understand this project and its context by writing a paragraph summarizing its goals and intended user experience and explaining how the PR meets those goals._
|
||||||
|
[Content design plan](LINK HERE)
|
||||||
|
|
||||||
|
### Docs Content review
|
||||||
|
|
||||||
|
_Give Docs Content any extra context, highlight areas for them to consider in their review, and ask them questions you need answered to ship the PR._
|
||||||
|
|
||||||
|
### Technical review
|
||||||
|
|
||||||
|
_Ping in technical reviewers, asking them to review whether content is technically accurate and right for the audience._
|
||||||
|
_Highlight areas for them to consider in their review and ask them questions you need answered to ship the PR._
|
||||||
|
|
||||||
|
### Content changes
|
||||||
|
|
||||||
|
[PR on staging](LINK HERE)
|
||||||
|
|
||||||
|
_Give a high-level overview of the changes in your PR and how they support the overall goals of the PR. Share links to important articles or changes in source and on staging. If your PR is large or complex, use a table to highlight changes with high user impact._
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
_Discuss test failures, versioning issues, or anything else reviewers should know to consider the overall user experience of the PR._
|
||||||
36
.github/workflows/add-review-template.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
name: Add review template
|
||||||
|
|
||||||
|
# **What it does**: When a specific label is added to a PR, adds the contents of .github/review-template.md as a comment in the PR
|
||||||
|
# **Why we have it**: To help Docs Content team members ensure that their PR is ready for review
|
||||||
|
# **Who does it impact**: docs-internal maintainers and contributors
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types:
|
||||||
|
- labeled
|
||||||
|
|
||||||
|
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'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: check out repo content
|
||||||
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||||
|
|
||||||
|
# Jump through some hoops to work with a multi-line file
|
||||||
|
- name: Store review template in variable
|
||||||
|
run: |
|
||||||
|
TEMPLATE=$(cat .github/workflows/review-template.md)
|
||||||
|
echo "TEMPLATE<<EOF" >> $GITHUB_ENV
|
||||||
|
echo "$TEMPLATE" >> $GITHUB_ENV
|
||||||
|
echo "EOF" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Comment on the PR
|
||||||
|
run: |
|
||||||
|
gh pr comment $PR --body "$TEMPLATE"
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{secrets.DOCUBOT_FR_PROJECT_BOARD_WORKFLOWS_REPO_ORG_READ_SCOPES}}
|
||||||
|
PR: ${{ github.event.pull_request.html_url }}
|
||||||
|
TEMPLATE: ${{ env.TEMPLATE }}
|
||||||
35
.github/workflows/docs-review-collect.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
name: Add docs-reviewers request to FR board
|
||||||
|
|
||||||
|
# **What it does**: Adds PRs in github/github that requested a review from docs-reviewers to the FR board
|
||||||
|
# **Why we have it**: To catch docs-reviewers requests in github/github
|
||||||
|
# **Who does it impact**: docs-content maintainers
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
- cron: '25 4 * * *'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
add-requests-to-board:
|
||||||
|
name: Add requests to board
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out repo content
|
||||||
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||||
|
|
||||||
|
- name: Set up Python 3.9
|
||||||
|
uses: actions/setup-python@dc73133d4da04e56a135ae2246682783cc7c7cb6
|
||||||
|
with:
|
||||||
|
python-version: '3.9'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install requests
|
||||||
|
|
||||||
|
- name: Run script
|
||||||
|
run: |
|
||||||
|
python .github/actions-scripts/fr-add-docs-reviewers-requests.py
|
||||||
|
env:
|
||||||
|
TOKEN: ${{ secrets.DOCS_BOT }}
|
||||||
@@ -158,12 +158,12 @@ We (usually the docs team, but sometimes GitHub product managers, engineers, or
|
|||||||
You should always review your own PR first.
|
You should always review your own PR first.
|
||||||
|
|
||||||
For content changes, make sure that you:
|
For content changes, make sure that you:
|
||||||
- [ ] Confirm that the changes address every part of the content design plan from your issue (if there are differences, explain them).
|
- [ ] Confirm that the changes meet the user experience and goals outlined in the content design plan (if there is one).
|
||||||
|
- [ ] Compare your pull request's source changes to staging to confirm that the output matches the source and that everything is rendering as expected. This helps spot issues like typos, content that doesn't follow the style guide, or content that isn't rendering due to versioning problems. Remember that lists and tables can be tricky.
|
||||||
- [ ] Review the content for technical accuracy.
|
- [ ] Review the content for technical accuracy.
|
||||||
- [ ] Review the entire pull request using the [localization checklist](contributing/localization-checklist.md).
|
- [ ] Review the entire pull request using the [localization checklist](contributing/localization-checklist.md).
|
||||||
- [ ] Copy-edit the changes for grammar, spelling, and adherence to the style guide.
|
- [ ] Copy-edit the changes for grammar, spelling, and adherence to the [style guide](https://github.com/github/docs/blob/main/contributing/content-style-guide.md).
|
||||||
- [ ] Check new or updated Liquid statements to confirm that versioning is correct.
|
- [ ] Check new or updated Liquid statements to confirm that versioning is correct.
|
||||||
- [ ] Check that all of your changes render correctly in staging. Remember, that lists and tables can be tricky.
|
|
||||||
- [ ] If there are any failing checks in your PR, troubleshoot them until they're all passing.
|
- [ ] If there are any failing checks in your PR, troubleshoot them until they're all passing.
|
||||||
|
|
||||||
### Pull request template
|
### Pull request template
|
||||||
|
|||||||
BIN
assets/images/help/codespaces/add-dotnet-prebuilt-container.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
assets/images/help/codespaces/add-dotnet-version.png
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
assets/images/help/codespaces/add-extension.png
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
BIN
assets/images/help/codespaces/add-java-prebuilt-container.png
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
assets/images/help/codespaces/add-java-version.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
assets/images/help/codespaces/autofetch-all.png
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
assets/images/help/codespaces/autofetch-search.png
Normal file
|
After Width: | Height: | Size: 316 KiB |
BIN
assets/images/help/codespaces/branch-in-status-bar.png
Normal file
|
After Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 190 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
BIN
assets/images/help/codespaces/codespace-overview-annotated.png
Normal file
|
After Width: | Height: | Size: 338 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 156 KiB |
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 126 KiB |
|
After Width: | Height: | Size: 22 KiB |
BIN
assets/images/help/codespaces/codespaces-manage.png
Normal file
|
After Width: | Height: | Size: 103 KiB |
BIN
assets/images/help/codespaces/codespaces-npm-run-dev.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
assets/images/help/codespaces/codespaces-option-secrets-org.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
assets/images/help/codespaces/codespaces-option-secrets.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 73 KiB |
BIN
assets/images/help/codespaces/create-new-branch.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
assets/images/help/codespaces/custom-prompt.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 354 KiB After Width: | Height: | Size: 407 KiB |
|
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 46 KiB |
BIN
assets/images/help/codespaces/dotnet-extensions.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/images/help/codespaces/dotnet-options.png
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
BIN
assets/images/help/codespaces/fairyfloss.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 6.6 KiB |
BIN
assets/images/help/codespaces/manage-button.png
Normal file
|
After Width: | Height: | Size: 85 KiB |
|
After Width: | Height: | Size: 109 KiB |
BIN
assets/images/help/codespaces/quickstart-port-toast.png
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
assets/images/help/codespaces/secret-repository-access.png
Normal file
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 68 KiB |
BIN
assets/images/help/codespaces/source-control-ellipsis-button.png
Normal file
|
After Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 50 KiB |
BIN
assets/images/help/pull_requests/pull-request-body.png
Normal file
|
After Width: | Height: | Size: 279 KiB |
BIN
assets/images/help/pull_requests/pull-request-comment.png
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
assets/images/help/repository/environments-top.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 32 KiB |
BIN
assets/images/help/settings/codespaces-audit-log-org.png
Normal file
|
After Width: | Height: | Size: 337 KiB |
BIN
assets/images/help/settings/codespaces-audit-log.png
Normal file
|
After Width: | Height: | Size: 244 KiB |
|
After Width: | Height: | Size: 129 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 67 KiB |
BIN
assets/images/help/sponsors/disable-your-account-button.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
assets/images/help/sponsors/unpublish-profile-button.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
assets/images/help/sponsors/unpublish-profile-dialog.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 36 KiB |
@@ -1,7 +1,7 @@
|
|||||||
import Head from 'next/head'
|
import Head from 'next/head'
|
||||||
|
|
||||||
// import { Sidebar } from 'components/Sidebar'
|
// import { Sidebar } from 'components/Sidebar'
|
||||||
// import { Header } from 'components/Header'
|
import { Header } from 'components/Header'
|
||||||
import { SmallFooter } from 'components/SmallFooter'
|
import { SmallFooter } from 'components/SmallFooter'
|
||||||
import { ScrollButton } from 'components/ScrollButton'
|
import { ScrollButton } from 'components/ScrollButton'
|
||||||
import { SupportSection } from 'components/SupportSection'
|
import { SupportSection } from 'components/SupportSection'
|
||||||
@@ -21,7 +21,7 @@ export const DefaultLayout = (props: Props) => {
|
|||||||
{/* <Sidebar /> */}
|
{/* <Sidebar /> */}
|
||||||
|
|
||||||
<main className="width-full">
|
<main className="width-full">
|
||||||
{/* <Header /> */}
|
<Header />
|
||||||
<DeprecationBanner />
|
<DeprecationBanner />
|
||||||
|
|
||||||
{props.children}
|
{props.children}
|
||||||
|
|||||||
113
components/Header.tsx
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
import { useState } from 'react'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import cx from 'classnames'
|
||||||
|
import { css } from 'styled-components'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import { ChevronDownIcon, MarkGithubIcon, ThreeBarsIcon, XIcon } from '@primer/octicons-react'
|
||||||
|
import { ButtonOutline } from '@primer/components'
|
||||||
|
|
||||||
|
import { useMainContext } from './context/MainContext'
|
||||||
|
import { LanguagePicker } from './LanguagePicker'
|
||||||
|
import { HeaderNotifications } from 'components/HeaderNotifications'
|
||||||
|
import { MobileProductDropdown } from 'components/MobileProductDropdown'
|
||||||
|
import { useTranslation } from 'components/hooks/useTranslation'
|
||||||
|
|
||||||
|
export const Header = () => {
|
||||||
|
const router = useRouter()
|
||||||
|
const { currentProduct, relativePath, error } = useMainContext()
|
||||||
|
const { t } = useTranslation(['header', 'homepage'])
|
||||||
|
|
||||||
|
const [isMenuOpen, setIsMenuOpen] = useState(false)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="border-bottom color-border-secondary no-print">
|
||||||
|
{error !== '404' && <HeaderNotifications />}
|
||||||
|
|
||||||
|
<header className="container-xl px-3 px-md-6 pt-3 pb-2 position-relative d-flex flex-justify-between width-full">
|
||||||
|
<div
|
||||||
|
className="d-flex flex-items-center d-lg-none"
|
||||||
|
style={{ zIndex: 3 }}
|
||||||
|
id="github-logo-mobile"
|
||||||
|
role="banner"
|
||||||
|
>
|
||||||
|
<Link href={`/${router.locale}`}>
|
||||||
|
<a aria-hidden="true" tabIndex={-1}>
|
||||||
|
<MarkGithubIcon size={32} className="color-icon-primary" />
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Link href={`/${router.locale}`}>
|
||||||
|
<a className="h4-mktg color-text-primary no-underline no-wrap pl-2">
|
||||||
|
{t('github_docs')}
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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)}>
|
||||||
|
{isMenuOpen ? <XIcon size="small" /> : <ThreeBarsIcon size="small" />}
|
||||||
|
</ButtonOutline>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{ zIndex: 2 }}
|
||||||
|
className={cx('nav-mobile-dropdown width-full', isMenuOpen && 'js-open')}
|
||||||
|
>
|
||||||
|
<div className="d-md-flex flex-justify-between flex-items-center">
|
||||||
|
<div className="py-2 py-md-0 d-md-inline-block">
|
||||||
|
<h4 className="text-mono f5 text-normal color-text-secondary d-md-none">
|
||||||
|
{t('explore_by_product')}
|
||||||
|
</h4>
|
||||||
|
<details className="dropdown-withArrow position-relative details details-reset d-md-none close-when-clicked-outside">
|
||||||
|
<summary
|
||||||
|
className="nav-desktop-productDropdownButton color-text-link py-2"
|
||||||
|
role="button"
|
||||||
|
aria-label="Toggle products list"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
id="current-product"
|
||||||
|
className="d-flex flex-items-center flex-justify-between"
|
||||||
|
style={{ paddingTop: 2 }}
|
||||||
|
>
|
||||||
|
{/* <!-- Product switcher - GitHub.com, Enterprise Server, etc -->
|
||||||
|
<!-- 404 and 500 error layouts are not real pages so we need to hardcode the name for those --> */}
|
||||||
|
{currentProduct.name}
|
||||||
|
<ChevronDownIcon size={24} className="arrow ml-md-1" />
|
||||||
|
</div>
|
||||||
|
</summary>
|
||||||
|
|
||||||
|
<MobileProductDropdown />
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* <!-- Versions picker that only appears in the header on landing pages --> */}
|
||||||
|
{/* {% include header-version-switcher %} */}
|
||||||
|
|
||||||
|
<div className="d-md-inline-block">
|
||||||
|
{/* <!-- Language picker - 'English', 'Japanese', etc --> */}
|
||||||
|
<div className="border-top border-md-top-0 py-2 py-md-0 d-md-inline-block">
|
||||||
|
<LanguagePicker />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* <!-- GitHub.com homepage and 404 page has a stylized search; Enterprise homepages do not --> */}
|
||||||
|
{relativePath !== 'index.md' && error !== '404'}
|
||||||
|
<div
|
||||||
|
className="pt-3 pt-md-0 d-md-inline-block ml-md-3 border-top border-md-top-0"
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
<div id="search-input-container" aria-hidden="true"></div>
|
||||||
|
<div id="search-results-container"></div>
|
||||||
|
<div class="search-overlay-desktop"></div>
|
||||||
|
`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
89
components/HeaderNotifications.tsx
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import cx from 'classnames'
|
||||||
|
|
||||||
|
import { useMainContext } from 'components/context/MainContext'
|
||||||
|
import { useTranslation } from 'components/hooks/useTranslation'
|
||||||
|
import { ExcludesNull } from 'components/lib/ExcludesNull'
|
||||||
|
import { useVersion } from './hooks/useVersion'
|
||||||
|
|
||||||
|
enum NotificationType {
|
||||||
|
RELEASE = 'RELEASE',
|
||||||
|
TRANSLATION = 'TRANSLATION',
|
||||||
|
EARLY_ACCESS = 'EARLY_ACCESS',
|
||||||
|
}
|
||||||
|
|
||||||
|
type Notif = {
|
||||||
|
content: string
|
||||||
|
type: NotificationType
|
||||||
|
}
|
||||||
|
export const HeaderNotifications = () => {
|
||||||
|
const router = useRouter()
|
||||||
|
const { currentVersion } = useVersion()
|
||||||
|
const { relativePath, allVersions, data, languages, currentLanguage } = useMainContext()
|
||||||
|
const { t } = useTranslation('header')
|
||||||
|
|
||||||
|
const translationNotices: Array<Notif> = []
|
||||||
|
if (router.locale !== 'en') {
|
||||||
|
if (relativePath?.includes('/site-policy')) {
|
||||||
|
translationNotices.push({
|
||||||
|
type: NotificationType.TRANSLATION,
|
||||||
|
content: data.reusables.policies.translation,
|
||||||
|
})
|
||||||
|
} else if (languages[currentLanguage].wip !== true) {
|
||||||
|
translationNotices.push({
|
||||||
|
type: NotificationType.TRANSLATION,
|
||||||
|
content: t('notices.localization_complete'),
|
||||||
|
})
|
||||||
|
} else if (languages[currentLanguage].wip) {
|
||||||
|
translationNotices.push({
|
||||||
|
type: NotificationType.TRANSLATION,
|
||||||
|
content: t('notices.localization_in_progress'),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const releaseNotices: Array<Notif> = []
|
||||||
|
if (currentVersion === 'github-ae@latest') {
|
||||||
|
releaseNotices.push({
|
||||||
|
type: NotificationType.RELEASE,
|
||||||
|
content: t('notices.ghae_silent_launch'),
|
||||||
|
})
|
||||||
|
} else if (currentVersion === data.variables.release_candidate.version) {
|
||||||
|
releaseNotices.push({
|
||||||
|
type: NotificationType.RELEASE,
|
||||||
|
content: `${allVersions[currentVersion].versionTitle}${t('notices.release_candidate')}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const allNotifications: Array<Notif> = [
|
||||||
|
...translationNotices,
|
||||||
|
...releaseNotices,
|
||||||
|
// ONEOFF EARLY ACCESS NOTICE
|
||||||
|
(relativePath || '').includes('early-access/')
|
||||||
|
? {
|
||||||
|
type: NotificationType.EARLY_ACCESS,
|
||||||
|
content: t('notices.early_access'),
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
].filter(ExcludesNull)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{allNotifications.map(({ type, content }, i) => {
|
||||||
|
const isLast = i !== allNotifications.length - 1
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cx(
|
||||||
|
'header-notifications text-center f5 color-text-primary py-4 px-6',
|
||||||
|
type === NotificationType.TRANSLATION && 'translation_notice color-bg-info',
|
||||||
|
type === NotificationType.RELEASE && 'release_notice color-bg-info',
|
||||||
|
type === NotificationType.EARLY_ACCESS && 'early_access color-bg-danger',
|
||||||
|
!isLast && 'border-bottom color-border-tertiary'
|
||||||
|
)}
|
||||||
|
dangerouslySetInnerHTML={{ __html: content }}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
42
components/LanguagePicker.tsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import Link from 'next/link'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import { Dropdown } from '@primer/components'
|
||||||
|
import { useMainContext } from './context/MainContext'
|
||||||
|
|
||||||
|
export const LanguagePicker = () => {
|
||||||
|
const router = useRouter()
|
||||||
|
const { languages } = useMainContext()
|
||||||
|
const locale = router.locale || 'en'
|
||||||
|
const langs = Object.values(languages)
|
||||||
|
const selectedLang = languages[locale]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="ml-4 d-flex flex-justify-center flex-items-center">
|
||||||
|
<Dropdown css>
|
||||||
|
<summary>
|
||||||
|
{selectedLang.nativeName || selectedLang.name}
|
||||||
|
<Dropdown.Caret />
|
||||||
|
</summary>
|
||||||
|
<Dropdown.Menu direction="sw">
|
||||||
|
{langs.map((lang) => {
|
||||||
|
return (
|
||||||
|
<Dropdown.Item key={lang.code}>
|
||||||
|
<Link href={router.asPath} locale={lang.hreflang}>
|
||||||
|
<a>
|
||||||
|
{lang.nativeName ? (
|
||||||
|
<>
|
||||||
|
{lang.nativeName} ({lang.name})
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
lang.name
|
||||||
|
)}
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
</Dropdown.Item>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</Dropdown.Menu>
|
||||||
|
</Dropdown>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
44
components/MobileProductDropdown.tsx
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import Link from 'next/link'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import { LinkExternalIcon } from '@primer/octicons-react'
|
||||||
|
import cx from 'classnames'
|
||||||
|
|
||||||
|
import { useMainContext } from 'components/context/MainContext'
|
||||||
|
|
||||||
|
export const MobileProductDropdown = () => {
|
||||||
|
const router = useRouter()
|
||||||
|
const { activeProducts, currentProduct } = useMainContext()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
id="homepages"
|
||||||
|
className="position-md-absolute nav-desktop-productDropdown p-md-4 left-md-n4 top-md-6"
|
||||||
|
style={{ zIndex: 6 }}
|
||||||
|
>
|
||||||
|
{activeProducts.map((product) => {
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
key={product.id}
|
||||||
|
href={`${product.external ? '' : `/${router.locale}`}${product.href}`}
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
className={cx(
|
||||||
|
'd-block py-2',
|
||||||
|
product.id === currentProduct.id
|
||||||
|
? 'color-text-link text-underline active'
|
||||||
|
: 'Link--primary no-underline'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{product.name}
|
||||||
|
{product.external && (
|
||||||
|
<span className="ml-1">
|
||||||
|
<LinkExternalIcon size="small" />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
25
components/TruncateLines.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import React, { ReactNode, ReactHTML } from 'react'
|
||||||
|
import cx from 'classnames'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
as?: keyof ReactHTML
|
||||||
|
maxLines: number
|
||||||
|
children: ReactNode
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
export const TruncateLines = (props: Props) => {
|
||||||
|
const Component = props.as || 'div'
|
||||||
|
return (
|
||||||
|
<Component className={cx('root', props.className)}>
|
||||||
|
{props.children}
|
||||||
|
<style jsx>{`
|
||||||
|
.root {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: ${props.maxLines};
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</Component>
|
||||||
|
)
|
||||||
|
}
|
||||||
44
components/VersionPicker.tsx
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import Link from 'next/link'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import { Dropdown } from '@primer/components'
|
||||||
|
import { useMainContext } from './context/MainContext'
|
||||||
|
import { useVersion } from './hooks/useVersion'
|
||||||
|
|
||||||
|
export const VersionPicker = () => {
|
||||||
|
const router = useRouter()
|
||||||
|
const { currentVersion } = useVersion()
|
||||||
|
const { allVersions } = useMainContext()
|
||||||
|
|
||||||
|
const versions = Object.values(allVersions)
|
||||||
|
const activeVersion = allVersions[currentVersion]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="ml-4 d-flex flex-justify-center flex-items-center">
|
||||||
|
<Dropdown css>
|
||||||
|
<summary>
|
||||||
|
{activeVersion.versionTitle}
|
||||||
|
<Dropdown.Caret />
|
||||||
|
</summary>
|
||||||
|
<Dropdown.Menu direction="sw">
|
||||||
|
{versions.map((version) => {
|
||||||
|
return (
|
||||||
|
<Dropdown.Item key={version.version}>
|
||||||
|
<Link
|
||||||
|
href={{
|
||||||
|
pathname: router.pathname,
|
||||||
|
query: {
|
||||||
|
...router.query,
|
||||||
|
versionId: version.version,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<a>{version.versionTitle}</a>
|
||||||
|
</Link>
|
||||||
|
</Dropdown.Item>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</Dropdown.Menu>
|
||||||
|
</Dropdown>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -9,6 +9,19 @@ type ProductT = {
|
|||||||
name: string
|
name: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LanguageItem = {
|
||||||
|
name: string
|
||||||
|
nativeName: string
|
||||||
|
code: string
|
||||||
|
hreflang: string
|
||||||
|
wip?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
type VersionItem = {
|
||||||
|
version: string
|
||||||
|
versionTitle: string
|
||||||
|
}
|
||||||
|
|
||||||
type DataT = {
|
type DataT = {
|
||||||
ui: Record<string, any>
|
ui: Record<string, any>
|
||||||
reusables: {
|
reusables: {
|
||||||
@@ -18,6 +31,12 @@ type DataT = {
|
|||||||
deprecation_details: string
|
deprecation_details: string
|
||||||
isOldestReleaseDeprecated: boolean
|
isOldestReleaseDeprecated: boolean
|
||||||
}
|
}
|
||||||
|
policies: {
|
||||||
|
translation: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
variables: {
|
||||||
|
release_candidate: { version: string }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
type EnterpriseServerReleases = {
|
type EnterpriseServerReleases = {
|
||||||
@@ -34,9 +53,13 @@ export type MainContextT = {
|
|||||||
currentLayoutName: string
|
currentLayoutName: string
|
||||||
data: DataT
|
data: DataT
|
||||||
airGap?: boolean
|
airGap?: boolean
|
||||||
|
error: string
|
||||||
currentCategory?: string
|
currentCategory?: string
|
||||||
relativePath?: string
|
relativePath?: string
|
||||||
enterpriseServerReleases: EnterpriseServerReleases
|
enterpriseServerReleases: EnterpriseServerReleases
|
||||||
|
currentLanguage: string
|
||||||
|
languages: Record<string, LanguageItem>
|
||||||
|
allVersions: Record<string, VersionItem>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getMainContextFromRequest = (req: any): MainContextT => {
|
export const getMainContextFromRequest = (req: any): MainContextT => {
|
||||||
@@ -47,16 +70,36 @@ export const getMainContextFromRequest = (req: any): MainContextT => {
|
|||||||
activeProducts: req.context.activeProducts,
|
activeProducts: req.context.activeProducts,
|
||||||
currentProduct: req.context.productMap[req.context.currentProduct],
|
currentProduct: req.context.productMap[req.context.currentProduct],
|
||||||
currentLayoutName: req.context.currentLayoutName,
|
currentLayoutName: req.context.currentLayoutName,
|
||||||
|
error: req.context.error || '',
|
||||||
data: {
|
data: {
|
||||||
ui: req.context.site.data.ui,
|
ui: req.context.site.data.ui,
|
||||||
reusables: {
|
reusables: {
|
||||||
enterprise_deprecation: req.context.site.data.reusables.enterprise_deprecation,
|
enterprise_deprecation: req.context.site.data.reusables.enterprise_deprecation,
|
||||||
|
policies: req.context.site.data.reusables.policies,
|
||||||
|
},
|
||||||
|
variables: {
|
||||||
|
release_candidate: req.context.site.data.variables.release_candidate,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
airGap: req.context.AIRGAP || false,
|
airGap: req.context.AIRGAP || false,
|
||||||
currentCategory: req.context.currentCategory || '',
|
currentCategory: req.context.currentCategory || '',
|
||||||
relativePath: req.context.page.relativePath,
|
relativePath: req.context.page.relativePath,
|
||||||
enterpriseServerReleases: req.context.enterpriseServerReleases,
|
enterpriseServerReleases: req.context.enterpriseServerReleases,
|
||||||
|
currentLanguage: req.context.currentLanguage,
|
||||||
|
languages: Object.fromEntries(
|
||||||
|
Object.entries(req.context.languages).map(([key, entry]: any) => {
|
||||||
|
return [
|
||||||
|
key,
|
||||||
|
{
|
||||||
|
name: entry.name,
|
||||||
|
nativeName: entry.nativeName || '',
|
||||||
|
code: entry.code,
|
||||||
|
hreflang: entry.hreflang,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
),
|
||||||
|
allVersions: req.context.allVersions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
126
components/context/ProductLandingContext.tsx
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
import { createContext, useContext } from 'react'
|
||||||
|
import pick from 'lodash/pick'
|
||||||
|
|
||||||
|
export type FeaturedLink = {
|
||||||
|
title: string
|
||||||
|
href: string
|
||||||
|
intro?: string
|
||||||
|
authors?: Array<string>
|
||||||
|
hideIntro?: boolean
|
||||||
|
date?: string
|
||||||
|
}
|
||||||
|
export type CodeExample = {
|
||||||
|
title: string
|
||||||
|
description: string
|
||||||
|
languages: string // single comma separated string
|
||||||
|
href: string
|
||||||
|
tags: Array<string>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProductLandingContextT = {
|
||||||
|
title: string
|
||||||
|
introPlainText: string
|
||||||
|
shortTitle: string
|
||||||
|
intro: string
|
||||||
|
beta_product: boolean
|
||||||
|
// primaryAction: LinkButtonT
|
||||||
|
// secondaryAction?: LinkButtonT
|
||||||
|
introLinks: {
|
||||||
|
quickstart?: string
|
||||||
|
reference?: string
|
||||||
|
overview?: string
|
||||||
|
}
|
||||||
|
product_video?: string
|
||||||
|
// featuredLinks?: {
|
||||||
|
// guides: Array<FeaturedLink>
|
||||||
|
// popular: Array<FeaturedLink>
|
||||||
|
// guideCards: Array<FeaturedLink>
|
||||||
|
// }
|
||||||
|
guideCards: Array<FeaturedLink>
|
||||||
|
productCodeExamples: Array<CodeExample>
|
||||||
|
productUserExamples: Array<{ username: string; description: string }>
|
||||||
|
productCommunityExamples: Array<{ repo: string; description: string }>
|
||||||
|
featuredArticles: Array<{
|
||||||
|
label: string // Guides
|
||||||
|
viewAllHref?: string // If provided, adds a "View All ->" to the header
|
||||||
|
articles: Array<FeaturedLink>
|
||||||
|
}>
|
||||||
|
changelog: { label: string; prefix: string }
|
||||||
|
changelogUrl?: string
|
||||||
|
whatsNewChangelog?: Array<{ href: string; title: string; date: string }>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ProductLandingContext = createContext<ProductLandingContextT | null>(null)
|
||||||
|
|
||||||
|
export const useProductLandingContext = (): ProductLandingContextT => {
|
||||||
|
const context = useContext(ProductLandingContext)
|
||||||
|
|
||||||
|
if (!context) {
|
||||||
|
throw new Error(
|
||||||
|
'"useProductLandingContext" may only be used inside "ProductLandingContext.Provider"'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getProductLandingContextFromRequest = (req: any): ProductLandingContextT => {
|
||||||
|
const { currentCategory, currentPath, data } = req.context
|
||||||
|
return {
|
||||||
|
...pick(req.context.page, [
|
||||||
|
'title',
|
||||||
|
'shortTitle',
|
||||||
|
'introPlainText',
|
||||||
|
'beta_product',
|
||||||
|
'intro',
|
||||||
|
'product_video',
|
||||||
|
'changelog',
|
||||||
|
]),
|
||||||
|
whatsNewChangelog: req.context.whatsNewChangelog,
|
||||||
|
changelogUrl: req.context.changelogUrl,
|
||||||
|
|
||||||
|
productCodeExamples: req.context.productCodeExamples || [],
|
||||||
|
|
||||||
|
productCommunityExamples: req.context.productCommunityExamples || [],
|
||||||
|
|
||||||
|
productUserExamples: (req.context.productUserExamples || []).map(
|
||||||
|
({ user, description }: any) => ({
|
||||||
|
username: user,
|
||||||
|
description,
|
||||||
|
})
|
||||||
|
),
|
||||||
|
|
||||||
|
introLinks: Object.fromEntries(
|
||||||
|
Object.entries(req.context.page.introLinks || {}).filter(([key, val]) => !!val)
|
||||||
|
),
|
||||||
|
|
||||||
|
guideCards: (req.context.featuredLinks.guideCards || []).map((link: any) => {
|
||||||
|
return {
|
||||||
|
href: link.href,
|
||||||
|
title: link.title,
|
||||||
|
intro: link.intro,
|
||||||
|
authors: link.page.authors || [],
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
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}` : '',
|
||||||
|
articles: links.map((link: any) => {
|
||||||
|
return {
|
||||||
|
hideIntro: key === 'popular',
|
||||||
|
href: link.href,
|
||||||
|
title: link.title,
|
||||||
|
intro: link.intro,
|
||||||
|
authors: link.page.authors || [],
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,13 +3,39 @@ import get from 'lodash/get'
|
|||||||
|
|
||||||
// The idea of this component is to mimic a popular i18n library (i18next)
|
// The idea of this component is to mimic a popular i18n library (i18next)
|
||||||
// so that we can set ourselves up to transition to it (or a similar library) in the future
|
// so that we can set ourselves up to transition to it (or a similar library) in the future
|
||||||
export const useTranslation = (translationGroup: string) => {
|
export const useTranslation = (namespaces: string | Array<string>) => {
|
||||||
const { data } = useMainContext()
|
const { data } = useMainContext()
|
||||||
|
|
||||||
|
// this can eventually be an object constructed from the input namespaces param above, but for now everything is already loaded
|
||||||
|
const loadedData: any = data.ui
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
// The compiled string supports prefixing with a namespace such as `my-namespace:path.to.value`
|
||||||
t: (strings: TemplateStringsArray | string, ...values: Array<any>) => {
|
t: (strings: TemplateStringsArray | string, ...values: Array<any>) => {
|
||||||
const key = typeof strings === 'string' ? strings : String.raw(strings, ...values)
|
const key = typeof strings === 'string' ? strings : String.raw(strings, ...values)
|
||||||
return get((data.ui as any)[translationGroup], key)
|
|
||||||
|
const splitKey = key.split(':')
|
||||||
|
if (splitKey.length > 2) {
|
||||||
|
throw new Error('Multiple ":" not allowed in translation lookup path')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (splitKey.length === 2) {
|
||||||
|
const [namespace, path] = splitKey
|
||||||
|
return get(loadedData[namespace], path)
|
||||||
|
}
|
||||||
|
|
||||||
|
const [path] = splitKey
|
||||||
|
if (Array.isArray(namespaces)) {
|
||||||
|
for (const namespace of namespaces) {
|
||||||
|
const val = get(loadedData[namespace], path)
|
||||||
|
if (val !== undefined) {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
} else {
|
||||||
|
return get(loadedData[namespaces], path)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
35
components/landing/CodeExampleCard.tsx
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { RepoIcon } from '@primer/octicons-react'
|
||||||
|
import { CodeExample } from 'components/context/ProductLandingContext'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
example: CodeExample
|
||||||
|
}
|
||||||
|
export const CodeExampleCard = ({ example }: Props) => {
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
className="Box d-flex flex-column flex-justify-between height-full color-shadow-medium hover-shadow-large no-underline color-text-primary"
|
||||||
|
href={`https://github.com/${example.href}`}
|
||||||
|
>
|
||||||
|
<div className="p-4">
|
||||||
|
<h4>{example.title}</h4>
|
||||||
|
<p className="mt-2 mb-4 color-text-tertiary">{example.description}</p>
|
||||||
|
<div className="d-flex flex-wrap">
|
||||||
|
{example.tags.map((tag) => {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
key={tag}
|
||||||
|
className="IssueLabel color-text-inverse color-bg-info-inverse mr-2 mb-1"
|
||||||
|
>
|
||||||
|
{tag}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<footer className="border-top p-4 color-text-secondary d-flex flex-items-center">
|
||||||
|
<RepoIcon className="flex-shrink-0" />
|
||||||
|
<span className="ml-2 text-mono text-small color-text-link">{example.href}</span>
|
||||||
|
</footer>
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
}
|
||||||
80
components/landing/CodeExamples.tsx
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import { useState } from 'react'
|
||||||
|
import { ArrowRightIcon, SearchIcon } from '@primer/octicons-react'
|
||||||
|
|
||||||
|
import { useProductLandingContext } from 'components/context/ProductLandingContext'
|
||||||
|
import { useTranslation } from 'components/hooks/useTranslation'
|
||||||
|
import { CodeExampleCard } from 'components/landing/CodeExampleCard'
|
||||||
|
|
||||||
|
const PAGE_SIZE = 6
|
||||||
|
export const CodeExamples = () => {
|
||||||
|
const { productCodeExamples } = useProductLandingContext()
|
||||||
|
const { t } = useTranslation('product_landing')
|
||||||
|
const [numVisible, setNumVisible] = useState(PAGE_SIZE)
|
||||||
|
const [search, setSearch] = useState('')
|
||||||
|
|
||||||
|
const onSearchChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||||
|
setSearch(e.target.value)
|
||||||
|
setNumVisible(PAGE_SIZE) // reset the visible count (only matters after searching)
|
||||||
|
}
|
||||||
|
|
||||||
|
const isSearching = !!search
|
||||||
|
let searchResults: typeof productCodeExamples = []
|
||||||
|
if (isSearching) {
|
||||||
|
const matchReg = new RegExp(search, 'i')
|
||||||
|
searchResults = productCodeExamples.filter((example) => {
|
||||||
|
const searchableStr = `${example.tags.join(' ')} ${example.title} ${example.description}`
|
||||||
|
return matchReg.test(searchableStr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="pr-lg-3 mb-5 mt-3">
|
||||||
|
<input
|
||||||
|
className="input-lg py-2 px-3 col-12 col-lg-8 form-control"
|
||||||
|
placeholder={t('search_code_examples')}
|
||||||
|
type="search"
|
||||||
|
autoComplete="off"
|
||||||
|
aria-label={t('search_code_examples')}
|
||||||
|
onChange={onSearchChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="d-flex flex-wrap gutter">
|
||||||
|
{(isSearching ? searchResults : productCodeExamples.slice(0, numVisible)).map((example) => {
|
||||||
|
return (
|
||||||
|
<div key={example.href} className="col-12 col-xl-4 col-lg-6 mb-4">
|
||||||
|
<CodeExampleCard example={example} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{numVisible < productCodeExamples.length && !isSearching && (
|
||||||
|
<button
|
||||||
|
className="btn btn-outline float-right"
|
||||||
|
onClick={() => setNumVisible(numVisible + PAGE_SIZE)}
|
||||||
|
>
|
||||||
|
{t('show_more')} <ArrowRightIcon />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isSearching && searchResults.length === 0 && (
|
||||||
|
<div className="d-none py-4 text-center color-text-secondary font-mktg">
|
||||||
|
<div className="mb-3">
|
||||||
|
<SearchIcon size={24} />{' '}
|
||||||
|
</div>
|
||||||
|
<h3 className="text-normal">
|
||||||
|
{t('sorry')} <strong className="js-filter-card-value"></strong>
|
||||||
|
</h3>
|
||||||
|
<p className="my-3 f4">
|
||||||
|
{t('no_example')} <br /> {t('try_another')}
|
||||||
|
</p>
|
||||||
|
<a href="https://github.com/github/docs/blob/main/data/variables/discussions_community_examples.yml">
|
||||||
|
{t('add_your_community')} <ArrowRightIcon />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
38
components/landing/CommunityExamples.tsx
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { useState } from 'react'
|
||||||
|
import { ArrowRightIcon } from '@primer/octicons-react'
|
||||||
|
|
||||||
|
import { useProductLandingContext } from 'components/context/ProductLandingContext'
|
||||||
|
import { useTranslation } from 'components/hooks/useTranslation'
|
||||||
|
import { RepoCard } from 'components/landing/RepoCard'
|
||||||
|
|
||||||
|
export const CommunityExamples = () => {
|
||||||
|
const { productCommunityExamples } = useProductLandingContext()
|
||||||
|
const { t } = useTranslation('product_landing')
|
||||||
|
const [numVisible, setNumVisible] = useState(6)
|
||||||
|
|
||||||
|
if (!productCommunityExamples) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="d-flex flex-wrap gutter">
|
||||||
|
{productCommunityExamples.slice(0, numVisible).map((repo, i) => {
|
||||||
|
return (
|
||||||
|
<div key={repo.repo} className="col-12 col-xl-4 col-lg-6 mb-4">
|
||||||
|
<RepoCard repo={repo} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
{numVisible < productCommunityExamples.length && (
|
||||||
|
<button
|
||||||
|
className="btn btn-outline float-right"
|
||||||
|
onClick={() => setNumVisible(productCommunityExamples.length)}
|
||||||
|
>
|
||||||
|
{t('show_more')} <ArrowRightIcon />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
109
components/landing/FeaturedArticles.tsx
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
import cx from 'classnames'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import { format } from 'date-fns'
|
||||||
|
|
||||||
|
import { ArrowRightIcon } from '@primer/octicons-react'
|
||||||
|
import { FeaturedLink, useProductLandingContext } from 'components/context/ProductLandingContext'
|
||||||
|
import { useTranslation } from 'components/hooks/useTranslation'
|
||||||
|
import { TruncateLines } from 'components/TruncateLines'
|
||||||
|
|
||||||
|
export const FeaturedArticles = () => {
|
||||||
|
const {
|
||||||
|
featuredArticles = [],
|
||||||
|
changelog,
|
||||||
|
whatsNewChangelog,
|
||||||
|
changelogUrl,
|
||||||
|
} = useProductLandingContext()
|
||||||
|
const { t } = useTranslation('toc')
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="d-lg-flex gutter my-6 py-6">
|
||||||
|
{featuredArticles.map((section, i) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={section.label}
|
||||||
|
className={cx('col-12 mb-4 mb-lg-0', changelog ? 'col-lg-4' : 'col-lg-6')}
|
||||||
|
>
|
||||||
|
<ArticleList
|
||||||
|
title={section.label}
|
||||||
|
viewAllHref={section.viewAllHref}
|
||||||
|
articles={section.articles}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
|
||||||
|
{changelog && (
|
||||||
|
<div className={cx('col-12 mb-4 mb-lg-0', changelog ? 'col-lg-4' : 'col-lg-6')}>
|
||||||
|
<ArticleList
|
||||||
|
title={t('whats_new')}
|
||||||
|
viewAllHref={changelogUrl}
|
||||||
|
articles={(whatsNewChangelog || []).map((link) => {
|
||||||
|
return {
|
||||||
|
title: link.title,
|
||||||
|
date: link.date,
|
||||||
|
href: link.href,
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ArticleListProps = {
|
||||||
|
title: string
|
||||||
|
viewAllHref?: string
|
||||||
|
articles: Array<FeaturedLink>
|
||||||
|
}
|
||||||
|
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>
|
||||||
|
{viewAllHref && (
|
||||||
|
<Link href={viewAllHref}>
|
||||||
|
<a className="ml-4">
|
||||||
|
View all <ArrowRightIcon size={14} className="v-align-middle" />
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul className="list-style-none">
|
||||||
|
{articles.map((link) => {
|
||||||
|
return (
|
||||||
|
<li key={link.href} className="border-top">
|
||||||
|
<Link href={link.href}>
|
||||||
|
<a className="link-with-intro Bump-link--hover no-underline d-block py-3">
|
||||||
|
<h4 className="link-with-intro-title">
|
||||||
|
{link.title}
|
||||||
|
<span className="Bump-link-symbol">→</span>
|
||||||
|
</h4>
|
||||||
|
{!link.hideIntro && link.intro && (
|
||||||
|
<TruncateLines
|
||||||
|
as="p"
|
||||||
|
maxLines={2}
|
||||||
|
className="link-with-intro-intro color-text-secondary mb-0 mt-1"
|
||||||
|
>
|
||||||
|
{link.intro}
|
||||||
|
</TruncateLines>
|
||||||
|
)}
|
||||||
|
{link.date && (
|
||||||
|
<time
|
||||||
|
className="tooltipped tooltipped-n color-text-tertiary text-mono mt-1"
|
||||||
|
aria-label={format(new Date(link.date), 'PPP')}
|
||||||
|
>
|
||||||
|
{format(new Date(link.date), 'MMMM dd')}
|
||||||
|
</time>
|
||||||
|
)}
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
52
components/landing/GuideCard.tsx
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import type { FeaturedLink } from 'components/context/ProductLandingContext'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
guide: FeaturedLink
|
||||||
|
}
|
||||||
|
export const GuideCard = ({ guide }: Props) => {
|
||||||
|
const authors = guide.authors && guide.authors.length > 0 ? guide.authors : ['GitHub']
|
||||||
|
const authorString = `@${authors.join(', @')}`
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="col-lg-4 col-12 mb-3">
|
||||||
|
<a
|
||||||
|
className="Box color-shadow-medium height-full d-block hover-shadow-large no-underline color-text-primary p-5"
|
||||||
|
href={guide.href}
|
||||||
|
>
|
||||||
|
<h2>{guide.title}</h2>
|
||||||
|
<p className="mt-2 mb-4 color-text-tertiary">{guide.intro}</p>
|
||||||
|
|
||||||
|
<footer className="d-flex">
|
||||||
|
<div className="mr-1">
|
||||||
|
{authors.length === 1 ? (
|
||||||
|
<img
|
||||||
|
className="avatar avatar-2 circle mr-1"
|
||||||
|
src={`https://github.com/${authors[0]}.png`}
|
||||||
|
alt={`@${authors[0]}`}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="AvatarStack AvatarStack--three-plus">
|
||||||
|
<div
|
||||||
|
className="AvatarStack-body tooltipped tooltipped-se tooltipped-align-left-1"
|
||||||
|
aria-label={authorString}
|
||||||
|
>
|
||||||
|
{authors.map((author) => {
|
||||||
|
return (
|
||||||
|
<img
|
||||||
|
className="avatar circle"
|
||||||
|
alt={`@${author}`}
|
||||||
|
src={`https://github.com/${author}.png`}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>{authorString}</div>
|
||||||
|
</footer>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
36
components/landing/GuideCards.tsx
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
|
import { ArrowRightIcon } from '@primer/octicons-react'
|
||||||
|
import { useMainContext } from 'components/context/MainContext'
|
||||||
|
|
||||||
|
import { useProductLandingContext } from 'components/context/ProductLandingContext'
|
||||||
|
import { GuideCard } from 'components/landing/GuideCard'
|
||||||
|
|
||||||
|
export const GuideCards = () => {
|
||||||
|
const router = useRouter()
|
||||||
|
const { currentCategory } = useMainContext()
|
||||||
|
const { guideCards } = useProductLandingContext()
|
||||||
|
|
||||||
|
if (!guideCards) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="d-lg-flex gutter-lg flex-items-stretch">
|
||||||
|
{(guideCards || []).map((guide) => {
|
||||||
|
return <GuideCard key={guide.href} guide={guide} />
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{!currentCategory && (
|
||||||
|
<Link href={`${router.asPath}/guides`}>
|
||||||
|
<a className="btn btn-outline float-right">
|
||||||
|
Explore guides <ArrowRightIcon />
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
107
components/landing/LandingHero.tsx
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import cx from 'classnames'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
import { useMainContext } from 'components/context/MainContext'
|
||||||
|
|
||||||
|
import { useProductLandingContext } from 'components/context/ProductLandingContext'
|
||||||
|
import { useTranslation } from 'components/hooks/useTranslation'
|
||||||
|
import { useVersion } from 'components/hooks/useVersion'
|
||||||
|
|
||||||
|
export const LandingHero = () => {
|
||||||
|
const { airGap } = useMainContext()
|
||||||
|
const { product_video, shortTitle, beta_product, intro, introLinks } = useProductLandingContext()
|
||||||
|
const { t } = useTranslation('product_landing')
|
||||||
|
|
||||||
|
return (
|
||||||
|
<header className="d-lg-flex gutter-lg mb-6">
|
||||||
|
<div className={cx(product_video && 'col-12 col-lg-6 mb-3 mb-lg-0')}>
|
||||||
|
<span className="text-mono color-text-secondary">Product</span>
|
||||||
|
<h1 className="mb-3 font-mktg">
|
||||||
|
{shortTitle}{' '}
|
||||||
|
{beta_product && <span className="Label Label--success v-align-middle">Beta</span>}
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className="lead-mktg color-text-secondary"
|
||||||
|
dangerouslySetInnerHTML={{ __html: intro }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* idea to abstract the introLinks into something more component-like */}
|
||||||
|
{/* {introLinks.map((link) => {
|
||||||
|
return (
|
||||||
|
<FullLink
|
||||||
|
href={link.href}
|
||||||
|
className={cx(
|
||||||
|
'btn-mktg btn-large f4 mt-3 mr-3',
|
||||||
|
link.secondary && 'btn-outline-mktg'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{t(link.translationKeyLabel)}
|
||||||
|
</FullLink>
|
||||||
|
)
|
||||||
|
})} */}
|
||||||
|
|
||||||
|
{introLinks?.quickstart && (
|
||||||
|
<FullLink href={introLinks.quickstart} className="btn-mktg btn-large f4 mt-3 mr-3">
|
||||||
|
{t('quickstart')}
|
||||||
|
</FullLink>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{introLinks?.reference && (
|
||||||
|
<FullLink
|
||||||
|
href={introLinks.reference}
|
||||||
|
className="btn-mktg btn-outline-mktg btn-large f4 mt-3 mr-3"
|
||||||
|
>
|
||||||
|
{t('reference')}
|
||||||
|
</FullLink>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{introLinks?.overview && (
|
||||||
|
<FullLink
|
||||||
|
href={introLinks.overview}
|
||||||
|
className="btn-mktg btn-outline-mktg btn-large f4 mt-3 mr-3"
|
||||||
|
>
|
||||||
|
{t('overview')}
|
||||||
|
</FullLink>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{product_video && (
|
||||||
|
<div className="col-12 col-lg-6">
|
||||||
|
<div className="position-relative" style={{ paddingBottom: '56.25%' }}>
|
||||||
|
{!airGap && (
|
||||||
|
<iframe
|
||||||
|
title={`${shortTitle} Video`}
|
||||||
|
className="top-0 left-0 position-absolute color-shadow-large rounded-1 width-full height-full"
|
||||||
|
src={product_video}
|
||||||
|
frameBorder="0"
|
||||||
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||||
|
allowFullScreen
|
||||||
|
></iframe>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</header>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fully Qualified Link - it includes the version and locale in the path
|
||||||
|
type Props = {
|
||||||
|
href: string
|
||||||
|
children: React.ReactNode
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
export const FullLink = ({ href, children, className }: Props) => {
|
||||||
|
const router = useRouter()
|
||||||
|
const { currentVersion } = useVersion()
|
||||||
|
const locale = router.locale || 'en'
|
||||||
|
const fullyQualifiedHref = `/${locale}${
|
||||||
|
currentVersion !== 'free-pro-team@latest' ? `/${currentVersion}` : ''
|
||||||
|
}${href}`
|
||||||
|
return (
|
||||||
|
<Link href={fullyQualifiedHref}>
|
||||||
|
<a className={className}>{children}</a>
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
}
|
||||||
15
components/landing/LandingSection.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import cx from 'classnames'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
title?: string
|
||||||
|
children?: React.ReactNode
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
export const LandingSection = ({ title, children, className }: Props) => {
|
||||||
|
return (
|
||||||
|
<div className={cx('container-xl px-3 px-md-6', className)}>
|
||||||
|
{title && <h2 className="font-mktg h1 mb-2">{title}</h2>}
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
27
components/landing/RepoCard.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
type Props = {
|
||||||
|
repo: {
|
||||||
|
repo: string
|
||||||
|
description: string
|
||||||
|
}
|
||||||
|
href?: string
|
||||||
|
}
|
||||||
|
export const RepoCard = ({ repo, href }: Props) => {
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
className="Box d-flex height-full color-shadow-medium hover-shadow-large no-underline color-text-primary p-4"
|
||||||
|
href={href || `https://github.com/${repo.repo}`}
|
||||||
|
>
|
||||||
|
<div className="flex-shrink-0 mr-3">
|
||||||
|
<img
|
||||||
|
src={`https://github.com/${repo.repo.split('/')[0]}.png`}
|
||||||
|
alt={repo.repo}
|
||||||
|
className="avatar avatar-8"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex-auto">
|
||||||
|
<h4>{repo.repo}</h4>
|
||||||
|
<p className="mt-1 color-text-tertiary">{repo.description}</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
}
|
||||||
34
components/landing/SponsorsExamples.tsx
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import Link from 'next/link'
|
||||||
|
import { ArrowRightIcon } from '@primer/octicons-react'
|
||||||
|
|
||||||
|
import { useProductLandingContext } from 'components/context/ProductLandingContext'
|
||||||
|
import { useTranslation } from 'components/hooks/useTranslation'
|
||||||
|
import { UserCard } from 'components/landing/UserCard'
|
||||||
|
|
||||||
|
export const SponsorsExamples = () => {
|
||||||
|
const { productUserExamples } = useProductLandingContext()
|
||||||
|
const { t } = useTranslation('product_landing')
|
||||||
|
|
||||||
|
if (!productUserExamples) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="d-flex flex-wrap gutter">
|
||||||
|
{productUserExamples.slice(0, 6).map((user) => {
|
||||||
|
return (
|
||||||
|
<div key={user.username} className="col-12 col-xl-4 col-lg-6 mb-4">
|
||||||
|
<UserCard href={`https://github.com/sponsors/${user.username}`} user={user} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<Link href={`https://github.com/sponsors/community`}>
|
||||||
|
<a className="btn btn-outline float-right">
|
||||||
|
{t('explore_people_and_projects')} <ArrowRightIcon />
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
27
components/landing/UserCard.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
type Props = {
|
||||||
|
user: {
|
||||||
|
username: string
|
||||||
|
description: string
|
||||||
|
}
|
||||||
|
href?: string
|
||||||
|
}
|
||||||
|
export const UserCard = ({ user, href }: Props) => {
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
className="Box d-flex height-full color-shadow-medium hover-shadow-large no-underline color-text-primary p-4"
|
||||||
|
href={href || `https://github.com/${user.username}`}
|
||||||
|
>
|
||||||
|
<div className="flex-shrink-0 mr-3">
|
||||||
|
<img
|
||||||
|
src={`https://github.com/${user.username}.png`}
|
||||||
|
alt={user.username}
|
||||||
|
className="avatar avatar-8 circle"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex-auto">
|
||||||
|
<h4>{user.username}</h4>
|
||||||
|
<p className="mt-1 color-text-tertiary">{user.description}</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
}
|
||||||
3
components/lib/ExcludesNull.tsx
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export function ExcludesNull<T>(x: T | null): x is T {
|
||||||
|
return x !== null
|
||||||
|
}
|
||||||
@@ -121,13 +121,13 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: A job to say hello
|
name: A job to say hello
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- id: foo
|
- id: foo
|
||||||
uses: actions/hello-world-composite-run-steps-action@v1
|
uses: actions/hello-world-composite-run-steps-action@v1
|
||||||
with:
|
with:
|
||||||
who-to-greet: 'Mona the Octocat'
|
who-to-greet: 'Mona the Octocat'
|
||||||
- run: echo random-number ${{ steps.foo.outputs.random-number }}
|
- run: echo random-number ${{ steps.foo.outputs.random-number }}
|
||||||
shell: bash
|
shell: bash
|
||||||
```
|
```
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
|
||||||
|
|||||||
@@ -195,14 +195,14 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: A job to say hello
|
name: A job to say hello
|
||||||
steps:
|
steps:
|
||||||
- name: Hello world action step
|
- name: Hello world action step
|
||||||
id: hello
|
id: hello
|
||||||
uses: actions/hello-world-docker-action@v1
|
uses: actions/hello-world-docker-action@v1
|
||||||
with:
|
with:
|
||||||
who-to-greet: 'Mona the Octocat'
|
who-to-greet: 'Mona the Octocat'
|
||||||
# Use the output from the `hello` step
|
# Use the output from the `hello` step
|
||||||
- name: Get the output time
|
- name: Get the output time
|
||||||
run: echo "The time was ${{ steps.hello.outputs.time }}"
|
run: echo "The time was ${{ steps.hello.outputs.time }}"
|
||||||
```
|
```
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
|
||||||
|
|||||||
@@ -227,14 +227,14 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: A job to say hello
|
name: A job to say hello
|
||||||
steps:
|
steps:
|
||||||
- name: Hello world action step
|
- name: Hello world action step
|
||||||
id: hello
|
id: hello
|
||||||
uses: actions/hello-world-javascript-action@v1.1
|
uses: actions/hello-world-javascript-action@v1.1
|
||||||
with:
|
with:
|
||||||
who-to-greet: 'Mona the Octocat'
|
who-to-greet: 'Mona the Octocat'
|
||||||
# Use the output from the `hello` step
|
# Use the output from the `hello` step
|
||||||
- name: Get the output time
|
- name: Get the output time
|
||||||
run: echo "The time was ${{ steps.hello.outputs.time }}"
|
run: echo "The time was ${{ steps.hello.outputs.time }}"
|
||||||
```
|
```
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ In this example, `cleanup.js` only runs on Linux-based runners:
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
pre: 'cleanup.js'
|
pre: 'cleanup.js'
|
||||||
pre-if: 'runner.os == linux'
|
pre-if: runner.os == 'linux'
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `post`
|
#### `post`
|
||||||
@@ -198,7 +198,7 @@ For example, this `cleanup.js` will only run on Linux-based runners:
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
post: 'cleanup.js'
|
post: 'cleanup.js'
|
||||||
post-if: 'runner.os == linux'
|
post-if: runner.os == 'linux'
|
||||||
```
|
```
|
||||||
|
|
||||||
### `runs` for composite run steps actions
|
### `runs` for composite run steps actions
|
||||||
@@ -296,7 +296,7 @@ runs:
|
|||||||
using: 'docker'
|
using: 'docker'
|
||||||
image: 'Dockerfile'
|
image: 'Dockerfile'
|
||||||
args:
|
args:
|
||||||
- 'bzz'
|
- 'bzz'
|
||||||
pre-entrypoint: 'setup.sh'
|
pre-entrypoint: 'setup.sh'
|
||||||
entrypoint: 'main.sh'
|
entrypoint: 'main.sh'
|
||||||
```
|
```
|
||||||
@@ -324,7 +324,7 @@ runs:
|
|||||||
using: 'docker'
|
using: 'docker'
|
||||||
image: 'Dockerfile'
|
image: 'Dockerfile'
|
||||||
args:
|
args:
|
||||||
- 'bzz'
|
- 'bzz'
|
||||||
entrypoint: 'main.sh'
|
entrypoint: 'main.sh'
|
||||||
post-entrypoint: 'cleanup.sh'
|
post-entrypoint: 'cleanup.sh'
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -47,17 +47,17 @@ jobs:
|
|||||||
dotnet-version: ['3.0', '3.1.x', '5.0.x' ]
|
dotnet-version: ['3.0', '3.1.x', '5.0.x' ]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Setup .NET Core SDK ${{ matrix.dotnet-version }}
|
- name: Setup .NET Core SDK ${{ matrix.dotnet-version }}
|
||||||
uses: actions/setup-dotnet@v1.7.2
|
uses: actions/setup-dotnet@v1.7.2
|
||||||
with:
|
with:
|
||||||
dotnet-version: ${{ matrix.dotnet-version }}
|
dotnet-version: ${{ matrix.dotnet-version }}
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: dotnet restore
|
run: dotnet restore
|
||||||
- name: Build
|
- name: Build
|
||||||
run: dotnet build --configuration Release --no-restore
|
run: dotnet build --configuration Release --no-restore
|
||||||
- name: Test
|
- name: Test
|
||||||
run: dotnet test --no-restore --verbosity normal
|
run: dotnet test --no-restore --verbosity normal
|
||||||
```
|
```
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
|
||||||
@@ -84,14 +84,14 @@ jobs:
|
|||||||
dotnet: [ '3.0', '3.1.x', '5.0.x' ]
|
dotnet: [ '3.0', '3.1.x', '5.0.x' ]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Setup dotnet ${{ matrix.dotnet-version }}
|
- name: Setup dotnet ${{ matrix.dotnet-version }}
|
||||||
uses: actions/setup-dotnet@v1
|
uses: actions/setup-dotnet@v1
|
||||||
with:
|
with:
|
||||||
dotnet-version: ${{ matrix.dotnet-version }}
|
dotnet-version: ${{ matrix.dotnet-version }}
|
||||||
# You can test your matrix by printing the current dotnet version
|
# You can test your matrix by printing the current dotnet version
|
||||||
- name: Display dotnet version
|
- name: Display dotnet version
|
||||||
run: dotnet --version
|
run: dotnet --version
|
||||||
```
|
```
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
|
||||||
@@ -204,22 +204,22 @@ jobs:
|
|||||||
dotnet-version: [ '3.0', '3.1.x', '5.0.x' ]
|
dotnet-version: [ '3.0', '3.1.x', '5.0.x' ]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Setup dotnet
|
- name: Setup dotnet
|
||||||
uses: actions/setup-dotnet@v1
|
uses: actions/setup-dotnet@v1
|
||||||
with:
|
with:
|
||||||
dotnet-version: ${{ matrix.dotnet-version }}
|
dotnet-version: ${{ matrix.dotnet-version }}
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: dotnet restore
|
run: dotnet restore
|
||||||
- name: Test with dotnet
|
- name: Test with dotnet
|
||||||
run: dotnet test --logger trx --results-directory "TestResults-${{ matrix.dotnet-version }}"
|
run: dotnet test --logger trx --results-directory "TestResults-${{ matrix.dotnet-version }}"
|
||||||
- name: Upload dotnet test results
|
- name: Upload dotnet test results
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: dotnet-results-${{ matrix.dotnet-version }}
|
name: dotnet-results-${{ matrix.dotnet-version }}
|
||||||
path: TestResults-${{ matrix.dotnet-version }}
|
path: TestResults-${{ matrix.dotnet-version }}
|
||||||
# Use always() to always run this step to publish test results when there are test failures
|
# Use always() to always run this step to publish test results when there are test failures
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
```
|
```
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
|
||||||
@@ -241,16 +241,16 @@ jobs:
|
|||||||
packages: write
|
packages: write
|
||||||
contents: read{% endif %}
|
contents: read{% endif %}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-dotnet@v1
|
- uses: actions/setup-dotnet@v1
|
||||||
with:
|
with:
|
||||||
dotnet-version: '3.1.x' # SDK Version to use.
|
dotnet-version: '3.1.x' # SDK Version to use.
|
||||||
source-url: https://nuget.pkg.github.com/<owner>/index.json
|
source-url: https://nuget.pkg.github.com/<owner>/index.json
|
||||||
env:
|
env:
|
||||||
NUGET_AUTH_TOKEN: {% raw %}${{secrets.GITHUB_TOKEN}}{% endraw %}
|
NUGET_AUTH_TOKEN: {% raw %}${{secrets.GITHUB_TOKEN}}{% endraw %}
|
||||||
- run: dotnet build --configuration Release <my project>
|
- run: dotnet build --configuration Release <my project>
|
||||||
- name: Create the package
|
- name: Create the package
|
||||||
run: dotnet pack --configuration Release <my project>
|
run: dotnet pack --configuration Release <my project>
|
||||||
- name: Publish the package to GPR
|
- name: Publish the package to GPR
|
||||||
run: dotnet nuget push <my project>/bin/Release/*.nupkg
|
run: dotnet nuget push <my project>/bin/Release/*.nupkg
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -59,14 +59,14 @@ jobs:
|
|||||||
node-version: [10.x, 12.x, 14.x, 15.x]
|
node-version: [10.x, 12.x, 14.x, 15.x]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm run build --if-present
|
- run: npm run build --if-present
|
||||||
- run: npm test
|
- run: npm test
|
||||||
```
|
```
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
|
||||||
@@ -119,14 +119,14 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Use Node.js
|
- name: Use Node.js
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: '12.x'
|
node-version: '12.x'
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm run build --if-present
|
- run: npm run build --if-present
|
||||||
- run: npm test
|
- run: npm test
|
||||||
```
|
```
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
|
||||||
|
|||||||
@@ -54,15 +54,15 @@ jobs:
|
|||||||
name: Pester test
|
name: Pester test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out repository code
|
- name: Check out repository code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Perform a Pester test from the command-line
|
- name: Perform a Pester test from the command-line
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: Test-Path resultsfile.log | Should -Be $true
|
run: Test-Path resultsfile.log | Should -Be $true
|
||||||
- name: Perform a Pester test from the Tests.ps1 file
|
- name: Perform a Pester test from the Tests.ps1 file
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
Invoke-Pester Unit.Tests.ps1 -Passthru
|
Invoke-Pester Unit.Tests.ps1 -Passthru
|
||||||
```
|
```
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
|
||||||
@@ -116,12 +116,12 @@ jobs:
|
|||||||
name: Install dependencies
|
name: Install dependencies
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Install from PSGallery
|
- name: Install from PSGallery
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
Set-PSRepository PSGallery -InstallationPolicy Trusted
|
Set-PSRepository PSGallery -InstallationPolicy Trusted
|
||||||
Install-Module SqlServer, PSScriptAnalyzer
|
Install-Module SqlServer, PSScriptAnalyzer
|
||||||
```
|
```
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
|
||||||
@@ -170,23 +170,23 @@ The following example installs `PSScriptAnalyzer` and uses it to lint all `ps1`
|
|||||||
name: Install and run PSScriptAnalyzer
|
name: Install and run PSScriptAnalyzer
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Install PSScriptAnalyzer module
|
- name: Install PSScriptAnalyzer module
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
Set-PSRepository PSGallery -InstallationPolicy Trusted
|
Set-PSRepository PSGallery -InstallationPolicy Trusted
|
||||||
Install-Module PSScriptAnalyzer -ErrorAction Stop
|
Install-Module PSScriptAnalyzer -ErrorAction Stop
|
||||||
- name: Lint with PSScriptAnalyzer
|
- name: Lint with PSScriptAnalyzer
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
Invoke-ScriptAnalyzer -Path *.ps1 -Recurse -Outvariable issues
|
Invoke-ScriptAnalyzer -Path *.ps1 -Recurse -Outvariable issues
|
||||||
$errors = $issues.Where({$_.Severity -eq 'Error'})
|
$errors = $issues.Where({$_.Severity -eq 'Error'})
|
||||||
$warnings = $issues.Where({$_.Severity -eq 'Warning'})
|
$warnings = $issues.Where({$_.Severity -eq 'Warning'})
|
||||||
if ($errors) {
|
if ($errors) {
|
||||||
Write-Error "There were $($errors.Count) errors and $($warnings.Count) warnings total." -ErrorAction Stop
|
Write-Error "There were $($errors.Count) errors and $($warnings.Count) warnings total." -ErrorAction Stop
|
||||||
} else {
|
} else {
|
||||||
Write-Output "There were $($errors.Count) errors and $($warnings.Count) warnings total."
|
Write-Output "There were $($errors.Count) errors and $($warnings.Count) warnings total."
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
|
||||||
@@ -207,15 +207,15 @@ jobs:
|
|||||||
name: Run Pester and upload results
|
name: Run Pester and upload results
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Test with Pester
|
- name: Test with Pester
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: Invoke-Pester Unit.Tests.ps1 -Passthru | Export-CliXml -Path Unit.Tests.xml
|
run: Invoke-Pester Unit.Tests.ps1 -Passthru | Export-CliXml -Path Unit.Tests.xml
|
||||||
- name: Upload test results
|
- name: Upload test results
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: ubuntu-Unit-Tests
|
name: ubuntu-Unit-Tests
|
||||||
path: Unit.Tests.xml
|
path: Unit.Tests.xml
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
```
|
```
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
@@ -240,13 +240,13 @@ jobs:
|
|||||||
publish-to-gallery:
|
publish-to-gallery:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Build and publish
|
- name: Build and publish
|
||||||
env:
|
env:
|
||||||
NUGET_KEY: ${{ secrets.NUGET_KEY }}
|
NUGET_KEY: ${{ secrets.NUGET_KEY }}
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
./build.ps1 -Path /tmp/samplemodule
|
./build.ps1 -Path /tmp/samplemodule
|
||||||
Publish-Module -Path /tmp/samplemodule -NuGetApiKey $env:NUGET_KEY -Verbose
|
Publish-Module -Path /tmp/samplemodule -NuGetApiKey $env:NUGET_KEY -Verbose
|
||||||
```
|
```
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
|||||||
@@ -58,25 +58,25 @@ jobs:
|
|||||||
python-version: [2.7, 3.5, 3.6, 3.7, 3.8]
|
python-version: [2.7, 3.5, 3.6, 3.7, 3.8]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install flake8 pytest
|
pip install flake8 pytest
|
||||||
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
||||||
- name: Lint with flake8
|
- name: Lint with flake8
|
||||||
run: |
|
run: |
|
||||||
# stop the build if there are Python syntax errors or undefined names
|
# stop the build if there are Python syntax errors or undefined names
|
||||||
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
|
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
|
||||||
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
|
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
|
||||||
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: |
|
run: |
|
||||||
pytest
|
pytest
|
||||||
```
|
```
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
|
||||||
@@ -117,14 +117,14 @@ jobs:
|
|||||||
python-version: [2.7, 3.5, 3.6, 3.7, 3.8]
|
python-version: [2.7, 3.5, 3.6, 3.7, 3.8]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
# You can test your matrix by printing the current Python version
|
# You can test your matrix by printing the current Python version
|
||||||
- name: Display Python version
|
- name: Display Python version
|
||||||
run: python -c "import sys; print(sys.version)"
|
run: python -c "import sys; print(sys.version)"
|
||||||
```
|
```
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
|
||||||
@@ -144,17 +144,17 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Set up Python 3.x
|
- name: Set up Python 3.x
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
# Semantic version range syntax or exact version of a Python version
|
# Semantic version range syntax or exact version of a Python version
|
||||||
python-version: '3.x'
|
python-version: '3.x'
|
||||||
# Optional - x64 or x86 architecture, defaults to x64
|
# Optional - x64 or x86 architecture, defaults to x64
|
||||||
architecture: 'x64'
|
architecture: 'x64'
|
||||||
# You can test your matrix by printing the current Python version
|
# You can test your matrix by printing the current Python version
|
||||||
- name: Display Python version
|
- name: Display Python version
|
||||||
run: python -c "import sys; print(sys.version)"
|
run: python -c "import sys; print(sys.version)"
|
||||||
```
|
```
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
|
||||||
@@ -373,25 +373,25 @@ jobs:
|
|||||||
python-version: [2.7, 3.5, 3.6, 3.7, 3.8]
|
python-version: [2.7, 3.5, 3.6, 3.7, 3.8]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Setup Python # Set Python version
|
- name: Setup Python # Set Python version
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
# Install pip and pytest
|
# Install pip and pytest
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install pytest
|
pip install pytest
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: pytest tests.py --doctest-modules --junitxml=junit/test-results-${{ matrix.python-version }}.xml
|
run: pytest tests.py --doctest-modules --junitxml=junit/test-results-${{ matrix.python-version }}.xml
|
||||||
- name: Upload pytest test results
|
- name: Upload pytest test results
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: pytest-results-${{ matrix.python-version }}
|
name: pytest-results-${{ matrix.python-version }}
|
||||||
path: junit/test-results-${{ matrix.python-version }}.xml
|
path: junit/test-results-${{ matrix.python-version }}.xml
|
||||||
# Use always() to always run this step to publish test results when there are test failures
|
# Use always() to always run this step to publish test results when there are test failures
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
```
|
```
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
|
||||||
@@ -413,22 +413,22 @@ jobs:
|
|||||||
deploy:
|
deploy:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
python-version: '3.x'
|
python-version: '3.x'
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install setuptools wheel twine
|
pip install setuptools wheel twine
|
||||||
- name: Build and publish
|
- name: Build and publish
|
||||||
env:
|
env:
|
||||||
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
|
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
|
||||||
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
|
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
|
||||||
run: |
|
run: |
|
||||||
python setup.py sdist bdist_wheel
|
python setup.py sdist bdist_wheel
|
||||||
twine upload dist/*
|
twine upload dist/*
|
||||||
```
|
```
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
|
||||||
|
|||||||
@@ -49,15 +49,15 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Set up Ruby
|
- name: Set up Ruby
|
||||||
uses: ruby/setup-ruby@v1
|
uses: ruby/setup-ruby@v1
|
||||||
with:
|
with:
|
||||||
ruby-version: 2.6
|
ruby-version: 2.6
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: bundle install
|
run: bundle install
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: bundle exec rake
|
run: bundle exec rake
|
||||||
```
|
```
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
|
||||||
@@ -119,15 +119,15 @@ jobs:
|
|||||||
ruby-version: [2.7.x, 2.6.x, 2.5.x]
|
ruby-version: [2.7.x, 2.6.x, 2.5.x]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Set up Ruby ${{ matrix.ruby-version }}
|
- name: Set up Ruby ${{ matrix.ruby-version }}
|
||||||
uses: ruby/setup-ruby@v1
|
uses: ruby/setup-ruby@v1
|
||||||
with:
|
with:
|
||||||
ruby-version: ${{ matrix.ruby-version }}
|
ruby-version: ${{ matrix.ruby-version }}
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: bundle install
|
run: bundle install
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: bundle exec rake
|
run: bundle exec rake
|
||||||
```
|
```
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
|
||||||
@@ -225,12 +225,12 @@ jobs:
|
|||||||
ruby: [2.5, 2.6, 2.7, head, debug, jruby, jruby-head, truffleruby, truffleruby-head]
|
ruby: [2.5, 2.6, 2.7, head, debug, jruby, jruby-head, truffleruby, truffleruby-head]
|
||||||
continue-on-error: ${{ endsWith(matrix.ruby, 'head') || matrix.ruby == 'debug' }}
|
continue-on-error: ${{ endsWith(matrix.ruby, 'head') || matrix.ruby == 'debug' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: ruby/setup-ruby@v1
|
- uses: ruby/setup-ruby@v1
|
||||||
with:
|
with:
|
||||||
ruby-version: ${{ matrix.ruby }}
|
ruby-version: ${{ matrix.ruby }}
|
||||||
- run: bundle install
|
- run: bundle install
|
||||||
- run: bundle exec rake
|
- run: bundle exec rake
|
||||||
```
|
```
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
|
||||||
@@ -248,13 +248,13 @@ jobs:
|
|||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: ruby/setup-ruby@v1
|
- uses: ruby/setup-ruby@v1
|
||||||
with:
|
with:
|
||||||
ruby-version: 2.6
|
ruby-version: 2.6
|
||||||
- run: bundle install
|
- run: bundle install
|
||||||
- name: Rubocop
|
- name: Rubocop
|
||||||
run: rubocop
|
run: rubocop
|
||||||
```
|
```
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
|
||||||
@@ -286,33 +286,33 @@ jobs:
|
|||||||
contents: read{% endif %}
|
contents: read{% endif %}
|
||||||
|
|
||||||
steps:{% raw %}
|
steps:{% raw %}
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Set up Ruby 2.6
|
- name: Set up Ruby 2.6
|
||||||
uses: ruby/setup-ruby@v1
|
uses: ruby/setup-ruby@v1
|
||||||
with:
|
with:
|
||||||
ruby-version: 2.6
|
ruby-version: 2.6
|
||||||
- run: bundle install
|
- run: bundle install
|
||||||
|
|
||||||
- name: Publish to GPR
|
- name: Publish to GPR
|
||||||
run: |
|
run: |
|
||||||
mkdir -p $HOME/.gem
|
mkdir -p $HOME/.gem
|
||||||
touch $HOME/.gem/credentials
|
touch $HOME/.gem/credentials
|
||||||
chmod 0600 $HOME/.gem/credentials
|
chmod 0600 $HOME/.gem/credentials
|
||||||
printf -- "---\n:github: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
printf -- "---\n:github: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
||||||
gem build *.gemspec
|
gem build *.gemspec
|
||||||
gem push --KEY github --host https://rubygems.pkg.github.com/${OWNER} *.gem
|
gem push --KEY github --host https://rubygems.pkg.github.com/${OWNER} *.gem
|
||||||
env:
|
env:
|
||||||
GEM_HOST_API_KEY: "Bearer ${{secrets.GITHUB_TOKEN}}"
|
GEM_HOST_API_KEY: "Bearer ${{secrets.GITHUB_TOKEN}}"
|
||||||
OWNER: ${{ github.repository_owner }}
|
OWNER: ${{ github.repository_owner }}
|
||||||
|
|
||||||
- name: Publish to RubyGems
|
- name: Publish to RubyGems
|
||||||
run: |
|
run: |
|
||||||
mkdir -p $HOME/.gem
|
mkdir -p $HOME/.gem
|
||||||
touch $HOME/.gem/credentials
|
touch $HOME/.gem/credentials
|
||||||
chmod 0600 $HOME/.gem/credentials
|
chmod 0600 $HOME/.gem/credentials
|
||||||
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
||||||
gem build *.gemspec
|
gem build *.gemspec
|
||||||
gem push *.gem
|
gem push *.gem
|
||||||
env:
|
env:
|
||||||
GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"{% endraw %}
|
GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"{% endraw %}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -47,11 +47,11 @@ jobs:
|
|||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Build
|
- name: Build
|
||||||
run: swift build
|
run: swift build
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: swift test
|
run: swift test
|
||||||
```
|
```
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
|
||||||
|
|||||||
@@ -55,28 +55,28 @@ jobs:
|
|||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Select default Xamarin bundle to 6_12_6
|
- name: Select default Xamarin bundle to 6_12_6
|
||||||
run: |
|
run: |
|
||||||
XAMARIN_SDK=6_12_6
|
XAMARIN_SDK=6_12_6
|
||||||
$VM_ASSETS/select-xamarin-sdk.sh $XAMARIN_SDK
|
$VM_ASSETS/select-xamarin-sdk.sh $XAMARIN_SDK
|
||||||
|
|
||||||
- name: Set default Xcode 12.3
|
- name: Set default Xcode 12.3
|
||||||
run: |
|
run: |
|
||||||
XCODE_ROOT=/Applications/Xcode_12.3.0.app
|
XCODE_ROOT=/Applications/Xcode_12.3.0.app
|
||||||
echo "MD_APPLE_SDK_ROOT=$XCODE_ROOT" >> $GITHUB_ENV
|
echo "MD_APPLE_SDK_ROOT=$XCODE_ROOT" >> $GITHUB_ENV
|
||||||
sudo xcode-select -s $XCODE_ROOT
|
sudo xcode-select -s $XCODE_ROOT
|
||||||
|
|
||||||
- name: Setup .NET Core SDK 5.0.x
|
- name: Setup .NET Core SDK 5.0.x
|
||||||
uses: actions/setup-dotnet@v1
|
uses: actions/setup-dotnet@v1
|
||||||
with:
|
with:
|
||||||
dotnet-version: '5.0.x'
|
dotnet-version: '5.0.x'
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: nuget restore <sln_file_path>
|
run: nuget restore <sln_file_path>
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: msbuild <csproj_file_path> /p:Configuration=Debug /p:Platform=iPhoneSimulator /t:Rebuild
|
run: msbuild <csproj_file_path> /p:Configuration=Debug /p:Platform=iPhoneSimulator /t:Rebuild
|
||||||
```
|
```
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
|
||||||
@@ -96,22 +96,22 @@ jobs:
|
|||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Select default Xamarin bundle to 6_12_6
|
- name: Select default Xamarin bundle to 6_12_6
|
||||||
run: |
|
run: |
|
||||||
XAMARIN_SDK=6_12_6
|
XAMARIN_SDK=6_12_6
|
||||||
$VM_ASSETS/select-xamarin-sdk.sh $XAMARIN_SDK
|
$VM_ASSETS/select-xamarin-sdk.sh $XAMARIN_SDK
|
||||||
|
|
||||||
- name: Setup .NET Core SDK 5.0.x
|
- name: Setup .NET Core SDK 5.0.x
|
||||||
uses: actions/setup-dotnet@v1
|
uses: actions/setup-dotnet@v1
|
||||||
with:
|
with:
|
||||||
dotnet-version: '5.0.x'
|
dotnet-version: '5.0.x'
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: nuget restore <sln_file_path>
|
run: nuget restore <sln_file_path>
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: msbuild <csproj_file_path> /t:PackageForAndroid /p:Configuration=Debug
|
run: msbuild <csproj_file_path> /t:PackageForAndroid /p:Configuration=Debug
|
||||||
```
|
```
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,16 @@ For more information, see [`actions/cache`](https://github.com/actions/cache).
|
|||||||
|
|
||||||
- `key`: **Required** The key created when saving a cache and the key used to search for a cache. Can be any combination of variables, context values, static strings, and functions. Keys have a maximum length of 512 characters, and keys longer than the maximum length will cause the action to fail.
|
- `key`: **Required** The key created when saving a cache and the key used to search for a cache. Can be any combination of variables, context values, static strings, and functions. Keys have a maximum length of 512 characters, and keys longer than the maximum length will cause the action to fail.
|
||||||
- `path`: **Required** The file path on the runner to cache or restore. The path can be an absolute path or relative to the working directory.
|
- `path`: **Required** The file path on the runner to cache or restore. The path can be an absolute path or relative to the working directory.
|
||||||
- With `v2` of the `cache` action, you can specify a single path, or multiple paths as a list. Paths can be either directories or single files, and glob patterns are supported.
|
- Paths can be either directories or single files, and glob patterns are supported.
|
||||||
|
- With `v2` of the `cache` action, you can specify a single path, or you can add multiple paths on separate lines. For example:
|
||||||
|
```
|
||||||
|
- name: Cache Gradle packages
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.gradle/caches
|
||||||
|
~/.gradle/wrapper
|
||||||
|
```
|
||||||
- With `v1` of the `cache` action, only a single path is supported and it must be a directory. You cannot cache a single file.
|
- With `v1` of the `cache` action, only a single path is supported and it must be a directory. You cannot cache a single file.
|
||||||
- `restore-keys`: **Optional** An ordered list of alternative keys to use for finding the cache if no cache hit occurred for `key`.
|
- `restore-keys`: **Optional** An ordered list of alternative keys to use for finding the cache if no cache hit occurred for `key`.
|
||||||
|
|
||||||
@@ -82,30 +91,29 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Cache node modules
|
- name: Cache node modules
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
env:
|
env:
|
||||||
cache-name: cache-node-modules
|
cache-name: cache-node-modules
|
||||||
with:
|
with:
|
||||||
# npm cache files are stored in `~/.npm` on Linux/macOS
|
# npm cache files are stored in `~/.npm` on Linux/macOS
|
||||||
path: ~/.npm
|
path: ~/.npm
|
||||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
|
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-build-${{ env.cache-name }}-
|
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||||
${{ runner.os }}-build-
|
${{ runner.os }}-build-
|
||||||
${{ runner.os }}-
|
${{ runner.os }}-
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: npm install
|
run: npm install
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: npm build
|
run: npm build
|
||||||
|
|
||||||
- name: Test
|
|
||||||
run: npm test
|
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: npm test
|
||||||
```
|
```
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ topics:
|
|||||||
|
|
||||||
### Introduction
|
### Introduction
|
||||||
|
|
||||||
This guide shows you workflow examples that configure a service container using the Docker Hub `postgres` image. The workflow runs a script to create a PostgreSQL client and populate the client with data. To test that the workflow creates and populates the PostgreSQL client, the script prints the client's data to the console.
|
This guide shows you workflow examples that configure a service container using the Docker Hub `postgres` image. The workflow runs a script that connects to the PostgreSQL service, creates a table, and then populates it with data. To test that the workflow creates and populates the PostgreSQL table, the script prints the data from the table to the console.
|
||||||
|
|
||||||
{% data reusables.github-actions.docker-container-os-support %}
|
{% data reusables.github-actions.docker-container-os-support %}
|
||||||
|
|
||||||
@@ -81,10 +81,10 @@ jobs:
|
|||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
- name: Connect to PostgreSQL
|
- name: Connect to PostgreSQL
|
||||||
# Runs a script that creates a PostgreSQL client, populates
|
# Runs a script that creates a PostgreSQL table, populates
|
||||||
# the client with data, and retrieves data
|
# the table with data, and then retrieves the data.
|
||||||
run: node client.js
|
run: node client.js
|
||||||
# Environment variable used by the `client.js` script to create a new PostgreSQL client.
|
# Environment variables used by the `client.js` script to create a new PostgreSQL table.
|
||||||
env:
|
env:
|
||||||
# The hostname used to communicate with the PostgreSQL service container
|
# The hostname used to communicate with the PostgreSQL service container
|
||||||
POSTGRES_HOST: postgres
|
POSTGRES_HOST: postgres
|
||||||
@@ -141,8 +141,8 @@ steps:
|
|||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
- name: Connect to PostgreSQL
|
- name: Connect to PostgreSQL
|
||||||
# Runs a script that creates a PostgreSQL client, populates
|
# Runs a script that creates a PostgreSQL table, populates
|
||||||
# the client with data, and retrieves data
|
# the table with data, and then retrieves the data.
|
||||||
run: node client.js
|
run: node client.js
|
||||||
# Environment variable used by the `client.js` script to create
|
# Environment variable used by the `client.js` script to create
|
||||||
# a new PostgreSQL client.
|
# a new PostgreSQL client.
|
||||||
@@ -204,11 +204,11 @@ jobs:
|
|||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
- name: Connect to PostgreSQL
|
- name: Connect to PostgreSQL
|
||||||
# Runs a script that creates a PostgreSQL client, populates
|
# Runs a script that creates a PostgreSQL table, populates
|
||||||
# the client with data, and retrieves data
|
# the table with data, and then retrieves the data
|
||||||
run: node client.js
|
run: node client.js
|
||||||
# Environment variable used by the `client.js` script to create
|
# Environment variables used by the `client.js` script to create
|
||||||
# a new PostgreSQL client.
|
# a new PostgreSQL table.
|
||||||
env:
|
env:
|
||||||
# The hostname used to communicate with the PostgreSQL service container
|
# The hostname used to communicate with the PostgreSQL service container
|
||||||
POSTGRES_HOST: localhost
|
POSTGRES_HOST: localhost
|
||||||
@@ -268,11 +268,11 @@ steps:
|
|||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
- name: Connect to PostgreSQL
|
- name: Connect to PostgreSQL
|
||||||
# Runs a script that creates a PostgreSQL client, populates
|
# Runs a script that creates a PostgreSQL table, populates
|
||||||
# the client with data, and retrieves data
|
# the table with data, and then retrieves the data
|
||||||
run: node client.js
|
run: node client.js
|
||||||
# Environment variable used by the `client.js` script to create
|
# Environment variables used by the `client.js` script to create
|
||||||
# a new PostgreSQL client.
|
# a new PostgreSQL table.
|
||||||
env:
|
env:
|
||||||
# The hostname used to communicate with the PostgreSQL service container
|
# The hostname used to communicate with the PostgreSQL service container
|
||||||
POSTGRES_HOST: localhost
|
POSTGRES_HOST: localhost
|
||||||
@@ -286,9 +286,9 @@ steps:
|
|||||||
|
|
||||||
### Testing the PostgreSQL service container
|
### Testing the PostgreSQL service container
|
||||||
|
|
||||||
You can test your workflow using the following script, which creates a PostgreSQL client and adds a new table with some placeholder data. The script then prints the values stored in the PostgreSQL client to the terminal. Your script can use any language you'd like, but this example uses Node.js and the `pg` npm module. For more information, see the [npm pg module](https://www.npmjs.com/package/pg).
|
You can test your workflow using the following script, which connects to the PostgreSQL service and adds a new table with some placeholder data. The script then prints the values stored in the PostgreSQL table to the terminal. Your script can use any language you'd like, but this example uses Node.js and the `pg` npm module. For more information, see the [npm pg module](https://www.npmjs.com/package/pg).
|
||||||
|
|
||||||
You can modify *client.js* to include any PostgreSQL operations needed by your workflow. In this example, the script creates the PostgreSQL client instance, creates a table, adds placeholder data, then retrieves the data.
|
You can modify *client.js* to include any PostgreSQL operations needed by your workflow. In this example, the script connects to the PostgreSQL service, adds a table to the `postgres` database, inserts some placeholder data, and then retrieves the data.
|
||||||
|
|
||||||
{% data reusables.github-actions.service-container-add-script %}
|
{% data reusables.github-actions.service-container-add-script %}
|
||||||
|
|
||||||
@@ -324,11 +324,11 @@ pgclient.query('SELECT * FROM student', (err, res) => {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
The script creates a new PostgreSQL `Client`, which accepts a `host` and `port` parameter. The script uses the `POSTGRES_HOST` and `POSTGRES_PORT` environment variables to set the client's IP address and port. If `host` and `port` are not defined, the default host is `localhost` and the default port is 5432.
|
The script creates a new connection to the PostgreSQL service, and uses the `POSTGRES_HOST` and `POSTGRES_PORT` environment variables to specify the PostgreSQL service IP address and port. If `host` and `port` are not defined, the default host is `localhost` and the default port is 5432.
|
||||||
|
|
||||||
The script creates a table and populates it with placeholder data. To test that the PostgreSQL database contains the data, the script prints the contents of the table to the console log.
|
The script creates a table and populates it with placeholder data. To test that the `postgres` database contains the data, the script prints the contents of the table to the console log.
|
||||||
|
|
||||||
When you run this workflow, you should see the following output in the "Connect to PostgreSQL" step confirming you created the PostgreSQL client and added data:
|
When you run this workflow, you should see the following output in the "Connect to PostgreSQL" step, which confirms that you successfully created the PostgreSQL table and added data:
|
||||||
|
|
||||||
```
|
```
|
||||||
null [ { id: 1,
|
null [ { id: 1,
|
||||||
|
|||||||
@@ -70,16 +70,16 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
# Setup .npmrc file to publish to npm
|
# Setup .npmrc file to publish to npm
|
||||||
- uses: actions/setup-node@v2
|
- uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: '12.x'
|
node-version: '12.x'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
- run: npm install
|
- run: npm install
|
||||||
- run: npm publish
|
- run: npm publish
|
||||||
env:
|
env:
|
||||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
```
|
```
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
|
||||||
@@ -132,18 +132,18 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
packages: write {% endif %}
|
packages: write {% endif %}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
# Setup .npmrc file to publish to GitHub Packages
|
# Setup .npmrc file to publish to GitHub Packages
|
||||||
- uses: actions/setup-node@v2
|
- uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: '12.x'
|
node-version: '12.x'
|
||||||
registry-url: 'https://npm.pkg.github.com'
|
registry-url: 'https://npm.pkg.github.com'
|
||||||
# Defaults to the user or organization that owns the workflow file
|
# Defaults to the user or organization that owns the workflow file
|
||||||
scope: '@octocat'
|
scope: '@octocat'
|
||||||
- run: npm install
|
- run: npm install
|
||||||
- run: npm publish
|
- run: npm publish
|
||||||
env:
|
env:
|
||||||
NODE_AUTH_TOKEN: {% raw %}${{ secrets.GITHUB_TOKEN }}{% endraw %}
|
NODE_AUTH_TOKEN: {% raw %}${{ secrets.GITHUB_TOKEN }}{% endraw %}
|
||||||
```
|
```
|
||||||
|
|
||||||
The `setup-node` action creates an *.npmrc* file on the runner. When you use the `scope` input to the `setup-node` action, the *.npmrc* file includes the scope prefix. By default, the `setup-node` action sets the scope in the *.npmrc* file to the account that contains that workflow file.
|
The `setup-node` action creates an *.npmrc* file on the runner. When you use the `scope` input to the `setup-node` action, the *.npmrc* file includes the scope prefix. By default, the `setup-node` action sets the scope in the *.npmrc* file to the account that contains that workflow file.
|
||||||
@@ -168,18 +168,18 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
# Setup .npmrc file to publish to npm
|
# Setup .npmrc file to publish to npm
|
||||||
- uses: actions/setup-node@v2
|
- uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: '12.x'
|
node-version: '12.x'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
# Defaults to the user or organization that owns the workflow file
|
# Defaults to the user or organization that owns the workflow file
|
||||||
scope: '@octocat'
|
scope: '@octocat'
|
||||||
- run: yarn
|
- run: yarn
|
||||||
- run: yarn publish
|
- run: yarn publish
|
||||||
env:
|
env:
|
||||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
```
|
```
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
|
||||||
@@ -216,25 +216,25 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
packages: write {% endif %}
|
packages: write {% endif %}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
# Setup .npmrc file to publish to npm
|
# Setup .npmrc file to publish to npm
|
||||||
- uses: actions/setup-node@v1
|
- uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: '10.x'
|
node-version: '10.x'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
- run: npm install
|
- run: npm install
|
||||||
# Publish to npm
|
# Publish to npm
|
||||||
- run: npm publish --access public
|
- run: npm publish --access public
|
||||||
env:{% raw %}
|
env:{% raw %}
|
||||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
# Setup .npmrc file to publish to GitHub Packages
|
# Setup .npmrc file to publish to GitHub Packages
|
||||||
- uses: actions/setup-node@v1
|
- uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
registry-url: 'https://npm.pkg.github.com'
|
registry-url: 'https://npm.pkg.github.com'
|
||||||
# Defaults to the user or organization that owns the workflow file
|
# Defaults to the user or organization that owns the workflow file
|
||||||
scope: '@octocat'
|
scope: '@octocat'
|
||||||
# Publish to GitHub Packages
|
# Publish to GitHub Packages
|
||||||
- run: npm publish
|
- run: npm publish
|
||||||
env:
|
env:
|
||||||
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}{% endraw %}
|
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}{% endraw %}
|
||||||
```
|
```
|
||||||
|
|||||||