build: update content when upstream changes

This commit adds support to respond to `repository_dispatch` events
coming from `electron/electron-website-updater` that contain the
SHA of the new commit in `electron/electron` to pin to in
`package.json`.

More information about the architecture can be found in #19.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Fix #19
Close #23
This commit is contained in:
Antón Molleda 2021-05-13 21:08:47 -07:00 коммит произвёл GitHub
Родитель ac21aa7f3b
Коммит 11a0f9d3f1
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
19 изменённых файлов: 2842 добавлений и 71 удалений

1
.env.example Normal file
Просмотреть файл

@ -0,0 +1 @@
GITHUB_TOKEN=

2
.github/workflows/test.yml поставляемый
Просмотреть файл

@ -10,7 +10,7 @@ on:
jobs:
tests:
name: Install & test
name: Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2

24
.github/workflows/update-docs.yml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,24 @@
name: 'Update docs'
on:
repository_dispatch:
types: [doc_changes]
jobs:
update-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '14'
- name: Install dependencies
run: 'yarn'
- name: Update pinned version
run: 'yarn update-pinned-version ${{ github.event.client_payload.sha }}'
- name: 'Prebuild'
run: 'yarn prebuild'
- name: 'Create PR'
run: 'yarn process-docs-changes'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

4
.gitignore поставляемый
Просмотреть файл

@ -1,9 +1,9 @@
node_modules
.docusaurus
.DS_Store
.env
.vscode/settings.json
build/
content/
docs/
blog/
package-lock.json
blog/

9
.vscode/extensions.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,9 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
// List of extensions which should be recommended for users of this workspace.
"recommendations": ["esbenp.prettier-vscode", "orta.vscode-jest"],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": []
}

18
.vscode/launch.json поставляемый
Просмотреть файл

@ -4,6 +4,22 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"name": "vscode-jest-tests",
"request": "launch",
"program": "${workspaceFolder}/node_modules/.bin/jest",
"args": [
"--runInBand"
],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"disableOptimisticBPs": true,
"windows": {
"program": "${workspaceFolder}/node_modules/jest/bin/jest"
}
},
{
"type": "pwa-node",
"request": "launch",
@ -21,6 +37,6 @@
"program": "${workspaceFolder}/create-electron-documentation/index.js",
"console": "integratedTerminal",
"cwd": "${workspaceFolder}/docs/how-to"
}
},
]
}

Просмотреть файл

@ -44,10 +44,33 @@ yarn start
`yarn start` starts a local development server and open up a browser window. Most changes are reflected live without having to restart the server.
## Build
# Repository content organization
```console
yarn build
This repository contains the code for 2 related things:
- The code to generate the contents of https://beta.electronjs.org
- [`create-electron-documentation`][ced] package
The content of this repository is organized as follows:
```
└─ root
|
├─ .github/workflows → The definitions for the GitHub actions
|
|
├─ create-electron-documentation → Code for the npm package
| of the same name. Read the readme in the folder
| for more information.
|
├─ scripts → The code for the package.json tasks and GitHub
| actions
|
├─ spec → Tests for the scripts
|
├─ src → Docusaurus code
|
├─ static → Docusaurus static assets
```
This command generates static content into the `build` directory and can be served using any static contents hosting service.
[ced]: https://npmjs.com/package/create-electron-documentation

195
jest.config.js Normal file
Просмотреть файл

@ -0,0 +1,195 @@
/*
* For a detailed explanation regarding each configuration property, visit:
* https://jestjs.io/docs/en/configuration.html
*/
module.exports = {
// All imported modules in your tests should be mocked automatically
// automock: false,
// Stop running tests after `n` failures
// bail: 0,
// The directory where Jest should store its cached dependency information
// cacheDirectory: "/tmp/jest_rs",
// Automatically clear mock calls and instances between every test
clearMocks: true,
// Indicates whether the coverage information should be collected while executing the test
// collectCoverage: false,
// An array of glob patterns indicating a set of files for which coverage information should be collected
// collectCoverageFrom: undefined,
// The directory where Jest should output its coverage files
// coverageDirectory: undefined,
// An array of regexp pattern strings used to skip coverage collection
// coveragePathIgnorePatterns: [
// "/node_modules/"
// ],
// Indicates which provider should be used to instrument code for coverage
coverageProvider: 'v8',
// A list of reporter names that Jest uses when writing coverage reports
// coverageReporters: [
// "json",
// "text",
// "lcov",
// "clover"
// ],
// An object that configures minimum threshold enforcement for coverage results
// coverageThreshold: undefined,
// A path to a custom dependency extractor
// dependencyExtractor: undefined,
// Make calling deprecated APIs throw helpful error messages
// errorOnDeprecated: false,
// Force coverage collection from ignored files using an array of glob patterns
// forceCoverageMatch: [],
// A path to a module which exports an async function that is triggered once before all test suites
// globalSetup: undefined,
// A path to a module which exports an async function that is triggered once after all test suites
// globalTeardown: undefined,
// A set of global variables that need to be available in all test environments
// globals: {},
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
// maxWorkers: "50%",
// An array of directory names to be searched recursively up from the requiring module's location
// moduleDirectories: [
// "node_modules"
// ],
// An array of file extensions your modules use
// moduleFileExtensions: [
// "js",
// "json",
// "jsx",
// "ts",
// "tsx",
// "node"
// ],
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
// moduleNameMapper: {},
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
// modulePathIgnorePatterns: [],
// Activates notifications for test results
// notify: false,
// An enum that specifies notification mode. Requires { notify: true }
// notifyMode: "failure-change",
// A preset that is used as a base for Jest's configuration
// preset: undefined,
// Run tests from one or more projects
// projects: undefined,
// Use this configuration option to add custom reporters to Jest
// reporters: undefined,
// Automatically reset mock state between every test
// resetMocks: false,
// Reset the module registry before running each individual test
// resetModules: false,
// A path to a custom resolver
// resolver: undefined,
// Automatically restore mock state between every test
// restoreMocks: false,
// The root directory that Jest should scan for tests and modules within
// rootDir: undefined,
// A list of paths to directories that Jest should use to search for files in
// roots: [
// "<rootDir>"
// ],
// Allows you to use a custom runner instead of Jest's default test runner
// runner: "jest-runner",
// The paths to modules that run some code to configure or set up the testing environment before each test
// setupFiles: [],
// A list of paths to modules that run some code to configure or set up the testing framework before each test
// setupFilesAfterEnv: [],
silent: true,
// The number of seconds after which a test is considered as slow and reported as such in the results.
// slowTestThreshold: 5,
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
// snapshotSerializers: [],
// The test environment that will be used for testing
testEnvironment: 'node',
// Options that will be passed to the testEnvironment
// testEnvironmentOptions: {},
// Adds a location field to test results
// testLocationInResults: false,
// The glob patterns Jest uses to detect test files
// testMatch: [
// "**/__tests__/**/*.[jt]s?(x)",
// "**/?(*.)+(spec|test).[tj]s?(x)"
// ],
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
// testPathIgnorePatterns: [
// "/node_modules/"
// ],
testTimeout: 50000,
// The regexp pattern or array of patterns that Jest uses to detect test files
// testRegex: [],
// This option allows the use of a custom results processor
// testResultsProcessor: undefined,
// This option allows use of a custom test runner
// testRunner: "jasmine2",
// This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
// testURL: "http://localhost",
// Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
// timers: "real",
// A map from regular expressions to paths to transformers
// transform: undefined,
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
transformIgnorePatterns: ['/node_modules/', '.js$'],
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
// unmockedModulePathPatterns: undefined,
// Indicates whether each individual test should be reported during the run
// verbose: undefined,
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
// watchPathIgnorePatterns: [],
// Whether to use watchman for file crawling
// watchman: true,
};

Просмотреть файл

@ -12,14 +12,19 @@
"serve": "docusaurus serve",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids",
"test": "prettier -c ./scripts/",
"prebuild": "node ./scripts/pre-build.js"
"lint": "prettier -c ./scripts/**/*.js",
"test": "yarn lint && jest",
"prebuild": "node ./scripts/pre-build.js",
"process-docs-changes": "node ./scripts/process-docs-changes.js",
"update-pinned-version": "node ./scripts/update-pinned-version.js"
},
"dependencies": {
"@docusaurus/core": "^2.0.0-beta.0",
"@docusaurus/preset-classic": "^2.0.0-beta.0",
"@mdx-js/react": "^1.6.21",
"clsx": "^1.1.1",
"dotenv-safe": "^8.2.0",
"execa": "^5.0.0",
"react": "^17.0.1",
"react-dom": "^17.0.1"
},
@ -36,17 +41,22 @@
]
},
"devDependencies": {
"@actions/core": "^1.2.7",
"@actions/github": "^4.0.0",
"@types/jest": "^26.0.23",
"@types/unist": "^2.0.3",
"del": "^6.0.0",
"fs-extra": "^9.1.0",
"globby": "^11.0.3",
"got": "^11.8.2",
"gunzip-maybe": "^1.4.2",
"jest": "^26.6.3",
"json5": "^2.2.0",
"latest-version": "^5.1.0",
"make-dir": "^3.1.0",
"prettier": "^2.2.1",
"tar-stream": "^2.2.0",
"unist-util-visit-parents": "^3.1.1"
}
},
"sha": "eb5ce11b415a956182c59465980204ba695fd4ee"
}

Просмотреть файл

@ -0,0 +1,58 @@
const gitMock = jest.createMockFromModule('../utils/git-commands');
jest.mock('../utils/git-commands', () => gitMock);
// Make sure we do not run any real git commands by accident
const executeMock = jest.createMockFromModule('../utils/execute');
jest.mock('../utils/execute', () => executeMock);
const { processDocsChanges } = require('../process-docs-changes');
describe('process-docs-changes', () => {
it('does not create any PR if there are no changes', async () => {
gitMock.getChanges.mockResolvedValue('');
await processDocsChanges();
expect(gitMock.createPR).toHaveBeenCalledTimes(0);
expect(gitMock.pushChanges).toHaveBeenCalledTimes(0);
});
it('does not create any PR if package.json is not modified', async () => {
gitMock.getChanges.mockResolvedValue('M sidebars.json');
await processDocsChanges();
expect(gitMock.createPR).toHaveBeenCalledTimes(0);
expect(gitMock.pushChanges).toHaveBeenCalledTimes(0);
});
it('pushes changes directly to main if only package.json is modified', async () => {
gitMock.getChanges.mockResolvedValue('M package.json');
await processDocsChanges();
expect(gitMock.createPR).toHaveBeenCalledTimes(0);
expect(gitMock.pushChanges).toHaveBeenCalledTimes(1);
expect(gitMock.pushChanges).toHaveBeenCalledWith(
'main',
'electron@github.com',
'electron-bot',
'"chore: update ref to docs (🤖)"'
);
});
it('does create a PR if more files than package.json are modified', async () => {
gitMock.getChanges.mockResolvedValue('M package.json\nM sidebars.json');
await processDocsChanges();
expect(gitMock.pushChanges).toHaveBeenCalledTimes(0);
expect(gitMock.createPR).toHaveBeenCalledTimes(1);
expect(gitMock.createPR).toHaveBeenCalledWith(
'chore/docs-updates',
'main',
'electron@github.com',
'electron-bot',
'"chore: update ref to docs (🤖)"'
);
});
});

Просмотреть файл

@ -0,0 +1,35 @@
const fs = require('fs');
const mock = {
promises: {
readFile: jest.fn(),
writeFile: jest.fn(),
},
};
jest.mock('fs', () => {
return mock;
});
const { updateSha } = require('../update-pinned-version');
describe('update-pinned-version', () => {
it('updates package.json with the given sha', async () => {
const sha = 'new-sha';
const expected = {
sha,
};
const packageJson = {
sha: 'old-sha',
};
mock.promises.readFile.mockResolvedValue(JSON.stringify(packageJson));
await updateSha(sha);
expect(mock.promises.writeFile).toBeCalledTimes(1);
expect(mock.promises.writeFile).toBeCalledWith(
expect.any(String),
`${JSON.stringify(expected, null, 2)}\n`,
'utf-8'
);
});
});

Просмотреть файл

@ -16,37 +16,46 @@ const { addFrontmatter } = require('./tasks/add-frontmatter');
const { createSidebar } = require('./tasks/create-sidebar');
const { fixContent } = require('./tasks/md-fixers');
const { copyNewContent } = require('./tasks/copy-new-content');
const { sha } = require('../package.json');
const DOCS_FOLDER = 'docs';
// const BLOG_FOLDER = 'blog';
/**
*
* @param {string} localElectron
* @param {string} source
*/
const start = async (localElectron) => {
console.log(`Detecting latest Electron version`);
const version = await latestVersion('electron');
const stableBranch = version.replace(/\.\d+\.\d+/, '-x-y');
console.log(`Latest version: ${version}`);
console.log(`Stable branch: ${stableBranch}`);
const start = async (source) => {
console.log(`Deleting previous content`);
await del(DOCS_FOLDER);
const localElectron =
source && (source.includes('/') || source.includes('\\'));
// TODO: Uncomment once we have the blog up and running
// await del(BLOG_FOLDER);
if (!localElectron) {
console.log(`Downloading docs from branch "${stableBranch}"`);
console.log(`Detecting latest Electron version`);
const version = await latestVersion('electron');
const stableBranch = version.replace(/\.\d+\.\d+/, '-x-y');
console.log(`Latest version: ${version}`);
console.log(`Stable branch: ${stableBranch}`);
console.log(`Specified SHA: ${sha}`);
const target = source || sha || stableBranch;
console.log(`Downloading docs using "${target}"`);
await download({
target: stableBranch,
target,
org: process.env.ORG || 'electron',
repository: 'electron',
destination: DOCS_FOLDER,
downloadMatch: 'docs',
});
} else if (existsSync(localElectron)) {
} else if (existsSync(source)) {
await copy({
target: localElectron,
target: source,
destination: DOCS_FOLDER,
downloadMatch: 'docs',
});

Просмотреть файл

@ -0,0 +1,68 @@
/**
* Checks if there are any changes in the repo and creates or updates
* a PR if needed. This is part of the `update-docs.yml` workflow and
* depends on `update-pinned-version` and `prebuild` being run before
* in order to produce the right result.
*/
//@ts-check
if (!(process.env.CI || process.env.NODE_ENV === 'test') && !GITHUB_TOKEN) {
console.error('Missing GITHUB_TOKEN environment variable');
process.exit(1);
}
const { createPR, getChanges, pushChanges } = require('./utils/git-commands');
const HEAD = 'main';
const PR_BRANCH = 'chore/docs-updates';
const COMMIT_MESSAGE = '"chore: update ref to docs (🤖)"';
const EMAIL = 'electron@github.com';
const NAME = 'electron-bot';
/**
* Wraps a function on a try/catch and changes the exit code if it fails.
* @param {Function} func
*/
const changeExitCodeIfException = async (func) => {
try {
await func();
} catch (e) {
console.error(e);
process.exitCode = 1;
}
};
const processDocsChanges = async () => {
const output = await getChanges();
if (output === '') {
console.log('Nothing updated, skipping');
return;
} else if (!output.includes('M package.json')) {
console.log('package.json is not modified, skipping');
return;
} else {
const lines = output.split('\n');
if (lines.length > 1) {
console.log(`New documents available, creating PR.`);
await createPR(PR_BRANCH, HEAD, EMAIL, NAME, COMMIT_MESSAGE);
} else {
console.log(
`Only existing content has been modified. Pushing changes directly.`
);
await pushChanges(HEAD, EMAIL, NAME, COMMIT_MESSAGE);
}
}
};
// When a file is run directly from Node.js, `require.main` is set to its module.
// That means that it is possible to determine whether a file has been run directly
// by testing `require.main === module`.
// https://nodejs.org/docs/latest/api/modules.html#modules_accessing_the_main_module
if (require.main === module) {
changeExitCodeIfException(processDocsChanges);
}
module.exports = {
processDocsChanges,
};

Просмотреть файл

@ -13,7 +13,8 @@ const fixedFolders = ['api', 'images', 'fiddles'];
/**
* @typedef DownloadOptions
* @type {object}
* @property {string} [repository] - The repository in the electron org to download the contents from
* @property {string} [org] - The organization to download the contents from
* @property {string} [repository] - The repository to download the contents from
* @property {string} destination - The destination absolute path.
* @property {string} target - The branch, commit, version. (e.g. `v1.0.0`, `master`)
* @property {string} downloadMatch - The math to use to filter the downloaded contents
@ -88,9 +89,9 @@ const saveContents = async (files, destination) => {
* @param {DownloadOptions} options
*/
const downloadFromGitHub = async (options) => {
const { repository, target, downloadMatch = '' } = options;
const { org, repository, target, downloadMatch = '' } = options;
const tarballUrl = `https://github.com/electron/${repository}/archive/${target}.tar.gz`;
const tarballUrl = `https://github.com/${org}/${repository}/archive/${target}.tar.gz`;
const contents = [];

Просмотреть файл

@ -0,0 +1,51 @@
//@ts-check
const fs = require('fs').promises;
const path = require('path');
const packageJsonPath = path.join(__dirname, '..', 'package.json');
/**
* Updates the field `sha` of the `package.json` with the value passed
* via parameter in the CLI.
* @param {string} sha
*/
const updateSha = async (sha) => {
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));
const oldSha = packageJson.sha;
console.log(`New SHA: ${sha}`);
console.log(`Old SHA: ${oldSha}`);
if (sha === oldSha) {
console.log(`Nothing to update`);
return;
}
packageJson.sha = sha;
await fs.writeFile(
packageJsonPath,
`${JSON.stringify(packageJson, null, 2)}\n`,
'utf-8'
);
console.log(`SHA updated`);
};
// When a file is run directly from Node.js, `require.main` is set to its module.
// That means that it is possible to determine whether a file has been run directly
// by testing `require.main === module`.
// https://nodejs.org/docs/latest/api/modules.html#modules_accessing_the_main_module
if (require.main === module) {
const sha = process.argv[2];
if (!sha) {
console.error('Please provide an SHA value as follows:');
console.error('yarn update-pinned-version SHA');
} else {
updateSha(sha);
}
}
module.exports = {
updateSha,
};

Просмотреть файл

@ -0,0 +1,52 @@
// Make sure we do not run any real git commands by accident
const executeMock = jest.createMockFromModule('../execute');
jest.mock('../execute', () => executeMock);
const octokitMock = {
pulls: { list: jest.fn(), create: jest.fn() },
};
const github = {
getOctokit: () => {
return octokitMock;
},
context: {
repo: {
repo: 'mock-repo',
owner: 'mock-owner',
},
},
};
jest.mock('@actions/github', () => github);
const { createPR } = require('../git-commands.js');
describe('git-commands', () => {
it('creates a new PR if there are no PRs opened', async () => {
executeMock.execute.mockResolvedValue();
octokitMock.pulls.list.mockResolvedValue({ data: [] });
octokitMock.pulls.create.mockResolvedValue({ data: { id: 42 } });
await createPR('mock-branch', 'mock-base', 'chore: mock mesage');
expect(octokitMock.pulls.list).toBeCalledTimes(1);
expect(octokitMock.pulls.create).toBeCalledTimes(1);
expect(octokitMock.pulls.create).toBeCalledWith({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
base: 'mock-base',
head: 'mock-branch',
});
});
it('force pushes to the branch if there is already a PR', async () => {
executeMock.execute.mockResolvedValue();
octokitMock.pulls.list.mockResolvedValue({
data: [{ head: { ref: 'mock-branch' } }],
});
octokitMock.pulls.create.mockResolvedValue({ data: { id: 42 } });
await createPR('mock-branch', 'mock-base', 'chore: mock mesage');
expect(octokitMock.pulls.list).toBeCalledTimes(1);
expect(octokitMock.pulls.create).toBeCalledTimes(0);
});
});

82
scripts/utils/execute.js Normal file
Просмотреть файл

@ -0,0 +1,82 @@
const path = require('path');
const execa = require('execa');
/**
* Groups all string arguments into a single one. E.g.:
* ```js
* ['-m', '"Upgrade:', 'to', 'latest', 'version"'] --> ['-m', '"Upgrade: to latest version"']`
* ```
*
* Original code writeng by @molant for webhint/hint.
* Distributed under an Apache-2.0 License
*
* https://github.com/webhintio/hint/blob/606fee86054a54aa55a598fdc6d86400d1269851/release/lib/utils.ts#L78
* @param {string[]} args The arguments
*/
const groupArgs = (args) => {
let isStringArgument = false;
const newArgs = args.reduce((acum, current) => {
if (isStringArgument) {
const last = acum[acum.length - 1];
acum[acum.length - 1] = `${last} ${current}`.replace(/"/g, '');
if (current.endsWith('"')) {
isStringArgument = false;
}
return acum;
}
if (current.startsWith('"')) {
/**
* Argument is split. I.e.: `['"part1', 'part2"'];`
*/
if (!current.endsWith('"')) {
isStringArgument = true;
acum.push(current);
return acum;
}
/**
* Argument is surrounded by "" that need to be removed.
* We just remove all the quotes because we don't escape any in our commands
*/
acum.push(current.replace(/"/g, ''));
return acum;
}
acum.push(current);
return acum;
}, []);
return newArgs;
};
/**
* Executes the given `command` using `execa` and doing the
* required transformations (e.g.: splitting and grouping arguments).
* @param {string} command
* @param {execa.Options} [options]
* @returns
*/
const execute = (command, options) => {
console.log(
`${options && options.cwd ? options.cwd : process.cwd()}${
path.sep
}${command}`
);
const args = command.split(' ');
const program = args.shift();
return execa(program, groupArgs(args), options);
};
module.exports = {
execute,
};

Просмотреть файл

@ -0,0 +1,91 @@
const github = require('@actions/github');
const { execute } = require('./execute');
const GITHUB_TOKEN = process.env.GITHUB_TOKEN;
/**
* Creates a new commit with the current changes.
* @param {string} email
* @param {string} name
* @param {string} commitMessage
*/
const createCommit = async (email, name, commitMessage) => {
await execute('git remote -vv');
await execute('git status');
await execute(`git config --global user.email ${email}`);
await execute(`git config --global user.name ${name}`);
await execute(`git add .`);
await execute(`git commit -am ${commitMessage}`);
};
/**
* Returns the current modified files in the repo.
*/
const getChanges = async () => {
const { stdout } = await execute('git status --porcelain');
return stdout.trim();
};
/**
* Creates a new commit and pushes the given branch
* @param {string} branch
* @param {string} email
* @param {string} name
* @param {string} message
*/
const pushChanges = async (branch, email, name, message) => {
await createCommit(email, name, message);
await execute(`git pull --rebase`);
await execute(`git push origin ${branch} --follow-tags`);
};
/**
* Force pushes the changes to the documentation update branch
* and creates a new PR if there is none available.
* @param {string} branch
* @param {string} base
* @param {string} email
* @param {string} name
* @param {string} message
*/
const createPR = async (branch, base, email, name, message) => {
await createCommit(email, name, message);
await execute(`git checkout -b ${branch}`);
await execute(`git push --force --set-upstream origin ${branch}`);
console.log(`Changes pushed to ${branch}`);
const { context } = github;
const octokit = github.getOctokit(GITHUB_TOKEN);
const pulls = await octokit.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
});
const doesExist = pulls.data.some((pull) => {
return pull.head.ref === branch;
});
if (doesExist) {
console.log('PR already exists, nothing to do');
} else {
console.log('Creating PR');
const result = await octokit.pulls.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: message,
base,
head: branch,
});
console.log(`PR created (#${result.data.id})`);
}
};
module.exports = {
createPR,
getChanges,
pushChanges,
};

2136
yarn.lock

Разница между файлами не показана из-за своего большого размера Загрузить разницу