mirror of
https://github.com/kjanat/articulate-parser.git
synced 2026-01-16 07:42:09 +01:00
Compare commits
3 Commits
33673d661b
...
v1.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
94a7924bed
|
|||
|
90b9d557d8
|
|||
|
33ff267644
|
42
.dprint.jsonc
Normal file
42
.dprint.jsonc
Normal file
@ -0,0 +1,42 @@
|
||||
{
|
||||
"typescript": {
|
||||
},
|
||||
"json": {
|
||||
},
|
||||
"markdown": {
|
||||
},
|
||||
"toml": {
|
||||
},
|
||||
"dockerfile": {
|
||||
},
|
||||
"oxc": {
|
||||
},
|
||||
"ruff": {
|
||||
},
|
||||
"jupyter": {
|
||||
},
|
||||
"malva": {
|
||||
},
|
||||
"markup": {
|
||||
},
|
||||
"yaml": {
|
||||
},
|
||||
"excludes": [
|
||||
"**/node_modules",
|
||||
"**/*-lock.json",
|
||||
],
|
||||
"plugins": [
|
||||
"https://plugins.dprint.dev/typescript-0.95.13.wasm",
|
||||
"https://plugins.dprint.dev/json-0.21.1.wasm",
|
||||
"https://plugins.dprint.dev/markdown-0.20.0.wasm",
|
||||
"https://plugins.dprint.dev/toml-0.7.0.wasm",
|
||||
"https://plugins.dprint.dev/dockerfile-0.3.3.wasm",
|
||||
"https://plugins.dprint.dev/oxc-0.1.0.wasm",
|
||||
"https://plugins.dprint.dev/ruff-0.6.11.wasm",
|
||||
"https://plugins.dprint.dev/jupyter-0.2.1.wasm",
|
||||
"https://plugins.dprint.dev/g-plane/malva-v0.15.1.wasm",
|
||||
"https://plugins.dprint.dev/g-plane/markup_fmt-v0.25.3.wasm",
|
||||
"https://plugins.dprint.dev/g-plane/pretty_yaml-v0.5.1.wasm",
|
||||
"https://plugins.dprint.dev/exec-0.6.0.json@a054130d458f124f9b5c91484833828950723a5af3f8ff2bd1523bd47b83b364",
|
||||
],
|
||||
}
|
||||
4
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
4
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -1,7 +1,7 @@
|
||||
name: Bug Report
|
||||
description: Create a report to help us improve
|
||||
title: '[BUG] '
|
||||
labels: ['bug', 'triage']
|
||||
title: "[BUG] "
|
||||
labels: ["bug", "triage"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
|
||||
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -5,11 +5,13 @@
|
||||
## Related Issue
|
||||
|
||||
<!-- Link to the issue this PR addresses using the syntax: Fixes #issue_number -->
|
||||
|
||||
Fixes #
|
||||
|
||||
## Type of Change
|
||||
|
||||
<!-- Mark the appropriate option with an "x" -->
|
||||
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
@ -21,6 +23,7 @@ Fixes #
|
||||
## Checklist
|
||||
|
||||
<!-- Mark the items you've completed with an "x" -->
|
||||
|
||||
- [ ] My code follows the style guidelines of this project
|
||||
- [ ] I have performed a self-review of my code
|
||||
- [ ] I have added comments to complex logic
|
||||
@ -42,6 +45,7 @@ Fixes #
|
||||
## Testing Instructions
|
||||
|
||||
<!-- Provide steps to test the changes, if applicable -->
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
98
.github/dependabot.yml
vendored
98
.github/dependabot.yml
vendored
@ -1,86 +1,86 @@
|
||||
version: 2
|
||||
updates:
|
||||
# Check for updates to GitHub Actions
|
||||
- package-ecosystem: 'github-actions'
|
||||
directory: '/'
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: 'weekly'
|
||||
day: 'monday'
|
||||
time: '07:00'
|
||||
timezone: 'Europe/Amsterdam'
|
||||
interval: "weekly"
|
||||
day: "monday"
|
||||
time: "07:00"
|
||||
timezone: "Europe/Amsterdam"
|
||||
open-pull-requests-limit: 2
|
||||
labels:
|
||||
- 'dependencies'
|
||||
- 'dependencies/github-actions'
|
||||
- "dependencies"
|
||||
- "dependencies/github-actions"
|
||||
commit-message:
|
||||
prefix: 'ci'
|
||||
include: 'scope'
|
||||
prefix: "ci"
|
||||
include: "scope"
|
||||
|
||||
# Check for updates to Docker
|
||||
- package-ecosystem: 'docker'
|
||||
directory: '/'
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: 'weekly'
|
||||
day: 'monday'
|
||||
time: '07:00'
|
||||
timezone: 'Europe/Amsterdam'
|
||||
interval: "weekly"
|
||||
day: "monday"
|
||||
time: "07:00"
|
||||
timezone: "Europe/Amsterdam"
|
||||
open-pull-requests-limit: 2
|
||||
labels:
|
||||
- 'dependencies'
|
||||
- 'dependencies/docker'
|
||||
- "dependencies"
|
||||
- "dependencies/docker"
|
||||
commit-message:
|
||||
prefix: 'docker'
|
||||
include: 'scope'
|
||||
prefix: "docker"
|
||||
include: "scope"
|
||||
groups:
|
||||
docker:
|
||||
patterns:
|
||||
- '*'
|
||||
- "*"
|
||||
update-types:
|
||||
- 'minor'
|
||||
- 'patch'
|
||||
- "minor"
|
||||
- "patch"
|
||||
|
||||
# Check for updates to Docker Compose
|
||||
- package-ecosystem: 'docker-compose'
|
||||
directory: '/'
|
||||
- package-ecosystem: "docker-compose"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: 'weekly'
|
||||
day: 'monday'
|
||||
time: '07:00'
|
||||
timezone: 'Europe/Amsterdam'
|
||||
interval: "weekly"
|
||||
day: "monday"
|
||||
time: "07:00"
|
||||
timezone: "Europe/Amsterdam"
|
||||
open-pull-requests-limit: 2
|
||||
labels:
|
||||
- 'dependencies'
|
||||
- 'dependencies/docker-compose'
|
||||
- "dependencies"
|
||||
- "dependencies/docker-compose"
|
||||
commit-message:
|
||||
prefix: 'docker'
|
||||
include: 'scope'
|
||||
prefix: "docker"
|
||||
include: "scope"
|
||||
groups:
|
||||
docker:
|
||||
patterns:
|
||||
- '*'
|
||||
- "*"
|
||||
update-types:
|
||||
- 'minor'
|
||||
- 'patch'
|
||||
- "minor"
|
||||
- "patch"
|
||||
|
||||
# Check for updates to Go modules
|
||||
- package-ecosystem: 'gomod'
|
||||
directory: '/'
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: 'weekly'
|
||||
day: 'monday'
|
||||
time: '07:00'
|
||||
timezone: 'Europe/Amsterdam'
|
||||
interval: "weekly"
|
||||
day: "monday"
|
||||
time: "07:00"
|
||||
timezone: "Europe/Amsterdam"
|
||||
open-pull-requests-limit: 2
|
||||
labels:
|
||||
- 'dependencies'
|
||||
- 'dependencies/go'
|
||||
- "dependencies"
|
||||
- "dependencies/go"
|
||||
commit-message:
|
||||
prefix: 'deps'
|
||||
include: 'scope'
|
||||
prefix: "deps"
|
||||
include: "scope"
|
||||
groups:
|
||||
go-modules:
|
||||
patterns:
|
||||
- '*'
|
||||
- "*"
|
||||
update-types:
|
||||
- 'minor'
|
||||
- 'patch'
|
||||
- "minor"
|
||||
- "patch"
|
||||
|
||||
105
.github/workflows/ci.yml
vendored
105
.github/workflows/ci.yml
vendored
@ -64,14 +64,18 @@ jobs:
|
||||
|
||||
- name: Run tests with enhanced reporting
|
||||
id: test
|
||||
env:
|
||||
CGO_ENABLED: 1
|
||||
run: |
|
||||
cat >> $GITHUB_STEP_SUMMARY << EOF
|
||||
{
|
||||
cat << EOF
|
||||
## 🔧 Test Environment
|
||||
- **Go Version:** ${{ matrix.go }}
|
||||
- **OS:** ubuntu-latest
|
||||
- **Timestamp:** $(date -u)
|
||||
|
||||
EOF
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
echo "Running tests with coverage..."
|
||||
task test:coverage 2>&1 | tee test-output.log
|
||||
@ -84,16 +88,17 @@ jobs:
|
||||
SKIPPED_TESTS=$(grep -c "--- SKIP:" test-output.log || echo "0")
|
||||
|
||||
# Generate test summary
|
||||
cat >> $GITHUB_STEP_SUMMARY << EOF
|
||||
{
|
||||
cat << EOF
|
||||
## 🧪 Test Results (Go ${{ matrix.go }})
|
||||
|
||||
| Metric | Value |
|
||||
| ----------- | ----------------------------------------------------------- |
|
||||
| ----------- | ------------------------------------------------------------- |
|
||||
| Total Tests | $TOTAL_TESTS |
|
||||
| Passed | $PASSED_TESTS |
|
||||
| Failed | $FAILED_TESTS |
|
||||
| Skipped | $SKIPPED_TESTS |
|
||||
| Status | $([ $TEST_STATUS -eq 0 ] && echo "PASSED" || echo "FAILED") |
|
||||
| Status | $([ "$TEST_STATUS" -eq 0 ] && echo "PASSED" || echo "FAILED") |
|
||||
|
||||
### 📦 Package Test Results
|
||||
|
||||
@ -105,38 +110,39 @@ jobs:
|
||||
grep "^ok\|^FAIL" test-output.log | while read -r line; do
|
||||
if [[ $line == ok* ]]; then
|
||||
pkg=$(echo "$line" | awk '{print $2}')
|
||||
echo "| $pkg | ✅ PASS |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| $pkg | ✅ PASS |"
|
||||
elif [[ $line == FAIL* ]]; then
|
||||
pkg=$(echo "$line" | awk '{print $2}')
|
||||
echo "| $pkg | ❌ FAIL |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| $pkg | ❌ FAIL |"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo ""
|
||||
|
||||
# Add detailed results if tests failed
|
||||
if [ $TEST_STATUS -ne 0 ]; then
|
||||
cat >> $GITHUB_STEP_SUMMARY << 'EOF'
|
||||
if [ "$TEST_STATUS" -ne 0 ]; then
|
||||
cat << 'EOF'
|
||||
### ❌ Failed Tests Details
|
||||
```
|
||||
EOF
|
||||
grep -A 10 "--- FAIL:" test-output.log | head -100 >> $GITHUB_STEP_SUMMARY
|
||||
cat >> $GITHUB_STEP_SUMMARY << 'EOF'
|
||||
grep -A 10 -- "--- FAIL:" test-output.log | head -100
|
||||
cat << 'EOF'
|
||||
```
|
||||
|
||||
EOF
|
||||
fi
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
# Set outputs for other steps
|
||||
cat >> $GITHUB_OUTPUT << EOF
|
||||
test-status=$TEST_STATUS
|
||||
total-tests=$TOTAL_TESTS
|
||||
passed-tests=$PASSED_TESTS
|
||||
failed-tests=$FAILED_TESTS
|
||||
EOF
|
||||
{
|
||||
echo "test-status=$TEST_STATUS"
|
||||
echo "total-tests=$TOTAL_TESTS"
|
||||
echo "passed-tests=$PASSED_TESTS"
|
||||
echo "failed-tests=$FAILED_TESTS"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
# Exit with the original test status
|
||||
exit $TEST_STATUS
|
||||
exit "$TEST_STATUS"
|
||||
|
||||
- name: Generate coverage report
|
||||
if: always()
|
||||
@ -144,7 +150,8 @@ jobs:
|
||||
if [ -f coverage/coverage.out ]; then
|
||||
COVERAGE=$(go tool cover -func=coverage/coverage.out | grep total | awk '{print $3}')
|
||||
|
||||
cat >> $GITHUB_STEP_SUMMARY << EOF
|
||||
{
|
||||
cat << EOF
|
||||
## 📊 Code Coverage (Go ${{ matrix.go }})
|
||||
|
||||
**Total Coverage: $COVERAGE**
|
||||
@ -184,17 +191,18 @@ jobs:
|
||||
avg = packages[pkg] / counts[pkg]
|
||||
printf "| %s | %.1f%% |\n", pkg, avg
|
||||
}
|
||||
}' "$temp_coverage" | sort >> $GITHUB_STEP_SUMMARY
|
||||
}' "$temp_coverage" | sort
|
||||
|
||||
rm -f "$temp_coverage"
|
||||
|
||||
cat >> $GITHUB_STEP_SUMMARY << 'EOF'
|
||||
cat << 'EOF'
|
||||
|
||||
</details>
|
||||
|
||||
EOF
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
else
|
||||
cat >> $GITHUB_STEP_SUMMARY << 'EOF'
|
||||
cat >> "$GITHUB_STEP_SUMMARY" << 'EOF'
|
||||
## ⚠️ Coverage Report
|
||||
No coverage file generated
|
||||
|
||||
@ -213,8 +221,8 @@ jobs:
|
||||
|
||||
- name: Run linters
|
||||
run: |
|
||||
# Initialize summary
|
||||
cat >> $GITHUB_STEP_SUMMARY << EOF
|
||||
{
|
||||
cat << EOF
|
||||
## 🔍 Static Analysis (Go ${{ matrix.go }})
|
||||
|
||||
EOF
|
||||
@ -223,42 +231,43 @@ jobs:
|
||||
VET_OUTPUT=$(task lint:vet 2>&1 || echo "")
|
||||
VET_STATUS=$?
|
||||
|
||||
if [ $VET_STATUS -eq 0 ]; then
|
||||
echo "✅ **go vet:** No issues found" >> $GITHUB_STEP_SUMMARY
|
||||
if [ "$VET_STATUS" -eq 0 ]; then
|
||||
echo "✅ **go vet:** No issues found"
|
||||
else
|
||||
cat >> $GITHUB_STEP_SUMMARY << 'EOF'
|
||||
cat << 'EOF'
|
||||
❌ **go vet:** Issues found
|
||||
|
||||
```
|
||||
EOF
|
||||
echo "$VET_OUTPUT" >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
echo "$VET_OUTPUT"
|
||||
echo '```'
|
||||
fi
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo ""
|
||||
|
||||
# Run go fmt check
|
||||
FMT_OUTPUT=$(task lint:fmt 2>&1 || echo "")
|
||||
FMT_STATUS=$?
|
||||
|
||||
if [ $FMT_STATUS -eq 0 ]; then
|
||||
echo "✅ **go fmt:** All files properly formatted" >> $GITHUB_STEP_SUMMARY
|
||||
if [ "$FMT_STATUS" -eq 0 ]; then
|
||||
echo "✅ **go fmt:** All files properly formatted"
|
||||
else
|
||||
cat >> $GITHUB_STEP_SUMMARY << 'EOF'
|
||||
cat << 'EOF'
|
||||
❌ **go fmt:** Files need formatting
|
||||
|
||||
```
|
||||
EOF
|
||||
echo "$FMT_OUTPUT" >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
echo "$FMT_OUTPUT"
|
||||
echo '```'
|
||||
fi
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
# Exit with error if any linter failed
|
||||
[ $VET_STATUS -eq 0 ] && [ $FMT_STATUS -eq 0 ] || exit 1
|
||||
[ "$VET_STATUS" -eq 0 ] && [ "$FMT_STATUS" -eq 0 ] || exit 1
|
||||
|
||||
- name: Job Summary
|
||||
if: always()
|
||||
run: |
|
||||
cat >> $GITHUB_STEP_SUMMARY << 'EOF'
|
||||
cat >> "$GITHUB_STEP_SUMMARY" << 'EOF'
|
||||
## 📋 Job Summary (Go ${{ matrix.go }})
|
||||
|
||||
| Step | Status |
|
||||
@ -313,7 +322,8 @@ jobs:
|
||||
|
||||
- name: Test Docker image using Task
|
||||
run: |
|
||||
cat >> $GITHUB_STEP_SUMMARY << 'EOF'
|
||||
{
|
||||
cat << 'EOF'
|
||||
## 🧪 Docker Image Tests
|
||||
|
||||
EOF
|
||||
@ -321,16 +331,17 @@ jobs:
|
||||
# Run Task docker test
|
||||
task docker:test
|
||||
|
||||
echo "**Testing help command:**" >> $GITHUB_STEP_SUMMARY
|
||||
echo '```terminaloutput' >> $GITHUB_STEP_SUMMARY
|
||||
docker run --rm articulate-parser:latest --help >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Testing help command:**"
|
||||
echo '```terminaloutput'
|
||||
docker run --rm articulate-parser:latest --help
|
||||
echo '```'
|
||||
echo ""
|
||||
|
||||
# Test image size
|
||||
IMAGE_SIZE=$(docker image inspect articulate-parser:latest --format='{{.Size}}' | numfmt --to=iec-i --suffix=B)
|
||||
echo "**Image size:** $IMAGE_SIZE" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Image size:** $IMAGE_SIZE"
|
||||
echo ""
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
dependency-review:
|
||||
name: Dependency Review
|
||||
@ -354,7 +365,7 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
needs: [test, docker-test, dependency-review]
|
||||
needs: [test, docker-test]
|
||||
if: |
|
||||
github.event_name == 'push' && (github.ref == 'refs/heads/master' ||
|
||||
github.ref == 'refs/heads/develop' ||
|
||||
@ -433,7 +444,7 @@ jobs:
|
||||
|
||||
- name: Generate Docker summary
|
||||
run: |
|
||||
cat >> $GITHUB_STEP_SUMMARY << 'EOF'
|
||||
cat >> "$GITHUB_STEP_SUMMARY" << 'EOF'
|
||||
## 🐳 Docker Build Summary
|
||||
|
||||
**Image:** `ghcr.io/${{ github.repository }}`
|
||||
|
||||
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
@ -17,7 +17,7 @@ name: "CodeQL"
|
||||
on:
|
||||
workflow_call:
|
||||
schedule:
|
||||
- cron: '44 16 * * 6'
|
||||
- cron: "44 16 * * 6"
|
||||
# push:
|
||||
# branches: [ "master" ]
|
||||
# pull_request:
|
||||
|
||||
4
.github/workflows/dependency-review.yml
vendored
4
.github/workflows/dependency-review.yml
vendored
@ -16,10 +16,10 @@ jobs:
|
||||
dependency-review:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 'Checkout Repository'
|
||||
- name: "Checkout Repository"
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: 'Dependency Review'
|
||||
- name: "Dependency Review"
|
||||
uses: actions/dependency-review-action@v4
|
||||
with:
|
||||
fail-on-severity: moderate
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@ -82,7 +82,7 @@ jobs:
|
||||
docker:
|
||||
name: Docker Build & Push
|
||||
runs-on: ubuntu-latest
|
||||
needs: ['release']
|
||||
needs: ["release"]
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
@ -349,7 +349,15 @@ linters:
|
||||
|
||||
# stylecheck settings
|
||||
staticcheck:
|
||||
checks: ["all", "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022"]
|
||||
checks: [
|
||||
"all",
|
||||
"-ST1000",
|
||||
"-ST1003",
|
||||
"-ST1016",
|
||||
"-ST1020",
|
||||
"-ST1021",
|
||||
"-ST1022",
|
||||
]
|
||||
|
||||
# maintidx settings
|
||||
maintidx:
|
||||
|
||||
75
.pre-commit-config.yaml
Normal file
75
.pre-commit-config.yaml
Normal file
@ -0,0 +1,75 @@
|
||||
# See https://pre-commit.com for more information
|
||||
# See https://pre-commit.com/hooks.html for more hooks
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v6.0.0
|
||||
hooks:
|
||||
# File quality
|
||||
- id: trailing-whitespace
|
||||
exclude: '^\.github/ISSUE_TEMPLATE/.*\.yml$'
|
||||
- id: end-of-file-fixer
|
||||
- id: mixed-line-ending
|
||||
args: ["--fix=lf"]
|
||||
|
||||
# File validation
|
||||
- id: check-yaml
|
||||
- id: check-json
|
||||
- id: check-toml
|
||||
|
||||
# Security
|
||||
- id: detect-private-key
|
||||
|
||||
# Git safety
|
||||
- id: check-merge-conflict
|
||||
- id: check-case-conflict
|
||||
- id: no-commit-to-branch
|
||||
args: ["--branch=master", "--branch=main"]
|
||||
|
||||
# File structure
|
||||
- id: check-added-large-files
|
||||
- id: check-symlinks
|
||||
- id: check-executables-have-shebangs
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: actionlint
|
||||
name: Lint GitHub Actions workflow files
|
||||
description: Runs actionlint to lint GitHub Actions workflow files
|
||||
language: golang
|
||||
types: ["yaml"]
|
||||
files: ^\.github/workflows/
|
||||
entry: actionlint
|
||||
minimum_pre_commit_version: 3.0.0
|
||||
- repo: https://github.com/golangci/golangci-lint
|
||||
rev: v2.7.2
|
||||
hooks:
|
||||
- id: golangci-lint
|
||||
name: golangci-lint
|
||||
description: Fast linters runner for Go. Note that only modified files are linted, so linters like 'unused' that need to scan all files won't work as expected.
|
||||
entry: golangci-lint run --new-from-rev HEAD --fix
|
||||
types: [go]
|
||||
language: golang
|
||||
require_serial: true
|
||||
pass_filenames: false
|
||||
# - id: golangci-lint-full
|
||||
# name: golangci-lint-full
|
||||
# description: Fast linters runner for Go. Runs on all files in the module. Use this hook if you use pre-commit in CI.
|
||||
# entry: golangci-lint run --fix
|
||||
# types: [go]
|
||||
# language: golang
|
||||
# require_serial: true
|
||||
# pass_filenames: false
|
||||
- id: golangci-lint-fmt
|
||||
name: golangci-lint-fmt
|
||||
description: Fast linters runner for Go. Formats all files in the repo.
|
||||
entry: golangci-lint fmt
|
||||
types: [go]
|
||||
language: golang
|
||||
require_serial: true
|
||||
pass_filenames: false
|
||||
- id: golangci-lint-config-verify
|
||||
name: golangci-lint-config-verify
|
||||
description: Verifies the configuration file
|
||||
entry: golangci-lint config verify
|
||||
files: '\.golangci\.(?:yml|yaml|toml|json)'
|
||||
language: golang
|
||||
pass_filenames: false
|
||||
@ -2,6 +2,11 @@
|
||||
|
||||
A Go CLI tool that parses Articulate Rise courses from URLs or local JSON files and exports them to Markdown, HTML, or DOCX formats.
|
||||
|
||||
## Repository Info
|
||||
|
||||
- **GitHub**: https://github.com/kjanat/articulate-parser
|
||||
- **Default branch**: `master` (not `main`)
|
||||
|
||||
## Build/Test Commands
|
||||
|
||||
### Primary Commands (using Taskfile)
|
||||
|
||||
@ -226,7 +226,7 @@ docker run --rm ghcr.io/kjanat/articulate-parser:latest --help
|
||||
### Available Tags
|
||||
|
||||
| Tag | Description | Use Case |
|
||||
|-----|-------------|----------|
|
||||
| --------------------- | ------------------------------------------- | ---------------------- |
|
||||
| `latest` | Latest stable release from master branch | Production use |
|
||||
| `edge` | Latest development build from master branch | Testing new features |
|
||||
| `v1.x.x` | Specific version releases | Production pinning |
|
||||
@ -314,7 +314,7 @@ docker build --build-arg VERSION=local --build-arg BUILD_TIME=$(date -u +%Y-%m-%
|
||||
The Docker image supports the following build-time arguments:
|
||||
|
||||
| Argument | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| ------------ | ------------------------------------- | -------------- |
|
||||
| `VERSION` | Version string embedded in the binary | `dev` |
|
||||
| `BUILD_TIME` | Build timestamp | Current time |
|
||||
| `GIT_COMMIT` | Git commit hash | Current commit |
|
||||
@ -460,7 +460,9 @@ This is a utility tool for educational content conversion. Please ensure you hav
|
||||
[Go report]: https://goreportcard.com/report/github.com/kjanat/articulate-parser
|
||||
[gomod]: go.mod
|
||||
[Issues]: https://github.com/kjanat/articulate-parser/issues
|
||||
|
||||
<!-- [Latest release]: https://github.com/kjanat/articulate-parser/releases/latest -->
|
||||
|
||||
[MIT License]: LICENSE
|
||||
[Package documentation]: https://godoc.org/github.com/kjanat/articulate-parser
|
||||
[Tags]: https://github.com/kjanat/articulate-parser/tags
|
||||
|
||||
@ -16,6 +16,13 @@ import (
|
||||
"github.com/kjanat/articulate-parser/internal/services"
|
||||
)
|
||||
|
||||
// Font sizes for DOCX document headings (in half-points, so "32" = 16pt).
|
||||
const (
|
||||
docxTitleSize = "32" // Course title (16pt)
|
||||
docxLessonSize = "28" // Lesson heading (14pt)
|
||||
docxItemSize = "24" // Item heading (12pt)
|
||||
)
|
||||
|
||||
// DocxExporter implements the Exporter interface for DOCX format.
|
||||
// It converts Articulate Rise course data into a Microsoft Word document
|
||||
// using the go-docx package.
|
||||
@ -53,7 +60,7 @@ func (e *DocxExporter) Export(course *models.Course, outputPath string) error {
|
||||
|
||||
// Add title
|
||||
titlePara := doc.AddParagraph()
|
||||
titlePara.AddText(course.Course.Title).Size("32").Bold()
|
||||
titlePara.AddText(course.Course.Title).Size(docxTitleSize).Bold()
|
||||
|
||||
// Add description if available
|
||||
if course.Course.Description != "" {
|
||||
@ -106,7 +113,7 @@ func (e *DocxExporter) Export(course *models.Course, outputPath string) error {
|
||||
func (e *DocxExporter) exportLesson(doc *docx.Docx, lesson *models.Lesson) {
|
||||
// Add lesson title
|
||||
lessonPara := doc.AddParagraph()
|
||||
lessonPara.AddText(fmt.Sprintf("Lesson: %s", lesson.Title)).Size("28").Bold()
|
||||
lessonPara.AddText(fmt.Sprintf("Lesson: %s", lesson.Title)).Size(docxLessonSize).Bold()
|
||||
|
||||
// Add lesson description if available
|
||||
if lesson.Description != "" {
|
||||
@ -132,7 +139,7 @@ func (e *DocxExporter) exportItem(doc *docx.Docx, item *models.Item) {
|
||||
if item.Type != "" {
|
||||
itemPara := doc.AddParagraph()
|
||||
caser := cases.Title(language.English)
|
||||
itemPara.AddText(caser.String(item.Type)).Size("24").Bold()
|
||||
itemPara.AddText(caser.String(item.Type)).Size(docxItemSize).Bold()
|
||||
}
|
||||
|
||||
// Add sub-items
|
||||
|
||||
@ -15,7 +15,7 @@ import (
|
||||
//go:embed html_styles.css
|
||||
var defaultCSS string
|
||||
|
||||
//go:embed html_template.html
|
||||
//go:embed html_template.gohtml
|
||||
var htmlTemplate string
|
||||
|
||||
// HTMLExporter implements the Exporter interface for HTML format.
|
||||
@ -69,7 +69,16 @@ func (e *HTMLExporter) Export(course *models.Course, outputPath string) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create file: %w", err)
|
||||
}
|
||||
defer f.Close()
|
||||
defer func() {
|
||||
// Close errors are logged but not fatal since the content has already been written.
|
||||
// The file must be closed to flush buffers, but a close error doesn't invalidate
|
||||
// the data already written to disk.
|
||||
if closeErr := f.Close(); closeErr != nil {
|
||||
// Note: In production, this should log via a logger passed to the exporter.
|
||||
// For now, we silently ignore close errors as they're non-fatal.
|
||||
_ = closeErr
|
||||
}
|
||||
}()
|
||||
|
||||
return e.WriteHTML(f, course)
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||
font-family:
|
||||
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu,
|
||||
Cantarell, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
max-width: 800px;
|
||||
|
||||
@ -96,7 +96,10 @@ func (e *MarkdownExporter) SupportedFormat() string {
|
||||
func (e *MarkdownExporter) processItemToMarkdown(buf *bytes.Buffer, item models.Item, level int) {
|
||||
headingPrefix := strings.Repeat("#", level)
|
||||
|
||||
switch item.Type {
|
||||
// Normalize item type to lowercase for consistent matching
|
||||
itemType := strings.ToLower(item.Type)
|
||||
|
||||
switch itemType {
|
||||
case "text":
|
||||
e.processTextItem(buf, item, headingPrefix)
|
||||
case "list":
|
||||
@ -105,7 +108,7 @@ func (e *MarkdownExporter) processItemToMarkdown(buf *bytes.Buffer, item models.
|
||||
e.processMultimediaItem(buf, item, headingPrefix)
|
||||
case "image":
|
||||
e.processImageItem(buf, item, headingPrefix)
|
||||
case "knowledgeCheck":
|
||||
case "knowledgecheck":
|
||||
e.processKnowledgeCheckItem(buf, item, headingPrefix)
|
||||
case "interactive":
|
||||
e.processInteractiveItem(buf, item, headingPrefix)
|
||||
|
||||
Binary file not shown.
@ -15,6 +15,9 @@ import (
|
||||
"github.com/kjanat/articulate-parser/internal/models"
|
||||
)
|
||||
|
||||
// shareIDRegex is compiled once at package init for extracting share IDs from URIs.
|
||||
var shareIDRegex = regexp.MustCompile(`/share/([a-zA-Z0-9_-]+)`)
|
||||
|
||||
// ArticulateParser implements the CourseParser interface specifically for Articulate Rise courses.
|
||||
// It can fetch courses from the Articulate Rise API or load them from local JSON files.
|
||||
type ArticulateParser struct {
|
||||
@ -78,15 +81,15 @@ func (p *ArticulateParser) FetchCourse(ctx context.Context, uri string) (*models
|
||||
}
|
||||
}()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("API returned status %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read response body: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("API returned status %d: %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
var course models.Course
|
||||
if err := json.Unmarshal(body, &course); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal JSON: %w", err)
|
||||
@ -133,8 +136,7 @@ func (p *ArticulateParser) extractShareID(uri string) (string, error) {
|
||||
return "", fmt.Errorf("invalid domain for Articulate Rise URI: %s", parsedURL.Host)
|
||||
}
|
||||
|
||||
re := regexp.MustCompile(`/share/([a-zA-Z0-9_-]+)`)
|
||||
matches := re.FindStringSubmatch(uri)
|
||||
matches := shareIDRegex.FindStringSubmatch(uri)
|
||||
if len(matches) < 2 {
|
||||
return "", fmt.Errorf("could not extract share ID from URI: %s", uri)
|
||||
}
|
||||
|
||||
2
main.go
2
main.go
@ -92,7 +92,7 @@ func run(args []string) int {
|
||||
// Returns:
|
||||
// - true if the string appears to be a URI, false otherwise
|
||||
func isURI(str string) bool {
|
||||
return len(str) > 7 && (str[:7] == "http://" || str[:8] == "https://")
|
||||
return strings.HasPrefix(str, "http://") || strings.HasPrefix(str, "https://")
|
||||
}
|
||||
|
||||
// printUsage prints the command-line usage information.
|
||||
|
||||
Reference in New Issue
Block a user