diff --git a/docs/images/circleCiGithubToken.png b/docs/images/circleCiGithubToken.png new file mode 100644 index 0000000000..649c954427 Binary files /dev/null and b/docs/images/circleCiGithubToken.png differ diff --git a/docs/images/circleCiSetUpProject.png b/docs/images/circleCiSetUpProject.png new file mode 100644 index 0000000000..b29b7b24b9 Binary files /dev/null and b/docs/images/circleCiSetUpProject.png differ diff --git a/docs/userGuide/deployingTheSite.md b/docs/userGuide/deployingTheSite.md index d87d5df3fa..e95ee80909 100644 --- a/docs/userGuide/deployingTheSite.md +++ b/docs/userGuide/deployingTheSite.md @@ -65,6 +65,14 @@ You can override the default deployment settings %%(e.g., repo/branch to deploy) ### Deploying to GitHub Pages via CI Tools **You can setup CI Tools to automatically build and deploy your site on GitHub Pages every time your GitHub repo is updated.** + + +With the exception of Github Actions, a Github Personal Access Token with **repo** permissions is required for deploying your MarkBind site to Github Pages via CI tools. + +You may refer to Github's documentation on [how to generate a Github Personal Access Token](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/#creating-a-token). Ensure that you have enabled **repo** permissions as shown from the screenshot below. Take note of the generated token - you will not be able to see it again once you navigate away from the page. + + + To instruct [Github Actions](https://docs.github.com/en/actions) to build and deploy the site when you push to the repository, add a Github Actions workflow file in your project repo at the location `/.github/workflows/deploy.yml` A sample workflow file is provided below: @@ -92,7 +100,7 @@ jobs: -The sample `deploy.yml` workflow above uses the [default Github Token secret](https://docs.github.com/en/actions/reference/authentication-in-a-workflow) that is generated automatically for each Github Actions workflow. You may also use a Github Personal Access Token in place of the default Github Token. For steps on setting up your Github Personal Access Token, you may refer to the [setup instructions for Travis CI](#configuring-your-repository-in-travis-ci). +The sample `deploy.yml` workflow above uses the [default Github Token secret](https://docs.github.com/en/actions/reference/authentication-in-a-workflow) that is generated automatically for each Github Actions workflow. You may also use a [Github Personal Access Token](#generating-a-github-personal-access-token) in place of the default Github Token. Once you have created the file, commit and push the file to your repo. Github Actions should start to build and deploy your markbind site. You can verify this by visiting `www.github.com///actions`. @@ -137,9 +145,7 @@ Since May 2018, Travis CI has been [undergoing migration to `travis-ci.com`](htt ##### Configuring your repository in Travis CI -1. [Generate a GitHub personal access token](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/#creating-a-token) with **repo** permissions. Take note of the generated token - you will not be able to see it again once you navigate away from the page. - -1. [Add an environment variable in Travis CI](https://docs.travis-ci.com/user/environment-variables/#defining-variables-in-repository-settings) named `GITHUB_TOKEN`, with the value set to the personal access token generated in the previous step. ==Ensure that _Display value in the build log_ is set to _Off_.== +1. [Add an environment variable in Travis CI](https://docs.travis-ci.com/user/environment-variables/#defining-variables-in-repository-settings) named `GITHUB_TOKEN`, with the value set to your [generated Github Personal Access Token](#generating-a-github-personal-access-token). ==Ensure that _Display value in the build log_ is set to _Off_.== 1. Add a `.travis.yml` file to instruct Travis CI to build and deploy the site when you push to the repository. An example `.travis.yml` file that can accomplish this is given below: ```yaml @@ -207,8 +213,7 @@ The `repo` value can be changed to your specific repository as desired. ##### Configuring your repository in AppVeyor CI -1. [Generate a Github personal access token](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/#creating-a-token) with **repo permissions**. - +1. Ensure that you have generated a [Github Personal Access token with **repo** permissions](#generating-a-github-personal-access-token). 1. Navigate to the project settings page of your repository in AppVeyor CI. 1. On the left menu, click on __Environment__. 1. Under the heading __Environment variables__, add a custom environment variable named `GITHUB_TOKEN`, with the value set to the personal access token that was generated in the first step. ==Ensure that you toggle variable encryption by clicking on the padlock.== @@ -236,6 +241,47 @@ build: off Commit and push `appveyor.yml` to your github repository. Thereafter, AppVeyor CI should begin to run the build script. You are able to view the current build status by clicking on your repository in the [AppVeyor projects page](https://ci.appveyor.com/projects). Once the build succeeds, you should be able to view your Markbind site, after a few seconds, at `http://.github.io/` e.g., http://se-edu.github.io/se-book. + + + + +##### Adding your repository to Circle CI +1. Ensure that you have generated a [Github Personal Access Token with **repo** permissions](#generating-a-github-personal-access-token). +1. Sign in to [Circle CI](https://circleci.com/) using your Github account. +1. In the projects dashboard, click on the `Set Up Project` button beside the repo containing your Markbind site. + + +##### Configuring your repository in Circle CI +1. Once you have set up your project, click on the `Project Settings` button. +2. On the left, click on the `Environment Variables` tab and add a custom Environment Variable, `GITHUB_TOKEN`, which contains the value of your Github Personal Access Token. + +3. Commit and push a `config.yml` file to the repo containg your Markbind Site that instructs Circle CI to build and deploy your Markbind site to Github Pages whenever you push to your repository. Ensure that the `config.yml` file is located in the `/.circleci/` directory. A sample `config.yml` file is shown below: +``` +jobs: + Build-And-Deploy: + docker: + - image: 'cimg/base:stable' + steps: + - checkout + - node/install: + node-version: "10" + npm-version: "6" + install-yarn: false + - run: node --version + - run: npm i -g markbind-cli + - run: markbind build + - run: markbind deploy --ci +version: 2.1 +orbs: + node: circleci/node@4.1.0 +workflows: + Deploy-Markbind-Site: + jobs: + - Build-And-Deploy +``` +After you have pushed the `config.yml` file to your remote repo, you should see Circle CI starting to run the Deploy job in your projects dashboard. Once it is successful, you should be able to view your Markbind site at `http://.github.io/`. + +For more information on customizing your build script, you may refer to [Circle CI Config Reference Document](https://circleci.com/docs/2.0/configuration-reference/#section=configuration).
diff --git a/packages/core/src/Site/index.js b/packages/core/src/Site/index.js index 81fac27d33..19e6c06942 100644 --- a/packages/core/src/Site/index.js +++ b/packages/core/src/Site/index.js @@ -1142,6 +1142,7 @@ class Site { const options = {}; options.branch = this.siteConfig.deploy.branch || defaultDeployConfig.branch; options.message = this.siteConfig.deploy.message || defaultDeployConfig.message; + options.message = options.message.concat(' [skip ci]'); options.repo = this.siteConfig.deploy.repo || defaultDeployConfig.repo; if (ciTokenVar) { @@ -1153,38 +1154,36 @@ class Site { let repoSlug; if (process.env.TRAVIS) { - if (options.repo) { - repoSlug = Site.extractRepoSlug(options.repo); - } else { - repoSlug = process.env.TRAVIS_REPO_SLUG; - } + repoSlug = Site.extractRepoSlug(options.repo, process.env.TRAVIS_REPO_SLUG); options.user = { name: 'Deployment Bot', email: 'deploy@travis-ci.org', }; } else if (process.env.APPVEYOR) { - if (options.repo) { - repoSlug = Site.extractRepoSlug(options.repo); - } else { - repoSlug = process.env.APPVEYOR_REPO_NAME; - } + repoSlug = Site.extractRepoSlug(options.repo, process.env.APPVEYOR_REPO_NAME); options.user = { name: 'AppVeyorBot', email: 'deploy@appveyor.com', }; } else if (process.env.GITHUB_ACTIONS) { - if (options.repo) { - repoSlug = Site.extractRepoSlug(options.repo); - } else { - repoSlug = process.env.GITHUB_REPOSITORY; - } + repoSlug = Site.extractRepoSlug(options.repo, process.env.GITHUB_REPOSITORY); options.user = { name: 'github-actions', email: 'github-actions@github.com', }; + } else if (process.env.CIRCLECI) { + repoSlug = Site.extractRepoSlug( + options.repo, + `${process.env.CIRCLE_PROJECT_USERNAME}/${process.env.CIRCLE_PROJECT_REPONAME}`, + ); + + options.user = { + name: 'circleci-bot', + email: 'deploy@circleci.com', + }; } else { throw new Error('-c/--ci should only be run in CI environments.'); } @@ -1199,7 +1198,10 @@ class Site { /** * Extract repo slug from user-specified repo URL so that we can include the access token */ - static extractRepoSlug(repo) { + static extractRepoSlug(repo, ciRepoSlug) { + if (!repo) { + return ciRepoSlug; + } const repoSlugRegex = /github\.com[:/]([\w-]+\/[\w-.]+)\.git$/; const repoSlugMatch = repoSlugRegex.exec(repo); if (!repoSlugMatch) { diff --git a/packages/core/test/unit/Site.test.js b/packages/core/test/unit/Site.test.js index 047cf87dd5..22d7ae2622 100644 --- a/packages/core/test/unit/Site.test.js +++ b/packages/core/test/unit/Site.test.js @@ -231,7 +231,7 @@ test('Site deploys with default settings', async () => { expect(ghpages.options) .toEqual({ branch: 'gh-pages', - message: 'Site Update.', + message: 'Site Update. [skip ci]', repo: '', remote: 'origin', }); @@ -256,7 +256,7 @@ test('Site deploys with custom settings', async () => { expect(ghpages.options) .toEqual({ branch: 'master', - message: 'Custom Site Update.', + message: 'Custom Site Update. [skip ci]', repo: 'https://github.com/USER/REPO.git', remote: 'origin', }); @@ -289,6 +289,9 @@ describe('Site deploy with various CI environments', () => { delete process.env.APPVEYOR_REPO_NAME; delete process.env.GITHUB_ACTIONS; delete process.env.GITHUB_REPOSITORY; + delete process.env.CIRCLECI; + delete process.env.CIRCLE_PROJECT_USERNAME; + delete process.env.CIRCLE_PROJECT_REPONAME; }); afterAll(() => { @@ -296,15 +299,24 @@ describe('Site deploy with various CI environments', () => { process.env = { ...OLD_ENV }; }); + /* eslint-disable max-len */ test.each([ - ['TRAVIS', 'TRAVIS_REPO_SLUG', { name: 'Deployment Bot', email: 'deploy@travis-ci.org' }], - ['APPVEYOR', 'APPVEYOR_REPO_NAME', { name: 'AppVeyorBot', email: 'deploy@appveyor.com' }], - ['GITHUB_ACTIONS', 'GITHUB_REPOSITORY', { name: 'github-actions', email: 'github-actions@github.com' }], + ['TRAVIS', { reposlug: 'TRAVIS_REPO_SLUG' }, { name: 'Deployment Bot', email: 'deploy@travis-ci.org' }], + ['APPVEYOR', { reposlug: 'APPVEYOR_REPO_NAME' }, { name: 'AppVeyorBot', email: 'deploy@appveyor.com' }], + ['GITHUB_ACTIONS', { reposlug: 'GITHUB_REPOSITORY' }, { name: 'github-actions', email: 'github-actions@github.com' }], + ['CIRCLECI', { username: 'CIRCLE_PROJECT_USERNAME', reponame: 'CIRCLE_PROJECT_REPONAME' }, { name: 'circleci-bot', email: 'deploy@circleci.com' }], ])('Site deploy -c/--ci deploys with default settings', + /* eslint-enable max-len */ async (ciIdentifier, repoSlugIdentifier, deployBotUser) => { process.env[ciIdentifier] = true; - process.env[repoSlugIdentifier] = 'GENERIC_USER/GENERIC_REPO'; process.env.GITHUB_TOKEN = 'githubToken'; + const genericRepoSlug = 'GENERIC_USER/GENERIC_REPO'; + if (repoSlugIdentifier.reposlug) { + process.env[repoSlugIdentifier.reposlug] = genericRepoSlug; + } else { + process.env[repoSlugIdentifier.username] = 'GENERIC_USER'; + process.env[repoSlugIdentifier.reponame] = 'GENERIC_REPO'; + } const json = { ...PAGE_NJK, @@ -315,20 +327,25 @@ describe('Site deploy with various CI environments', () => { const site = new Site('./', '_site'); await site.deploy(true); expect(ghpages.options.repo) - // eslint-disable-next-line max-len - .toEqual(`https://x-access-token:${process.env.GITHUB_TOKEN}@github.com/${process.env[repoSlugIdentifier]}.git`); + .toEqual(`https://x-access-token:${process.env.GITHUB_TOKEN}@github.com/${genericRepoSlug}.git`); expect(ghpages.options.user).toEqual(deployBotUser); }); test.each([ - ['TRAVIS', 'TRAVIS_REPO_SLUG'], - ['APPVEYOR', 'APPVEYOR_REPO_NAME'], - ['GITHUB_ACTIONS', 'GITHUB_REPOSITORY'], + ['TRAVIS', { reposlug: 'TRAVIS_REPO_SLUG' }], + ['APPVEYOR', { reposlug: 'APPVEYOR_REPO_NAME' }], + ['GITHUB_ACTIONS', { reposlug: 'GITHUB_REPOSITORY' }], + ['CIRCLECI', { username: 'CIRCLE_PROJECT_USERNAME', reponame: 'CIRCLE_PROJECT_REPONAME' }], ])('Site deploy -c/--ci deploys with custom GitHub repo', async (ciIdentifier, repoSlugIdentifier) => { process.env[ciIdentifier] = true; - process.env[repoSlugIdentifier] = 'GENERIC_USER/GENERIC_REPO'; process.env.GITHUB_TOKEN = 'githubToken'; + if (repoSlugIdentifier.reposlug) { + process.env[repoSlugIdentifier.reposlug] = 'GENERIC_USER/GENERIC_REPO'; + } else { + process.env[repoSlugIdentifier.username] = 'GENERIC_USER'; + process.env[repoSlugIdentifier.reponame] = 'GENERIC_REPO'; + } const customRepoConfig = JSON.parse(SITE_JSON_DEFAULT); customRepoConfig.deploy.repo = 'https://github.com/USER/REPO.git'; @@ -345,14 +362,21 @@ describe('Site deploy with various CI environments', () => { }); test.each([ - ['TRAVIS', 'TRAVIS_REPO_SLUG'], - ['APPVEYOR', 'APPVEYOR_REPO_NAME'], - ['GITHUB_ACTIONS', 'GITHUB_REPOSITORY'], + ['TRAVIS', { reposlug: 'TRAVIS_REPO_SLUG' }], + ['APPVEYOR', { reposlug: 'APPVEYOR_REPO_NAME' }], + ['GITHUB_ACTIONS', { reposlug: 'GITHUB_REPOSITORY' }], + ['CIRCLECI', { username: 'CIRCLE_PROJECT_USERNAME', reponame: 'CIRCLE_PROJECT_REPONAME' }], ])('Site deploy -c/--ci deploys to correct repo when .git is in repo name', async (ciIdentifier, repoSlugIdentifier) => { process.env[ciIdentifier] = true; - process.env[repoSlugIdentifier] = 'GENERIC_USER/GENERIC_REPO.github.io'; process.env.GITHUB_TOKEN = 'githubToken'; + const genericRepoSlug = 'GENERIC_USER/GENERIC_REPO.github.io'; + if (repoSlugIdentifier.reposlug) { + process.env[repoSlugIdentifier.reposlug] = genericRepoSlug; + } else { + process.env[repoSlugIdentifier.username] = 'GENERIC_USER'; + process.env[repoSlugIdentifier.reponame] = 'GENERIC_REPO.github.io'; + } const json = { ...PAGE_NJK, @@ -364,7 +388,7 @@ describe('Site deploy with various CI environments', () => { await site.deploy(true); expect(ghpages.options.repo) // eslint-disable-next-line max-len - .toEqual(`https://x-access-token:${process.env.GITHUB_TOKEN}@github.com/${process.env[repoSlugIdentifier]}.git`); + .toEqual(`https://x-access-token:${process.env.GITHUB_TOKEN}@github.com/${genericRepoSlug}.git`); }); test('Site deploy -c/--ci should not deploy if not in CI environment', async () => { @@ -386,6 +410,7 @@ describe('Site deploy with various CI environments', () => { ['TRAVIS'], ['APPVEYOR'], ['GITHUB_ACTIONS'], + ['CIRCLECI'], ])('Site deploy -c/--ci should not deploy without authentication token', async (ciIdentifier) => { process.env[ciIdentifier] = true; @@ -405,6 +430,7 @@ describe('Site deploy with various CI environments', () => { ['TRAVIS'], ['APPVEYOR'], ['GITHUB_ACTIONS'], + ['CIRCLECI'], ])('Site deploy -c/--ci should not deploy if custom repository is not on GitHub', async (ciIdentifier) => { process.env[ciIdentifier] = true; process.env.GITHUB_TOKEN = 'githubToken';