fix(lint): resolve golangci-lint failures and modernize string handling

CI's golangci-lint (v2.12.2) job was failing with 14 issues, which blocked
the dependent test job. This addresses all of them:

- goconst: extract repeated string literals into constants
  - format aliases ("md", "word", "htm") in the exporter factory
  - "section" lesson type shared by markdown and HTML exporters
  - default Articulate Rise base URL and host in the parser
  - reuse existing itemType* constants in the markdown switch
- staticcheck (QF1012): replace buf.WriteString(fmt.Sprintf(...)) with
  fmt.Fprintf(...) in the markdown exporter

Also drop the hardcoded `go: "1.24"` from .golangci.yml so the target Go
version is autodetected from go.mod.
This commit is contained in:
2026-06-15 16:59:06 +00:00
parent 9ea51458c3
commit bf07d6a172
5 changed files with 45 additions and 26 deletions
+13 -4
View File
@@ -13,6 +13,11 @@ const (
FormatMarkdown = "markdown"
FormatDocx = "docx"
FormatHTML = "html"
// Format aliases accepted by CreateExporter.
formatAliasMarkdown = "md"
formatAliasDocx = "word"
formatAliasHTML = "htm"
)
// Factory implements the ExporterFactory interface.
@@ -41,11 +46,11 @@ func NewFactory(htmlCleaner *services.HTMLCleaner) interfaces.ExporterFactory {
// Format strings are case-insensitive (e.g., "markdown", "DOCX").
func (f *Factory) CreateExporter(format string) (interfaces.Exporter, error) {
switch strings.ToLower(format) {
case FormatMarkdown, "md":
case FormatMarkdown, formatAliasMarkdown:
return NewMarkdownExporter(f.htmlCleaner), nil
case FormatDocx, "word":
case FormatDocx, formatAliasDocx:
return NewDocxExporter(f.htmlCleaner), nil
case FormatHTML, "htm":
case FormatHTML, formatAliasHTML:
return NewHTMLExporter(f.htmlCleaner), nil
default:
return nil, fmt.Errorf("unsupported export format: %s", format)
@@ -55,5 +60,9 @@ func (f *Factory) CreateExporter(format string) (interfaces.Exporter, error) {
// SupportedFormats returns a list of all supported export formats,
// including both primary format names and their aliases.
func (f *Factory) SupportedFormats() []string {
return []string{FormatMarkdown, "md", FormatDocx, "word", FormatHTML, "htm"}
return []string{
FormatMarkdown, formatAliasMarkdown,
FormatDocx, formatAliasDocx,
FormatHTML, formatAliasHTML,
}
}
+4 -1
View File
@@ -21,6 +21,9 @@ const (
itemTypeDivider = "divider"
)
// lessonTypeSection identifies a lesson that acts as a section header.
const lessonTypeSection = "section"
// templateData represents the data structure passed to the HTML template.
type templateData struct {
Course models.CourseInfo
@@ -74,7 +77,7 @@ func prepareTemplateData(course *models.Course, htmlCleaner *services.HTMLCleane
Description: lesson.Description,
}
if lesson.Type != "section" {
if lesson.Type != lessonTypeSection {
lessonCounter++
section.Number = lessonCounter
section.Items = prepareItems(lesson.Items, htmlCleaner)
+17 -17
View File
@@ -40,35 +40,35 @@ func (e *MarkdownExporter) Export(course *models.Course, outputPath string) erro
var buf bytes.Buffer
// Write course header
buf.WriteString(fmt.Sprintf("# %s\n\n", course.Course.Title))
fmt.Fprintf(&buf, "# %s\n\n", course.Course.Title)
if course.Course.Description != "" {
buf.WriteString(fmt.Sprintf("%s\n\n", e.htmlCleaner.CleanHTML(course.Course.Description)))
fmt.Fprintf(&buf, "%s\n\n", e.htmlCleaner.CleanHTML(course.Course.Description))
}
// Add metadata
buf.WriteString("## Course Information\n\n")
buf.WriteString(fmt.Sprintf("- **Course ID**: %s\n", course.Course.ID))
buf.WriteString(fmt.Sprintf("- **Share ID**: %s\n", course.ShareID))
buf.WriteString(fmt.Sprintf("- **Navigation Mode**: %s\n", course.Course.NavigationMode))
fmt.Fprintf(&buf, "- **Course ID**: %s\n", course.Course.ID)
fmt.Fprintf(&buf, "- **Share ID**: %s\n", course.ShareID)
fmt.Fprintf(&buf, "- **Navigation Mode**: %s\n", course.Course.NavigationMode)
if course.Course.ExportSettings != nil {
buf.WriteString(fmt.Sprintf("- **Export Format**: %s\n", course.Course.ExportSettings.Format))
fmt.Fprintf(&buf, "- **Export Format**: %s\n", course.Course.ExportSettings.Format)
}
buf.WriteString("\n---\n\n")
// Process lessons
lessonCounter := 0
for _, lesson := range course.Course.Lessons {
if lesson.Type == "section" {
buf.WriteString(fmt.Sprintf("# %s\n\n", lesson.Title))
if lesson.Type == lessonTypeSection {
fmt.Fprintf(&buf, "# %s\n\n", lesson.Title)
continue
}
lessonCounter++
buf.WriteString(fmt.Sprintf("## Lesson %d: %s\n\n", lessonCounter, lesson.Title))
fmt.Fprintf(&buf, "## Lesson %d: %s\n\n", lessonCounter, lesson.Title)
if lesson.Description != "" {
buf.WriteString(fmt.Sprintf("%s\n\n", e.htmlCleaner.CleanHTML(lesson.Description)))
fmt.Fprintf(&buf, "%s\n\n", e.htmlCleaner.CleanHTML(lesson.Description))
}
// Process lesson items
@@ -100,19 +100,19 @@ func (e *MarkdownExporter) processItemToMarkdown(buf *bytes.Buffer, item models.
itemType := strings.ToLower(item.Type)
switch itemType {
case "text":
case itemTypeText:
e.processTextItem(buf, item, headingPrefix)
case "list":
case itemTypeList:
e.processListItem(buf, item)
case "multimedia":
case itemTypeMultimedia:
e.processMultimediaItem(buf, item, headingPrefix)
case "image":
case itemTypeImage:
e.processImageItem(buf, item, headingPrefix)
case "knowledgecheck":
case itemTypeKnowledgeCheck:
e.processKnowledgeCheckItem(buf, item, headingPrefix)
case "interactive":
case itemTypeInteractive:
e.processInteractiveItem(buf, item, headingPrefix)
case "divider":
case itemTypeDivider:
e.processDividerItem(buf)
default:
e.processUnknownItem(buf, item, headingPrefix)