From be35cc80d0281cfe6ccc34fb775419ac5ca142f6 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 12 Jul 2024 16:56:07 -0500 Subject: [PATCH 1/2] Reapply "Revert "ESM support (#569)" (#587)" This reverts commit 67bfd13 Co-authored-by: Matt Wynne Co-authored-by: nitrocode <7775707+nitrocode@users.noreply.github.com> BREAKING CHANGE: the project is now ESM only --- index.js | 5 +- lib/mergeArrayByName.js | 6 +- lib/plugins/branches.js | 2 +- lib/plugins/collaborators.js | 4 +- lib/plugins/diffable.js | 2 +- lib/plugins/labels.js | 4 +- lib/plugins/milestones.js | 4 +- lib/plugins/repository.js | 2 +- lib/plugins/teams.js | 4 +- lib/settings.js | 26 ++-- package.json | 3 +- script/{console => console.cjs} | 0 test/integration/common.js | 76 ++++++++++++ .../integration/plugins/collaborators.test.js | 45 +++++++ test/integration/plugins/milestones.test.js | 64 ++++++++++ test/integration/plugins/repository.test.js | 113 ++++++++++++++++++ test/integration/plugins/teams.test.js | 58 +++++++++ test/integration/triggers/push.test.js | 26 ++++ .../triggers/repository-created.test.js | 33 +++++ .../triggers/repository-edited.test.js | 43 +++++++ test/unit/index.test.js | 18 ++- test/unit/lib/mergeArrayByName.test.js | 4 +- test/unit/lib/plugins/branches.test.js | 3 +- test/unit/lib/plugins/collaborators.test.js | 3 +- test/unit/lib/plugins/labels.test.js | 3 +- test/unit/lib/plugins/milestones.test.js | 3 +- test/unit/lib/plugins/repository.test.js | 3 +- test/unit/lib/plugins/teams.test.js | 7 +- 28 files changed, 520 insertions(+), 44 deletions(-) rename script/{console => console.cjs} (100%) create mode 100644 test/integration/common.js create mode 100644 test/integration/plugins/collaborators.test.js create mode 100644 test/integration/plugins/milestones.test.js create mode 100644 test/integration/plugins/repository.test.js create mode 100644 test/integration/plugins/teams.test.js create mode 100644 test/integration/triggers/push.test.js create mode 100644 test/integration/triggers/repository-created.test.js create mode 100644 test/integration/triggers/repository-edited.test.js diff --git a/index.js b/index.js index d8aaeeafe8..167b48832c 100644 --- a/index.js +++ b/index.js @@ -1,9 +1,10 @@ -const mergeArrayByName = require('./lib/mergeArrayByName') +import mergeArrayByName from './lib/mergeArrayByName' +import SettingsApp from './lib/settings' /** * @param {import('probot').Probot} robot */ -module.exports = (robot, _, Settings = require('./lib/settings')) => { +export default (robot, _, Settings = SettingsApp) => { async function syncSettings (context, repo = context.repo()) { const config = await context.config('settings.yml', {}, { arrayMerge: mergeArrayByName }) return Settings.sync(context.octokit, repo, config) diff --git a/lib/mergeArrayByName.js b/lib/mergeArrayByName.js index 1caa6956b8..f1ef9dbb72 100644 --- a/lib/mergeArrayByName.js +++ b/lib/mergeArrayByName.js @@ -1,6 +1,6 @@ // https://github.com/KyleAMathews/deepmerge#arraymerge -const merge = require('deepmerge') +import merge from 'deepmerge' function findMatchingIndex (sourceItem, target) { if (Object.prototype.hasOwnProperty.call(sourceItem, 'name')) { @@ -10,7 +10,7 @@ function findMatchingIndex (sourceItem, target) { } } -function mergeByName (target, source, options) { +export default function mergeByName (target, source, options) { const destination = target.slice() source.forEach(sourceItem => { @@ -24,5 +24,3 @@ function mergeByName (target, source, options) { return destination } - -module.exports = mergeByName diff --git a/lib/plugins/branches.js b/lib/plugins/branches.js index 2c487523e4..a176455d76 100644 --- a/lib/plugins/branches.js +++ b/lib/plugins/branches.js @@ -3,7 +3,7 @@ const previewHeaders = { 'application/vnd.github.hellcat-preview+json,application/vnd.github.luke-cage-preview+json,application/vnd.github.zzzax-preview+json' } -module.exports = class Branches { +export default class Branches { constructor (github, repo, settings) { this.github = github this.repo = repo diff --git a/lib/plugins/collaborators.js b/lib/plugins/collaborators.js index 71f67a6389..27d0fb41a5 100644 --- a/lib/plugins/collaborators.js +++ b/lib/plugins/collaborators.js @@ -1,6 +1,6 @@ -const Diffable = require('./diffable') +import Diffable from './diffable' -module.exports = class Collaborators extends Diffable { +export default class Collaborators extends Diffable { constructor (...args) { super(...args) diff --git a/lib/plugins/diffable.js b/lib/plugins/diffable.js index e95af70966..a3101b0654 100644 --- a/lib/plugins/diffable.js +++ b/lib/plugins/diffable.js @@ -19,7 +19,7 @@ // remove(existing) { // } // } -module.exports = class Diffable { +export default class Diffable { constructor (github, repo, entries) { this.github = github this.repo = repo diff --git a/lib/plugins/labels.js b/lib/plugins/labels.js index f1a03fb681..6a6d53ecf6 100644 --- a/lib/plugins/labels.js +++ b/lib/plugins/labels.js @@ -1,7 +1,7 @@ -const Diffable = require('./diffable') +import Diffable from './diffable' const previewHeaders = { accept: 'application/vnd.github.symmetra-preview+json' } -module.exports = class Labels extends Diffable { +export default class Labels extends Diffable { constructor (...args) { super(...args) diff --git a/lib/plugins/milestones.js b/lib/plugins/milestones.js index c6dff78336..904b8a4bd8 100644 --- a/lib/plugins/milestones.js +++ b/lib/plugins/milestones.js @@ -1,6 +1,6 @@ -const Diffable = require('./diffable') +import Diffable from './diffable' -module.exports = class Milestones extends Diffable { +export default class Milestones extends Diffable { constructor (...args) { super(...args) diff --git a/lib/plugins/repository.js b/lib/plugins/repository.js index e4ed8ed803..cfb3800077 100644 --- a/lib/plugins/repository.js +++ b/lib/plugins/repository.js @@ -32,7 +32,7 @@ const enableVulnerabilityAlerts = ({ github, settings, enabled }) => { return github.repos[methodName](args) } -module.exports = class Repository { +export default class Repository { constructor (github, repo, settings) { this.github = github this.settings = Object.assign({ mediaType: { previews: ['baptiste'] } }, settings, repo) diff --git a/lib/plugins/teams.js b/lib/plugins/teams.js index 176bae85d1..bc34f61b23 100644 --- a/lib/plugins/teams.js +++ b/lib/plugins/teams.js @@ -1,10 +1,10 @@ -const Diffable = require('./diffable') +import Diffable from './diffable' // it is necessary to use this endpoint until GitHub Enterprise supports // the modern version under /orgs const teamRepoEndpoint = '/teams/:team_id/repos/:owner/:repo' -module.exports = class Teams extends Diffable { +export default class Teams extends Diffable { find () { return this.github.repos.listTeams(this.repo).then(res => res.data) } diff --git a/lib/settings.js b/lib/settings.js index cf2da95db5..755d4740e6 100644 --- a/lib/settings.js +++ b/lib/settings.js @@ -1,4 +1,12 @@ -class Settings { +import Repository from './plugins/repository' +import Labels from './plugins/labels' +import Collaborators from './plugins/collaborators' +import Teams from './plugins/teams' +import Milestones from './plugins/milestones' +import Branches from './plugins/branches' +import Environments from './plugins/environments' + +export default class Settings { static sync (github, repo, config) { return new Settings(github, repo, config).update() } @@ -35,13 +43,11 @@ class Settings { Settings.FILE_NAME = '.github/settings.yml' Settings.PLUGINS = { - repository: require('./plugins/repository'), - labels: require('./plugins/labels'), - collaborators: require('./plugins/collaborators'), - environments: require('./plugins/environments'), - teams: require('./plugins/teams'), - milestones: require('./plugins/milestones'), - branches: require('./plugins/branches') + repository: Repository, + labels: Labels, + collaborators: Collaborators, + teams: Teams, + milestones: Milestones, + branches: Branches, + environments: Environments } - -module.exports = Settings diff --git a/package.json b/package.json index 5fa89b82b7..206058255a 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.0.0-semantically-released", "description": "Pull Requests for GitHub repository settings", "repository": "github:repository-settings/app", + "type": "module", "main": "index.js", "scripts": { "dev": "nodemon", @@ -14,7 +15,7 @@ "lint:engines": "ls-engines", "lint:peer": "npm ls >/dev/null", "lint:publish": "publint --strict", - "test:unit": "jest 'test/unit/'", + "test:unit": "NODE_OPTIONS=--experimental-vm-modules NODE_NO_WARNINGS=1 jest 'test/unit/'", "test:unit:watch": "npm run test:unit -- --watch", "test:integration": "run-s 'test:integration:base -- --profile noWip'", "test:integration:base": "NODE_OPTIONS=--enable-source-maps DEBUG=any cucumber-js test/integration", diff --git a/script/console b/script/console.cjs similarity index 100% rename from script/console rename to script/console.cjs diff --git a/test/integration/common.js b/test/integration/common.js new file mode 100644 index 0000000000..83e9917cd1 --- /dev/null +++ b/test/integration/common.js @@ -0,0 +1,76 @@ +import { Probot } from 'probot' +import nock from 'nock' +import any from '@travi/any' +import settingsBot from '../../index' +import settings from '../../lib/settings' + +nock.disableNetConnect() + +const repository = { + default_branch: 'master', + name: 'botland', + owner: { + name: 'bkeepers-inc', + login: 'bkeepers-inc', + email: null + } +} + +function loadInstance () { + const probot = new Probot({ appId: 1, privateKey: 'test', githubToken: 'test' }) + probot.load(settingsBot) + + return probot +} + +function initializeNock () { + return nock('https://api.github.com') +} + +function teardownNock (githubScope) { + expect(githubScope.pendingMocks()).toStrictEqual([]) + + nock.cleanAll() +} + +function buildPushEvent () { + return { + name: 'push', + payload: { + ref: 'refs/heads/master', + repository, + commits: [{ modified: [settings.FILE_NAME], added: [] }] + } + } +} + +function buildRepositoryEditedEvent () { + return { + name: 'repository.edited', + payload: { + changes: { default_branch: { from: any.word() } }, + repository + } + } +} + +function buildRepositoryCreatedEvent () { + return { + name: 'repository.created', + payload: { repository } + } +} + +function buildTriggerEvent () { + return any.fromList([buildPushEvent(), buildRepositoryCreatedEvent(), buildRepositoryEditedEvent()]) +} + +export { + loadInstance, + initializeNock, + teardownNock, + buildTriggerEvent, + buildRepositoryCreatedEvent, + buildRepositoryEditedEvent, + repository +} diff --git a/test/integration/plugins/collaborators.test.js b/test/integration/plugins/collaborators.test.js new file mode 100644 index 0000000000..01985faee2 --- /dev/null +++ b/test/integration/plugins/collaborators.test.js @@ -0,0 +1,45 @@ +import fs from 'fs' +import { CREATED, NO_CONTENT, OK } from 'http-status-codes' +import Settings from '../../../lib/settings' +import { buildTriggerEvent, initializeNock, loadInstance, repository, teardownNock } from '../common' + +describe('collaborators plugin', function () { + let probot, githubScope + + beforeEach(() => { + githubScope = initializeNock() + probot = loadInstance() + }) + + afterEach(() => { + teardownNock(githubScope) + }) + + it('syncs collaborators', async () => { + const pathToConfig = new URL('../../fixtures/collaborators-config.yml', import.meta.url) + const configFile = Buffer.from(fs.readFileSync(pathToConfig, 'utf8')) + const config = configFile.toString() + githubScope + .get(`/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent(Settings.FILE_NAME)}`) + .reply(OK, config) + githubScope.get(`/repos/${repository.owner.name}/${repository.name}/collaborators?affiliation=direct`).reply(OK, [ + { login: 'travi', permissions: { admin: true } }, + { login: 'bkeepers', permissions: { push: true } } + ]) + githubScope + .put(`/repos/${repository.owner.name}/${repository.name}/collaborators/hubot`, body => { + expect(body).toMatchObject({ permission: 'pull' }) + return true + }) + .reply(CREATED) + githubScope + .put(`/repos/${repository.owner.name}/${repository.name}/collaborators/octokit-bot`, body => { + expect(body).toMatchObject({ permission: 'triage' }) + return true + }) + .reply(CREATED) + githubScope.delete(`/repos/${repository.owner.name}/${repository.name}/collaborators/travi`).reply(NO_CONTENT) + + await probot.receive(buildTriggerEvent()) + }) +}) diff --git a/test/integration/plugins/milestones.test.js b/test/integration/plugins/milestones.test.js new file mode 100644 index 0000000000..876415af6f --- /dev/null +++ b/test/integration/plugins/milestones.test.js @@ -0,0 +1,64 @@ +import fs from 'fs' +import { CREATED, NO_CONTENT, OK } from 'http-status-codes' +import Settings from '../../../lib/settings' +import { buildTriggerEvent, initializeNock, loadInstance, repository, teardownNock } from '../common' + +describe('milestones plugin', function () { + let probot, githubScope + + beforeEach(() => { + githubScope = initializeNock() + probot = loadInstance() + }) + + afterEach(() => { + teardownNock(githubScope) + }) + + it('syncs milestones', async () => { + const pathToConfig = new URL('../../fixtures/milestones-config.yml', import.meta.url) + const configFile = Buffer.from(fs.readFileSync(pathToConfig, 'utf8')) + const config = configFile.toString() + githubScope + .get(`/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent(Settings.FILE_NAME)}`) + .reply(OK, config) + githubScope.patch(`/repos/${repository.owner.name}/${repository.name}`).reply(200) + githubScope.get(`/repos/${repository.owner.name}/${repository.name}/milestones?per_page=100&state=all`).reply(OK, [ + { + number: 42, + title: 'existing-milestone', + description: 'this milestone should get updated', + state: 'open' + }, + { + number: 8, + title: 'old-milestone', + description: 'this milestone should get deleted', + state: 'closed' + } + ]) + githubScope + .post(`/repos/${repository.owner.name}/${repository.name}/milestones`, body => { + expect(body).toMatchObject({ + title: 'new-milestone', + description: 'this milestone should get added', + state: 'open' + }) + return true + }) + .reply(CREATED) + githubScope + .patch(`/repos/${repository.owner.name}/${repository.name}/milestones/42`, body => { + expect(body).toMatchObject({ + title: 'existing-milestone', + description: 'this milestone should get updated', + state: 'closed' + }) + return true + }) + .reply(OK) + githubScope.delete(`/repos/${repository.owner.name}/${repository.name}/milestones/8`).reply(NO_CONTENT) + + await probot.receive(buildTriggerEvent()) + }) +}) diff --git a/test/integration/plugins/repository.test.js b/test/integration/plugins/repository.test.js new file mode 100644 index 0000000000..3b6aa97a80 --- /dev/null +++ b/test/integration/plugins/repository.test.js @@ -0,0 +1,113 @@ +import path from 'path' +import fs from 'fs' +import Settings from '../../../lib/settings' +import { buildTriggerEvent, initializeNock, loadInstance, repository, teardownNock } from '../common' + +const loadConfig = configFilename => { + const pathToConfig = new URL(path.join('../../fixtures/repository', configFilename), import.meta.url) + const configFile = Buffer.from(fs.readFileSync(pathToConfig, 'utf8')) + + return configFile.toString() +} + +describe('repository plugin', function () { + let probot, githubScope + + beforeEach(() => { + githubScope = initializeNock() + probot = loadInstance() + }) + + afterEach(() => { + teardownNock(githubScope) + }) + + it('syncs repo with basic settings', async () => { + const config = loadConfig('basic-config.yml') + const repoSettings = Object.assign({}, config.repository) + + githubScope + .get(`/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent(Settings.FILE_NAME)}`) + .reply(200, config) + githubScope + .patch(`/repos/${repository.owner.name}/${repository.name}`, body => { + expect(body).toMatchObject(repoSettings) + return true + }) + .matchHeader('accept', ['application/vnd.github.baptiste-preview+json']) + .reply(200) + + await probot.receive(buildTriggerEvent()) + }) + + it('replaces topics, when provided', async () => { + const config = loadConfig('basic-config-with-topics.yml') + const repoSettings = Object.assign({}, config.repository) + + githubScope + .get(`/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent(Settings.FILE_NAME)}`) + .reply(200, config) + githubScope + .patch(`/repos/${repository.owner.name}/${repository.name}`, body => { + expect(body).toMatchObject(repoSettings) + return true + }) + .matchHeader('accept', ['application/vnd.github.baptiste-preview+json']) + .reply(200) + githubScope + .put(`/repos/${repository.owner.name}/${repository.name}/topics`, body => { + expect(body).toMatchObject({ names: ['github', 'probot'] }) + return true + }) + .matchHeader('accept', ['application/vnd.github.mercy-preview+json']) + .reply(200) + + await probot.receive(buildTriggerEvent()) + }) + + it('syncs repo with basic settings and vulnerability alerts enabled', async () => { + const config = loadConfig('basic-config-with-vulnerability-alerts.yml') + const repoSettings = Object.assign({}, config.repository) + delete repoSettings.enable_vulnerability_alerts + + githubScope + .get(`/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent(Settings.FILE_NAME)}`) + .reply(200, config) + githubScope + .patch(`/repos/${repository.owner.name}/${repository.name}`, body => { + expect(body).toMatchObject(repoSettings) + return true + }) + .matchHeader('accept', ['application/vnd.github.baptiste-preview+json']) + .reply(200) + githubScope + .put(`/repos/${repository.owner.name}/${repository.name}/vulnerability-alerts`, body => true) + .matchHeader('accept', ['application/vnd.github.dorian-preview+json']) + .reply(200) + + await probot.receive(buildTriggerEvent()) + }) + + it('syncs repo with basic settings and security fixes enabled', async () => { + const config = loadConfig('basic-config-with-security-fixes.yml') + const repoSettings = Object.assign({}, config.repository) + delete repoSettings.enable_automated_security_fixes + + githubScope + .get(`/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent(Settings.FILE_NAME)}`) + .reply(200, config) + githubScope + .patch(`/repos/${repository.owner.name}/${repository.name}`, body => { + expect(body).toMatchObject(repoSettings) + return true + }) + .matchHeader('accept', ['application/vnd.github.baptiste-preview+json']) + .reply(200) + githubScope + .put(`/repos/${repository.owner.name}/${repository.name}/automated-security-fixes`, body => true) + .matchHeader('accept', ['application/vnd.github.london-preview+json']) + .reply(200) + + await probot.receive(buildTriggerEvent()) + }) +}) diff --git a/test/integration/plugins/teams.test.js b/test/integration/plugins/teams.test.js new file mode 100644 index 0000000000..884759d32e --- /dev/null +++ b/test/integration/plugins/teams.test.js @@ -0,0 +1,58 @@ +import fs from 'fs' +import { CREATED, NO_CONTENT, OK } from 'http-status-codes' +import any from '@travi/any' +import Settings from '../../../lib/settings' +import { buildTriggerEvent, initializeNock, loadInstance, repository, teardownNock } from '../common' + +describe('teams plugin', function () { + let probot, githubScope + + beforeEach(() => { + githubScope = initializeNock() + probot = loadInstance() + }) + + afterEach(() => { + teardownNock(githubScope) + }) + + it('syncs teams', async () => { + const pathToConfig = new URL('../../fixtures/teams-config.yml', import.meta.url) + const configFile = Buffer.from(fs.readFileSync(pathToConfig, 'utf8')) + const config = configFile.toString() + const probotTeamId = any.integer() + const githubTeamId = any.integer() + const greenkeeperKeeperTeamId = any.integer() + const formationTeamId = any.integer() + githubScope + .get(`/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent(Settings.FILE_NAME)}`) + .reply(OK, config) + githubScope.get(`/repos/${repository.owner.name}/${repository.name}/teams`).reply(OK, [ + { slug: 'greenkeeper-keeper', id: greenkeeperKeeperTeamId, permission: 'pull' }, + { slug: 'form8ion', id: formationTeamId, permission: 'push' } + ]) + githubScope.get(`/orgs/${repository.owner.name}/teams/probot`).reply(OK, { id: probotTeamId }) + githubScope.get(`/orgs/${repository.owner.name}/teams/github`).reply(OK, { id: githubTeamId }) + githubScope + .put(`/teams/${probotTeamId}/repos/${repository.owner.name}/${repository.name}`, body => { + expect(body).toMatchObject({ permission: 'admin' }) + return true + }) + .reply(CREATED) + githubScope + .put(`/teams/${githubTeamId}/repos/${repository.owner.name}/${repository.name}`, body => { + expect(body).toMatchObject({ permission: 'maintain' }) + return true + }) + .reply(CREATED) + githubScope + .put(`/teams/${greenkeeperKeeperTeamId}/repos/${repository.owner.name}/${repository.name}`, body => { + expect(body).toMatchObject({ permission: 'push' }) + return true + }) + .reply(OK) + githubScope.delete(`/teams/${formationTeamId}/repos/${repository.owner.name}/${repository.name}`).reply(NO_CONTENT) + + await probot.receive(buildTriggerEvent()) + }) +}) diff --git a/test/integration/triggers/push.test.js b/test/integration/triggers/push.test.js new file mode 100644 index 0000000000..77bd22cf6d --- /dev/null +++ b/test/integration/triggers/push.test.js @@ -0,0 +1,26 @@ +import Settings from '../../../lib/settings' +import { initializeNock, loadInstance, repository, teardownNock } from '../common' + +describe('push trigger', function () { + let probot, githubScope + + beforeEach(() => { + githubScope = initializeNock() + probot = loadInstance() + }) + + afterEach(() => { + teardownNock(githubScope) + }) + + it('does not apply configuration when not on the default branch', async () => { + await probot.receive({ + name: 'push', + payload: { + ref: 'refs/heads/wip', + repository, + commits: [{ modified: [Settings.FILE_NAME], added: [] }] + } + }) + }) +}) diff --git a/test/integration/triggers/repository-created.test.js b/test/integration/triggers/repository-created.test.js new file mode 100644 index 0000000000..1e0410dc83 --- /dev/null +++ b/test/integration/triggers/repository-created.test.js @@ -0,0 +1,33 @@ +import { NOT_FOUND } from 'http-status-codes' +import Settings from '../../../lib/settings' +import { buildRepositoryCreatedEvent, initializeNock, loadInstance, repository, teardownNock } from '../common' + +describe('repository.created trigger', function () { + let probot, githubScope + + beforeEach(() => { + githubScope = initializeNock() + probot = loadInstance() + }) + + afterEach(() => { + teardownNock(githubScope) + }) + + it('does not apply configuration when the repository does not have a settings.yml', async () => { + githubScope + .get(`/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent(Settings.FILE_NAME)}`) + .reply(NOT_FOUND, { + message: 'Not Found', + documentation_url: 'https://developer.github.com/v3/repos/contents/#get-contents' + }) + githubScope + .get(`/repos/${repository.owner.name}/.github/contents/${encodeURIComponent(Settings.FILE_NAME)}`) + .reply(NOT_FOUND, { + message: 'Not Found', + documentation_url: 'https://developer.github.com/v3/repos/contents/#get-contents' + }) + + await probot.receive(buildRepositoryCreatedEvent()) + }) +}) diff --git a/test/integration/triggers/repository-edited.test.js b/test/integration/triggers/repository-edited.test.js new file mode 100644 index 0000000000..c4e653fb26 --- /dev/null +++ b/test/integration/triggers/repository-edited.test.js @@ -0,0 +1,43 @@ +import { NOT_FOUND } from 'http-status-codes' +import any from '@travi/any' +import Settings from '../../../lib/settings' +import { buildRepositoryEditedEvent, initializeNock, loadInstance, repository, teardownNock } from '../common' + +describe('repository.edited trigger', function () { + let probot, githubScope + + beforeEach(() => { + githubScope = initializeNock() + probot = loadInstance() + }) + + afterEach(() => { + teardownNock(githubScope) + }) + + it('does not apply configuration when the default branch was not changed', async () => { + await probot.receive({ + name: 'repository.edited', + payload: { + changes: any.simpleObject() + } + }) + }) + + it('does not apply configuration when the repository does not have a settings.yml', async () => { + githubScope + .get(`/repos/${repository.owner.name}/${repository.name}/contents/${encodeURIComponent(Settings.FILE_NAME)}`) + .reply(NOT_FOUND, { + message: 'Not Found', + documentation_url: 'https://developer.github.com/v3/repos/contents/#get-contents' + }) + githubScope + .get(`/repos/${repository.owner.name}/.github/contents/${encodeURIComponent(Settings.FILE_NAME)}`) + .reply(NOT_FOUND, { + message: 'Not Found', + documentation_url: 'https://developer.github.com/v3/repos/contents/#get-contents' + }) + + await probot.receive(buildRepositoryEditedEvent()) + }) +}) diff --git a/test/unit/index.test.js b/test/unit/index.test.js index e60da2c52b..b1c7f7b9ed 100644 --- a/test/unit/index.test.js +++ b/test/unit/index.test.js @@ -1,6 +1,12 @@ -const { Probot, ProbotOctokit } = require('probot') -const any = require('@travi/any') -const plugin = require('../../index') +import { Probot, ProbotOctokit } from 'probot' +import any from '@travi/any' +import plugin from '../../index' +import { readFileSync } from 'fs' +import { jest } from '@jest/globals' + +const pushSettings = JSON.parse(readFileSync(new URL('../fixtures/events/push.settings.json', import.meta.url))) +const pushReadme = JSON.parse(readFileSync(new URL('../fixtures/events/push.readme.json', import.meta.url))) +const repositoryEdited = JSON.parse(readFileSync(new URL('../fixtures/events/repository.edited.json', import.meta.url))) describe('plugin', () => { let app, event, sync @@ -26,7 +32,7 @@ describe('plugin', () => { event = { name: 'push', - payload: JSON.parse(JSON.stringify(require('../fixtures/events/push.settings.json'))) + payload: pushSettings } sync = jest.fn() @@ -53,7 +59,7 @@ describe('plugin', () => { describe('with other files modified', () => { beforeEach(() => { - event.payload = require('../fixtures/events/push.readme.json') + event.payload = pushReadme }) it('does not sync settings', async () => { @@ -66,7 +72,7 @@ describe('plugin', () => { beforeEach(() => { event = { name: 'repository.edited', - payload: require('../fixtures/events/repository.edited.json') + payload: repositoryEdited } }) diff --git a/test/unit/lib/mergeArrayByName.test.js b/test/unit/lib/mergeArrayByName.test.js index 1949a5a601..5f64f383db 100644 --- a/test/unit/lib/mergeArrayByName.test.js +++ b/test/unit/lib/mergeArrayByName.test.js @@ -1,5 +1,5 @@ -const branchArrayMerge = require('../../../lib/mergeArrayByName') -const YAML = require('js-yaml') +import branchArrayMerge from '../../../lib/mergeArrayByName' +import YAML from 'js-yaml' describe('mergeArrayByName', () => { it('works', () => { diff --git a/test/unit/lib/plugins/branches.test.js b/test/unit/lib/plugins/branches.test.js index fa3b2c0ead..4cafd53c6f 100644 --- a/test/unit/lib/plugins/branches.test.js +++ b/test/unit/lib/plugins/branches.test.js @@ -1,4 +1,5 @@ -const Branches = require('../../../../lib/plugins/branches') +import Branches from '../../../../lib/plugins/branches' +import { jest } from '@jest/globals' describe('Branches', () => { let github diff --git a/test/unit/lib/plugins/collaborators.test.js b/test/unit/lib/plugins/collaborators.test.js index 72e6e94181..086653234c 100644 --- a/test/unit/lib/plugins/collaborators.test.js +++ b/test/unit/lib/plugins/collaborators.test.js @@ -1,4 +1,5 @@ -const Collaborators = require('../../../../lib/plugins/collaborators') +import Collaborators from '../../../../lib/plugins/collaborators' +import { jest } from '@jest/globals' describe('Collaborators', () => { let github diff --git a/test/unit/lib/plugins/labels.test.js b/test/unit/lib/plugins/labels.test.js index 5ceeeaf4b9..bb08ca25e6 100644 --- a/test/unit/lib/plugins/labels.test.js +++ b/test/unit/lib/plugins/labels.test.js @@ -1,4 +1,5 @@ -const Labels = require('../../../../lib/plugins/labels') +import Labels from '../../../../lib/plugins/labels' +import { jest } from '@jest/globals' describe('Labels', () => { let github diff --git a/test/unit/lib/plugins/milestones.test.js b/test/unit/lib/plugins/milestones.test.js index faec3a5c81..cb1514bf87 100644 --- a/test/unit/lib/plugins/milestones.test.js +++ b/test/unit/lib/plugins/milestones.test.js @@ -1,4 +1,5 @@ -const Milestones = require('../../../../lib/plugins/milestones') +import Milestones from '../../../../lib/plugins/milestones' +import { jest } from '@jest/globals' describe('Milestones', () => { let github diff --git a/test/unit/lib/plugins/repository.test.js b/test/unit/lib/plugins/repository.test.js index ac619b0e3c..004c209f4c 100644 --- a/test/unit/lib/plugins/repository.test.js +++ b/test/unit/lib/plugins/repository.test.js @@ -1,4 +1,5 @@ -const Repository = require('../../../../lib/plugins/repository') +import Repository from '../../../../lib/plugins/repository' +import { jest } from '@jest/globals' describe('Repository', () => { let github diff --git a/test/unit/lib/plugins/teams.test.js b/test/unit/lib/plugins/teams.test.js index 4a90e08c3a..05615ecef5 100644 --- a/test/unit/lib/plugins/teams.test.js +++ b/test/unit/lib/plugins/teams.test.js @@ -1,6 +1,7 @@ -const { when } = require('jest-when') -const any = require('@travi/any') -const Teams = require('../../../../lib/plugins/teams') +import { when } from 'jest-when' +import any from '@travi/any' +import Teams from '../../../../lib/plugins/teams' +import { jest } from '@jest/globals' describe('Teams', () => { let github From 220c602fb401b7ea6d9833645cf4dc116c53334a Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Sun, 14 Jul 2024 08:10:37 -0500 Subject: [PATCH 2/2] refactor(esm): converted environments plugin and ensured `start` script is working --- cucumber.mjs => cucumber.js | 0 index.js | 4 ++-- lib/plugins/collaborators.js | 2 +- lib/plugins/environments.js | 4 ++-- lib/plugins/labels.js | 2 +- lib/plugins/milestones.js | 2 +- lib/plugins/teams.js | 2 +- lib/settings.js | 14 +++++++------- package.json | 3 ++- test/unit/index.test.js | 2 +- test/unit/lib/plugins/environments.test.js | 5 +++-- 11 files changed, 21 insertions(+), 19 deletions(-) rename cucumber.mjs => cucumber.js (100%) diff --git a/cucumber.mjs b/cucumber.js similarity index 100% rename from cucumber.mjs rename to cucumber.js diff --git a/index.js b/index.js index 167b48832c..eaa4e882f8 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,5 @@ -import mergeArrayByName from './lib/mergeArrayByName' -import SettingsApp from './lib/settings' +import mergeArrayByName from './lib/mergeArrayByName.js' +import SettingsApp from './lib/settings.js' /** * @param {import('probot').Probot} robot diff --git a/lib/plugins/collaborators.js b/lib/plugins/collaborators.js index 27d0fb41a5..ac6b444b52 100644 --- a/lib/plugins/collaborators.js +++ b/lib/plugins/collaborators.js @@ -1,4 +1,4 @@ -import Diffable from './diffable' +import Diffable from './diffable.js' export default class Collaborators extends Diffable { constructor (...args) { diff --git a/lib/plugins/environments.js b/lib/plugins/environments.js index f9ceee401d..bd7bbd5814 100644 --- a/lib/plugins/environments.js +++ b/lib/plugins/environments.js @@ -1,4 +1,4 @@ -const Diffable = require('./diffable') +import Diffable from './diffable.js' const environmentRepoEndpoint = '/repos/:org/:repo/environments/:environment_name' @@ -60,7 +60,7 @@ function deploymentBranchPolicyHasChanged (existing, attrs) { ) } -module.exports = class Environments extends Diffable { +export default class Environments extends Diffable { constructor (...args) { super(...args) diff --git a/lib/plugins/labels.js b/lib/plugins/labels.js index 6a6d53ecf6..129870aef0 100644 --- a/lib/plugins/labels.js +++ b/lib/plugins/labels.js @@ -1,4 +1,4 @@ -import Diffable from './diffable' +import Diffable from './diffable.js' const previewHeaders = { accept: 'application/vnd.github.symmetra-preview+json' } export default class Labels extends Diffable { diff --git a/lib/plugins/milestones.js b/lib/plugins/milestones.js index 904b8a4bd8..d141db8691 100644 --- a/lib/plugins/milestones.js +++ b/lib/plugins/milestones.js @@ -1,4 +1,4 @@ -import Diffable from './diffable' +import Diffable from './diffable.js' export default class Milestones extends Diffable { constructor (...args) { diff --git a/lib/plugins/teams.js b/lib/plugins/teams.js index bc34f61b23..30317de202 100644 --- a/lib/plugins/teams.js +++ b/lib/plugins/teams.js @@ -1,4 +1,4 @@ -import Diffable from './diffable' +import Diffable from './diffable.js' // it is necessary to use this endpoint until GitHub Enterprise supports // the modern version under /orgs diff --git a/lib/settings.js b/lib/settings.js index 755d4740e6..54e2087f77 100644 --- a/lib/settings.js +++ b/lib/settings.js @@ -1,10 +1,10 @@ -import Repository from './plugins/repository' -import Labels from './plugins/labels' -import Collaborators from './plugins/collaborators' -import Teams from './plugins/teams' -import Milestones from './plugins/milestones' -import Branches from './plugins/branches' -import Environments from './plugins/environments' +import Repository from './plugins/repository.js' +import Labels from './plugins/labels.js' +import Collaborators from './plugins/collaborators.js' +import Teams from './plugins/teams.js' +import Milestones from './plugins/milestones.js' +import Branches from './plugins/branches.js' +import Environments from './plugins/environments.js' export default class Settings { static sync (github, repo, config) { diff --git a/package.json b/package.json index 206058255a..2246ce759a 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "description": "Pull Requests for GitHub repository settings", "repository": "github:repository-settings/app", "type": "module", - "main": "index.js", + "main": "./index.js", + "exports": "./index.js", "scripts": { "dev": "nodemon", "start": "probot run ./index.js", diff --git a/test/unit/index.test.js b/test/unit/index.test.js index b1c7f7b9ed..d7b8bf1de4 100644 --- a/test/unit/index.test.js +++ b/test/unit/index.test.js @@ -1,6 +1,6 @@ import { Probot, ProbotOctokit } from 'probot' import any from '@travi/any' -import plugin from '../../index' +import plugin from '../../index.js' import { readFileSync } from 'fs' import { jest } from '@jest/globals' diff --git a/test/unit/lib/plugins/environments.test.js b/test/unit/lib/plugins/environments.test.js index 55fcc0ac1c..bb7e796513 100644 --- a/test/unit/lib/plugins/environments.test.js +++ b/test/unit/lib/plugins/environments.test.js @@ -1,5 +1,6 @@ -const { when } = require('jest-when') -const Environments = require('../../../../lib/plugins/environments') +import { when } from 'jest-when' +import { jest } from '@jest/globals' +import Environments from '../../../../lib/plugins/environments.js' describe('Environments', () => { let github