From 9f3dc9511f3ee020e4725f9728a6794d2546bc3b Mon Sep 17 00:00:00 2001 From: Dmitrii Suchkov Date: Mon, 15 Dec 2025 14:16:15 +0000 Subject: [PATCH 1/4] Please provide a way to publish a chapter without pages see https://bugtracker.codiodev.com/issue/codio-17329 --- package.json | 2 +- src/lib/assignment.ts | 11 ++++++++--- src/lib/tools.ts | 14 +++++++------- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 02b7f68..d135f77 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codio-api-js", - "version": "0.17.1", + "version": "0.18.0", "description": "JS client to Codio API", "repository": "https://github.com/codio/codio-api-js", "author": "Max Kraev ", diff --git a/src/lib/assignment.ts b/src/lib/assignment.ts index 43340e0..ee93bee 100644 --- a/src/lib/assignment.ts +++ b/src/lib/assignment.ts @@ -20,6 +20,7 @@ type YamlRaw = { assignmentName: string | undefined paths: (string | PathMap)[] | undefined section: string | string[] + withChildren: boolean | undefined } type Yaml = { @@ -27,6 +28,7 @@ type Yaml = { assignmentName: string | undefined paths: (string | PathMap)[] section: string[][] + withChildren: boolean } export type TimeExtension = { @@ -200,11 +202,13 @@ function validityState(ymls: YamlRaw[]): Yaml[] { item.section.push(section) item.paths = item.paths.concat(yml.paths || []) } else { + const withChildren = yml.withChildren !== false map.set(assignmentId, { assignment: yml.assignment, assignmentName: yml.assignmentName, paths: yml.paths || [], - section: [section] + section: [section], + withChildren: withChildren }) } } @@ -233,7 +237,8 @@ function validateYmlCfg(ymls: Yaml[]): Yaml[] { assignment: yml.assignment, assignmentName: yml.assignmentName, paths: yml.paths || [], - section: section + section: section, + withChildren: yml.withChildren }) } } @@ -296,7 +301,7 @@ async function reducePublish(courseId: string, srcDir: string, yamlDir: string, if (!item.assignment) { throw new Error(`assignment not found with name "${item.assignmentName}"`) } - await tools.reduce(srcDir, tmpDstDir, item.section, _.compact(paths)) + await tools.reduce(srcDir, tmpDstDir, item.section, _.compact(paths), item.withChildren) await assignment.publish(courseId, item.assignment, tmpDstDir, changelogOrOptions) fs.rmSync(tmpDstDir, {recursive: true}) } diff --git a/src/lib/tools.ts b/src/lib/tools.ts index a10af07..a571797 100644 --- a/src/lib/tools.ts +++ b/src/lib/tools.ts @@ -22,13 +22,13 @@ export async function fixGuidesVersion(projectPath: string) { } export async function reduce( - srcDir: string, dstDir: string, yaml_sections: string[][], paths: (string | PathMap)[]): Promise { + srcDir: string, dstDir: string, yaml_sections: string[][], paths: (string | PathMap)[], withChildren: boolean): Promise { await fixGuidesVersion(srcDir) const contentDir = path.join(srcDir, GUIDES_CONTENT_DIR) const rootMetadataPath = path.join(contentDir, INDEX_METADATA_FILE) const rootMetadata = readMetadataFile(rootMetadataPath) const guidesStructure = getGuidesStructure(rootMetadata, srcDir, '') - const filter = collectFilter(guidesStructure, _.cloneDeep(yaml_sections)) + const filter = collectFilter(guidesStructure, _.cloneDeep(yaml_sections), withChildren) const strippedStructure = stripStructure(guidesStructure, filter) const strippedSectionsIds = getStrippedSectionIds(strippedStructure) const excludePaths = getExcludedPaths(guidesStructure, strippedSectionsIds) @@ -83,7 +83,7 @@ const DEFAULT_ALL_SECTION: Section = { children: {} } -function collectFilter(guidesStructure, yaml_sections) { +function collectFilter(guidesStructure, yaml_sections, withChildren: boolean) { const filterMap = { all: false, children: {} @@ -93,7 +93,7 @@ function collectFilter(guidesStructure, yaml_sections) { if (sectionPath.length === 0) { continue } - const section = traverseItems(guidesStructure, sectionPath, filterMap) + const section = traverseItems(guidesStructure, sectionPath, filterMap, withChildren) if (!section) { throw new Error(`${section} not found`) } @@ -105,7 +105,7 @@ function collectFilter(guidesStructure, yaml_sections) { return filterMap } -function traverseItems(structure, sectionPath: string[], filterMap: Section) { +function traverseItems(structure, sectionPath: string[], filterMap: Section, withChildren: boolean) { const sectionName = sectionPath.shift() if (!sectionName) { return @@ -122,9 +122,9 @@ function traverseItems(structure, sectionPath: string[], filterMap: Section) { } if (sectionPath.length > 0) { // fill-in filterMap - traverseItems(section.children, sectionPath, filterMap.children[section.id]) + traverseItems(section.children, sectionPath, filterMap.children[section.id], withChildren) } else { - filterMap.children[section.id].all = true + filterMap.children[section.id].all = withChildren } return section } From b49d998955a93f15b6adcf0ad66b8ec981433f98 Mon Sep 17 00:00:00 2001 From: Dmitrii Suchkov Date: Mon, 15 Dec 2025 14:39:32 +0000 Subject: [PATCH 2/4] add readme and default value --- README.md | 7 ++++++- src/lib/tools.ts | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f8fbabd..1ee4a28 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ GitHub Action Usage Example: https://github.com/ksimuk/codio-test-publish/blob/m Truncate pages from project. This method creates in the `dstDir` reduced version of the project, which contains only pages specified in `sections: string[]` and files specified in `paths: string[]` ``` - await codio.v1.tools.reduce(srcDir, dstDir, sections, paths) + await codio.v1.tools.reduce(srcDir, dstDir, sections, paths, withChildren) ``` ## Reduce Publish @@ -74,6 +74,7 @@ Similar to reduce but publishes generated projects as assignments. `assignmentName` - or name of the assignment to publish `section` - section name or array of paths to the section `paths` - an array of files that needs to be exported, `.guides` is exported fully to all assignments +`withChildren` - boolean - preserve children structure, `true` by default ``` - assignment: @@ -86,6 +87,10 @@ Similar to reduce but publishes generated projects as assignments. - assignmentName: section: Section 1 +- assignmentName: + section: Section 2 + withChildren: false + ``` GitHub Action: https://github.com/codio/codio-assignment-publish-action diff --git a/src/lib/tools.ts b/src/lib/tools.ts index a571797..7d09a30 100644 --- a/src/lib/tools.ts +++ b/src/lib/tools.ts @@ -22,7 +22,8 @@ export async function fixGuidesVersion(projectPath: string) { } export async function reduce( - srcDir: string, dstDir: string, yaml_sections: string[][], paths: (string | PathMap)[], withChildren: boolean): Promise { + srcDir: string, dstDir: string, yaml_sections: string[][], paths: (string | PathMap)[], withChildren=true +): Promise { await fixGuidesVersion(srcDir) const contentDir = path.join(srcDir, GUIDES_CONTENT_DIR) const rootMetadataPath = path.join(contentDir, INDEX_METADATA_FILE) From 9c678a8aafeb2f43d111d64e9c2958a9674a5cf3 Mon Sep 17 00:00:00 2001 From: Dmitrii Suchkov Date: Mon, 15 Dec 2025 16:53:57 +0000 Subject: [PATCH 3/4] store config per section --- src/lib/assignment.ts | 22 +++++++++++++++++----- src/lib/tools.ts | 12 +++++++----- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/lib/assignment.ts b/src/lib/assignment.ts index ee93bee..cfe263c 100644 --- a/src/lib/assignment.ts +++ b/src/lib/assignment.ts @@ -23,12 +23,20 @@ type YamlRaw = { withChildren: boolean | undefined } +export type SectionConfig = { + withChildren: boolean +} + +export function sectionToKey(section: string[]) { + return section.join('\n') +} + type Yaml = { assignment: string | undefined assignmentName: string | undefined paths: (string | PathMap)[] section: string[][] - withChildren: boolean + sectionConfig: Map } export type TimeExtension = { @@ -194,6 +202,7 @@ function validityState(ymls: YamlRaw[]): Yaml[] { if (assignmentId === undefined) { throw new Error('assignment and assignmentName does not exist') } + const withChildren = yml.withChildren !== false if (map.has(assignmentId)) { const item = map.get(assignmentId) if (!item) { @@ -201,14 +210,16 @@ function validityState(ymls: YamlRaw[]): Yaml[] { } item.section.push(section) item.paths = item.paths.concat(yml.paths || []) + item.sectionConfig.set(sectionToKey(section), {withChildren: withChildren}) } else { - const withChildren = yml.withChildren !== false + const sectionConfig: Map = new Map() + sectionConfig.set(sectionToKey(section), {withChildren: withChildren}) map.set(assignmentId, { assignment: yml.assignment, assignmentName: yml.assignmentName, paths: yml.paths || [], section: [section], - withChildren: withChildren + sectionConfig: sectionConfig }) } } @@ -232,13 +243,14 @@ function validateYmlCfg(ymls: Yaml[]): Yaml[] { } item.section = item.section.concat(section) item.paths = item.paths.concat(yml.paths || []) + item.sectionConfig = yml.sectionConfig } else { map.set(assignmentId, { assignment: yml.assignment, assignmentName: yml.assignmentName, paths: yml.paths || [], section: section, - withChildren: yml.withChildren + sectionConfig: yml.sectionConfig }) } } @@ -301,7 +313,7 @@ async function reducePublish(courseId: string, srcDir: string, yamlDir: string, if (!item.assignment) { throw new Error(`assignment not found with name "${item.assignmentName}"`) } - await tools.reduce(srcDir, tmpDstDir, item.section, _.compact(paths), item.withChildren) + await tools.reduce(srcDir, tmpDstDir, item.section, _.compact(paths), item.sectionConfig) await assignment.publish(courseId, item.assignment, tmpDstDir, changelogOrOptions) fs.rmSync(tmpDstDir, {recursive: true}) } diff --git a/src/lib/tools.ts b/src/lib/tools.ts index 7d09a30..24d1009 100644 --- a/src/lib/tools.ts +++ b/src/lib/tools.ts @@ -7,7 +7,7 @@ import {excludePaths} from './config' import tar from 'tar' import { ZSTDCompress } from 'simple-zstd' import config from './config' -import { PathMap } from './assignment' +import { PathMap, SectionConfig, sectionToKey } from './assignment' const CONVERTER_VERSION = '4ca4944ddf9d4fe4df9697bec06cbd0a6c170419' const GUIDES_CONTENT_DIR = '.guides/content' @@ -22,14 +22,15 @@ export async function fixGuidesVersion(projectPath: string) { } export async function reduce( - srcDir: string, dstDir: string, yaml_sections: string[][], paths: (string | PathMap)[], withChildren=true + srcDir: string, dstDir: string, yaml_sections: string[][], + paths: (string | PathMap)[], sectionConfig: Map = new Map() ): Promise { await fixGuidesVersion(srcDir) const contentDir = path.join(srcDir, GUIDES_CONTENT_DIR) const rootMetadataPath = path.join(contentDir, INDEX_METADATA_FILE) const rootMetadata = readMetadataFile(rootMetadataPath) const guidesStructure = getGuidesStructure(rootMetadata, srcDir, '') - const filter = collectFilter(guidesStructure, _.cloneDeep(yaml_sections), withChildren) + const filter = collectFilter(guidesStructure, _.cloneDeep(yaml_sections), sectionConfig) const strippedStructure = stripStructure(guidesStructure, filter) const strippedSectionsIds = getStrippedSectionIds(strippedStructure) const excludePaths = getExcludedPaths(guidesStructure, strippedSectionsIds) @@ -84,7 +85,7 @@ const DEFAULT_ALL_SECTION: Section = { children: {} } -function collectFilter(guidesStructure, yaml_sections, withChildren: boolean) { +function collectFilter(guidesStructure, yaml_sections: string[][], sectionConfig: Map) { const filterMap = { all: false, children: {} @@ -94,7 +95,8 @@ function collectFilter(guidesStructure, yaml_sections, withChildren: boolean) { if (sectionPath.length === 0) { continue } - const section = traverseItems(guidesStructure, sectionPath, filterMap, withChildren) + const withChildren = sectionConfig.has(sectionToKey(sectionPath)) ? sectionConfig.get(sectionToKey(sectionPath))?.withChildren : true + const section = traverseItems(guidesStructure, sectionPath, filterMap, withChildren??true) if (!section) { throw new Error(`${section} not found`) } From b7aca6d4d0cd0f29084875d3ac15d785d8305fe9 Mon Sep 17 00:00:00 2001 From: Dmitrii Suchkov Date: Mon, 15 Dec 2025 17:03:50 +0000 Subject: [PATCH 4/4] update readme remove duplicate operation and set default value --- README.md | 2 +- src/lib/tools.ts | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 1ee4a28..b45a6ed 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ GitHub Action Usage Example: https://github.com/ksimuk/codio-test-publish/blob/m Truncate pages from project. This method creates in the `dstDir` reduced version of the project, which contains only pages specified in `sections: string[]` and files specified in `paths: string[]` ``` - await codio.v1.tools.reduce(srcDir, dstDir, sections, paths, withChildren) + await codio.v1.tools.reduce(srcDir, dstDir, sections, paths, sectionsConfig) ``` ## Reduce Publish diff --git a/src/lib/tools.ts b/src/lib/tools.ts index 24d1009..30ca286 100644 --- a/src/lib/tools.ts +++ b/src/lib/tools.ts @@ -23,14 +23,14 @@ export async function fixGuidesVersion(projectPath: string) { export async function reduce( srcDir: string, dstDir: string, yaml_sections: string[][], - paths: (string | PathMap)[], sectionConfig: Map = new Map() + paths: (string | PathMap)[], sectionsConfig: Map = new Map() ): Promise { await fixGuidesVersion(srcDir) const contentDir = path.join(srcDir, GUIDES_CONTENT_DIR) const rootMetadataPath = path.join(contentDir, INDEX_METADATA_FILE) const rootMetadata = readMetadataFile(rootMetadataPath) const guidesStructure = getGuidesStructure(rootMetadata, srcDir, '') - const filter = collectFilter(guidesStructure, _.cloneDeep(yaml_sections), sectionConfig) + const filter = collectFilter(guidesStructure, _.cloneDeep(yaml_sections), sectionsConfig) const strippedStructure = stripStructure(guidesStructure, filter) const strippedSectionsIds = getStrippedSectionIds(strippedStructure) const excludePaths = getExcludedPaths(guidesStructure, strippedSectionsIds) @@ -85,7 +85,7 @@ const DEFAULT_ALL_SECTION: Section = { children: {} } -function collectFilter(guidesStructure, yaml_sections: string[][], sectionConfig: Map) { +function collectFilter(guidesStructure, yaml_sections: string[][], sectionsConfig: Map) { const filterMap = { all: false, children: {} @@ -95,7 +95,8 @@ function collectFilter(guidesStructure, yaml_sections: string[][], sectionConfig if (sectionPath.length === 0) { continue } - const withChildren = sectionConfig.has(sectionToKey(sectionPath)) ? sectionConfig.get(sectionToKey(sectionPath))?.withChildren : true + const key = sectionToKey(sectionPath) + const withChildren = sectionsConfig.has(key) ? sectionsConfig.get(key)?.withChildren : true const section = traverseItems(guidesStructure, sectionPath, filterMap, withChildren??true) if (!section) { throw new Error(`${section} not found`)