From efd8b5a34240f39703f3d16b841fbe9fd6d42508 Mon Sep 17 00:00:00 2001 From: Emilien Escalle Date: Tue, 17 Nov 2020 21:04:17 +0100 Subject: [PATCH] ci: merge publish workflow in main ci workflow, add helper workflows --- .github/workflows/continuous-integration.yml | 85 +++++++++++++++++-- .github/workflows/greetings.yml | 13 +++ .github/workflows/label.yml | 19 +++++ .github/workflows/publish.yml | 85 ------------------- .github/workflows/stale.yml | 19 +++++ .gitignore | 3 +- __tests__/test-project/.gitignore | 3 +- package.json | 5 +- .../CreateComponent.spec.ts.snap | 5 +- .../CreateCrudComponent.spec.ts.snap | 59 +++++++++++-- 10 files changed, 191 insertions(+), 105 deletions(-) create mode 100644 .github/workflows/greetings.yml create mode 100644 .github/workflows/label.yml delete mode 100644 .github/workflows/publish.yml create mode 100644 .github/workflows/stale.yml diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index b1ee6dfe..3d3e2e2a 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -7,6 +7,9 @@ on: pull_request: paths-ignore: - '**.md' + workflow_dispatch: + branches: + - master jobs: continuous-integration: @@ -14,7 +17,7 @@ jobs: strategy: matrix: - node-version: [12.x] + node-version: [14.x] steps: - uses: actions/checkout@v2 @@ -29,24 +32,88 @@ jobs: run: echo "::set-output name=dir::$(yarn cache dir)" - uses: actions/cache@v2 - id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) with: path: | ${{ steps.yarn-cache-dir-path.outputs.dir }} node_modules + */*/node_modules key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - name: Install Dependencies + - name: โš™๏ธ Install Dependencies run: yarn - - name: Build + - name: ๐Ÿ—๏ธ Build run: yarn build - - name: Test - run: yarn test --coverage + - name: ๐Ÿงช Test + run: | + yarn lint + yarn test:coverage env: CI: true - - uses: codecov/codecov-action@v1 + - name: ๐Ÿ“Š Code coverage + uses: codecov/codecov-action@v1 + + publish: + needs: continuous-integration + if: github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/master' + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [14.x] + + steps: + - uses: actions/checkout@v2 + + - name: Setup Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v2.1.2 + with: + node-version: ${{ matrix.node-version }} + registry-url: https://registry.npmjs.org/ + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + + - uses: actions/cache@v2 + id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) + with: + path: | + ${{ steps.yarn-cache-dir-path.outputs.dir }} + node_modules + */*/node_modules + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + + - name: โš™๏ธ Install Dependencies + run: yarn + + - name: ๐Ÿ—๏ธ Build + run: | + yarn build + yarn build:doc + + - name: ๐Ÿ”– Prepare release + run: | + git config --local user.email "${{ github.actor }}@users.noreply.github.com" + git config --local user.name "${{ github.actor }}" + npx standard-version + echo `git add -A && git commit -m "chore: prepare release"` + git push --follow-tags origin master + mkdir -p /tmp + cp -R build/docs /tmp/publish + cp -R docs /tmp/publish/ + + - name: ๐ŸŒ Publish Github pages + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: /tmp/publish + enable_jekyll: true + + - name: ๐Ÿ“ฆ Publish NPM packages + run: npm publish --access public + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }} diff --git a/.github/workflows/greetings.yml b/.github/workflows/greetings.yml new file mode 100644 index 00000000..4989f566 --- /dev/null +++ b/.github/workflows/greetings.yml @@ -0,0 +1,13 @@ +name: Greetings + +on: [pull_request, issues] + +jobs: + greeting: + runs-on: ubuntu-latest + steps: + - uses: actions/first-interaction@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + issue-message: 'Hi, thank for reporting an issue, we will check it out very soon' + pr-message: 'Hi, thank you for creating your PR, we will check it out very soon' diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml new file mode 100644 index 00000000..ec5b29ac --- /dev/null +++ b/.github/workflows/label.yml @@ -0,0 +1,19 @@ +# This workflow will triage pull requests and apply a label based on the +# paths that are modified in the pull request. +# +# To use this workflow, you will need to set up a .github/labeler.yml +# file with configuration. For more information, see: +# https://github.com/actions/labeler + +name: Labeler +on: [pull_request] + +jobs: + label: + + runs-on: ubuntu-latest + + steps: + - uses: actions/labeler@v3 + with: + repo-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index ba31a1fd..00000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,85 +0,0 @@ -name: Publish - -on: - watch: - types: [started] - -jobs: - authorize: - runs-on: ubuntu-latest - - steps: - - uses: octokit/request-action@v2.0.17 - id: get_latest_release - with: - route: GET /repos/:repository/collaborators/${{ github.actor }} - repository: ${{ github.repository }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - publish: - needs: [authorize] - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [12.x] - - steps: - - uses: actions/checkout@v2 - - - name: Setup Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2.1.2 - with: - node-version: ${{ matrix.node-version }} - registry-url: https://registry.npmjs.org/ - - - name: Get yarn cache directory path - id: yarn-cache-dir-path - run: echo "::set-output name=dir::$(yarn cache dir)" - - - uses: actions/cache@v2 - id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) - with: - path: | - ${{ steps.yarn-cache-dir-path.outputs.dir }} - node_modules - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - - name: Install Dependencies - run: yarn - - - name: Build - run: yarn build - - - name: Prepare release - run: | - git config user.email "${{ github.actor }}@users.noreply.github.com" - git config user.name "${{ github.actor }}" - npx standard-version - echo `git add -A && git commit -m "chore: prepare release"` - git push --follow-tags origin master - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Publish release - run: npm publish --access public - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }} - - - name: Prepare gh-pages - run: | - mkdir build - cp README.md build/index.md - npm run build:doc - - - name: Publish gh-pages - uses: JamesIves/github-pages-deploy-action@3.7.1 - with: - ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} - BASE_BRANCH: master # The branch the action should deploy from. - BRANCH: gh-pages # The branch the action should deploy to. - FOLDER: build # The folder the action should deploy. diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 00000000..33a5be94 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,19 @@ +name: Mark stale issues and pull requests + +on: + schedule: + - cron: "30 1 * * *" + +jobs: + stale: + + runs-on: ubuntu-latest + + steps: + - uses: actions/stale@v3.0.13 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + stale-issue-message: 'Stale issue message' + stale-pr-message: 'Stale pull request message' + stale-issue-label: 'no-issue-activity' + stale-pr-label: 'no-pr-activity' diff --git a/.gitignore b/.gitignore index 3bedf128..6859b516 100644 --- a/.gitignore +++ b/.gitignore @@ -60,4 +60,5 @@ typings/ # next.js build output .next -/dist \ No newline at end of file +/dist +/build \ No newline at end of file diff --git a/__tests__/test-project/.gitignore b/__tests__/test-project/.gitignore index 2fd7648e..7438d64b 100644 --- a/__tests__/test-project/.gitignore +++ b/__tests__/test-project/.gitignore @@ -3,4 +3,5 @@ # But not these files... !.gitignore -!package.json \ No newline at end of file +!package.json +!yarn.lock \ No newline at end of file diff --git a/package.json b/package.json index b0960c27..b23444d7 100644 --- a/package.json +++ b/package.json @@ -8,11 +8,12 @@ }, "scripts": { "start": "yarn link && tsc-watch", - "build": "rimraf ./dist && yarn lint && tsc && yarn build:copy-templates", + "build": "rimraf ./dist && tsc && yarn build:copy-templates", "build:copy-templates": "rimraf ./dist/**/*.template && cd src && copyfiles ./**/*.template ../dist/ && cd ..", - "build:doc": "rimraf -rf docs && typedoc", + "build:doc": "rimraf -rf build/docs && typedoc", "lint": "eslint \"src/**/*.{ts,tsx}\"", "test": "jest", + "test:coverage": "yarn test --coverage", "postinstall": "ts-dev-tools install" }, "repository": { diff --git a/src/actions/create-component/__snapshots__/CreateComponent.spec.ts.snap b/src/actions/create-component/__snapshots__/CreateComponent.spec.ts.snap index cc66e95b..fafce9ce 100644 --- a/src/actions/create-component/__snapshots__/CreateComponent.spec.ts.snap +++ b/src/actions/create-component/__snapshots__/CreateComponent.spec.ts.snap @@ -72,11 +72,13 @@ describe('App', () => { `; exports[`createComponent run should create expected components files 1`] = ` -"import React, { FC } from 'react'; +"import { FC } from 'react'; import { Helmet } from 'react-helmet'; import { useTranslation } from 'react-i18next'; interface ITestComponentProps { } + + const TestComponent: FC = (props) => { const { t } = useTranslation(); @@ -89,6 +91,7 @@ const TestComponent: FC = (props) => { ; }; + export default TestComponent;" `; diff --git a/src/actions/create-component/__snapshots__/CreateCrudComponent.spec.ts.snap b/src/actions/create-component/__snapshots__/CreateCrudComponent.spec.ts.snap index 8690ff1c..871b5e57 100644 --- a/src/actions/create-component/__snapshots__/CreateCrudComponent.spec.ts.snap +++ b/src/actions/create-component/__snapshots__/CreateCrudComponent.spec.ts.snap @@ -28,12 +28,14 @@ describe('CreateTestEntity', () => { exports[`createCrudComponent run should create all crud components files 2`] = ` "import { Create, IFormProps, IOnSubmitForm } from '@reactionable/ui-bootstrap'; -import React, { FC } from 'react'; +import { FC } from 'react'; import { Helmet } from 'react-helmet'; import { useTranslation } from 'react-i18next'; import { ITestEntityData, ITestEntityValues, testEntityFormChildren, testEntityFormSchema, testEntityFormValues } from '../TestEntitiesConfig'; interface IProps extends Pick, 'onSuccess'> { } + + const CreateTestEntity: FC = (props) => { const { t } = useTranslation(); const onSubmit: IOnSubmitForm = async (values, formikHelpers) => { @@ -54,6 +56,7 @@ const CreateTestEntity: FC = (props) => { form={form} children={props.children}/> ; }; + export default CreateTestEntity;" `; @@ -85,7 +88,7 @@ describe('DeleteTestEntity', () => { exports[`createCrudComponent run should create all crud components files 4`] = ` "import { Delete, IDeleteProps } from '@reactionable/ui-bootstrap'; -import React, { FC } from 'react'; +import { FC } from 'react'; import { Helmet } from 'react-helmet'; import { useTranslation } from 'react-i18next'; import { ITestEntityData } from '../TestEntitiesConfig'; @@ -94,6 +97,8 @@ interface IProps extends Pick, 'onSuccess'> { id: string; label?: boolean; } + + const DeleteTestEntity: FC = (props) => { const { t } = useTranslation(); const { id, label, ...deleteProps} = props; @@ -113,6 +118,7 @@ const DeleteTestEntity: FC = (props) => { /> ; }; + export default DeleteTestEntity;" `; @@ -146,18 +152,23 @@ exports[`createCrudComponent run should create all crud components files 6`] = ` "import { useQueryList } from '@reactionable/amplify'; import { Suspense, generatePath } from '@reactionable/core'; import { List } from '@reactionable/ui-bootstrap'; -import React, { FC, lazy } from 'react'; +import { FC, lazy } from 'react'; import { Helmet } from 'react-helmet'; import { useTranslation } from 'react-i18next'; import { Link, useRouteMatch } from 'react-router-dom'; import { IListTestEntitiesQueryVariables, ITestEntityData } from '../TestEntitiesConfig'; const CreateTestEntity = lazy(() => import('../create-test-entity/CreateTestEntity')); + const DeleteTestEntity = lazy(() => import('../delete-test-entity/DeleteTestEntity')); + + interface ITestEntityItemProps { data: ITestEntityData; refetch: () => void; } + + const ListTestEntitiesItem: FC = ({ data, refetch }) => { const { t } = useTranslation(); const match = useRouteMatch(); @@ -182,7 +193,12 @@ const ListTestEntitiesItem: FC = ({ data, refetch }) => { ; }; + + + interface IListTestEntitiesProps { } + + const ListTestEntities: FC = (props) => { const { t } = useTranslation(); const { data, refetch, next, previous, ...listProps } = useQueryList({ @@ -218,6 +234,7 @@ const ListTestEntities: FC = (props) => { /> ; }; + export default ListTestEntities;" `; @@ -251,17 +268,21 @@ exports[`createCrudComponent run should create all crud components files 8`] = ` "import { useQueryList } from '@reactionable/amplify'; import { Suspense } from '@reactionable/core'; import { Read } from '@reactionable/ui-bootstrap'; -import React, { FC, lazy } from 'react'; +import { FC, lazy } from 'react'; import { Helmet } from 'react-helmet'; import { useTranslation } from 'react-i18next'; import { Link, useParams } from 'react-router-dom'; import { IGetTestEntityQueryVariables, ITestEntityData } from '../TestEntitiesConfig'; const UpdateTestEntity = lazy(() => import('../update-test-entity/UpdateTestEntity')); + + interface ITestEntityItemProps { data: ITestEntityData; refetch: () => void; } + + const ReadTestEntityItem: FC = ({ data, refetch }) => { const { t } = useTranslation(); const match = useRouteMatch(); @@ -282,7 +303,12 @@ const ReadTestEntityItem: FC = ({ data, refetch }) => { } ; }; + + + interface IReadTestEntityProps { } + + const ReadTestEntity: FC = (props) => { const { t } = useTranslation(); const matchParams = useParams<{ testEntityId: string }>(); @@ -302,6 +328,7 @@ const ReadTestEntity: FC = (props) => { /> ; }; + export default ReadTestEntity;" `; @@ -333,13 +360,15 @@ describe('UpdateTestEntity', () => { exports[`createCrudComponent run should create all crud components files 10`] = ` "import { IFormProps, IOnSubmitForm, Update } from '@reactionable/ui-bootstrap'; -import React, { FC } from 'react'; +import { FC } from 'react'; import { Helmet } from 'react-helmet'; import { useTranslation } from 'react-i18next'; import { useParams } from 'react-router-dom'; import { ITestEntityData, ITestEntityValues, testEntityFormChildren, testEntityFormSchema, testEntityFormValues } from '../TestEntitiesConfig'; interface IProps extends Pick, 'onSuccess' | 'formValues'> { } + + const UpdateTestEntity: FC = (props) => { const { t } = useTranslation(); const matchParams = useParams<{ testEntityId: string }>(); @@ -369,6 +398,7 @@ const UpdateTestEntity: FC = (props) => { modal form={form} children={props.children}/> ; }; + export default UpdateTestEntity;" `; @@ -401,14 +431,19 @@ describe('TestEntities', () => { exports[`createCrudComponent run should create all crud components files 12`] = ` "import { Crud } from '@reactionable/core'; import { IUseLayoutProps } from '@reactionable/ui-bootstrap'; -import React, { FC, lazy } from 'react'; +import { FC, lazy } from 'react'; import { Helmet } from 'react-helmet'; import { useTranslation } from 'react-i18next'; import { ITestEntityData } from './TestEntitiesConfig'; const ListTestEntities = lazy(() => import('./list-test-entities/ListTestEntities')); + const ReadTestEntity = lazy(() => import('./read-test-entity/ReadTestEntity')); + + interface ITestEntitiesProps { } + + const TestEntities: FC = (props) => { const { t } = useTranslation(); @@ -422,6 +457,7 @@ const TestEntities: FC = (props) => { /> ; }; + export default TestEntities;" `; @@ -436,21 +472,32 @@ export type ITestEntityData = { id: string; label: string; }; + export type ITestEntityValues = Omit; + + export type IGetTestEntityQueryVariables = Pick & { }; + + export type IListTestEntitiesQueryVariables = { }; + + export const testEntityFormValues: ITestEntityValues = { label: '', }; + + export const testEntityFormSchema = (t: TFunction) => ({ label: string() .min(1, t('Test entity label must be at least 1 characters')) .required(t('Test entity label is required')), }); + + export const testEntityFormChildren = (t: TFunction) => (formikProps: IFormChildrenProps) => <>