Merge pull request #20999 from atom/dependency-automation

Automate dependency bumps
This commit is contained in:
Musa Ibrahim
2020-07-06 20:27:49 +01:00
committed by GitHub
18 changed files with 2642 additions and 3 deletions

View File

@@ -0,0 +1,80 @@
const fetch = require('node-fetch');
const npmCheck = require('npm-check');
// this may be updated to use github releases instead
const apm = async function({ dependencies, packageDependencies }) {
try {
console.log('Checking apm registry...');
const coreDependencies = Object.keys(dependencies).filter(dependency => {
// all core packages point to a remote url
return dependencies[dependency].match(new RegExp('^https?://'));
});
const promises = coreDependencies.map(async dependency => {
return fetch(`https://atom.io/api/packages/${dependency}`)
.then(res => res.json())
.then(res => res)
.catch(ex => console.log(ex.message));
});
const packages = await Promise.all(promises);
const outdatedPackages = [];
packages.map(dependency => {
if (dependency.hasOwnProperty('name')) {
const latestVersion = dependency.releases.latest;
const installed = packageDependencies[dependency.name];
if (latestVersion > installed) {
outdatedPackages.push({
moduleName: dependency.name,
latest: dependency.releases.latest,
isCorePackage: true,
installed
});
}
}
});
console.log(`${outdatedPackages.length} outdated package(s) found`);
return outdatedPackages;
} catch (ex) {
console.error(`An error occured: ${ex.message}`);
}
};
const npm = async function(cwd) {
try {
console.log('Checking npm registry...');
const currentState = await npmCheck({
cwd,
ignoreDev: true,
skipUnused: true
});
const outdatedPackages = currentState
.get('packages')
.filter(p => {
if (p.packageJson && p.latest && p.installed) {
return p.latest > p.installed;
}
})
.map(({ packageJson, installed, moduleName, latest }) => ({
packageJson,
installed,
moduleName,
latest,
isCorePackage: false
}));
console.log(`${outdatedPackages.length} outdated package(s) found`);
return outdatedPackages;
} catch (ex) {
console.error(`An error occured: ${ex.message}`);
}
};
module.exports = {
apm,
npm
};

View File

@@ -0,0 +1,67 @@
const git = (git, repositoryRootPath) => {
const path = require('path');
const packageJsonFilePath = path.join(repositoryRootPath, 'package.json');
const packageLockFilePath = path.join(
repositoryRootPath,
'package-lock.json'
);
try {
git.getRemotes((err, remotes) => {
if (!err && !remotes.map(({ name }) => name).includes('ATOM')) {
git.addRemote(
'ATOM',
`https://atom:${process.env.AUTH_TOKEN}@github.com/atom/atom.git/`
);
}
});
} catch (ex) {
console.log(ex.message);
}
return {
switchToMaster: async function() {
const { current } = await git.branch();
if (current !== 'master') {
await git.checkout('master');
}
},
makeBranch: async function(dependency) {
const newBranch = `${dependency.moduleName}-${dependency.latest}`;
const { branches } = await git.branch();
const { files } = await git.status();
if (files.length > 0) {
await git.reset('hard');
}
const found = Object.keys(branches).find(
branch => branch.indexOf(newBranch) > -1
);
found
? await git.checkout(found)
: await git.checkoutLocalBranch(newBranch);
return { found, newBranch };
},
createCommit: async function({ moduleName, latest }) {
try {
const commitMessage = `:arrow_up: ${moduleName}@${latest}`;
await git.add([packageJsonFilePath, packageLockFilePath]);
await git.commit(commitMessage);
} catch (ex) {
throw Error(ex.message);
}
},
publishBranch: async function(branch) {
try {
await git.push('ATOM', branch);
} catch (ex) {
throw Error(ex.message);
}
},
deleteBranch: async function(branch) {
try {
await git.deleteLocalBranch(branch, true);
} catch (ex) {
throw Error(ex.message);
}
}
};
};
module.exports = git;

View File

@@ -0,0 +1,3 @@
const run = require('./main');
run();

View File

@@ -0,0 +1,97 @@
/* eslint-disable camelcase */
const simpleGit = require('simple-git');
const path = require('path');
const { repositoryRootPath } = require('../../config');
const packageJSON = require(path.join(repositoryRootPath, 'package.json'));
const git = simpleGit(repositoryRootPath);
const { createPR, findPR, addLabel } = require('./pull-request');
const runApmInstall = require('../run-apm-install');
const {
makeBranch,
createCommit,
switchToMaster,
publishBranch,
deleteBranch
} = require('./git')(git, repositoryRootPath);
const { updatePackageJson, sleep } = require('./util')(repositoryRootPath);
const fetchOutdatedDependencies = require('./fetch-outdated-dependencies');
module.exports = async function() {
try {
// ensure we are on master
await switchToMaster();
const failedBumps = [];
const successfullBumps = [];
const outdateDependencies = [
...(await fetchOutdatedDependencies.npm(repositoryRootPath)),
...(await fetchOutdatedDependencies.apm(packageJSON))
];
const totalDependencies = outdateDependencies.length;
const pendingPRs = [];
for (const dependency of outdateDependencies) {
const { found, newBranch } = await makeBranch(dependency);
if (found) {
console.log(`Branch was found ${found}`);
console.log('checking if a PR already exists');
const {
data: { total_count }
} = await findPR(dependency, newBranch);
if (total_count > 0) {
console.log(`pull request found!`);
} else {
console.log(`pull request not found!`);
const pr = { dependency, branch: newBranch, branchIsRemote: false };
// confirm if branch found is a local branch
if (found.indexOf('remotes') === -1) {
await publishBranch(found);
} else {
pr.branchIsRemote = true;
}
pendingPRs.push(pr);
}
} else {
await updatePackageJson(dependency);
runApmInstall(repositoryRootPath, false);
await createCommit(dependency);
await publishBranch(newBranch);
pendingPRs.push({
dependency,
branch: newBranch,
branchIsRemote: false
});
}
await switchToMaster();
}
// create PRs here
for (const { dependency, branch, branchIsRemote } of pendingPRs) {
const { status, data = {} } = await createPR(dependency, branch);
if (status === 201) {
successfullBumps.push(dependency);
await addLabel(data.number);
} else {
failedBumps.push(dependency);
}
if (!branchIsRemote) {
await deleteBranch(branch);
}
// https://developer.github.com/v3/guides/best-practices-for-integrators/#dealing-with-abuse-rate-limits
await sleep(2000);
}
console.table([
{
totalDependencies,
totalSuccessfullBumps: successfullBumps.length,
totalFailedBumps: failedBumps.length
}
]);
console.log('Successfull bumps');
console.table(successfullBumps);
console.log('Failed bumps');
console.table(failedBumps);
} catch (ex) {
console.log(ex.message);
}
};

View File

@@ -0,0 +1,40 @@
const { request } = require('@octokit/request');
const requestWithAuth = request.defaults({
baseUrl: 'https://api.github.com',
headers: {
'user-agent': 'atom',
authorization: `token ${process.env.AUTH_TOKEN}`
},
owner: 'atom',
repo: 'atom'
});
module.exports = {
createPR: async (
{ moduleName, isCorePackage, latest, installed },
branch
) => {
let description = `Bumps ${moduleName} from ${installed} to ${latest}`;
if (isCorePackage) {
description = `*List of changes between ${moduleName}@${installed} and ${moduleName}@${latest}: https://github.com/atom/${moduleName}/compare/v${installed}...v${latest}*`;
}
return requestWithAuth('POST /repos/:owner/:repo/pulls', {
title: `⬆️ ${moduleName}@${latest}`,
body: description,
base: 'master',
head: branch
});
},
findPR: async ({ moduleName, latest }, branch) => {
return requestWithAuth('GET /search/issues', {
q: `${moduleName} type:pr ${moduleName}@${latest} in:title repo:atom/atom head:${branch} state:open`
});
},
addLabel: async pullRequestNumber => {
return requestWithAuth('PATCH /repos/:owner/:repo/issues/:issue_number', {
labels: ['depency ⬆️'],
issue_number: pullRequestNumber
});
}
};

View File

@@ -0,0 +1,27 @@
const path = require('path');
const fetchOutdatedDependencies = require('../fetch-outdated-dependencies');
const { nativeDependencies } = require('./helpers');
const repositoryRootPath = path.resolve('.', 'fixtures', 'dummy');
const packageJSON = require(path.join(repositoryRootPath, 'package.json'));
describe('Fetch outdated dependencies', function() {
it('should fetch outdated native dependencies', async () => {
spyOn(fetchOutdatedDependencies, 'npm').andReturn(
Promise.resolve(nativeDependencies)
);
expect(await fetchOutdatedDependencies.npm(repositoryRootPath)).toEqual(
nativeDependencies
);
});
it('should fetch outdated core dependencies', async () => {
spyOn(fetchOutdatedDependencies, 'apm').andReturn(
Promise.resolve(nativeDependencies)
);
expect(await fetchOutdatedDependencies.apm(packageJSON)).toEqual(
nativeDependencies
);
});
});

View File

@@ -0,0 +1,524 @@
{
"url": "https://api.github.com/repos/atom/octocat/pulls/1347",
"id": 1,
"node_id": "MDExOlB1bGxSZXF1ZXN0MQ==",
"html_url": "https://github.com/atom/octocat/pull/1347",
"diff_url": "https://github.com/atom/octocat/pull/1347.diff",
"patch_url": "https://github.com/atom/octocat/pull/1347.patch",
"issue_url": "https://api.github.com/repos/atom/octocat/issues/1347",
"commits_url": "https://api.github.com/repos/atom/octocat/pulls/1347/commits",
"review_comments_url": "https://api.github.com/repos/atom/octocat/pulls/1347/comments",
"review_comment_url": "https://api.github.com/repos/atom/octocat/pulls/comments{/number}",
"comments_url": "https://api.github.com/repos/atom/octocat/issues/1347/comments",
"statuses_url": "https://api.github.com/repos/atom/octocat/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e",
"number": 1347,
"state": "open",
"locked": true,
"title": "⬆️ octocat@2.0.0",
"user": {
"login": "octocat",
"id": 1,
"node_id": "MDQ6VXNlcjE=",
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
"gravatar_id": "",
"url": "https://api.github.com/users/octocat",
"html_url": "https://github.com/octocat",
"followers_url": "https://api.github.com/users/octocat/followers",
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
"organizations_url": "https://api.github.com/users/octocat/orgs",
"repos_url": "https://api.github.com/users/octocat/repos",
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/octocat/received_events",
"type": "User",
"site_admin": false
},
"body": "Bumps octocat from 1.0.0 to 2.0.0",
"labels": [
{
"id": 208045946,
"node_id": "MDU6TGFiZWwyMDgwNDU5NDY=",
"url": "https://api.github.com/repos/atom/octocat/labels/bug",
"name": "bug",
"description": "Something isn't working",
"color": "f29513",
"default": true
}
],
"milestone": {
"url": "https://api.github.com/repos/atom/octocat/milestones/1",
"html_url": "https://github.com/atom/octocat/milestones/v1.0",
"labels_url": "https://api.github.com/repos/atom/octocat/milestones/1/labels",
"id": 1002604,
"node_id": "MDk6TWlsZXN0b25lMTAwMjYwNA==",
"number": 1,
"state": "open",
"title": "v1.0",
"description": "Tracking milestone for version 1.0",
"creator": {
"login": "octocat",
"id": 1,
"node_id": "MDQ6VXNlcjE=",
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
"gravatar_id": "",
"url": "https://api.github.com/users/octocat",
"html_url": "https://github.com/octocat",
"followers_url": "https://api.github.com/users/octocat/followers",
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
"organizations_url": "https://api.github.com/users/octocat/orgs",
"repos_url": "https://api.github.com/users/octocat/repos",
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/octocat/received_events",
"type": "User",
"site_admin": false
},
"open_issues": 4,
"closed_issues": 8,
"created_at": "2011-04-10T20:09:31Z",
"updated_at": "2014-03-03T18:58:10Z",
"closed_at": "2013-02-12T13:22:01Z",
"due_on": "2012-10-09T23:39:01Z"
},
"active_lock_reason": "too heated",
"created_at": "2011-01-26T19:01:12Z",
"updated_at": "2011-01-26T19:01:12Z",
"closed_at": "2011-01-26T19:01:12Z",
"merged_at": "2011-01-26T19:01:12Z",
"merge_commit_sha": "e5bd3914e2e596debea16f433f57875b5b90bcd6",
"assignee": {
"login": "octocat",
"id": 1,
"node_id": "MDQ6VXNlcjE=",
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
"gravatar_id": "",
"url": "https://api.github.com/users/octocat",
"html_url": "https://github.com/octocat",
"followers_url": "https://api.github.com/users/octocat/followers",
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
"organizations_url": "https://api.github.com/users/octocat/orgs",
"repos_url": "https://api.github.com/users/octocat/repos",
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/octocat/received_events",
"type": "User",
"site_admin": false
},
"assignees": [
{
"login": "octocat",
"id": 1,
"node_id": "MDQ6VXNlcjE=",
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
"gravatar_id": "",
"url": "https://api.github.com/users/octocat",
"html_url": "https://github.com/octocat",
"followers_url": "https://api.github.com/users/octocat/followers",
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
"organizations_url": "https://api.github.com/users/octocat/orgs",
"repos_url": "https://api.github.com/users/octocat/repos",
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/octocat/received_events",
"type": "User",
"site_admin": false
},
{
"login": "hubot",
"id": 1,
"node_id": "MDQ6VXNlcjE=",
"avatar_url": "https://github.com/images/error/hubot_happy.gif",
"gravatar_id": "",
"url": "https://api.github.com/users/hubot",
"html_url": "https://github.com/hubot",
"followers_url": "https://api.github.com/users/hubot/followers",
"following_url": "https://api.github.com/users/hubot/following{/other_user}",
"gists_url": "https://api.github.com/users/hubot/gists{/gist_id}",
"starred_url": "https://api.github.com/users/hubot/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/hubot/subscriptions",
"organizations_url": "https://api.github.com/users/hubot/orgs",
"repos_url": "https://api.github.com/users/hubot/repos",
"events_url": "https://api.github.com/users/hubot/events{/privacy}",
"received_events_url": "https://api.github.com/users/hubot/received_events",
"type": "User",
"site_admin": true
}
],
"requested_reviewers": [
{
"login": "other_user",
"id": 1,
"node_id": "MDQ6VXNlcjE=",
"avatar_url": "https://github.com/images/error/other_user_happy.gif",
"gravatar_id": "",
"url": "https://api.github.com/users/other_user",
"html_url": "https://github.com/other_user",
"followers_url": "https://api.github.com/users/other_user/followers",
"following_url": "https://api.github.com/users/other_user/following{/other_user}",
"gists_url": "https://api.github.com/users/other_user/gists{/gist_id}",
"starred_url": "https://api.github.com/users/other_user/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/other_user/subscriptions",
"organizations_url": "https://api.github.com/users/other_user/orgs",
"repos_url": "https://api.github.com/users/other_user/repos",
"events_url": "https://api.github.com/users/other_user/events{/privacy}",
"received_events_url": "https://api.github.com/users/other_user/received_events",
"type": "User",
"site_admin": false
}
],
"requested_teams": [
{
"id": 1,
"node_id": "MDQ6VGVhbTE=",
"url": "https://api.github.com/teams/1",
"html_url": "https://api.github.com/teams/justice-league",
"name": "Justice League",
"slug": "justice-league",
"description": "A great team.",
"privacy": "closed",
"permission": "admin",
"members_url": "https://api.github.com/teams/1/members{/member}",
"repositories_url": "https://api.github.com/teams/1/repos",
"parent": null
}
],
"head": {
"label": "atom:octocat-2.0.0",
"ref": "octocat-2.0.0",
"sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e",
"user": {
"login": "octocat",
"id": 1,
"node_id": "MDQ6VXNlcjE=",
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
"gravatar_id": "",
"url": "https://api.github.com/users/octocat",
"html_url": "https://github.com/octocat",
"followers_url": "https://api.github.com/users/octocat/followers",
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
"organizations_url": "https://api.github.com/users/octocat/orgs",
"repos_url": "https://api.github.com/users/octocat/repos",
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/octocat/received_events",
"type": "User",
"site_admin": false
},
"repo": {
"id": 1296269,
"node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5",
"name": "Hello-World",
"full_name": "atom/octocat",
"owner": {
"login": "octocat",
"id": 1,
"node_id": "MDQ6VXNlcjE=",
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
"gravatar_id": "",
"url": "https://api.github.com/users/octocat",
"html_url": "https://github.com/octocat",
"followers_url": "https://api.github.com/users/octocat/followers",
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
"organizations_url": "https://api.github.com/users/octocat/orgs",
"repos_url": "https://api.github.com/users/octocat/repos",
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/octocat/received_events",
"type": "User",
"site_admin": false
},
"private": false,
"html_url": "https://github.com/atom/octocat",
"description": "This your first repo!",
"fork": false,
"url": "https://api.github.com/repos/atom/octocat",
"archive_url": "http://api.github.com/repos/atom/octocat/{archive_format}{/ref}",
"assignees_url": "http://api.github.com/repos/atom/octocat/assignees{/user}",
"blobs_url": "http://api.github.com/repos/atom/octocat/git/blobs{/sha}",
"branches_url": "http://api.github.com/repos/atom/octocat/branches{/branch}",
"collaborators_url": "http://api.github.com/repos/atom/octocat/collaborators{/collaborator}",
"comments_url": "http://api.github.com/repos/atom/octocat/comments{/number}",
"commits_url": "http://api.github.com/repos/atom/octocat/commits{/sha}",
"compare_url": "http://api.github.com/repos/atom/octocat/compare/{base}...{head}",
"contents_url": "http://api.github.com/repos/atom/octocat/contents/{+path}",
"contributors_url": "http://api.github.com/repos/atom/octocat/contributors",
"deployments_url": "http://api.github.com/repos/atom/octocat/deployments",
"downloads_url": "http://api.github.com/repos/atom/octocat/downloads",
"events_url": "http://api.github.com/repos/atom/octocat/events",
"forks_url": "http://api.github.com/repos/atom/octocat/forks",
"git_commits_url": "http://api.github.com/repos/atom/octocat/git/commits{/sha}",
"git_refs_url": "http://api.github.com/repos/atom/octocat/git/refs{/sha}",
"git_tags_url": "http://api.github.com/repos/atom/octocat/git/tags{/sha}",
"git_url": "git:github.com/atom/octocat.git",
"issue_comment_url": "http://api.github.com/repos/atom/octocat/issues/comments{/number}",
"issue_events_url": "http://api.github.com/repos/atom/octocat/issues/events{/number}",
"issues_url": "http://api.github.com/repos/atom/octocat/issues{/number}",
"keys_url": "http://api.github.com/repos/atom/octocat/keys{/key_id}",
"labels_url": "http://api.github.com/repos/atom/octocat/labels{/name}",
"languages_url": "http://api.github.com/repos/atom/octocat/languages",
"merges_url": "http://api.github.com/repos/atom/octocat/merges",
"milestones_url": "http://api.github.com/repos/atom/octocat/milestones{/number}",
"notifications_url": "http://api.github.com/repos/atom/octocat/notifications{?since,all,participating}",
"pulls_url": "http://api.github.com/repos/atom/octocat/pulls{/number}",
"releases_url": "http://api.github.com/repos/atom/octocat/releases{/id}",
"ssh_url": "git@github.com:atom/octocat.git",
"stargazers_url": "http://api.github.com/repos/atom/octocat/stargazers",
"statuses_url": "http://api.github.com/repos/atom/octocat/statuses/{sha}",
"subscribers_url": "http://api.github.com/repos/atom/octocat/subscribers",
"subscription_url": "http://api.github.com/repos/atom/octocat/subscription",
"tags_url": "http://api.github.com/repos/atom/octocat/tags",
"teams_url": "http://api.github.com/repos/atom/octocat/teams",
"trees_url": "http://api.github.com/repos/atom/octocat/git/trees{/sha}",
"clone_url": "https://github.com/atom/octocat.git",
"mirror_url": "git:git.example.com/atom/octocat",
"hooks_url": "http://api.github.com/repos/atom/octocat/hooks",
"svn_url": "https://svn.github.com/atom/octocat",
"homepage": "https://github.com",
"language": null,
"forks_count": 9,
"stargazers_count": 80,
"watchers_count": 80,
"size": 108,
"default_branch": "master",
"open_issues_count": 0,
"is_template": true,
"topics": [
"octocat",
"atom",
"electron",
"api"
],
"has_issues": true,
"has_projects": true,
"has_wiki": true,
"has_pages": false,
"has_downloads": true,
"archived": false,
"disabled": false,
"visibility": "public",
"pushed_at": "2011-01-26T19:06:43Z",
"created_at": "2011-01-26T19:01:12Z",
"updated_at": "2011-01-26T19:14:43Z",
"permissions": {
"admin": false,
"push": false,
"pull": true
},
"allow_rebase_merge": true,
"template_repository": null,
"temp_clone_token": "ABTLWHOULUVAXGTRYU7OC2876QJ2O",
"allow_squash_merge": true,
"delete_branch_on_merge": true,
"allow_merge_commit": true,
"subscribers_count": 42,
"network_count": 0
}
},
"base": {
"label": "octocat:master",
"ref": "master",
"sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e",
"user": {
"login": "octocat",
"id": 1,
"node_id": "MDQ6VXNlcjE=",
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
"gravatar_id": "",
"url": "https://api.github.com/users/octocat",
"html_url": "https://github.com/octocat",
"followers_url": "https://api.github.com/users/octocat/followers",
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
"organizations_url": "https://api.github.com/users/octocat/orgs",
"repos_url": "https://api.github.com/users/octocat/repos",
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/octocat/received_events",
"type": "User",
"site_admin": false
},
"repo": {
"id": 1296269,
"node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5",
"name": "Hello-World",
"full_name": "atom/octocat",
"owner": {
"login": "octocat",
"id": 1,
"node_id": "MDQ6VXNlcjE=",
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
"gravatar_id": "",
"url": "https://api.github.com/users/octocat",
"html_url": "https://github.com/octocat",
"followers_url": "https://api.github.com/users/octocat/followers",
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
"organizations_url": "https://api.github.com/users/octocat/orgs",
"repos_url": "https://api.github.com/users/octocat/repos",
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/octocat/received_events",
"type": "User",
"site_admin": false
},
"private": false,
"html_url": "https://github.com/atom/octocat",
"description": "This your first repo!",
"fork": false,
"url": "https://api.github.com/repos/atom/octocat",
"archive_url": "http://api.github.com/repos/atom/octocat/{archive_format}{/ref}",
"assignees_url": "http://api.github.com/repos/atom/octocat/assignees{/user}",
"blobs_url": "http://api.github.com/repos/atom/octocat/git/blobs{/sha}",
"branches_url": "http://api.github.com/repos/atom/octocat/branches{/branch}",
"collaborators_url": "http://api.github.com/repos/atom/octocat/collaborators{/collaborator}",
"comments_url": "http://api.github.com/repos/atom/octocat/comments{/number}",
"commits_url": "http://api.github.com/repos/atom/octocat/commits{/sha}",
"compare_url": "http://api.github.com/repos/atom/octocat/compare/{base}...{head}",
"contents_url": "http://api.github.com/repos/atom/octocat/contents/{+path}",
"contributors_url": "http://api.github.com/repos/atom/octocat/contributors",
"deployments_url": "http://api.github.com/repos/atom/octocat/deployments",
"downloads_url": "http://api.github.com/repos/atom/octocat/downloads",
"events_url": "http://api.github.com/repos/atom/octocat/events",
"forks_url": "http://api.github.com/repos/atom/octocat/forks",
"git_commits_url": "http://api.github.com/repos/atom/octocat/git/commits{/sha}",
"git_refs_url": "http://api.github.com/repos/atom/octocat/git/refs{/sha}",
"git_tags_url": "http://api.github.com/repos/atom/octocat/git/tags{/sha}",
"git_url": "git:github.com/atom/octocat.git",
"issue_comment_url": "http://api.github.com/repos/atom/octocat/issues/comments{/number}",
"issue_events_url": "http://api.github.com/repos/atom/octocat/issues/events{/number}",
"issues_url": "http://api.github.com/repos/atom/octocat/issues{/number}",
"keys_url": "http://api.github.com/repos/atom/octocat/keys{/key_id}",
"labels_url": "http://api.github.com/repos/atom/octocat/labels{/name}",
"languages_url": "http://api.github.com/repos/atom/octocat/languages",
"merges_url": "http://api.github.com/repos/atom/octocat/merges",
"milestones_url": "http://api.github.com/repos/atom/octocat/milestones{/number}",
"notifications_url": "http://api.github.com/repos/atom/octocat/notifications{?since,all,participating}",
"pulls_url": "http://api.github.com/repos/atom/octocat/pulls{/number}",
"releases_url": "http://api.github.com/repos/atom/octocat/releases{/id}",
"ssh_url": "git@github.com:atom/octocat.git",
"stargazers_url": "http://api.github.com/repos/atom/octocat/stargazers",
"statuses_url": "http://api.github.com/repos/atom/octocat/statuses/{sha}",
"subscribers_url": "http://api.github.com/repos/atom/octocat/subscribers",
"subscription_url": "http://api.github.com/repos/atom/octocat/subscription",
"tags_url": "http://api.github.com/repos/atom/octocat/tags",
"teams_url": "http://api.github.com/repos/atom/octocat/teams",
"trees_url": "http://api.github.com/repos/atom/octocat/git/trees{/sha}",
"clone_url": "https://github.com/atom/octocat.git",
"mirror_url": "git:git.example.com/atom/octocat",
"hooks_url": "http://api.github.com/repos/atom/octocat/hooks",
"svn_url": "https://svn.github.com/atom/octocat",
"homepage": "https://github.com",
"language": null,
"forks_count": 9,
"stargazers_count": 80,
"watchers_count": 80,
"size": 108,
"default_branch": "master",
"open_issues_count": 0,
"is_template": true,
"topics": [
"octocat",
"atom",
"electron",
"api"
],
"has_issues": true,
"has_projects": true,
"has_wiki": true,
"has_pages": false,
"has_downloads": true,
"archived": false,
"disabled": false,
"visibility": "public",
"pushed_at": "2011-01-26T19:06:43Z",
"created_at": "2011-01-26T19:01:12Z",
"updated_at": "2011-01-26T19:14:43Z",
"permissions": {
"admin": false,
"push": false,
"pull": true
},
"allow_rebase_merge": true,
"template_repository": null,
"temp_clone_token": "ABTLWHOULUVAXGTRYU7OC2876QJ2O",
"allow_squash_merge": true,
"delete_branch_on_merge": true,
"allow_merge_commit": true,
"subscribers_count": 42,
"network_count": 0
}
},
"_links": {
"self": {
"href": "https://api.github.com/repos/atom/octocat/pulls/1347"
},
"html": {
"href": "https://github.com/atom/octocat/pull/1347"
},
"issue": {
"href": "https://api.github.com/repos/atom/octocat/issues/1347"
},
"comments": {
"href": "https://api.github.com/repos/atom/octocat/issues/1347/comments"
},
"review_comments": {
"href": "https://api.github.com/repos/atom/octocat/pulls/1347/comments"
},
"review_comment": {
"href": "https://api.github.com/repos/atom/octocat/pulls/comments{/number}"
},
"commits": {
"href": "https://api.github.com/repos/atom/octocat/pulls/1347/commits"
},
"statuses": {
"href": "https://api.github.com/repos/atom/octocat/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e"
}
},
"author_association": "OWNER",
"draft": false,
"merged": false,
"mergeable": true,
"rebaseable": true,
"mergeable_state": "clean",
"merged_by": {
"login": "octocat",
"id": 1,
"node_id": "MDQ6VXNlcjE=",
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
"gravatar_id": "",
"url": "https://api.github.com/users/octocat",
"html_url": "https://github.com/octocat",
"followers_url": "https://api.github.com/users/octocat/followers",
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
"organizations_url": "https://api.github.com/users/octocat/orgs",
"repos_url": "https://api.github.com/users/octocat/repos",
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/octocat/received_events",
"type": "User",
"site_admin": false
},
"comments": 10,
"review_comments": 0,
"maintainer_can_modify": true,
"commits": 3,
"additions": 100,
"deletions": 3,
"changed_files": 5
}

Submodule script/lib/update-dependency/spec/fixtures/dummy added at 526c508361

View File

@@ -0,0 +1,28 @@
{
"name": "test",
"version": "1.0.0",
"description": "just test",
"main": "index.js",
"dependencies": {
"spell-check": "https://www.atom.io/api/packages/spell-check/versions/0.79.1/tarball",
"status-bar": "https://www.atom.io/api/packages/status-bar/versions/2.8.17/tarball",
"styleguide": "https://www.atom.io/api/packages/styleguide/versions/1.49.12/tarball",
"symbols-view": "https://www.atom.io/api/packages/symbols-view/versions/0.118.5/tarball",
"@atom/watcher": "1.3.1",
"clear-cut": "^2.0.3",
"dedent": "^1.0.0",
"devtron": "1.2.6"
},
"packageDependencies": {
"spell-check": "0.79.1",
"status-bar": "2.8.17",
"styleguide": "1.49.12",
"symbols-view": "0.118.5"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "darangi",
"license": "ISC"
}

View File

@@ -0,0 +1,41 @@
{
"total_count": 40,
"incomplete_results": false,
"items": [
{
"id": 3081286,
"node_id": "MDEwOlJlcG9zaXRvcnkzMDgxMjg2",
"name": "Tetris",
"full_name": "dtrupenn/Tetris",
"owner": {
"login": "dtrupenn",
"id": 872147,
"node_id": "MDQ6VXNlcjg3MjE0Nw==",
"avatar_url": "https://secure.gravatar.com/avatar/e7956084e75f239de85d3a31bc172ace?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-user-420.png",
"gravatar_id": "",
"url": "https://api.github.com/users/dtrupenn",
"received_events_url": "https://api.github.com/users/dtrupenn/received_events",
"type": "User"
},
"private": false,
"html_url": "https://github.com/dtrupenn/Tetris",
"description": "A C implementation of Tetris using Pennsim through LC4",
"fork": false,
"url": "https://api.github.com/repos/dtrupenn/Tetris",
"created_at": "2012-01-01T00:31:50Z",
"updated_at": "2013-01-05T17:58:47Z",
"pushed_at": "2012-01-01T00:37:02Z",
"homepage": "",
"size": 524,
"stargazers_count": 1,
"watchers_count": 1,
"language": "Assembly",
"forks_count": 0,
"open_issues_count": 0,
"master_branch": "master",
"default_branch": "master",
"score": 1.0
}
]
}

View File

@@ -0,0 +1,94 @@
const path = require('path');
const simpleGit = require('simple-git');
const repositoryRootPath = path.resolve('.', 'fixtures', 'dummy');
const git = simpleGit(repositoryRootPath);
const {
switchToMaster,
makeBranch,
publishBranch,
createCommit,
deleteBranch
} = require('../git')(git, repositoryRootPath);
describe('GIT', () => {
async function findBranch(branch) {
const { branches } = await git.branch();
return Object.keys(branches).find(_branch => _branch.indexOf(branch) > -1);
}
const dependency = {
moduleName: 'atom',
latest: '2.0.0'
};
const branch = `${dependency.moduleName}-${dependency.latest}`;
beforeEach(async () => {
await git.checkout('master');
});
it('remotes should include ATOM', async () => {
const remotes = await git.getRemotes();
expect(remotes.map(({ name }) => name).includes('ATOM')).toBeTruthy();
});
it('current branch should be master', async () => {
const testBranchExists = await findBranch('test');
testBranchExists
? await git.checkout('test')
: await git.checkoutLocalBranch('test');
expect((await git.branch()).current).toBe('test');
await switchToMaster();
expect((await git.branch()).current).toBe('master');
await git.deleteLocalBranch('test', true);
});
it('should make new branch and checkout to the new branch', async () => {
const { found, newBranch } = await makeBranch(dependency);
expect(found).toBe(undefined);
expect(newBranch).toBe(branch);
expect((await git.branch()).current).toBe(branch);
await git.checkout('master');
await git.deleteLocalBranch(branch, true);
});
it('should find an existing branch and checkout to the branch', async () => {
await git.checkoutLocalBranch(branch);
const { found } = await makeBranch(dependency);
expect(found).not.toBe(undefined);
expect((await git.branch()).current).toBe(found);
await git.checkout('master');
await git.deleteLocalBranch(branch, true);
});
it('should create a commit', async () => {
const packageJsonFilePath = path.join(repositoryRootPath, 'package.json');
const packageLockFilePath = path.join(
repositoryRootPath,
'package-lock.json'
);
spyOn(git, 'commit');
spyOn(git, 'add');
await createCommit(dependency);
expect(git.add).toHaveBeenCalledWith([
packageJsonFilePath,
packageLockFilePath
]);
expect(git.commit).toHaveBeenCalledWith(
`${`:arrow_up: ${dependency.moduleName}@${dependency.latest}`}`
);
});
it('should publish branch', async () => {
spyOn(git, 'push');
await publishBranch(branch);
expect(git.push).toHaveBeenCalledWith('ATOM', branch);
});
it('should delete an existing branch', async () => {
await git.checkoutLocalBranch(branch);
await git.checkout('master');
expect(await findBranch(branch)).not.toBe(undefined);
await deleteBranch(branch);
expect(await findBranch(branch)).toBe(undefined);
});
});

View File

@@ -0,0 +1,28 @@
const latestPackageJSON = require('./fixtures/latest-package.json');
const packageJSON = require('./fixtures/dummy/package.json');
module.exports = {
coreDependencies: Object.keys(packageJSON.packageDependencies).map(
dependency => {
return {
latest: latestPackageJSON.packageDependencies[dependency],
installed: packageJSON.packageDependencies[dependency],
moduleName: dependency,
isCorePackage: true
};
}
),
nativeDependencies: Object.keys(packageJSON.dependencies)
.filter(
dependency =>
!packageJSON.dependencies[dependency].match(new RegExp('^https?://'))
)
.map(dependency => {
return {
latest: latestPackageJSON.dependencies[dependency],
packageJson: packageJSON.dependencies[dependency],
installed: packageJSON.dependencies[dependency],
moduleName: dependency,
isCorePackage: false
};
})
};

View File

@@ -0,0 +1,53 @@
const nock = require('nock');
const { createPR, findPR } = require('../pull-request');
const createPrResponse = require('./fixtures/create-pr-response.json');
const searchResponse = require('./fixtures/search-response.json');
describe('Pull Request', () => {
it('Should create a pull request', async () => {
const scope = nock('https://api.github.com')
.post('/repos/atom/atom/pulls', {
title: '⬆️ octocat@2.0.0',
body: 'Bumps octocat from 1.0.0 to 2.0.0',
head: 'octocat-2.0.0',
base: 'master'
})
.reply(200, createPrResponse);
const response = await createPR(
{
moduleName: 'octocat',
installed: '1.0.0',
latest: '2.0.0',
isCorePackage: false
},
'octocat-2.0.0'
);
scope.done();
expect(response.data).toEqual(createPrResponse);
});
it('Should search for a pull request', async () => {
const scope = nock('https://api.github.com')
.get('/search/issues')
.query({
q:
'octocat type:pr octocat@2.0.0 in:title repo:atom/atom head:octocat-2.0.0 state:open',
owner: 'atom',
repo: 'atom'
})
.reply(200, searchResponse);
const response = await findPR(
{
moduleName: 'octocat',
installed: '1.0.0',
latest: '2.0.0'
},
'octocat-2.0.0'
);
scope.done();
expect(response.data).toEqual(searchResponse);
});
});

View File

@@ -0,0 +1,38 @@
const path = require('path');
const fs = require('fs');
const repositoryRootPath = path.resolve('.', 'fixtures', 'dummy');
const packageJsonFilePath = path.join(repositoryRootPath, 'package.json');
const { updatePackageJson } = require('../util')(repositoryRootPath);
const { coreDependencies, nativeDependencies } = require('./helpers');
describe('Update-dependency', function() {
const oldPackageJson = JSON.parse(
JSON.stringify(require(packageJsonFilePath))
);
var packageJson;
it('bumps package.json properly', async function() {
const dependencies = [...coreDependencies, ...nativeDependencies];
for (const dependency of dependencies) {
await updatePackageJson(dependency);
packageJson = JSON.parse(fs.readFileSync(packageJsonFilePath, 'utf-8'));
if (dependency.isCorePackage) {
expect(packageJson.packageDependencies[dependency.moduleName]).toBe(
dependency.latest
);
expect(packageJson.dependencies[dependency.moduleName]).toContain(
dependency.latest
);
} else {
expect(packageJson.dependencies[dependency.moduleName]).toBe(
dependency.latest
);
}
}
fs.writeFileSync(
packageJsonFilePath,
JSON.stringify(oldPackageJson, null, 2)
);
});
});

View File

@@ -0,0 +1,61 @@
const fs = require('fs');
const path = require('path');
const util = repositoryRootPath => {
const packageJsonFilePath = path.join(repositoryRootPath, 'package.json');
const packageJSON = require(packageJsonFilePath);
return {
updatePackageJson: async function({
moduleName,
installed,
latest,
isCorePackage = false,
packageJson = ''
}) {
console.log(`Bumping ${moduleName} from ${installed} to ${latest}`);
const updatePackageJson = JSON.parse(JSON.stringify(packageJSON));
if (updatePackageJson.dependencies[moduleName]) {
let searchString = installed;
// gets the exact version installed in package json for native packages
if (!isCorePackage) {
if (/\^|~/.test(packageJson)) {
searchString = new RegExp(`\\${packageJson}`);
} else {
searchString = packageJson;
}
}
updatePackageJson.dependencies[
moduleName
] = updatePackageJson.dependencies[moduleName].replace(
searchString,
latest
);
}
if (updatePackageJson.packageDependencies[moduleName]) {
updatePackageJson.packageDependencies[
moduleName
] = updatePackageJson.packageDependencies[moduleName].replace(
new RegExp(installed),
latest
);
}
return new Promise((resolve, reject) => {
fs.writeFile(
packageJsonFilePath,
JSON.stringify(updatePackageJson, null, 2),
function(err) {
if (err) {
return reject(err);
}
console.log(`Bumped ${moduleName} from ${installed} to ${latest}`);
return resolve();
}
);
});
},
sleep: ms => new Promise(resolve => setTimeout(resolve, ms))
};
};
module.exports = util;

1426
script/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,8 @@
"description": "Atom build scripts",
"dependencies": {
"7zip-bin": "^4.0.2",
"@atom/electron-winstaller": "0.0.1",
"@octokit/request": "^5.4.5",
"async": "2.0.1",
"babel-core": "5.8.38",
"babel-eslint": "^10.0.1",
@@ -14,7 +16,6 @@
"electron-link": "0.4.1",
"electron-mksnapshot": "^9.0.2",
"electron-packager": "12.2.0",
"@atom/electron-winstaller": "0.0.1",
"eslint": "^5.16.0",
"eslint-config-prettier": "^4.2.0",
"eslint-config-standard": "^12.0.0",
@@ -33,14 +34,18 @@
"lodash.template": "4.5.0",
"minidump": "0.9.0",
"mkdirp": "0.5.1",
"nock": "^13.0.2",
"node-fetch": "^2.6.0",
"normalize-package-data": "2.3.5",
"npm": "6.14.4",
"npm-check": "^5.9.2",
"passwd-user": "2.1.0",
"pegjs": "0.9.0",
"prettier": "^1.17.0",
"random-seed": "^0.3.0",
"season": "5.3.0",
"semver": "5.3.0",
"simple-git": "^2.7.0",
"stylelint": "^9.0.0",
"stylelint-config-standard": "^18.1.0",
"sync-request": "3.0.1",

View File

@@ -58,3 +58,33 @@ jobs:
ATOM_RELEASES_S3_BUCKET: $(ATOM_RELEASES_S3_BUCKET)
PACKAGE_CLOUD_API_KEY: $(PACKAGE_CLOUD_API_KEY)
displayName: Create Nightly Release
- job: bump_dependencies
displayName: Bump Dependencies
timeoutInMinutes: 180
pool:
vmImage: macos-10.14
steps:
- task: NodeTool@0
inputs:
versionSpec: 12.13.1
displayName: Install Node.js 12.13.1
- script: npm install --global npm@6.12.1
displayName: Update npm
- script: |
script/bootstrap
displayName: Bootstrap
- script: |
cd script/lib
npm install
displayName: npm install
- script: |
cd script/lib/update-dependency
node index.js
displayName: Bump depedencies
env:
AUTH_TOKEN: $(GITHUB_TOKEN)