Skip to content

Commit

Permalink
Get submodule branch from .gitmodules
Browse files Browse the repository at this point in the history
  • Loading branch information
TylerZeroMaster committed Feb 19, 2024
1 parent 57b942b commit 639deb7
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 27 deletions.
3 changes: 2 additions & 1 deletion client/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import path from 'path'
import fs from 'fs'
import vscode from 'vscode'
import { type LanguageClient } from 'vscode-languageclient/node'
import { pushContent, validateContent, setDefaultGitConfig } from './push-content'
import { pushContent, validateContent, setDefaultGitConfig, initPrivateSubmodule } from './push-content'
import { TocEditorPanel } from './panel-toc-editor'
import { CnxmlPreviewPanel } from './panel-cnxml-preview'
import { expect, ensureCatch, ensureCatchPromise, launchLanguageServer, populateXsdSchemaFiles, getRootPathUri, configureWorkspaceSettings } from './utils'
Expand Down Expand Up @@ -108,6 +108,7 @@ function doRest(client: LanguageClient): ExtensionExports {
vscode.commands.registerCommand('openstax.toggleTocTreesFiltering', ensureCatch(toggleTocTreesFilteringHandler(tocTreesView, tocTreesProvider)))
vscode.commands.registerCommand('openstax.validateContent', ensureCatch(validateContent))
void ensureCatchPromise(setDefaultGitConfig())
void ensureCatchPromise(initPrivateSubmodule(hostContext))

// It is a logic error for anything else to listen to this event from the client.
// It is only allowed a single handler, from what we can tell
Expand Down
93 changes: 77 additions & 16 deletions client/src/push-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import vscode from 'vscode'
import { expect, getErrorDiagnosticsBySource, getRootPathUri } from './utils'
import { type GitExtension, GitErrorCodes, type CommitOptions, type Repository, Status } from './git-api/git'
import { type ExtensionHostContext } from './panel'
import { DiagnosticSource, requestEnsureIds } from '../../common/src/requests'
import { DiagnosticSource, requestEnsureIds, requestGetSubmoduleConfig } from '../../common/src/requests'
import { type LanguageClient } from 'vscode-languageclient/node'

const PRIVATE_SUBMODULE_NAME = 'private'

export enum DocumentsToOpen {
all = 'All Content',
Expand Down Expand Up @@ -207,6 +210,44 @@ export const getMessage = async (): Promise<string | undefined> => {
return message
}

// NOTE: When the private submodule is not initialized, this will return undefined
export const _getPrivateSubmodule = () => getRepo(PRIVATE_SUBMODULE_NAME)

/* istanbul ignore next (already tested in pushContent) */
export const initPrivateSubmodule = async (hostContext: ExtensionHostContext) => {
const privateSubmodule = _getPrivateSubmodule()
if (privateSubmodule !== undefined) {
await preparePrivateSubmodule(hostContext.client, privateSubmodule)
} else {
console.warn(`Private submodule not found: ${PRIVATE_SUBMODULE_NAME}`)
}
}

export const preparePrivateSubmodule = async (
client: LanguageClient,
privateSubmodule: Repository
): Promise<void> => {
// TODO: What if submodule needs to be initialized? (not a problem in gitpod)
const uri = expect(getRootPathUri(), 'Could not get root path')
const maybeGitModules = await requestGetSubmoduleConfig(
client,
{ workspaceUri: uri.toString() }
)
const submoduleBranch = expect(
maybeGitModules?.[`submodule.${PRIVATE_SUBMODULE_NAME}.branch`],
'Could not determine which private submodule branch to use'
)
try {
await privateSubmodule.fetch()
await privateSubmodule.checkout(submoduleBranch)
} catch (e) {
console.error(e)
throw new Error(
`Could not checkout private submodule branch: ${submoduleBranch}`
)
}
}

export const pushContent = (hostContext: ExtensionHostContext) => async () => {
// Do a precursory check for known errors (fast!)
if (canPush(getErrorDiagnosticsBySource())) {
Expand All @@ -223,17 +264,42 @@ export const pushContent = (hostContext: ExtensionHostContext) => async () => {
progress.report({ message: 'Creating Auto Element IDs, please wait...' })
// const serverErrorMessage = 'Server cannot properly find workspace'
const uri = expect(getRootPathUri(), 'No root path in which to generate a module')
const pushTargets = [getBookRepo()]
// fix ids
// TODO: better ui in future. Add `increment` value in `progress.report` and use a callback to update real progress
await requestEnsureIds(hostContext.client, { workspaceUri: uri.toString() })
const privateSubmodule = _getPrivateSubmodule()
if (privateSubmodule !== undefined) {
try {
await preparePrivateSubmodule(hostContext.client, privateSubmodule)
pushTargets.push(privateSubmodule)
} catch (e) {
const err = e as Error
await vscode.window.showErrorMessage(err.message)
return
}
} else {
console.warn('Could not find private submodule')
}
// push content
progress.report({ message: 'Pushing...' })
await _pushContent(
getBookRepo,
getMessage,
vscode.window.showInformationMessage,
vscode.window.showErrorMessage
)()
const commitMessage = await getMessage()
/* istanbul ignore if */
if (commitMessage == null || !canPush(await openAndValidate(DocumentsToOpen.modified))) {
return
}

for (const repo of pushTargets) {
const infoWithRepo = async (msg: string) => {
/* istanbul ignore next (just wrapping function) */
return await vscode.window.showInformationMessage(`${repo.rootUri.fsPath}: ${msg}`)
}
const errorWithRepo = async (msg: string) => {
/* istanbul ignore next (just wrapping function) */
return await vscode.window.showErrorMessage(`${repo.rootUri.fsPath}: ${msg}`)
}
await _pushContent(repo, commitMessage, infoWithRepo, errorWithRepo)()
}
})
}
}
Expand All @@ -244,21 +310,16 @@ interface GitError extends Error {
}

export const _pushContent = (
_getRepo: () => Repository,
_getMessage: () => Thenable<string | undefined>,
repo: Repository,
commitMessage: string,
infoReporter: (msg: string) => Thenable<string | undefined>,
errorReporter: (msg: string) => Thenable<string | undefined>
errorReporter: (msg: string) => Thenable<string | undefined>,
options?: { branchName?: string }
) => async () => {
const repo = _getRepo()
const commitOptions: CommitOptions = { all: true }

let commitSucceeded = false

const commitMessage = await _getMessage()
/* istanbul ignore if */
if (commitMessage == null || !canPush(await openAndValidate(DocumentsToOpen.modified))) {
return
}
try {
await repo.commit(commitMessage, commitOptions)
commitSucceeded = true
Expand Down
17 changes: 11 additions & 6 deletions common/src/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ export type Opt<T> = T | undefined
export enum ExtensionServerRequest {
BundleEnsureIds = 'BUNDLE_ENSURE_IDS',
TocModification = 'TOC_MODIFICATION',
GenerateReadme = 'GENREATE_README'
GenerateReadme = 'GENREATE_README',
GetSubmoduleConfig = 'GET_SUBMODULE_CONFIG'
}

export enum ExtensionServerNotification {
Expand Down Expand Up @@ -49,18 +50,22 @@ interface LanguageClient {
sendRequest: <R>(method: string, param: any) => Promise<R>
}

export interface BundleEnsureIdsParams {
export interface BundleRequestParams {
workspaceUri: string
}

export interface BundleGenerateReadme {
workspaceUri: string
}
export interface BundleEnsureIdsParams extends BundleRequestParams { }
export interface BundleGenerateReadmeParams extends BundleRequestParams { }
export interface BundleGetSubmoduleConfigParams extends BundleRequestParams { }

export const requestEnsureIds = async (client: LanguageClient, args: BundleEnsureIdsParams): Promise<void> => {
await client.sendRequest(ExtensionServerRequest.BundleEnsureIds, args)
}

export const requestGenerateReadme = async (client: LanguageClient, args: BundleGenerateReadme): Promise<void> => {
export const requestGenerateReadme = async (client: LanguageClient, args: BundleGenerateReadmeParams): Promise<void> => {
await client.sendRequest(ExtensionServerRequest.GenerateReadme, args)
}

export const requestGetSubmoduleConfig = async (client: LanguageClient, args: BundleGetSubmoduleConfigParams): Promise<Record<string, string> | null> => {
return await client.sendRequest(ExtensionServerRequest.GetSubmoduleConfig, args)
}
28 changes: 28 additions & 0 deletions server/src/git-config-parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// RegExp from: vscode/extensions/git/src/git.ts
const lineSep = /\r?\n/
const propertyPattern = /^\s*(\w+)\s*=\s*"?([^"]+)"?$/
const sectionPattern = /^\s*\[\s*([^\]]+?)\s*("[^"]+")*\]\s*$/

export function parseGitConfig(config: string): Record<string, string> {
let sectionName = ''
const modulesConfig: Record<string, string> = {}
for (const line of config.split(lineSep)) {
// Sections
const sectionMatch = line.match(sectionPattern)
if (sectionMatch?.length === 3) {
const subSectionName = sectionMatch[2]?.replace(/"/g, '')
sectionName = subSectionName !== undefined
? `${sectionMatch[1]}.${subSectionName}`
: sectionMatch[1]
continue
}
// Properties
const propertyMatch = line.match(propertyPattern)
if (propertyMatch?.length === 3) {
if (sectionName.length === 0) continue
const key = `${sectionName}.${propertyMatch[1]}`
modulesConfig[key] = propertyMatch[2]
}
}
return modulesConfig
}
16 changes: 13 additions & 3 deletions server/src/server-handler.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import fs from 'node:fs'

import { type BundleGenerateReadme, type BundleEnsureIdsParams } from '../../common/src/requests'
import type { BundleGenerateReadmeParams, BundleEnsureIdsParams, BundleGetSubmoduleConfigParams } from '../../common/src/requests'
import { idFixer } from './fix-document-ids'
import { bundleFactory } from './server'
import { type ModelManager } from './model-manager'
import { type CompletionItem, type CompletionParams } from 'vscode-languageserver/node'
import { PageValidationKind } from './model/page'
import { generateReadmeForWorkspace } from './readme-generator'
import { URI } from 'vscode-uri'
import { parseGitConfig } from './git-config-parser'

export function bundleEnsureIdsHandler(): (request: BundleEnsureIdsParams) => Promise<void> {
return async (request: BundleEnsureIdsParams) => {
Expand All @@ -22,8 +23,8 @@ export function bundleEnsureIdsHandler(): (request: BundleEnsureIdsParams) => Pr
}
}

export function bundleGenerateReadme(): (request: BundleGenerateReadme) => Promise<void> {
return async (request: BundleEnsureIdsParams) => {
export function bundleGenerateReadme(): (request: BundleGenerateReadmeParams) => Promise<void> {
return async (request: BundleGenerateReadmeParams) => {
const manager = bundleFactory.getOrAdd(request.workspaceUri)
const books = manager.bundle.books
const readmePath = `${URI.parse(request.workspaceUri).fsPath}/README.md`
Expand All @@ -35,6 +36,15 @@ export function bundleGenerateReadme(): (request: BundleGenerateReadme) => Promi
}
}

export function bundleGetSubmoduleConfig(): (request: BundleGetSubmoduleConfigParams) => Promise<Record<string, string> | null> {
return async (request: BundleGetSubmoduleConfigParams) => {
const gitmodules = `${URI.parse(request.workspaceUri).fsPath}/.gitmodules`
return fs.existsSync(gitmodules)
? parseGitConfig(fs.readFileSync(gitmodules, 'utf-8'))
: null
}
}

export async function resourceAutocompleteHandler(documentPosition: CompletionParams, manager: ModelManager): Promise<CompletionItem[]> {
await manager.loadEnoughForOrphans()
const cursor = documentPosition.position
Expand Down
3 changes: 2 additions & 1 deletion server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { URI, Utils } from 'vscode-uri'
import { expectValue } from './model/utils'

import { ExtensionServerRequest } from '../../common/src/requests'
import { bundleEnsureIdsHandler, bundleGenerateReadme, resourceAutocompleteHandler } from './server-handler'
import { bundleEnsureIdsHandler, bundleGenerateReadme, bundleGetSubmoduleConfig, resourceAutocompleteHandler } from './server-handler'

import * as sourcemaps from 'source-map-support'
import { Bundle } from './model/bundle'
Expand Down Expand Up @@ -154,6 +154,7 @@ connection.onRequest(ExtensionServerRequest.TocModification, async (params: TocM

connection.onRequest(ExtensionServerRequest.BundleEnsureIds, bundleEnsureIdsHandler())
connection.onRequest(ExtensionServerRequest.GenerateReadme, bundleGenerateReadme())
connection.onRequest(ExtensionServerRequest.GetSubmoduleConfig, bundleGetSubmoduleConfig())

connection.onCompletionResolve((a: CompletionItem, token: CancellationToken): CompletionItem => a)

Expand Down

0 comments on commit 639deb7

Please sign in to comment.