Compare commits
1 Commits
feat/expor
...
alpha-post
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19620f4fa4 |
13
.eslintignore
Normal file
13
.eslintignore
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
.tmp
|
||||||
|
**/.git
|
||||||
|
**/.hg
|
||||||
|
**/.pnp.*
|
||||||
|
**/.svn
|
||||||
|
**/.yarn/**
|
||||||
|
**/build
|
||||||
|
**/dist/**
|
||||||
|
**/node_modules
|
||||||
|
**/temp
|
||||||
|
playwright.config.ts
|
||||||
|
jest.config.js
|
||||||
|
test/live-preview/next-app
|
||||||
64
.eslintrc.cjs
Normal file
64
.eslintrc.cjs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
/** @type {import('eslint').Linter.Config} */
|
||||||
|
module.exports = {
|
||||||
|
extends: ['@payloadcms'],
|
||||||
|
ignorePatterns: ['README.md', 'packages/**/*.spec.ts'],
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ['packages/**'],
|
||||||
|
plugins: ['payload'],
|
||||||
|
rules: {
|
||||||
|
'payload/no-jsx-import-statements': 'warn',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['scripts/**'],
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/no-unused-vars': 'off',
|
||||||
|
'no-console': 'off',
|
||||||
|
'perfectionist/sort-object-types': 'off',
|
||||||
|
'perfectionist/sort-objects': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
extends: ['plugin:@typescript-eslint/disable-type-checked'],
|
||||||
|
files: ['*.js', '*.cjs', '*.json', '*.md', '*.yml', '*.yaml'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['packages/eslint-config-payload/**'],
|
||||||
|
rules: {
|
||||||
|
'perfectionist/sort-objects': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['package.json', 'tsconfig.json'],
|
||||||
|
rules: {
|
||||||
|
'perfectionist/sort-array-includes': 'off',
|
||||||
|
'perfectionist/sort-astro-attributes': 'off',
|
||||||
|
'perfectionist/sort-classes': 'off',
|
||||||
|
'perfectionist/sort-enums': 'off',
|
||||||
|
'perfectionist/sort-exports': 'off',
|
||||||
|
'perfectionist/sort-imports': 'off',
|
||||||
|
'perfectionist/sort-interfaces': 'off',
|
||||||
|
'perfectionist/sort-jsx-props': 'off',
|
||||||
|
'perfectionist/sort-keys': 'off',
|
||||||
|
'perfectionist/sort-maps': 'off',
|
||||||
|
'perfectionist/sort-named-exports': 'off',
|
||||||
|
'perfectionist/sort-named-imports': 'off',
|
||||||
|
'perfectionist/sort-object-types': 'off',
|
||||||
|
'perfectionist/sort-objects': 'off',
|
||||||
|
'perfectionist/sort-svelte-attributes': 'off',
|
||||||
|
'perfectionist/sort-union-types': 'off',
|
||||||
|
'perfectionist/sort-vue-attributes': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
parserOptions: {
|
||||||
|
project: ['./tsconfig.json'],
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
EXPERIMENTAL_useSourceOfProjectReferenceRedirect: true,
|
||||||
|
EXPERIMENTAL_useProjectService: true,
|
||||||
|
sourceType: 'module',
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
},
|
||||||
|
root: true,
|
||||||
|
}
|
||||||
@@ -19,12 +19,3 @@ fb7d1be2f3325d076b7c967b1730afcef37922c2
|
|||||||
|
|
||||||
# 3.0 prettier & lint everywhere
|
# 3.0 prettier & lint everywhere
|
||||||
6789e61488a1d3de56f472ac3214faf344030005
|
6789e61488a1d3de56f472ac3214faf344030005
|
||||||
|
|
||||||
# 3.0 prettier & lint everywhere again
|
|
||||||
83fd4c66222d7846eeb5cc332dfa99bf1e830831
|
|
||||||
|
|
||||||
# Upgrade to typescript-eslint v8, then prettier & lint everywhere
|
|
||||||
86fdad0bb8ab27810599c8a32f3d8cba1341e1df
|
|
||||||
|
|
||||||
# Prettier and lint remaining db packages
|
|
||||||
7fd736ea5b2e9fc4ef936e9dc9e5e3d722f6d8bf
|
|
||||||
|
|||||||
48
.github/CODEOWNERS
vendored
48
.github/CODEOWNERS
vendored
@@ -1,29 +1,41 @@
|
|||||||
# Order matters. The last matching pattern takes precedence.
|
# Order matters. The last matching pattern takes precedence.
|
||||||
# Approvals are not required currently but may be enabled in the future.
|
|
||||||
|
|
||||||
### Package Exports ###
|
### Core ###
|
||||||
/**/exports/ @denolfe @jmikrut @DanRibbens
|
/packages/payload/src/uploads/ @denolfe
|
||||||
|
/packages/payload/src/admin/ @jmikrut @jacobsfletch @JarrodMFlesch
|
||||||
|
|
||||||
### Packages ###
|
### Adapters ###
|
||||||
/packages/plugin-cloud*/src/ @denolfe
|
/packages/db-*/ @denolfe @jmikrut @DanRibbens
|
||||||
/packages/email-*/src/ @denolfe
|
/packages/richtext-*/ @denolfe @jmikrut @DanRibbens @AlessioGr
|
||||||
/packages/storage-*/src/ @denolfe
|
|
||||||
/packages/create-payload-app/src/ @denolfe
|
### Plugins ###
|
||||||
/packages/eslint-*/ @denolfe @AlessioGr
|
/packages/plugin-*/ @denolfe @jmikrut @DanRibbens
|
||||||
|
/packages/plugin-cloud*/ @denolfe
|
||||||
|
/packages/plugin-form-builder/ @jacobsfletch
|
||||||
|
/packages/plugin-live-preview*/ @jacobsfletch
|
||||||
|
/packages/plugin-nested-docs/ @jacobsfletch
|
||||||
|
/packages/plugin-redirects/ @jacobsfletch
|
||||||
|
/packages/plugin-search/ @jacobsfletch
|
||||||
|
/packages/plugin-sentry/ @JessChowdhury
|
||||||
|
/packages/plugin-seo/ @jacobsfletch
|
||||||
|
/packages/plugin-stripe/ @jacobsfletch
|
||||||
|
|
||||||
|
### Examples ###
|
||||||
|
/examples/ @jacobsfletch
|
||||||
|
/examples/testing/ @JarrodMFlesch
|
||||||
|
/examples/email/ @JessChowdhury
|
||||||
|
/examples/whitelabel/ @JessChowdhury
|
||||||
|
|
||||||
### Templates ###
|
### Templates ###
|
||||||
/templates/_data/ @denolfe
|
/templates/ @jacobsfletch @denolfe
|
||||||
/templates/_template/ @denolfe
|
|
||||||
|
|
||||||
### Build Files ###
|
### Misc ###
|
||||||
/tsconfig.json @denolfe
|
/packages/create-payload-app/ @denolfe
|
||||||
/**/tsconfig*.json @denolfe
|
/packages/eslint-config-payload/ @denolfe
|
||||||
/jest.config.js @denolfe
|
/packages/payload-admin-bar/ @jacobsfletch
|
||||||
/**/jest.config.js @denolfe
|
|
||||||
|
|
||||||
### Root ###
|
### Root ###
|
||||||
/package.json @denolfe
|
/package.json @denolfe
|
||||||
/scripts/ @denolfe
|
/scripts/ @denolfe
|
||||||
/.husky/ @denolfe
|
|
||||||
/.vscode/ @denolfe @AlessioGr
|
|
||||||
/.github/ @denolfe
|
/.github/ @denolfe
|
||||||
|
/.github/CODEOWNERS @denolfe
|
||||||
|
|||||||
47
.github/ISSUE_TEMPLATE/1.bug_report.yml
vendored
Normal file
47
.github/ISSUE_TEMPLATE/1.bug_report.yml
vendored
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
name: Bug Report
|
||||||
|
description: Create a bug report for Payload
|
||||||
|
labels: ['[possible-bug]']
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
*Note:* Feature requests should be opened as [discussions](https://github.com/payloadcms/payload/discussions/new?category=feature-requests-ideas).
|
||||||
|
- type: input
|
||||||
|
id: reproduction-link
|
||||||
|
attributes:
|
||||||
|
label: Link to reproduction
|
||||||
|
description: Want us to look into your issue faster? Follow the [reproduction-guide](https://github.com/payloadcms/payload/blob/main/.github/reproduction-guide.md) for more information.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Describe the Bug
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: To Reproduce
|
||||||
|
description: Steps to reproduce the behavior, please provide a clear description of how to reproduce the issue, based on the linked minimal reproduction. Screenshots can be provided in the issue body below. If using code blocks, make sure that [syntax highlighting is correct](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlighting) and double check that the rendered preview is not broken.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: version
|
||||||
|
attributes:
|
||||||
|
label: Payload Version
|
||||||
|
description: What version of Payload are you running?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: adapters-plugins
|
||||||
|
attributes:
|
||||||
|
label: Adapters and Plugins
|
||||||
|
description: What adapters and plugins are you using? ie. db-mongodb, db-postgres, bundler-webpack, etc.
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: Before submitting the issue, go through the steps you've written down to make sure the steps provided are detailed and clear.
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: Contributors should be able to follow the steps provided in order to reproduce the bug.
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: These steps are used to add integration tests to ensure the same issue does not happen again. Thanks in advance!
|
||||||
76
.github/ISSUE_TEMPLATE/1.bug_report_v3.yml
vendored
76
.github/ISSUE_TEMPLATE/1.bug_report_v3.yml
vendored
@@ -1,76 +0,0 @@
|
|||||||
name: Functionality Bug
|
|
||||||
description: '[REPRODUCTION REQUIRED] - Create a bug report'
|
|
||||||
labels: ['status: needs-triage', 'v3', 'validate-reproduction']
|
|
||||||
body:
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Describe the Bug
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: input
|
|
||||||
id: reproduction-link
|
|
||||||
attributes:
|
|
||||||
label: Link to the code that reproduces this issue
|
|
||||||
description: >-
|
|
||||||
_REQUIRED_: Please provide a link to your reproduction. Note, if the URL is invalid (404 or a private repository), we may close the issue.
|
|
||||||
Either use `npx create-payload-app@beta -t blank` then push to a repo or follow the [reproduction-guide](https://github.com/payloadcms/payload/blob/main/.github/reproduction-guide.md) for more information.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Reproduction Steps
|
|
||||||
description: Steps to reproduce the behavior, please provide a clear description of how to reproduce the issue, based on the linked minimal reproduction. Screenshots can be provided in the issue body below. If using code blocks, make sure that [syntax highlighting is correct](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlighting) and double check that the rendered preview is not broken.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: dropdown
|
|
||||||
attributes:
|
|
||||||
label: Which area(s) are affected? (Select all that apply)
|
|
||||||
multiple: true
|
|
||||||
options:
|
|
||||||
- 'Not sure'
|
|
||||||
- 'area: core'
|
|
||||||
- 'area: docs'
|
|
||||||
- 'area: templates'
|
|
||||||
- 'area: ui'
|
|
||||||
- 'db-mongodb'
|
|
||||||
- 'db-postgres'
|
|
||||||
- 'db-sqlite'
|
|
||||||
- 'db-vercel-postgres'
|
|
||||||
- 'plugin: cloud'
|
|
||||||
- 'plugin: cloud-storage'
|
|
||||||
- 'plugin: form-builder'
|
|
||||||
- 'plugin: nested-docs'
|
|
||||||
- 'plugin: richtext-lexical'
|
|
||||||
- 'plugin: richtext-slate'
|
|
||||||
- 'plugin: search'
|
|
||||||
- 'plugin: sentry'
|
|
||||||
- 'plugin: seo'
|
|
||||||
- 'plugin: stripe'
|
|
||||||
- 'plugin: other'
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Environment Info
|
|
||||||
description: Paste output from `pnpm payload info` (>= beta.92) _or_ Payload, Node.js, and Next.js versions.
|
|
||||||
render: text
|
|
||||||
placeholder: |
|
|
||||||
Payload:
|
|
||||||
Node.js:
|
|
||||||
Next.js:
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: Before submitting the issue, go through the steps you've written down to make sure the steps provided are detailed and clear.
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: Contributors should be able to follow the steps provided in order to reproduce the bug.
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: These steps are used to add integration tests to ensure the same issue does not happen again. Thanks in advance!
|
|
||||||
40
.github/ISSUE_TEMPLATE/2.design_issue.yml
vendored
40
.github/ISSUE_TEMPLATE/2.design_issue.yml
vendored
@@ -1,40 +0,0 @@
|
|||||||
name: Design Issue
|
|
||||||
description: '[SCREENSHOT REQUIRED] - Create a design issue report'
|
|
||||||
labels: ['status: needs-triage', 'v3', 'area: ui']
|
|
||||||
body:
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Describe the Bug.
|
|
||||||
description: >-
|
|
||||||
_REQUIRED:_ Please a screenshot/video of the issue along with a detailed description of the problem.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Reproduction Steps
|
|
||||||
description: Steps to reproduce the behavior, please provide a clear description of how to reproduce the issue, based on the linked minimal reproduction. Screenshots can be provided in the issue body below. If using code blocks, make sure that [syntax highlighting is correct](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlighting) and double check that the rendered preview is not broken.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Environment Info
|
|
||||||
description: Paste output from `pnpm payload info` (>= beta.92) _or_ Payload, Node.js, and Next.js versions.
|
|
||||||
render: text
|
|
||||||
placeholder: |
|
|
||||||
Payload:
|
|
||||||
Node.js:
|
|
||||||
Next.js:
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: Before submitting the issue, go through the steps you've written down to make sure the steps provided are detailed and clear.
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: Contributors should be able to follow the steps provided in order to reproduce the bug.
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: These steps are used to add integration tests to ensure the same issue does not happen again. Thanks in advance!
|
|
||||||
47
.github/ISSUE_TEMPLATE/3.bug_report_v2.yml
vendored
47
.github/ISSUE_TEMPLATE/3.bug_report_v2.yml
vendored
@@ -1,47 +0,0 @@
|
|||||||
name: v2 Bug Report
|
|
||||||
description: Report a bug for Payload v2. ONLY CRITICAL bugs will be fixed in v2.
|
|
||||||
labels: ['status: needs-triage', 'v2']
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
ONLY CRITICAL bugs will be fixed in v2.
|
|
||||||
- type: input
|
|
||||||
id: reproduction-link
|
|
||||||
attributes:
|
|
||||||
label: Link to reproduction
|
|
||||||
description: Want us to look into your issue faster? Follow the [reproduction-guide](https://github.com/payloadcms/payload/blob/main/.github/reproduction-guide.md) for more information.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Describe the Bug
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: To Reproduce
|
|
||||||
description: Steps to reproduce the behavior, please provide a clear description of how to reproduce the issue, based on the linked minimal reproduction. Screenshots can be provided in the issue body below. If using code blocks, make sure that [syntax highlighting is correct](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlighting) and double check that the rendered preview is not broken.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: input
|
|
||||||
id: version
|
|
||||||
attributes:
|
|
||||||
label: Payload Version
|
|
||||||
description: What version of Payload are you running?
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: input
|
|
||||||
id: adapters-plugins
|
|
||||||
attributes:
|
|
||||||
label: Adapters and Plugins
|
|
||||||
description: What adapters and plugins are you using? ie. db-mongodb, db-postgres, bundler-webpack, etc.
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: Before submitting the issue, go through the steps you've written down to make sure the steps provided are detailed and clear.
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: Contributors should be able to follow the steps provided in order to reproduce the bug.
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: These steps are used to add integration tests to ensure the same issue does not happen again. Thanks in advance!
|
|
||||||
32
.github/PULL_REQUEST_TEMPLATE.md
vendored
32
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,23 +1,23 @@
|
|||||||
<!--
|
## Description
|
||||||
|
|
||||||
Thank you for the PR! Please go through the checklist below and make sure you've completed all the steps.
|
<!-- Please include a summary of the pull request and any related issues it fixes. Please also include relevant motivation and context. -->
|
||||||
|
|
||||||
Please review the [CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md) document in this repository if you haven't already.
|
- [ ] I have read and understand the [CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md) document in this repository.
|
||||||
|
|
||||||
The following items will ensure that your PR is handled as smoothly as possible:
|
## Type of change
|
||||||
|
|
||||||
- PR Title must follow conventional commits format. For example, `feat: my new feature`, `fix(plugin-seo): my fix`.
|
<!-- Please delete options that are not relevant. -->
|
||||||
- Minimal description explained as if explained to someone not immediately familiar with the code.
|
|
||||||
- Provide before/after screenshots or code diffs if applicable.
|
|
||||||
- Link any related issues/discussions from GitHub or Discord.
|
|
||||||
- Add review comments if necessary to explain to the reviewer the logic behind a change
|
|
||||||
|
|
||||||
### What?
|
- [ ] Chore (non-breaking change which does not add functionality)
|
||||||
|
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||||
|
- [ ] New feature (non-breaking change which adds functionality)
|
||||||
|
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||||
|
- [ ] Change to the [templates](https://github.com/payloadcms/payload/tree/main/templates) directory (does not affect core functionality)
|
||||||
|
- [ ] Change to the [examples](https://github.com/payloadcms/payload/tree/main/examples) directory (does not affect core functionality)
|
||||||
|
- [ ] This change requires a documentation update
|
||||||
|
|
||||||
### Why?
|
## Checklist:
|
||||||
|
|
||||||
### How?
|
- [ ] I have added tests that prove my fix is effective or that my feature works
|
||||||
|
- [ ] Existing test suite passes locally with my changes
|
||||||
Fixes #
|
- [ ] I have made corresponding changes to the documentation
|
||||||
|
|
||||||
-->
|
|
||||||
|
|||||||
13
.github/actions/release-commenter/.eslintrc.js
vendored
13
.github/actions/release-commenter/.eslintrc.js
vendored
@@ -1,13 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
env: {
|
|
||||||
es6: true,
|
|
||||||
node: true,
|
|
||||||
},
|
|
||||||
extends: ['eslint:recommended', 'plugin:@typescript-eslint/eslint-recommended'],
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
parserOptions: {
|
|
||||||
ecmaVersion: 2018,
|
|
||||||
sourceType: 'module',
|
|
||||||
},
|
|
||||||
plugins: ['@typescript-eslint'],
|
|
||||||
}
|
|
||||||
74
.github/actions/release-commenter/README.md
vendored
74
.github/actions/release-commenter/README.md
vendored
@@ -1,74 +0,0 @@
|
|||||||
# Release Commenter
|
|
||||||
|
|
||||||
This GitHub Action automatically comments on and/or labels Issues and PRs when a fix is released for them.
|
|
||||||
|
|
||||||
> [!IMPORTANT]
|
|
||||||
> 🔧 Heavily modified version of https://github.com/apexskier/github-release-commenter
|
|
||||||
|
|
||||||
## Fork Modifications
|
|
||||||
|
|
||||||
- Filters to closed PRs only
|
|
||||||
- Adds tag filter to support non-linear releases
|
|
||||||
- Better logging
|
|
||||||
- Moved to pnpm
|
|
||||||
- Uses @vercel/ncc for packaging
|
|
||||||
- Comments on locked issues by unlocking then re-locking
|
|
||||||
|
|
||||||
## How it works
|
|
||||||
|
|
||||||
Use this action in a workflow [triggered by a release](https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows#release). It will scan commits between that and the prior release, find associated Issues and PRs, and comment on them to let people know a release has been made. Associated Issues and PRs can be directly [linked](https://docs.github.com/en/free-pro-team@latest/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue) to the commit or manually linked from a PR associated with the commit.
|
|
||||||
|
|
||||||
## Inputs
|
|
||||||
|
|
||||||
**GITHUB_TOKEN**
|
|
||||||
|
|
||||||
A GitHub personal access token with repo scope, such as [`secrets.GITHUB_TOKEN`](https://docs.github.com/en/free-pro-team@latest/actions/reference/authentication-in-a-workflow#about-the-github_token-secret).
|
|
||||||
|
|
||||||
**comment-template** (optional)
|
|
||||||
|
|
||||||
Override the comment posted on Issues and PRs. Set to the empty string to disable commenting. Several variables strings will be automatically replaced:
|
|
||||||
|
|
||||||
- `{release_link}` - a markdown link to the release
|
|
||||||
- `{release_name}` - the release's name
|
|
||||||
- `{release_tag}` - the release's tag
|
|
||||||
|
|
||||||
**label-template** (optional)
|
|
||||||
|
|
||||||
Add the given label. Multiple labels can be separated by commas. Several variable strings will be automatically replaced:
|
|
||||||
|
|
||||||
- `{release_name}` - the release's name
|
|
||||||
- `{release_tag}` - the release's tag
|
|
||||||
|
|
||||||
**skip-label** (optional)
|
|
||||||
|
|
||||||
Skip processing if any of the given labels are present. Same processing rules as **label-template**. Default is "dependencies".
|
|
||||||
|
|
||||||
## Example
|
|
||||||
|
|
||||||
```yml
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
release:
|
|
||||||
steps:
|
|
||||||
- uses: apexskier/github-release-commenter@v1
|
|
||||||
with:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
comment-template: |
|
|
||||||
Release {release_link} addresses this.
|
|
||||||
```
|
|
||||||
|
|
||||||
## Known limitations
|
|
||||||
|
|
||||||
These are some known limitations of this action. I'd like to try to address them in the future.
|
|
||||||
|
|
||||||
- Non-linear releases aren't supported. For example, releasing a patch to a prior major release after a new major release has been bumped.
|
|
||||||
- Non-sequential releases aren't supported. For example, if you release multiple prereleases between two official releases, this will only create a comment for the first prerelease in which a fix is released, not the final release.
|
|
||||||
- The first release for a project will be ignored. This is intentional, as the use case is unlikely. Most projects will either have several alphas that don't need release comments, or won't use issues/PRs for the first commit.
|
|
||||||
- If a large number of things are commented on, you may see the error `Error: You have triggered an abuse detection mechanism. Please wait a few minutes before you try again.`. Consider using the `skip-label` input to reduce your load on the GitHub API.
|
|
||||||
|
|
||||||
## Versions
|
|
||||||
|
|
||||||
Workflows will automatically update the tags `v1` and `latest`, allowing you to reference one of those instead of locking to a specific release.
|
|
||||||
32
.github/actions/release-commenter/action.yml
vendored
32
.github/actions/release-commenter/action.yml
vendored
@@ -1,32 +0,0 @@
|
|||||||
name: Release Commenter
|
|
||||||
description: Comment on PRs and Issues when a fix is released
|
|
||||||
branding:
|
|
||||||
icon: message-square
|
|
||||||
color: blue
|
|
||||||
inputs:
|
|
||||||
GITHUB_TOKEN:
|
|
||||||
description: |
|
|
||||||
A GitHub personal access token with repo scope, such as
|
|
||||||
secrets.GITHUB_TOKEN.
|
|
||||||
required: true
|
|
||||||
comment-template:
|
|
||||||
description: |
|
|
||||||
Text template for the comment string.
|
|
||||||
required: false
|
|
||||||
default: |
|
|
||||||
Included in release {release_link}
|
|
||||||
label-template:
|
|
||||||
description: Add the given label. Multiple labels can be separated by commas.
|
|
||||||
required: false
|
|
||||||
skip-label:
|
|
||||||
description: Skip commenting if any of the given label are present. Multiple labels can be separated by commas.
|
|
||||||
required: false
|
|
||||||
default: 'dependencies'
|
|
||||||
tag-filter:
|
|
||||||
description: |
|
|
||||||
Filter tags by a regular expression. Must be escaped. e.g. 'v\\d' to isolate tags between major versions.
|
|
||||||
required: false
|
|
||||||
default: null
|
|
||||||
runs:
|
|
||||||
using: node20
|
|
||||||
main: dist/index.js
|
|
||||||
34199
.github/actions/release-commenter/dist/index.js
vendored
34199
.github/actions/release-commenter/dist/index.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,7 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
testEnvironment: 'node',
|
|
||||||
testPathIgnorePatterns: ['/node_modules/', '<rootDir>/dist/'],
|
|
||||||
transform: {
|
|
||||||
'^.+\\.(t|j)sx?$': ['@swc/jest'],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
34
.github/actions/release-commenter/package.json
vendored
34
.github/actions/release-commenter/package.json
vendored
@@ -1,34 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "release-commenter",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"private": true,
|
|
||||||
"description": "GitHub Action to automatically comment on PRs and Issues when a fix is released.",
|
|
||||||
"license": "MIT",
|
|
||||||
"main": "dist/index.js",
|
|
||||||
"scripts": {
|
|
||||||
"build": "pnpm build:typecheck && pnpm build:ncc",
|
|
||||||
"build:ncc": "ncc build src/index.ts -t -o dist",
|
|
||||||
"build:typecheck": "tsc",
|
|
||||||
"clean": "rimraf dist",
|
|
||||||
"test": "jest"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@actions/core": "^1.3.0",
|
|
||||||
"@actions/github": "^5.0.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@octokit/webhooks-types": "^7.5.1",
|
|
||||||
"@swc/jest": "^0.2.36",
|
|
||||||
"@types/jest": "^27.5.2",
|
|
||||||
"@types/node": "^20.16.5",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^4.33.0",
|
|
||||||
"@typescript-eslint/parser": "^4.33.0",
|
|
||||||
"@vercel/ncc": "0.38.1",
|
|
||||||
"concurrently": "^8.2.2",
|
|
||||||
"eslint": "^7.32.0",
|
|
||||||
"jest": "^29.7.0",
|
|
||||||
"prettier": "^3.3.3",
|
|
||||||
"ts-jest": "^26.5.6",
|
|
||||||
"typescript": "^4.9.5"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
5419
.github/actions/release-commenter/pnpm-lock.yaml
generated
vendored
5419
.github/actions/release-commenter/pnpm-lock.yaml
generated
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,266 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`tests feature tests can apply labels 1`] = `
|
|
||||||
[
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"issue_number": 123,
|
|
||||||
"labels": [
|
|
||||||
":dart: landed",
|
|
||||||
"release-current_tag_name",
|
|
||||||
"Release Name",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"issue_number": 7,
|
|
||||||
"labels": [
|
|
||||||
":dart: landed",
|
|
||||||
"release-current_tag_name",
|
|
||||||
"Release Name",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`tests main test 1`] = `
|
|
||||||
{
|
|
||||||
"graphql": [MockFunction] {
|
|
||||||
"calls": [
|
|
||||||
[
|
|
||||||
"
|
|
||||||
{
|
|
||||||
resource(url: "http://repository/commit/SHA1") {
|
|
||||||
... on Commit {
|
|
||||||
messageHeadlineHTML
|
|
||||||
messageBodyHTML
|
|
||||||
associatedPullRequests(first: 10) {
|
|
||||||
pageInfo {
|
|
||||||
hasNextPage
|
|
||||||
}
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
bodyHTML
|
|
||||||
number
|
|
||||||
state
|
|
||||||
labels(first: 10) {
|
|
||||||
pageInfo {
|
|
||||||
hasNextPage
|
|
||||||
}
|
|
||||||
nodes {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
timelineItems(itemTypes: [CONNECTED_EVENT, DISCONNECTED_EVENT], first: 100) {
|
|
||||||
pageInfo {
|
|
||||||
hasNextPage
|
|
||||||
}
|
|
||||||
nodes {
|
|
||||||
... on ConnectedEvent {
|
|
||||||
__typename
|
|
||||||
isCrossRepository
|
|
||||||
subject {
|
|
||||||
... on Issue {
|
|
||||||
number
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
... on DisconnectedEvent {
|
|
||||||
__typename
|
|
||||||
isCrossRepository
|
|
||||||
subject {
|
|
||||||
... on Issue {
|
|
||||||
number
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"
|
|
||||||
{
|
|
||||||
resource(url: "http://repository/commit/SHA2") {
|
|
||||||
... on Commit {
|
|
||||||
messageHeadlineHTML
|
|
||||||
messageBodyHTML
|
|
||||||
associatedPullRequests(first: 10) {
|
|
||||||
pageInfo {
|
|
||||||
hasNextPage
|
|
||||||
}
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
bodyHTML
|
|
||||||
number
|
|
||||||
state
|
|
||||||
labels(first: 10) {
|
|
||||||
pageInfo {
|
|
||||||
hasNextPage
|
|
||||||
}
|
|
||||||
nodes {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
timelineItems(itemTypes: [CONNECTED_EVENT, DISCONNECTED_EVENT], first: 100) {
|
|
||||||
pageInfo {
|
|
||||||
hasNextPage
|
|
||||||
}
|
|
||||||
nodes {
|
|
||||||
... on ConnectedEvent {
|
|
||||||
__typename
|
|
||||||
isCrossRepository
|
|
||||||
subject {
|
|
||||||
... on Issue {
|
|
||||||
number
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
... on DisconnectedEvent {
|
|
||||||
__typename
|
|
||||||
isCrossRepository
|
|
||||||
subject {
|
|
||||||
... on Issue {
|
|
||||||
number
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
",
|
|
||||||
],
|
|
||||||
],
|
|
||||||
"results": [
|
|
||||||
{
|
|
||||||
"type": "return",
|
|
||||||
"value": Promise {},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "return",
|
|
||||||
"value": Promise {},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
"rest": {
|
|
||||||
"issues": {
|
|
||||||
"addLabels": [MockFunction],
|
|
||||||
"createComment": [MockFunction] {
|
|
||||||
"calls": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"body": "Included in release [current_tag_name](http://current_release). Replacements: current_tag_name, current_tag_name.",
|
|
||||||
"issue_number": 3,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"body": "Included in release [current_tag_name](http://current_release). Replacements: current_tag_name, current_tag_name.",
|
|
||||||
"issue_number": 123,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"body": "Included in release [current_tag_name](http://current_release). Replacements: current_tag_name, current_tag_name.",
|
|
||||||
"issue_number": 7,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
],
|
|
||||||
"results": [
|
|
||||||
{
|
|
||||||
"type": "return",
|
|
||||||
"value": Promise {},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "return",
|
|
||||||
"value": Promise {},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "return",
|
|
||||||
"value": Promise {},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
"get": [MockFunction] {
|
|
||||||
"calls": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"issue_number": 3,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"issue_number": 123,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"issue_number": 7,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
],
|
|
||||||
"results": [
|
|
||||||
{
|
|
||||||
"type": "return",
|
|
||||||
"value": Promise {},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "return",
|
|
||||||
"value": Promise {},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "return",
|
|
||||||
"value": Promise {},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"repos": {
|
|
||||||
"compareCommits": [MockFunction] {
|
|
||||||
"calls": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"base": "prior_tag_name",
|
|
||||||
"head": "current_tag_name",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
],
|
|
||||||
"results": [
|
|
||||||
{
|
|
||||||
"type": "return",
|
|
||||||
"value": Promise {},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
"listReleases": [MockFunction] {
|
|
||||||
"calls": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"per_page": 100,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
],
|
|
||||||
"results": [
|
|
||||||
{
|
|
||||||
"type": "return",
|
|
||||||
"value": Promise {},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
399
.github/actions/release-commenter/src/index.test.ts
vendored
399
.github/actions/release-commenter/src/index.test.ts
vendored
@@ -1,399 +0,0 @@
|
|||||||
import type * as githubModule from '@actions/github'
|
|
||||||
import type * as coreModule from '@actions/core'
|
|
||||||
import { mock } from 'node:test'
|
|
||||||
|
|
||||||
jest.mock('@actions/core')
|
|
||||||
jest.mock('@actions/github')
|
|
||||||
|
|
||||||
type Mocked<T> = {
|
|
||||||
-readonly [P in keyof T]: T[P] extends Function ? jest.Mock<T[P]> : jest.Mocked<Partial<T[P]>>
|
|
||||||
}
|
|
||||||
|
|
||||||
const github = require('@actions/github') as jest.Mocked<Mocked<typeof githubModule>>
|
|
||||||
const core = require('@actions/core') as jest.Mocked<Mocked<typeof coreModule>>
|
|
||||||
|
|
||||||
describe('tests', () => {
|
|
||||||
let mockOctokit: any = {}
|
|
||||||
let currentTag: string = 'current_tag_name'
|
|
||||||
|
|
||||||
;(core.warning as any) = jest.fn(console.warn.bind(console))
|
|
||||||
;(core.error as any) = jest.fn(console.error.bind(console))
|
|
||||||
|
|
||||||
let commentTempate: string = ''
|
|
||||||
let labelTemplate: string | null = null
|
|
||||||
const skipLabelTemplate: string | null = 'skip,test'
|
|
||||||
let tagFilter: string | RegExp | null = null
|
|
||||||
|
|
||||||
let simpleMockOctokit: any = {}
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
tagFilter = null
|
|
||||||
currentTag = 'current_tag_name'
|
|
||||||
;(github.context as any) = {
|
|
||||||
payload: {
|
|
||||||
repo: {
|
|
||||||
owner: 'owner',
|
|
||||||
repo: 'repo',
|
|
||||||
},
|
|
||||||
release: {
|
|
||||||
tag_name: currentTag,
|
|
||||||
},
|
|
||||||
repository: { html_url: 'http://repository' },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
github.getOctokit.mockReset().mockImplementationOnce(((token: string) => {
|
|
||||||
expect(token).toBe('GITHUB_TOKEN_VALUE')
|
|
||||||
return mockOctokit
|
|
||||||
}) as any)
|
|
||||||
;(core.getInput as any).mockImplementation((key: string) => {
|
|
||||||
if (key == 'GITHUB_TOKEN') {
|
|
||||||
return 'GITHUB_TOKEN_VALUE'
|
|
||||||
}
|
|
||||||
if (key == 'comment-template') {
|
|
||||||
return commentTempate
|
|
||||||
}
|
|
||||||
if (key == 'label-template') {
|
|
||||||
return labelTemplate
|
|
||||||
}
|
|
||||||
if (key == 'skip-label') {
|
|
||||||
return skipLabelTemplate
|
|
||||||
}
|
|
||||||
if (key == 'tag-filter') {
|
|
||||||
return tagFilter
|
|
||||||
}
|
|
||||||
fail(`Unexpected input key ${key}`)
|
|
||||||
})
|
|
||||||
|
|
||||||
commentTempate =
|
|
||||||
'Included in release {release_link}. Replacements: {release_name}, {release_tag}.'
|
|
||||||
labelTemplate = null
|
|
||||||
simpleMockOctokit = {
|
|
||||||
rest: {
|
|
||||||
issues: {
|
|
||||||
get: jest.fn(() => Promise.resolve({ data: { locked: false } })),
|
|
||||||
createComment: jest.fn(() => Promise.resolve()),
|
|
||||||
addLabels: jest.fn(() => Promise.resolve()),
|
|
||||||
},
|
|
||||||
repos: {
|
|
||||||
listReleases: jest.fn(() =>
|
|
||||||
Promise.resolve({
|
|
||||||
data: [
|
|
||||||
{
|
|
||||||
name: 'Release Name',
|
|
||||||
tag_name: 'current_tag_name',
|
|
||||||
html_url: 'http://current_release',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag_name: 'prior_tag_name',
|
|
||||||
html_url: 'http://prior_release',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
compareCommits: jest.fn(() =>
|
|
||||||
Promise.resolve({
|
|
||||||
data: { commits: [{ sha: 'SHA1' }] },
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
graphql: jest.fn(() =>
|
|
||||||
Promise.resolve({
|
|
||||||
resource: {
|
|
||||||
messageHeadlineHTML: '',
|
|
||||||
messageBodyHTML:
|
|
||||||
'<span class="issue-keyword tooltipped tooltipped-se" aria-label="This commit closes issue #123.">Closes</span> <p><span class="issue-keyword tooltipped tooltipped-se" aria-label="This pull request closes issue #7.">Closes</span>',
|
|
||||||
associatedPullRequests: {
|
|
||||||
pageInfo: { hasNextPage: false },
|
|
||||||
edges: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
expect(core.error).not.toHaveBeenCalled()
|
|
||||||
expect(core.warning).not.toHaveBeenCalled()
|
|
||||||
expect(core.setFailed).not.toHaveBeenCalled()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('main test', async () => {
|
|
||||||
mockOctokit = {
|
|
||||||
...simpleMockOctokit,
|
|
||||||
rest: {
|
|
||||||
issues: {
|
|
||||||
get: jest.fn(() => Promise.resolve({ data: { locked: false } })),
|
|
||||||
createComment: jest.fn(() => Promise.resolve()),
|
|
||||||
addLabels: jest.fn(() => Promise.resolve()),
|
|
||||||
},
|
|
||||||
repos: {
|
|
||||||
listReleases: jest.fn(() =>
|
|
||||||
Promise.resolve({
|
|
||||||
data: [
|
|
||||||
{
|
|
||||||
tag_name: 'current_tag_name',
|
|
||||||
html_url: 'http://current_release',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag_name: 'prior_tag_name',
|
|
||||||
html_url: 'http://prior_release',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
compareCommits: jest.fn(() =>
|
|
||||||
Promise.resolve({
|
|
||||||
data: { commits: [{ sha: 'SHA1' }, { sha: 'SHA2' }] },
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
graphql: jest.fn(() =>
|
|
||||||
Promise.resolve({
|
|
||||||
resource: {
|
|
||||||
messageHeadlineHTML:
|
|
||||||
'<span class="issue-keyword tooltipped tooltipped-se" aria-label="This commit closes issue #3.">Closes</span> <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="718013420" data-permission-text="Title is private" data-url="https://github.com/apexskier/github-release-commenter/issues/1" data-hovercard-type="issue" data-hovercard-url="/apexskier/github-release-commenter/issues/1/hovercard" href="https://github.com/apexskier/github-release-commenter/issues/1">#1</a>',
|
|
||||||
messageBodyHTML:
|
|
||||||
'<span class="issue-keyword tooltipped tooltipped-se" aria-label="This commit closes issue #123.">Closes</span> <p><span class="issue-keyword tooltipped tooltipped-se" aria-label="This pull request closes issue #7.">Closes</span>',
|
|
||||||
associatedPullRequests: {
|
|
||||||
pageInfo: { hasNextPage: false },
|
|
||||||
edges: [
|
|
||||||
{
|
|
||||||
node: {
|
|
||||||
bodyHTML:
|
|
||||||
'<span class="issue-keyword tooltipped tooltipped-se" aria-label="This commit closes issue #4.">Closes</span> <span class="issue-keyword tooltipped tooltipped-se" aria-label="This commit closes issue #5.">Closes</span>',
|
|
||||||
number: 9,
|
|
||||||
labels: {
|
|
||||||
pageInfo: { hasNextPage: false },
|
|
||||||
nodes: [{ name: 'label1' }, { name: 'label2' }],
|
|
||||||
},
|
|
||||||
timelineItems: {
|
|
||||||
pageInfo: { hasNextPage: false },
|
|
||||||
nodes: [
|
|
||||||
{
|
|
||||||
isCrossRepository: true,
|
|
||||||
__typename: 'ConnectedEvent',
|
|
||||||
subject: { number: 1 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCrossRepository: false,
|
|
||||||
__typename: 'ConnectedEvent',
|
|
||||||
subject: { number: 2 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCrossRepository: false,
|
|
||||||
__typename: 'DisconnectedEvent',
|
|
||||||
subject: { number: 2 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isCrossRepository: false,
|
|
||||||
__typename: 'ConnectedEvent',
|
|
||||||
subject: { number: 2 },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
node: {
|
|
||||||
bodyHTML: '',
|
|
||||||
number: 42,
|
|
||||||
labels: {
|
|
||||||
pageInfo: { hasNextPage: false },
|
|
||||||
nodes: [{ name: 'label1' }, { name: 'skip' }],
|
|
||||||
},
|
|
||||||
timelineItems: {
|
|
||||||
pageInfo: { hasNextPage: false },
|
|
||||||
nodes: [
|
|
||||||
{
|
|
||||||
isCrossRepository: true,
|
|
||||||
__typename: 'ConnectedEvent',
|
|
||||||
subject: { number: 82 },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
jest.isolateModules(() => {
|
|
||||||
require('./index')
|
|
||||||
})
|
|
||||||
|
|
||||||
await new Promise<void>(setImmediate)
|
|
||||||
|
|
||||||
expect(mockOctokit).toMatchSnapshot()
|
|
||||||
expect(mockOctokit.rest.issues.createComment).toHaveBeenCalledTimes(3)
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('can filter tags', () => {
|
|
||||||
const v3prev = 'v3.0.1'
|
|
||||||
const v3current = 'v3.0.2'
|
|
||||||
const v2prev = 'v2.0.1'
|
|
||||||
const v2current = 'v2.0.2'
|
|
||||||
|
|
||||||
const listReleasesData = [
|
|
||||||
{
|
|
||||||
name: 'Current Release Name',
|
|
||||||
tag_name: v3current,
|
|
||||||
html_url: 'http://v3.0.2',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Prev Release Name',
|
|
||||||
tag_name: v3prev,
|
|
||||||
html_url: 'http://v3.0.1',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'v2 Current Release Name',
|
|
||||||
tag_name: v2current,
|
|
||||||
html_url: 'http://v2.0.2',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'v2 Prev Release Name',
|
|
||||||
tag_name: v2prev,
|
|
||||||
html_url: 'http://v2.0.1',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
it.each`
|
|
||||||
description | prevTag | currentTag | filter
|
|
||||||
${'no filter'} | ${v3prev} | ${v3current} | ${null}
|
|
||||||
${'v3'} | ${v3prev} | ${v3current} | ${'v\\d'}
|
|
||||||
${'v2'} | ${v2prev} | ${v2current} | ${'v\\d'}
|
|
||||||
`('should filter tags with $description', async ({ prevTag, currentTag, filter }) => {
|
|
||||||
// @ts-ignore
|
|
||||||
github.context.payload.release.tag_name = currentTag
|
|
||||||
|
|
||||||
tagFilter = filter
|
|
||||||
|
|
||||||
mockOctokit = {
|
|
||||||
...simpleMockOctokit,
|
|
||||||
rest: {
|
|
||||||
issues: {
|
|
||||||
get: jest.fn(() => Promise.resolve({ data: { locked: false } })),
|
|
||||||
createComment: jest.fn(() => Promise.resolve()),
|
|
||||||
addLabels: jest.fn(() => Promise.resolve()),
|
|
||||||
},
|
|
||||||
repos: {
|
|
||||||
listReleases: jest.fn(() =>
|
|
||||||
Promise.resolve({
|
|
||||||
data: listReleasesData,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
compareCommits: jest.fn(() =>
|
|
||||||
Promise.resolve({
|
|
||||||
data: { commits: [{ sha: 'SHA1' }] },
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
graphql: jest.fn(() =>
|
|
||||||
Promise.resolve({
|
|
||||||
resource: {
|
|
||||||
messageHeadlineHTML: '',
|
|
||||||
messageBodyHTML:
|
|
||||||
'<span class="issue-keyword tooltipped tooltipped-se" aria-label="This commit closes issue #123.">Closes</span> <p><span class="issue-keyword tooltipped tooltipped-se" aria-label="This pull request closes issue #7.">Closes</span>',
|
|
||||||
associatedPullRequests: {
|
|
||||||
pageInfo: { hasNextPage: false },
|
|
||||||
edges: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
jest.isolateModules(() => {
|
|
||||||
require('./index')
|
|
||||||
})
|
|
||||||
|
|
||||||
await new Promise<void>((resolve) => setImmediate(() => resolve()))
|
|
||||||
|
|
||||||
expect(github.getOctokit).toHaveBeenCalled()
|
|
||||||
expect(mockOctokit.rest.repos.compareCommits.mock.calls).toEqual([
|
|
||||||
[{ base: prevTag, head: currentTag }],
|
|
||||||
])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('feature tests', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
mockOctokit = simpleMockOctokit
|
|
||||||
})
|
|
||||||
|
|
||||||
it('can disable comments', async () => {
|
|
||||||
commentTempate = ''
|
|
||||||
|
|
||||||
jest.isolateModules(() => {
|
|
||||||
require('./index')
|
|
||||||
})
|
|
||||||
|
|
||||||
await new Promise<void>((resolve) => setImmediate(() => resolve()))
|
|
||||||
|
|
||||||
expect(github.getOctokit).toHaveBeenCalled()
|
|
||||||
expect(mockOctokit.rest.issues.createComment).not.toHaveBeenCalled()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should unlock and comment', async () => {
|
|
||||||
mockOctokit = {
|
|
||||||
...simpleMockOctokit,
|
|
||||||
rest: {
|
|
||||||
...simpleMockOctokit.rest,
|
|
||||||
issues: {
|
|
||||||
// Return locked for both issues to be commented on
|
|
||||||
get: jest.fn(() => Promise.resolve({ data: { locked: true } })),
|
|
||||||
lock: jest.fn(() => Promise.resolve()),
|
|
||||||
unlock: jest.fn(() => Promise.resolve()),
|
|
||||||
createComment: jest.fn(() => Promise.resolve()),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
graphql: jest.fn(() =>
|
|
||||||
Promise.resolve({
|
|
||||||
resource: {
|
|
||||||
messageHeadlineHTML: '',
|
|
||||||
messageBodyHTML:
|
|
||||||
'<span class="issue-keyword tooltipped tooltipped-se" aria-label="This commit closes issue #123.">Closes</span> <p><span class="issue-keyword tooltipped tooltipped-se" aria-label="This pull request closes issue #7.">Closes</span>',
|
|
||||||
associatedPullRequests: {
|
|
||||||
pageInfo: { hasNextPage: false },
|
|
||||||
edges: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
jest.isolateModules(() => {
|
|
||||||
require('./index')
|
|
||||||
})
|
|
||||||
|
|
||||||
await new Promise<void>((resolve) => setImmediate(() => resolve()))
|
|
||||||
|
|
||||||
expect(github.getOctokit).toHaveBeenCalled()
|
|
||||||
|
|
||||||
// Should call once for both linked issues
|
|
||||||
expect(mockOctokit.rest.issues.unlock).toHaveBeenCalledTimes(2)
|
|
||||||
expect(mockOctokit.rest.issues.createComment).toHaveBeenCalledTimes(2)
|
|
||||||
expect(mockOctokit.rest.issues.lock).toHaveBeenCalledTimes(2)
|
|
||||||
})
|
|
||||||
|
|
||||||
it.skip('can apply labels', async () => {
|
|
||||||
labelTemplate = ':dart: landed,release-{release_tag},{release_name}'
|
|
||||||
|
|
||||||
jest.isolateModules(() => {
|
|
||||||
require('./index')
|
|
||||||
})
|
|
||||||
|
|
||||||
await new Promise<void>((resolve) => setImmediate(() => resolve()))
|
|
||||||
|
|
||||||
expect(github.getOctokit).toHaveBeenCalled()
|
|
||||||
expect(mockOctokit.rest.issues.addLabels.mock.calls).toMatchSnapshot()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
349
.github/actions/release-commenter/src/index.ts
vendored
349
.github/actions/release-commenter/src/index.ts
vendored
@@ -1,349 +0,0 @@
|
|||||||
import * as core from '@actions/core'
|
|
||||||
import * as github from '@actions/github'
|
|
||||||
import type * as Webhooks from '@octokit/webhooks-types'
|
|
||||||
|
|
||||||
const closesMatcher = /aria-label="This (?:commit|pull request) closes issue #(\d+)\."/g
|
|
||||||
|
|
||||||
const releaseLinkTemplateRegex = /{release_link}/g
|
|
||||||
const releaseNameTemplateRegex = /{release_name}/g
|
|
||||||
const releaseTagTemplateRegex = /{release_tag}/g
|
|
||||||
|
|
||||||
;(async function main() {
|
|
||||||
try {
|
|
||||||
const payload = github.context.payload as Webhooks.EventPayloadMap['release']
|
|
||||||
|
|
||||||
const githubToken = core.getInput('GITHUB_TOKEN')
|
|
||||||
const tagFilter = core.getInput('tag-filter') || undefined // Accept tag filter as an input
|
|
||||||
const octokit = github.getOctokit(githubToken)
|
|
||||||
|
|
||||||
const commentTemplate = core.getInput('comment-template')
|
|
||||||
const labelTemplate = core.getInput('label-template') || null
|
|
||||||
const skipLabelTemplate = core.getInput('skip-label') || null
|
|
||||||
|
|
||||||
// Fetch the releases with the optional tag filter applied
|
|
||||||
const { data: rawReleases } = await octokit.rest.repos.listReleases({
|
|
||||||
...github.context.repo,
|
|
||||||
per_page: 100,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Get the current release tag or latest tag
|
|
||||||
const currentTag = payload?.release?.tag_name || rawReleases?.[0]?.tag_name
|
|
||||||
|
|
||||||
let releases = rawReleases
|
|
||||||
|
|
||||||
// Filter releases by the tag filter if provided
|
|
||||||
if (tagFilter) {
|
|
||||||
core.info(`Filtering releases by tag filter: ${tagFilter}`)
|
|
||||||
// Get the matching part of the current release tag
|
|
||||||
const regexMatch = currentTag.match(tagFilter)?.[0]
|
|
||||||
if (!regexMatch) {
|
|
||||||
core.error(`Current release tag ${currentTag} does not match the tag filter ${tagFilter}`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
core.info(`Matched string from filter: ${regexMatch}`)
|
|
||||||
|
|
||||||
releases = releases
|
|
||||||
.filter((release) => {
|
|
||||||
const match = release.tag_name.match(regexMatch)?.[0]
|
|
||||||
return match
|
|
||||||
})
|
|
||||||
.slice(0, 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
core.info(`Releases: ${JSON.stringify(releases, null, 2)}`)
|
|
||||||
|
|
||||||
if (releases.length < 2) {
|
|
||||||
if (!releases.length) {
|
|
||||||
core.error(`No releases found with the provided tag filter: '${tagFilter}'`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
core.info('first release')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const [currentRelease, priorRelease] = releases
|
|
||||||
|
|
||||||
core.info(`${priorRelease.tag_name}...${currentRelease.tag_name}`)
|
|
||||||
|
|
||||||
const {
|
|
||||||
data: { commits },
|
|
||||||
} = await octokit.rest.repos.compareCommits({
|
|
||||||
...github.context.repo,
|
|
||||||
base: priorRelease.tag_name,
|
|
||||||
head: currentRelease.tag_name,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!currentRelease.name) {
|
|
||||||
core.info('Current release has no name, will fall back to the tag name.')
|
|
||||||
}
|
|
||||||
const releaseLabel = currentRelease.name || currentRelease.tag_name
|
|
||||||
|
|
||||||
const comment = commentTemplate
|
|
||||||
.trim()
|
|
||||||
.split(releaseLinkTemplateRegex)
|
|
||||||
.join(`[${releaseLabel}](${currentRelease.html_url})`)
|
|
||||||
.split(releaseNameTemplateRegex)
|
|
||||||
.join(releaseLabel)
|
|
||||||
.split(releaseTagTemplateRegex)
|
|
||||||
.join(currentRelease.tag_name)
|
|
||||||
|
|
||||||
const parseLabels = (rawInput: string | null) =>
|
|
||||||
rawInput
|
|
||||||
?.split(releaseNameTemplateRegex)
|
|
||||||
.join(releaseLabel)
|
|
||||||
?.split(releaseTagTemplateRegex)
|
|
||||||
.join(currentRelease.tag_name)
|
|
||||||
?.split(',')
|
|
||||||
?.map((l) => l.trim())
|
|
||||||
.filter((l) => l)
|
|
||||||
|
|
||||||
const labels = parseLabels(labelTemplate)
|
|
||||||
const skipLabels = parseLabels(skipLabelTemplate)
|
|
||||||
|
|
||||||
const linkedIssuesPrs = new Set<number>()
|
|
||||||
|
|
||||||
await Promise.all(
|
|
||||||
commits.map((commit) =>
|
|
||||||
(async () => {
|
|
||||||
const query = `
|
|
||||||
{
|
|
||||||
resource(url: "${payload.repository.html_url}/commit/${commit.sha}") {
|
|
||||||
... on Commit {
|
|
||||||
messageHeadlineHTML
|
|
||||||
messageBodyHTML
|
|
||||||
associatedPullRequests(first: 10) {
|
|
||||||
pageInfo {
|
|
||||||
hasNextPage
|
|
||||||
}
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
bodyHTML
|
|
||||||
number
|
|
||||||
state
|
|
||||||
labels(first: 10) {
|
|
||||||
pageInfo {
|
|
||||||
hasNextPage
|
|
||||||
}
|
|
||||||
nodes {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
timelineItems(itemTypes: [CONNECTED_EVENT, DISCONNECTED_EVENT], first: 100) {
|
|
||||||
pageInfo {
|
|
||||||
hasNextPage
|
|
||||||
}
|
|
||||||
nodes {
|
|
||||||
... on ConnectedEvent {
|
|
||||||
__typename
|
|
||||||
isCrossRepository
|
|
||||||
subject {
|
|
||||||
... on Issue {
|
|
||||||
number
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
... on DisconnectedEvent {
|
|
||||||
__typename
|
|
||||||
isCrossRepository
|
|
||||||
subject {
|
|
||||||
... on Issue {
|
|
||||||
number
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
const response: {
|
|
||||||
resource: null | {
|
|
||||||
messageHeadlineHTML: string
|
|
||||||
messageBodyHTML: string
|
|
||||||
associatedPullRequests: {
|
|
||||||
pageInfo: { hasNextPage: boolean }
|
|
||||||
edges: ReadonlyArray<{
|
|
||||||
node: {
|
|
||||||
bodyHTML: string
|
|
||||||
number: number
|
|
||||||
state: 'OPEN' | 'CLOSED' | 'MERGED'
|
|
||||||
labels: {
|
|
||||||
pageInfo: { hasNextPage: boolean }
|
|
||||||
nodes: ReadonlyArray<{
|
|
||||||
name: string
|
|
||||||
}>
|
|
||||||
}
|
|
||||||
timelineItems: {
|
|
||||||
pageInfo: { hasNextPage: boolean }
|
|
||||||
nodes: ReadonlyArray<{
|
|
||||||
__typename: 'ConnectedEvent' | 'DisconnectedEvent'
|
|
||||||
isCrossRepository: boolean
|
|
||||||
subject: {
|
|
||||||
number: number
|
|
||||||
}
|
|
||||||
}>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} = await octokit.graphql(query)
|
|
||||||
|
|
||||||
if (!response.resource) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// core.info(JSON.stringify(response.resource, null, 2))
|
|
||||||
|
|
||||||
core.info(`Checking commit: ${payload.repository.html_url}/commit/${commit.sha}`)
|
|
||||||
|
|
||||||
const associatedClosedPREdges = response.resource.associatedPullRequests.edges.filter(
|
|
||||||
(e) => e.node.state === 'MERGED',
|
|
||||||
)
|
|
||||||
|
|
||||||
if (associatedClosedPREdges.length) {
|
|
||||||
core.info(
|
|
||||||
` Associated Merged PRs:\n ${associatedClosedPREdges.map((pr) => `${payload.repository.html_url}/pull/${pr.node.number}`).join('\n ')}`,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
core.info(' No associated merged PRs')
|
|
||||||
}
|
|
||||||
|
|
||||||
const html = [
|
|
||||||
response.resource.messageHeadlineHTML,
|
|
||||||
response.resource.messageBodyHTML,
|
|
||||||
...associatedClosedPREdges.map((pr) => pr.node.bodyHTML),
|
|
||||||
].join(' ')
|
|
||||||
|
|
||||||
for (const match of html.matchAll(closesMatcher)) {
|
|
||||||
const [, num] = match
|
|
||||||
linkedIssuesPrs.add(parseInt(num, 10))
|
|
||||||
core.info(
|
|
||||||
` Linked issue/PR from closesMatcher: ${payload.repository.html_url}/pull/${num}`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.resource.associatedPullRequests.pageInfo.hasNextPage) {
|
|
||||||
core.warning(`Too many PRs associated with ${commit.sha}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const seen = new Set<number>()
|
|
||||||
for (const associatedPR of associatedClosedPREdges) {
|
|
||||||
if (associatedPR.node.timelineItems.pageInfo.hasNextPage) {
|
|
||||||
core.warning(`Too many links for #${associatedPR.node.number}`)
|
|
||||||
}
|
|
||||||
if (associatedPR.node.labels.pageInfo.hasNextPage) {
|
|
||||||
core.warning(`Too many labels for #${associatedPR.node.number}`)
|
|
||||||
}
|
|
||||||
// a skip labels is present on this PR
|
|
||||||
if (
|
|
||||||
skipLabels?.some((l) => associatedPR.node.labels.nodes.some(({ name }) => name === l))
|
|
||||||
) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
linkedIssuesPrs.add(associatedPR.node.number)
|
|
||||||
core.info(
|
|
||||||
` Linked issue/PR from associated PR: ${payload.repository.html_url}/pull/${associatedPR.node.number}`,
|
|
||||||
)
|
|
||||||
|
|
||||||
// These are sorted by creation date in ascending order. The latest event for a given issue/PR is all we need
|
|
||||||
// ignore links that aren't part of this repo
|
|
||||||
const links = associatedPR.node.timelineItems.nodes
|
|
||||||
.filter((node) => !node.isCrossRepository)
|
|
||||||
.reverse()
|
|
||||||
for (const link of links) {
|
|
||||||
if (seen.has(link.subject.number)) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (link.__typename == 'ConnectedEvent') {
|
|
||||||
linkedIssuesPrs.add(link.subject.number)
|
|
||||||
core.info(
|
|
||||||
`Linked issue/PR from connected event: ${payload.repository.html_url}/pull/${link.subject.number}`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
seen.add(link.subject.number)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
core.info(
|
|
||||||
`Final issues/PRs to be commented on: \n${Array.from(linkedIssuesPrs)
|
|
||||||
.map((num) => ` ${payload.repository.html_url}/pull/${num}`)
|
|
||||||
.join('\n')}`,
|
|
||||||
)
|
|
||||||
|
|
||||||
const requests: Array<Promise<unknown>> = []
|
|
||||||
for (const issueNumber of linkedIssuesPrs) {
|
|
||||||
const baseRequest = {
|
|
||||||
...github.context.repo,
|
|
||||||
issue_number: issueNumber,
|
|
||||||
}
|
|
||||||
if (comment) {
|
|
||||||
const commentRequest = {
|
|
||||||
...baseRequest,
|
|
||||||
body: comment,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if issue is locked or not
|
|
||||||
const { data: issue } = await octokit.rest.issues.get(baseRequest)
|
|
||||||
|
|
||||||
let createCommentPromise: () => Promise<void>
|
|
||||||
if (!issue.locked) {
|
|
||||||
createCommentPromise = async () => {
|
|
||||||
try {
|
|
||||||
await octokit.rest.issues.createComment(commentRequest)
|
|
||||||
} catch (error) {
|
|
||||||
core.error(error as Error)
|
|
||||||
core.error(
|
|
||||||
`Failed to comment on issue/PR: ${issueNumber}. ${payload.repository.html_url}/pull/${issueNumber}`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
core.info(
|
|
||||||
`Issue/PR is locked: ${issueNumber}. Unlocking, commenting, and re-locking. ${payload.repository.html_url}/pull/${issueNumber}`,
|
|
||||||
)
|
|
||||||
createCommentPromise = async () => {
|
|
||||||
try {
|
|
||||||
core.debug(`Unlocking issue/PR: ${issueNumber}`)
|
|
||||||
await octokit.rest.issues.unlock(baseRequest)
|
|
||||||
core.debug(`Commenting on issue/PR: ${issueNumber}`)
|
|
||||||
await octokit.rest.issues.createComment(commentRequest)
|
|
||||||
core.debug(`Re-locking issue/PR: ${issueNumber}`)
|
|
||||||
await octokit.rest.issues.lock(baseRequest)
|
|
||||||
} catch (error) {
|
|
||||||
core.error(error as Error)
|
|
||||||
core.error(
|
|
||||||
`Failed to unlock, comment, and re-lock issue/PR: ${issueNumber}. ${payload.repository.html_url}/pull/${issueNumber}`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
requests.push(createCommentPromise())
|
|
||||||
}
|
|
||||||
if (labels) {
|
|
||||||
const request = {
|
|
||||||
...baseRequest,
|
|
||||||
labels,
|
|
||||||
}
|
|
||||||
// core.info(JSON.stringify(request, null, 2))
|
|
||||||
requests.push(octokit.rest.issues.addLabels(request))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await Promise.all(requests)
|
|
||||||
} catch (error) {
|
|
||||||
core.error(error as Error)
|
|
||||||
core.setFailed((error as Error).message)
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
15
.github/actions/release-commenter/tsconfig.json
vendored
15
.github/actions/release-commenter/tsconfig.json
vendored
@@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "es5",
|
|
||||||
"lib": ["es2020.string"],
|
|
||||||
"noEmit": true,
|
|
||||||
"strict": true,
|
|
||||||
"noUnusedLocals": true,
|
|
||||||
"noImplicitReturns": true,
|
|
||||||
"noFallthroughCasesInSwitch": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"downlevelIteration": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
},
|
|
||||||
"exclude": ["src/**/*.test.ts"]
|
|
||||||
}
|
|
||||||
48
.github/actions/setup/action.yml
vendored
48
.github/actions/setup/action.yml
vendored
@@ -1,48 +0,0 @@
|
|||||||
name: Setup node and pnpm
|
|
||||||
description: Configure the Node.js and pnpm versions
|
|
||||||
|
|
||||||
inputs:
|
|
||||||
node-version:
|
|
||||||
description: 'The Node.js version to use'
|
|
||||||
required: true
|
|
||||||
default: 22.6.2
|
|
||||||
pnpm-version:
|
|
||||||
description: 'The pnpm version to use'
|
|
||||||
required: true
|
|
||||||
default: 9.7.1
|
|
||||||
|
|
||||||
runs:
|
|
||||||
using: composite
|
|
||||||
steps:
|
|
||||||
# https://github.com/actions/virtual-environments/issues/1187
|
|
||||||
- name: tune linux network
|
|
||||||
shell: bash
|
|
||||||
run: sudo ethtool -K eth0 tx off rx off
|
|
||||||
|
|
||||||
- name: Setup Node@${{ inputs.node-version }}
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: ${{ inputs.node-version }}
|
|
||||||
|
|
||||||
- name: Install pnpm
|
|
||||||
uses: pnpm/action-setup@v4
|
|
||||||
with:
|
|
||||||
version: ${{ inputs.pnpm-version }}
|
|
||||||
run_install: false
|
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Setup pnpm cache
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ${{ env.STORE_PATH }}
|
|
||||||
key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
||||||
restore-keys: |
|
|
||||||
pnpm-store-
|
|
||||||
pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
||||||
|
|
||||||
- shell: bash
|
|
||||||
run: pnpm install
|
|
||||||
22
.github/actions/triage/LICENSE
vendored
22
.github/actions/triage/LICENSE
vendored
@@ -1,22 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2024 Payload <info@payloadcms.com>. All modification and additions are copyright of Payload.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Original license:
|
|
||||||
ISC License
|
|
||||||
|
|
||||||
Copyright (c) 2023, Balázs Orbán
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
|
||||||
copyright notice and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
21
.github/actions/triage/README.md
vendored
21
.github/actions/triage/README.md
vendored
@@ -1,21 +0,0 @@
|
|||||||
# Triage
|
|
||||||
|
|
||||||
Modified version of https://github.com/balazsorban44/nissuer
|
|
||||||
|
|
||||||
## Modifications
|
|
||||||
|
|
||||||
- Port to TypeScript
|
|
||||||
- Remove issue locking
|
|
||||||
- Remove reproduction blocklist
|
|
||||||
- Uses `@vercel/ncc` for packaging
|
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
> [!IMPORTANT]
|
|
||||||
> Whenever a modification is made to the action, the action built to `dist` must be committed to the repository.
|
|
||||||
|
|
||||||
This is done by running:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
pnpm build
|
|
||||||
```
|
|
||||||
40
.github/actions/triage/action.yml
vendored
40
.github/actions/triage/action.yml
vendored
@@ -1,40 +0,0 @@
|
|||||||
name: Triage
|
|
||||||
description: Initial triage for issues
|
|
||||||
|
|
||||||
inputs:
|
|
||||||
reproduction-comment:
|
|
||||||
description: 'Either a string or a path to a .md file inside the repository. Example: ".github/invalid-reproduction.md"'
|
|
||||||
default: '.github/invalid-reproduction.md'
|
|
||||||
reproduction-hosts:
|
|
||||||
description: 'Comma-separated list of hostnames that are allowed for reproductions. Example: "github.com,codesandbox.io"'
|
|
||||||
default: github.com
|
|
||||||
reproduction-invalid-label:
|
|
||||||
description: 'Label to apply to issues without a valid reproduction. Example: "invalid-reproduction"'
|
|
||||||
default: 'invalid-reproduction'
|
|
||||||
reproduction-issue-labels:
|
|
||||||
description: 'Comma-separated list of issue labels. If configured, only verify reproduction URLs of issues with one of these labels present. Adding a comma at the end will handle non-labeled issues as invalid. Example: "bug,", will consider issues with the label "bug" or no label.'
|
|
||||||
default: ''
|
|
||||||
reproduction-link-section:
|
|
||||||
description: 'A regular expression string with "(.*)" matching a valid URL in the issue body. The result is trimmed. Example: "### Link to reproduction(.*)### To reproduce"'
|
|
||||||
default: '### Link to reproduction(.*)### To reproduce'
|
|
||||||
tag-only:
|
|
||||||
description: Log and tag only. Do not perform closing or commenting actions.
|
|
||||||
default: false
|
|
||||||
|
|
||||||
runs:
|
|
||||||
using: 'composite'
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
if: ${{ github.event_name != 'pull_request' }}
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Run action
|
|
||||||
run: node ${{ github.action_path }}/dist/index.js
|
|
||||||
shell: sh
|
|
||||||
# https://github.com/actions/runner/issues/665#issuecomment-676581170
|
|
||||||
env:
|
|
||||||
'INPUT_REPRODUCTION_COMMENT': ${{inputs.reproduction-comment}}
|
|
||||||
'INPUT_REPRODUCTION_HOSTS': ${{inputs.reproduction-hosts}}
|
|
||||||
'INPUT_REPRODUCTION_INVALID_LABEL': ${{inputs.reproduction-invalid-label}}
|
|
||||||
'INPUT_REPRODUCTION_ISSUE_LABELS': ${{inputs.reproduction-issue-labels}}
|
|
||||||
'INPUT_REPRODUCTION_LINK_SECTION': ${{inputs.reproduction-link-section}}
|
|
||||||
'INPUT_TAG_ONLY': ${{inputs.tag-only}}
|
|
||||||
34048
.github/actions/triage/dist/index.js
vendored
34048
.github/actions/triage/dist/index.js
vendored
File diff suppressed because one or more lines are too long
7
.github/actions/triage/jest.config.js
vendored
7
.github/actions/triage/jest.config.js
vendored
@@ -1,7 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
testEnvironment: 'node',
|
|
||||||
testPathIgnorePatterns: ['/node_modules/', '<rootDir>/dist/'],
|
|
||||||
transform: {
|
|
||||||
'^.+\\.(t|j)sx?$': ['@swc/jest'],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
34
.github/actions/triage/package.json
vendored
34
.github/actions/triage/package.json
vendored
@@ -1,34 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "triage",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"private": true,
|
|
||||||
"description": "GitHub Action to triage new issues",
|
|
||||||
"license": "MIT",
|
|
||||||
"main": "dist/index.js",
|
|
||||||
"scripts": {
|
|
||||||
"build": "pnpm build:typecheck && pnpm build:ncc",
|
|
||||||
"build:ncc": "ncc build src/index.ts -t -o dist",
|
|
||||||
"build:typecheck": "tsc",
|
|
||||||
"clean": "rimraf dist",
|
|
||||||
"test": "jest"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@actions/core": "^1.3.0",
|
|
||||||
"@actions/github": "^5.0.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@octokit/webhooks-types": "^7.5.1",
|
|
||||||
"@swc/jest": "^0.2.36",
|
|
||||||
"@types/jest": "^27.5.2",
|
|
||||||
"@types/node": "^20.16.5",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^4.33.0",
|
|
||||||
"@typescript-eslint/parser": "^4.33.0",
|
|
||||||
"@vercel/ncc": "0.38.1",
|
|
||||||
"concurrently": "^8.2.2",
|
|
||||||
"eslint": "^7.32.0",
|
|
||||||
"jest": "^29.7.0",
|
|
||||||
"prettier": "^3.3.3",
|
|
||||||
"ts-jest": "^26.5.6",
|
|
||||||
"typescript": "^4.9.5"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
5419
.github/actions/triage/pnpm-lock.yaml
generated
vendored
5419
.github/actions/triage/pnpm-lock.yaml
generated
vendored
File diff suppressed because it is too large
Load Diff
195
.github/actions/triage/src/index.ts
vendored
195
.github/actions/triage/src/index.ts
vendored
@@ -1,195 +0,0 @@
|
|||||||
import { debug, error, getBooleanInput, getInput, info, setFailed } from '@actions/core'
|
|
||||||
|
|
||||||
import { context, getOctokit } from '@actions/github'
|
|
||||||
import { readFile, access } from 'node:fs/promises'
|
|
||||||
import { join } from 'node:path'
|
|
||||||
|
|
||||||
// Ensure GITHUB_TOKEN and GITHUB_WORKSPACE are present
|
|
||||||
if (!process.env.GITHUB_TOKEN) throw new TypeError('No GITHUB_TOKEN provided')
|
|
||||||
if (!process.env.GITHUB_WORKSPACE) throw new TypeError('Not a GitHub workspace')
|
|
||||||
|
|
||||||
// Define the configuration object
|
|
||||||
interface Config {
|
|
||||||
invalidLink: {
|
|
||||||
comment: string
|
|
||||||
bugLabels: string[]
|
|
||||||
hosts: string[]
|
|
||||||
label: string
|
|
||||||
linkSection: string
|
|
||||||
}
|
|
||||||
tagOnly: boolean
|
|
||||||
token: string
|
|
||||||
workspace: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const config: Config = {
|
|
||||||
invalidLink: {
|
|
||||||
comment: getInput('reproduction_comment') || '.github/invalid-reproduction.md',
|
|
||||||
bugLabels: getInput('reproduction_issue_labels')
|
|
||||||
.split(',')
|
|
||||||
.map((l) => l.trim()),
|
|
||||||
hosts: (getInput('reproduction_hosts') || 'github.com').split(',').map((h) => h.trim()),
|
|
||||||
label: getInput('reproduction_invalid_label') || 'invalid-reproduction',
|
|
||||||
linkSection:
|
|
||||||
getInput('reproduction_link_section') || '### Link to reproduction(.*)### To reproduce',
|
|
||||||
},
|
|
||||||
tagOnly: getBooleanOrUndefined('tag_only') || false,
|
|
||||||
token: process.env.GITHUB_TOKEN,
|
|
||||||
workspace: process.env.GITHUB_WORKSPACE,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt to parse JSON, return parsed object or error
|
|
||||||
function tryParse(json: string): Record<string, unknown> {
|
|
||||||
try {
|
|
||||||
return JSON.parse(json)
|
|
||||||
} catch (e) {
|
|
||||||
setFailed(`Could not parse JSON: ${e instanceof Error ? e.message : e}`)
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieves a boolean input or undefined based on environment variables
|
|
||||||
function getBooleanOrUndefined(value: string): boolean | undefined {
|
|
||||||
const variable = process.env[`INPUT_${value.toUpperCase()}`]
|
|
||||||
return variable === undefined || variable === '' ? undefined : getBooleanInput(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the appropriate label match type
|
|
||||||
function getLabelMatch(value: string | undefined): 'name' | 'description' {
|
|
||||||
return value === 'name' ? 'name' : 'description'
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to check if an issue contains a valid reproduction link
|
|
||||||
async function checkValidReproduction(): Promise<void> {
|
|
||||||
const { issue, action } = context.payload as {
|
|
||||||
issue: { number: number; body: string; labels: { name: string }[] } | undefined
|
|
||||||
action: string
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action !== 'opened' || !issue?.body) return
|
|
||||||
|
|
||||||
const labels = issue.labels.map((l) => l.name)
|
|
||||||
|
|
||||||
const issueMatchingLabel =
|
|
||||||
labels.length &&
|
|
||||||
config.invalidLink.bugLabels.length &&
|
|
||||||
labels.some((l) => config.invalidLink.bugLabels.includes(l))
|
|
||||||
|
|
||||||
if (!issueMatchingLabel) {
|
|
||||||
info(
|
|
||||||
`Issue #${issue.number} does not match required labels: ${config.invalidLink.bugLabels.join(', ')}`,
|
|
||||||
)
|
|
||||||
info(`Issue labels: ${labels.join(', ')}`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
info(`Issue #${issue.number} labels: ${labels.join(', ')}`)
|
|
||||||
|
|
||||||
const { rest: client } = getOctokit(config.token)
|
|
||||||
const common = { ...context.repo, issue_number: issue.number }
|
|
||||||
|
|
||||||
const labelsToRemove = labels.filter((l) => config.invalidLink.bugLabels.includes(l))
|
|
||||||
|
|
||||||
if (await isValidReproduction(issue.body)) {
|
|
||||||
await Promise.all(
|
|
||||||
labelsToRemove.map((label) => client.issues.removeLabel({ ...common, name: label })),
|
|
||||||
)
|
|
||||||
|
|
||||||
return info(`Issue #${issue.number} contains a valid reproduction 💚`)
|
|
||||||
}
|
|
||||||
|
|
||||||
info(`Invalid reproduction, issue will be closed/labeled/commented...`)
|
|
||||||
|
|
||||||
// Adjust labels
|
|
||||||
await Promise.all(
|
|
||||||
labelsToRemove.map((label) => client.issues.removeLabel({ ...common, name: label })),
|
|
||||||
)
|
|
||||||
info(`Issue #${issue.number} - validate label removed`)
|
|
||||||
await client.issues.addLabels({ ...common, labels: [config.invalidLink.label] })
|
|
||||||
info(`Issue #${issue.number} - labeled`)
|
|
||||||
|
|
||||||
// If tagOnly, do not close or comment
|
|
||||||
if (config.tagOnly) {
|
|
||||||
info('Tag-only enabled, no closing/commenting actions taken')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform closing and commenting actions
|
|
||||||
await client.issues.update({ ...common, state: 'closed' })
|
|
||||||
info(`Issue #${issue.number} - closed`)
|
|
||||||
|
|
||||||
const comment = join(config.workspace, config.invalidLink.comment)
|
|
||||||
await client.issues.createComment({ ...common, body: await getCommentBody(comment) })
|
|
||||||
info(`Issue #${issue.number} - commented`)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if an issue contains a valid/accessible link to a reproduction.
|
|
||||||
*
|
|
||||||
* Returns `true` if the link is valid.
|
|
||||||
* @param body - The body content of the issue
|
|
||||||
*/
|
|
||||||
async function isValidReproduction(body: string): Promise<boolean> {
|
|
||||||
const linkSectionRe = new RegExp(config.invalidLink.linkSection, 'is')
|
|
||||||
const link = body.match(linkSectionRe)?.[1]?.trim()
|
|
||||||
|
|
||||||
if (!link) {
|
|
||||||
info('Missing link')
|
|
||||||
info(`Link section regex: ${linkSectionRe}`)
|
|
||||||
info(`Link section: ${body}`)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
info(`Checking validity of link: ${link}`)
|
|
||||||
|
|
||||||
if (!URL.canParse(link)) {
|
|
||||||
info(`Invalid URL: ${link}`)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = new URL(link)
|
|
||||||
|
|
||||||
if (!config.invalidLink.hosts.includes(url.hostname)) {
|
|
||||||
info('Link did not match allowed reproduction hosts')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Verify that the link can be accessed
|
|
||||||
const response = await fetch(link)
|
|
||||||
const isOk = response.status < 400 || response.status >= 500
|
|
||||||
|
|
||||||
info(`Link status: ${response.status}`)
|
|
||||||
if (!isOk) {
|
|
||||||
info(`Link returned status ${response.status}`)
|
|
||||||
}
|
|
||||||
return isOk
|
|
||||||
} catch (error) {
|
|
||||||
info(`Error fetching link: ${(error as Error).message}`)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return either a file's content or a string
|
|
||||||
* @param {string} pathOrComment
|
|
||||||
*/
|
|
||||||
async function getCommentBody(pathOrComment: string) {
|
|
||||||
try {
|
|
||||||
await access(pathOrComment)
|
|
||||||
return await readFile(pathOrComment, 'utf8')
|
|
||||||
} catch (error: any) {
|
|
||||||
if (error.code === 'ENOENT') return pathOrComment
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function run() {
|
|
||||||
const { token, workspace, ...safeConfig } = config
|
|
||||||
info('Configuration:')
|
|
||||||
info(JSON.stringify(safeConfig, null, 2))
|
|
||||||
|
|
||||||
await checkValidReproduction()
|
|
||||||
}
|
|
||||||
|
|
||||||
run().catch(setFailed)
|
|
||||||
15
.github/actions/triage/tsconfig.json
vendored
15
.github/actions/triage/tsconfig.json
vendored
@@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "es5",
|
|
||||||
"lib": ["es2020.string"],
|
|
||||||
"noEmit": true,
|
|
||||||
"strict": true,
|
|
||||||
"noUnusedLocals": false, // Undo this
|
|
||||||
"noImplicitReturns": true,
|
|
||||||
"noFallthroughCasesInSwitch": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"downlevelIteration": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
},
|
|
||||||
"exclude": ["src/**/*.test.ts"]
|
|
||||||
}
|
|
||||||
18
.github/comments/invalid-reproduction.md
vendored
18
.github/comments/invalid-reproduction.md
vendored
@@ -1,18 +0,0 @@
|
|||||||
We cannot recreate the issue with the provided information. **Please add a reproduction in order for us to be able to investigate.**
|
|
||||||
|
|
||||||
### Why was this issue marked with the `invalid-reproduction` label?
|
|
||||||
|
|
||||||
To be able to investigate, we need access to a reproduction to identify what triggered the issue. We prefer a link to a public GitHub repository created with `create-payload-app@beta -t blank` or a forked/branched version of this repository with tests added (more info in the [reproduction-guide](https://github.com/payloadcms/payload/blob/main/.github/reproduction-guide.md)).
|
|
||||||
|
|
||||||
To make sure the issue is resolved as quickly as possible, please make sure that the reproduction is as **minimal** as possible. This means that you should **remove unnecessary code, files, and dependencies** that do not contribute to the issue. Ensure your reproduction does not depend on secrets, 3rd party registries, private dependencies, or any other data that cannot be made public. Avoid a reproduction including a whole monorepo (unless relevant to the issue). The easier it is to reproduce the issue, the quicker we can help.
|
|
||||||
|
|
||||||
Please test your reproduction against the latest version of Payload to make sure your issue has not already been fixed.
|
|
||||||
|
|
||||||
### I added a link, why was it still marked?
|
|
||||||
|
|
||||||
Ensure the link is pointing to a codebase that is accessible (e.g. not a private repository). "[example.com](http://example.com/)", "n/a", "will add later", etc. are not acceptable links -- we need to see a public codebase. See the above section for accepted links.
|
|
||||||
|
|
||||||
### Useful Resources
|
|
||||||
|
|
||||||
- [Reproduction Guide](https://github.com/payloadcms/payload/blob/main/.github/reproduction-guide.md)
|
|
||||||
- [Contributing to Payload](https://www.youtube.com/watch?v=08Qa3ggR9rw)
|
|
||||||
74
.github/dependabot.yml
vendored
74
.github/dependabot.yml
vendored
@@ -1,74 +0,0 @@
|
|||||||
# docs: https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
|
||||||
|
|
||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: github-actions
|
|
||||||
directories:
|
|
||||||
- /
|
|
||||||
- /.github/workflows
|
|
||||||
- /.github/actions/* # Not working until resolved: https://github.com/dependabot/dependabot-core/issues/6345
|
|
||||||
- /.github/actions/setup
|
|
||||||
target-branch: beta
|
|
||||||
schedule:
|
|
||||||
interval: monthly
|
|
||||||
timezone: America/Detroit
|
|
||||||
time: '06:00'
|
|
||||||
groups:
|
|
||||||
github_actions:
|
|
||||||
patterns:
|
|
||||||
- '*'
|
|
||||||
|
|
||||||
- package-ecosystem: npm
|
|
||||||
directory: /
|
|
||||||
target-branch: beta
|
|
||||||
schedule:
|
|
||||||
interval: weekly
|
|
||||||
day: sunday
|
|
||||||
timezone: America/Detroit
|
|
||||||
time: '06:00'
|
|
||||||
commit-message:
|
|
||||||
prefix: 'chore(deps)'
|
|
||||||
labels:
|
|
||||||
- dependencies
|
|
||||||
groups:
|
|
||||||
production-deps:
|
|
||||||
dependency-type: production
|
|
||||||
update-types:
|
|
||||||
- minor
|
|
||||||
- patch
|
|
||||||
patterns:
|
|
||||||
- '*'
|
|
||||||
exclude-patterns:
|
|
||||||
- 'drizzle*'
|
|
||||||
dev-deps:
|
|
||||||
dependency-type: development
|
|
||||||
update-types:
|
|
||||||
- minor
|
|
||||||
- patch
|
|
||||||
patterns:
|
|
||||||
- '*'
|
|
||||||
exclude-patterns:
|
|
||||||
- 'drizzle*'
|
|
||||||
|
|
||||||
# Only bump patch versions for 2.x
|
|
||||||
- package-ecosystem: npm
|
|
||||||
directory: /
|
|
||||||
target-branch: main
|
|
||||||
schedule:
|
|
||||||
interval: weekly
|
|
||||||
day: sunday
|
|
||||||
timezone: America/Detroit
|
|
||||||
time: '06:00'
|
|
||||||
commit-message:
|
|
||||||
prefix: 'chore(deps)'
|
|
||||||
labels:
|
|
||||||
- dependencies
|
|
||||||
groups:
|
|
||||||
production-deps:
|
|
||||||
dependency-type: production
|
|
||||||
update-types:
|
|
||||||
- patch
|
|
||||||
patterns:
|
|
||||||
- '*'
|
|
||||||
exclude-patterns:
|
|
||||||
- 'drizzle*'
|
|
||||||
3976
.github/pnpm-lock.yaml
generated
vendored
3976
.github/pnpm-lock.yaml
generated
vendored
File diff suppressed because it is too large
Load Diff
2
.github/pnpm-workspace.yaml
vendored
2
.github/pnpm-workspace.yaml
vendored
@@ -1,2 +0,0 @@
|
|||||||
packages:
|
|
||||||
- 'actions/*'
|
|
||||||
116
.github/workflows/label-on-change.yml
vendored
116
.github/workflows/label-on-change.yml
vendored
@@ -1,116 +0,0 @@
|
|||||||
name: label-on-change
|
|
||||||
|
|
||||||
on:
|
|
||||||
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target
|
|
||||||
issues:
|
|
||||||
types:
|
|
||||||
- assigned
|
|
||||||
- closed
|
|
||||||
- labeled
|
|
||||||
- reopened
|
|
||||||
|
|
||||||
# TODO: Handle labeling on comment
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
on-labeled-ensure-one-status:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
issues: write
|
|
||||||
# Only run on issue labeled and if label starts with 'status:'
|
|
||||||
if: github.event.action == 'labeled' && startsWith(github.event.label.name, 'status:')
|
|
||||||
steps:
|
|
||||||
- name: Ensure only one status label
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
script: |
|
|
||||||
// Get all labels that start with 'status:' and are not the incoming label
|
|
||||||
const incomingLabelName = context.payload.label.name;
|
|
||||||
const labelNamesToRemove = context.payload.issue.labels
|
|
||||||
.filter(label => label.name.startsWith('status:') && label.name !== incomingLabelName)
|
|
||||||
.map(label => label.name);
|
|
||||||
|
|
||||||
if (!labelNamesToRemove.length) {
|
|
||||||
console.log('No labels to remove');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`Labels to remove: '${labelNamesToRemove}'`);
|
|
||||||
|
|
||||||
// If there is more than one status label, remove all but the incoming label
|
|
||||||
for (const labelName of labelNamesToRemove) {
|
|
||||||
await github.rest.issues.removeLabel({
|
|
||||||
issue_number: context.issue.number,
|
|
||||||
name: labelName,
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
});
|
|
||||||
console.log(`Removed '${labelName}' label`);
|
|
||||||
}
|
|
||||||
|
|
||||||
on-issue-close:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
issues: write
|
|
||||||
if: github.event.action == 'closed'
|
|
||||||
steps:
|
|
||||||
- name: Remove all labels on issue close
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
script: |
|
|
||||||
// Get all labels that start with 'status:' and 'stale'
|
|
||||||
const labelNamesToRemove = context.payload.issue.labels
|
|
||||||
.filter(label => label.name.startsWith('status:') || label.name === 'stale')
|
|
||||||
.map(label => label.name);
|
|
||||||
|
|
||||||
if (!labelNamesToRemove.length) {
|
|
||||||
console.log('No labels to remove');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`Labels to remove: '${labelNamesToRemove}'`);
|
|
||||||
|
|
||||||
for (const labelName of labelNamesToRemove) {
|
|
||||||
await github.rest.issues.removeLabel({
|
|
||||||
issue_number: context.issue.number,
|
|
||||||
name: labelName,
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
});
|
|
||||||
console.log(`Removed '${labelName}' label`);
|
|
||||||
}
|
|
||||||
|
|
||||||
on-issue-reopen:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
issues: write
|
|
||||||
if: github.event.action == 'reopened'
|
|
||||||
steps:
|
|
||||||
- name: Add needs-triage label on issue reopen
|
|
||||||
uses: actions-ecosystem/action-add-labels@v1
|
|
||||||
with:
|
|
||||||
labels: 'status: needs-triage'
|
|
||||||
|
|
||||||
on-issue-assigned:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
issues: write
|
|
||||||
if: >
|
|
||||||
github.event.action == 'assigned' &&
|
|
||||||
contains(github.event.issue.labels.*.name, 'status: needs-triage')
|
|
||||||
steps:
|
|
||||||
- name: Remove needs-triage label on issue assign
|
|
||||||
uses: actions-ecosystem/action-remove-labels@v1
|
|
||||||
with:
|
|
||||||
labels: 'status: needs-triage'
|
|
||||||
|
|
||||||
# on-pr-merge:
|
|
||||||
# runs-on: ubuntu-latest
|
|
||||||
# if: github.event.pull_request.merged == true
|
|
||||||
# steps:
|
|
||||||
|
|
||||||
# on-pr-close:
|
|
||||||
# runs-on: ubuntu-latest
|
|
||||||
# if: github.event_name == 'pull_request_target' && github.event.pull_request.merged == false
|
|
||||||
# steps:
|
|
||||||
26
.github/workflows/lock-issues.yml
vendored
26
.github/workflows/lock-issues.yml
vendored
@@ -1,26 +0,0 @@
|
|||||||
name: lock-issues
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
# Run nightly at 12am EST
|
|
||||||
- cron: '0 4 * * *'
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
issues: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
lock_issues:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Lock issues
|
|
||||||
uses: dessant/lock-threads@v5
|
|
||||||
with:
|
|
||||||
process-only: 'issues'
|
|
||||||
issue-inactive-days: '1'
|
|
||||||
exclude-any-issue-labels: 'status: awaiting-reply'
|
|
||||||
log-output: true
|
|
||||||
issue-comment: >
|
|
||||||
This issue has been automatically locked.
|
|
||||||
|
|
||||||
Please open a new issue if this issue persists with any additional detail.
|
|
||||||
441
.github/workflows/main.yml
vendored
441
.github/workflows/main.yml
vendored
@@ -2,25 +2,9 @@ name: build
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
types:
|
types: [opened, reopened, synchronize]
|
||||||
- opened
|
|
||||||
- reopened
|
|
||||||
- synchronize
|
|
||||||
push:
|
push:
|
||||||
branches:
|
branches: ['main', 'alpha']
|
||||||
- main
|
|
||||||
- beta
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
# <workflow_name>-<branch_name>-<true || commit_sha if branch is protected>
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.ref_protected && github.sha || ''}}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
env:
|
|
||||||
NODE_VERSION: 22.6.0
|
|
||||||
PNPM_VERSION: 9.7.1
|
|
||||||
DO_NOT_TRACK: 1 # Disable Turbopack telemetry
|
|
||||||
NEXT_TELEMETRY_DISABLED: 1 # Disable Next telemetry
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
changes:
|
changes:
|
||||||
@@ -31,10 +15,6 @@ jobs:
|
|||||||
needs_build: ${{ steps.filter.outputs.needs_build }}
|
needs_build: ${{ steps.filter.outputs.needs_build }}
|
||||||
templates: ${{ steps.filter.outputs.templates }}
|
templates: ${{ steps.filter.outputs.templates }}
|
||||||
steps:
|
steps:
|
||||||
# https://github.com/actions/virtual-environments/issues/1187
|
|
||||||
- name: tune linux network
|
|
||||||
run: sudo ethtool -K eth0 tx off rx off
|
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 25
|
fetch-depth: 25
|
||||||
@@ -55,52 +35,7 @@ jobs:
|
|||||||
echo "needs_build: ${{ steps.filter.outputs.needs_build }}"
|
echo "needs_build: ${{ steps.filter.outputs.needs_build }}"
|
||||||
echo "templates: ${{ steps.filter.outputs.templates }}"
|
echo "templates: ${{ steps.filter.outputs.templates }}"
|
||||||
|
|
||||||
lint:
|
core-build:
|
||||||
if: >
|
|
||||||
github.event_name == 'pull_request' && !contains(github.event.pull_request.title, 'no-lint') && !contains(github.event.pull_request.title, 'skip-lint')
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
# https://github.com/actions/virtual-environments/issues/1187
|
|
||||||
- name: tune linux network
|
|
||||||
run: sudo ethtool -K eth0 tx off rx off
|
|
||||||
|
|
||||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
|
||||||
|
|
||||||
- name: Install pnpm
|
|
||||||
uses: pnpm/action-setup@v4
|
|
||||||
with:
|
|
||||||
version: ${{ env.PNPM_VERSION }}
|
|
||||||
run_install: false
|
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Setup pnpm cache
|
|
||||||
uses: actions/cache@v4
|
|
||||||
timeout-minutes: 720
|
|
||||||
with:
|
|
||||||
path: ${{ env.STORE_PATH }}
|
|
||||||
key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
||||||
restore-keys: |
|
|
||||||
pnpm-store-
|
|
||||||
pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
||||||
|
|
||||||
- run: pnpm install
|
|
||||||
- name: Lint staged
|
|
||||||
run: |
|
|
||||||
git diff --name-only --diff-filter=d origin/${GITHUB_BASE_REF}...${GITHUB_SHA}
|
|
||||||
npx lint-staged --diff="origin/${GITHUB_BASE_REF}...${GITHUB_SHA}"
|
|
||||||
|
|
||||||
build:
|
|
||||||
needs: changes
|
needs: changes
|
||||||
if: ${{ needs.changes.outputs.needs_build == 'true' }}
|
if: ${{ needs.changes.outputs.needs_build == 'true' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -110,19 +45,15 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 25
|
fetch-depth: 25
|
||||||
|
|
||||||
# https://github.com/actions/virtual-environments/issues/1187
|
- name: Use Node.js 18
|
||||||
- name: tune linux network
|
|
||||||
run: sudo ethtool -K eth0 tx off rx off
|
|
||||||
|
|
||||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: 18
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v3
|
||||||
with:
|
with:
|
||||||
version: ${{ env.PNPM_VERSION }}
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
- name: Get pnpm store directory
|
||||||
@@ -130,63 +61,65 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Setup pnpm cache
|
- uses: actions/cache@v4
|
||||||
uses: actions/cache@v4
|
name: Setup pnpm cache
|
||||||
timeout-minutes: 720
|
|
||||||
with:
|
with:
|
||||||
path: ${{ env.STORE_PATH }}
|
path: ${{ env.STORE_PATH }}
|
||||||
key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
pnpm-store-
|
${{ runner.os }}-pnpm-store-
|
||||||
pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||||
|
|
||||||
- run: pnpm install
|
- run: pnpm install
|
||||||
- run: pnpm run build:all
|
- run: pnpm run build:core
|
||||||
env:
|
|
||||||
DO_NOT_TRACK: 1 # Disable Turbopack telemetry
|
|
||||||
|
|
||||||
- name: Cache build
|
- name: Cache build
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
timeout-minutes: 10
|
|
||||||
with:
|
with:
|
||||||
path: ./*
|
path: ./*
|
||||||
key: ${{ github.sha }}-${{ github.run_number }}
|
key: ${{ github.sha }}-${{ github.run_number }}
|
||||||
|
|
||||||
tests-unit:
|
plugins-build:
|
||||||
|
needs: changes
|
||||||
|
if: ${{ needs.changes.outputs.needs_build == 'true' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: build
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
# https://github.com/actions/virtual-environments/issues/1187
|
- uses: actions/checkout@v4
|
||||||
- name: tune linux network
|
with:
|
||||||
run: sudo ethtool -K eth0 tx off rx off
|
fetch-depth: 25
|
||||||
|
|
||||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
- name: Use Node.js 18
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: 18
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v3
|
||||||
with:
|
with:
|
||||||
version: ${{ env.PNPM_VERSION }}
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
|
|
||||||
- name: Restore build
|
- name: Get pnpm store directory
|
||||||
uses: actions/cache@v4
|
shell: bash
|
||||||
timeout-minutes: 10
|
run: |
|
||||||
|
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- uses: actions/cache@v4
|
||||||
|
name: Setup pnpm cache
|
||||||
with:
|
with:
|
||||||
path: ./*
|
path: ${{ env.STORE_PATH }}
|
||||||
key: ${{ github.sha }}-${{ github.run_number }}
|
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-pnpm-store-
|
||||||
|
${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||||
|
|
||||||
- name: Unit Tests
|
- run: pnpm install
|
||||||
run: pnpm test:unit
|
- run: pnpm run build:plugins
|
||||||
env:
|
|
||||||
NODE_OPTIONS: --max-old-space-size=8096
|
|
||||||
|
|
||||||
tests-int:
|
tests:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: build
|
needs: core-build
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@@ -196,7 +129,6 @@ jobs:
|
|||||||
- postgres-custom-schema
|
- postgres-custom-schema
|
||||||
- postgres-uuid
|
- postgres-uuid
|
||||||
- supabase
|
- supabase
|
||||||
- sqlite
|
|
||||||
env:
|
env:
|
||||||
POSTGRES_USER: postgres
|
POSTGRES_USER: postgres
|
||||||
POSTGRES_PASSWORD: postgres
|
POSTGRES_PASSWORD: postgres
|
||||||
@@ -207,25 +139,22 @@ jobs:
|
|||||||
AWS_REGION: us-east-1
|
AWS_REGION: us-east-1
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Use Node.js 18
|
||||||
with:
|
|
||||||
fetch-depth: 25
|
|
||||||
# https://github.com/actions/virtual-environments/issues/1187
|
|
||||||
- name: tune linux network
|
|
||||||
run: sudo ethtool -K eth0 tx off rx off
|
|
||||||
|
|
||||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: 18
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v3
|
||||||
with:
|
with:
|
||||||
version: ${{ env.PNPM_VERSION }}
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
|
|
||||||
- run: pnpm install
|
- name: Restore build
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ./*
|
||||||
|
key: ${{ github.sha }}-${{ github.run_number }}
|
||||||
|
|
||||||
- name: Start LocalStack
|
- name: Start LocalStack
|
||||||
run: pnpm docker:start
|
run: pnpm docker:start
|
||||||
@@ -273,7 +202,7 @@ jobs:
|
|||||||
if: matrix.database == 'supabase'
|
if: matrix.database == 'supabase'
|
||||||
|
|
||||||
- name: Integration Tests
|
- name: Integration Tests
|
||||||
run: pnpm test:int
|
run: pnpm test:int --testPathIgnorePatterns=test/fields # Ignore fields tests until reworked
|
||||||
env:
|
env:
|
||||||
NODE_OPTIONS: --max-old-space-size=8096
|
NODE_OPTIONS: --max-old-space-size=8096
|
||||||
PAYLOAD_DATABASE: ${{ matrix.database }}
|
PAYLOAD_DATABASE: ${{ matrix.database }}
|
||||||
@@ -281,7 +210,7 @@ jobs:
|
|||||||
|
|
||||||
tests-e2e:
|
tests-e2e:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: build
|
needs: core-build
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@@ -289,175 +218,75 @@ jobs:
|
|||||||
suite:
|
suite:
|
||||||
- _community
|
- _community
|
||||||
- access-control
|
- access-control
|
||||||
- admin__e2e__1
|
# - admin
|
||||||
- admin__e2e__2
|
|
||||||
- admin-root
|
|
||||||
- auth
|
- auth
|
||||||
- field-error-states
|
# - field-error-states
|
||||||
- fields-relationship
|
# - fields-relationship
|
||||||
- fields
|
# - fields
|
||||||
- fields__collections__Blocks
|
- fields/lexical
|
||||||
- fields__collections__Array
|
|
||||||
- fields__collections__Relationship
|
|
||||||
- fields__collections__RichText
|
|
||||||
- fields__collections__Lexical__e2e__main
|
|
||||||
- fields__collections__Lexical__e2e__blocks
|
|
||||||
- fields__collections__Date
|
|
||||||
- fields__collections__Number
|
|
||||||
- fields__collections__Point
|
|
||||||
- fields__collections__Tabs
|
|
||||||
- fields__collections__Text
|
|
||||||
- fields__collections__Upload
|
|
||||||
- live-preview
|
- live-preview
|
||||||
- localization
|
# - localization
|
||||||
- locked-documents
|
# - plugin-nested-docs
|
||||||
- i18n
|
# - plugin-seo
|
||||||
- plugin-cloud-storage
|
# - refresh-permissions
|
||||||
- plugin-form-builder
|
# - uploads
|
||||||
- plugin-nested-docs
|
# - versions
|
||||||
- plugin-seo
|
|
||||||
- versions
|
|
||||||
- uploads
|
|
||||||
env:
|
|
||||||
SUITE_NAME: ${{ matrix.suite }}
|
|
||||||
steps:
|
|
||||||
# https://github.com/actions/virtual-environments/issues/1187
|
|
||||||
- name: tune linux network
|
|
||||||
run: sudo ethtool -K eth0 tx off rx off
|
|
||||||
|
|
||||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
steps:
|
||||||
|
- name: Use Node.js 18
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: 18
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v3
|
||||||
with:
|
with:
|
||||||
version: ${{ env.PNPM_VERSION }}
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
|
|
||||||
- name: Restore build
|
- name: Restore build
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
timeout-minutes: 10
|
|
||||||
with:
|
with:
|
||||||
path: ./*
|
path: ./*
|
||||||
key: ${{ github.sha }}-${{ github.run_number }}
|
key: ${{ github.sha }}-${{ github.run_number }}
|
||||||
|
|
||||||
- name: Start LocalStack
|
- name: Install Playwright
|
||||||
run: pnpm docker:start
|
run: pnpm exec playwright install
|
||||||
if: ${{ matrix.suite == 'plugin-cloud-storage' }}
|
|
||||||
|
|
||||||
- name: Store Playwright's Version
|
|
||||||
run: |
|
|
||||||
# Extract the version number using a more targeted regex pattern with awk
|
|
||||||
PLAYWRIGHT_VERSION=$(pnpm ls @playwright/test --depth=0 | awk '/@playwright\/test/ {print $2}')
|
|
||||||
echo "Playwright's Version: $PLAYWRIGHT_VERSION"
|
|
||||||
echo "PLAYWRIGHT_VERSION=$PLAYWRIGHT_VERSION" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Cache Playwright Browsers for Playwright's Version
|
|
||||||
id: cache-playwright-browsers
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ~/.cache/ms-playwright
|
|
||||||
key: playwright-browsers-${{ env.PLAYWRIGHT_VERSION }}
|
|
||||||
|
|
||||||
- name: Setup Playwright - Browsers and Dependencies
|
|
||||||
if: steps.cache-playwright-browsers.outputs.cache-hit != 'true'
|
|
||||||
run: pnpm exec playwright install --with-deps chromium
|
|
||||||
|
|
||||||
- name: Setup Playwright - Dependencies-only
|
|
||||||
if: steps.cache-playwright-browsers.outputs.cache-hit == 'true'
|
|
||||||
run: pnpm exec playwright install-deps chromium
|
|
||||||
|
|
||||||
- name: E2E Tests
|
- name: E2E Tests
|
||||||
run: PLAYWRIGHT_JSON_OUTPUT_NAME=results_${{ matrix.suite }}.json pnpm test:e2e:prod:ci ${{ matrix.suite }}
|
uses: nick-fields/retry@v3
|
||||||
env:
|
with:
|
||||||
PLAYWRIGHT_JSON_OUTPUT_NAME: results_${{ matrix.suite }}.json
|
retry_on: error
|
||||||
NEXT_TELEMETRY_DISABLED: 1
|
max_attempts: 2
|
||||||
|
timeout_minutes: 15
|
||||||
|
command: pnpm test:e2e ${{ matrix.suite }}
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
name: test-results-${{ matrix.suite }}
|
name: test-results
|
||||||
path: test/test-results/
|
path: test/test-results/
|
||||||
if-no-files-found: ignore
|
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
|
|
||||||
# Disabled until this is fixed: https://github.com/daun/playwright-report-summary/issues/156
|
|
||||||
# - uses: daun/playwright-report-summary@v3
|
|
||||||
# with:
|
|
||||||
# report-file: results_${{ matrix.suite }}.json
|
|
||||||
# report-tag: ${{ matrix.suite }}
|
|
||||||
# job-summary: true
|
|
||||||
|
|
||||||
app-build-with-packed:
|
|
||||||
if: false # Disable until package resolution in tgzs can be figured out
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: build
|
|
||||||
|
|
||||||
steps:
|
|
||||||
# https://github.com/actions/virtual-environments/issues/1187
|
|
||||||
- name: tune linux network
|
|
||||||
run: sudo ethtool -K eth0 tx off rx off
|
|
||||||
|
|
||||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
|
||||||
|
|
||||||
- name: Install pnpm
|
|
||||||
uses: pnpm/action-setup@v4
|
|
||||||
with:
|
|
||||||
version: ${{ env.PNPM_VERSION }}
|
|
||||||
run_install: false
|
|
||||||
|
|
||||||
- name: Restore build
|
|
||||||
uses: actions/cache@v4
|
|
||||||
timeout-minutes: 10
|
|
||||||
with:
|
|
||||||
path: ./*
|
|
||||||
key: ${{ github.sha }}-${{ github.run_number }}
|
|
||||||
|
|
||||||
- name: Start MongoDB
|
|
||||||
uses: supercharge/mongodb-github-action@1.11.0
|
|
||||||
with:
|
|
||||||
mongodb-version: 6.0
|
|
||||||
|
|
||||||
- name: Pack and build app
|
|
||||||
run: |
|
|
||||||
set -ex
|
|
||||||
pnpm run script:pack --dest templates/blank
|
|
||||||
cd templates/blank
|
|
||||||
cp .env.example .env
|
|
||||||
ls -la
|
|
||||||
pnpm add ./*.tgz --ignore-workspace
|
|
||||||
pnpm install --ignore-workspace --no-frozen-lockfile
|
|
||||||
cat package.json
|
|
||||||
pnpm run build
|
|
||||||
|
|
||||||
tests-type-generation:
|
tests-type-generation:
|
||||||
|
if: false # This should be replaced with gen on a real Payload project
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: build
|
needs: core-build
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
# https://github.com/actions/virtual-environments/issues/1187
|
- name: Use Node.js 18
|
||||||
- name: tune linux network
|
|
||||||
run: sudo ethtool -K eth0 tx off rx off
|
|
||||||
|
|
||||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: 18
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v3
|
||||||
with:
|
with:
|
||||||
version: ${{ env.PNPM_VERSION }}
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
|
|
||||||
- name: Restore build
|
- name: Restore build
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
timeout-minutes: 10
|
|
||||||
with:
|
with:
|
||||||
path: ./*
|
path: ./*
|
||||||
key: ${{ github.sha }}-${{ github.run_number }}
|
key: ${{ github.sha }}-${{ github.run_number }}
|
||||||
@@ -468,6 +297,46 @@ jobs:
|
|||||||
- name: Generate GraphQL schema file
|
- name: Generate GraphQL schema file
|
||||||
run: pnpm dev:generate-graphql-schema graphql-schema-gen
|
run: pnpm dev:generate-graphql-schema graphql-schema-gen
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: core-build
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
pkg:
|
||||||
|
- create-payload-app
|
||||||
|
- plugin-cloud
|
||||||
|
- plugin-cloud-storage
|
||||||
|
- plugin-form-builder
|
||||||
|
- plugin-nested-docs
|
||||||
|
- plugin-search
|
||||||
|
- plugin-sentry
|
||||||
|
- plugin-seo
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Use Node.js 18
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 18
|
||||||
|
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v3
|
||||||
|
with:
|
||||||
|
version: 8
|
||||||
|
run_install: false
|
||||||
|
|
||||||
|
- name: Restore build
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ./*
|
||||||
|
key: ${{ github.sha }}-${{ github.run_number }}
|
||||||
|
|
||||||
|
- name: Build ${{ matrix.pkg }}
|
||||||
|
run: pnpm turbo run build --filter=${{ matrix.pkg }}
|
||||||
|
|
||||||
|
- name: Test ${{ matrix.pkg }}
|
||||||
|
run: pnpm --filter ${{ matrix.pkg }} run test
|
||||||
|
|
||||||
templates:
|
templates:
|
||||||
needs: changes
|
needs: changes
|
||||||
if: false # Disable until templates are updated for 3.0
|
if: false # Disable until templates are updated for 3.0
|
||||||
@@ -481,17 +350,14 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 25
|
fetch-depth: 25
|
||||||
# https://github.com/actions/virtual-environments/issues/1187
|
|
||||||
- name: tune linux network
|
|
||||||
run: sudo ethtool -K eth0 tx off rx off
|
|
||||||
|
|
||||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
- name: Use Node.js 18
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: 18
|
||||||
|
|
||||||
- name: Start MongoDB
|
- name: Start MongoDB
|
||||||
uses: supercharge/mongodb-github-action@1.11.0
|
uses: supercharge/mongodb-github-action@1.10.0
|
||||||
with:
|
with:
|
||||||
mongodb-version: 6.0
|
mongodb-version: 6.0
|
||||||
|
|
||||||
@@ -502,62 +368,3 @@ jobs:
|
|||||||
yarn install
|
yarn install
|
||||||
yarn build
|
yarn build
|
||||||
yarn generate:types
|
yarn generate:types
|
||||||
|
|
||||||
generated-templates:
|
|
||||||
needs: build
|
|
||||||
if: false # Needs to pull in tgz files from build
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
# https://github.com/actions/virtual-environments/issues/1187
|
|
||||||
- name: tune linux network
|
|
||||||
run: sudo ethtool -K eth0 tx off rx off
|
|
||||||
|
|
||||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
|
||||||
|
|
||||||
- name: Install pnpm
|
|
||||||
uses: pnpm/action-setup@v4
|
|
||||||
with:
|
|
||||||
version: ${{ env.PNPM_VERSION }}
|
|
||||||
run_install: false
|
|
||||||
|
|
||||||
- name: Restore build
|
|
||||||
uses: actions/cache@v4
|
|
||||||
timeout-minutes: 10
|
|
||||||
with:
|
|
||||||
path: ./*
|
|
||||||
key: ${{ github.sha }}-${{ github.run_number }}
|
|
||||||
|
|
||||||
- name: Build all generated templates
|
|
||||||
run: pnpm tsx ./scripts/build-generated-templates.ts
|
|
||||||
|
|
||||||
all-green:
|
|
||||||
name: All Green
|
|
||||||
if: always()
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs:
|
|
||||||
- lint
|
|
||||||
- build
|
|
||||||
- tests-unit
|
|
||||||
- tests-int
|
|
||||||
- tests-e2e
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- if: ${{ always() && (contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')) }}
|
|
||||||
run: exit 1
|
|
||||||
|
|
||||||
publish-canary:
|
|
||||||
name: Publish Canary
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs:
|
|
||||||
- all-green
|
|
||||||
|
|
||||||
steps:
|
|
||||||
# debug github.ref output
|
|
||||||
- run: |
|
|
||||||
echo github.ref: ${{ github.ref }}
|
|
||||||
echo isBeta: ${{ github.ref == 'refs/heads/beta' }}
|
|
||||||
echo isMain: ${{ github.ref == 'refs/heads/main' }}
|
|
||||||
|
|||||||
30
.github/workflows/post-release.yml
vendored
30
.github/workflows/post-release.yml
vendored
@@ -1,30 +0,0 @@
|
|||||||
name: post-release
|
|
||||||
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types:
|
|
||||||
- published
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
post_release:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
# Only needed if debugging on a branch other than default
|
|
||||||
# ref: ${{ github.event.release.target_commitish || github.ref }}
|
|
||||||
- uses: ./.github/actions/release-commenter
|
|
||||||
continue-on-error: true
|
|
||||||
env:
|
|
||||||
ACTIONS_STEP_DEBUG: true
|
|
||||||
with:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
tag-filter: 'v\d'
|
|
||||||
|
|
||||||
# Change to blank to disable commenting
|
|
||||||
# comment-template: ''
|
|
||||||
|
|
||||||
comment-template: |
|
|
||||||
🚀 This is included in version {release_link}
|
|
||||||
121
.github/workflows/pr-title.yml
vendored
121
.github/workflows/pr-title.yml
vendored
@@ -1,121 +0,0 @@
|
|||||||
name: pr-title
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types:
|
|
||||||
- opened
|
|
||||||
- edited
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
main:
|
|
||||||
name: lint-pr-title
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: amannn/action-semantic-pull-request@v5
|
|
||||||
id: lint_pr_title
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
with:
|
|
||||||
types: |
|
|
||||||
build
|
|
||||||
chore
|
|
||||||
ci
|
|
||||||
docs
|
|
||||||
feat
|
|
||||||
fix
|
|
||||||
perf
|
|
||||||
refactor
|
|
||||||
revert
|
|
||||||
style
|
|
||||||
test
|
|
||||||
types
|
|
||||||
scopes: |
|
|
||||||
cpa
|
|
||||||
db-\*
|
|
||||||
db-mongodb
|
|
||||||
db-postgres
|
|
||||||
db-vercel-postgres
|
|
||||||
db-sqlite
|
|
||||||
drizzle
|
|
||||||
email-nodemailer
|
|
||||||
eslint
|
|
||||||
graphql
|
|
||||||
live-preview
|
|
||||||
live-preview-react
|
|
||||||
next
|
|
||||||
payload-cloud
|
|
||||||
plugin-cloud
|
|
||||||
plugin-cloud-storage
|
|
||||||
plugin-form-builder
|
|
||||||
plugin-nested-docs
|
|
||||||
plugin-redirects
|
|
||||||
plugin-search
|
|
||||||
plugin-sentry
|
|
||||||
plugin-seo
|
|
||||||
plugin-stripe
|
|
||||||
richtext-\*
|
|
||||||
richtext-lexical
|
|
||||||
richtext-slate
|
|
||||||
storage-\*
|
|
||||||
storage-azure
|
|
||||||
storage-gcs
|
|
||||||
storage-uploadthing
|
|
||||||
storage-vercel-blob
|
|
||||||
storage-s3
|
|
||||||
translations
|
|
||||||
ui
|
|
||||||
templates
|
|
||||||
examples(\/(\w|-)+)?
|
|
||||||
deps
|
|
||||||
|
|
||||||
# Disallow uppercase letters at the beginning of the subject
|
|
||||||
subjectPattern: ^(?![A-Z]).+$
|
|
||||||
subjectPatternError: |
|
|
||||||
The subject "{subject}" found in the pull request title "{title}"
|
|
||||||
didn't match the configured pattern. Please ensure that the subject
|
|
||||||
doesn't start with an uppercase character.
|
|
||||||
|
|
||||||
- uses: marocchino/sticky-pull-request-comment@v2
|
|
||||||
# When the previous steps fails, the workflow would stop. By adding this
|
|
||||||
# condition you can continue the execution with the populated error message.
|
|
||||||
if: always() && (steps.lint_pr_title.outputs.error_message != null)
|
|
||||||
with:
|
|
||||||
header: pr-title-lint-error
|
|
||||||
message: |
|
|
||||||
Pull Request titles must follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/) and have valid scopes.
|
|
||||||
|
|
||||||
${{ steps.lint_pr_title.outputs.error_message }}
|
|
||||||
|
|
||||||
```
|
|
||||||
feat(ui): add Button component
|
|
||||||
^ ^ ^
|
|
||||||
| | |__ Subject
|
|
||||||
| |_______ Scope
|
|
||||||
|____________ Type
|
|
||||||
```
|
|
||||||
|
|
||||||
# Delete a previous comment when the issue has been resolved
|
|
||||||
- if: ${{ steps.lint_pr_title.outputs.error_message == null }}
|
|
||||||
uses: marocchino/sticky-pull-request-comment@v2
|
|
||||||
with:
|
|
||||||
header: pr-title-lint-error
|
|
||||||
delete: true
|
|
||||||
|
|
||||||
label-pr-on-open:
|
|
||||||
name: label-pr-on-open
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.event.action == 'opened'
|
|
||||||
steps:
|
|
||||||
- name: Tag with main branch with v2
|
|
||||||
if: github.event.pull_request.base.ref == 'main'
|
|
||||||
uses: actions-ecosystem/action-add-labels@v1
|
|
||||||
with:
|
|
||||||
labels: v2
|
|
||||||
- name: Tag with beta branch with v3
|
|
||||||
if: github.event.pull_request.base.ref == 'beta'
|
|
||||||
uses: actions-ecosystem/action-add-labels@v1
|
|
||||||
with:
|
|
||||||
labels: v3
|
|
||||||
36
.github/workflows/release-canary.yml
vendored
36
.github/workflows/release-canary.yml
vendored
@@ -1,36 +0,0 @@
|
|||||||
name: release-canary
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
branches:
|
|
||||||
- beta
|
|
||||||
|
|
||||||
env:
|
|
||||||
NODE_VERSION: 22.6.0
|
|
||||||
PNPM_VERSION: 9.7.1
|
|
||||||
DO_NOT_TRACK: 1 # Disable Turbopack telemetry
|
|
||||||
NEXT_TELEMETRY_DISABLED: 1 # Disable Next telemetry
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
release:
|
|
||||||
permissions:
|
|
||||||
id-token: write
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Setup
|
|
||||||
uses: ./.github/actions/setup
|
|
||||||
with:
|
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
|
||||||
pnpm-version: ${{ env.PNPM_VERSION }}
|
|
||||||
- name: Load npm token
|
|
||||||
run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
|
|
||||||
env:
|
|
||||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
||||||
- name: Canary release script
|
|
||||||
# dry run hard-coded to true for testing and no npm token provided
|
|
||||||
run: pnpm tsx ./scripts/publish-canary.ts
|
|
||||||
env:
|
|
||||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
||||||
NPM_CONFIG_PROVENANCE: true
|
|
||||||
42
.github/workflows/stale.yml
vendored
42
.github/workflows/stale.yml
vendored
@@ -1,42 +0,0 @@
|
|||||||
name: stale
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
stale:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
issues: write
|
|
||||||
pull-requests: write
|
|
||||||
steps:
|
|
||||||
- uses: actions/stale@v9
|
|
||||||
id: stale
|
|
||||||
with:
|
|
||||||
debug-only: true
|
|
||||||
days-before-stale: 90
|
|
||||||
days-before-close: 7
|
|
||||||
ascending: true
|
|
||||||
operations-per-run: 300
|
|
||||||
|
|
||||||
# Ignore all assigned
|
|
||||||
exempt-all-assignees: false
|
|
||||||
|
|
||||||
# Issues
|
|
||||||
stale-issue-label: 'stale'
|
|
||||||
exempt-issue-labels: 'blocked,must,should,keep,created-by: Payload team,created-by: Contributor'
|
|
||||||
stale-issue-message: >
|
|
||||||
This issue has been marked as stale due to lack of activity. To keep the ticket open, please indicate that it is still relevant in a comment below.
|
|
||||||
close-issue-message: >
|
|
||||||
This issue was automatically closed due to lack of activity.
|
|
||||||
|
|
||||||
# Pull Requests
|
|
||||||
stale-pr-label: 'stale'
|
|
||||||
exempt-pr-labels: 'blocked,must,should,keep,created-by: Payload team,created-by: Contributor'
|
|
||||||
stale-pr-message: >
|
|
||||||
This PR is stale due to lack of activity. To keep the PR open, please indicate that it is still relevant in a comment below.
|
|
||||||
close-pr-message: >
|
|
||||||
This pull request was automatically closed due to lack of activity.
|
|
||||||
|
|
||||||
- name: Print outputs
|
|
||||||
run: echo ${{ format('{0},{1}', toJSON(steps.stale.outputs.staled-issues-prs), toJSON(steps.stale.outputs.closed-issues-prs)) }}
|
|
||||||
102
.github/workflows/triage.yml
vendored
102
.github/workflows/triage.yml
vendored
@@ -1,102 +0,0 @@
|
|||||||
name: triage
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types:
|
|
||||||
- opened
|
|
||||||
issues:
|
|
||||||
types:
|
|
||||||
- opened
|
|
||||||
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
issues: write
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
debug-context:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: View context attributes
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
script: console.log({ context })
|
|
||||||
|
|
||||||
label-created-by:
|
|
||||||
name: label-on-open
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Tag with 'created-by'
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
if: github.event.action == 'opened'
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
script: |
|
|
||||||
const payloadTeamUsernames = [
|
|
||||||
'denolfe',
|
|
||||||
'jmikrut',
|
|
||||||
'DanRibbens',
|
|
||||||
'jacobsfletch',
|
|
||||||
'JarrodMFlesch',
|
|
||||||
'AlessioGr',
|
|
||||||
'JessChowdhury',
|
|
||||||
'kendelljoseph',
|
|
||||||
'PatrikKozak',
|
|
||||||
'tylandavis',
|
|
||||||
'paulpopus',
|
|
||||||
'r1tsuu',
|
|
||||||
'GermanJablo',
|
|
||||||
];
|
|
||||||
|
|
||||||
const type = context.payload.pull_request ? 'pull_request' : 'issue';
|
|
||||||
|
|
||||||
const isTeamMember = payloadTeamUsernames
|
|
||||||
.map(n => n.toLowerCase())
|
|
||||||
.includes(context.payload[type].user.login.toLowerCase());
|
|
||||||
|
|
||||||
if (isTeamMember) {
|
|
||||||
github.rest.issues.addLabels({
|
|
||||||
issue_number: context.issue.number,
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
labels: ['created-by: Payload team'],
|
|
||||||
});
|
|
||||||
console.log(`Added 'created-by: Payload team' label`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const association = context.payload[type].author_association;
|
|
||||||
let label = ''
|
|
||||||
if (association === 'MEMBER' || association === 'OWNER') {
|
|
||||||
label = 'created-by: Payload team';
|
|
||||||
} else if (association === 'CONTRIBUTOR') {
|
|
||||||
label = 'created-by: Contributor';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!label) return;
|
|
||||||
|
|
||||||
github.rest.issues.addLabels({
|
|
||||||
issue_number: context.issue.number,
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
labels: [label],
|
|
||||||
});
|
|
||||||
console.log(`Added '${label}' label.`);
|
|
||||||
|
|
||||||
triage:
|
|
||||||
name: initial-triage
|
|
||||||
if: github.event_name == 'issues'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- uses: ./.github/actions/triage
|
|
||||||
with:
|
|
||||||
reproduction-comment: '.github/comments/invalid-reproduction.md'
|
|
||||||
reproduction-link-section: '### Link to the code that reproduces this issue(.*)### Reproduction Steps'
|
|
||||||
reproduction-issue-labels: 'validate-reproduction'
|
|
||||||
tag-only: 'true'
|
|
||||||
30
.gitignore
vendored
30
.gitignore
vendored
@@ -3,12 +3,7 @@ package-lock.json
|
|||||||
dist
|
dist
|
||||||
/.idea/*
|
/.idea/*
|
||||||
!/.idea/runConfigurations
|
!/.idea/runConfigurations
|
||||||
!/.idea/payload.iml
|
|
||||||
|
|
||||||
# Custom actions
|
|
||||||
!.github/actions/**/dist
|
|
||||||
|
|
||||||
test/packed
|
|
||||||
test-results
|
test-results
|
||||||
.devcontainer
|
.devcontainer
|
||||||
.localstack
|
.localstack
|
||||||
@@ -16,20 +11,10 @@ test-results
|
|||||||
.localstack
|
.localstack
|
||||||
.turbo
|
.turbo
|
||||||
|
|
||||||
meta_client.json
|
|
||||||
meta_server.json
|
|
||||||
meta_index.json
|
|
||||||
meta_shared.json
|
|
||||||
|
|
||||||
.turbo
|
.turbo
|
||||||
|
|
||||||
# Ignore test directory media folder/files
|
# Ignore test directory media folder/files
|
||||||
/media
|
/media
|
||||||
test/media
|
|
||||||
*payloadtests.db
|
|
||||||
*payloadtests.db-journal
|
|
||||||
*payloadtests.db-shm
|
|
||||||
*payloadtests.db-wal
|
|
||||||
/versions
|
/versions
|
||||||
|
|
||||||
# Created by https://www.toptal.com/developers/gitignore/api/node,macos,windows,webstorm,sublimetext,visualstudiocode
|
# Created by https://www.toptal.com/developers/gitignore/api/node,macos,windows,webstorm,sublimetext,visualstudiocode
|
||||||
@@ -157,7 +142,6 @@ out
|
|||||||
# Nuxt.js build / generate output
|
# Nuxt.js build / generate output
|
||||||
.nuxt
|
.nuxt
|
||||||
dist
|
dist
|
||||||
dist_optimized
|
|
||||||
|
|
||||||
# Gatsby files
|
# Gatsby files
|
||||||
.cache/
|
.cache/
|
||||||
@@ -304,16 +288,4 @@ $RECYCLE.BIN/
|
|||||||
# End of https://www.toptal.com/developers/gitignore/api/node,macos,windows,webstorm,sublimetext,visualstudiocode
|
# End of https://www.toptal.com/developers/gitignore/api/node,macos,windows,webstorm,sublimetext,visualstudiocode
|
||||||
|
|
||||||
/build
|
/build
|
||||||
.swc
|
.swc
|
||||||
app/(payload)/admin/importMap.js
|
|
||||||
test/live-preview/app/(payload)/admin/importMap.js
|
|
||||||
/test/live-preview/app/(payload)/admin/importMap.js
|
|
||||||
test/admin-root/app/(payload)/admin/importMap.js
|
|
||||||
/test/admin-root/app/(payload)/admin/importMap.js
|
|
||||||
test/app/(payload)/admin/importMap.js
|
|
||||||
/test/app/(payload)/admin/importMap.js
|
|
||||||
test/pnpm-lock.yaml
|
|
||||||
test/databaseAdapter.js
|
|
||||||
/filename-compound-index
|
|
||||||
/media-with-relation-preview
|
|
||||||
/media-without-relation-preview
|
|
||||||
@@ -1 +1,4 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname -- "$0")/_/husky.sh"
|
||||||
|
|
||||||
pnpm run lint-staged --quiet
|
pnpm run lint-staged --quiet
|
||||||
|
|||||||
87
.idea/payload.iml
generated
87
.idea/payload.iml
generated
@@ -1,87 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<module type="WEB_MODULE" version="4">
|
|
||||||
<component name="NewModuleRootManager">
|
|
||||||
<content url="file://$MODULE_DIR$">
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/payload/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/payload/components" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/payload/dist" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/.swc" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/examples" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/media" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/create-payload-app/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/create-payload-app/dist" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/db-mongodb/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/db-mongodb/dist" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/db-postgres/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/db-postgres/dist" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/graphql/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/graphql/dist" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/live-preview-react/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/live-preview-react/dist" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/live-preview/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/live-preview/dist" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/next/.swc" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/next/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/next/dist" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/payload/fields" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-cloud-storage/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-cloud-storage/dist" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-cloud/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-cloud/dist" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-form-builder/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-nested-docs/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-nested-docs/dist" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-redirects/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-redirects/dist" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-search/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-search/dist" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-sentry/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-seo/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-seo/dist" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-stripe/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/richtext-lexical/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/richtext-lexical/dist" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/templates" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/test/.swc" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/versions" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/richtext-slate/dist" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/richtext-slate/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/email-nodemailer/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/email-nodemailer/dist" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/email-resend/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/email-resend/dist" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/live-preview-vue/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/live-preview-vue/dist" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/payload/.swc" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-form-builder/dist" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-relationship-object-ids/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-relationship-object-ids/dist" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-stripe/dist" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/storage-azure/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/storage-azure/dist" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/storage-gcs/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/storage-gcs/dist" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/storage-s3/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/storage-s3/dist" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/storage-uploadthing/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/storage-uploadthing/dist" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/storage-vercel-blob/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/storage-vercel-blob/dist" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/translations/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/translations/dist" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/ui/.swc" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/ui/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/ui/dist" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/drizzle/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/drizzle/dist" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/db-sqlite/.turbo" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages/db-sqlite/dist" />
|
|
||||||
</content>
|
|
||||||
<orderEntry type="inheritedJdk" />
|
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
|
||||||
</component>
|
|
||||||
</module>
|
|
||||||
13
.idea/runConfigurations/Run_Dev_Fields.xml
generated
13
.idea/runConfigurations/Run_Dev_Fields.xml
generated
@@ -1,13 +1,8 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="Run Dev Fields" type="js.build_tools.npm">
|
<configuration default="false" name="Run Dev Fields" type="NodeJSConfigurationType" application-parameters="--no-deprecation fields" path-to-js-file="test/dev.js" working-dir="$PROJECT_DIR$">
|
||||||
<package-json value="$PROJECT_DIR$/package.json" />
|
<envs>
|
||||||
<command value="run" />
|
<env name="NODE_OPTIONS" value="--no-deprecation" />
|
||||||
<scripts>
|
</envs>
|
||||||
<script value="dev" />
|
|
||||||
</scripts>
|
|
||||||
<arguments value="fields" />
|
|
||||||
<node-interpreter value="project" />
|
|
||||||
<envs />
|
|
||||||
<method v="2" />
|
<method v="2" />
|
||||||
</configuration>
|
</configuration>
|
||||||
</component>
|
</component>
|
||||||
13
.idea/runConfigurations/Run_Dev__community.xml
generated
13
.idea/runConfigurations/Run_Dev__community.xml
generated
@@ -1,13 +1,8 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="Run Dev _community" type="js.build_tools.npm">
|
<configuration default="false" name="Run Dev _community" type="NodeJSConfigurationType" application-parameters="--no-deprecation _community" path-to-js-file="test/dev.js" working-dir="$PROJECT_DIR$">
|
||||||
<package-json value="$PROJECT_DIR$/package.json" />
|
<envs>
|
||||||
<command value="run" />
|
<env name="NODE_OPTIONS" value="--no-deprecation" />
|
||||||
<scripts>
|
</envs>
|
||||||
<script value="dev" />
|
|
||||||
</scripts>
|
|
||||||
<arguments value="_community" />
|
|
||||||
<node-interpreter value="project" />
|
|
||||||
<envs />
|
|
||||||
<method v="2" />
|
<method v="2" />
|
||||||
</configuration>
|
</configuration>
|
||||||
</component>
|
</component>
|
||||||
13
.idea/runConfigurations/Run_Dev_admin.xml
generated
13
.idea/runConfigurations/Run_Dev_admin.xml
generated
@@ -1,13 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="Run Dev admin" type="js.build_tools.npm">
|
|
||||||
<package-json value="$PROJECT_DIR$/package.json" />
|
|
||||||
<command value="run" />
|
|
||||||
<scripts>
|
|
||||||
<script value="dev" />
|
|
||||||
</scripts>
|
|
||||||
<arguments value="admin" />
|
|
||||||
<node-interpreter value="project" />
|
|
||||||
<envs />
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="true" type="JavaScriptTestRunnerJest">
|
|
||||||
<node-interpreter value="project" />
|
|
||||||
<node-options value="--no-deprecation" />
|
|
||||||
<envs />
|
|
||||||
<scope-kind value="ALL" />
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1 +1 @@
|
|||||||
v22.6.0
|
v18.19.1
|
||||||
|
|||||||
3
.npmrc
3
.npmrc
@@ -1,3 +1,2 @@
|
|||||||
symlink=true
|
symlink=true
|
||||||
node-linker=isolated
|
node-linker=isolated # due to a typescript bug, isolated mode requires @types/express-serve-static-core, terser and monaco-editor to be installed https://github.com/microsoft/TypeScript/issues/47663#issuecomment-1519138189 along with two other changes in the code which I've marked with (tsbugisolatedmode) in the code
|
||||||
hoist-workspace-packages=false # the default in pnpm v9 is true, but that can break our runtime dependency checks
|
|
||||||
@@ -10,7 +10,3 @@
|
|||||||
**/temp
|
**/temp
|
||||||
**/docs/**
|
**/docs/**
|
||||||
tsconfig.json
|
tsconfig.json
|
||||||
packages/payload/*.js
|
|
||||||
packages/payload/*.d.ts
|
|
||||||
payload-types.ts
|
|
||||||
tsconfig.tsbuildinfo
|
|
||||||
|
|||||||
9
.swcrc
9
.swcrc
@@ -7,15 +7,6 @@
|
|||||||
"syntax": "typescript",
|
"syntax": "typescript",
|
||||||
"tsx": true,
|
"tsx": true,
|
||||||
"dts": true
|
"dts": true
|
||||||
},
|
|
||||||
"transform": {
|
|
||||||
"react": {
|
|
||||||
"runtime": "automatic",
|
|
||||||
"pragmaFrag": "React.Fragment",
|
|
||||||
"throwIfNamespace": true,
|
|
||||||
"development": false,
|
|
||||||
"useBuiltins": true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"module": {
|
"module": {
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
pnpm 9.7.1
|
|
||||||
nodejs 22.6.0
|
|
||||||
89
.vscode/launch.json
vendored
89
.vscode/launch.json
vendored
@@ -10,66 +10,19 @@
|
|||||||
"cwd": "${workspaceFolder}"
|
"cwd": "${workspaceFolder}"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "pnpm tsx --no-deprecation test/dev.ts _community",
|
"command": "node --no-deprecation test/dev.js fields",
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
"name": "Run Dev Community",
|
"name": "Run Dev Community",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"type": "node-terminal"
|
"type": "node-terminal"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "pnpm tsx --no-deprecation test/dev.ts storage-uploadthing",
|
"command": "pnpm run dev live-preview -- --no-turbo",
|
||||||
"cwd": "${workspaceFolder}",
|
|
||||||
"name": "Uploadthing",
|
|
||||||
"request": "launch",
|
|
||||||
"type": "node-terminal",
|
|
||||||
"envFile": "${workspaceFolder}/test/storage-uploadthing/.env"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"command": "pnpm tsx --no-deprecation test/dev.ts live-preview",
|
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
"name": "Run Dev Live Preview",
|
"name": "Run Dev Live Preview",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"type": "node-terminal"
|
"type": "node-terminal"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"command": "node --no-deprecation test/loader/init.js",
|
|
||||||
"cwd": "${workspaceFolder}",
|
|
||||||
"name": "Run Loader",
|
|
||||||
"request": "launch",
|
|
||||||
"type": "node-terminal",
|
|
||||||
"env": {
|
|
||||||
"LOADER_TEST_FILE_PATH": "./dependency-test.js"
|
|
||||||
// "LOADER_TEST_FILE_PATH": "../fields/config.ts"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"command": "pnpm tsx --no-deprecation test/dev.ts admin",
|
|
||||||
"cwd": "${workspaceFolder}",
|
|
||||||
"name": "Run Dev Admin",
|
|
||||||
"request": "launch",
|
|
||||||
"type": "node-terminal"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"command": "pnpm tsx --no-deprecation test/dev.ts auth",
|
|
||||||
"cwd": "${workspaceFolder}",
|
|
||||||
"name": "Run Dev Auth",
|
|
||||||
"request": "launch",
|
|
||||||
"type": "node-terminal"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"command": "pnpm tsx --no-deprecation test/dev.ts fields-relationship",
|
|
||||||
"cwd": "${workspaceFolder}",
|
|
||||||
"name": "Run Dev Fields-Relationship",
|
|
||||||
"request": "launch",
|
|
||||||
"type": "node-terminal"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"command": "pnpm tsx --no-deprecation test/dev.ts login-with-username",
|
|
||||||
"cwd": "${workspaceFolder}",
|
|
||||||
"name": "Run Dev Login-With-Username",
|
|
||||||
"request": "launch",
|
|
||||||
"type": "node-terminal"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"command": "pnpm run dev plugin-cloud-storage",
|
"command": "pnpm run dev plugin-cloud-storage",
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
@@ -81,63 +34,49 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "pnpm tsx --no-deprecation test/dev.ts collections-graphql",
|
"command": "pnpm run dev fields",
|
||||||
"cwd": "${workspaceFolder}",
|
|
||||||
"name": "Run Dev GraphQL",
|
|
||||||
"request": "launch",
|
|
||||||
"type": "node-terminal"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"command": "pnpm tsx --no-deprecation test/dev.ts fields",
|
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
"name": "Run Dev Fields",
|
"name": "Run Dev Fields",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"type": "node-terminal"
|
"type": "node-terminal"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "pnpm tsx --no-deprecation test/dev.ts versions",
|
"command": "pnpm run dev:postgres versions",
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
"name": "Run Dev Postgres",
|
"name": "Run Dev Postgres",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"type": "node-terminal",
|
"type": "node-terminal"
|
||||||
"env": {
|
|
||||||
"PAYLOAD_DATABASE": "postgres"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "pnpm tsx --no-deprecation test/dev.ts versions",
|
"command": "pnpm run dev versions",
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
"name": "Run Dev Versions",
|
"name": "Run Dev Versions",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"type": "node-terminal"
|
"type": "node-terminal"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "pnpm tsx --no-deprecation test/dev.ts localization",
|
"command": "pnpm run dev localization",
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
"name": "Run Dev Localization",
|
"name": "Run Dev Localization",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"type": "node-terminal"
|
"type": "node-terminal"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "pnpm tsx --no-deprecation test/dev.ts locked-documents",
|
"command": "pnpm run dev uploads",
|
||||||
"cwd": "${workspaceFolder}",
|
|
||||||
"name": "Run Dev Locked Documents",
|
|
||||||
"request": "launch",
|
|
||||||
"type": "node-terminal"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"command": "pnpm tsx --no-deprecation test/dev.ts uploads",
|
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
"name": "Run Dev Uploads",
|
"name": "Run Dev Uploads",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"type": "node-terminal"
|
"type": "node-terminal"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "pnpm tsx --no-deprecation test/dev.ts field-error-states",
|
"command": "PAYLOAD_BUNDLER=vite pnpm run dev fields",
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
"name": "Run Dev Field Error States",
|
"name": "Run Dev Fields (Vite)",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"type": "node-terminal"
|
"type": "node-terminal",
|
||||||
|
"env": {
|
||||||
|
"NODE_ENV": "production"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "pnpm run test:int live-preview",
|
"command": "pnpm run test:int live-preview",
|
||||||
|
|||||||
16
.vscode/settings.json
vendored
16
.vscode/settings.json
vendored
@@ -31,15 +31,8 @@
|
|||||||
"editor.formatOnSave": true
|
"editor.formatOnSave": true
|
||||||
},
|
},
|
||||||
"editor.formatOnSaveMode": "file",
|
"editor.formatOnSaveMode": "file",
|
||||||
"eslint.rules.customizations": [
|
// All ESLint rules to 'warn' to differentate from TypeScript's 'error' level
|
||||||
// Defaultt all ESLint errors to 'warn' to differentate from TypeScript's 'error' level
|
"eslint.rules.customizations": [{ "rule": "*", "severity": "warn" }],
|
||||||
{ "rule": "*", "severity": "warn" },
|
|
||||||
|
|
||||||
// Silence some warnings that will get auto-fixed
|
|
||||||
{ "rule": "perfectionist/*", "severity": "off", "fixable": true },
|
|
||||||
{ "rule": "curly", "severity": "off", "fixable": true },
|
|
||||||
{ "rule": "object-shorthand", "severity": "off", "fixable": true }
|
|
||||||
],
|
|
||||||
"typescript.tsdk": "node_modules/typescript/lib",
|
"typescript.tsdk": "node_modules/typescript/lib",
|
||||||
// Load .git-blame-ignore-revs file
|
// Load .git-blame-ignore-revs file
|
||||||
"gitlens.advanced.blame.customArguments": ["--ignore-revs-file", ".git-blame-ignore-revs"],
|
"gitlens.advanced.blame.customArguments": ["--ignore-revs-file", ".git-blame-ignore-revs"],
|
||||||
@@ -47,10 +40,5 @@
|
|||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll.eslint": "explicit"
|
"source.fixAll.eslint": "explicit"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"files.insertFinalNewline": true,
|
|
||||||
"jestrunner.jestCommand": "pnpm exec cross-env NODE_OPTIONS=\"--no-deprecation\" node 'node_modules/jest/bin/jest.js'",
|
|
||||||
"jestrunner.debugOptions": {
|
|
||||||
"runtimeArgs": ["--no-deprecation"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
818
CHANGELOG.md
818
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@@ -63,7 +63,7 @@ Each test directory is split up in this way specifically to reduce friction when
|
|||||||
|
|
||||||
The following command will start Payload with your config: `pnpm dev my-test-dir`. Example: `pnpm dev fields` for the test/`fields` test suite. This command will start up Payload using your config and refresh a test database on every restart. If you're using VS Code, the most common run configs are automatically added to your editor - you should be able to find them in your VS Code launch tab.
|
The following command will start Payload with your config: `pnpm dev my-test-dir`. Example: `pnpm dev fields` for the test/`fields` test suite. This command will start up Payload using your config and refresh a test database on every restart. If you're using VS Code, the most common run configs are automatically added to your editor - you should be able to find them in your VS Code launch tab.
|
||||||
|
|
||||||
By default, payload will [automatically log you in](https://payloadcms.com/docs/authentication/overview#admin-autologin) with the default credentials. To disable that, you can either pass in the --no-auto-login flag (example: `pnpm dev my-test-dir --no-auto-login`) or set the `PAYLOAD_PUBLIC_DISABLE_AUTO_LOGIN` environment variable to `false`.
|
By default, payload will [automatically log you in](https://payloadcms.com/docs/authentication/config#admin-autologin) with the default credentials. To disable that, you can either pass in the --no-auto-login flag (example: `pnpm dev my-test-dir --no-auto-login`) or set the `PAYLOAD_PUBLIC_DISABLE_AUTO_LOGIN` environment variable to `false`.
|
||||||
|
|
||||||
The default credentials are `dev@payloadcms.com` as E-Mail and `test` as password. These are used in the auto-login.
|
The default credentials are `dev@payloadcms.com` as E-Mail and `test` as password. These are used in the auto-login.
|
||||||
|
|
||||||
@@ -120,21 +120,5 @@ This is how you can preview changes you made locally to the docs:
|
|||||||
2. Run `yarn install`
|
2. Run `yarn install`
|
||||||
3. Duplicate the `.env.example` file and rename it to `.env`
|
3. Duplicate the `.env.example` file and rename it to `.env`
|
||||||
4. Add a `DOCS_DIR` environment variable to the `.env` file which points to the absolute path of your modified docs folder. For example `DOCS_DIR=/Users/yourname/Documents/GitHub/payload/docs`
|
4. Add a `DOCS_DIR` environment variable to the `.env` file which points to the absolute path of your modified docs folder. For example `DOCS_DIR=/Users/yourname/Documents/GitHub/payload/docs`
|
||||||
5. Run `yarn run fetchDocs:local`. If this was successful, you should see no error messages and the following output: _Docs successfully written to /.../website/src/app/docs.json_. There could be error messages if you have incorrect markdown in your local docs folder. In this case, it will tell you how you can fix it
|
5. Run `yarn run fetchDocs:local`. If this was successful, you should see no error messages and the following output: *Docs successfully written to /.../website/src/app/docs.json*. There could be error messages if you have incorrect markdown in your local docs folder. In this case, it will tell you how you can fix it
|
||||||
6. You're done! Now you can start the website locally using `yarn run dev` and preview the docs under [http://localhost:3000/docs/](http://localhost:3000/docs/)
|
6. You're done! Now you can start the website locally using `yarn run dev` and preview the docs under [http://localhost:3000/docs/](http://localhost:3000/docs/)
|
||||||
|
|
||||||
## Internationalization (i18n)
|
|
||||||
|
|
||||||
If your PR adds a string to the UI, we need to make sure to translate it into all the languages that Payload supports. To do that:
|
|
||||||
|
|
||||||
- Find the appropriate internationalization file for your package. These are typically located in `packages/translations/src/languages`, although some packages (e.g., richtext-lexical) have separate i18n files for each feature.
|
|
||||||
- Add the string to the English locale "en".
|
|
||||||
- Translate it to other languages. You can use the `translateNewKeys` script if you have an OpenAI API key in your `.env` (under `OPENAI_KEY`), or you can use ChatGPT or Google translate - whatever is easier for you. For payload core translations (in packages/translations) you can run the `translateNewKeys` script using `cd packages/translations && pnpm translateNewKeys`. For lexical translations, you can run it using `cd packages/richtext-lexical && pnpm translateNewKeys`. External contributors can skip this step and leave it to us.
|
|
||||||
|
|
||||||
To display translation strings in the UI, make sure to use the `t` utility of the `useTranslation` hook:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const { t } = useTranslation()
|
|
||||||
// ...
|
|
||||||
t('yourStringKey')
|
|
||||||
```
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<a href="https://payloadcms.com"><img width="100%" src="https://github.com/payloadcms/payload/blob/main/packages/payload/src/admin/assets/images/github-banner-alt.jpg?raw=true" alt="Payload headless CMS Admin panel built with React" /></a>
|
<a href="https://payloadcms.com"><img width="100%" src="https://github.com/payloadcms/payload/blob/main/packages/payload/src/admin/assets/images/github-banner-alt.jpg?raw=true" alt="Payload headless CMS Admin panel built with React" /></a>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<p align="left">
|
<p align="left">
|
||||||
<a href="https://github.com/payloadcms/payload/actions"><img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/payloadcms/payload/main.yml?style=flat-square"></a>
|
<a href="https://github.com/payloadcms/payload/actions"><img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/payloadcms/payload/main.yml?style=flat-square"></a>
|
||||||
|
|
||||||
@@ -100,10 +99,6 @@ If you want to add contributions to this repository, please follow the instructi
|
|||||||
|
|
||||||
The [Examples Directory](./examples) is a great resource for learning how to setup Payload in a variety of different ways, but you can also find great examples in our blog and throughout our social media.
|
The [Examples Directory](./examples) is a great resource for learning how to setup Payload in a variety of different ways, but you can also find great examples in our blog and throughout our social media.
|
||||||
|
|
||||||
If you'd like to run the examples, you can either copy them to a folder outside this repo or run them directly by (1) navigating to the example's subfolder (`cd examples/your-example-folder`) and (2) using the `--ignore-workspace` flag to bypass workspace restrictions (e.g., `pnpm --ignore-workspace install` or `pnpm --ignore-workspace dev`).
|
|
||||||
|
|
||||||
You can see more examples at:
|
|
||||||
|
|
||||||
- [Examples Directory](./examples)
|
- [Examples Directory](./examples)
|
||||||
- [Payload Blog](https://payloadcms.com/blog)
|
- [Payload Blog](https://payloadcms.com/blog)
|
||||||
- [Payload YouTube](https://www.youtube.com/@payloadcms)
|
- [Payload YouTube](https://www.youtube.com/@payloadcms)
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
|
|
||||||
export const metadata = {
|
|
||||||
description: 'Generated by Next.js',
|
|
||||||
title: 'Next.js',
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
||||||
return (
|
|
||||||
<html lang="en">
|
|
||||||
<body>{children}</body>
|
|
||||||
</html>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import configPromise from '@payload-config'
|
|
||||||
import { getPayloadHMR } from '@payloadcms/next/utilities'
|
|
||||||
|
|
||||||
export const Page = async ({ params, searchParams }) => {
|
|
||||||
const payload = await getPayloadHMR({
|
|
||||||
config: configPromise,
|
|
||||||
})
|
|
||||||
return <div>test ${payload?.config?.collections?.length}</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Page
|
|
||||||
@@ -1,25 +1,17 @@
|
|||||||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
||||||
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
|
||||||
import type { Metadata } from 'next'
|
|
||||||
|
|
||||||
import config from '@payload-config'
|
import config from '@payload-config'
|
||||||
import { generatePageMetadata, NotFoundPage } from '@payloadcms/next/views'
|
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
||||||
|
import { NotFoundView } from '@payloadcms/next/views/NotFound/index.js'
|
||||||
import { importMap } from '../importMap.js'
|
|
||||||
|
|
||||||
type Args = {
|
type Args = {
|
||||||
params: Promise<{
|
params: {
|
||||||
segments: string[]
|
segments: string[]
|
||||||
}>
|
}
|
||||||
searchParams: Promise<{
|
searchParams: {
|
||||||
[key: string]: string | string[]
|
[key: string]: string | string[]
|
||||||
}>
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>
|
const NotFound = ({ params, searchParams }: Args) => NotFoundView({ config, params, searchParams })
|
||||||
generatePageMetadata({ config, params, searchParams })
|
|
||||||
|
|
||||||
const NotFound = ({ params, searchParams }: Args) =>
|
|
||||||
NotFoundPage({ config, importMap, params, searchParams })
|
|
||||||
|
|
||||||
export default NotFound
|
export default NotFound
|
||||||
|
|||||||
@@ -1,25 +1,22 @@
|
|||||||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
||||||
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
|
||||||
import type { Metadata } from 'next'
|
import type { Metadata } from 'next'
|
||||||
|
|
||||||
import config from '@payload-config'
|
import config from '@payload-config'
|
||||||
import { generatePageMetadata, RootPage } from '@payloadcms/next/views'
|
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
||||||
|
import { RootPage, generatePageMetadata } from '@payloadcms/next/views/Root/index.js'
|
||||||
import { importMap } from '../importMap.js'
|
|
||||||
|
|
||||||
type Args = {
|
type Args = {
|
||||||
params: Promise<{
|
params: {
|
||||||
segments: string[]
|
segments: string[]
|
||||||
}>
|
}
|
||||||
searchParams: Promise<{
|
searchParams: {
|
||||||
[key: string]: string | string[]
|
[key: string]: string | string[]
|
||||||
}>
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>
|
export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>
|
||||||
generatePageMetadata({ config, params, searchParams })
|
generatePageMetadata({ config, params, searchParams })
|
||||||
|
|
||||||
const Page = ({ params, searchParams }: Args) =>
|
const Page = ({ params, searchParams }: Args) => RootPage({ config, params, searchParams })
|
||||||
RootPage({ config, importMap, params, searchParams })
|
|
||||||
|
|
||||||
export default Page
|
export default Page
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
||||||
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
/* DO NOT MODIFY it because it could be re-written at any time. */
|
||||||
import config from '@payload-config'
|
import config from '@payload-config'
|
||||||
import { REST_DELETE, REST_GET, REST_OPTIONS, REST_PATCH, REST_POST } from '@payloadcms/next/routes'
|
import { REST_DELETE, REST_GET, REST_PATCH, REST_POST } from '@payloadcms/next/routes/index.js'
|
||||||
|
|
||||||
export const GET = REST_GET(config)
|
export const GET = REST_GET(config)
|
||||||
export const POST = REST_POST(config)
|
export const POST = REST_POST(config)
|
||||||
export const DELETE = REST_DELETE(config)
|
export const DELETE = REST_DELETE(config)
|
||||||
export const PATCH = REST_PATCH(config)
|
export const PATCH = REST_PATCH(config)
|
||||||
export const OPTIONS = REST_OPTIONS(config)
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
||||||
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
/* DO NOT MODIFY it because it could be re-written at any time. */
|
||||||
import config from '@payload-config'
|
import config from '@payload-config'
|
||||||
import { GRAPHQL_PLAYGROUND_GET } from '@payloadcms/next/routes'
|
import { GRAPHQL_PLAYGROUND_GET } from '@payloadcms/next/routes/index.js'
|
||||||
|
|
||||||
export const GET = GRAPHQL_PLAYGROUND_GET(config)
|
export const GET = GRAPHQL_PLAYGROUND_GET(config)
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
||||||
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
/* DO NOT MODIFY it because it could be re-written at any time. */
|
||||||
import config from '@payload-config'
|
import config from '@payload-config'
|
||||||
import { GRAPHQL_POST, REST_OPTIONS } from '@payloadcms/next/routes'
|
import { GRAPHQL_POST } from '@payloadcms/next/routes/index.js'
|
||||||
|
|
||||||
export const POST = GRAPHQL_POST(config)
|
export const POST = GRAPHQL_POST(config)
|
||||||
|
|
||||||
export const OPTIONS = REST_OPTIONS(config)
|
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
#custom-css {
|
|
||||||
font-family: monospace;
|
|
||||||
background-image: url('/placeholder.png');
|
|
||||||
}
|
|
||||||
|
|
||||||
#custom-css::after {
|
|
||||||
content: 'custom-css';
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,21 +1,15 @@
|
|||||||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
||||||
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
|
||||||
import configPromise from '@payload-config'
|
import configPromise from '@payload-config'
|
||||||
import { RootLayout } from '@payloadcms/next/layouts'
|
import { RootLayout } from '@payloadcms/next/layouts/Root/index.js'
|
||||||
// import '@payloadcms/ui/styles.css' // Uncomment this line if `@payloadcms/ui` in `tsconfig.json` points to `/ui/dist` instead of `/ui/src`
|
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { importMap } from './admin/importMap.js'
|
|
||||||
import './custom.scss'
|
import './custom.scss'
|
||||||
|
|
||||||
type Args = {
|
type Args = {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
const Layout = ({ children }: Args) => (
|
const Layout = ({ children }: Args) => <RootLayout config={configPromise}>{children}</RootLayout>
|
||||||
<RootLayout config={configPromise} importMap={importMap}>
|
|
||||||
{children}
|
|
||||||
</RootLayout>
|
|
||||||
)
|
|
||||||
|
|
||||||
export default Layout
|
export default Layout
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
/* eslint-disable no-restricted-exports */
|
|
||||||
'use client'
|
|
||||||
|
|
||||||
import * as Sentry from '@sentry/nextjs'
|
|
||||||
import NextError from 'next/error.js'
|
|
||||||
import { useEffect } from 'react'
|
|
||||||
|
|
||||||
export default function GlobalError({ error }: { error: { digest?: string } & Error }) {
|
|
||||||
useEffect(() => {
|
|
||||||
if (process.env.NEXT_PUBLIC_SENTRY_DSN) {
|
|
||||||
Sentry.captureException(error)
|
|
||||||
}
|
|
||||||
}, [error])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<html lang="en-US">
|
|
||||||
<body>
|
|
||||||
{/* `NextError` is the default Next.js error page component. Its type
|
|
||||||
definition requires a `statusCode` prop. However, since the App Router
|
|
||||||
does not expose status codes for errors, we simply pass 0 to render a
|
|
||||||
generic error message. */}
|
|
||||||
{/* @ts-expect-error types repo */}
|
|
||||||
<NextError statusCode={0} />
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
43
app/live-preview/(pages)/[slug]/page.client.tsx
Normal file
43
app/live-preview/(pages)/[slug]/page.client.tsx
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useLivePreview } from '@payloadcms/live-preview-react'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import type { Page as PageType } from '../../../../test/live-preview/payload-types.js'
|
||||||
|
|
||||||
|
import { PAYLOAD_SERVER_URL } from '../../_api/serverURL.js'
|
||||||
|
import { Blocks } from '../../_components/Blocks/index.js'
|
||||||
|
import { Gutter } from '../../_components/Gutter/index.js'
|
||||||
|
import { Hero } from '../../_components/Hero/index.js'
|
||||||
|
|
||||||
|
export const PageClient: React.FC<{
|
||||||
|
page: PageType
|
||||||
|
}> = ({ page: initialPage }) => {
|
||||||
|
const { data } = useLivePreview<PageType>({
|
||||||
|
depth: 2,
|
||||||
|
initialData: initialPage,
|
||||||
|
serverURL: PAYLOAD_SERVER_URL,
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<Gutter>
|
||||||
|
<h1 id="page-title">{data.title}</h1>
|
||||||
|
</Gutter>
|
||||||
|
<Hero {...data?.hero} />
|
||||||
|
<Blocks
|
||||||
|
blocks={[
|
||||||
|
...(data?.layout ?? []),
|
||||||
|
{
|
||||||
|
blockName: 'Relationships',
|
||||||
|
blockType: 'relationships',
|
||||||
|
data,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
disableTopPadding={
|
||||||
|
!data?.hero || data?.hero?.type === 'none' || data?.hero?.type === 'lowImpact'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
37
app/live-preview/(pages)/[slug]/page.tsx
Normal file
37
app/live-preview/(pages)/[slug]/page.tsx
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { notFound } from 'next/navigation.js'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import type { Page } from '../../../../test/live-preview/payload-types.js'
|
||||||
|
|
||||||
|
import { fetchDoc } from '../../_api/fetchDoc.js'
|
||||||
|
import { fetchDocs } from '../../_api/fetchDocs.js'
|
||||||
|
import { PageClient } from './page.client.js'
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-restricted-exports
|
||||||
|
export default async function Page({ params: { slug = 'home' } }) {
|
||||||
|
let page: Page | null = null
|
||||||
|
|
||||||
|
try {
|
||||||
|
page = await fetchDoc<Page>({
|
||||||
|
slug,
|
||||||
|
collection: 'pages',
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!page) {
|
||||||
|
return notFound()
|
||||||
|
}
|
||||||
|
|
||||||
|
return <PageClient page={page} />
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function generateStaticParams() {
|
||||||
|
try {
|
||||||
|
const pages = await fetchDocs<Page>('pages')
|
||||||
|
return pages?.map(({ slug }) => slug)
|
||||||
|
} catch (error) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
68
app/live-preview/(pages)/posts/[slug]/page.client.tsx
Normal file
68
app/live-preview/(pages)/posts/[slug]/page.client.tsx
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useLivePreview } from '@payloadcms/live-preview-react'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import type { Post as PostType } from '../../../../../test/live-preview/payload-types.js'
|
||||||
|
|
||||||
|
import { PAYLOAD_SERVER_URL } from '../../../_api/serverURL.js'
|
||||||
|
import { Blocks } from '../../../_components/Blocks/index.js'
|
||||||
|
import { PostHero } from '../../../_heros/PostHero/index.js'
|
||||||
|
|
||||||
|
export const PostClient: React.FC<{
|
||||||
|
post: PostType
|
||||||
|
}> = ({ post: initialPost }) => {
|
||||||
|
const { data } = useLivePreview<PostType>({
|
||||||
|
depth: 2,
|
||||||
|
initialData: initialPost,
|
||||||
|
serverURL: PAYLOAD_SERVER_URL,
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<PostHero post={data} />
|
||||||
|
<Blocks blocks={data?.layout} />
|
||||||
|
<Blocks
|
||||||
|
blocks={[
|
||||||
|
{
|
||||||
|
blockName: 'Related Posts',
|
||||||
|
blockType: 'relatedPosts',
|
||||||
|
docs: data?.relatedPosts,
|
||||||
|
introContent: [
|
||||||
|
{
|
||||||
|
type: 'h4',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
text: 'Related posts',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'p',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
text: 'The posts displayed here are individually selected for this page. Admins can select any number of related posts to display here and the layout will adjust accordingly. Alternatively, you could swap this out for the "Archive" block to automatically populate posts by category complete with pagination. To manage related posts, ',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'link',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
text: 'navigate to the admin dashboard',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
url: `/admin/collections/posts/${data?.id}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
relationTo: 'posts',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
disableTopPadding
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
36
app/live-preview/(pages)/posts/[slug]/page.tsx
Normal file
36
app/live-preview/(pages)/posts/[slug]/page.tsx
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { notFound } from 'next/navigation.js'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import type { Post } from '../../../../../test/live-preview/payload-types.js'
|
||||||
|
|
||||||
|
import { fetchDoc } from '../../../_api/fetchDoc.js'
|
||||||
|
import { fetchDocs } from '../../../_api/fetchDocs.js'
|
||||||
|
import { PostClient } from './page.client.js'
|
||||||
|
|
||||||
|
export default async function Post({ params: { slug = '' } }) {
|
||||||
|
let post: Post | null = null
|
||||||
|
|
||||||
|
try {
|
||||||
|
post = await fetchDoc<Post>({
|
||||||
|
slug,
|
||||||
|
collection: 'posts',
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error) // eslint-disable-line no-console
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!post) {
|
||||||
|
notFound()
|
||||||
|
}
|
||||||
|
|
||||||
|
return <PostClient post={post} />
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function generateStaticParams() {
|
||||||
|
try {
|
||||||
|
const posts = await fetchDocs<Post>('posts')
|
||||||
|
return posts?.map(({ slug }) => slug)
|
||||||
|
} catch (error) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
35
app/live-preview/_api/fetchDoc.ts
Normal file
35
app/live-preview/_api/fetchDoc.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import QueryString from 'qs'
|
||||||
|
|
||||||
|
import { PAYLOAD_SERVER_URL } from './serverURL.js'
|
||||||
|
|
||||||
|
export const fetchDoc = async <T>(args: {
|
||||||
|
collection: string
|
||||||
|
depth?: number
|
||||||
|
id?: string
|
||||||
|
slug?: string
|
||||||
|
}): Promise<T> => {
|
||||||
|
const { id, slug, collection, depth = 2 } = args || {}
|
||||||
|
|
||||||
|
const queryString = QueryString.stringify(
|
||||||
|
{
|
||||||
|
...(slug ? { 'where[slug][equals]': slug } : {}),
|
||||||
|
...(depth ? { depth } : {}),
|
||||||
|
},
|
||||||
|
{ addQueryPrefix: true },
|
||||||
|
)
|
||||||
|
|
||||||
|
const doc: T = await fetch(`${PAYLOAD_SERVER_URL}/api/${collection}${queryString}`, {
|
||||||
|
cache: 'no-store',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
?.then((res) => res.json())
|
||||||
|
?.then((res) => {
|
||||||
|
if (res.errors) throw new Error(res?.errors?.[0]?.message ?? 'Error fetching doc')
|
||||||
|
return res?.docs?.[0]
|
||||||
|
})
|
||||||
|
|
||||||
|
return doc
|
||||||
|
}
|
||||||
19
app/live-preview/_api/fetchDocs.ts
Normal file
19
app/live-preview/_api/fetchDocs.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { PAYLOAD_SERVER_URL } from './serverURL.js'
|
||||||
|
|
||||||
|
export const fetchDocs = async <T>(collection: string): Promise<T[]> => {
|
||||||
|
const docs: T[] = await fetch(`${PAYLOAD_SERVER_URL}/api/${collection}?depth=0&limit=100`, {
|
||||||
|
cache: 'no-store',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
?.then((res) => res.json())
|
||||||
|
?.then((res) => {
|
||||||
|
if (res.errors) throw new Error(res?.errors?.[0]?.message ?? 'Error fetching docs')
|
||||||
|
|
||||||
|
return res?.docs
|
||||||
|
})
|
||||||
|
|
||||||
|
return docs
|
||||||
|
}
|
||||||
25
app/live-preview/_api/fetchFooter.ts
Normal file
25
app/live-preview/_api/fetchFooter.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import type { Footer } from '../../../test/live-preview/payload-types.js'
|
||||||
|
|
||||||
|
import { PAYLOAD_SERVER_URL } from './serverURL.js'
|
||||||
|
|
||||||
|
export async function fetchFooter(): Promise<Footer> {
|
||||||
|
if (!PAYLOAD_SERVER_URL) throw new Error('PAYLOAD_SERVER_URL not found')
|
||||||
|
|
||||||
|
const footer = await fetch(`${PAYLOAD_SERVER_URL}/api/globals/footer`, {
|
||||||
|
cache: 'no-store',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (!res.ok) throw new Error('Error fetching doc')
|
||||||
|
return res.json()
|
||||||
|
})
|
||||||
|
?.then((res) => {
|
||||||
|
if (res?.errors) throw new Error(res?.errors[0]?.message || 'Error fetching footer')
|
||||||
|
return res
|
||||||
|
})
|
||||||
|
|
||||||
|
return footer
|
||||||
|
}
|
||||||
25
app/live-preview/_api/fetchHeader.ts
Normal file
25
app/live-preview/_api/fetchHeader.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import type { Header } from '../../../test/live-preview/payload-types.js'
|
||||||
|
|
||||||
|
import { PAYLOAD_SERVER_URL } from './serverURL.js'
|
||||||
|
|
||||||
|
export async function fetchHeader(): Promise<Header> {
|
||||||
|
if (!PAYLOAD_SERVER_URL) throw new Error('PAYLOAD_SERVER_URL not found')
|
||||||
|
|
||||||
|
const header = await fetch(`${PAYLOAD_SERVER_URL}/api/globals/header`, {
|
||||||
|
cache: 'no-store',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
?.then((res) => {
|
||||||
|
if (!res.ok) throw new Error('Error fetching doc')
|
||||||
|
return res.json()
|
||||||
|
})
|
||||||
|
?.then((res) => {
|
||||||
|
if (res?.errors) throw new Error(res?.errors[0]?.message || 'Error fetching header')
|
||||||
|
return res
|
||||||
|
})
|
||||||
|
|
||||||
|
return header
|
||||||
|
}
|
||||||
46
app/live-preview/_blocks/ArchiveBlock/index.tsx
Normal file
46
app/live-preview/_blocks/ArchiveBlock/index.tsx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import type { ArchiveBlockProps } from './types.js'
|
||||||
|
|
||||||
|
import { CollectionArchive } from '../../_components/CollectionArchive/index.js'
|
||||||
|
import { Gutter } from '../../_components/Gutter/index.js'
|
||||||
|
import RichText from '../../_components/RichText/index.js'
|
||||||
|
import classes from './index.module.scss'
|
||||||
|
|
||||||
|
export const ArchiveBlock: React.FC<
|
||||||
|
ArchiveBlockProps & {
|
||||||
|
id?: string
|
||||||
|
}
|
||||||
|
> = (props) => {
|
||||||
|
const {
|
||||||
|
id,
|
||||||
|
categories,
|
||||||
|
introContent,
|
||||||
|
limit,
|
||||||
|
populateBy,
|
||||||
|
populatedDocs,
|
||||||
|
populatedDocsTotal,
|
||||||
|
relationTo,
|
||||||
|
selectedDocs,
|
||||||
|
} = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes.archiveBlock} id={`block-${id}`}>
|
||||||
|
{introContent && (
|
||||||
|
<Gutter className={classes.introContent}>
|
||||||
|
<RichText content={introContent} />
|
||||||
|
</Gutter>
|
||||||
|
)}
|
||||||
|
<CollectionArchive
|
||||||
|
categories={categories}
|
||||||
|
limit={limit}
|
||||||
|
populateBy={populateBy}
|
||||||
|
populatedDocs={populatedDocs}
|
||||||
|
populatedDocsTotal={populatedDocsTotal}
|
||||||
|
relationTo={relationTo}
|
||||||
|
selectedDocs={selectedDocs}
|
||||||
|
sort="-publishedDate"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
6
app/live-preview/_blocks/ArchiveBlock/types.ts
Normal file
6
app/live-preview/_blocks/ArchiveBlock/types.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import type { Page } from '../../../../test/live-preview/payload-types.js'
|
||||||
|
|
||||||
|
export type ArchiveBlockProps = Extract<
|
||||||
|
Exclude<Page['layout'], undefined>[0],
|
||||||
|
{ blockType: 'archive' }
|
||||||
|
>
|
||||||
38
app/live-preview/_blocks/CallToAction/index.tsx
Normal file
38
app/live-preview/_blocks/CallToAction/index.tsx
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import type { Page } from '../../../../test/live-preview/payload-types.js'
|
||||||
|
|
||||||
|
import { Gutter } from '../../_components/Gutter/index.js'
|
||||||
|
import { CMSLink } from '../../_components/Link/index.js'
|
||||||
|
import RichText from '../../_components/RichText/index.js'
|
||||||
|
import { VerticalPadding } from '../../_components/VerticalPadding/index.js'
|
||||||
|
import classes from './index.module.scss'
|
||||||
|
|
||||||
|
type Props = Extract<Exclude<Page['layout'], undefined>[0], { blockType: 'cta' }>
|
||||||
|
|
||||||
|
export const CallToActionBlock: React.FC<
|
||||||
|
Props & {
|
||||||
|
id?: string
|
||||||
|
}
|
||||||
|
> = ({ invertBackground, links, richText }) => {
|
||||||
|
return (
|
||||||
|
<Gutter>
|
||||||
|
<VerticalPadding
|
||||||
|
className={[classes.callToAction, invertBackground && classes.invert]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' ')}
|
||||||
|
>
|
||||||
|
<div className={classes.wrap}>
|
||||||
|
<div className={classes.content}>
|
||||||
|
<RichText className={classes.richText} content={richText} />
|
||||||
|
</div>
|
||||||
|
<div className={classes.linkGroup}>
|
||||||
|
{(links || []).map(({ link }, i) => {
|
||||||
|
return <CMSLink key={i} {...link} invert={invertBackground} />
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</VerticalPadding>
|
||||||
|
</Gutter>
|
||||||
|
)
|
||||||
|
}
|
||||||
39
app/live-preview/_blocks/Content/index.tsx
Normal file
39
app/live-preview/_blocks/Content/index.tsx
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import React, { Fragment } from 'react'
|
||||||
|
|
||||||
|
import type { Page } from '../../../../test/live-preview/payload-types.js'
|
||||||
|
|
||||||
|
import { Gutter } from '../../_components/Gutter/index.js'
|
||||||
|
import { CMSLink } from '../../_components/Link/index.js'
|
||||||
|
import RichText from '../../_components/RichText/index.js'
|
||||||
|
import classes from './index.module.scss'
|
||||||
|
|
||||||
|
type Props = Extract<Exclude<Page['layout'], undefined>[0], { blockType: 'content' }>
|
||||||
|
|
||||||
|
export const ContentBlock: React.FC<
|
||||||
|
Props & {
|
||||||
|
id?: string
|
||||||
|
}
|
||||||
|
> = (props) => {
|
||||||
|
const { columns } = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Gutter className={classes.content}>
|
||||||
|
<div className={classes.grid}>
|
||||||
|
{columns && columns.length > 0 ? (
|
||||||
|
<Fragment>
|
||||||
|
{columns.map((col, index) => {
|
||||||
|
const { enableLink, link, richText, size } = col
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={[classes.column, classes[`column--${size}`]].join(' ')} key={index}>
|
||||||
|
<RichText content={richText} />
|
||||||
|
{enableLink && <CMSLink className={classes.link} {...link} />}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</Fragment>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</Gutter>
|
||||||
|
)
|
||||||
|
}
|
||||||
42
app/live-preview/_blocks/MediaBlock/index.tsx
Normal file
42
app/live-preview/_blocks/MediaBlock/index.tsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import type { StaticImageData } from 'next/image.js'
|
||||||
|
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import type { Page } from '../../../../test/live-preview/payload-types.js'
|
||||||
|
|
||||||
|
import { Gutter } from '../../_components/Gutter/index.js'
|
||||||
|
import { Media } from '../../_components/Media/index.js'
|
||||||
|
import RichText from '../../_components/RichText/index.js'
|
||||||
|
import classes from './index.module.scss'
|
||||||
|
|
||||||
|
type Props = Extract<Exclude<Page['layout'], undefined>[0], { blockType: 'mediaBlock' }> & {
|
||||||
|
id?: string
|
||||||
|
staticImage?: StaticImageData
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MediaBlock: React.FC<Props> = (props) => {
|
||||||
|
const { media, position = 'default', staticImage } = props
|
||||||
|
|
||||||
|
let caption
|
||||||
|
if (media && typeof media === 'object') caption = media.caption
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes.mediaBlock}>
|
||||||
|
{position === 'fullscreen' && (
|
||||||
|
<div className={classes.fullscreen}>
|
||||||
|
<Media resource={media} src={staticImage} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{position === 'default' && (
|
||||||
|
<Gutter>
|
||||||
|
<Media resource={media} src={staticImage} />
|
||||||
|
</Gutter>
|
||||||
|
)}
|
||||||
|
{caption && (
|
||||||
|
<Gutter className={classes.caption}>
|
||||||
|
<RichText content={caption} />
|
||||||
|
</Gutter>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
52
app/live-preview/_blocks/RelatedPosts/index.tsx
Normal file
52
app/live-preview/_blocks/RelatedPosts/index.tsx
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import type { Post } from '../../../../test/live-preview/payload-types.js'
|
||||||
|
|
||||||
|
import { Card } from '../../_components/Card/index.js'
|
||||||
|
import { Gutter } from '../../_components/Gutter/index.js'
|
||||||
|
import RichText from '../../_components/RichText/index.js'
|
||||||
|
import classes from './index.module.scss'
|
||||||
|
|
||||||
|
export type RelatedPostsProps = {
|
||||||
|
blockName: string
|
||||||
|
blockType: 'relatedPosts'
|
||||||
|
docs?: (Post | string)[] | null
|
||||||
|
introContent?: any
|
||||||
|
relationTo: 'posts'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RelatedPosts: React.FC<RelatedPostsProps> = (props) => {
|
||||||
|
const { docs, introContent, relationTo } = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes.relatedPosts}>
|
||||||
|
{introContent && (
|
||||||
|
<Gutter className={classes.introContent}>
|
||||||
|
<RichText content={introContent} />
|
||||||
|
</Gutter>
|
||||||
|
)}
|
||||||
|
<Gutter>
|
||||||
|
<div className={classes.grid}>
|
||||||
|
{docs?.map((doc, index) => {
|
||||||
|
if (typeof doc === 'string') return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={[
|
||||||
|
classes.column,
|
||||||
|
docs.length === 2 && classes['cols-half'],
|
||||||
|
docs.length >= 3 && classes['cols-thirds'],
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' ')}
|
||||||
|
key={index}
|
||||||
|
>
|
||||||
|
<Card doc={doc} relationTo={relationTo} showCategories />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</Gutter>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
196
app/live-preview/_blocks/Relationships/index.tsx
Normal file
196
app/live-preview/_blocks/Relationships/index.tsx
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
import React, { Fragment } from 'react'
|
||||||
|
|
||||||
|
import type { Page } from '../../../../test/live-preview/payload-types.js'
|
||||||
|
|
||||||
|
import { Gutter } from '../../_components/Gutter/index.js'
|
||||||
|
import RichText from '../../_components/RichText/index.js'
|
||||||
|
import classes from './index.module.scss'
|
||||||
|
|
||||||
|
export type RelationshipsBlockProps = {
|
||||||
|
blockName: string
|
||||||
|
blockType: 'relationships'
|
||||||
|
data: Page
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RelationshipsBlock: React.FC<RelationshipsBlockProps> = (props) => {
|
||||||
|
const { data } = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes.relationshipsBlock}>
|
||||||
|
<Gutter>
|
||||||
|
<p>
|
||||||
|
This block is for testing purposes only. It renders every possible type of relationship.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<b>Rich Text — Slate:</b>
|
||||||
|
</p>
|
||||||
|
{data?.richTextSlate && <RichText content={data.richTextSlate} renderUploadFilenameOnly />}
|
||||||
|
<p>
|
||||||
|
<b>Rich Text — Lexical:</b>
|
||||||
|
</p>
|
||||||
|
{data?.richTextLexical && (
|
||||||
|
<RichText content={data.richTextLexical} renderUploadFilenameOnly serializer="lexical" />
|
||||||
|
)}
|
||||||
|
<p>
|
||||||
|
<b>Upload:</b>
|
||||||
|
</p>
|
||||||
|
{data?.relationshipAsUpload ? (
|
||||||
|
<div>
|
||||||
|
{typeof data?.relationshipAsUpload === 'string'
|
||||||
|
? data?.relationshipAsUpload
|
||||||
|
: data?.relationshipAsUpload.filename}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>None</div>
|
||||||
|
)}
|
||||||
|
<p>
|
||||||
|
<b>Monomorphic Has One:</b>
|
||||||
|
</p>
|
||||||
|
{data?.relationshipMonoHasOne ? (
|
||||||
|
<div>
|
||||||
|
{typeof data?.relationshipMonoHasOne === 'string'
|
||||||
|
? data?.relationshipMonoHasOne
|
||||||
|
: data?.relationshipMonoHasOne.title}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>None</div>
|
||||||
|
)}
|
||||||
|
<p>
|
||||||
|
<b>Monomorphic Has Many:</b>
|
||||||
|
</p>
|
||||||
|
{data?.relationshipMonoHasMany ? (
|
||||||
|
<Fragment>
|
||||||
|
{data?.relationshipMonoHasMany.length
|
||||||
|
? data?.relationshipMonoHasMany?.map((item, index) =>
|
||||||
|
item ? (
|
||||||
|
<div key={index}>{typeof item === 'string' ? item : item.title}</div>
|
||||||
|
) : (
|
||||||
|
'null'
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: 'None'}
|
||||||
|
</Fragment>
|
||||||
|
) : (
|
||||||
|
<div>None</div>
|
||||||
|
)}
|
||||||
|
<p>
|
||||||
|
<b>Polymorphic Has One:</b>
|
||||||
|
</p>
|
||||||
|
{data?.relationshipPolyHasOne ? (
|
||||||
|
<div>
|
||||||
|
{typeof data?.relationshipPolyHasOne.value === 'string'
|
||||||
|
? data?.relationshipPolyHasOne.value
|
||||||
|
: data?.relationshipPolyHasOne.value.title}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>None</div>
|
||||||
|
)}
|
||||||
|
<p>
|
||||||
|
<b>Polymorphic Has Many:</b>
|
||||||
|
</p>
|
||||||
|
{data?.relationshipPolyHasMany ? (
|
||||||
|
<Fragment>
|
||||||
|
{data?.relationshipPolyHasMany.length
|
||||||
|
? data?.relationshipPolyHasMany?.map((item, index) =>
|
||||||
|
item.value ? (
|
||||||
|
<div key={index}>
|
||||||
|
{typeof item.value === 'string' ? item.value : item.value.title}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
'null'
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: 'None'}
|
||||||
|
</Fragment>
|
||||||
|
) : (
|
||||||
|
<div>None</div>
|
||||||
|
)}
|
||||||
|
<p>
|
||||||
|
<b>Array of Relationships:</b>
|
||||||
|
</p>
|
||||||
|
{data?.arrayOfRelationships?.map((item, index) => (
|
||||||
|
<div className={classes.array} key={index}>
|
||||||
|
<p>
|
||||||
|
<b>Rich Text:</b>
|
||||||
|
</p>
|
||||||
|
{item?.richTextInArray && <RichText content={item.richTextInArray} />}
|
||||||
|
<p>
|
||||||
|
<b>Upload:</b>
|
||||||
|
</p>
|
||||||
|
{item?.uploadInArray ? (
|
||||||
|
<div>
|
||||||
|
{typeof item?.uploadInArray === 'string'
|
||||||
|
? item?.uploadInArray
|
||||||
|
: item?.uploadInArray.filename}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>None</div>
|
||||||
|
)}
|
||||||
|
<p>
|
||||||
|
<b>Monomorphic Has One:</b>
|
||||||
|
</p>
|
||||||
|
{item?.relationshipInArrayMonoHasOne ? (
|
||||||
|
<div>
|
||||||
|
{typeof item?.relationshipInArrayMonoHasOne === 'string'
|
||||||
|
? item?.relationshipInArrayMonoHasOne
|
||||||
|
: item?.relationshipInArrayMonoHasOne.title}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>None</div>
|
||||||
|
)}
|
||||||
|
<p>
|
||||||
|
<b>Monomorphic Has Many:</b>
|
||||||
|
</p>
|
||||||
|
{item?.relationshipInArrayMonoHasMany ? (
|
||||||
|
<Fragment>
|
||||||
|
{item?.relationshipInArrayMonoHasMany.length
|
||||||
|
? item?.relationshipInArrayMonoHasMany?.map((rel, relIndex) =>
|
||||||
|
rel ? (
|
||||||
|
<div key={relIndex}>{typeof rel === 'string' ? rel : rel.title}</div>
|
||||||
|
) : (
|
||||||
|
'null'
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: 'None'}
|
||||||
|
</Fragment>
|
||||||
|
) : (
|
||||||
|
<div>None</div>
|
||||||
|
)}
|
||||||
|
<p>
|
||||||
|
<b>Polymorphic Has One:</b>
|
||||||
|
</p>
|
||||||
|
{item?.relationshipInArrayPolyHasOne ? (
|
||||||
|
<div>
|
||||||
|
{typeof item?.relationshipInArrayPolyHasOne.value === 'string'
|
||||||
|
? item?.relationshipInArrayPolyHasOne.value
|
||||||
|
: item?.relationshipInArrayPolyHasOne.value.title}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>None</div>
|
||||||
|
)}
|
||||||
|
<p>
|
||||||
|
<b>Polymorphic Has Many:</b>
|
||||||
|
</p>
|
||||||
|
{item?.relationshipInArrayPolyHasMany ? (
|
||||||
|
<Fragment>
|
||||||
|
{item?.relationshipInArrayPolyHasMany.length
|
||||||
|
? item?.relationshipInArrayPolyHasMany?.map((rel, relIndex) =>
|
||||||
|
rel.value ? (
|
||||||
|
<div key={relIndex}>
|
||||||
|
{typeof rel.value === 'string' ? rel.value : rel.value.title}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
'null'
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: 'None'}
|
||||||
|
</Fragment>
|
||||||
|
) : (
|
||||||
|
<div>None</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</Gutter>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
88
app/live-preview/_components/Blocks/index.tsx
Normal file
88
app/live-preview/_components/Blocks/index.tsx
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import React, { Fragment } from 'react'
|
||||||
|
|
||||||
|
import type { Page } from '../../../../test/live-preview/payload-types.js'
|
||||||
|
import type { RelationshipsBlockProps } from '../../_blocks/Relationships/index.js'
|
||||||
|
import type { VerticalPaddingOptions } from '../VerticalPadding/index.js'
|
||||||
|
|
||||||
|
import { ArchiveBlock } from '../../_blocks/ArchiveBlock/index.js'
|
||||||
|
import { CallToActionBlock } from '../../_blocks/CallToAction/index.js'
|
||||||
|
import { ContentBlock } from '../../_blocks/Content/index.js'
|
||||||
|
import { MediaBlock } from '../../_blocks/MediaBlock/index.js'
|
||||||
|
import { RelatedPosts, type RelatedPostsProps } from '../../_blocks/RelatedPosts/index.js'
|
||||||
|
import { RelationshipsBlock } from '../../_blocks/Relationships/index.js'
|
||||||
|
import { toKebabCase } from '../../_utilities/toKebabCase.js'
|
||||||
|
import { BackgroundColor } from '../BackgroundColor/index.js'
|
||||||
|
import { VerticalPadding } from '../VerticalPadding/index.js'
|
||||||
|
|
||||||
|
const blockComponents = {
|
||||||
|
archive: ArchiveBlock,
|
||||||
|
content: ContentBlock,
|
||||||
|
cta: CallToActionBlock,
|
||||||
|
mediaBlock: MediaBlock,
|
||||||
|
relatedPosts: RelatedPosts,
|
||||||
|
relationships: RelationshipsBlock,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Block = NonNullable<Page['layout']>[number]
|
||||||
|
|
||||||
|
export const Blocks: React.FC<{
|
||||||
|
blocks?: (Block | RelatedPostsProps | RelationshipsBlockProps)[] | null
|
||||||
|
disableTopPadding?: boolean
|
||||||
|
}> = (props) => {
|
||||||
|
const { blocks, disableTopPadding } = props
|
||||||
|
|
||||||
|
const hasBlocks = blocks && Array.isArray(blocks) && blocks.length > 0
|
||||||
|
|
||||||
|
if (hasBlocks) {
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
{blocks.map((block, index) => {
|
||||||
|
const { blockName, blockType } = block
|
||||||
|
|
||||||
|
if (blockType && blockType in blockComponents) {
|
||||||
|
const Block = blockComponents[blockType]
|
||||||
|
|
||||||
|
// the cta block is containerized, so we don't consider it to be inverted at the block-level
|
||||||
|
const blockIsInverted =
|
||||||
|
'invertBackground' in block && blockType !== 'cta' ? block.invertBackground : false
|
||||||
|
const prevBlock = blocks[index - 1]
|
||||||
|
|
||||||
|
const prevBlockInverted =
|
||||||
|
prevBlock && 'invertBackground' in prevBlock && prevBlock?.invertBackground
|
||||||
|
|
||||||
|
const isPrevSame = Boolean(blockIsInverted) === Boolean(prevBlockInverted)
|
||||||
|
|
||||||
|
let paddingTop: VerticalPaddingOptions = 'large'
|
||||||
|
let paddingBottom: VerticalPaddingOptions = 'large'
|
||||||
|
|
||||||
|
if (prevBlock && isPrevSame) {
|
||||||
|
paddingTop = 'none'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index === blocks.length - 1) {
|
||||||
|
paddingBottom = 'large'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disableTopPadding && index === 0) {
|
||||||
|
paddingTop = 'none'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Block) {
|
||||||
|
return (
|
||||||
|
<BackgroundColor invert={blockIsInverted} key={index}>
|
||||||
|
<VerticalPadding bottom={paddingBottom} top={paddingTop}>
|
||||||
|
{/* @ts-expect-error */}
|
||||||
|
<Block id={toKebabCase(blockName)} {...block} />
|
||||||
|
</VerticalPadding>
|
||||||
|
</BackgroundColor>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
})}
|
||||||
|
</Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
88
app/live-preview/_components/Card/index.tsx
Normal file
88
app/live-preview/_components/Card/index.tsx
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import LinkWithDefault from 'next/link.js'
|
||||||
|
import React, { Fragment } from 'react'
|
||||||
|
|
||||||
|
import type { Post } from '../../../../test/live-preview/payload-types.js'
|
||||||
|
|
||||||
|
import { Media } from '../Media/index.js'
|
||||||
|
import classes from './index.module.scss'
|
||||||
|
|
||||||
|
const Link = (LinkWithDefault.default || LinkWithDefault) as typeof LinkWithDefault.default
|
||||||
|
|
||||||
|
export const Card: React.FC<{
|
||||||
|
alignItems?: 'center'
|
||||||
|
className?: string
|
||||||
|
doc?: Post
|
||||||
|
hideImagesOnMobile?: boolean
|
||||||
|
orientation?: 'horizontal' | 'vertical'
|
||||||
|
relationTo?: 'posts'
|
||||||
|
showCategories?: boolean
|
||||||
|
title?: string
|
||||||
|
}> = (props) => {
|
||||||
|
const {
|
||||||
|
className,
|
||||||
|
doc,
|
||||||
|
orientation = 'vertical',
|
||||||
|
relationTo,
|
||||||
|
showCategories,
|
||||||
|
title: titleFromProps,
|
||||||
|
} = props
|
||||||
|
|
||||||
|
const { slug, categories, meta, title } = doc || {}
|
||||||
|
const { description, image: metaImage } = meta || {}
|
||||||
|
|
||||||
|
const hasCategories = categories && Array.isArray(categories) && categories.length > 0
|
||||||
|
const titleToUse = titleFromProps || title
|
||||||
|
const sanitizedDescription = description?.replace(/\s/g, ' ') // replace non-breaking space with white space
|
||||||
|
const href = `/live-preview/${relationTo}/${slug}`
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={[classes.card, className, orientation && classes[orientation]]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' ')}
|
||||||
|
>
|
||||||
|
<Link className={classes.mediaWrapper} href={href}>
|
||||||
|
{!metaImage && <div className={classes.placeholder}>No image</div>}
|
||||||
|
{metaImage && typeof metaImage !== 'string' && (
|
||||||
|
<Media fill imgClassName={classes.image} resource={metaImage} />
|
||||||
|
)}
|
||||||
|
</Link>
|
||||||
|
<div className={classes.content}>
|
||||||
|
{showCategories && hasCategories && (
|
||||||
|
<div className={classes.leader}>
|
||||||
|
{showCategories && hasCategories && (
|
||||||
|
<div>
|
||||||
|
{categories?.map((category, index) => {
|
||||||
|
const titleFromCategory = typeof category === 'string' ? category : category.title
|
||||||
|
|
||||||
|
const categoryTitle = titleFromCategory || 'Untitled category'
|
||||||
|
|
||||||
|
const isLast = index === categories.length - 1
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment key={index}>
|
||||||
|
{categoryTitle}
|
||||||
|
{!isLast && <Fragment>, </Fragment>}
|
||||||
|
</Fragment>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{titleToUse && (
|
||||||
|
<h4 className={classes.title}>
|
||||||
|
<Link className={classes.titleLink} href={href}>
|
||||||
|
{titleToUse}
|
||||||
|
</Link>
|
||||||
|
</h4>
|
||||||
|
)}
|
||||||
|
{description && (
|
||||||
|
<div className={classes.body}>
|
||||||
|
{description && <p className={classes.description}>{sanitizedDescription}</p>}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user