diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 021cf63593e5..4cbf33904ee6 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -19,7 +19,8 @@
// Set *default* container specific settings.json values on container create.
"settings": {
"terminal.integrated.shell.linux": "/bin/bash",
- "cSpell.language": ",en"
+ "cSpell.language": ",en",
+ "git.autofetch": true
},
// Visual Studio Code extensions which help authoring for docs.github.com.
"extensions": [
@@ -57,7 +58,7 @@
},
// Use 'postCreateCommand' to run commands after the container is created.
- "postCreateCommand": "npm ci",
+ "postCreateCommand": "npm ci && npm start",
// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "node",
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index f55b6cdde90c..56f191de0e62 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -1,4 +1,10 @@
version: 2
+registries:
+ ghcr: # Define access for a private registry
+ type: docker-registry
+ url: ghcr.io
+ username: PAT
+ password: ${{secrets.CONTAINER_BUILDER_TOKEN}}
updates:
- package-ecosystem: npm
directory: '/'
@@ -23,11 +29,18 @@ updates:
- dependency-name: '*'
update-types:
['version-update:semver-patch', 'version-update:semver-minor']
+ - dependency-name: 'github/internal-actions'
- package-ecosystem: 'docker'
+ registries:
+ - ghcr
directory: '/'
schedule:
interval: weekly
day: thursday
+ groups:
+ baseImages:
+ patterns:
+ - '*'
ignore:
- dependency-name: 'node'
diff --git a/.github/workflows/moda-ci.yaml b/.github/workflows/moda-ci.yaml
new file mode 100644
index 000000000000..0545be426c12
--- /dev/null
+++ b/.github/workflows/moda-ci.yaml
@@ -0,0 +1,65 @@
+name: docs-internal Moda CI
+
+# More info on CI actions setup can be found here:
+# https://github.com/github/ops/blob/master/docs/playbooks/build-systems/moving-moda-apps-from-bp-to-actions.md
+
+on:
+ workflow_dispatch:
+ push:
+ branches-ignore:
+ - 'gh-readonly-queue/**'
+ merge_group:
+ types: [checks_requested]
+
+jobs:
+ moda-config-bundle:
+ if: ${{ github.repository == 'github/docs-internal' }}
+ name: ${{ matrix.ci_job.job }}
+ strategy:
+ fail-fast: false
+ matrix:
+ ci_job: [{ 'job': 'docs-internal-moda-config-bundle' }]
+ uses: github/internal-actions/.github/workflows/moda.yml@main
+ with:
+ ci-formatted-job-name: ${{ matrix.ci_job.job }}
+ vault-keys: ${{ vars.VAULT_KEYS }}
+ secrets:
+ dx-bot-token: ${{ secrets.INTERNAL_ACTIONS_DX_BOT_ACCOUNT_TOKEN }}
+ datadog-api-key: ${{ secrets.DATADOG_API_KEY }}
+
+ docker-image:
+ if: ${{ github.repository == 'github/docs-internal' }}
+ name: ${{ matrix.ci_job.job }}
+ strategy:
+ fail-fast: false
+ matrix:
+ ci_job: [{ 'job': 'docs-internal-docker-image' }]
+ uses: github/internal-actions/.github/workflows/kube.yml@main
+ with:
+ ci-formatted-job-name: ${{ matrix.ci_job.job }}
+ vault-keys: ${{ vars.VAULT_KEYS }}
+ secrets:
+ dx-bot-token: ${{ secrets.INTERNAL_ACTIONS_DX_BOT_ACCOUNT_TOKEN }}
+ datadog-api-key: ${{ secrets.DATADOG_API_KEY }}
+
+ docker-security:
+ if: ${{ github.repository == 'github/docs-internal' }}
+ name: ${{ matrix.ci_job.job }}
+ strategy:
+ fail-fast: false
+ matrix:
+ ci_job: [{ 'job': 'docs-internal-docker-security' }]
+ uses: github/internal-actions/.github/workflows/docker_security.yml@main
+ with:
+ ci-formatted-job-name: ${{ matrix.ci_job.job }}
+ vault-keys: ${{ vars.VAULT_KEYS }}
+ secrets:
+ dx-bot-token: ${{ secrets.INTERNAL_ACTIONS_DX_BOT_ACCOUNT_TOKEN }}
+ datadog-api-key: ${{ secrets.DATADOG_API_KEY }}
+
+permissions:
+ actions: read
+ checks: read
+ contents: read
+ statuses: read
+ id-token: write
diff --git a/Dockerfile b/Dockerfile
index dd9033bb3752..eadf79b32d54 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -10,6 +10,9 @@ FROM node:22-alpine@sha256:c13b26e7e602ef2f1074aef304ce6e9b7dd284c419b35d89fcf3c
# This directory is owned by the node user
ARG APP_HOME=/home/node/app
+# Make sure there's a translations directory available to not error the COPY command
+RUN mkdir -p translations && chown -R node:node translations
+
# Make sure we don't run anything as the root user
USER node
@@ -109,4 +112,4 @@ FROM preview AS production
ENV ENABLED_LANGUAGES "en,zh,es,pt,ru,ja,fr,de,ko"
# Copy in all translations
-COPY --chown=node:node translations ./translations
+COPY --chown=node:node --from=base translations ./translations
diff --git a/assets/images/help/issues/issue-type-edit.png b/assets/images/help/issues/issue-type-edit.png
index 2d661df0896b..04e53e2b6543 100644
Binary files a/assets/images/help/issues/issue-type-edit.png and b/assets/images/help/issues/issue-type-edit.png differ
diff --git a/config/kubernetes/production/deployments/webapp.yaml b/config/kubernetes/production/deployments/webapp.yaml
new file mode 100644
index 000000000000..fba6734ca36d
--- /dev/null
+++ b/config/kubernetes/production/deployments/webapp.yaml
@@ -0,0 +1,53 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: webapp
+spec:
+ replicas: 2
+ selector:
+ matchLabels:
+ app: webapp
+ template:
+ metadata:
+ labels:
+ app: webapp
+ annotations:
+ # If you emit structured logs, you can specify a parser to use so your logs are parsed
+ # properly and are much nicer to query in splunk. For more details, see
+ # https://thehub.github.com/engineering/development-and-ops/observability/logging/fluent-bit/
+ # fluentbit.io/parser: logfmt
+ spec:
+ dnsPolicy: Default
+ containers:
+ - name: webapp
+ image: docs-internal
+ resources:
+ requests:
+ cpu: 4000m
+ memory: 5Gi
+ limits:
+ cpu: 4000m
+ memory: 14Gi
+ ports:
+ - name: http
+ containerPort: 4000
+ protocol: TCP
+ envFrom:
+ - secretRef:
+ name: vault-secrets
+ - configMapRef:
+ name: kube-cluster-metadata
+ # Zero-downtime deploys
+ # https://thehub.github.com/engineering/products-and-services/internal/moda/feature-documentation/pod-lifecycle/#required-prestop-hook
+ # https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks
+ lifecycle:
+ preStop:
+ exec:
+ command: ['sleep', '5']
+ readinessProbe:
+ initialDelaySeconds: 5
+ 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
+ port: http
diff --git a/config/kubernetes/production/services/webapp.yaml b/config/kubernetes/production/services/webapp.yaml
new file mode 100644
index 000000000000..4c96ca5be8b5
--- /dev/null
+++ b/config/kubernetes/production/services/webapp.yaml
@@ -0,0 +1,23 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: webapp
+ labels:
+ service: webapp
+ annotations:
+ moda.github.net/domain-name: 'docs-internal.github.com'
+ moda.github.net/dns-registration-enabled: 'false'
+ moda.github.net/load-balancer-type:
+ public-external-http
+ # moda.github.net/allowed-ips: '23.235.32.0/20,43.249.72.0/22,103.244.50.0/24,103.245.222.0/23,103.245.224.0/24,104.156.80.0/20,140.248.64.0/18,140.248.128.0/17,146.75.0.0/17,151.101.0.0/16,157.52.64.0/18,167.82.0.0/17,167.82.128.0/20,167.82.160.0/20,167.82.224.0/20,172.111.64.0/18,185.31.16.0/22,199.27.72.0/21,199.232.0.0/1'
+ # ipv6 addresses not included
+ # curl -i "https://api.fastly.com/public-ip-list"
+spec:
+ ports:
+ - name: http
+ port: 4000
+ protocol: TCP
+ targetPort: http
+ selector:
+ app: webapp
+ type: LoadBalancer
diff --git a/config/moda/deployment.yaml b/config/moda/deployment.yaml
new file mode 100644
index 000000000000..a8edafe09980
--- /dev/null
+++ b/config/moda/deployment.yaml
@@ -0,0 +1,13 @@
+required_builds:
+ - docs-internal-moda-config-bundle / docs-internal-moda-config-bundle
+ - docs-internal-docker-image / docs-internal-docker-image
+ - docs-internal-docker-security / docs-internal-docker-security
+environments:
+ - name: production
+ auto_deploy: true
+ cluster_selector:
+ profile: general
+ region: iad
+notifications:
+ slack_channels:
+ - '#docs-ops'
diff --git a/content/issues/planning-and-tracking-with-projects/customizing-views-in-your-project/filtering-projects.md b/content/issues/planning-and-tracking-with-projects/customizing-views-in-your-project/filtering-projects.md
index 71d79e160ab8..49e2c3fda500 100644
--- a/content/issues/planning-and-tracking-with-projects/customizing-views-in-your-project/filtering-projects.md
+++ b/content/issues/planning-and-tracking-with-projects/customizing-views-in-your-project/filtering-projects.md
@@ -77,6 +77,16 @@ You can invert any filter, including combinations, by prefixing with a hyphen.
|-field:VALUE
| **-status:done** will not show any items with a status of "done."
|-field:VALUE,VALUE
| **-priority:1,2** will not show any items with a priority of either 1 or 2.
+## Filtering for items that have a value
+
+You can use `has:` to filter for items that have a value
+
+| Qualifier | Example
+| ---------- | -------------
+|has:assignee
| **has:assignee** will show items with an assignee.
+|has:label
| **has:label** will show items with a label.
+|has:FIELD | **has:priority** will show items with a priority field value.
+
## Filtering for items that are missing a value
You can use `no:` to filter for items that are missing a value
diff --git a/content/issues/tracking-your-work-with-issues/configuring-issues/managing-issue-types-in-an-organization.md b/content/issues/tracking-your-work-with-issues/configuring-issues/managing-issue-types-in-an-organization.md
index 4ac78256c421..170c67ef047b 100644
--- a/content/issues/tracking-your-work-with-issues/configuring-issues/managing-issue-types-in-an-organization.md
+++ b/content/issues/tracking-your-work-with-issues/configuring-issues/managing-issue-types-in-an-organization.md
@@ -12,7 +12,7 @@ permissions: 'Organization owners can modify issue types.'
{% data reusables.issues.release-stage %}
-You can use issue types to classify and manage different types of issues across your organization. You can create up to ten issue types that your organization members can apply to issues, making it easier for you and your members to find issues and plan work.
+You can use issue types to classify and manage different types of issues across your organization. You can create up to {% data variables.projects.issue_type_limit %} issue types that your organization members can apply to issues, making it easier for you and your members to find issues and plan work.
Default issue types are included in every organization, but these can edited, disabled, or deleted. The default types are task, bug, and feature.
diff --git a/content/issues/tracking-your-work-with-issues/using-issues/adding-sub-issues.md b/content/issues/tracking-your-work-with-issues/using-issues/adding-sub-issues.md
index 10a492f7703a..c9954ff3b142 100644
--- a/content/issues/tracking-your-work-with-issues/using-issues/adding-sub-issues.md
+++ b/content/issues/tracking-your-work-with-issues/using-issues/adding-sub-issues.md
@@ -24,7 +24,7 @@ You can add sub-issues to an issue to break down larger pieces of work into task
Your sub-issues can themselves contain sub-issues, allowing you to create full hierarchies of issues that visualize entire projects or pieces of work and show the relationships between your issues.
-You can add up to fifty sub-issues per parent issue and create up to eight levels of nested sub-issues.
+You can add up to {% data variables.projects.sub-issue_limit %} sub-issues per parent issue and create up to eight levels of nested sub-issues.
## Creating a sub-issue
diff --git a/data/variables/projects.yml b/data/variables/projects.yml
index 69e43d04d6c5..7b533b851aba 100644
--- a/data/variables/projects.yml
+++ b/data/variables/projects.yml
@@ -22,5 +22,7 @@ projects_v2_and_v1_if_create: '{% data variables.projects.projects_v2 %}{% ifver
# Limits
item_limit: '1,200'
archived_item_limit: '10,000'
+issue_type_limit: '25'
+sub-issue_limit: '100'
tasklists: 'tasklists (beta)'
diff --git a/ownership.yaml b/ownership.yaml
new file mode 100644
index 000000000000..fd23b50c4c67
--- /dev/null
+++ b/ownership.yaml
@@ -0,0 +1,22 @@
+---
+version: 1
+ownership:
+ - team: github/docs-engineering
+ repo: https://github.com/github/docs-internal
+ name: docs-internal
+ kind: moda
+ long_name: Docs on Moda
+ description: Please use instead.
+ exec_sponsor: nerdneha
+ product_manager: docs-bot
+ qos: best_effort
+ tier: 2
+ sev1:
+ pagerduty: https://github.pagerduty.com/escalation_policies#PN57VQ1
+ tta: 30m
+ sev2:
+ issue: https://github.com/github/docs-engineering/issues
+ tta: 1d
+ sev3:
+ issue: https://github.com/github/docs-engineering/issues
+ tta: 1w
diff --git a/src/workflows/tests/actions-workflows.ts b/src/workflows/tests/actions-workflows.ts
index 6f2a231dfbda..df1461e96fea 100644
--- a/src/workflows/tests/actions-workflows.ts
+++ b/src/workflows/tests/actions-workflows.ts
@@ -27,6 +27,7 @@ const workflowsDir = path.join(__dirname, '../../../.github/workflows')
const workflows: WorkflowMeta[] = fs
.readdirSync(workflowsDir)
.filter((filename) => filename.endsWith('.yml') || filename.endsWith('.yaml'))
+ .filter((filename) => filename !== 'moda-ci.yaml') // Skip moda-ci
.map((filename) => {
const fullpath = path.join(workflowsDir, filename)
const data = yaml.load(fs.readFileSync(fullpath, 'utf8')) as WorkflowMeta['data']