Skip to content

Commit e2f0fba

Browse files
committed
Merge branch 'main' into widget-richtext
2 parents 02f54af + f95c085 commit e2f0fba

File tree

68 files changed

+2755
-2282
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+2755
-2282
lines changed

.github/workflows/nodejs.yml

Lines changed: 60 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
name: Node CI
22

3+
concurrency:
4+
group: ci-${{ github.workflow }}-${{ github.ref }}
5+
cancel-in-progress: true
6+
37
on:
48
push:
9+
branches:
10+
- main
11+
- master
512
pull_request:
613
types: [opened, synchronize, reopened]
714

@@ -11,51 +18,91 @@ jobs:
1118
outputs:
1219
cms: ${{ steps.filter.outputs.cms }}
1320
steps:
14-
- uses: actions/checkout@v3
21+
- uses: actions/checkout@v4
1522
- uses: dorny/paths-filter@v2
1623
id: filter
1724
with:
1825
filters: |
1926
cms:
2027
- '!website/**'
2128
22-
build:
29+
build-unit:
2330
needs: changes
2431
runs-on: ${{ matrix.os }}
2532
strategy:
2633
matrix:
27-
os: [macos-latest, windows-latest, ubuntu-latest]
28-
node-version: [20.x, 22.x]
34+
os: [ubuntu-latest, macos-latest, windows-latest]
35+
node-version: [24.x]
36+
include:
37+
- os: ubuntu-latest
38+
node-version: 22.x
2939
fail-fast: true
3040
if: ${{ needs.changes.outputs.cms == 'true' }}
3141
steps:
32-
- uses: actions/checkout@v3
42+
- uses: actions/checkout@v4
3343
- name: Use Node.js ${{ matrix.node-version }}
34-
uses: actions/setup-node@v3
44+
uses: actions/setup-node@v4
3545
with:
3646
node-version: ${{ matrix.node-version }}
3747
check-latest: true
3848
cache: 'npm'
3949
- name: log versions
40-
run: node --version && npm --version && yarn --version
41-
- name: install dependencies
50+
run: node --version && npm --version
51+
- name: install dependencies (skip Cypress binary)
4252
run: npm ci
43-
- name: run unit tests
53+
env:
54+
CYPRESS_INSTALL_BINARY: 0
55+
- name: run lint + types + unit tests
4456
run: npm run test:ci
57+
58+
e2e:
59+
needs: changes
60+
if: ${{ needs.changes.outputs.cms == 'true' }}
61+
runs-on: ubuntu-latest
62+
strategy:
63+
fail-fast: true
64+
matrix:
65+
machine: [1, 2, 3, 4]
66+
steps:
67+
- uses: actions/checkout@v4
68+
- name: Use Node.js 24.x
69+
uses: actions/setup-node@v4
70+
with:
71+
node-version: 24.x
72+
check-latest: true
73+
cache: 'npm'
74+
- name: Cache Nx local cache
75+
uses: actions/cache@v4
76+
with:
77+
path: .nx/cache
78+
key: nx-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
79+
restore-keys: |
80+
nx-${{ runner.os }}-
81+
- name: Cache Cypress binary
82+
uses: actions/cache@v4
83+
with:
84+
path: ~/.cache/Cypress
85+
key: cypress-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
86+
restore-keys: |
87+
cypress-${{ runner.os }}-
88+
- name: install dependencies
89+
run: npm ci
4590
- name: build demo site
4691
run: npm run build:demo
47-
- name: run e2e tests
48-
if: matrix.os == 'ubuntu-latest' && matrix.node-version == '22.x'
92+
- name: run e2e tests (parallel)
4993
run: npm run test:e2e:run-ci
5094
env:
95+
CI_BUILD_ID: ${{ github.run_id }}-${{ github.run_attempt }}
5196
IS_FORK: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true || github.repository_owner != 'decaporg' }}
97+
MACHINE_INDEX: ${{ matrix.machine }}
98+
MACHINE_COUNT: 4
5299
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
53100
NODE_OPTIONS: --max-old-space-size=4096
54101
TZ: Europe/Amsterdam
55102
- uses: actions/upload-artifact@v4
56-
if: matrix.os == 'ubuntu-latest' && matrix.node-version == '22.x' && failure()
103+
if: failure()
57104
with:
58-
name: cypress-results
105+
name: cypress-results-${{ matrix.machine }}
59106
path: |
60107
cypress/screenshots
61108
cypress/videos

.github/workflows/publish.yml

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
name: Publish Packages
2+
3+
on:
4+
push:
5+
tags:
6+
- 'decap-*@*'
7+
8+
permissions:
9+
contents: read
10+
id-token: write # Required for OIDC trusted publishers
11+
12+
jobs:
13+
publish:
14+
runs-on: ubuntu-latest
15+
environment: production # Optional: adds approval requirements and deployment protection
16+
steps:
17+
- name: Checkout
18+
uses: actions/checkout@v4
19+
with:
20+
fetch-depth: 0 # Required for Lerna to detect changes
21+
22+
- name: Setup Node.js
23+
uses: actions/setup-node@v4
24+
with:
25+
node-version: '24'
26+
registry-url: 'https://registry.npmjs.org'
27+
28+
- name: Install dependencies
29+
run: npm ci
30+
31+
- name: Build packages
32+
run: npm run build
33+
34+
- name: Run tests
35+
run: npm run test:ci
36+
37+
- name: Debug
38+
run: |
39+
echo "::group::Node & npm versions"
40+
node -v
41+
npm -v
42+
echo "::endgroup::"
43+
44+
echo "::group::npm config (redacted)"
45+
npm config get registry || true
46+
npm config list -l | sed 's/_authToken.*/_authToken=[REDACTED]/' || true
47+
echo "::endgroup::"
48+
49+
echo "::group::Env checks"
50+
if [ -n "${NODE_AUTH_TOKEN}" ]; then echo "NODE_AUTH_TOKEN set? yes"; else echo "NODE_AUTH_TOKEN set? no"; fi
51+
if [ -n "${NPM_CONFIG_USERCONFIG}" ]; then echo "NPM_CONFIG_USERCONFIG set? yes"; else echo "NPM_CONFIG_USERCONFIG set? no"; fi
52+
echo "::endgroup::"
53+
54+
echo "::group::Git state"
55+
git status --porcelain || true
56+
git tag --list --points-at HEAD || true
57+
echo "::endgroup::"
58+
59+
echo "::group::Registry package info"
60+
npm view decap-server version || true
61+
npm view decap-server versions --json || true
62+
echo "::endgroup::"
63+
64+
echo "::group::Lerna packages"
65+
npx lerna ls --json || true
66+
echo "::endgroup::"
67+
68+
- name: Publish to npm
69+
run: npm run lerna:publish -- --loglevel silly
70+
# No NODE_AUTH_TOKEN needed - uses OIDC automatically via trusted publishers

.npmrc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
legacy-peer-deps=true
2-
lockfileVersion=3

CONTRIBUTING.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,63 @@ npx jest -t "true on p" ".+backend-gitlab/.+/API.spec.js"
193193

194194
For more information about running tests exactly the way you want, check out the official documentation for [Jest CLI](https://jestjs.io/docs/cli).
195195

196+
## Releasing
197+
198+
Decap CMS uses NPM trusted publishers with OIDC for secure, automated package publishing.
199+
200+
### How It Works
201+
202+
- Publishing is automated via GitHub Actions when version tags are pushed
203+
- Uses OpenID Connect (OIDC) for authentication. No NPM tokens required
204+
- Each package has a trusted publisher configured on npmjs.com
205+
- Workflow generates short-lived, cryptographically-signed tokens automatically
206+
- Publishes all changed packages in the monorepo via Lerna
207+
208+
### Release Process
209+
210+
1. **Prepare the release:**
211+
```sh
212+
# Ensure your local `main` branch is up to date
213+
npm prune
214+
npm install
215+
npm run test
216+
217+
# Bump versions for changed packages
218+
npx lerna version
219+
220+
# This will:
221+
# - Detect changed packages since last release
222+
# - Bump versions according to conventional commits
223+
# - Update CHANGELOG.md
224+
# - Create git commit and tags
225+
# - Push to upstream
226+
```
227+
228+
2. **Automated publishing:**
229+
- Tags pushed to `main` trigger the publish workflow automatically
230+
- GitHub Actions runs tests and builds packages
231+
- Lerna publishes changed packages to npm using OIDC
232+
- Provenance attestations are generated automatically
233+
234+
3. **Create GitHub release:**
235+
- Go to [Releases](https://github.com/decaporg/decap-cms/releases)
236+
- Draft a new release from the tag
237+
- Add release notes highlighting changes
238+
239+
### Manual Publishing (Emergency Only)
240+
241+
If automated publishing fails and you need to publish manually:
242+
243+
```sh
244+
# Authenticate with npm (uses session-based auth with 2FA)
245+
npm login
246+
247+
# Publish changed packages
248+
npm run lerna:publish
249+
```
250+
251+
Note: Manual publishing still requires 2FA. Use recovery codes if you don't have access to your 2FA device.
252+
196253
## License
197254

198255
By contributing to Decap CMS, you agree that your contributions will be licensed

SECURITY.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Security Policy
2+
3+
Decap CMS takes security seriously. This document outlines our security policy, supported versions, and how to report security vulnerabilities.
4+
5+
## Supported Versions
6+
7+
Security updates are provided for:
8+
9+
| Version | Status | Lifecycle |
10+
|---------|--------|-----------|
11+
| 3.x | ✅ Actively Supported | Current stable release |
12+
| 2.x (Netlify CMS) | ❌ Unsupported | Legacy - no updates |
13+
| 1.x (Netlify CMS) | ❌ Unsupported | Legacy - no updates |
14+
15+
**Note:** Decap CMS was renamed from Netlify CMS in February 2023. Versions 1.x and 2.x are no longer maintained. We recommend upgrading to version 3.x for security updates and new features.
16+
17+
## Reporting a Vulnerability
18+
19+
If you discover a security vulnerability in Decap CMS, please report it **confidentially** through GitHub Security Advisories. This allows us to investigate and address the issue without exposing it to the public until a fix is ready.
20+
21+
**Submit your report at:** https://github.com/decaporg/decap-cms/security/advisories/new
22+
23+
### What NOT to Do
24+
25+
- Do not open a public GitHub issue for the vulnerability
26+
- Do not post details on social media or public forums
27+
- Do not attempt to exploit the vulnerability beyond confirming it exists
28+
- Do not access data beyond what's necessary to demonstrate the issue
29+
30+
## Response Timeline
31+
32+
This project follows a 90-day disclosure timeline.
33+
34+
## Security Practices
35+
36+
- Dependabot is enabled for automated security update checks
37+
- All code changes are tested in CI, including linting
38+
- End-to-end tests provide coverage of critical functionality
39+
- All pull requests require code review before merging
40+
- Passwords are not stored by Decap CMS; authentication is delegated to providers
41+
42+
## Known Limitations
43+
44+
- This is a **community-maintained open-source project**, not a commercial product with dedicated security resources
45+
- Security depends on the stability and practices of underlying dependencies and backend providers
46+
- Some vulnerabilities in dependencies may not be immediately patchable if they break backwards compatibility
47+
- This is a project with a long history, and many legacy dependencies can't be updated without significant refactoring

babel.config.js

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,7 @@ const isESM = process.env.NODE_ENV === 'esm';
99
console.log('Build Package:', path.basename(process.cwd()));
1010

1111
// Always enabled plugins
12-
const basePlugins = [
13-
[
14-
'babel-plugin-transform-builtin-extend',
15-
{
16-
globals: ['Error'],
17-
},
18-
],
19-
'babel-plugin-inline-json-import',
20-
];
12+
const basePlugins = ['babel-plugin-inline-json-import'];
2113

2214
// All legacy transforms have been removed as they are now included in @babel/preset-env
2315
// Features like class properties, optional chaining, nullish coalescing are now standard in modern JS

cypress/run.mjs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,30 @@ async function runCypress() {
1010
process.exit(1);
1111
}
1212

13-
if (process.env.IS_FORK === 'true') {
14-
const machineIndex = parseInt(process.env.MACHINE_INDEX);
15-
const machineCount = parseInt(process.env.MACHINE_COUNT);
13+
const machineIndex = Number(process.env.MACHINE_INDEX || 0);
14+
const machineCount = Number(process.env.MACHINE_COUNT || 0);
15+
16+
if (machineIndex && machineCount) {
1617
const specsPerMachine = Math.floor(specs.length / machineCount);
1718
const start = (machineIndex - 1) * specsPerMachine;
1819
const machineSpecs =
1920
machineIndex === machineCount
2021
? specs.slice(start)
2122
: specs.slice(start, start + specsPerMachine);
2223

24+
console.log(
25+
`Sharding specs manually: machine ${machineIndex}/${machineCount} running ${machineSpecs.length} specs`,
26+
);
2327
args.push('--spec', machineSpecs.join(','));
2428
} else {
29+
const ciBuildId =
30+
process.env.CI_BUILD_ID || process.env.GITHUB_RUN_ID || process.env.GITHUB_SHA;
31+
2532
args.push(
2633
'--record',
2734
'--parallel',
2835
'--ci-build-id',
29-
process.env.GITHUB_SHA,
36+
ciBuildId,
3037
'--group',
3138
'GitHub CI',
3239
'--spec',
@@ -35,7 +42,11 @@ async function runCypress() {
3542
}
3643

3744
console.log('Running Cypress with args:', args.join(' '));
38-
await execa('cypress', args, { stdio: 'inherit', preferLocal: true });
45+
await execa('cypress', args, {
46+
stdio: 'inherit',
47+
preferLocal: true,
48+
timeout: 60 * 60 * 1000, // 1 hour
49+
});
3950
}
4051

4152
runCypress();

cypress/utils/config.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const fs = require('fs-extra');
22
const path = require('path');
3-
const yaml = require('js-yaml');
3+
const { parse, stringify } = require('yaml');
44

55
const devTestDirectory = path.join(__dirname, '..', '..', 'dev-test');
66
const backendsDirectory = path.join(devTestDirectory, 'backends');
@@ -18,10 +18,10 @@ async function copyBackendFiles(backend) {
1818

1919
async function updateConfig(configModifier) {
2020
const configFile = path.join(devTestDirectory, 'config.yml');
21-
const configContent = await fs.readFile(configFile);
22-
const config = yaml.load(configContent);
21+
const configContent = await fs.readFile(configFile, 'utf8');
22+
const config = parse(configContent);
2323
await configModifier(config);
24-
await fs.writeFileSync(configFile, yaml.dump(config));
24+
await fs.writeFile(configFile, stringify(config));
2525
}
2626

2727
async function switchVersion(version) {

0 commit comments

Comments
 (0)