Skip to content

Commit

Permalink
feat: allow GIT_USER env var to be unset if SSH is used (#5840)
Browse files Browse the repository at this point in the history
* feat: allow GIT_USER env var to be unset if SSH is used

* fix: packages/docusaurus/src/commands/deploy.ts

Co-authored-by: Joshua Chen <sidachen2003@gmail.com>

* feat: allow user to specify deploymentBranch property in docusaurus.config.js (#5841)

* feat: allow user to specify deploymentBranch property in docusaurus.config.js

* docs: remove extra backtick

* docs: fix broken code block

* docs: fix i18n routes to feature requests (#5843)

* docs: fix i18n routes to feature requests

* Add redirect rules

* feat: allow GIT_USER env var to be unset if SSH is used

* fix: packages/docusaurus/src/commands/deploy.ts

Co-authored-by: Joshua Chen <sidachen2003@gmail.com>

* fix: avoid escaping hyphen in regex

* Refactor

* Update deployment.mdx

* Make SSH higher priority

* Only infer but not override

* Add tests

* Fix tests

* Fix

Co-authored-by: Joshua Chen <sidachen2003@gmail.com>
  • Loading branch information
wpyoga and Josh-Cena committed Nov 10, 2021
1 parent ecce576 commit f5732e7
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 151 deletions.
10 changes: 9 additions & 1 deletion packages/create-docusaurus/templates/facebook/README.md
Expand Up @@ -26,8 +26,16 @@ This command generates static content into the `build` directory and can be serv

### Deployment

Using SSH:

```
$ USE_SSH=true yarn deploy
```

Not using SSH:

```
$ GIT_USER=<Your GitHub username> USE_SSH=true yarn deploy
$ GIT_USER=<Your GitHub username> yarn deploy
```

If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
Expand Down
10 changes: 9 additions & 1 deletion packages/create-docusaurus/templates/shared/README.md
Expand Up @@ -26,8 +26,16 @@ This command generates static content into the `build` directory and can be serv

### Deployment

Using SSH:

```
$ USE_SSH=true yarn deploy
```

Not using SSH:

```
$ GIT_USER=<Your GitHub username> USE_SSH=true yarn deploy
$ GIT_USER=<Your GitHub username> yarn deploy
```

If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.

This file was deleted.

62 changes: 62 additions & 0 deletions packages/docusaurus/src/commands/__tests__/deploy.test.ts
@@ -0,0 +1,62 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import {buildSshUrl, buildHttpsUrl, hasSSHProtocol} from '../deploy';

describe('remoteBranchUrl', () => {
test('should build a normal ssh url', () => {
const url = buildSshUrl('github.com', 'facebook', 'docusaurus');
expect(url).toEqual('git@github.com:facebook/docusaurus.git');
});
test('should build a ssh url with port', () => {
const url = buildSshUrl('github.com', 'facebook', 'docusaurus', '422');
expect(url).toEqual('ssh://git@github.com:422/facebook/docusaurus.git');
});
test('should build a normal http url', () => {
const url = buildHttpsUrl(
'user:pass',
'github.com',
'facebook',
'docusaurus',
);
expect(url).toEqual('https://user:pass@github.com/facebook/docusaurus.git');
});
test('should build a normal http url', () => {
const url = buildHttpsUrl(
'user:pass',
'github.com',
'facebook',
'docusaurus',
'5433',
);
expect(url).toEqual(
'https://user:pass@github.com:5433/facebook/docusaurus.git',
);
});
});

describe('hasSSHProtocol', () => {
test('should recognize explicit SSH protocol', () => {
const url = 'ssh://git@github.com:422/facebook/docusaurus.git';
expect(hasSSHProtocol(url)).toEqual(true);
});

test('should recognize implied SSH protocol', () => {
const url = 'git@github.com:facebook/docusaurus.git';
expect(hasSSHProtocol(url)).toEqual(true);
});

test('should not recognize HTTPS with credentials', () => {
const url = 'https://user:pass@github.com/facebook/docusaurus.git';
expect(hasSSHProtocol(url)).toEqual(false);
});

test('should not recognize plain HTTPS URL', () => {
const url = 'https://github.com:5433/facebook/docusaurus.git';
expect(hasSSHProtocol(url)).toEqual(false);
});
});
50 changes: 0 additions & 50 deletions packages/docusaurus/src/commands/buildRemoteBranchUrl.ts

This file was deleted.

113 changes: 83 additions & 30 deletions packages/docusaurus/src/commands/deploy.ts
Expand Up @@ -13,7 +13,6 @@ import build from './build';
import {BuildCLIOptions} from '@docusaurus/types';
import path from 'path';
import os from 'os';
import {buildUrl} from './buildRemoteBranchUrl';

// GIT_PASS env variable should not appear in logs
function obfuscateGitPass(str: string) {
Expand All @@ -38,6 +37,43 @@ function shellExecLog(cmd: string) {
}
}

export function buildSshUrl(
githubHost: string,
organizationName: string,
projectName: string,
githubPort?: string,
): string {
if (githubPort) {
return `ssh://git@${githubHost}:${githubPort}/${organizationName}/${projectName}.git`;
}
return `git@${githubHost}:${organizationName}/${projectName}.git`;
}

export function buildHttpsUrl(
gitCredentials: string,
githubHost: string,
organizationName: string,
projectName: string,
githubPort?: string,
): string {
if (githubPort) {
return `https://${gitCredentials}@${githubHost}:${githubPort}/${organizationName}/${projectName}.git`;
}
return `https://${gitCredentials}@${githubHost}/${organizationName}/${projectName}.git`;
}

export function hasSSHProtocol(sourceRepoUrl: string): boolean {
try {
if (new URL(sourceRepoUrl).protocol === 'ssh:') {
return true;
}
return false;
} catch {
// Fails when there isn't a protocol
return /^([\w-]+@)?[\w.-]+:[\w./_-]+(\.git)?/.test(sourceRepoUrl); // git@github.com:facebook/docusaurus.git
}
}

export default async function deploy(
siteDir: string,
cliOptions: Partial<BuildCLIOptions> = {},
Expand All @@ -63,15 +99,32 @@ This behavior can have SEO impacts and create relative link issues.
throw new Error('Git not installed or on the PATH!');
}

const gitUser = process.env.GIT_USER;
if (!gitUser) {
throw new Error('Please set the GIT_USER environment variable!');
}
// Source repo is the repo from where the command is invoked
const sourceRepoUrl = shell
.exec('git config --get remote.origin.url', {silent: true})
.stdout.trim();

// The branch that contains the latest docs changes that will be deployed.
const currentBranch =
// The source branch; defaults to the currently checked out branch
const sourceBranch =
process.env.CURRENT_BRANCH ||
shell.exec('git rev-parse --abbrev-ref HEAD').stdout.trim();
shell.exec('git rev-parse --abbrev-ref HEAD', {silent: true}).stdout.trim();

const gitUser = process.env.GIT_USER;

let useSSH =
process.env.USE_SSH !== undefined &&
process.env.USE_SSH.toLowerCase() === 'true';

if (!gitUser && !useSSH) {
// If USE_SSH is unspecified: try inferring from repo URL
if (process.env.USE_SSH === undefined && hasSSHProtocol(sourceRepoUrl)) {
useSSH = true;
} else {
throw new Error(
'Please set the GIT_USER environment variable, or explicitly specify USE_SSH instead!',
);
}
}

const organizationName =
process.env.ORGANIZATION_NAME ||
Expand Down Expand Up @@ -107,8 +160,7 @@ This behavior can have SEO impacts and create relative link issues.
// Organization deploys looks like:
// - Git repo: https://github.com/<organization>/<organization>.github.io
// - Site url: https://<organization>.github.io
const isGitHubPagesOrganizationDeploy =
projectName.indexOf('.github.io') !== -1;
const isGitHubPagesOrganizationDeploy = projectName.includes('.github.io');
if (
isGitHubPagesOrganizationDeploy &&
!process.env.DEPLOYMENT_BRANCH &&
Expand All @@ -127,38 +179,39 @@ You can also set the deploymentBranch property in docusaurus.config.js .`);
process.env.GITHUB_HOST || siteConfig.githubHost || 'github.com';
const githubPort = process.env.GITHUB_PORT || siteConfig.githubPort;

const gitPass: string | undefined = process.env.GIT_PASS;
let gitCredentials = `${gitUser}`;
if (gitPass) {
gitCredentials = `${gitCredentials}:${gitPass}`;
let remoteBranch: string;
if (useSSH) {
remoteBranch = buildSshUrl(
githubHost,
organizationName,
projectName,
githubPort,
);
} else {
const gitPass = process.env.GIT_PASS;
const gitCredentials = gitPass ? `${gitUser!}:${gitPass}` : gitUser!;
remoteBranch = buildHttpsUrl(
gitCredentials,
githubHost,
organizationName,
projectName,
githubPort,
);
}

const useSSH = process.env.USE_SSH;
const remoteBranch = buildUrl(
githubHost,
githubPort,
gitCredentials,
organizationName,
projectName,
useSSH !== undefined && useSSH.toLowerCase() === 'true',
);

console.log(
`${chalk.cyan('Remote branch:')} ${obfuscateGitPass(remoteBranch)}`,
);

// Check if this is a cross-repo publish.
const currentRepoUrl = shell
.exec('git config --get remote.origin.url')
.stdout.trim();
const crossRepoPublish = !currentRepoUrl.endsWith(
const crossRepoPublish = !sourceRepoUrl.endsWith(
`${organizationName}/${projectName}.git`,
);

// We don't allow deploying to the same branch unless it's a cross publish.
if (currentBranch === deploymentBranch && !crossRepoPublish) {
if (sourceBranch === deploymentBranch && !crossRepoPublish) {
throw new Error(
`You cannot deploy from this branch (${currentBranch}).` +
`You cannot deploy from this branch (${sourceBranch}).` +
'\nYou will need to checkout to a different branch!',
);
}
Expand Down

0 comments on commit f5732e7

Please sign in to comment.