From f01cb2911a430fa34279836a7733775fc092e5ac Mon Sep 17 00:00:00 2001 From: Etienne St-Pierre Date: Tue, 19 Dec 2023 13:26:05 -0500 Subject: [PATCH] Extract the Accent file version from the current branch name --- cli/README.md | 65 +++++++++++++++++++++-------- cli/examples/simple/accent.json | 7 +++- cli/src/commands/stats.ts | 18 ++++---- cli/src/commands/sync.ts | 30 +++++++------ cli/src/services/config.ts | 25 +++++++++-- cli/src/services/project-fetcher.ts | 22 +++++----- cli/src/types/config.ts | 6 ++- cli/src/types/version-config.ts | 4 ++ docker-compose.yml | 1 + 9 files changed, 121 insertions(+), 57 deletions(-) create mode 100644 cli/src/types/version-config.ts diff --git a/cli/README.md b/cli/README.md index da6cd8d2f..1c177b486 100644 --- a/cli/README.md +++ b/cli/README.md @@ -1,20 +1,22 @@ -Accent CLI -====== +# Accent CLI [![Version](https://img.shields.io/npm/v/accent-cli.svg)](https://npmjs.org/package/accent-cli) [![Build Status](https://img.shields.io/travis/v/accent-cli.svg?branch=master)](https://travis-ci.com/mirego/accent-cli) -* [Usage](#usage) -* [Configuration](#configuration) -* [Commands](#commands) -* [GitHub Actions](#github-actions) -* [License](#license) -* [About Mirego](#about-mirego) + +- [Usage](#usage) +- [Configuration](#configuration) +- [Commands](#commands) +- [GitHub Actions](#github-actions) +- [License](#license) +- [About Mirego](#about-mirego) # Usage + + ```sh-session $ npm install -g accent-cli $ accent COMMAND @@ -26,6 +28,7 @@ USAGE $ accent COMMAND ... ``` + # Configuration @@ -74,6 +77,29 @@ Each operation section `sync` and `addTranslations` can contain the following ob - `namePattern`: Pattern to use to save the document name in Accent. - `hooks`: List of hooks to be run +## Version configuration + +Format for the `accent.json` file. + +- `apiUrl`: The base URL of your Accent Instance +- `apiKey`: Api Key to your Accent Instance +- `project`: Your Project uuid + +Available ENV variables. (Each variable will override `accent.json` variables if set) + +- `ACCENT_API_KEY`: The base URL of your Accent Instance +- `ACCENT_API_URL`: Api Key to your Accent Instance +- `ACCENT_PROJECT`: Your Project uuid + +Each operation section `sync` and `addTranslations` can contain the following object: + +- `language`: The identifier of the document’s language +- `format`: The format of the document +- `source`: The path of the document. This can contain glob pattern (See [the node glob library] used as a dependancy (https://github.com/isaacs/node-glob)) +- `target`: Path of the target languages +- `namePattern`: Pattern to use to save the document name in Accent. +- `hooks`: List of hooks to be run + ## Name pattern `file` (default): Use the name of the file without the extension. In the example, the document name in Accent will be `Localizable`. @@ -131,14 +157,16 @@ Here is a list of available hooks. Those are self-explanatory - `afterExport` # Commands + -* [`accent export`](#accent-export) -* [`accent format`](#accent-format) -* [`accent help [COMMAND]`](#accent-help-command) -* [`accent jipt PSEUDOLANGUAGENAME`](#accent-jipt-pseudolanguagename) -* [`accent lint`](#accent-lint) -* [`accent stats`](#accent-stats) -* [`accent sync`](#accent-sync) + +- [`accent export`](#accent-export) +- [`accent format`](#accent-format) +- [`accent help [COMMAND]`](#accent-help-command) +- [`accent jipt PSEUDOLANGUAGENAME`](#accent-jipt-pseudolanguagename) +- [`accent lint`](#accent-lint) +- [`accent stats`](#accent-stats) +- [`accent sync`](#accent-sync) ## `accent export` @@ -280,6 +308,7 @@ EXAMPLES ``` _See code: [src/commands/sync.ts](https://github.com/mirego/accent/blob/v0.14.2/src/commands/sync.ts)_ + # GitHub Actions @@ -293,7 +322,7 @@ name: Accent on: schedule: - - cron: "0 4 * * *" + - cron: '0 4 * * *' jobs: build: @@ -307,7 +336,7 @@ jobs: - run: accent sync --add-translations --merge-type=passive --order-by=key - uses: mirego/create-pull-request@v5 with: - add-paths: "*.json" + add-paths: '*.json' commit-message: Update translations committer: github-actions[bot] author: github-actions[bot] @@ -322,7 +351,7 @@ In this example the translations will be synchronized daily at midnight eastern # License -`accent-cli` is © 2019 [Mirego](http://www.mirego.com) and may be freely distributed under the [New BSD license](http://opensource.org/licenses/BSD-3-Clause). See the [`LICENSE.md`](https://github.com/mirego/accent-cli/blob/master/LICENSE.md) file. +`accent-cli` is © 2019 [Mirego](http://www.mirego.com) and may be freely distributed under the [New BSD license](http://opensource.org/licenses/BSD-3-Clause). See the [`LICENSE.md`](https://github.com/mirego/accent-cli/blob/master/LICENSE.md) file. # About Mirego diff --git a/cli/examples/simple/accent.json b/cli/examples/simple/accent.json index 521492d56..ffe7bae74 100644 --- a/cli/examples/simple/accent.json +++ b/cli/examples/simple/accent.json @@ -5,5 +5,8 @@ "source": "localization.json", "target": "%document_path%.json" } - ] -} + ], + "version": { + "branchVersionPrefix": "release/" + } +} \ No newline at end of file diff --git a/cli/src/commands/stats.ts b/cli/src/commands/stats.ts index 75ea97e69..05ec8d5ea 100644 --- a/cli/src/commands/stats.ts +++ b/cli/src/commands/stats.ts @@ -2,14 +2,14 @@ import * as chalk from 'chalk'; // Command -import {flags} from '@oclif/command'; -import Command, {configFlag} from '../base'; -import {CLIError} from '@oclif/errors'; +import { flags } from '@oclif/command'; +import Command, { configFlag } from '../base'; +import { CLIError } from '@oclif/errors'; // Services import Formatter from '../services/formatters/project-stats'; import ProjectFetcher from '../services/project-fetcher'; -import {Revision} from '../types/project'; +import { Revision } from '../types/project'; export default class Stats extends Command { static description = 'Fetch stats from the API and display them beautifully'; @@ -27,12 +27,16 @@ export default class Stats extends Command { }; async run() { - const {flags} = this.parse(Stats); + const { flags } = this.parse(Stats); + + if (this.projectConfig.config.version?.identifier && !flags.version) { + flags.version = this.projectConfig.config.version.identifier + } if (flags.version) { const config = this.projectConfig.config; const fetcher = new ProjectFetcher(); - const response = await fetcher.fetch(config, {versionId: flags.version}); + const response = await fetcher.fetch(config, { versionId: flags.version }); this.project = response.project; } @@ -57,7 +61,7 @@ export default class Stats extends Command { chalk.red( `Project${versionFormat} has ${conflictsCount} strings to be reviewed` ), - {exit: 1} + { exit: 1 } ); } } diff --git a/cli/src/commands/sync.ts b/cli/src/commands/sync.ts index 0cef76c79..db051b1b5 100644 --- a/cli/src/commands/sync.ts +++ b/cli/src/commands/sync.ts @@ -1,9 +1,9 @@ // Vendor -import {flags} from '@oclif/command'; -import {existsSync} from 'fs'; +import { flags } from '@oclif/command'; +import { existsSync } from 'fs'; // Command -import Command, {configFlag} from '../base'; +import Command, { configFlag } from '../base'; // Formatters import AddTranslationsFormatter from '../services/formatters/project-add-translations'; @@ -17,10 +17,10 @@ import CommitOperationFormatter from '../services/formatters/commit-operation'; import DocumentExportFormatter from '../services/formatters/document-export'; import HookRunner from '../services/hook-runner'; -import {fetchFromRevision} from '../services/revision-slug-fetcher'; +import { fetchFromRevision } from '../services/revision-slug-fetcher'; // Types -import {Hooks} from '../types/document-config'; +import { Hooks } from '../types/document-config'; export default class Sync extends Command { static description = @@ -70,10 +70,14 @@ export default class Sync extends Command { }; async run() { - const {flags} = this.parse(Sync); + const { flags } = this.parse(Sync); const t0 = process.hrtime.bigint(); const documents = this.projectConfig.files(); + if (this.projectConfig.config.version?.identifier && !flags.version) { + flags.version = this.config.version + } + // From all the documentConfigs, do the sync or peek operations and log the results. const syncFormatter = new SyncFormatter(); syncFormatter.log(this.project!, flags); @@ -117,7 +121,7 @@ export default class Sync extends Command { const targets = new DocumentPathsFetcher().fetch(this.project!, document); for (const target of targets) { - const {path, language, documentPath} = target; + const { path, language, documentPath } = target; const localFile = document.fetchLocalFile(documentPath, path); formatter.log(path, documentPath, language); @@ -132,7 +136,7 @@ export default class Sync extends Command { } private async syncDocumentConfig(document: Document) { - const {flags} = this.parse(Sync); + const { flags } = this.parse(Sync); const formatter = new CommitOperationFormatter(); for (const path of document.paths) { @@ -148,25 +152,25 @@ export default class Sync extends Command { } private async addTranslationsDocumentConfig(document: Document) { - const {flags} = this.parse(Sync); + const { flags } = this.parse(Sync); const formatter = new CommitOperationFormatter(); const masterLanguage = fetchFromRevision(this.project!.masterRevision); const targets = new DocumentPathsFetcher() .fetch(this.project!, document) - .filter(({language}) => language !== masterLanguage); + .filter(({ language }) => language !== masterLanguage); - const existingTargets = targets.filter(({path}) => existsSync(path)); + const existingTargets = targets.filter(({ path }) => existsSync(path)); if (existingTargets.length === 0) { - targets.forEach(({path}) => formatter.logEmptyExistingTarget(path)); + targets.forEach(({ path }) => formatter.logEmptyExistingTarget(path)); } if (targets.length === 0) { formatter.logEmptyTarget(document.config.source); } for (const target of existingTargets) { - const {path, language} = target; + const { path, language } = target; const documentPath = document.parseDocumentName(path, document.config); const operation = await document.addTranslations( path, diff --git a/cli/src/services/config.ts b/cli/src/services/config.ts index 40d434980..95f836cdf 100644 --- a/cli/src/services/config.ts +++ b/cli/src/services/config.ts @@ -1,14 +1,14 @@ // Vendor -import {error} from '@oclif/errors'; +import { error } from '@oclif/errors'; +import { execSync } from 'child_process'; import * as fs from 'fs-extra'; import * as path from 'path'; import * as chalk from 'chalk'; - // Services import Document from './document'; // Types -import {Config} from '../types/config'; +import { Config } from '../types/config'; export default class ConfigFetcher { readonly config: Config; @@ -19,6 +19,7 @@ export default class ConfigFetcher { this.config.apiKey = process.env.ACCENT_API_KEY || this.config.apiKey; this.config.apiUrl = process.env.ACCENT_API_URL || this.config.apiUrl; this.config.project = process.env.ACCENT_PROJECT || this.config.project; + this.warnings = []; if (!this.config.apiKey) { @@ -37,13 +38,19 @@ export default class ConfigFetcher { error('You must have at least 1 document set in your config'); } + const branchName = this.getCurrentBranchName(); + + if (this.config.version?.branchVersionPrefix && branchName.startsWith(this.config.version?.branchVersionPrefix)) { + this.config.version.identifier = this.exctractVersionFromBranch(branchName, this.config.version?.branchVersionPrefix) + } + const sameFolderPathWarning: Set = new Set(); this.config.files.forEach((documentConfig) => { const folderPath = this.sourceFolderPath(documentConfig.source); const sameFolderPath = this.config.files - .filter(({source}) => source !== documentConfig.source) + .filter(({ source }) => source !== documentConfig.source) .some( (otherDocumentConfig) => this.sourceFolderPath(otherDocumentConfig.source) === folderPath @@ -71,4 +78,14 @@ Only your master language should be listed in your files config.` private sourceFolderPath(source: string) { return source.replace(path.basename(source), ''); } + + private getCurrentBranchName() { + return execSync('git rev-parse --abbrev-ref HEAD') + .toString('utf8') + .replace(/[\n\r\s]+$/, ''); + } + + private exctractVersionFromBranch(branchName: string, gitBranchVersionMatch: string): string { + return branchName.replace(gitBranchVersionMatch, ''); + } } diff --git a/cli/src/services/project-fetcher.ts b/cli/src/services/project-fetcher.ts index e4da0bd7a..02a5acaa6 100644 --- a/cli/src/services/project-fetcher.ts +++ b/cli/src/services/project-fetcher.ts @@ -1,22 +1,22 @@ // Vendor -import {CLIError} from '@oclif/errors'; +import { CLIError } from '@oclif/errors'; import * as chalk from 'chalk'; import fetch from 'node-fetch'; // Types -import {Config} from '../types/config'; -import {ProjectViewer} from '../types/project'; +import { Config } from '../types/config'; +import { ProjectViewer } from '../types/project'; export default class ProjectFetcher { async fetch(config: Config, params?: object): Promise { const response = await this.graphql(config, params || {}); try { - const data = (await response.json()) as {data: any}; + const data = (await response.json()) as { data: any }; if (!data.data) { throw new CLIError( chalk.red(`Can’t find the project for the key: ${config.apiKey}`), - {exit: 1} + { exit: 1 } ); } @@ -26,19 +26,19 @@ export default class ProjectFetcher { chalk.red( `Can’t fetch the project on ${config.apiUrl} with key ${config.apiKey}` ), - {exit: 1} + { exit: 1 } ); } } private async graphql(config: Config, params: object) { - const query = `query ProjectDetails($projectId: ID! $versionId: ID) { + const query = `query ProjectDetails($project_id: ID! $versionId: ID) { viewer { user { fullname } - project(id: $projectId) { + project(id: $project_id) { id name logo @@ -106,11 +106,11 @@ export default class ProjectFetcher { } }`; - const configParams = config.project ? {projectId: config.project} : {}; - const variables = {...configParams, ...params}; + const configParams = config.project ? { project_id: config.project } : {}; + const variables = { ...configParams, ...params }; return await fetch(`${config.apiUrl}/graphql`, { - body: JSON.stringify({query, variables}), + body: JSON.stringify({ query, variables }), headers: { 'Content-Type': 'application/json', authorization: `Bearer ${config.apiKey}`, diff --git a/cli/src/types/config.ts b/cli/src/types/config.ts index baea47441..56386214f 100644 --- a/cli/src/types/config.ts +++ b/cli/src/types/config.ts @@ -1,9 +1,11 @@ // Types -import {DocumentConfig} from './document-config'; +import { DocumentConfig } from './document-config'; +import { VersionConfig } from './version-config'; export interface Config { apiUrl: string; apiKey: string; - project: string | null | undefined; + project?: string | null; + version?: VersionConfig; files: DocumentConfig[]; } diff --git a/cli/src/types/version-config.ts b/cli/src/types/version-config.ts new file mode 100644 index 000000000..d0c2ecc00 --- /dev/null +++ b/cli/src/types/version-config.ts @@ -0,0 +1,4 @@ +export interface VersionConfig { + branchVersionPrefix?: string; + identifier?: string; +} diff --git a/docker-compose.yml b/docker-compose.yml index 399c6476a..d4a8fcaf3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,6 +9,7 @@ services: depends_on: - postgresql environment: + - DUMMY_LOGIN_ENABLED=1 - PORT=4000 - DATABASE_URL=postgres://postgres@postgresql:5432/accent_development postgresql: