Skip to content

Commit 955c125

Browse files
committed
(chore) Add automated dependency update workflow
Adds a workflow and script to automatically check for and update OpenMRS dependencies, including verification and deduplication. Other changes include: - Augmenting the existing Transifex integration pull workflow with auto-approve/merge capabilities - Improving workflow documentation - Moving config files such as the i18next parser config and Jest setup into the tools directory - Updating the primary CI workflow to use Node 20
1 parent b3b0a3a commit 955c125

File tree

11 files changed

+275
-106
lines changed

11 files changed

+275
-106
lines changed

.github/workflows/e2e.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
- name: Setup node
2323
uses: actions/setup-node@v4
2424
with:
25-
node-version: 18
25+
node-version: 20
2626

2727
- name: Cache dependencies
2828
id: cache-dependencies
@@ -59,7 +59,7 @@ jobs:
5959
run: yarn playwright test
6060

6161
- name: Stop dev server
62-
if: "!cancelled()"
62+
if: '!cancelled()'
6363
run: docker stop $(docker ps -a -q)
6464

6565
- name: 📤 Upload report

.github/workflows/node.js.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Node.js CI
1+
name: OpenMRS CI
22

33
on:
44
push:
@@ -23,7 +23,7 @@ jobs:
2323
- name: 🛠️ Setup Node.js
2424
uses: actions/setup-node@v4
2525
with:
26-
node-version: "18"
26+
node-version: 20
2727

2828
- name: 💾 Cache dependencies
2929
id: cache
@@ -66,7 +66,7 @@ jobs:
6666
- name: 🛠️ Setup Node.js
6767
uses: actions/setup-node@v4
6868
with:
69-
node-version: "18"
69+
node-version: 20
7070

7171
- name: 💾 Cache dependencies
7272
id: cache
@@ -120,7 +120,7 @@ jobs:
120120
- name: 🛠️ Use Node.js
121121
uses: actions/setup-node@v4
122122
with:
123-
node-version: "18"
123+
node-version: 20
124124
registry-url: 'https://registry.npmjs.org'
125125

126126
- name: 💾 Cache dependencies
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Workflow to automatically check and update OpenMRS dependencies
2+
# Runs hourly and can be triggered manually
3+
4+
name: 'Check for OpenMRS Dependency Updates'
5+
6+
on:
7+
workflow_dispatch:
8+
schedule:
9+
# Runs every hour at minute 30
10+
- cron: '30 * * * *'
11+
12+
jobs:
13+
check-for-updates:
14+
name: Check for updates to OpenMRS libraries
15+
runs-on: ubuntu-latest
16+
17+
if: github.repository_owner == 'openmrs'
18+
19+
permissions:
20+
contents: write
21+
pull-requests: write
22+
23+
steps:
24+
# Step 1: Check out repository
25+
- name: Checkout repository
26+
uses: actions/checkout@v4
27+
with:
28+
fetch-depth: 1 # Shallow clone for better performance
29+
30+
# Step 2: Setup Node.js environment
31+
- name: 🟢 Setup Node.js
32+
uses: actions/setup-node@v4
33+
with:
34+
node-version: 20
35+
36+
# Step 3: Cache dependencies
37+
- name: 💾 Cache dependencies
38+
id: cache
39+
uses: actions/cache@v4
40+
with:
41+
path: '**/node_modules'
42+
key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
43+
44+
# Step 4: Install dependencies if cache miss
45+
- name: 📦 Install dependencies
46+
if: steps.cache.outputs.cache-hit != 'true'
47+
run: yarn install --immutable
48+
49+
# Step 5: Run dependency update check
50+
- name: ✅ Check for updates
51+
run: node ./tools/update-openmrs-deps.mjs
52+
53+
# Step 6: Create PR with updates if necessary
54+
- name: ⬆️ Create PR if necessary
55+
id: cpr
56+
uses: peter-evans/create-pull-request@v7
57+
with:
58+
commit-message: '(chore) Update OpenMRS dependencies'
59+
title: '(chore) Update OpenMRS dependencies'
60+
body: |
61+
# OpenMRS Dependencies Update
62+
63+
This PR contains updates to OpenMRS dependencies.
64+
65+
## Changes
66+
* Automated dependency updates for OpenMRS packages
67+
* Generated by the OpenMRS Dependency Update workflow
68+
69+
## Verification
70+
- [ ] All dependencies are valid versions
71+
- [ ] No breaking changes introduced
72+
73+
> This PR was automatically generated and will be automatically merged if checks pass.
74+
branch: 'chore/update-openmrs-deps'
75+
author: 'OpenMRS Bot <infrastructure@openmrs.org>'
76+
token: ${{ secrets.OMRS_BOT_GH_TOKEN }}
77+
labels: |
78+
dependencies
79+
automated-pr
80+
delete-branch: true # Clean up branch after merge
81+
82+
# Step 7: Auto-approve the PR if created or updated
83+
- name: ✅ Auto approve PR
84+
if: steps.cpr.outputs.pull-request-operation == 'created' || steps.cpr.outputs.pull-request-operation == 'updated'
85+
run: gh pr review --approve "${{ steps.cpr.outputs.pull-request-number }}"
86+
env:
87+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
88+
89+
# Step 8: Auto-merge the PR if created or updated
90+
- name: 🔀 Auto merge PR
91+
if: steps.cpr.outputs.pull-request-operation == 'created' || steps.cpr.outputs.pull-request-operation == 'updated'
92+
run: gh pr merge --auto --squash "${{ steps.cpr.outputs.pull-request-number }}"
93+
env:
94+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

jest.config.js

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
1+
/**
2+
* @returns {Promise<import('jest').Config>}
3+
*/
14
module.exports = {
25
clearMocks: true,
36
transform: {
4-
"^.+\\.tsx?$": "@swc/jest",
7+
'^.+\\.tsx?$': '@swc/jest',
58
},
6-
transformIgnorePatterns: ["/node_modules/(?!@openmrs)"],
9+
transformIgnorePatterns: ['/node_modules/(?!@openmrs)'],
710
moduleNameMapper: {
8-
"\\.(s?css)$": "identity-obj-proxy",
9-
"@openmrs/esm-framework": "@openmrs/esm-framework/mock",
10-
"^dexie$": require.resolve("dexie"),
11-
"^lodash-es/(.*)$": "lodash/$1",
12-
"^lodash-es$": "lodash",
13-
"^uuid$": "<rootDir>/node_modules/uuid/dist/index.js",
11+
'\\.(s?css)$': 'identity-obj-proxy',
12+
'@openmrs/esm-framework': '@openmrs/esm-framework/mock',
13+
'^dexie$': require.resolve('dexie'),
14+
'^lodash-es/(.*)$': 'lodash/$1',
15+
'^lodash-es$': 'lodash',
16+
'^uuid$': '<rootDir>/node_modules/uuid/dist/index.js',
1417
},
15-
setupFilesAfterEnv: ["<rootDir>/src/setup-tests.ts"],
16-
testEnvironment: "jsdom",
18+
setupFilesAfterEnv: ['<rootDir>/tools/setup-tests.ts'],
19+
testEnvironment: 'jsdom',
1720
testEnvironmentOptions: {
18-
url: "http://localhost/",
21+
url: 'http://localhost/',
1922
},
20-
testPathIgnorePatterns: ["<rootDir>/e2e"],
23+
testPathIgnorePatterns: ['<rootDir>/e2e'],
2124
};

playwright.config.ts

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,30 @@
1-
import { devices, PlaywrightTestConfig } from "@playwright/test";
2-
import * as dotenv from "dotenv";
1+
import { devices, type PlaywrightTestConfig } from '@playwright/test';
2+
import * as dotenv from 'dotenv';
33
dotenv.config();
44

55
// See https://playwright.dev/docs/test-configuration.
66
const config: PlaywrightTestConfig = {
7-
testDir: "./e2e/specs",
7+
testDir: './e2e/specs',
88
timeout: 3 * 60 * 1000,
99
expect: {
1010
timeout: 40 * 1000,
1111
},
1212
workers: 1,
1313
forbidOnly: !!process.env.CI,
1414
retries: 0,
15-
reporter: process.env.CI
16-
? [["junit", { outputFile: "results.xml" }], ["html"]]
17-
: [["html"]],
18-
globalSetup: require.resolve("./e2e/core/global-setup"),
15+
reporter: process.env.CI ? [['junit', { outputFile: 'results.xml' }], ['html']] : [['html']],
16+
globalSetup: require.resolve('./e2e/core/global-setup'),
1917
use: {
2018
baseURL: `${process.env.E2E_BASE_URL}/spa/`,
21-
locale: "en-US",
22-
storageState: "e2e/storageState.json",
23-
video: "retain-on-failure",
19+
locale: 'en-US',
20+
storageState: 'e2e/storageState.json',
21+
video: 'retain-on-failure',
2422
},
2523
projects: [
2624
{
27-
name: "chromium",
25+
name: 'chromium',
2826
use: {
29-
...devices["Desktop Chrome"],
27+
...devices['Desktop Chrome'],
3028
},
3129
},
3230
],

src/setup-tests.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.

tools/setup-tests.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import '@testing-library/jest-dom';

tools/update-openmrs-deps.mjs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { execSync } from 'node:child_process';
2+
3+
try {
4+
execSync(`yarn up --fixed '@openmrs/esm-framework@next' '@openmrs/esm-form-engine-lib@next' 'openmrs@next'`, {
5+
stdio: ['ignore', 'inherit', 'inherit'],
6+
windowsHide: true,
7+
});
8+
} catch (error) {
9+
console.error(`Error while updating dependencies: ${error.message ?? error}`);
10+
process.exit(1);
11+
}
12+
13+
try {
14+
execSync(`yarn dedupe`, {
15+
stdio: ['ignore', 'inherit', 'inherit'],
16+
windowsHide: true,
17+
});
18+
} catch (error) {
19+
console.error(`Error while deduplicating dependencies: ${error.message ?? error}`);
20+
process.exit(1);
21+
}
22+
23+
try {
24+
execSync(`git diff-index --quiet HEAD --`, {
25+
stdio: 'ignore',
26+
windowsHide: true,
27+
});
28+
process.exit(0);
29+
} catch (error) {
30+
// git diff-index --quite HEAD --
31+
// exits with status 1 if there are changes; we only need to run yarn verify if there are changes
32+
}
33+
34+
try {
35+
execSync(`yarn verify`, {
36+
stdio: ['ignore', 'inherit', 'inherit'],
37+
windowsHide: true,
38+
});
39+
} catch (error) {
40+
console.error(`Error while running yarn verify: ${error.message ?? error}. Updates require manual intervention.`);
41+
process.exit(1);
42+
}

webpack.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
const config = (module.exports = require("openmrs/default-webpack-config"));
1+
const config = (module.exports = require('openmrs/default-webpack-config'));
22
module.exports = config;

0 commit comments

Comments
 (0)