diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index ac2a6ebbb3a8..9cef36755736 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -4,7 +4,8 @@ Thank you for contributing to this project! You must fill out the information be ### Why: -Closes: ISSUE_NUMBER + +Closes: diff --git a/.github/actions/precompute-pageinfo/action.yml b/.github/actions/precompute-pageinfo/action.yml index 4b9923417a6c..c6e7da64fc09 100644 --- a/.github/actions/precompute-pageinfo/action.yml +++ b/.github/actions/precompute-pageinfo/action.yml @@ -23,9 +23,8 @@ runs: key: pageinfo-cache- restore-keys: pageinfo-cache- - # When we use this composite action from the workflows like - # Azure Preview Deploy and Azure Production Deploy, we don't have - # any Node installed or any of its packages. I.e. we never + # When we use this composite action from deployment workflows + # we don't have any Node installed or any of its packages. I.e. we never # run `npm ci` in those actions. For security sake. # So we can't do things that require Node code. # Tests and others will omit the `restore-only` input, but diff --git a/.github/actions/warmup-remotejson-cache/action.yml b/.github/actions/warmup-remotejson-cache/action.yml index 8d8d9a5e4304..b1e7fe3b87b7 100644 --- a/.github/actions/warmup-remotejson-cache/action.yml +++ b/.github/actions/warmup-remotejson-cache/action.yml @@ -20,9 +20,8 @@ runs: key: remotejson-cache- restore-keys: remotejson-cache- - # When we use this composite action from the workflows like - # Azure Preview Deploy and Azure Production Deploy, we don't have - # any Node installed or any of its packages. I.e. we never + # When we use this composite action from deployment workflows + # we don't have any Node installed or any of its packages. I.e. we never # run `npm ci` in those actions. For security sake. # So we can't do things that require Node code. # Tests and others will omit the `restore-only` input, but diff --git a/.github/branch_protection_settings/main.json b/.github/branch_protection_settings/main.json index 29e0c828d766..b557412aee0c 100644 --- a/.github/branch_protection_settings/main.json +++ b/.github/branch_protection_settings/main.json @@ -4,7 +4,6 @@ "url": "https://api.github.com/repos/github/docs-internal/branches/main/protection/required_status_checks", "strict": true, "contexts": [ - "Build and deploy Azure preview environment", "automated-pipelines", "github-apps", "graphql", @@ -48,10 +47,6 @@ ], "contexts_url": "https://api.github.com/repos/github/docs-internal/branches/main/protection/required_status_checks/contexts", "checks": [ - { - "context": "Build and deploy Azure preview environment", - "app_id": 15368 - }, { "context": "automated-pipelines", "app_id": 15368 }, { "context": "github-apps", "app_id": 15368 }, { "context": "graphql", "app_id": 15368 }, diff --git a/Dockerfile.azure b/Dockerfile.azure deleted file mode 100644 index dd9033bb3752..000000000000 --- a/Dockerfile.azure +++ /dev/null @@ -1,112 +0,0 @@ -# This Dockerfile is used for docker-based deployments to Azure for both preview environments and production - -# -------------------------------------------------------------------------------- -# BASE IMAGE -# -------------------------------------------------------------------------------- -# To update the sha, run `docker pull node:$VERSION-alpine` -# look for something like: `Digest: sha256:0123456789abcdef` -FROM node:22-alpine@sha256:c13b26e7e602ef2f1074aef304ce6e9b7dd284c419b35d89fcf3cc8e44a8def9 AS base - -# This directory is owned by the node user -ARG APP_HOME=/home/node/app - -# Make sure we don't run anything as the root user -USER node - -WORKDIR $APP_HOME - - -# --------------- -# ALL DEPS -# --------------- -FROM base AS all_deps - -COPY --chown=node:node package.json package-lock.json ./ - -RUN npm ci --no-optional --registry https://registry.npmjs.org/ - -# For Next.js v12+ -# This the appropriate necessary extra for node:VERSION-alpine -# Other options are https://www.npmjs.com/search?q=%40next%2Fswc -RUN npm i @next/swc-linux-x64-musl --no-save || npm i @next/swc-linux-arm64-musl --no-save - - -# --------------- -# PROD DEPS -# --------------- -FROM all_deps AS prod_deps - -RUN npm prune --production - - -# --------------- -# BUILDER -# --------------- -FROM all_deps AS builder - -COPY src ./src -# The star is because it's an optional directory -COPY .remotejson-cache* ./.remotejson-cache -# The star is because it's an optional file -COPY .pageinfo-cache.json.br* ./.pageinfo-cache.json.br -# Certain content is necessary for being able to build -COPY content/index.md ./content/index.md -COPY content/rest ./content/rest -COPY data ./data - -COPY next.config.js ./next.config.js -COPY tsconfig.json ./tsconfig.json - -RUN npm run build - -# -------------------------------------------------------------------------------- -# PREVIEW IMAGE - no translations -# -------------------------------------------------------------------------------- - -FROM base AS preview - -# Copy just prod dependencies -COPY --chown=node:node --from=prod_deps $APP_HOME/node_modules $APP_HOME/node_modules - -# Copy our front-end code -COPY --chown=node:node --from=builder $APP_HOME/.next $APP_HOME/.next - -# We should always be running in production mode -ENV NODE_ENV=production - -# Preferred port for server.js -ENV PORT=4000 - -ENV ENABLED_LANGUAGES="en" - -# This makes it possible to set `--build-arg BUILD_SHA=abc123` -# and it then becomes available as an environment variable in the docker run. -ARG BUILD_SHA -ENV BUILD_SHA=$BUILD_SHA - -# Copy only what's needed to run the server -COPY --chown=node:node package.json ./ -COPY --chown=node:node assets ./assets -COPY --chown=node:node content ./content -COPY --chown=node:node src ./src -COPY --chown=node:node .remotejson-cache* ./.remotejson-cache -COPY --chown=node:node .pageinfo-cache.json.br* ./.pageinfo-cache.json.br -COPY --chown=node:node data ./data -COPY --chown=node:node next.config.js ./ -COPY --chown=node:node tsconfig.json ./ - -EXPOSE $PORT - -CMD ["node_modules/.bin/tsx", "src/frame/server.ts"] - -# -------------------------------------------------------------------------------- -# PRODUCTION IMAGE - includes all translations -# -------------------------------------------------------------------------------- -FROM preview AS production - -# Override what was set for previews -# Make this match the default of `Object.keys(languages)` in src/languages/lib/languages.js -ENV ENABLED_LANGUAGES "en,zh,es,pt,ru,ja,fr,de,ko" - -# Copy in all translations -COPY --chown=node:node translations ./translations diff --git a/config/kubernetes/production/deployments/webapp.yaml b/config/kubernetes/production/deployments/webapp.yaml index fc0bfb0feb54..a0dac38bf5a6 100644 --- a/config/kubernetes/production/deployments/webapp.yaml +++ b/config/kubernetes/production/deployments/webapp.yaml @@ -53,5 +53,5 @@ spec: httpGet: # WARNING: This should be updated to a meaningful endpoint for your application which will return a 200 once the app is fully started. # See: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-readiness-probes - path: /healthz + path: /healthcheck port: http diff --git a/content/code-security/code-scanning/managing-code-scanning-alerts/fixing-alerts-in-security-campaign.md b/content/code-security/code-scanning/managing-code-scanning-alerts/fixing-alerts-in-security-campaign.md index 72339d23de27..79532f274edf 100644 --- a/content/code-security/code-scanning/managing-code-scanning-alerts/fixing-alerts-in-security-campaign.md +++ b/content/code-security/code-scanning/managing-code-scanning-alerts/fixing-alerts-in-security-campaign.md @@ -26,7 +26,7 @@ You can take part in a security campaign by fixing one or more of the alerts cho In addition to the benefit of removing an important security problem from your code, alerts in a security campaign have several other benefits compared with fixing another alert in your repository. -* You have a contact on the security team to collaborate with. +* You have a campaign manager on the security team to collaborate with and a specific contact link for discussing campaign activities. * You know that you are fixing a security alert that is important to the company. * Potentially, you may have access to targeted training materials.{% ifversion security-campaigns-autofix %} * You don't need to request a {% data variables.product.prodname_copilot_autofix %} suggestion, it is already available as a starting point.{% endif %}{% ifversion copilot %} diff --git a/content/code-security/securing-your-organization/fixing-security-alerts-at-scale/best-practice-fix-alerts-at-scale.md b/content/code-security/securing-your-organization/fixing-security-alerts-at-scale/best-practice-fix-alerts-at-scale.md index a47f8967a0e4..669a210c252c 100644 --- a/content/code-security/securing-your-organization/fixing-security-alerts-at-scale/best-practice-fix-alerts-at-scale.md +++ b/content/code-security/securing-your-organization/fixing-security-alerts-at-scale/best-practice-fix-alerts-at-scale.md @@ -19,7 +19,8 @@ topics: Successful security campaigns to fix alerts at scale have many features in common, including: * Selecting a related group of security alerts for remediation. -* Making sure that the manager for the campaign is available for collaboration, reviews, and questions about fixes. +* Using {% data variables.product.prodname_copilot_autofix_short %} suggestions where possible to help developers remediate alerts faster and more effectively. +* Making sure that the campaign managers are available for collaboration, reviews, and questions about fixes. * Providing access to educational information about the type of alerts included in the campaign.{% ifversion ghec %} * Making {% data variables.product.prodname_copilot_chat %} available for developers to use to learn about the vulnerabilities highlighted by the security alerts in the campaign. {% endif %} * Defining a realistic deadline for campaign, bearing in mind the number of alerts you aim to fix. @@ -36,7 +37,11 @@ For example, if you have many alerts for cross-site scripting vulnerabilities, y * Create educational content for developers in a repository using resources from the OWASP Foundation, see [Cross Site Scripting (XSS)](https://owasp.org/www-community/attacks/xss/). * Create a campaign to remediate all alerts for this vulnerability, including a link to the educational content in the campaign description. * Hold a training session or other event to highlight this opportunity to gain confidence in secure coding while fixing real bugs. -* Make sure that the security team member assigned to manage the campaign is available to review the pull requests created to fix the campaign alerts, collaborating as needed. +* Make sure that the security team members assigned to manage the campaign are available to review the pull requests created to fix the campaign alerts, collaborating as needed. + +### Using {% data variables.product.prodname_copilot_autofix_short %} to help remediate security alerts + +{% data variables.product.prodname_copilot_autofix %} is an expansion of {% data variables.product.prodname_code_scanning %} that provides users with targeted recommendations to help fix {% data variables.product.prodname_code_scanning %} alerts. When you select alerts to include in a security campaign, you can preferentially include alerts that are eligible to be fixed with the help of {% data variables.product.prodname_copilot_autofix %} using the `autofix:supported` filter. ### Campaign filter templates @@ -47,15 +52,19 @@ When you select alerts to include in a security campaign, you can use any of the The following limitations are intended to encourage you to take a balanced and measured approach to remediating alerts in your code. An iterative approach, addressing a few targeted sets of alerts at a time, is likely to lead to a sustainable and long-term change in security posture. * A maximum of 10 active security campaigns at a time (no limits on closed campaigns). -* Each campaign can contain up to 1000 alerts spread across up to 100 repositories. +* Each campaign can contain up to 1000 alerts. If you choose to create a campaign that exceeds these limits, alerts will be omitted to bring the campaign into line with the limits. Alerts in repositories with recent pushes are prioritized for inclusion in the campaign. -## Defining the role of the campaign manager +## Specifying campaign managers and contact links + +When you create a security campaign, you must select one or more "Campaign managers." A campaign manager must be either: +* A user with the organization owner role, or the security manager role. +* A member of a team with either the organization owner role, or the security manager role. -When you create a security campaign, you must select a "Campaign manager." The campaign manager must have either the organization owner or security manager role. +The names of the campaign managers are visible to developers when they take part in the campaign. To support communication between developers and the campaigns managers, you can also provide a contact link, such as a link to a {% data variables.product.prodname_discussions %} or another communication channel, when you create a campaign. -The name of the campaign manager is visible to developers when they take part in the campaign. If you want to increase the remediation rate for alerts and scale the knowledge of the security team, this is a key opportunity to build collaborative relationships with developers. Ideally, a campaign manager is available to answer questions, collaborate on difficult fixes, and review pull requests for fixes over the whole course of the campaign. +If you want to increase the remediation rate for alerts and scale the knowledge of the security team, this is a key opportunity to build collaborative relationships with developers. Ideally, the campaign managers are available to answer questions and collaborate on difficult fixes via the contact link. Campaign managers should also be available to review pull requests for fixes over the whole course of the campaign. ## Combining security training with a security campaign diff --git a/content/code-security/securing-your-organization/fixing-security-alerts-at-scale/creating-tracking-security-campaigns.md b/content/code-security/securing-your-organization/fixing-security-alerts-at-scale/creating-tracking-security-campaigns.md index 39b7fd6c5162..7f85a8310bde 100644 --- a/content/code-security/securing-your-organization/fixing-security-alerts-at-scale/creating-tracking-security-campaigns.md +++ b/content/code-security/securing-your-organization/fixing-security-alerts-at-scale/creating-tracking-security-campaigns.md @@ -32,12 +32,11 @@ The campaign templates contain filters for the most common alert selections. {% 1. Select one of the pre-defined filter templates to open a "New campaign from TEMPLATE_NAME template" dialog box. 1. If the message "This looks like a big campaign" is displayed, click **Back to filters** to display the {% data variables.product.prodname_code_scanning %} alerts view with the campaign template filter shown. 1. Add further filters to reduce the number of alerts shown, for example, filtering by "Team" or by custom property. - 1. When there are fewer than 1000 alerts in 100 repositories, click **Create campaign** to redisplay the "New campaign" dialog. + 1. When there are 1000 alerts or fewer, click **Create campaign** to redisplay the "New campaign" dialog. Alternatively, you can click **Continue creating a campaign** and create the campaign. {% data reusables.security-campaigns.too-many-alerts %} -1. Edit the "Campaign name" and "Short description" to match your campaign needs and to link to any resources that support the campaign. -1. Define a "Campaign due date" and select a "Campaign manager" as the primary contact for the campaign (an owner or security manager of this organization). -1. When you're ready to create the campaign, click **Create campaign**. + +{% data reusables.security-campaigns.campaign-configuration %} The security campaign is created and the campaign overview page is displayed. @@ -46,16 +45,15 @@ The security campaign is created and the campaign overview page is displayed. {% data reusables.organizations.navigate-to-org %} {% data reusables.organizations.security-overview %} 1. In the left sidebar, under "Alerts" click **{% octicon "codescan" aria-hidden="true" %} {% data variables.product.prodname_code_scanning_caps %}** to show the alerts view. -1. Add filters to select a subset of alerts for your campaign. When you have chosen fewer than 1000 alerts, spread across fewer than 100 repositories, you are ready to create a campaign. +1. Add filters to select a subset of alerts for your campaign. When you have chosen 1000 alerts or fewer, you are ready to create a campaign. 1. Above the table of alerts, click **Create campaign** to start creating a campaign. 1. If the message "This looks like a big campaign" is displayed, click **Back to filters** to display the {% data variables.product.prodname_code_scanning %} alerts view with your existing filters. 1. Add further filters to reduce the number of alerts shown, for example, filtering by "Team" or by custom property. - 1. When there are fewer than 1000 alerts in 100 repositories, click **Create campaign** to redisplay the "New campaign" dialog. + 1. When there are fewer than 1000 alerts, click **Create campaign** to redisplay the "New campaign" dialog. Alternatively, you can click **Continue creating a campaign** and create the campaign. {% data reusables.security-campaigns.too-many-alerts %} -1. Edit the "Campaign name" and "Short description" to match your campaign needs and to link to any resources that support the campaign. -1. Define a "Campaign due date" and select a "Campaign manager" as the primary contact for the campaign (an owner or security manager of this organization). -1. When you're ready to create the campaign, click **Create campaign**. + +{% data reusables.security-campaigns.campaign-configuration %} ### Examples of useful filters @@ -85,7 +83,7 @@ When you create a campaign all the alerts are automatically submitted to {% data ### How developers know a security campaign has started -Everyone with **write** access to a repository that is included in the campaign is notified, according to their notification preferences, about the campaign. +When a campaign is started, anyone with **write** access to a repository included in the campaign, and who and has subscribed to watch either "All activity" or "security alerts" in that repository, is notified. > [!NOTE] During the {% data variables.release-phases.public_preview %}, notifications are only sent to users who have email notification enabled. diff --git a/contributing/deployments.md b/contributing/deployments.md index 61d919c8fcce..034e052ae960 100644 --- a/contributing/deployments.md +++ b/contributing/deployments.md @@ -4,12 +4,7 @@ Staging and production deployments are automated by a deployer service created a ### Preview deployments -When a pull request is **opened**, **reopened**, or **synchronized** (i.e has new commits), it is automatically deployed to a unique preview URL. - -If a preview deployment fails, you can trigger a new deployment in a few ways: - - close and re-open the pull request - - push another commit to the branch - - click **Update Branch** on the pull request page on github.com, if it's clickable +When a pull request contains only content changes, it can be previewed without a deployment. Code changes will require a deployment. GitHub Staff can deploy such a PR to a staging environment. ### Production deployments diff --git a/data/reusables/security-campaigns/campaign-configuration.md b/data/reusables/security-campaigns/campaign-configuration.md new file mode 100644 index 000000000000..96ced7a4a8a6 --- /dev/null +++ b/data/reusables/security-campaigns/campaign-configuration.md @@ -0,0 +1,4 @@ +1. Edit the "Campaign name" and "Short description" to match your campaign needs and to link to any resources that support the campaign. +1. Define a "Campaign due date" and select one or more "Campaign managers" as the primary contacts for the campaign. Campaign managers must be users or teams that are owners or security managers in the organization. +1. Optionally, provide a "Contact link", for example a link to a {% data variables.product.prodname_discussions %} or another communication channel, for contacting the campaign managers. +1. When you're ready to create the campaign, click **Create campaign**. diff --git a/data/reusables/security-campaigns/too-many-alerts.md b/data/reusables/security-campaigns/too-many-alerts.md index 494ddf8d5570..ad7fee69dd51 100644 --- a/data/reusables/security-campaigns/too-many-alerts.md +++ b/data/reusables/security-campaigns/too-many-alerts.md @@ -1 +1 @@ -Alerts will be omitted to until there are fewer than 1000 alerts in fewer than 100 repositories remaining. Alerts in repositories with recent pushes are prioritized for inclusion in the campaign. +Alerts will be omitted to until there are 1000 or fewer alerts remaining. Alerts in repositories with recent pushes are prioritized for inclusion in the campaign. diff --git a/package.json b/package.json index b7132a147261..7bbe34a03665 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,6 @@ "analyze-comment": "tsx src/events/scripts/analyze-comment-cli.ts", "archive-version": "tsx --max-old-space-size=16384 src/ghes-releases/scripts/archive-version.ts", "build": "next build", - "check-canary-slots": "tsx src/workflows/check-canary-slots.ts", "check-content-type": "tsx src/workflows/check-content-type.ts", "check-github-github-links": "tsx src/links/scripts/check-github-github-links.ts", "close-dangling-prs": "tsx src/workflows/close-dangling-prs.ts", @@ -29,7 +28,6 @@ "content-changes-table-comment": "tsx src/workflows/content-changes-table-comment.ts", "copy-fixture-data": "tsx src/tests/scripts/copy-fixture-data.js", "count-translation-corruptions": "tsx src/languages/scripts/count-translation-corruptions.ts", - "create-acr-token": "tsx src/workflows/acr-create-token.ts", "create-enterprise-issue": "tsx src/ghes-releases/scripts/create-enterprise-issue.js", "debug": "cross-env NODE_ENV=development ENABLED_LANGUAGES=en nodemon --inspect src/frame/server.ts", "delete-orphan-translation-files": "tsx src/workflows/delete-orphan-translation-files.ts", diff --git a/src/frame/middleware/healthz.ts b/src/frame/middleware/healthcheck.ts similarity index 90% rename from src/frame/middleware/healthz.ts rename to src/frame/middleware/healthcheck.ts index 4a6b5799fd84..e3f8e5b9ff13 100644 --- a/src/frame/middleware/healthz.ts +++ b/src/frame/middleware/healthcheck.ts @@ -9,7 +9,7 @@ const router = express.Router() * instance remains in the pool to handle requests * For example: if we have a failing database connection we may return a 500 status here. */ -router.get('/', function healthz(req, res) { +router.get('/', function healthcheck(req, res) { noCacheControl(res) res.sendStatus(200) diff --git a/src/frame/middleware/index.ts b/src/frame/middleware/index.ts index 518bc14bd9a2..9e2c574f48c4 100644 --- a/src/frame/middleware/index.ts +++ b/src/frame/middleware/index.ts @@ -27,7 +27,7 @@ import findPage from './find-page.js' import blockRobots from './block-robots' import archivedEnterpriseVersionsAssets from '@/archives/middleware/archived-enterprise-versions-assets' import api from './api' -import healthz from './healthz' +import healthcheck from './healthcheck' import manifestJson from './manifest-json' import buildInfo from './build-info' import reqHeaders from './req-headers' @@ -66,16 +66,14 @@ import shielding from '@/shielding/middleware' import tracking from '@/tracking/middleware' import { MAX_REQUEST_TIMEOUT } from '@/frame/lib/constants.js' -const { DEPLOYMENT_ENV, NODE_ENV } = process.env +const { NODE_ENV } = process.env const isTest = NODE_ENV === 'test' || process.env.GITHUB_ACTIONS === 'true' // By default, logging each request (with morgan), is on. And by default // it's off if you're in a production environment or running automated tests. // But if you set the env var, that takes precedence. const ENABLE_DEV_LOGGING = Boolean( - process.env.ENABLE_DEV_LOGGING - ? JSON.parse(process.env.ENABLE_DEV_LOGGING) - : !(DEPLOYMENT_ENV === 'azure' || isTest), + process.env.ENABLE_DEV_LOGGING ? JSON.parse(process.env.ENABLE_DEV_LOGGING) : !isTest, ) const ENABLE_FASTLY_TESTING = JSON.parse(process.env.ENABLE_FASTLY_TESTING || 'false') @@ -120,7 +118,7 @@ export default function (app: Express) { // Put this early to make it as fast as possible because it's used, // and used very often, by the Azure load balancer to check the // health of each node. - app.use('/healthz', healthz) + app.use('/healthcheck', healthcheck) // Must appear before static assets and all other requests // otherwise we won't be able to benefit from that functionality diff --git a/src/redirects/middleware/handle-redirects.ts b/src/redirects/middleware/handle-redirects.ts index a009eb2bda6b..070f5bb42a22 100644 --- a/src/redirects/middleware/handle-redirects.ts +++ b/src/redirects/middleware/handle-redirects.ts @@ -87,7 +87,7 @@ export default function handleRedirects(req: ExtendedRequest, res: Response, nex // the language prefix. // We can't always force on the language prefix because some URLs // aren't pages. They're other middleware endpoints such as - // `/healthz` which should never redirect. + // `/healthcheck` which should never redirect. // But for example, a `/authentication/connecting-to-github-with-ssh` // needs to become `/en/authentication/connecting-to-github-with-ssh` const possibleRedirectTo = `/en${req.path}` diff --git a/src/workflows/acr-create-token.ts b/src/workflows/acr-create-token.ts deleted file mode 100755 index 4a17817ca211..000000000000 --- a/src/workflows/acr-create-token.ts +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env node -import { execSync } from 'child_process' -import * as core from '@actions/core' -import dotenv from 'dotenv' - -type IsoDateString = string - -// For local testing set environment variables in the .env file -dotenv.config() - -const acrTokenName = process.env.ACR_TOKEN_NAME -const acrServer = process.env.CONTAINER_REGISTRY_SERVER -const repo = process.env.GITHUB_REPOSITORY - -function main() { - // Get the current time and add 30 minutes to it - // Convert Date format from YYYY-MM-DDTHH:mm:ss.sssZ to - // YYYY-MM-DDTHH:mm:ssZ (remove .sss) - const expirationDate: IsoDateString = - new Date(Date.now() + 30 * 60 * 1000).toISOString().split('.')[0] + 'Z' - - let resp - try { - const cmd = `az acr token create \ - --name ${acrTokenName} \ - --registry ${acrServer} \ - --repository ${repo} \ - content/write \ - content/read \ - --expiration ${expirationDate} \ - --output json` - - console.log('Executing az acr token create command.') - resp = JSON.parse(execSync(cmd, { encoding: 'utf8' })) - } catch (error) { - console.error('An error occurred while creating ACR token with the Azure CLI') - throw error - } - - const acrTokenValue = resp?.credentials?.passwords[0]?.value - if (!acrTokenValue) { - throw new Error( - 'The response from the Azure CLI was not in the expected format: \n' + - JSON.stringify(resp, null, 2), - ) - } - - // Set the ACR_TOKEN_VALUE environment variable so - // that it can be used in the subsequent steps - core.exportVariable('ACR_TOKEN_VALUE', acrTokenValue) - execSync(`echo $ACR_TOKEN_VALUE`) -} - -main() diff --git a/src/workflows/check-canary-slots.ts b/src/workflows/check-canary-slots.ts deleted file mode 100755 index 55bc88dbf2c5..000000000000 --- a/src/workflows/check-canary-slots.ts +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env node -import { execSync } from 'child_process' -import yaml from 'js-yaml' - -const slotName = process.env.SLOT_NAME || '' -const appServiceName = process.env.APP_SERVICE_NAME || '' -const resourceGroupName = process.env.RESOURCE_GROUP_NAME || '' -const expectedSHA = process.env.EXPECTED_SHA || '' -const waitDuration = parseInt(process.env.CHECK_INTERVAL || '', 10) || 10000 -const maxWaitingTimeSeconds = parseInt(process.env.MAX_WAITING_TIME || '', 10) || 10 * 60 * 1000 - -function getBuildSha(slot: string, appService: string, resourceGroup: string) { - console.log('Getting Canary App Service Docker config') - const t0 = Date.now() - let config - try { - config = JSON.parse( - execSync( - `az webapp config container show --show-multicontainer-config --slot ${slot} -n ${appService} -g ${resourceGroup}`, - { encoding: 'utf8' }, - ), - ) - } catch { - console.log('Error getting the Canary App Service Slot config') - return null - } - - // The config is an array of objects. One of the objects - // contains a copy of the Docker compose configuration file - // pushed to the slot (see src/workflows/docker-compose.prod.tmpl.yaml). - // The value key contains the stringified YAML file, so we - // need to parse it to JSON to extract the image sha. - const dockerComposeYaml = config.find( - (obj: Record) => obj.name === 'DOCKER_CUSTOM_IMAGE_NAME_DECODED', - ).value - - let dockerComposeConfig - try { - dockerComposeConfig = yaml.load(dockerComposeYaml) as Record - } catch { - console.log('Error loading the YAML configuration data from the Canary App Service Slot config') - return null - } - // The image key looks like this: - // `ghdocsprod.azurecr.io/github/docs-internal:d7ee70f225a0f10f293ffdd2d43931acf02c6751` - const sha = dockerComposeConfig.services['ghdocs-prod'].image.split(':')[1] - console.log(`Fetched Canary App Service Slot configuration}. Took ${Date.now() - t0}ms`) - return sha -} - -function getStatesForSlot(slot: string, appService: string, resourceGroup: string) { - return JSON.parse( - execSync( - `az webapp list-instances --slot ${slot} --query "[].state" -n ${appService} -g ${resourceGroup}`, - { encoding: 'utf8' }, - ), - ) -} - -let attempts = 0 -async function doCheck() { - attempts++ - console.log('Attempt:', attempts) - const buildSha = getBuildSha(slotName, appServiceName, resourceGroupName) - console.log('Canary build SHA:', buildSha || '*unknown/failed*', 'Expected SHA:', expectedSHA) - - const states = getStatesForSlot(slotName, appServiceName, resourceGroupName) - console.log('Instance states:', states) - - const isAllReady = states.every((s: string) => s === 'READY') - - if (buildSha === expectedSHA && isAllReady) { - console.log('Got the expected build SHA and all slots are ready! 🚀') - return - } - - if (attempts * waitDuration > maxWaitingTimeSeconds) { - throw new Error(`Giving up after a total of ${(attempts * waitDuration) / 1000} seconds`) - } - - console.log(`checking again in ${waitDuration}ms`) - setTimeout(doCheck, waitDuration) -} - -doCheck() diff --git a/src/workflows/docker-compose.prod.tmpl.yaml b/src/workflows/docker-compose.prod.tmpl.yaml deleted file mode 100644 index aa908d22095a..000000000000 --- a/src/workflows/docker-compose.prod.tmpl.yaml +++ /dev/null @@ -1,33 +0,0 @@ -version: '3.7' - -services: - ghdocs-prod: - image: '#{IMAGE}#' - ports: - - '4000:4000' - environment: - NODE_ENV: ${NODE_ENV} - NODE_OPTIONS: ${NODE_OPTIONS} - DD_API_KEY: ${DD_API_KEY} - COOKIE_SECRET: ${COOKIE_SECRET} - HYDRO_ENDPOINT: ${HYDRO_ENDPOINT} - HYDRO_SECRET: ${HYDRO_SECRET} - HAYSTACK_URL: ${HAYSTACK_URL} - HEROKU_APP_NAME: ${HEROKU_APP_NAME} - DEPLOYMENT_ENV: ${DEPLOYMENT_ENV} - HEROKU_PRODUCTION_APP: true - PORT: 4000 - DD_AGENT_HOST: datadog-agent - depends_on: - - datadog-agent - restart: always - - datadog-agent: - image: 'ghdocsprod.azurecr.io/datadog/dogstatsd:latest' - ports: - - '8125:8125' - environment: - DD_API_KEY: ${DD_API_KEY} - DD_AGENT_HOST: datadog-agent - DD_HISTOGRAM_PERCENTILES: 0.99 0.95 0.50 - DD_HOSTNAME_TRUST_UTS_NAMESPACE: true diff --git a/src/workflows/docker-compose.staging.tmpl.yaml b/src/workflows/docker-compose.staging.tmpl.yaml deleted file mode 100644 index 0ca9b2d0fae1..000000000000 --- a/src/workflows/docker-compose.staging.tmpl.yaml +++ /dev/null @@ -1,33 +0,0 @@ -version: '3.7' - -services: - ghdocs-staging: - image: '#{IMAGE}#' - ports: - - '4000:4000' - environment: - NODE_ENV: ${NODE_ENV} - NODE_OPTIONS: ${NODE_OPTIONS} - DD_API_KEY: ${DD_API_KEY} - COOKIE_SECRET: ${COOKIE_SECRET} - HYDRO_ENDPOINT: ${HYDRO_ENDPOINT} - HYDRO_SECRET: ${HYDRO_SECRET} - HAYSTACK_URL: ${HAYSTACK_URL} - HEROKU_APP_NAME: ${HEROKU_APP_NAME} - ENABLED_LANGUAGES: ${ENABLED_LANGUAGES} - DEPLOYMENT_ENV: ${DEPLOYMENT_ENV} - HEROKU_PRODUCTION_APP: true - PORT: 4000 - DD_AGENT_HOST: datadog-agent - depends_on: - - datadog-agent - restart: always - - datadog-agent: - image: datadog/dogstatsd:7.32.4 - ports: - - '8125:8125' - environment: - DD_API_KEY: ${DD_API_KEY} - DD_AGENT_HOST: datadog-agent - DD_HISTOGRAM_PERCENTILES: 0.99 0.95 0.50 diff --git a/src/workflows/wait-until-url-is-healthy.ts b/src/workflows/wait-until-url-is-healthy.ts index 6d3b64374e6d..efed9e05295f 100644 --- a/src/workflows/wait-until-url-is-healthy.ts +++ b/src/workflows/wait-until-url-is-healthy.ts @@ -6,7 +6,7 @@ const DELAY_SECONDS = 15 /* * Promise resolves once url is healthy or fails if timeout has passed - * @param {string} url - health url, e.g. docs.com/healthz + * @param {string} url - health url, e.g. docs.com/healthcheck */ export async function waitUntilUrlIsHealthy(url: string) { try {