diff --git a/.editorconfig b/.editorconfig index 178260d5d..e86d443fd 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,7 +11,7 @@ insert_final_newline = true [{package.json,yarn.lock}] indent_size = 2 -[generators/*/templates/circleci.yaml.ejs] +[generators/*/templates/workflow.yaml.ejs] indent_size = 2 [generators/*/templates/ansible/**/*.yaml.ejs] diff --git a/generators/app/index.test.ts b/generators/app/index.test.ts index a0c15550f..6f87efd16 100644 --- a/generators/app/index.test.ts +++ b/generators/app/index.test.ts @@ -2,7 +2,6 @@ import fs from 'fs'; import path from 'path'; import YAML from 'yaml'; import helpers from 'yeoman-test'; -import * as CircleCI from '../../utils/circleci'; describe('When running the generator with React', () => { let root: string; @@ -33,16 +32,6 @@ describe('When running the generator with React', () => { expect(config.devDependencies.vite).toBeDefined(); }); - - test('It add the right dependencies to the deploy job', async () => { - const content = await fs.promises.readFile(path.resolve(root, '.circleci', 'config.yml'), 'utf8'); - const config = CircleCI.Config.fromRaw(YAML.parse(content)); - - expect(config.workflows.build?.jobs.deploy?.requires).toEqual([ - 'backend-archive', - 'frontend-archive', - ]); - }); }); describe('When running the generator with Next.js', () => { @@ -74,45 +63,6 @@ describe('When running the generator with Next.js', () => { expect(config.dependencies.next).toBeDefined(); }); - - test('It add the right dependencies to the deploy job', async () => { - const content = await fs.promises.readFile(path.resolve(root, '.circleci', 'config.yml'), 'utf8'); - const config = CircleCI.Config.fromRaw(YAML.parse(content)); - - expect(config.workflows.build?.jobs.deploy?.requires).toEqual([ - 'backend-archive', - 'frontend-archive', - ]); - }); -}); - -describe('When running the generator with Symfony', () => { - let root: string; - - beforeAll(async () => { - const result = await helpers.run(__dirname) - .withPrompts({ - backend: 'symfony', - contactEmail: 'test@example.com', - add: false, - twig: true, - }); - - root = result.cwd; - }); - - afterAll(async () => { - await fs.promises.rm(root, { recursive: true }); - }); - - test('It add the right dependencies to the deploy job', async () => { - const content = await fs.promises.readFile(path.resolve(root, '.circleci', 'config.yml'), 'utf8'); - const config = CircleCI.Config.fromRaw(YAML.parse(content)); - - expect(config.workflows.build?.jobs.deploy?.requires).toEqual([ - 'backend-build', - ]); - }); }); describe('When running the generator with Flutter', () => { diff --git a/generators/express/index.test.ts b/generators/express/index.test.ts index b9391318d..e394f25b8 100644 --- a/generators/express/index.test.ts +++ b/generators/express/index.test.ts @@ -3,7 +3,6 @@ import path from 'path'; import execa from 'execa'; import YAML from 'yaml'; import helpers from 'yeoman-test'; -import { Config } from '../../utils/circleci'; describe('When running the generator', () => { let root: string; @@ -90,12 +89,6 @@ describe('When running the generator with kubernetes deployment', () => { await fs.promises.rm(root, { recursive: true }); }); - test('It generates a valid CircleCI config', async () => { - const content = await fs.promises.readFile(path.join(root, '.circleci', 'config.yml'), 'utf8'); - - Config.fromRaw(YAML.parse(content)); - }); - test('It generates a valid terraform config', async () => { const cwd = path.join(root, 'environments', 'staging'); diff --git a/generators/express/index.ts b/generators/express/index.ts index fe11315a2..e47d783f2 100644 --- a/generators/express/index.ts +++ b/generators/express/index.ts @@ -23,7 +23,7 @@ class ExpressGenerator extends PackageGenerator { this.configureDockerCompose('docker-compose.yaml.ejs'); - this.configureCircleCI('circleci.yaml.ejs'); + this.renderTemplate('workflow.yaml.ejs', `.github/workflows/${packageName}.yaml`); switch (this.config.get('deployment')) { case DeploymentChoice.Ansible: @@ -32,10 +32,6 @@ class ExpressGenerator extends PackageGenerator { encrypt: createEncrypt(this.readDestination('ansible/vault_pass.txt').trim()), cookieSecret: cryptoRandomString({ length: 64, type: 'alphanumeric' }), }); - - this.updateCircleCIConfig((config) => { - config.workflows.build!.jobs.deploy!.requires.push(`${packageName}-archive`); - }); break; case DeploymentChoice.Kubernetes: { const packageVar = varName(packageName); diff --git a/generators/express/templates/circleci.yaml.ejs b/generators/express/templates/circleci.yaml.ejs deleted file mode 100644 index b9c5d46f8..000000000 --- a/generators/express/templates/circleci.yaml.ejs +++ /dev/null @@ -1,186 +0,0 @@ -version: '2.1' - -orbs: -<% if (deployment === 'kubernetes') { %> - docker: circleci/docker@2.0.1 -<% } %> - node: circleci/node@4.5.1 - -executors: - node: - docker: - - image: node:18.16.0 - -jobs: - <%= packageName %>-yarn-install: - executor: node - steps: - - checkout - - node/install-packages: - app-dir: <%= packagePath %> - include-branch-in-cache-key: false - pkg-manager: yarn - - persist_to_workspace: - root: . - paths: - - <%= packagePath %>/node_modules - - <%= packageName %>-lint: - executor: node - working_directory: ~/project/<%= packagePath %> - steps: - - checkout: - path: ~/project - - attach_workspace: - at: ~/project - - run: - command: yarn lint - - <%= packageName %>-test: - executor: node - working_directory: ~/project/<%= packagePath %> - steps: - - checkout: - path: ~/project - - attach_workspace: - at: ~/project - - run: - command: yarn test - -<% if (deployment === 'ansible') { %> - <%= packageName %>-build: - executor: node - working_directory: ~/project/<%= packagePath %> - steps: - - checkout: - path: ~/project - - attach_workspace: - at: ~/project - - run: echo "export default '${CIRCLE_SHA1}';" > src/version.ts - - run: - command: yarn build - environment: - NODE_ENV: production - - persist_to_workspace: - root: ~/project - paths: - - <%= packagePath %>/dist - - <%= packageName %>-archive: - executor: node - working_directory: ~/project/<%= packagePath %> - steps: - - checkout: - path: ~/project - - attach_workspace: - at: ~/project - - run: - command: yarn install --prod --frozen-lockfile - - run: - name: Create archive - command: | - tar --create --gzip --file=archive.tar.gz --owner=0 --group=0 \ - dist/ \ - node_modules/ \ - - store_artifacts: - path: archive.tar.gz - destination: <%= packageName %>.tar.gz - - persist_to_workspace: - root: ~/project - paths: - - <%= packagePath %>/archive.tar.gz - - <%= packageName %>-sentry-release: - docker: - - image: getsentry/sentry-cli:1.61.0 - entrypoint: '' - environment: - SENTRY_PROJECT: <%= projectName %>-<%= packageName %> - steps: - - attach_workspace: - at: ~/project - - run: sentry-cli releases new <%= projectName %>-<%= packageName %>@${CIRCLE_SHA1} - - run: sentry-cli releases files <%= projectName %>-<%= packageName %>@${CIRCLE_SHA1} upload-sourcemaps <%= packagePath %>/dist - - run: sentry-cli releases finalize <%= projectName %>-<%= packageName %>@${CIRCLE_SHA1} -<% } %> -<% if (deployment === 'kubernetes') { %> - <%= packageName %>-build: - executor: docker/docker - steps: - - setup_remote_docker: - version: 20.10.11 - - checkout - - docker/build: - image: <%= projectName %>-<%= packageName %> - path: <%= packagePath %> - docker-context: <%= packagePath %> - extra_build_args: '--build-arg VERSION=$(git log --max-count 1 --pretty=format:%H "<%= packagePath %>")' - <%= packageName %>-build-and-push: - executor: docker/docker - steps: - - setup_remote_docker: - version: 20.10.11 - - checkout - - docker/build: - registry: $DOCKER_REGISTRY - image: <%= projectName %>-<%= packageName %> - tag: << pipeline.git.branch >> - path: <%= packagePath %> - docker-context: <%= packagePath %> - extra_build_args: '--build-arg VERSION=$(git log --max-count 1 --pretty=format:%H "<%= packagePath %>")' - - docker/check: - registry: $DOCKER_REGISTRY - - docker/push: - registry: $DOCKER_REGISTRY - image: <%= projectName %>-<%= packageName %> - tag: << pipeline.git.branch >> -<% } %> - -workflows: - version: '2' - build: - jobs: - - <%= packageName %>-yarn-install - - <%= packageName %>-lint: - requires: - - <%= packageName %>-yarn-install - - <%= packageName %>-test: - requires: - - <%= packageName %>-lint -<% if (deployment === 'ansible') { %> - - <%= packageName %>-build: - requires: - - <%= packageName %>-test - - <%= packageName %>-archive: - requires: - - <%= packageName %>-build - - <%= packageName %>-sentry-release: - context: sentry.io - requires: - - <%= packageName %>-build - filters: - branches: - only: - - develop - - main -<% } %> -<% if (deployment === 'kubernetes') { %> - - <%= packageName %>-build-and-push: - context: - - docker-registry - requires: - - <%= packageName %>-test - filters: - branches: - only: - - develop - - main - - <%= packageName %>-build: - requires: - - <%= packageName %>-test - filters: - branches: - ignore: - - develop - - main -<% } %> diff --git a/generators/express/templates/deployment/ansible/package/deployment.yaml.ejs b/generators/express/templates/deployment/ansible/package/deployment.yaml.ejs index 20d739d69..97d302120 100644 --- a/generators/express/templates/deployment/ansible/package/deployment.yaml.ejs +++ b/generators/express/templates/deployment/ansible/package/deployment.yaml.ejs @@ -1,18 +1,16 @@ --- - import_role: - name: build + name: find_github_artifact vars: - job_name: <%= packageName %>-archive repository_name: <%= repositoryName %> + workflow_name: <%= packageName %> delegate_to: localhost + when: artifact_url is not defined - import_role: - name: deploy + name: deploy_artifact environment: '{{ <%= varName(packageName) %>_env }}' vars: path: /home/<%= packageName %> - artifact_path: <%= packageName %>.tar.gz - job_name: <%= packageName %>-archive - repository_name: <%= repositoryName %> before_finalize: - ./node_modules/.bin/typeorm --dataSource dist/infrastructure/database/data-source.js migration:run after_finalize: diff --git a/generators/express/templates/deployment/ansible/package/deployment_ci.yaml.ejs b/generators/express/templates/deployment/ansible/package/deployment_ci.yaml.ejs deleted file mode 100644 index 90bcbfff9..000000000 --- a/generators/express/templates/deployment/ansible/package/deployment_ci.yaml.ejs +++ /dev/null @@ -1,11 +0,0 @@ ---- -- import_role: - name: deploy - environment: '{{ <%= varName(packageName) %>_env }}' - vars: - path: /home/<%= packageName %> - artifact_file: '{{ playbook_dir }}/../<%= packagePath %>/archive.tar.gz' - before_finalize: - - ./node_modules/.bin/typeorm --dataSource dist/infrastructure/database/data-source.js migration:run - after_finalize: - - systemctl restart <%= packageName %> diff --git a/generators/express/templates/workflow.yaml.ejs b/generators/express/templates/workflow.yaml.ejs new file mode 100644 index 000000000..8515de230 --- /dev/null +++ b/generators/express/templates/workflow.yaml.ejs @@ -0,0 +1,109 @@ +name: <%= packageName %> + +on: + pull_request: + paths: <%= packagePath %>/** + push: + branches: + - main + - develop + +defaults: + run: + working-directory: <%= packagePath %> + +jobs: + code-style: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18.16.0 + cache: yarn + cache-dependency-path: <%= packagePath %>/yarn.lock + - run: yarn install --frozen-lockfile + - run: yarn lint + + test: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18.16.0 + cache: yarn + cache-dependency-path: <%= packagePath %>/yarn.lock + - run: yarn install --frozen-lockfile + - run: yarn test + +<%_ if (deployment === 'ansible') { _%> + build: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18.16.0 + cache: yarn + cache-dependency-path: <%= packagePath %>/yarn.lock + - run: yarn install --frozen-lockfile + - run: echo "export default '${{ github.sha }}';" > src/version.ts + - run: yarn build + env: + NODE_ENV: production + - run: yarn install --frozen-lockfile --prod + - run: | + tar --create \ + --file=archive.tar \ + --owner=0 \ + --group=0 \ + --exclude '*.map' \ + dist/ \ + node_modules/ \ + - uses: actions/upload-artifact@v3 + with: + name: <%= packageName %> + path: <%= packagePath %>/archive.tar + if-no-files-found: error + if: github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/main' + - uses: getsentry/action-release@v1 + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + SENTRY_PROJECT: <%= projectName %>-<%= packageName %> + with: + ignore_missing: true + sourcemaps: dist + version_prefix: <%= projectName %>-<%= packageName %>@ + working_directory: <%= packagePath %> + if: github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/main' + deploy-staging: + if: github.ref == 'refs/heads/develop' + needs: [build] + uses: ./.github/workflows/deploy.yaml + with: + package: <%= packageName %> + secrets: inherit +<%_ } _%> +<%_ if (deployment === 'kubernetes') { _%> + build: + runs-on: ubuntu-22.04 + steps: + - uses: docker/setup-buildx-action@v2.5.0 + - uses: docker/login-action@v2.1.0 + with: + registry: ${{ secrets.STAGING_REGISTRY }} + username: ${{ secrets.STAGING_REGISTRY_LOGIN }} + password: ${{ secrets.STAGING_REGISTRY_PASSWORD }} + if: github.ref == 'refs/heads/develop' + - uses: docker/build-push-action@v4.0.0 + with: + context: '{{ defaultContext }}:<%= packagePath %>' + cache-from: type=gha + cache-to: type=gha,mode=max + tags: ${{ secrets.STAGING_REGISTRY }}/<%= projectName %>/<%= packageName %> + push: ${{ github.ref == 'refs/heads/develop' }} + build-args: | + VERSION=${{ github.sha }} +<%_ } _%> diff --git a/generators/next-js/index.test.ts b/generators/next-js/index.test.ts index 323c0114b..3a1e44522 100644 --- a/generators/next-js/index.test.ts +++ b/generators/next-js/index.test.ts @@ -3,7 +3,6 @@ import path from 'path'; import execa from 'execa'; import YAML from 'yaml'; import helpers from 'yeoman-test'; -import { Config } from '../../utils/circleci'; describe('When running the generator', () => { let root: string; @@ -64,12 +63,6 @@ describe('When running the generator with kubernetes deployment', () => { await fs.promises.rm(root, { recursive: true }); }); - test('It generates a valid CircleCI config', async () => { - const content = await fs.promises.readFile(path.join(root, '.circleci', 'config.yml'), 'utf8'); - - Config.fromRaw(YAML.parse(content)); - }); - test('It generates a valid terraform config', async () => { const cwd = path.join(root, 'environments', 'staging'); diff --git a/generators/next-js/index.ts b/generators/next-js/index.ts index b427d2c15..1295d915b 100644 --- a/generators/next-js/index.ts +++ b/generators/next-js/index.ts @@ -20,17 +20,13 @@ class NextJSGenerator extends PackageGenerator { this.configureDockerCompose('docker-compose.yaml.ejs'); - this.configureCircleCI('circleci.yaml.ejs'); + this.renderTemplate('workflow.yaml.ejs', `.github/workflows/${packageName}.yaml`); switch (this.config.get('deployment')) { case DeploymentChoice.Ansible: this.configureAnsible('deployment/ansible', { repositoryName: this.config.get('repositoryName'), }); - - this.updateCircleCIConfig((config) => { - config.workflows.build!.jobs.deploy!.requires.push(`${packageName}-archive`); - }); break; case DeploymentChoice.Kubernetes: { const packageVar = varName(packageName); diff --git a/generators/next-js/templates/circleci.yaml.ejs b/generators/next-js/templates/circleci.yaml.ejs deleted file mode 100644 index 3aed385f0..000000000 --- a/generators/next-js/templates/circleci.yaml.ejs +++ /dev/null @@ -1,152 +0,0 @@ -version: '2.1' - -orbs: -<% if (deployment === 'kubernetes') { %> - docker: circleci/docker@2.0.1 -<% } %> - node: circleci/node@4.5.1 - -executors: - node: - docker: - - image: node:18.16.0 - -jobs: - <%= packageName %>-yarn-install: - executor: node - steps: - - checkout - - node/install-packages: - app-dir: <%= packagePath %> - include-branch-in-cache-key: false - pkg-manager: yarn - - persist_to_workspace: - root: . - paths: - - <%= packagePath %>/node_modules - - <%= packageName %>-lint: - executor: node - working_directory: ~/project/<%= packagePath %> - steps: - - checkout: - path: ~/project - - attach_workspace: - at: ~/project - - run: - command: yarn lint - -<% if (deployment === 'ansible') { %> - <%= packageName %>-build: - executor: node - working_directory: ~/project/<%= packagePath %> - steps: - - checkout: - path: ~/project - - attach_workspace: - at: ~/project - - run: echo "export default '${CIRCLE_SHA1}';" > version.ts - - run: - command: yarn build - environment: - NODE_ENV: production - - persist_to_workspace: - root: ~/project - paths: - - <%= packagePath %>/build - - <%= packageName %>-archive: - executor: node - working_directory: ~/project/<%= packagePath %> - steps: - - checkout: - path: ~/project - - attach_workspace: - at: ~/project - - run: - command: yarn install --prod --frozen-lockfile - - run: - name: Create archive - command: | - tar --create --gzip --file=archive.tar.gz --owner=0 --group=0 \ - build/ \ - next.config.js \ - node_modules/ \ - public/ \ - - store_artifacts: - path: archive.tar.gz - destination: <%= packageName %>.tar.gz - - persist_to_workspace: - root: ~/project - paths: - - <%= packagePath %>/archive.tar.gz -<% } %> -<% if (deployment === 'kubernetes') { %> - <%= packageName %>-build: - executor: docker/docker - steps: - - setup_remote_docker: - version: 20.10.11 - - checkout - - docker/build: - image: <%= projectName %>-<%= packageName %> - path: <%= packagePath %> - docker-context: <%= packagePath %> - extra_build_args: '--build-arg VERSION=$(git log --max-count 1 --pretty=format:%H "<%= packagePath %>")' - <%= packageName %>-build-and-push: - executor: docker/docker - steps: - - setup_remote_docker: - version: 20.10.11 - - checkout - - docker/build: - registry: $DOCKER_REGISTRY - image: <%= projectName %>-<%= packageName %> - tag: << pipeline.git.branch >> - path: <%= packagePath %> - docker-context: <%= packagePath %> - extra_build_args: '--build-arg VERSION=$(git log --max-count 1 --pretty=format:%H "<%= packagePath %>")' - - docker/check: - registry: $DOCKER_REGISTRY - - docker/push: - registry: $DOCKER_REGISTRY - image: <%= projectName %>-<%= packageName %> - tag: << pipeline.git.branch >> -<% } %> - -workflows: - version: '2' - build: - jobs: - - <%= packageName %>-yarn-install - - <%= packageName %>-lint: - requires: - - <%= packageName %>-yarn-install -<% if (deployment === 'ansible') { %> - - <%= packageName %>-build: - requires: - - <%= packageName %>-lint - - <%= packageName %>-archive: - requires: - - <%= packageName %>-build -<% } %> -<% if (deployment === 'kubernetes') { %> - - <%= packageName %>-build-and-push: - context: - - docker-registry - requires: - - <%= packageName %>-test - filters: - branches: - only: - - develop - - main - - <%= packageName %>-build: - requires: - - <%= packageName %>-test - filters: - branches: - ignore: - - develop - - main -<% } %> diff --git a/generators/next-js/templates/deployment/ansible/package/deployment.yaml.ejs b/generators/next-js/templates/deployment/ansible/package/deployment.yaml.ejs index 4900d72fd..6de5059b0 100644 --- a/generators/next-js/templates/deployment/ansible/package/deployment.yaml.ejs +++ b/generators/next-js/templates/deployment/ansible/package/deployment.yaml.ejs @@ -1,17 +1,15 @@ --- - import_role: - name: build + name: find_github_artifact vars: - job_name: <%= packageName %>-archive repository_name: <%= repositoryName %> + workflow_name: <%= packageName %> delegate_to: localhost + when: artifact_url is not defined - import_role: - name: deploy + name: deploy_artifact environment: '{{ <%= varName(packageName) %>_env }}' vars: path: /home/<%= packageName %> - artifact_path: <%= packageName %>.tar.gz - job_name: <%= packageName %>-archive - repository_name: <%= repositoryName %> after_finalize: - systemctl restart <%= packageName %> diff --git a/generators/next-js/templates/deployment/ansible/package/deployment_ci.yaml.ejs b/generators/next-js/templates/deployment/ansible/package/deployment_ci.yaml.ejs deleted file mode 100644 index 066634d7f..000000000 --- a/generators/next-js/templates/deployment/ansible/package/deployment_ci.yaml.ejs +++ /dev/null @@ -1,9 +0,0 @@ ---- -- import_role: - name: deploy - environment: '{{ <%= varName(packageName) %>_env }}' - vars: - path: /home/<%= packageName %> - artifact_file: '{{ playbook_dir }}/../<%= packagePath %>/archive.tar.gz' - after_finalize: - - systemctl restart <%= packageName %> diff --git a/generators/next-js/templates/workflow.yaml.ejs b/generators/next-js/templates/workflow.yaml.ejs new file mode 100644 index 000000000..e5575a817 --- /dev/null +++ b/generators/next-js/templates/workflow.yaml.ejs @@ -0,0 +1,87 @@ +name: <%= packageName %> + +on: + pull_request: + paths: <%= packagePath %>/** + push: + branches: + - main + - develop + +defaults: + run: + working-directory: <%= packagePath %> + +jobs: + code-style: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18.16.0 + cache: yarn + cache-dependency-path: <%= packagePath %>/yarn.lock + - run: yarn install --frozen-lockfile + - run: yarn lint + +<%_ if (deployment === 'ansible') { _%> + build: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18.16.0 + cache: yarn + cache-dependency-path: <%= packagePath %>/yarn.lock + - run: yarn install --frozen-lockfile + - run: yarn build + env: + NODE_ENV: production + - run: yarn install --frozen-lockfile --prod + - run: | + tar --create \ + --file=archive.tar \ + --owner=0 \ + --group=0 \ + --exclude '*.map' \ + build/ \ + next.config.js \ + node_modules/ \ + public/ \ + - uses: actions/upload-artifact@v3 + with: + name: <%= packageName %> + path: <%= packagePath %>/archive.tar + if-no-files-found: error + if: github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/main' + deploy-staging: + if: github.ref == 'refs/heads/develop' + needs: [build] + uses: ./.github/workflows/deploy.yaml + with: + package: <%= packageName %> + secrets: inherit +<%_ } _%> +<%_ if (deployment === 'kubernetes') { _%> + build: + runs-on: ubuntu-22.04 + steps: + - uses: docker/setup-buildx-action@v2.5.0 + - uses: docker/login-action@v2.1.0 + with: + registry: ${{ secrets.STAGING_REGISTRY }} + username: ${{ secrets.STAGING_REGISTRY_LOGIN }} + password: ${{ secrets.STAGING_REGISTRY_PASSWORD }} + if: github.ref == 'refs/heads/develop' + - uses: docker/build-push-action@v4.0.0 + with: + context: '{{ defaultContext }}:<%= packagePath %>' + cache-from: type=gha + cache-to: type=gha,mode=max + tags: ${{ secrets.STAGING_REGISTRY }}/<%= projectName %>/<%= packageName %> + push: ${{ github.ref == 'refs/heads/develop' }} + build-args: | + VERSION=${{ github.sha }} +<%_ } _%> diff --git a/generators/react/index.test.ts b/generators/react/index.test.ts index 8b4041eb5..d9ce458fe 100644 --- a/generators/react/index.test.ts +++ b/generators/react/index.test.ts @@ -3,7 +3,6 @@ import path from 'path'; import execa from 'execa'; import YAML from 'yaml'; import helpers from 'yeoman-test'; -import { Config } from '../../utils/circleci'; describe('When running the generator', () => { let root: string; @@ -73,12 +72,6 @@ describe('When running the generator with kubernetes deployment', () => { await fs.promises.rm(root, { recursive: true }); }); - test('It generates a valid CircleCI config', async () => { - const content = await fs.promises.readFile(path.join(root, '.circleci', 'config.yml'), 'utf8'); - - Config.fromRaw(YAML.parse(content)); - }); - test('It generates a project with a valid terraform config', async () => { const cwd = path.join(root, 'environments', 'staging'); diff --git a/generators/react/index.ts b/generators/react/index.ts index 3125673fd..674de90cf 100644 --- a/generators/react/index.ts +++ b/generators/react/index.ts @@ -20,17 +20,13 @@ class CreateReactAppGenerator extends PackageGenerator { this.configureDockerCompose('docker-compose.yaml.ejs'); - this.configureCircleCI('circleci.yaml.ejs'); + this.renderTemplate('workflow.yaml.ejs', `.github/workflows/${packageName}.yaml`); switch (this.config.get('deployment')) { case DeploymentChoice.Ansible: this.configureAnsible('deployment/ansible', { repositoryName: this.config.get('repositoryName'), }); - - this.updateCircleCIConfig((config) => { - config.workflows.build!.jobs.deploy!.requires.push(`${packageName}-archive`); - }); break; case DeploymentChoice.Kubernetes: { const packageVar = varName(packageName); diff --git a/generators/react/templates/circleci.yaml.ejs b/generators/react/templates/circleci.yaml.ejs deleted file mode 100644 index a1acc45cf..000000000 --- a/generators/react/templates/circleci.yaml.ejs +++ /dev/null @@ -1,188 +0,0 @@ -version: '2.1' - -orbs: -<% if (deployment === 'kubernetes') { %> - docker: circleci/docker@2.0.1 -<% } %> - node: circleci/node@4.5.1 - -executors: - node: - docker: - - image: node:18.16.0 - -jobs: - <%= packageName %>-yarn-install: - executor: node - steps: - - checkout - - node/install-packages: - app-dir: <%= packagePath %> - include-branch-in-cache-key: false - pkg-manager: yarn - - persist_to_workspace: - root: . - paths: - - <%= packagePath %>/node_modules - - <%= packageName %>-lint: - executor: node - working_directory: ~/project/<%= packagePath %> - steps: - - checkout: - path: ~/project - - attach_workspace: - at: ~/project - - run: - command: yarn lint - -<% if (deployment === 'ansible') { %> - <%= packageName %>-build: - executor: node - working_directory: ~/project/<%= packagePath %> - steps: - - checkout: - path: ~/project - - attach_workspace: - at: ~/project - - run: echo "export default '${CIRCLE_SHA1}';" > src/version.ts - - run: - command: yarn build - environment: - NODE_ENV: production - - persist_to_workspace: - root: ~/project - paths: - - <%= packagePath %>/dist - - <%= packageName %>-archive: - executor: node - working_directory: ~/project/<%= packagePath %> - steps: - - checkout: - path: ~/project - - attach_workspace: - at: ~/project - - run: - name: Create archive - command: | - tar --create --gzip --file=archive.tar.gz --owner=0 --group=0 \ - dist/ \ - - store_artifacts: - path: archive.tar.gz - destination: <%= packageName %>.tar.gz - - persist_to_workspace: - root: ~/project - paths: - - <%= packagePath %>/archive.tar.gz - - <%= packageName %>-sentry-release: - docker: - - image: getsentry/sentry-cli:1.61.0 - entrypoint: '' - environment: - SENTRY_PROJECT: <%= projectName %>-<%= packageName %> - steps: - - attach_workspace: - at: ~/project - - run: sentry-cli releases new <%= projectName %>-<%= packageName %>@${CIRCLE_SHA1} - - run: sentry-cli releases files <%= projectName %>-<%= packageName %>@${CIRCLE_SHA1} upload-sourcemaps <%= packagePath %>/dist - - run: sentry-cli releases finalize <%= projectName %>-<%= packageName %>@${CIRCLE_SHA1} -<% } %> - -<% if (deployment === 'kubernetes') { %> - <%= packageName %>-build: - executor: docker/docker - steps: - - setup_remote_docker: - version: 20.10.11 - - checkout - - docker/build: - image: <%= projectName %>-<%= packageName %> - path: <%= packagePath %> - docker-context: <%= packagePath %> - extra_build_args: '--build-arg VERSION=$(git log --max-count 1 --pretty=format:%H "<%= packagePath %>")' - <%= packageName %>-build-and-push: - executor: docker/docker - steps: - - setup_remote_docker: - version: 20.10.11 - - checkout - - docker/build: - registry: $DOCKER_REGISTRY - image: <%= projectName %>-<%= packageName %> - tag: << pipeline.git.branch >> - path: <%= packagePath %> - docker-context: <%= packagePath %> - extra_build_args: '--build-arg VERSION=$(git log --max-count 1 --pretty=format:%H "<%= packagePath %>")' - - docker/build: - registry: $DOCKER_REGISTRY - image: <%= projectName %>-<%= packageName %> - tag: << pipeline.git.branch >>-sentry - path: <%= packagePath %> - docker-context: <%= packagePath %> - extra_build_args: '--build-arg VERSION=$(git log --max-count 1 --pretty=format:%H "<%= packagePath %>") --target sentry' - - docker/check: - registry: $DOCKER_REGISTRY - - docker/push: - registry: $DOCKER_REGISTRY - image: <%= projectName %>-<%= packageName %> - tag: << pipeline.git.branch >> - - run: - name: Publish source maps to sentry - command: | - IMAGE="$DOCKER_REGISTRY/<%= projectName %>-<%= packageName %>:<< pipeline.git.branch >>-sentry" - ENV="--env SENTRY_AUTH_TOKEN --env SENTRY_ORG --env SENTRY_PROJECT=<%= projectName %>-<%= packageName %>" - VERSION=<%= projectName %>-<%= packageName %>@$(git log --max-count 1 --pretty=format:%H "<%= packagePath %>") - - docker run --rm $ENV $IMAGE releases new $VERSION - docker run --rm $ENV $IMAGE releases files $VERSION upload-sourcemaps . - docker run --rm $ENV $IMAGE releases finalize $VERSION -<% } %> - -workflows: - version: '2' - build: - jobs: - - <%= packageName %>-yarn-install - - <%= packageName %>-lint: - requires: - - <%= packageName %>-yarn-install -<% if (deployment === 'ansible') { %> - - <%= packageName %>-build: - requires: - - <%= packageName %>-lint - - <%= packageName %>-archive: - requires: - - <%= packageName %>-build - - <%= packageName %>-sentry-release: - context: sentry.io - requires: - - <%= packageName %>-build - filters: - branches: - only: - - develop - - main -<% } %> -<% if (deployment === 'kubernetes') { %> - - <%= packageName %>-build-and-push: - context: - - docker-registry - - sentry.io - requires: - - <%= packageName %>-lint - filters: - branches: - only: - - develop - - main - - <%= packageName %>-build: - requires: - - <%= packageName %>-lint - filters: - branches: - ignore: - - develop - - main -<% } %> diff --git a/generators/react/templates/deployment/ansible/package/deployment.yaml.ejs b/generators/react/templates/deployment/ansible/package/deployment.yaml.ejs index f4f56b4cf..63b166ab5 100644 --- a/generators/react/templates/deployment/ansible/package/deployment.yaml.ejs +++ b/generators/react/templates/deployment/ansible/package/deployment.yaml.ejs @@ -1,19 +1,17 @@ --- - import_role: - name: build - delegate_to: localhost + name: find_github_artifact vars: - job_name: <%= packageName %>-archive repository_name: <%= repositoryName %> + workflow_name: <%= packageName %> + delegate_to: localhost + when: artifact_url is not defined - import_role: - name: deploy + name: deploy_artifact vars: path: /home/<%= packageName %> - artifact_path: <%= packageName %>.tar.gz before_finalize: - > sed --in-place --expression='s#
]*>#
_data|dictsort %} data-{{ key }}="{{ value }}"{% endfor -%} >#g' dist/index.html - job_name: <%= packageName %>-archive - repository_name: <%= repositoryName %> diff --git a/generators/react/templates/deployment/ansible/package/deployment_ci.yaml.ejs b/generators/react/templates/deployment/ansible/package/deployment_ci.yaml.ejs deleted file mode 100644 index d7ad117e4..000000000 --- a/generators/react/templates/deployment/ansible/package/deployment_ci.yaml.ejs +++ /dev/null @@ -1,12 +0,0 @@ ---- -- import_role: - name: deploy - environment: '{{ <%= varName(packageName) %>_env }}' - vars: - path: /home/<%= packageName %> - artifact_file: '{{ playbook_dir }}/../<%= packagePath %>/archive.tar.gz' - before_finalize: - - > - sed --in-place --expression='s#
]*>#
_data|dictsort %} data-{{ key }}="{{ value }}"{% endfor -%} - >#g' dist/index.html diff --git a/generators/react/templates/workflow.yaml.ejs b/generators/react/templates/workflow.yaml.ejs new file mode 100644 index 000000000..26ad71086 --- /dev/null +++ b/generators/react/templates/workflow.yaml.ejs @@ -0,0 +1,119 @@ +name: <%= packageName %> + +on: + pull_request: + paths: <%= packagePath %>/** + push: + branches: + - main + - develop + +defaults: + run: + working-directory: <%= packagePath %> + +jobs: + code-style: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18.16.0 + cache: yarn + cache-dependency-path: <%= packagePath %>/yarn.lock + - run: yarn install --frozen-lockfile + - run: yarn lint + +<%_ if (deployment === 'ansible') { _%> + build: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18.16.0 + cache: yarn + cache-dependency-path: <%= packagePath %>/yarn.lock + - run: yarn install --frozen-lockfile + - run: echo "export default '${{ github.sha }}';" > src/version.ts + - run: yarn build + env: + NODE_ENV: production + - run: yarn install --frozen-lockfile --prod + - run: | + tar --create \ + --file=archive.tar \ + --owner=0 \ + --group=0 \ + --exclude '*.map' \ + dist/ + - uses: actions/upload-artifact@v3 + with: + name: <%= packageName %> + path: <%= packagePath %>/archive.tar + if-no-files-found: error + if: github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/main' + - uses: getsentry/action-release@v1 + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + SENTRY_PROJECT: <%= projectName %>-<%= packageName %> + with: + ignore_missing: true + sourcemaps: dist + version_prefix: <%= projectName %>-<%= packageName %>@ + working_directory: <%= packagePath %> + if: github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/main' + deploy-staging: + if: github.ref == 'refs/heads/develop' + needs: [build] + uses: ./.github/workflows/deploy.yaml + with: + package: <%= packageName %> + secrets: inherit +<%_ } _%> +<%_ if (deployment === 'kubernetes') { _%> + build: + runs-on: ubuntu-22.04 + steps: + - uses: docker/setup-buildx-action@v2.5.0 + - uses: docker/login-action@v2.1.0 + with: + registry: ${{ secrets.STAGING_REGISTRY }} + username: ${{ secrets.STAGING_REGISTRY_LOGIN }} + password: ${{ secrets.STAGING_REGISTRY_PASSWORD }} + if: github.ref == 'refs/heads/develop' + - uses: docker/build-push-action@v4.0.0 + with: + context: '{{ defaultContext }}:<%= packagePath %>' + cache-from: type=gha + cache-to: type=gha,mode=max + tags: ${{ secrets.STAGING_REGISTRY }}/<%= projectName %>/<%= packageName %> + push: ${{ github.ref == 'refs/heads/develop' }} + build-args: | + VERSION=${{ github.sha }} + - uses: docker/build-push-action@v4.0.0 + with: + context: '{{ defaultContext }}:<%= packagePath %>' + cache-from: type=gha + cache-to: type=gha,mode=max + tags: ${{ secrets.STAGING_REGISTRY }}/<%= projectName %>/<%= packageName %>/sentry + target: sentry + load: true + build-args: | + VERSION=${{ github.sha }} + - run: | + ENV="--env SENTRY_AUTH_TOKEN --env SENTRY_ORG --env SENTRY_PROJECT" + docker run --rm $ENV $IMAGE releases new $VERSION + docker run --rm $ENV $IMAGE releases files $VERSION upload-sourcemaps . + docker run --rm $ENV $IMAGE releases finalize $VERSION + working-directory: . + env: + IMAGE: ${{ secrets.STAGING_REGISTRY }}/<%= projectName %>/<%= packageName %>/sentry + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_PROJECT: <%= projectName %>-<%= packageName %> + SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + VERSION: <%= projectName %>-<%= packageName %>@${{ github.sha }} + if: github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/main' +<%_ } _%> diff --git a/generators/root/templates/deployment/ansible/.circleci/.editorconfig b/generators/root/templates/base/.github/workflows/.editorconfig similarity index 55% rename from generators/root/templates/deployment/ansible/.circleci/.editorconfig rename to generators/root/templates/base/.github/workflows/.editorconfig index 4d3cd4d62..848b94ced 100644 --- a/generators/root/templates/deployment/ansible/.circleci/.editorconfig +++ b/generators/root/templates/base/.github/workflows/.editorconfig @@ -1,2 +1,2 @@ -[config.yml] +[*.yaml] indent_size = 2 diff --git a/generators/root/templates/deployment/ansible/.circleci/config.yml b/generators/root/templates/deployment/ansible/.circleci/config.yml deleted file mode 100644 index ad70334d2..000000000 --- a/generators/root/templates/deployment/ansible/.circleci/config.yml +++ /dev/null @@ -1,52 +0,0 @@ -version: '2.1' -orbs: - terraform: circleci/terraform@3.0.1 - -jobs: - deploy: - docker: - - image: python:3.8.0 - steps: - - checkout - - attach_workspace: - at: ~/project - - terraform/install: - terraform_version: 1.1.7 - - run: - name: Setup terraform - command: | - echo "scw_access_key = $SCW_ACCESS_KEY" >> terraform.tfvars - echo "scw_secret_key = $SCW_SECRET_KEY" >> terraform.tfvars - - terraform init --backend-config="access_key=$SCW_ACCESS_KEY" --backend-config="secret_key=$SCW_SECRET_KEY" - working_directory: ~/project/terraform/staging - - run: - name: Setup ansible - command: | - apt-get update - apt-get install --yes jq - - pip install ansible==2.9.6 jmespath - - echo "$VAULT_PASS" > vault_pass.txt - - git submodule update --init - - ssh-keyscan -H $(cd ../terraform/staging/ && terraform output --json hosts | jq --raw-output .server) >> ~/.ssh/known_hosts - working_directory: ~/project/ansible - - run: - command: ansible-playbook --inventory staging.py deployment_ci.yaml - working_directory: ~/project/ansible - -workflows: - version: '2' - build: - jobs: - - deploy: - context: - - scaleway - requires: [] - filters: - branches: - only: - - develop diff --git a/generators/root/templates/deployment/ansible/.github/workflows/deploy.yaml b/generators/root/templates/deployment/ansible/.github/workflows/deploy.yaml new file mode 100644 index 000000000..d0d1cdd34 --- /dev/null +++ b/generators/root/templates/deployment/ansible/.github/workflows/deploy.yaml @@ -0,0 +1,49 @@ +on: + workflow_call: + inputs: + package: + required: true + type: string + +jobs: + deploy: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + with: + submodules: true + - name: Setup terraform + run: | + echo 'scw_access_key = ${{ secrets.SCW_ACCESS_KEY }}' >> terraform.tfvars + echo 'scw_secret_key = ${{ secrets.SCW_SECRET_KEY }}' >> terraform.tfvars + + terraform init --backend-config="access_key=${{ secrets.SCW_ACCESS_KEY }}" --backend-config="secret_key=${{ secrets.SCW_SECRET_KEY }}" + shell: bash + working-directory: terraform/staging + - name: Setup ansible + run: | + pipx inject ansible-core jmespath + + echo '${{ secrets.SSH_PRIVATE_KEY }}' > ./id_rsa && chmod 600 ./id_rsa + echo 'github_token: ${{ github.token }}' > vars/github.yaml + echo '${{ secrets.VAULT_PASS }}' > ./vault_pass.txt + + mkdir -p ~/.ssh/ && ssh-keyscan -H $(cd ../terraform/staging/ && terraform output --json hosts | jq --raw-output .server) >> ~/.ssh/known_hosts + shell: bash + working-directory: ansible + - name: Download artifact + id: download + uses: actions/download-artifact@v3.0.2 + with: + name: ${{ inputs.package }} + - name: Deploy + run: | + ansible \ + --inventory staging.py \ + --private-key ./id_rsa \ + --module-name include_tasks + --args file=packages/${{ inputs.package }}/deployment.yaml + --extra-vars artifact_url=file://${{ steps.download.outputs.download-path }} + server + shell: bash + working-directory: ansible diff --git a/generators/root/templates/deployment/ansible/ansible/.gitignore b/generators/root/templates/deployment/ansible/ansible/.gitignore index e013b1d82..30838f9b8 100644 --- a/generators/root/templates/deployment/ansible/ansible/.gitignore +++ b/generators/root/templates/deployment/ansible/ansible/.gitignore @@ -1,4 +1,3 @@ /__pycache__/ /vault_pass.txt -/vars/circleci.yaml /vars/github.yaml diff --git a/generators/root/templates/deployment/ansible/ansible/README.md b/generators/root/templates/deployment/ansible/ansible/README.md index 4ae3da9dd..76a8ed0b9 100644 --- a/generators/root/templates/deployment/ansible/ansible/README.md +++ b/generators/root/templates/deployment/ansible/ansible/README.md @@ -50,13 +50,11 @@ You can use these files' contents as playbook variables like this: - vars/variables.yaml ``` -Some `folder-roles` (`build`, `deploy`) require the presence of `circleci.yaml` -and `github.yaml`. Use the `.dist` files in the `vars/` folder to create them, and -follow their instructions to fill them. +Some `folder-roles` (`build`, `deploy`) require the presence of `github.yaml`. Use the `.dist` +files in the `vars/` folder to create them, and follow their instructions to fill them. These two files should be in your `.gitignore`: - `vars/github.yaml` -- `vars/circleci.yaml` ## Group vars diff --git a/generators/root/templates/deployment/ansible/ansible/deployment.yaml b/generators/root/templates/deployment/ansible/ansible/deployment.yaml index 8fedccf12..d84d8a365 100644 --- a/generators/root/templates/deployment/ansible/ansible/deployment.yaml +++ b/generators/root/templates/deployment/ansible/ansible/deployment.yaml @@ -13,5 +13,4 @@ loop_control: loop_var: package vars_files: - - vars/circleci.yaml - vars/github.yaml diff --git a/generators/root/templates/deployment/ansible/ansible/deployment_ci.yaml b/generators/root/templates/deployment/ansible/ansible/deployment_ci.yaml deleted file mode 100644 index 69ba3f00a..000000000 --- a/generators/root/templates/deployment/ansible/ansible/deployment_ci.yaml +++ /dev/null @@ -1,14 +0,0 @@ ---- -- hosts: server - tasks: - - name: List packages - find: - paths: '{{ playbook_dir }}/packages' - file_type: directory - delegate_to: localhost - register: packages - - name: Include packages deployment - include_tasks: '{{ package.path }}/deployment_ci.yaml' - loop: '{{ packages.files }}' - loop_control: - loop_var: package diff --git a/generators/root/templates/deployment/ansible/ansible/vars/circleci.yaml.dist b/generators/root/templates/deployment/ansible/ansible/vars/circleci.yaml.dist deleted file mode 100644 index e9f9736bc..000000000 --- a/generators/root/templates/deployment/ansible/ansible/vars/circleci.yaml.dist +++ /dev/null @@ -1,3 +0,0 @@ ---- -# Generate a token at https://circleci.com/account/api -circleci_token: ~ diff --git a/generators/symfony/index.test.ts b/generators/symfony/index.test.ts index 91679625b..c10e7adfe 100644 --- a/generators/symfony/index.test.ts +++ b/generators/symfony/index.test.ts @@ -3,7 +3,6 @@ import path from 'path'; import execa from 'execa'; import YAML from 'yaml'; import helpers from 'yeoman-test'; -import { Config } from '../../utils/circleci'; describe('When running the generator', () => { let root: string; @@ -92,12 +91,6 @@ describe('When running the generator with kubernetes deployment', () => { await fs.promises.rm(root, { recursive: true }); }); - test('It generates a valid CircleCI config', async () => { - const content = await fs.promises.readFile(path.join(root, '.circleci', 'config.yml'), 'utf8'); - - Config.fromRaw(YAML.parse(content)); - }); - test('It generates a valid terraform config', async () => { const cwd = path.join(root, 'environments', 'staging'); @@ -131,12 +124,6 @@ describe('When running the generator with twig frontend and kubernetes deploymen .withArguments(['test']); }); - test('It generates a valid CircleCI config', async () => { - const content = await fs.promises.readFile(path.join(root, '.circleci', 'config.yml'), 'utf8'); - - Config.fromRaw(YAML.parse(content)); - }); - afterAll(async () => { await fs.promises.rm(root, { recursive: true }); }); diff --git a/generators/symfony/index.ts b/generators/symfony/index.ts index 8246014f9..790c908ab 100644 --- a/generators/symfony/index.ts +++ b/generators/symfony/index.ts @@ -45,7 +45,8 @@ class SymfonyGenerator extends PackageGenerator { this.configureDockerCompose('docker-compose.yaml.ejs'); - this.configureCircleCI('circleci.yaml.ejs', { twig }); + this.renderTemplate('actions', '.github/actions'); + this.renderTemplate('workflow.yaml.ejs', `.github/workflows/${packageName}.yaml`, { twig }); if (twig) { this.renderTemplate('base-twig', packagePath); @@ -61,10 +62,6 @@ class SymfonyGenerator extends PackageGenerator { secret: cryptoRandomString({ length: 64, type: 'alphanumeric' }), twig, }); - - this.updateCircleCIConfig((config) => { - config.workflows.build!.jobs.deploy!.requires.push(`${packageName}-build`); - }); break; case DeploymentChoice.Kubernetes: { const packageVar = varName(packageName); diff --git a/generators/symfony/templates/actions/composer-install/action.yaml b/generators/symfony/templates/actions/composer-install/action.yaml new file mode 100644 index 000000000..37994ccf5 --- /dev/null +++ b/generators/symfony/templates/actions/composer-install/action.yaml @@ -0,0 +1,19 @@ +name: composer install +inputs: + working_directory: + default: '.' +runs: + using: composite + steps: + - id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + shell: bash + working-directory: ${{ inputs.working_directory }} + - uses: actions/cache@v3 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + - run: composer install --no-scripts --prefer-dist + shell: bash + working-directory: ${{ inputs.working_directory }} diff --git a/generators/symfony/templates/circleci.yaml.ejs b/generators/symfony/templates/circleci.yaml.ejs deleted file mode 100644 index 4cc5cb786..000000000 --- a/generators/symfony/templates/circleci.yaml.ejs +++ /dev/null @@ -1,351 +0,0 @@ -version: '2.1' - -orbs: - docker: circleci/docker@2.0.1 -<% if (twig) { %> - node: circleci/node@4.5.1 -<% } %> - php: circleci/php@1.1.0 - -executors: -<% if (twig) { %> - node: - docker: - - image: node:18.16.0 -<% } %> - php: - docker: - - image: php:8.2.2 - -commands: - install-composer: - steps: - - run: - name: Install composer - command: | - curl -f -L "https://getcomposer.org/download/2.2.6/composer.phar" -o /usr/local/bin/composer - echo "1d58486b891e59e9e064c0d54bb38538f74d6014f75481542c69ad84d4e97704 /usr/local/bin/composer" | sha256sum -c - - chmod 755 /usr/local/bin/composer - working_directory: /tmp/ - install-php-extensions: - parameters: - extensions: - type: string - steps: - - run: - name: Install PHP extensions - command: | - curl --fail --location --output /usr/local/bin/install-php-extensions https://raw.githubusercontent.com/mlocati/docker-php-extension-installer/1.0.4/install-php-extensions - echo "8bb61096c6cb1edc4d5039cf085c0e774f222c45d3f0546f3c58053a58253fb7 /usr/local/bin/install-php-extensions" | sha256sum -c - - chmod uga+x /usr/local/bin/install-php-extensions - - install-php-extensions << parameters.extensions >> - working_directory: /tmp/ - -jobs: - <%= packageName %>-php-dependencies: - docker: - - image: composer/composer:2.2.6 - steps: - - checkout - - php/install-packages: - app-dir: <%= packagePath %> - install-flags: --ignore-platform-reqs --no-scripts - - persist_to_workspace: - root: . - paths: - - <%= packagePath %>/vendor - - <%= packageName %>-php-lint: - executor: php - working_directory: ~/project/<%= packagePath %> - steps: - - checkout: - path: ~/project - - attach_workspace: - at: ~/project - - run: - name: PHP-CS-Fixer - command: vendor/bin/php-cs-fixer fix --dry-run --diff --using-cache no - - run: - name: PHPStan - command: vendor/bin/phpstan analyse src tests - - <%= packageName %>-migrations: - docker: - - image: php:8.2.2 - environment: - DATABASE_URL: postgresql://thetribe:424242@localhost:5432/thetribe - - image: postgres:12.5 - environment: - POSTGRES_USER: thetribe - POSTGRES_PASSWORD: 424242 - working_directory: ~/project/<%= packagePath %> - steps: - - install-php-extensions: - extensions: intl pdo_pgsql - - checkout: - path: ~/project - - docker/install-dockerize - - attach_workspace: - at: ~/project - - run: - name: Wait for db - command: dockerize -wait tcp://localhost:5432 -timeout 1m - - run: bin/console doctrine:migrations:migrate --no-interaction --allow-no-migration - - run: bin/console doctrine:schema:validate - - <% if (twig) { %> - <%= packageName %>-yarn-install: - executor: node - steps: - - checkout - - node/install-packages: - app-dir: <%= packagePath %> - include-branch-in-cache-key: false - pkg-manager: yarn - - persist_to_workspace: - root: . - paths: - - <%= packagePath %>/node_modules - <%= packageName %>-lint-js: - executor: node - working_directory: ~/project/<%= packagePath %> - steps: - - checkout: - path: ~/project - - attach_workspace: - at: ~/project - - run: yarn lint:js - - <%= packageName %>-lint-scss: - executor: node - working_directory: ~/project/<%= packagePath %> - steps: - - checkout: - path: ~/project - - attach_workspace: - at: ~/project - - run: yarn lint:scss - - <%= packageName %>-lint-twig: - executor: php - working_directory: ~/project/<%= packagePath %> - environment: - APP_ENV: prod - steps: - - install-php-extensions: - extensions: intl - - checkout: - path: ~/project - - attach_workspace: - at: ~/project - - run: - name: Lint - command: bin/console lint:twig templates - <% } %> - - <% if (deployment === 'ansible') { %> - <% if (twig) { %> - <%= packageName %>-build-assets: - executor: node - working_directory: ~/project/<%= packagePath %> - steps: - - checkout: - path: ~/project - - attach_workspace: - at: ~/project - - run: yarn build - - persist_to_workspace: - root: ~/project - paths: - - <%= packagePath %>/public/assets - - <%= packagePath %>/var/manifest.json - <% } %> - - <%= packageName %>-build: - executor: php - environment: - APP_ENV: prod - working_directory: ~/project/<%= packagePath %> - steps: - - install-php-extensions: - extensions: intl - - install-composer - - checkout: - path: ~/project - - attach_workspace: - at: ~/project - - run: composer install --ignore-platform-reqs --no-scripts --no-dev --optimize-autoloader --classmap-authoritative - - run: bin/console cache:warmup - - run: bin/console assets:install - - run: - name: Create archive - command: | - tar -czf archive.tar.gz --owner=0 --group=0 \ - bin/console \ - config/bootstrap.php \ - config/bundles.php \ - migrations/ \ - public/ \ - src/ \ - <%_ if (twig) { _%> - templates/ \ - <%_ } _%> - var/cache/prod/ \ - <%_ if (twig) { _%> - var/manifest.json \ - <%_ } _%> - vendor/ \ - - store_artifacts: - path: archive.tar.gz - destination: <%= packageName %>.tar.gz - - persist_to_workspace: - root: ~/project - paths: - - <%= packagePath %>/archive.tar.gz - <% } %> - - <% if (deployment === 'kubernetes') { %> - <%= packageName %>-build: - executor: docker/docker - steps: - - setup_remote_docker: - version: 20.10.11 - - checkout - - docker/build: - image: <%= projectName %>-<%= packageName %>-php - path: <%= packagePath %> - docker-context: <%= packagePath %> - extra_build_args: '--target=php --build-arg VERSION=$(git log --max-count 1 --pretty=format:%H "<%= packagePath %>")' - - docker/build: - image: <%= projectName %>-<%= packageName %>-nginx - path: <%= packagePath %> - docker-context: <%= packagePath %> - extra_build_args: '--target=nginx --build-arg VERSION=$(git log --max-count 1 --pretty=format:%H "<%= packagePath %>")' - <%= packageName %>-build-and-push: - executor: docker/docker - steps: - - setup_remote_docker: - version: 20.10.11 - - checkout - - docker/build: - registry: $DOCKER_REGISTRY - image: <%= projectName %>-<%= packageName %>-php - tag: << pipeline.git.branch >> - path: <%= packagePath %> - docker-context: <%= packagePath %> - extra_build_args: '--target=php --build-arg VERSION=$(git log --max-count 1 --pretty=format:%H "<%= packagePath %>")' - - docker/build: - registry: $DOCKER_REGISTRY - image: <%= projectName %>-<%= packageName %>-nginx - tag: << pipeline.git.branch >> - path: <%= packagePath %> - docker-context: <%= packagePath %> - extra_build_args: '--target=nginx --build-arg VERSION=$(git log --max-count 1 --pretty=format:%H "<%= packagePath %>")' - <% if (twig) { %> - - docker/build: - registry: $DOCKER_REGISTRY - image: <%= projectName %>-<%= packageName %>-sentry - tag: << pipeline.git.branch >> - path: <%= packagePath %> - docker-context: <%= packagePath %> - extra_build_args: '--target=sentry --build-arg VERSION=$(git log --max-count 1 --pretty=format:%H "<%= packagePath %>")' - <% } %> - - docker/check: - registry: $DOCKER_REGISTRY - - docker/push: - registry: $DOCKER_REGISTRY - image: <%= projectName %>-<%= packageName %>-php - tag: << pipeline.git.branch >> - - docker/push: - registry: $DOCKER_REGISTRY - image: <%= projectName %>-<%= packageName %>-nginx - tag: << pipeline.git.branch >> - <% if (twig) { %> - - run: - name: Publish source maps to sentry - command: | - IMAGE="$DOCKER_REGISTRY/<%= projectName %>-<%= packageName %>-sentry:<< pipeline.git.branch >>" - ENV="--env SENTRY_AUTH_TOKEN --env SENTRY_ORG --env SENTRY_PROJECT=<%= projectName %>-<%= packageName %>" - VERSION=<%= projectName %>-<%= packageName %>@$(git log --max-count 1 --pretty=format:%H "<%= packagePath %>") - - docker run --rm $ENV $IMAGE releases new $VERSION - docker run --rm $ENV $IMAGE releases files $VERSION upload-sourcemaps ./assets - docker run --rm $ENV $IMAGE releases finalize $VERSION - <% } %> - <% } %> - -workflows: - version: '2' - build: - jobs: - - <%= packageName %>-php-dependencies - - <%= packageName %>-php-lint: - requires: - - <%= packageName %>-php-dependencies - - <%= packageName %>-migrations: - requires: - - <%= packageName %>-php-dependencies - <% if (twig) { %> - - <%= packageName %>-lint-js: - requires: - - <%= packageName %>-yarn-install - - <%= packageName %>-lint-scss: - requires: - - <%= packageName %>-yarn-install - - <%= packageName %>-lint-twig: - requires: - - <%= packageName %>-php-dependencies - - <%= packageName %>-yarn-install - <% } %> - - <% if (deployment === 'ansible') { %> - <% if (twig) { %> - - <%= packageName %>-build-assets: - requires: - - <%= packageName %>-yarn-install - <% } %> - - <%= packageName %>-build: - requires: - - <%= packageName %>-php-dependencies - <% if (twig) { %> - - <%= packageName %>-build-assets - <% } %> - <% } %> - - <% if (deployment === 'kubernetes') { %> - - <%= packageName %>-build-and-push: - context: - - docker-registry - <% if (twig) { %> - - sentry.io - <% } %> - requires: - - <%= packageName %>-migrations - - <%= packageName %>-php-lint - <% if (twig) { %> - - <%= packageName %>-lint-js - - <%= packageName %>-lint-scss - - <%= packageName %>-lint-twig - <% } %> - filters: - branches: - only: - - develop - - main - - <%= packageName %>-build: - requires: - - <%= packageName %>-migrations - - <%= packageName %>-php-lint - <% if (twig) { %> - - <%= packageName %>-lint-js - - <%= packageName %>-lint-scss - - <%= packageName %>-lint-twig - <% } %> - filters: - branches: - ignore: - - develop - - main - <% } %> diff --git a/generators/symfony/templates/deployment/ansible/package/deployment.yaml.ejs b/generators/symfony/templates/deployment/ansible/package/deployment.yaml.ejs index 1bf51c3ac..5cb96ad07 100644 --- a/generators/symfony/templates/deployment/ansible/package/deployment.yaml.ejs +++ b/generators/symfony/templates/deployment/ansible/package/deployment.yaml.ejs @@ -1,17 +1,15 @@ --- - import_role: - name: build + name: find_github_artifact vars: - job_name: <%= packageName %>-build repository_name: <%= repositoryName %> + workflow_name: <%= packageName %> delegate_to: localhost + when: artifact_url is not defined - import_role: - name: deploy + name: deploy_artifact vars: path: /home/<%= packageName %> - artifact_path: <%= packageName %>.tar.gz before_finalize: - ./bin/console doctrine:migrations:migrate --no-interaction --allow-no-migration - job_name: <%= packageName %>-build - repository_name: <%= repositoryName %> environment: '{{ <%= varName(packageName) %>_env }}' diff --git a/generators/symfony/templates/deployment/ansible/package/deployment_ci.yaml.ejs b/generators/symfony/templates/deployment/ansible/package/deployment_ci.yaml.ejs deleted file mode 100644 index 8de0bc393..000000000 --- a/generators/symfony/templates/deployment/ansible/package/deployment_ci.yaml.ejs +++ /dev/null @@ -1,9 +0,0 @@ ---- -- import_role: - name: deploy - environment: '{{ <%= varName(packageName) %>_env }}' - vars: - path: /home/<%= packageName %> - artifact_file: '{{ playbook_dir }}/../<%= packagePath %>/archive.tar.gz' - before_finalize: - - ./bin/console doctrine:migrations:migrate --no-interaction --allow-no-migration diff --git a/generators/symfony/templates/workflow.yaml.ejs b/generators/symfony/templates/workflow.yaml.ejs new file mode 100644 index 000000000..9d2e67077 --- /dev/null +++ b/generators/symfony/templates/workflow.yaml.ejs @@ -0,0 +1,243 @@ +name: <%= packageName %> + +on: + pull_request: + paths: <%= packagePath %>/** + push: + branches: + - main + - develop + +defaults: + run: + working-directory: <%= packagePath %> + +jobs: +<%_ if (twig) { _%> + lint-twig: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - uses: shivammathur/setup-php@v2 + with: + php-version: '8.2' + - uses: ./.github/actions/composer-install + with: + working_directory: <%= packagePath %> + - run: bin/console lint:twig + + code-style-js: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18.16.0 + cache: yarn + cache-dependency-path: <%= packagePath %>/yarn.lock + - run: yarn install --frozen-lockfile + - run: yarn lint:js --quiet + +<%_ } _%> + code-style-php: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - uses: shivammathur/setup-php@v2 + with: + php-version: '8.2' + - uses: ./.github/actions/composer-install + with: + working_directory: <%= packagePath %> + - run: vendor/bin/php-cs-fixer fix --dry-run --diff --using-cache no + +<%_ if (twig) { _%> + code-style-scss: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18.16.0 + cache: yarn + cache-dependency-path: <%= packagePath %>/yarn.lock + - run: yarn install --frozen-lockfile + - run: yarn lint:scss + +<%_ } _%> + static-analysis: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - uses: shivammathur/setup-php@v2 + with: + php-version: '8.2' + - uses: ./.github/actions/composer-install + with: + working_directory: <%= packagePath %> + - run: vendor/bin/phpstan analyse src tests + - run: bin/console lint:container + + test-migrations: + runs-on: ubuntu-22.04 + env: + DATABASE_URL: postgresql://thetribe:424242@localhost:5432/thetribe + services: + postgres: + image: postgres:12.5 + env: + POSTGRES_USER: thetribe + POSTGRES_PASSWORD: 424242 + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + steps: + - uses: actions/checkout@v3 + - uses: shivammathur/setup-php@v2 + with: + php-version: '8.2' + extensions: intl,pdo_pgsql + - uses: ./.github/actions/composer-install + with: + working_directory: <%= packagePath %> + - run: bin/console doctrine:migrations:migrate --no-interaction --allow-no-migration + - run: bin/console doctrine:schema:validate + +<%_ if (deployment === 'ansible') { _%> + build: + runs-on: ubuntu-22.04 + env: + APP_ENV: prod + steps: + - uses: actions/checkout@v3 + - uses: shivammathur/setup-php@v2 + with: + php-version: '8.2' + extensions: intl +<%_ if (twig) { _%> + - uses: actions/setup-node@v3 + with: + node-version: 18.16.0 + cache: yarn + cache-dependency-path: <%= packagePath %>/yarn.lock +<%_ } _%> + - uses: ./.github/actions/composer-install + with: + working_directory: <%= packagePath %> + - run: composer install --no-dev --no-scripts --prefer-dist +<%_ if (twig) { _%> + - run: yarn install --frozen-lockfile +<%_ } _%> + - run: bin/console cache:warmup + - run: bin/console assets:install +<%_ if (twig) { _%> + - run: yarn build +<%_ } _%> + - run: | + tar --create \ + --file=archive.tar \ + --owner=0 \ + --group=0 \ + --exclude '*.map' \ + bin/console \ + config/bootstrap.php \ + config/bundles.php \ + migrations/ \ + public/ \ + src/ \ + <%_ if (twig) { _%> + templates/ \ + <%_ } _%> + var/cache/prod/ \ + <%_ if (twig) { _%> + var/manifest.json \ + <%_ } _%> + vendor/ \ + - uses: actions/upload-artifact@v3 + with: + name: <%= packageName %> + path: <%= packagePath %>/archive.tar + if-no-files-found: error + if: github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/main' +<%_ if (twig) { _%> + - uses: getsentry/action-release@v1 + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + SENTRY_PROJECT: <%= projectName %>-<%= packageName %> + with: + ignore_missing: true + sourcemaps: public/assets + version_prefix: <%= projectName %>-<%= packageName %>@ + working_directory: <%= packagePath %> + if: github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/main' +<%_ } _%> + deploy-staging: + if: github.ref == 'refs/heads/develop' + needs: [build] + uses: ./.github/workflows/deploy.yaml + with: + package: <%= packageName %> + secrets: inherit +<%_ } _%> +<%_ if (deployment === 'kubernetes') { _%> + build: + runs-on: ubuntu-22.04 + steps: + - uses: docker/setup-buildx-action@v2.5.0 + - uses: docker/login-action@v2.1.0 + with: + registry: ${{ secrets.STAGING_REGISTRY }} + username: ${{ secrets.STAGING_REGISTRY_LOGIN }} + password: ${{ secrets.STAGING_REGISTRY_PASSWORD }} + if: github.ref == 'refs/heads/develop' + - uses: docker/build-push-action@v4.0.0 + with: + context: '{{ defaultContext }}:<%= packagePath %>' + cache-from: type=gha + cache-to: type=gha,mode=max + tags: ${{ secrets.STAGING_REGISTRY }}/<%= projectName %>/<%= packageName %>/nginx + target: nginx + push: ${{ github.ref == 'refs/heads/develop' }} + build-args: | + VERSION=${{ github.sha }} + - uses: docker/build-push-action@v4.0.0 + with: + context: '{{ defaultContext }}:<%= packagePath %>' + cache-from: type=gha + cache-to: type=gha,mode=max + tags: ${{ secrets.STAGING_REGISTRY }}/<%= projectName %>/<%= packageName %>/php + target: php + push: ${{ github.ref == 'refs/heads/develop' }} + build-args: | + VERSION=${{ github.sha }} +<%_ if (twig) { _%> + - uses: docker/build-push-action@v4.0.0 + with: + context: '{{ defaultContext }}:<%= packagePath %>' + cache-from: type=gha + cache-to: type=gha,mode=max + tags: ${{ secrets.STAGING_REGISTRY }}/<%= projectName %>/<%= packageName %>/sentry + target: sentry + load: true + build-args: | + VERSION=${{ github.sha }} + - run: | + ENV="--env SENTRY_AUTH_TOKEN --env SENTRY_ORG --env SENTRY_PROJECT" + docker run --rm $ENV $IMAGE releases new $VERSION + docker run --rm $ENV $IMAGE releases files $VERSION upload-sourcemaps assets + docker run --rm $ENV $IMAGE releases finalize $VERSION + working-directory: . + env: + IMAGE: ${{ secrets.STAGING_REGISTRY }}/<%= projectName %>/<%= packageName %>/sentry + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_PROJECT: <%= projectName %>-<%= packageName %> + SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + VERSION: <%= projectName %>-<%= packageName %>@${{ github.sha }} + if: github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/main' +<%_ } _%> +<%_ } _%> diff --git a/scripts/updateNode.ts b/scripts/updateNode.ts index a84644297..0967ee408 100755 --- a/scripts/updateNode.ts +++ b/scripts/updateNode.ts @@ -56,15 +56,15 @@ let updated = false; // Update CircleCI config for (const file of [ - 'generators/express/templates/circleci.yaml.ejs', - 'generators/next-js/templates/circleci.yaml.ejs', - 'generators/react/templates/circleci.yaml.ejs', - 'generators/symfony/templates/circleci.yaml.ejs', + 'generators/express/templates/workflow.yaml.ejs', + 'generators/next-js/templates/workflow.yaml.ejs', + 'generators/react/templates/workflow.yaml.ejs', + 'generators/symfony/templates/workflow.yaml.ejs', ]) { updated = await replace( file, - /image: node:\d+\.\d+\.\d+/, - `image: node:${lastVersion}`, + /node-version: \d+\.\d+\.\d+/, + `node-version: ${lastVersion}`, ) || updated; } diff --git a/utils/PackageGenerator.ts b/utils/PackageGenerator.ts index d853704a4..96d24e2bc 100644 --- a/utils/PackageGenerator.ts +++ b/utils/PackageGenerator.ts @@ -3,7 +3,6 @@ import { Data as TemplateData } from 'ejs'; import YAML from 'yaml'; import { GeneratorOptions } from 'yeoman-generator'; import BaseGenerator from './BaseGenerator'; -import * as CircleCI from './circleci'; import * as Codemagic from './codemagic'; import validateProjectPath from './validation/validatePackagePath'; @@ -76,27 +75,6 @@ class PackageGenerator CircleCI.mergeConfig( - CircleCI.Config.fromRaw(oldConfig), - CircleCI.Config.fromRaw(config), - ).toRaw(), - { indent: 2 }, - ); - } - - updateCircleCIConfig(updater: (config: CircleCI.Config) => void): void { - const config = CircleCI.Config.fromRaw(YAML.parse(this.readDestination('.circleci/config.yml'))); - - updater(config); - - this.writeDestination('.circleci/config.yml', YAML.stringify(config.toRaw())); - } - configureAnsible(templatePath: string, context: TemplateData = {}): void { const { packageName } = this.options; @@ -106,7 +84,7 @@ class PackageGenerator, - }; - commands?: { - [name: string]: any, - } - jobs: { - [name: string]: Record, - }; - workflowsVersion: '2'; - workflows: { - [name: string]: Workflow, - }; -} - -class Config { - version: string; - - orbs: { - [name: string]: string, - }; - - executors: { - [name: string]: Record, - }; - - commands: { - [name: string]: any, - }; - - jobs: { - [name: string]: Record, - }; - - workflowsVersion: '2'; - - workflows: { - [name: string]: Workflow, - }; - - constructor({ - version, - orbs = {}, - executors = {}, - commands = {}, - jobs, - workflowsVersion, - workflows, - }: ConfigConstructor) { - this.version = version; - this.orbs = orbs; - this.executors = executors; - this.commands = commands; - this.jobs = jobs; - this.workflowsVersion = workflowsVersion; - this.workflows = workflows; - } - - static fromRaw({ - version, - orbs, - executors, - commands, - jobs, - workflows: { version: workflowsVersion, ...workflows }, - }: any): Config { - if ('2' !== workflowsVersion) { - throw new Error(`Invalid workflows version: ${workflowsVersion}`); - } - - // TODO: add more validation - - return new Config({ - version, - orbs, - executors, - commands, - jobs, - workflowsVersion, - workflows: map, Record>(Workflow.fromRaw, workflows), - }); - } - - toRaw(): any { - const raw: any = { - version: this.version, - }; - - if (Object.keys(this.orbs).length > 0) { - raw.orbs = this.orbs; - } - - if (Object.keys(this.executors).length > 0) { - raw.executors = this.executors; - } - - if (Object.keys(this.commands).length > 0) { - raw.commands = this.commands; - } - - raw.jobs = this.jobs; - raw.workflows = { - version: this.workflowsVersion, - ...map, Record>( - (workflow: Workflow) => workflow.toRaw(), - this.workflows, - ), - }; - - return raw; - } -} - -export default Config; diff --git a/utils/circleci/Workflow.ts b/utils/circleci/Workflow.ts deleted file mode 100644 index 5acacba46..000000000 --- a/utils/circleci/Workflow.ts +++ /dev/null @@ -1,41 +0,0 @@ -interface WorkflowConstructor { - jobs: { - [name: string]: Record, - } -} - -class Workflow { - jobs: { - [name: string]: Record, - }; - - constructor({ jobs }: WorkflowConstructor) { - this.jobs = jobs; - } - - static fromRaw(raw: any): Workflow { - const { jobs } = raw; - - // TODO: add validation - - return new Workflow({ - jobs: Object.fromEntries(jobs.map((rawJob: any) => { - if ('string' === typeof rawJob) { - return [rawJob, {}]; - } - - return Object.entries(rawJob)[0]; - })), - }); - } - - toRaw(): any { - return { - jobs: Object.entries(this.jobs).map( - ([name, config]) => (Object.keys(config).length === 0 ? name : { [name]: config }), - ), - }; - } -} - -export default Workflow; diff --git a/utils/circleci/__tests__/Config.ts b/utils/circleci/__tests__/Config.ts deleted file mode 100644 index d95b765af..000000000 --- a/utils/circleci/__tests__/Config.ts +++ /dev/null @@ -1,77 +0,0 @@ -import Config from '../Config'; -import Workflow from '../Workflow'; - -test('fromRaw parse the workflows', () => { - const config = Config.fromRaw({ - workflows: { - version: '2', - build: { - jobs: [ - 'install', - { lint: { requires: ['install'] } }, - ], - }, - }, - }); - - expect(config.workflowsVersion).toEqual('2'); - expect(config.workflows.build).toEqual(new Workflow({ - jobs: { - install: {}, - lint: { requires: ['install'] }, - }, - })); -}); - -test('fromRaw parse the executors', () => { - const config = Config.fromRaw({ - executors: { - node: {}, - }, - workflows: { - version: '2', - }, - }); - - expect(config.executors.node).toBeDefined(); -}); - -test('fromRaw errors on invalid workflow version', () => { - expect(() => { - Config.fromRaw({ - workflows: { - version: '1', - }, - }); - }).toThrow('Invalid workflows version: 1'); -}); - -test('toRaw returns the formated config', () => { - const config = new Config({ - version: '2.1', - jobs: {}, - workflowsVersion: '2', - workflows: { - build: new Workflow({ - jobs: { - install: {}, - lint: { requires: ['install'] }, - }, - }), - }, - }); - - expect(config.toRaw()).toEqual({ - version: '2.1', - jobs: {}, - workflows: { - version: '2', - build: { - jobs: [ - 'install', - { lint: { requires: ['install'] } }, - ], - }, - }, - }); -}); diff --git a/utils/circleci/__tests__/mergeConfig.ts b/utils/circleci/__tests__/mergeConfig.ts deleted file mode 100644 index 72444b898..000000000 --- a/utils/circleci/__tests__/mergeConfig.ts +++ /dev/null @@ -1,175 +0,0 @@ -import Config from '../Config'; -import mergeConfig from '../mergeConfig'; -import Workflow from '../Workflow'; - -test('It keeps workflow from first config if second is not present', () => { - const firstConfig = new Config({ - version: '2', - executors: {}, - jobs: {}, - workflowsVersion: '2', - workflows: { - build: new Workflow({ - jobs: { - firstJob: {}, - }, - }), - }, - }); - - const secondConfig = new Config({ - version: '2', - executors: {}, - jobs: {}, - workflowsVersion: '2', - workflows: {}, - }); - - const mergedConfig = mergeConfig(firstConfig, secondConfig); - - expect(mergedConfig.workflows.build).toEqual(firstConfig.workflows.build); -}); - -test('It keeps workflow from second config if first is not present', () => { - const firstConfig = new Config({ - version: '2', - executors: {}, - jobs: {}, - workflowsVersion: '2', - workflows: {}, - }); - - const secondConfig = new Config({ - version: '2', - executors: {}, - jobs: {}, - workflowsVersion: '2', - workflows: { - build: new Workflow({ - jobs: { - secondJob: {}, - }, - }), - }, - }); - - const mergedConfig = mergeConfig(firstConfig, secondConfig); - - expect(mergedConfig.workflows.build).toEqual(secondConfig.workflows.build); -}); - -test('It merges jobs of the same workflow from different configs', () => { - const firstConfig = new Config({ - version: '2', - executors: {}, - jobs: {}, - workflowsVersion: '2', - workflows: { - build: new Workflow({ - jobs: { - firstJob: {}, - }, - }), - }, - }); - - const secondConfig = new Config({ - version: '2', - executors: {}, - jobs: {}, - workflowsVersion: '2', - workflows: { - build: new Workflow({ - jobs: { - secondJob: {}, - }, - }), - }, - }); - - const mergedConfig = mergeConfig(firstConfig, secondConfig); - - expect(mergedConfig.workflows.build?.jobs.firstJob).toBeDefined(); - expect(mergedConfig.workflows.build?.jobs.secondJob).toBeDefined(); -}); - -test('It merges executors from different configs', () => { - const firstConfig = new Config({ - version: '2', - executors: { - first: {}, - }, - jobs: {}, - workflowsVersion: '2', - workflows: {}, - }); - - const secondConfig = new Config({ - version: '2', - executors: { - second: {}, - }, - jobs: {}, - workflowsVersion: '2', - workflows: {}, - }); - - const mergedConfig = mergeConfig(firstConfig, secondConfig); - - expect(mergedConfig.executors.first).toBeDefined(); - expect(mergedConfig.executors.second).toBeDefined(); -}); - -test('It merges commands from different configs', () => { - const firstConfig = new Config({ - version: '2', - commands: { - first: {}, - }, - jobs: {}, - workflowsVersion: '2', - workflows: {}, - }); - - const secondConfig = new Config({ - version: '2', - commands: { - second: {}, - }, - jobs: {}, - workflowsVersion: '2', - workflows: {}, - }); - - const mergedConfig = mergeConfig(firstConfig, secondConfig); - - expect(mergedConfig.commands.first).toBeDefined(); - expect(mergedConfig.commands.second).toBeDefined(); -}); - -test('It merges orbs from different configs', () => { - const firstConfig = new Config({ - version: '2', - orbs: { - first: 'first', - }, - jobs: {}, - workflowsVersion: '2', - workflows: {}, - }); - - const secondConfig = new Config({ - version: '2', - orbs: { - second: 'second', - }, - jobs: {}, - workflowsVersion: '2', - workflows: {}, - }); - - const mergedConfig = mergeConfig(firstConfig, secondConfig); - - expect(mergedConfig.orbs.first).toBe('first'); - expect(mergedConfig.orbs.second).toBe('second'); -}); diff --git a/utils/circleci/index.ts b/utils/circleci/index.ts deleted file mode 100644 index f4388a5ec..000000000 --- a/utils/circleci/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default as Config } from './Config'; -export { default as mergeConfig } from './mergeConfig'; diff --git a/utils/circleci/mergeConfig.ts b/utils/circleci/mergeConfig.ts deleted file mode 100644 index 7018d4104..000000000 --- a/utils/circleci/mergeConfig.ts +++ /dev/null @@ -1,48 +0,0 @@ -import Config from './Config'; -import Workflow from './Workflow'; - -/** - * Returns keys that are present in either objects - */ -const allKeys = (first: object, second: object): string[] => Array.from(new Set([ - ...Object.keys(first), - ...Object.keys(second), -])); - -/** - * Merge two configs together - */ -const mergeConfig = (first: Config, second: Config): Config => new Config({ - version: first.version, - orbs: { - ...first.orbs, - ...second.orbs, - }, - executors: { - ...first.executors, - ...second.executors, - }, - commands: { - ...first.commands, - ...second.commands, - }, - jobs: { - ...first.jobs, - ...second.jobs, - }, - workflowsVersion: first.workflowsVersion, - workflows: Object.fromEntries(allKeys(first.workflows, second.workflows) - .map((workflowName: string): [string, Workflow] => { - const firstWorkflow = first.workflows[workflowName]; - const secondWorkflow = second.workflows[workflowName]; - - return [workflowName, new Workflow({ - jobs: { - ...firstWorkflow ? firstWorkflow.jobs : {}, - ...secondWorkflow ? secondWorkflow.jobs : {}, - }, - })]; - })), -}); - -export default mergeConfig;