Skip to content

Commit 033aa34

Browse files
committed
fix: package json can have no dependencies
1 parent 9c81b65 commit 033aa34

File tree

8 files changed

+156
-26
lines changed

8 files changed

+156
-26
lines changed

README.md

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,25 @@ GitHub Action that handles automated update of dependencies in package.json betw
1515

1616
## Why I Created This Action?
1717

18-
//TODO
18+
GitHub Action that handles automated update of dependencies in package.json between projects from the same GitHub organization.
19+
20+
The main goal was to automate bump of dependencies between packages from the same organization. You might have several projects depending on each other, and your option to efficiently work with them should not only be a monorepo. In my opinion, people reach for monorepo to quickly, letting themselves to solve one complex problem by introducing another one.
21+
22+
You cannot apply monorepo everywhere, sometimes it doesn't make sense, and you still have some dependencies that you need to bump manually. This action doesn't have such a problem.
23+
24+
## How It Works?
25+
26+
tl;dr To find dependent projects, GitHub Search is utilized.
27+
28+
1. You run this action in package `@myorg/test`
29+
1. After releasing `@myorg/test`, you want latest version of the package to be bumped in other packages in your organization/user called `myorg`
30+
1. The following search is performed `"@myorg/test" user:myorg in:file filename:package.json`
31+
1. Search is not perfect, quotations from `"@myorg/test"` are ignored and result can also contain repositories that have only `@myorg/test-sdk` as dependency
32+
1. All found repositories are cloned (except of `@myorg/test`)
33+
1. Action verifies if you really have `@myorg/test` in dependencies or devDependencies
34+
1. Now the rest, bumping + pushing + creating a pull request
35+
36+
Approach with using GitHub search has only one disadvantage, bumping will not work in forks, as forks do not show up in search results. It is still better than cloning all repositories from your organization.
1937

2038
## Action Flow
2139

@@ -48,11 +66,4 @@ repos_to_ignore | Comma-separated list of repositories that should not get updat
4866
# GITHUB_REPOSITORY provide name of org/user and the repo in which this workflow is suppose to run
4967
# PACKAGE_JSON_LOC=test is a path to package.json file against you want to test
5068
GITHUB_TOKEN=token PACKAGE_JSON_LOC=test GITHUB_REPOSITORY="lukasz-lab/.github" npm start
51-
```
52-
53-
## TODO
54-
55-
This action should not be used yet. I need to:
56-
- write decent amount of tests as this action can do some harm
57-
- make final clarification of search usage
58-
- provide examples
69+
```

dist/index.js

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11420,14 +11420,19 @@ const { createBranch, clone, push } = __webpack_require__(374);
1142011420
const { getReposList, createPr, getRepoDefaultBranch } = __webpack_require__(119);
1142111421
const { readPackageJson, parseCommaList, verifyDependencyType, installDependency } = __webpack_require__(918);
1142211422

11423+
/* eslint-disable sonarjs/cognitive-complexity */
11424+
/**
11425+
* Disabled the rule for this function as there is no way to make it shorter in a meaningfull way.
11426+
* It looks complex because of extensive usage of core package to log as much as possible
11427+
*/
1142311428
async function run() {
1142411429
try {
1142511430
const gitHubKey = process.env.GITHUB_TOKEN || core.getInput('github_token', { required: true });
1142611431
const committerUsername = core.getInput('committer_username') || 'web-flow';
1142711432
const committerEmail = core.getInput('committer_email') || '[email protected]';
1142811433
const commitMessageProd = core.getInput('commit_message_prod') || 'Update dependency';
1142911434
const commitMessageDev = core.getInput('commit_message_dev') || 'Update devDependency';
11430-
const packageJsonPath = process.env.PACKAGE_JSON_LOC || core.getInput('packagejson_loc');
11435+
const packageJsonPath = process.env.PACKAGE_JSON_LOC || core.getInput('packagejson_loc') || './';
1143111436
const reposToIgnore = core.getInput('repos_to_ignore');
1143211437

1143311438
const [owner, repo] = process.env.GITHUB_REPOSITORY.split('/');
@@ -11436,7 +11441,7 @@ async function run() {
1143611441
//by default repo where workflow runs should always be ignored
1143711442
ignoredRepositories.push(repo);
1143811443

11439-
const { name: dependencyName, version: dependencyVersion} = await readPackageJson(__webpack_require__.ab + "org-projects-dependency-manager/" + packageJsonPath || './' + '/package.json');
11444+
const { name: dependencyName, version: dependencyVersion} = await readPackageJson(__webpack_require__.ab + "org-projects-dependency-manager/" + packageJsonPath + '/package.json');
1144011445
core.info(`Identified dependency name as ${dependencyName} with version ${dependencyVersion}. Now it will be bumped in dependent projects.`);
1144111446

1144211447
const reposList = await getReposList(octokit, dependencyName, owner);
@@ -11448,7 +11453,7 @@ async function run() {
1144811453
for (const {path: filepath, repository: { name, html_url, node_id }} of reposList) {
1144911454
if (ignoredRepositories.includes(name)) continue;
1145011455

11451-
const cloneDir = __webpack_require__.ab + "clones/" + name;
11456+
const cloneDir = path.join(process.cwd(), './clones', name);
1145211457
await mkdir(cloneDir, {recursive: true});
1145311458

1145411459
const branchName = `bot/bump-${dependencyName}-${dependencyVersion}`;
@@ -11465,7 +11470,7 @@ async function run() {
1146511470
const packageJson = await readPackageJson(packageJsonLocation);
1146611471
const dependencyType = await verifyDependencyType(packageJson, dependencyName);
1146711472
if (dependencyType === 'NONE') {
11468-
core.info(`This is super strange. We could not find ${dependencyName} neither in dependencies property nor in the devDependencies property. This repo should not show up on the list as it should not show up in search results. On to the next one`);
11473+
core.info(`We could not find ${dependencyName} neither in dependencies property nor in the devDependencies property. No further steps will be performed. It was found as GitHub search is not perfect and you probably use a package with similar name.`);
1146911474
continue;
1147011475
}
1147111476

@@ -12128,8 +12133,8 @@ function verifyDependencyType(json, dependencyName) {
1212812133
const prodDependencies = json.dependencies;
1212912134
const devDependencies = json.devDependencies;
1213012135

12131-
const isProd = prodDependencies[dependencyName];
12132-
const isDev = devDependencies[dependencyName];
12136+
const isProd = prodDependencies && prodDependencies[dependencyName];
12137+
const isDev = devDependencies && devDependencies[dependencyName];
1213312138

1213412139
if (isProd && isDev) return 'PROD';
1213512140
if (!isProd && !isDev) return 'NONE';

lib/index.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,19 @@ const { createBranch, clone, push } = require('./git');
88
const { getReposList, createPr, getRepoDefaultBranch } = require('./api-calls');
99
const { readPackageJson, parseCommaList, verifyDependencyType, installDependency } = require('./utils');
1010

11+
/* eslint-disable sonarjs/cognitive-complexity */
12+
/**
13+
* Disabled the rule for this function as there is no way to make it shorter in a meaningfull way.
14+
* It looks complex because of extensive usage of core package to log as much as possible
15+
*/
1116
async function run() {
1217
try {
1318
const gitHubKey = process.env.GITHUB_TOKEN || core.getInput('github_token', { required: true });
1419
const committerUsername = core.getInput('committer_username') || 'web-flow';
1520
const committerEmail = core.getInput('committer_email') || '[email protected]';
1621
const commitMessageProd = core.getInput('commit_message_prod') || 'Update dependency';
1722
const commitMessageDev = core.getInput('commit_message_dev') || 'Update devDependency';
18-
const packageJsonPath = process.env.PACKAGE_JSON_LOC || core.getInput('packagejson_loc');
23+
const packageJsonPath = process.env.PACKAGE_JSON_LOC || core.getInput('packagejson_loc') || './';
1924
const reposToIgnore = core.getInput('repos_to_ignore');
2025

2126
const [owner, repo] = process.env.GITHUB_REPOSITORY.split('/');
@@ -24,7 +29,7 @@ async function run() {
2429
//by default repo where workflow runs should always be ignored
2530
ignoredRepositories.push(repo);
2631

27-
const { name: dependencyName, version: dependencyVersion} = await readPackageJson(path.join(process.cwd(), packageJsonPath || './', 'package.json'));
32+
const { name: dependencyName, version: dependencyVersion} = await readPackageJson(path.join(process.cwd(), packageJsonPath, 'package.json'));
2833
core.info(`Identified dependency name as ${dependencyName} with version ${dependencyVersion}. Now it will be bumped in dependent projects.`);
2934

3035
const reposList = await getReposList(octokit, dependencyName, owner);
@@ -53,7 +58,7 @@ async function run() {
5358
const packageJson = await readPackageJson(packageJsonLocation);
5459
const dependencyType = await verifyDependencyType(packageJson, dependencyName);
5560
if (dependencyType === 'NONE') {
56-
core.info(`This is super strange. We could not find ${dependencyName} neither in dependencies property nor in the devDependencies property. This repo should not show up on the list as it should not show up in search results. On to the next one`);
61+
core.info(`We could not find ${dependencyName} neither in dependencies property nor in the devDependencies property. No further steps will be performed. It was found as GitHub search is not perfect and you probably use a package with similar name.`);
5762
continue;
5863
}
5964

lib/utils.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ function verifyDependencyType(json, dependencyName) {
3030
const prodDependencies = json.dependencies;
3131
const devDependencies = json.devDependencies;
3232

33-
const isProd = prodDependencies[dependencyName];
34-
const isDev = devDependencies[dependencyName];
33+
const isProd = prodDependencies && prodDependencies[dependencyName];
34+
const isDev = devDependencies && devDependencies[dependencyName];
3535

3636
if (isProd && isDev) return 'PROD';
3737
if (!isProd && !isDev) return 'NONE';

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"scripts": {
77
"start": "rimraf ./clones && node lib/index.js",
88
"dev": "DEBUG=simple-git npm start",
9-
"package": "ncc build lib/index.js -o dist",
9+
"package": "ncc build lib/index.js -o dist && rimraf dist/org-projects-dependency-manager",
1010
"gitAdd": "git add dist/index.js",
1111
"gen-readme-toc": "markdown-toc -i README.md",
1212
"lint": "eslint --max-warnings 0 .",

test/package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
{
22
"name": "@asyncapi/generator",
3-
"version": "1.0.1"
4-
}
5-
3+
"version": "1.0.0-rc.15"
4+
}

test/utils.test.js

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
const { parseCommaList, verifyDependencyType } = require('../lib/utils');
2+
3+
describe('parseCommaList()', () => {
4+
it('parses role model string without issues', () => {
5+
const list = 'test1, test2, test3, test4';
6+
const parsed = parseCommaList(list);
7+
8+
expect(parsed).toStrictEqual(['test1', 'test2', 'test3', 'test4']);
9+
});
10+
11+
it('parses string with many spaces between commas', () => {
12+
const list = 'test1 , test2, test3, test4';
13+
const parsed = parseCommaList(list);
14+
15+
expect(parsed).toStrictEqual(['test1', 'test2', 'test3', 'test4']);
16+
});
17+
18+
it('parses string if quotes are used', () => {
19+
const list = '\'test1\' , \'test2\', "test3", "test4"';
20+
const parsed = parseCommaList(list);
21+
22+
expect(parsed).toStrictEqual(['test1', 'test2', 'test3', 'test4']);
23+
});
24+
});
25+
26+
describe('verifyDependencyType()', () => {
27+
it('returns NONE type if neither dependencies nor devDependencies are in the file', () => {
28+
const parsedPackageJson = {
29+
name: '@my/test1',
30+
version: '1.0.0'
31+
};
32+
33+
const nameOfDependencyToCheck = '@my/test1';
34+
const verifyResult = verifyDependencyType(parsedPackageJson, nameOfDependencyToCheck);
35+
36+
expect(verifyResult).toStrictEqual('NONE');
37+
});
38+
39+
it('returns NONE type if verified package is neither in dependencies nor in devDependencies', () => {
40+
const parsedPackageJson = {
41+
name: '@my/test1',
42+
version: '1.0.0',
43+
dependencies: {
44+
'@my/test2': '0.0.1'
45+
},
46+
devDependencies: {
47+
'@my/test2': '0.0.1'
48+
}
49+
};
50+
51+
const nameOfDependencyToCheck = '@my/test1';
52+
const verifyResult = verifyDependencyType(parsedPackageJson, nameOfDependencyToCheck);
53+
54+
expect(verifyResult).toStrictEqual('NONE');
55+
});
56+
57+
it('returns PROD type if verified package is in dependencies', () => {
58+
const parsedPackageJson = {
59+
name: '@my/test1',
60+
version: '1.0.0',
61+
dependencies: {
62+
'@my/test1': '0.0.1'
63+
},
64+
devDependencies: {
65+
'@my/test2': '0.0.1'
66+
}
67+
};
68+
69+
const nameOfDependencyToCheck = '@my/test1';
70+
const verifyResult = verifyDependencyType(parsedPackageJson, nameOfDependencyToCheck);
71+
72+
expect(verifyResult).toStrictEqual('PROD');
73+
});
74+
75+
it('returns DEV type if verified package is in devDependencies', () => {
76+
const parsedPackageJson = {
77+
name: '@my/test1',
78+
version: '1.0.0',
79+
dependencies: {
80+
'@my/test2': '0.0.1'
81+
},
82+
devDependencies: {
83+
'@my/test1': '0.0.1'
84+
}
85+
};
86+
87+
const nameOfDependencyToCheck = '@my/test1';
88+
const verifyResult = verifyDependencyType(parsedPackageJson, nameOfDependencyToCheck);
89+
90+
expect(verifyResult).toStrictEqual('DEV');
91+
});
92+
93+
it('returns PROD type if verified package is in dependencies and devDependencies', () => {
94+
const parsedPackageJson = {
95+
name: '@my/test1',
96+
version: '1.0.0',
97+
dependencies: {
98+
'@my/test1': '0.0.1'
99+
},
100+
devDependencies: {
101+
'@my/test1': '0.0.1'
102+
}
103+
};
104+
105+
const nameOfDependencyToCheck = '@my/test1';
106+
const verifyResult = verifyDependencyType(parsedPackageJson, nameOfDependencyToCheck);
107+
108+
expect(verifyResult).toStrictEqual('PROD');
109+
});
110+
});

0 commit comments

Comments
 (0)