diff --git a/content/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners.md b/content/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners.md index dc2546a41eaf..77bee700cae8 100644 --- a/content/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners.md +++ b/content/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners.md @@ -117,7 +117,7 @@ The following operating systems are supported for the self-hosted runner applica The following processor architectures are supported for the self-hosted runner application. * `x64` - Linux, macOS, Windows. -* `ARM64` - Linux{% ifversion actions-macos-arm %}, macOS{% endif %}{% ifversion actions-windows-arm %}, Windows (currently in beta){% endif %}. +* `ARM64` - Linux, macOS{% ifversion actions-windows-arm %}, Windows (currently in beta){% endif %}. * `ARM32` - Linux. {% ifversion ghes %} diff --git a/content/actions/security-guides/automatic-token-authentication.md b/content/actions/security-guides/automatic-token-authentication.md index 67141e49bf14..5742eec50553 100644 --- a/content/actions/security-guides/automatic-token-authentication.md +++ b/content/actions/security-guides/automatic-token-authentication.md @@ -88,6 +88,7 @@ The following table shows the permissions granted to the `GITHUB_TOKEN` by defau | checks | read/write | none | read | | contents | read/write | read | read | | deployments | read/write | none | read | +| discussions | read/write | none | read | | {% ifversion fpt or ghec %} | | id-token | none | none | read | | {% endif %} | diff --git a/content/actions/using-workflows/workflow-commands-for-github-actions.md b/content/actions/using-workflows/workflow-commands-for-github-actions.md index 97fd1ebdbe19..72d094242df0 100644 --- a/content/actions/using-workflows/workflow-commands-for-github-actions.md +++ b/content/actions/using-workflows/workflow-commands-for-github-actions.md @@ -813,8 +813,6 @@ This example demonstrates how to set the `SELECTED_COLOR` output parameter and l {% endpowershell %} -{% ifversion actions-job-summaries %} - ## Adding a job summary {% bash %} @@ -955,8 +953,6 @@ After a step has completed, job summaries are uploaded and subsequent steps cann Job summaries are isolated between steps and each step is restricted to a maximum size of 1MiB. Isolation is enforced between steps so that potentially malformed Markdown from a single step cannot break Markdown rendering for subsequent steps. If more than 1MiB of content is added for a step, then the upload for the step will fail and an error annotation will be created. Upload failures for job summaries do not affect the overall status of a step or a job. A maximum of 20 job summaries from steps are displayed per job. -{% endif %} - ## Adding a system path Prepends a directory to the system `PATH` variable and automatically makes it available to all subsequent actions in the current job; the currently running action cannot access the updated path variable. To see the currently defined paths for your job, you can use `echo "$PATH"` in a step or an action. diff --git a/content/issues/planning-and-tracking-with-projects/automating-your-project/adding-items-automatically.md b/content/issues/planning-and-tracking-with-projects/automating-your-project/adding-items-automatically.md index 01c450d37532..31280a1da70a 100644 --- a/content/issues/planning-and-tracking-with-projects/automating-your-project/adding-items-automatically.md +++ b/content/issues/planning-and-tracking-with-projects/automating-your-project/adding-items-automatically.md @@ -2,7 +2,9 @@ title: Adding items automatically intro: 'You can configure your project''s built-in workflows to automatically add items from {% ifversion projects-v2-duplicate-auto-add %}repositories{% else%}a repository{% endif %} that match a filter.' versions: - feature: projects-v2-auto-add + fpt: '*' + ghes: '*' + ghec: '*' type: tutorial topics: - Projects diff --git a/content/issues/planning-and-tracking-with-projects/automating-your-project/archiving-items-automatically.md b/content/issues/planning-and-tracking-with-projects/automating-your-project/archiving-items-automatically.md index a1c26861af61..c4617ccaa99b 100644 --- a/content/issues/planning-and-tracking-with-projects/automating-your-project/archiving-items-automatically.md +++ b/content/issues/planning-and-tracking-with-projects/automating-your-project/archiving-items-automatically.md @@ -3,7 +3,9 @@ title: Archiving items automatically shortTitle: Archiving items automatically intro: You can configure your project's built-in workflows to automatically archive items that match a filter. versions: - feature: projects-v2-auto-archive + fpt: '*' + ghes: '*' + ghec: '*' type: tutorial topics: - Projects diff --git a/content/issues/planning-and-tracking-with-projects/learning-about-projects/about-projects.md b/content/issues/planning-and-tracking-with-projects/learning-about-projects/about-projects.md index a564c49549e4..d6cc28d978c0 100644 --- a/content/issues/planning-and-tracking-with-projects/learning-about-projects/about-projects.md +++ b/content/issues/planning-and-tracking-with-projects/learning-about-projects/about-projects.md @@ -39,7 +39,7 @@ To learn more about the different fields you can add to a project, see "[AUTOTIT ### Automating your projects -There are a number of ways you can add automation to your project. Built-in workflows allow you to automatically set fields when items are added or changed{% ifversion projects-v2-auto-archive %}, and you can also configure your project to automatically archive items when they meet certain criteria{% ifversion projects-v2-auto-add %} and automatically add items from a repository when they match set criteria{% endif %}{% endif %}. For more information, see "[AUTOTITLE](/issues/planning-and-tracking-with-projects/automating-your-project/using-the-built-in-automations)." +There are a number of ways you can add automation to your project. Built-in workflows allow you to automatically set fields when items are added or changed, and you can also configure your project to automatically archive items when they meet certain criteria and automatically add items from a repository when they match set criteria. For more information, see "[AUTOTITLE](/issues/planning-and-tracking-with-projects/automating-your-project/using-the-built-in-automations)." You can also use the GraphQL API and {% data variables.product.prodname_actions %} to take even greater control of your project. For more information, see "[AUTOTITLE](/issues/planning-and-tracking-with-projects/automating-your-project/using-the-api-to-manage-projects)" and "[AUTOTITLE](/issues/planning-and-tracking-with-projects/automating-your-project/automating-projects-using-actions)." diff --git a/content/issues/planning-and-tracking-with-projects/learning-about-projects/best-practices-for-projects.md b/content/issues/planning-and-tracking-with-projects/learning-about-projects/best-practices-for-projects.md index 6310628e9b10..b1b7647692d1 100644 --- a/content/issues/planning-and-tracking-with-projects/learning-about-projects/best-practices-for-projects.md +++ b/content/issues/planning-and-tracking-with-projects/learning-about-projects/best-practices-for-projects.md @@ -65,13 +65,13 @@ To prevent information from getting out of sync, maintain a single source of tru You can automate tasks to spend less time on busy work and more time on the project itself. The less you need to remember to do manually, the more likely your project will stay up to date. -{% data variables.product.prodname_projects_v2 %} offers built-in workflows. For example, when an issue is closed, you can automatically set the status to "Done." {% ifversion projects-v2-auto-archive %}You can also configure built-in workflows to automatically archive items when they meet certain criteria{% ifversion projects-v2-auto-add %} and to automatically add items from a repository when they match a filter{% endif %}.{% endif %} +{% data variables.product.prodname_projects_v2 %} offers built-in workflows. For example, when an issue is closed, you can automatically set the status to "Done". You can also configure built-in workflows to automatically archive items when they meet certain criteria and to automatically add items from a repository when they match a filter. Additionally, {% data variables.product.prodname_actions %} and the GraphQL API enable you to automate routine project management tasks. For example, to keep track of pull requests awaiting review, you can create a workflow that adds a pull request to a project and sets the status to "needs review"; this process can be automatically triggered when a pull request is marked as "ready for review." -* For more information about the built-in workflows, see "[AUTOTITLE](/issues/planning-and-tracking-with-projects/automating-your-project/using-the-built-in-automations)."{% ifversion projects-v2-auto-archive %} -* For more information about automatically archiving items, see "[AUTOTITLE](/issues/planning-and-tracking-with-projects/automating-your-project/archiving-items-automatically)."{% endif %}{% ifversion projects-v2-auto-add %} -* For more information about automatically adding items, see "[AUTOTITLE](/issues/planning-and-tracking-with-projects/automating-your-project/adding-items-automatically)."{% endif %} +* For more information about the built-in workflows, see "[AUTOTITLE](/issues/planning-and-tracking-with-projects/automating-your-project/using-the-built-in-automations)." +* For more information about automatically archiving items, see "[AUTOTITLE](/issues/planning-and-tracking-with-projects/automating-your-project/archiving-items-automatically)." +* For more information about automatically adding items, see "[AUTOTITLE](/issues/planning-and-tracking-with-projects/automating-your-project/adding-items-automatically)." * For an example workflow, see "[AUTOTITLE](/issues/planning-and-tracking-with-projects/automating-your-project/automating-projects-using-actions)." * For more information about the API, see "[AUTOTITLE](/issues/planning-and-tracking-with-projects/automating-your-project/using-the-api-to-manage-projects)." * For more information about {% data variables.product.prodname_actions %}, see "[AUTOTITLE](/actions)." diff --git a/content/issues/planning-and-tracking-with-projects/learning-about-projects/quickstart-for-projects.md b/content/issues/planning-and-tracking-with-projects/learning-about-projects/quickstart-for-projects.md index 7b11b81c932a..81a085f1abbb 100644 --- a/content/issues/planning-and-tracking-with-projects/learning-about-projects/quickstart-for-projects.md +++ b/content/issues/planning-and-tracking-with-projects/learning-about-projects/quickstart-for-projects.md @@ -127,8 +127,6 @@ To indicate the purpose of the view, give it a descriptive name. ## Configure built-in automation -{% ifversion projects-v2-auto-add %} - Next, configure the auto-add workflow to automatically add issues opened in a repository with a specific label to your project. {% data reusables.projects.access-workflows %} @@ -141,8 +139,6 @@ Next, configure the auto-add workflow to automatically add issues opened in a re 1. Next to the repository selection, type the filter criteria you want items to match before they are automatically added to your project. For example, to catch all issues and PRs opened with the label "bug", use `is:issue,pr label:bug`. 1. To enable the new workflow, click **Save and turn on workflow**. -{% endif %} - Finally, add a built in workflow to set the status to **Todo** when an item is added to your project. 1. In the top-right, click {% octicon "kebab-horizontal" aria-label="The menu icon" %} to open the menu. diff --git a/content/issues/planning-and-tracking-with-projects/managing-items-in-your-project/adding-items-to-your-project.md b/content/issues/planning-and-tracking-with-projects/managing-items-in-your-project/adding-items-to-your-project.md index 07b8f87493e4..fa6d30a56e11 100644 --- a/content/issues/planning-and-tracking-with-projects/managing-items-in-your-project/adding-items-to-your-project.md +++ b/content/issues/planning-and-tracking-with-projects/managing-items-in-your-project/adding-items-to-your-project.md @@ -12,13 +12,13 @@ allowTitleToDifferFromFilename: true {% note %} -**Note:** A project can contain a maximum of {% data variables.projects.item_limit %} items and {% data variables.projects.archived_item_limit %} archived items. {% ifversion projects-v2-auto-archive %}To learn more about automatically archiving items when they meet specific criteria, see "[AUTOTITLE](/issues/planning-and-tracking-with-projects/automating-your-project/archiving-items-automatically)."{% endif %} +**Note:** A project can contain a maximum of {% data variables.projects.item_limit %} items and {% data variables.projects.archived_item_limit %} archived items. To learn more about automatically archiving items when they meet specific criteria, see "[AUTOTITLE](/issues/planning-and-tracking-with-projects/automating-your-project/archiving-items-automatically)." {% endnote %} ## Adding issues and pull requests to a project -You have several options for adding issues and pull requests to your project. You can add them individually{% ifversion projects-v2-auto-add %}, automatically, {% endif %} or in bulk. Furthermore, you can include issues and pull requests from any organization, and you also have the ability to add draft issues that can be converted into regular issues later on. For more information, see "[Creating draft issues](#creating-draft-issues)." +You have several options for adding issues and pull requests to your project. You can add them individually, automatically, or in bulk. Furthermore, you can include issues and pull requests from any organization, and you also have the ability to add draft issues that can be converted into regular issues later on. For more information, see "[Creating draft issues](#creating-draft-issues)." {% ifversion projects-v2-timeline-events %} @@ -38,14 +38,10 @@ For more information about making bulk changes to your items after adding them, {% endif %} -{% ifversion projects-v2-auto-add %} - ### Automatically adding issues and pull requests You can configure a built-in workflow to automatically add issues and pull requests from a repository when they meet specific filter criteria. For more information about configuring a workflow, see "[AUTOTITLE](/issues/planning-and-tracking-with-projects/automating-your-project/adding-items-automatically)." -{% endif %} - ### Pasting the URL of an issue or pull request You can copy the URL of an issue or pull request into your clipboard and paste that into your project. diff --git a/content/issues/planning-and-tracking-with-projects/managing-items-in-your-project/archiving-items-from-your-project.md b/content/issues/planning-and-tracking-with-projects/managing-items-in-your-project/archiving-items-from-your-project.md index 7ff2540c614d..b784f010d573 100644 --- a/content/issues/planning-and-tracking-with-projects/managing-items-in-your-project/archiving-items-from-your-project.md +++ b/content/issues/planning-and-tracking-with-projects/managing-items-in-your-project/archiving-items-from-your-project.md @@ -18,7 +18,7 @@ allowTitleToDifferFromFilename: true ## Archiving items -You can archive an item to keep the context about the item in the project but remove it from the project views. {% ifversion projects-v2-auto-archive %}You can also configure your project's built-in workflows to automatically archive items that meet certain criteria. For more information, see "[AUTOTITLE](/issues/planning-and-tracking-with-projects/automating-your-project/archiving-items-automatically)."{% endif %} +You can archive an item to keep the context about the item in the project but remove it from the project views. You can also configure your project's built-in workflows to automatically archive items that meet certain criteria. For more information, see "[AUTOTITLE](/issues/planning-and-tracking-with-projects/automating-your-project/archiving-items-automatically)." {% data reusables.projects.select-an-item %} {% data reusables.projects.open-item-menu %} @@ -31,7 +31,7 @@ You can archive an item to keep the context about the item in the project but re 1. In the top-right, click {% octicon "kebab-horizontal" aria-label="More options" %}. ![Screenshot showing a project's menu bar. The menu icon is highlighted with an orange outline.](/assets/images/help/projects-v2/open-menu.png) - + 1. In the menu, click {% octicon "archive" aria-hidden="true" %} **Archived items**. 1. Optionally, to filter the archived items displayed, type your filter into the text box above the list of items. For more information about the available filters, see "[AUTOTITLE](/issues/planning-and-tracking-with-projects/customizing-views-in-your-project/filtering-projects)." 1. To the left of each item title, select the items you would like to restore. diff --git a/src/frame/middleware/context/product-groups.js b/src/frame/middleware/context/product-groups.ts similarity index 63% rename from src/frame/middleware/context/product-groups.js rename to src/frame/middleware/context/product-groups.ts index add02931f8ea..59f7e282fb4d 100644 --- a/src/frame/middleware/context/product-groups.js +++ b/src/frame/middleware/context/product-groups.ts @@ -1,9 +1,12 @@ -import { getProductGroups } from '#src/products/lib/get-product-groups.js' -import warmServer from '#src/frame/lib/warm-server.js' -import { languageKeys } from '#src/languages/lib/languages.js' -import { allVersionKeys } from '#src/versions/lib/all-versions.js' +import type { Response, NextFunction } from 'express' -const isHomepage = (path) => { +import type { ExtendedRequest } from '@/types' +import { getProductGroups } from '@/products/lib/get-product-groups' +import warmServer from '@/frame/lib/warm-server.js' +import { languageKeys } from '@/languages/lib/languages.js' +import { allVersionKeys } from '@/versions/lib/all-versions.js' + +const isHomepage = (path: string) => { const split = path.split('/') // E.g. `/foo` but not `foo/bar` or `foo/` if (split.length === 2 && split[1] && !split[0]) { @@ -17,7 +20,14 @@ const isHomepage = (path) => { return false } -export default async function productGroups(req, res, next) { +export default async function productGroups( + req: ExtendedRequest, + res: Response, + next: NextFunction, +) { + if (!req.context) throw new Error('request is not contextualized') + if (!req.pagePath) throw new Error('pagePath is not set on request') + if (!req.language) throw new Error('language is not set on request') // It's important to use `req.pathPage` instead of `req.path` because // the request could be the client-side routing from Next where the URL // might be something like `/_next/data/foo/bar.json` which is translated, @@ -31,7 +41,7 @@ export default async function productGroups(req, res, next) { // known versions. Because if it's not valid, any possible // use of `{% ifversion ... %}` in Liquid, will throw an error. if (isHomepage(req.pagePath) && req.context.currentVersionObj) { - const { pages } = await warmServer() + const { pages } = await warmServer([]) req.context.productGroups = await getProductGroups(pages, req.language, req.context) } diff --git a/src/frame/middleware/index.ts b/src/frame/middleware/index.ts index 96c22c8c569c..512e5ffdc914 100644 --- a/src/frame/middleware/index.ts +++ b/src/frame/middleware/index.ts @@ -47,7 +47,7 @@ import glossaries from './context/glossaries' import renderProductName from './context/render-product-name' import features from '@/versions/middleware/features.js' import productExamples from './context/product-examples' -import productGroups from './context/product-groups.js' +import productGroups from './context/product-groups' import featuredLinks from '@/landings/middleware/featured-links.js' import learningTrack from '@/learning-track/middleware/learning-track.js' import next from './next.js' diff --git a/src/products/lib/all-products.d.ts b/src/products/lib/all-products.d.ts index 755864c34f76..923571e2a944 100644 --- a/src/products/lib/all-products.d.ts +++ b/src/products/lib/all-products.d.ts @@ -1,6 +1,6 @@ -import type { Product } from '@/types' +import type { PageFrontmatter, Product } from '@/types' -export const { data }: Record +export const data: PageFrontmatter export const productIds: string[] diff --git a/src/products/lib/get-product-groups.js b/src/products/lib/get-product-groups.ts similarity index 79% rename from src/products/lib/get-product-groups.js rename to src/products/lib/get-product-groups.ts index 242e90525aad..916e6a5c23fb 100644 --- a/src/products/lib/get-product-groups.js +++ b/src/products/lib/get-product-groups.ts @@ -1,12 +1,21 @@ import path from 'path' +import type { Page, ProductGroup, ProductGroupChild, Context } from '@/types' import { productMap, data } from './all-products.js' -import { renderContentWithFallback } from '#src/languages/lib/render-with-fallback.js' -import removeFPTFromPath from '#src/versions/lib/remove-fpt-from-path.js' +import { renderContentWithFallback } from '@/languages/lib/render-with-fallback.js' +import removeFPTFromPath from '@/versions/lib/remove-fpt-from-path.js' -async function getPage(id, lang, pageMap, context) { +type PageMap = Record + +async function getPage( + id: string, + lang: string, + pageMap: PageMap, + context: Context, +): Promise { const productId = id.split('/')[0] const product = productMap[productId] + const external = product.external || false // undefined becomes false // The href depends. Initially all we have is an `id` which might be @@ -26,6 +35,8 @@ async function getPage(id, lang, pageMap, context) { let name = product.name + if (!context.currentVersion) throw new Error('context.currentVersion is not set') + if (!external) { // First we have to find it as a page object based on its ID. href = removeFPTFromPath(path.posix.join('/', lang, context.currentVersion, id)) @@ -34,6 +45,7 @@ async function getPage(id, lang, pageMap, context) { // fall back it its default version, which is `product.versions[0]`. // For example, you're on `/en/enterprise-server@3.1` and you're // but a `/foo/bar` is only available in `enterprise-cloud@latest`. + if (!product.versions) throw new Error(`Product ${productId} has no versions`) href = removeFPTFromPath(path.posix.join('/', lang, product.versions[0], id)) } const page = pageMap[href] @@ -74,9 +86,13 @@ async function getPage(id, lang, pageMap, context) { } } -export async function getProductGroups(pageMap, lang, context) { +export async function getProductGroups( + pageMap: PageMap, + lang: string, + context: Context, +): Promise { return await Promise.all( - data.childGroups.map(async (group) => { + data.childGroups!.map(async (group) => { return { name: group.name, icon: group.icon || null, @@ -84,7 +100,7 @@ export async function getProductGroups(pageMap, lang, context) { // Typically the children are product IDs, but we support deeper page paths too children: ( await Promise.all(group.children.map((id) => getPage(id, lang, pageMap, context))) - ).filter(Boolean), + ).filter(Boolean) as ProductGroupChild[], } }), ) diff --git a/src/types.ts b/src/types.ts index 9132407ce668..0b5ba9984110 100644 --- a/src/types.ts +++ b/src/types.ts @@ -14,6 +14,66 @@ export type ExtendedRequest = Request & { // Add more properties here as needed } +// TODO: Make this type from inference using AJV based on the schema. +// For now, it's based on `schema` in frame/lib/frontmatter.js +export type PageFrontmatter = { + title: string + versions: FrontmatterVersions + shortTitle?: string + intro?: string + product?: string + permissions?: string + showMiniToc?: boolean + miniTocMaxHeadingLevel?: number + mapTopic?: boolean + hidden?: boolean + noEarlyAccessBanner?: boolean + earlyAccessToc?: string + layout?: string | boolean + redirect_from?: string[] + allowTitleToDifferFromFilename?: boolean + introLinks?: object + authors?: string[] + examples_source?: string + effectiveDate?: string + + featuredLinks?: { + gettingStarted?: string[] + startHere?: string[] + guideCards?: string[] + popular?: string[] + popularHeading?: string + videos?: { + title: string + href: string + }[] + videoHeadings?: string + }[] + changelog?: ChangeLog + type?: string + topics?: string[] + includeGuides?: string[] + learningTracks?: string[] + beta_product?: boolean + product_video?: boolean + product_video_transcript?: string + interactive?: boolean + communityRedirect?: { + name: string + href: string + } + defaultPlatform?: 'mac' | 'windows' | 'linux' + defaultTool?: string + childGroups?: ChildGroup[] +} + +export type ChildGroup = { + name: string + octicon: string + children: string[] + icon?: string +} + export type Product = { id: string name: string @@ -23,6 +83,7 @@ export type Product = { wip?: boolean hidden?: boolean versions?: string[] + external?: boolean } type ProductMap = { @@ -95,6 +156,21 @@ export type Context = { currentProductName?: string productCommunityExamples?: ProductExample[] productUserExamples?: ProductExample[] + productGroups?: ProductGroup[] +} + +export type ProductGroup = { + name: string + icon: string | null + octicon: string | null + children: ProductGroupChild[] +} + +export type ProductGroupChild = { + id: string + name: string + href: string + external: boolean } export type Glossary = {