mirror of
https://github.com/kjanat/articulate-parser.git
synced 2026-01-16 18:22:08 +01:00
Compare commits
3 Commits
94a7924bed
...
remove-ci-
| Author | SHA1 | Date | |
|---|---|---|---|
| a1a49a75b7 | |||
| 8d606706e2 | |||
| e7de5d044a |
@ -1,42 +0,0 @@
|
|||||||
{
|
|
||||||
"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",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
22
.github/CONTRIBUTING.md
vendored
22
.github/CONTRIBUTING.md
vendored
@ -45,23 +45,23 @@ Enhancement suggestions are welcome! Please use the feature request template and
|
|||||||
|
|
||||||
2. **Clone and setup:**
|
2. **Clone and setup:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/your-username/articulate-parser.git
|
git clone https://github.com/your-username/articulate-parser.git
|
||||||
cd articulate-parser
|
cd articulate-parser
|
||||||
go mod download
|
go mod download
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **Run tests:**
|
3. **Run tests:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
go test -v ./...
|
go test -v ./...
|
||||||
```
|
```
|
||||||
|
|
||||||
4. **Build:**
|
4. **Build:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
go build main.go
|
go build main.go
|
||||||
```
|
```
|
||||||
|
|
||||||
## Coding Standards
|
## Coding Standards
|
||||||
|
|
||||||
|
|||||||
4
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
4
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -1,7 +1,7 @@
|
|||||||
name: Bug Report
|
name: Bug Report
|
||||||
description: Create a report to help us improve
|
description: Create a report to help us improve
|
||||||
title: "[BUG] "
|
title: '[BUG] '
|
||||||
labels: ["bug", "triage"]
|
labels: ['bug', 'triage']
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
|
|||||||
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -5,13 +5,11 @@
|
|||||||
## Related Issue
|
## Related Issue
|
||||||
|
|
||||||
<!-- Link to the issue this PR addresses using the syntax: Fixes #issue_number -->
|
<!-- Link to the issue this PR addresses using the syntax: Fixes #issue_number -->
|
||||||
|
|
||||||
Fixes #
|
Fixes #
|
||||||
|
|
||||||
## Type of Change
|
## Type of Change
|
||||||
|
|
||||||
<!-- Mark the appropriate option with an "x" -->
|
<!-- Mark the appropriate option with an "x" -->
|
||||||
|
|
||||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||||
- [ ] New feature (non-breaking change which adds functionality)
|
- [ ] New feature (non-breaking change which adds functionality)
|
||||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||||
@ -23,7 +21,6 @@ Fixes #
|
|||||||
## Checklist
|
## Checklist
|
||||||
|
|
||||||
<!-- Mark the items you've completed with an "x" -->
|
<!-- Mark the items you've completed with an "x" -->
|
||||||
|
|
||||||
- [ ] My code follows the style guidelines of this project
|
- [ ] My code follows the style guidelines of this project
|
||||||
- [ ] I have performed a self-review of my code
|
- [ ] I have performed a self-review of my code
|
||||||
- [ ] I have added comments to complex logic
|
- [ ] I have added comments to complex logic
|
||||||
@ -45,7 +42,6 @@ Fixes #
|
|||||||
## Testing Instructions
|
## Testing Instructions
|
||||||
|
|
||||||
<!-- Provide steps to test the changes, if applicable -->
|
<!-- Provide steps to test the changes, if applicable -->
|
||||||
|
|
||||||
1.
|
1.
|
||||||
2.
|
2.
|
||||||
3.
|
3.
|
||||||
|
|||||||
98
.github/dependabot.yml
vendored
98
.github/dependabot.yml
vendored
@ -1,86 +1,86 @@
|
|||||||
version: 2
|
version: 2
|
||||||
updates:
|
updates:
|
||||||
# Check for updates to GitHub Actions
|
# Check for updates to GitHub Actions
|
||||||
- package-ecosystem: "github-actions"
|
- package-ecosystem: 'github-actions'
|
||||||
directory: "/"
|
directory: '/'
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: 'weekly'
|
||||||
day: "monday"
|
day: 'monday'
|
||||||
time: "07:00"
|
time: '07:00'
|
||||||
timezone: "Europe/Amsterdam"
|
timezone: 'Europe/Amsterdam'
|
||||||
open-pull-requests-limit: 2
|
open-pull-requests-limit: 2
|
||||||
labels:
|
labels:
|
||||||
- "dependencies"
|
- 'dependencies'
|
||||||
- "dependencies/github-actions"
|
- 'dependencies/github-actions'
|
||||||
commit-message:
|
commit-message:
|
||||||
prefix: "ci"
|
prefix: 'ci'
|
||||||
include: "scope"
|
include: 'scope'
|
||||||
|
|
||||||
# Check for updates to Docker
|
# Check for updates to Docker
|
||||||
- package-ecosystem: "docker"
|
- package-ecosystem: 'docker'
|
||||||
directory: "/"
|
directory: '/'
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: 'weekly'
|
||||||
day: "monday"
|
day: 'monday'
|
||||||
time: "07:00"
|
time: '07:00'
|
||||||
timezone: "Europe/Amsterdam"
|
timezone: 'Europe/Amsterdam'
|
||||||
open-pull-requests-limit: 2
|
open-pull-requests-limit: 2
|
||||||
labels:
|
labels:
|
||||||
- "dependencies"
|
- 'dependencies'
|
||||||
- "dependencies/docker"
|
- 'dependencies/docker'
|
||||||
commit-message:
|
commit-message:
|
||||||
prefix: "docker"
|
prefix: 'docker'
|
||||||
include: "scope"
|
include: 'scope'
|
||||||
groups:
|
groups:
|
||||||
docker:
|
docker:
|
||||||
patterns:
|
patterns:
|
||||||
- "*"
|
- '*'
|
||||||
update-types:
|
update-types:
|
||||||
- "minor"
|
- 'minor'
|
||||||
- "patch"
|
- 'patch'
|
||||||
|
|
||||||
# Check for updates to Docker Compose
|
# Check for updates to Docker Compose
|
||||||
- package-ecosystem: "docker-compose"
|
- package-ecosystem: 'docker-compose'
|
||||||
directory: "/"
|
directory: '/'
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: 'weekly'
|
||||||
day: "monday"
|
day: 'monday'
|
||||||
time: "07:00"
|
time: '07:00'
|
||||||
timezone: "Europe/Amsterdam"
|
timezone: 'Europe/Amsterdam'
|
||||||
open-pull-requests-limit: 2
|
open-pull-requests-limit: 2
|
||||||
labels:
|
labels:
|
||||||
- "dependencies"
|
- 'dependencies'
|
||||||
- "dependencies/docker-compose"
|
- 'dependencies/docker-compose'
|
||||||
commit-message:
|
commit-message:
|
||||||
prefix: "docker"
|
prefix: 'docker'
|
||||||
include: "scope"
|
include: 'scope'
|
||||||
groups:
|
groups:
|
||||||
docker:
|
docker:
|
||||||
patterns:
|
patterns:
|
||||||
- "*"
|
- '*'
|
||||||
update-types:
|
update-types:
|
||||||
- "minor"
|
- 'minor'
|
||||||
- "patch"
|
- 'patch'
|
||||||
|
|
||||||
# Check for updates to Go modules
|
# Check for updates to Go modules
|
||||||
- package-ecosystem: "gomod"
|
- package-ecosystem: 'gomod'
|
||||||
directory: "/"
|
directory: '/'
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: 'weekly'
|
||||||
day: "monday"
|
day: 'monday'
|
||||||
time: "07:00"
|
time: '07:00'
|
||||||
timezone: "Europe/Amsterdam"
|
timezone: 'Europe/Amsterdam'
|
||||||
open-pull-requests-limit: 2
|
open-pull-requests-limit: 2
|
||||||
labels:
|
labels:
|
||||||
- "dependencies"
|
- 'dependencies'
|
||||||
- "dependencies/go"
|
- 'dependencies/go'
|
||||||
commit-message:
|
commit-message:
|
||||||
prefix: "deps"
|
prefix: 'deps'
|
||||||
include: "scope"
|
include: 'scope'
|
||||||
groups:
|
groups:
|
||||||
go-modules:
|
go-modules:
|
||||||
patterns:
|
patterns:
|
||||||
- "*"
|
- '*'
|
||||||
update-types:
|
update-types:
|
||||||
- "minor"
|
- 'minor'
|
||||||
- "patch"
|
- 'patch'
|
||||||
|
|||||||
8
.github/workflows/autofix.yml
vendored
8
.github/workflows/autofix.yml
vendored
@ -2,7 +2,7 @@ name: autofix.ci
|
|||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
push:
|
push:
|
||||||
branches: ["master"]
|
branches: [ "master" ]
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
@ -11,18 +11,18 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Install Task
|
- name: Install Task
|
||||||
uses: go-task/setup-task@v1
|
uses: go-task/setup-task@v1
|
||||||
|
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v6
|
||||||
with: { go-version-file: "go.mod" }
|
with: { go-version-file: 'go.mod' }
|
||||||
|
|
||||||
- name: Setup go deps
|
- name: Setup go deps
|
||||||
run: |
|
run: |
|
||||||
# Install golangci-lint
|
# Install golangci-lint
|
||||||
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b $(go env GOPATH)/bin
|
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b "$(go env GOPATH)/bin"
|
||||||
|
|
||||||
# Install go-task dependencies
|
# Install go-task dependencies
|
||||||
go install golang.org/x/tools/cmd/goimports@latest
|
go install golang.org/x/tools/cmd/goimports@latest
|
||||||
|
|||||||
29
.github/workflows/ci.yml
vendored
29
.github/workflows/ci.yml
vendored
@ -2,7 +2,7 @@ name: CI
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ["master", "develop"]
|
branches: ['master', 'develop']
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
@ -21,12 +21,12 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
pull-requests: read
|
pull-requests: read
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v5
|
||||||
- uses: actions/setup-go@v6
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: stable
|
go-version: stable
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v9
|
uses: golangci/golangci-lint-action@v8
|
||||||
with: { version: latest }
|
with: { version: latest }
|
||||||
|
|
||||||
test:
|
test:
|
||||||
@ -42,7 +42,7 @@ jobs:
|
|||||||
- 1.25.x
|
- 1.25.x
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Set up Go ${{ matrix.go }}
|
- name: Set up Go ${{ matrix.go }}
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v6
|
||||||
@ -83,9 +83,9 @@ jobs:
|
|||||||
# Extract test results for summary
|
# Extract test results for summary
|
||||||
TEST_STATUS=$?
|
TEST_STATUS=$?
|
||||||
TOTAL_TESTS=$(grep -c "=== RUN" test-output.log || echo "0")
|
TOTAL_TESTS=$(grep -c "=== RUN" test-output.log || echo "0")
|
||||||
PASSED_TESTS=$(grep -c "--- PASS:" test-output.log || echo "0")
|
PASSED_TESTS=$(grep -c -- "--- PASS:" test-output.log || echo "0")
|
||||||
FAILED_TESTS=$(grep -c "--- FAIL:" test-output.log || echo "0")
|
FAILED_TESTS=$(grep -c -- "--- FAIL:" test-output.log || echo "0")
|
||||||
SKIPPED_TESTS=$(grep -c "--- SKIP:" test-output.log || echo "0")
|
SKIPPED_TESTS=$(grep -c -- "--- SKIP:" test-output.log || echo "0")
|
||||||
|
|
||||||
# Generate test summary
|
# Generate test summary
|
||||||
{
|
{
|
||||||
@ -211,7 +211,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload test artifacts
|
- name: Upload test artifacts
|
||||||
if: failure()
|
if: failure()
|
||||||
uses: actions/upload-artifact@v6
|
uses: actions/upload-artifact@v5
|
||||||
with:
|
with:
|
||||||
name: test-results-go-${{ matrix.go }}
|
name: test-results-go-${{ matrix.go }}
|
||||||
path: |
|
path: |
|
||||||
@ -221,6 +221,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Run linters
|
- name: Run linters
|
||||||
run: |
|
run: |
|
||||||
|
# Initialize summary
|
||||||
{
|
{
|
||||||
cat << EOF
|
cat << EOF
|
||||||
## 🔍 Static Analysis (Go ${{ matrix.go }})
|
## 🔍 Static Analysis (Go ${{ matrix.go }})
|
||||||
@ -303,7 +304,7 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v6
|
uses: actions/setup-go@v6
|
||||||
@ -350,10 +351,10 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout Repository"
|
- name: 'Checkout Repository'
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: "Dependency Review"
|
- name: 'Dependency Review'
|
||||||
uses: actions/dependency-review-action@v4
|
uses: actions/dependency-review-action@v4
|
||||||
with:
|
with:
|
||||||
fail-on-severity: moderate
|
fail-on-severity: moderate
|
||||||
@ -365,14 +366,14 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
needs: [test, docker-test]
|
needs: [test]
|
||||||
if: |
|
if: |
|
||||||
github.event_name == 'push' && (github.ref == 'refs/heads/master' ||
|
github.event_name == 'push' && (github.ref == 'refs/heads/master' ||
|
||||||
github.ref == 'refs/heads/develop' ||
|
github.ref == 'refs/heads/develop' ||
|
||||||
startsWith(github.ref, 'refs/heads/feature/docker'))
|
startsWith(github.ref, 'refs/heads/feature/docker'))
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
|
|||||||
4
.github/workflows/codeql.yml
vendored
4
.github/workflows/codeql.yml
vendored
@ -17,7 +17,7 @@ name: "CodeQL"
|
|||||||
on:
|
on:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "44 16 * * 6"
|
- cron: '44 16 * * 6'
|
||||||
# push:
|
# push:
|
||||||
# branches: [ "master" ]
|
# branches: [ "master" ]
|
||||||
# pull_request:
|
# pull_request:
|
||||||
@ -61,7 +61,7 @@ jobs:
|
|||||||
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
# Add any setup steps before running the `github/codeql-action/init` action.
|
# Add any setup steps before running the `github/codeql-action/init` action.
|
||||||
# This includes steps like installing compilers or runtimes (`actions/setup-node`
|
# This includes steps like installing compilers or runtimes (`actions/setup-node`
|
||||||
|
|||||||
6
.github/workflows/dependency-review.yml
vendored
6
.github/workflows/dependency-review.yml
vendored
@ -16,10 +16,10 @@ jobs:
|
|||||||
dependency-review:
|
dependency-review:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout Repository"
|
- name: 'Checkout Repository'
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: "Dependency Review"
|
- name: 'Dependency Review'
|
||||||
uses: actions/dependency-review-action@v4
|
uses: actions/dependency-review-action@v4
|
||||||
with:
|
with:
|
||||||
fail-on-severity: moderate
|
fail-on-severity: moderate
|
||||||
|
|||||||
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@ -20,7 +20,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
@ -82,13 +82,13 @@ jobs:
|
|||||||
docker:
|
docker:
|
||||||
name: Docker Build & Push
|
name: Docker Build & Push
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: ["release"]
|
needs: ['release']
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
|
|||||||
@ -349,15 +349,7 @@ linters:
|
|||||||
|
|
||||||
# stylecheck settings
|
# stylecheck settings
|
||||||
staticcheck:
|
staticcheck:
|
||||||
checks: [
|
checks: ["all", "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022"]
|
||||||
"all",
|
|
||||||
"-ST1000",
|
|
||||||
"-ST1003",
|
|
||||||
"-ST1016",
|
|
||||||
"-ST1020",
|
|
||||||
"-ST1021",
|
|
||||||
"-ST1022",
|
|
||||||
]
|
|
||||||
|
|
||||||
# maintidx settings
|
# maintidx settings
|
||||||
maintidx:
|
maintidx:
|
||||||
|
|||||||
@ -9,7 +9,7 @@ repos:
|
|||||||
exclude: '^\.github/ISSUE_TEMPLATE/.*\.yml$'
|
exclude: '^\.github/ISSUE_TEMPLATE/.*\.yml$'
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
- id: mixed-line-ending
|
- id: mixed-line-ending
|
||||||
args: ["--fix=lf"]
|
args: ['--fix=lf']
|
||||||
|
|
||||||
# File validation
|
# File validation
|
||||||
- id: check-yaml
|
- id: check-yaml
|
||||||
@ -23,7 +23,7 @@ repos:
|
|||||||
- id: check-merge-conflict
|
- id: check-merge-conflict
|
||||||
- id: check-case-conflict
|
- id: check-case-conflict
|
||||||
- id: no-commit-to-branch
|
- id: no-commit-to-branch
|
||||||
args: ["--branch=master", "--branch=main"]
|
args: ['--branch=master', '--branch=main']
|
||||||
|
|
||||||
# File structure
|
# File structure
|
||||||
- id: check-added-large-files
|
- id: check-added-large-files
|
||||||
@ -40,7 +40,7 @@ repos:
|
|||||||
entry: actionlint
|
entry: actionlint
|
||||||
minimum_pre_commit_version: 3.0.0
|
minimum_pre_commit_version: 3.0.0
|
||||||
- repo: https://github.com/golangci/golangci-lint
|
- repo: https://github.com/golangci/golangci-lint
|
||||||
rev: v2.7.2
|
rev: v2.6.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: golangci-lint
|
- id: golangci-lint
|
||||||
name: golangci-lint
|
name: golangci-lint
|
||||||
|
|||||||
502
AGENTS.md
502
AGENTS.md
@ -1,87 +1,36 @@
|
|||||||
# Agent Guidelines for articulate-parser
|
# Agent Guidelines for articulate-parser
|
||||||
|
|
||||||
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
|
## Build/Test Commands
|
||||||
|
|
||||||
### Primary Commands (using Taskfile)
|
- **Build**: `task build` or `go build -o bin/articulate-parser main.go`
|
||||||
|
- **Run tests**: `task test` or `go test -race -timeout 5m ./...`
|
||||||
```bash
|
- **Run single test**: `go test -v -race -run ^TestName$ ./path/to/package`
|
||||||
task build # Build binary to bin/articulate-parser
|
- **Test with coverage**:
|
||||||
task test # Run all tests with race detection
|
- `task test:coverage` or
|
||||||
task lint # Run all linters (vet, fmt, staticcheck, golangci-lint)
|
- `go test -race -coverprofile=coverage/coverage.out -covermode=atomic ./...`
|
||||||
task fmt # Format all Go files
|
- **Lint**: `task lint` (runs vet, fmt check, staticcheck, golangci-lint)
|
||||||
task ci # Full CI pipeline: deps, lint, test with coverage, build
|
- **Format**: `task fmt` or `gofmt -s -w .`
|
||||||
task qa # Quick QA: fmt + lint + test
|
- **CI checks**: `task ci` (deps, lint, test with coverage, build)
|
||||||
```
|
|
||||||
|
|
||||||
### Direct Go Commands
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Build
|
|
||||||
go build -o bin/articulate-parser main.go
|
|
||||||
|
|
||||||
# Run all tests
|
|
||||||
go test -race -timeout 5m ./...
|
|
||||||
|
|
||||||
# Run single test by name
|
|
||||||
go test -v -race -run ^TestMarkdownExporter_Export$ ./internal/exporters
|
|
||||||
|
|
||||||
# Run tests in specific package
|
|
||||||
go test -v -race ./internal/services
|
|
||||||
|
|
||||||
# Run tests matching pattern
|
|
||||||
go test -v -race -run "TestParser" ./...
|
|
||||||
|
|
||||||
# Test with coverage
|
|
||||||
go test -race -coverprofile=coverage/coverage.out -covermode=atomic ./...
|
|
||||||
go tool cover -html=coverage/coverage.out -o coverage/coverage.html
|
|
||||||
|
|
||||||
# Benchmarks
|
|
||||||
go test -bench=. -benchmem ./...
|
|
||||||
go test -bench=BenchmarkMarkdownExporter ./internal/exporters
|
|
||||||
```
|
|
||||||
|
|
||||||
### Security & Auditing
|
|
||||||
|
|
||||||
```bash
|
|
||||||
task security:check # Run gosec security scanner
|
|
||||||
task security:audit # Run govulncheck for vulnerabilities
|
|
||||||
```
|
|
||||||
|
|
||||||
## Code Style Guidelines
|
## Code Style Guidelines
|
||||||
|
|
||||||
### Imports
|
### Imports
|
||||||
|
|
||||||
- Use `goimports` with local prefix: `github.com/kjanat/articulate-parser`
|
- Use `goimports` with local prefix: `github.com/kjanat/articulate-parser`
|
||||||
- Order: stdlib, blank line, external packages, blank line, internal packages
|
- Order: stdlib, external, internal packages
|
||||||
|
- Group related imports together
|
||||||
```go
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/fumiama/go-docx"
|
|
||||||
|
|
||||||
"github.com/kjanat/articulate-parser/internal/interfaces"
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Formatting
|
### Formatting
|
||||||
|
|
||||||
- Use `gofmt -s` (simplify) and `gofumpt` with extra rules
|
- Use `gofmt -s` (simplify) and `gofumpt` with extra rules
|
||||||
- Function length: max 100 lines, 50 statements
|
- Function length: max 100 lines, 50 statements
|
||||||
- Cyclomatic complexity: max 15; Cognitive complexity: max 20
|
- Cyclomatic complexity: max 15
|
||||||
|
- Cognitive complexity: max 20
|
||||||
|
|
||||||
### Types & Naming
|
### Types & Naming
|
||||||
|
|
||||||
- Use interface-based design (see `internal/interfaces/`)
|
- Use interface-based design (see `internal/interfaces/`)
|
||||||
- Exported types/functions require godoc comments ending with period
|
- Export types/functions with clear godoc comments ending with period
|
||||||
- Use descriptive names: `ArticulateParser`, `MarkdownExporter`
|
- Use descriptive names: `ArticulateParser`, `MarkdownExporter`
|
||||||
- Receiver names: short (1-2 chars), consistent per type
|
- Receiver names: short (1-2 chars), consistent per type
|
||||||
|
|
||||||
@ -92,20 +41,6 @@ import (
|
|||||||
- Check all error returns (enforced by `errcheck`)
|
- Check all error returns (enforced by `errcheck`)
|
||||||
- Document error handling rationale in defer blocks when ignoring close errors
|
- Document error handling rationale in defer blocks when ignoring close errors
|
||||||
|
|
||||||
```go
|
|
||||||
// Good: Error wrapping with context
|
|
||||||
if err := json.Unmarshal(body, &course); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to unmarshal JSON: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Good: Documented defer with error handling
|
|
||||||
defer func() {
|
|
||||||
if err := resp.Body.Close(); err != nil {
|
|
||||||
p.Logger.Warn("failed to close response body", "error", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
```
|
|
||||||
|
|
||||||
### Comments
|
### Comments
|
||||||
|
|
||||||
- All exported types/functions require godoc comments
|
- All exported types/functions require godoc comments
|
||||||
@ -114,70 +49,379 @@ defer func() {
|
|||||||
|
|
||||||
### Security
|
### Security
|
||||||
|
|
||||||
- Use `#nosec` with justification for deliberate security exceptions
|
- Use `#nosec` with justification for deliberate security exceptions (G304 for CLI file paths, G306 for export file permissions)
|
||||||
- G304: File paths from CLI args; G306: Export file permissions
|
- Run `gosec` and `govulncheck` for security audits
|
||||||
|
|
||||||
```go
|
|
||||||
// #nosec G304 - File path provided by user via CLI argument
|
|
||||||
data, err := os.ReadFile(filePath)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
|
|
||||||
- Enable race detection: `-race` flag always
|
- Enable race detection: `-race` flag
|
||||||
- Use table-driven tests where applicable
|
- Use table-driven tests where applicable
|
||||||
- Mark test helpers with `t.Helper()`
|
- Mark test helpers with `t.Helper()`
|
||||||
- Use `t.TempDir()` for temporary files
|
|
||||||
- Benchmarks in `*_bench_test.go`, examples in `*_example_test.go`
|
- Benchmarks in `*_bench_test.go`, examples in `*_example_test.go`
|
||||||
- Test naming: `Test<Type>_<Method>` or `Test<Function>`
|
|
||||||
|
|
||||||
```go
|
|
||||||
func TestMarkdownExporter_ProcessItemToMarkdown_AllTypes(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name, itemType, expectedText string
|
|
||||||
}{
|
|
||||||
{"text item", "text", ""},
|
|
||||||
{"divider item", "divider", "---"},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
// test implementation
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
- Minimal external dependencies (go-docx, golang.org/x/net, golang.org/x/text)
|
- Minimal external dependencies (currently: go-docx, golang.org/x/net, golang.org/x/text)
|
||||||
- Run `task deps:tidy` after adding/removing dependencies
|
- Run `task deps:tidy` after adding/removing dependencies
|
||||||
- CGO disabled by default (`CGO_ENABLED=0`)
|
|
||||||
|
|
||||||
## Project Structure
|
---
|
||||||
|
|
||||||
```
|
## Go 1.24 & 1.25 New Features Reference
|
||||||
articulate-parser/
|
|
||||||
internal/
|
|
||||||
config/ # Configuration loading
|
|
||||||
exporters/ # Export implementations (markdown, html, docx)
|
|
||||||
interfaces/ # Core interfaces (Exporter, CourseParser, Logger)
|
|
||||||
models/ # Data models (Course, Lesson, Item, Media)
|
|
||||||
services/ # Core services (parser, html cleaner, app, logger)
|
|
||||||
version/ # Version information
|
|
||||||
main.go # Application entry point
|
|
||||||
```
|
|
||||||
|
|
||||||
## Common Patterns
|
This project uses Go 1.24+. Below is a comprehensive summary of new features and changes in Go 1.24 and 1.25 that may be relevant for development and maintenance.
|
||||||
|
|
||||||
### Creating a new exporter
|
### Go 1.24 Major Changes (Released February 2025)
|
||||||
|
|
||||||
1. Implement `interfaces.Exporter` interface
|
#### Language Features
|
||||||
2. Add factory method to `internal/exporters/factory.go`
|
|
||||||
3. Register format in `NewFactory()`
|
|
||||||
4. Add tests following existing patterns
|
|
||||||
|
|
||||||
### Adding configuration options
|
**Generic Type Aliases**
|
||||||
|
|
||||||
1. Add field to `Config` struct in `internal/config/config.go`
|
- Type aliases can now be parameterized with type parameters
|
||||||
2. Load from environment variable with sensible default
|
- Example: `type List[T any] = []T`
|
||||||
3. Document in config struct comments
|
- Can be disabled via `GOEXPERIMENT=noaliastypeparams` (removed in 1.25)
|
||||||
|
|
||||||
|
#### Tooling Enhancements
|
||||||
|
|
||||||
|
**Module Tool Dependencies**
|
||||||
|
|
||||||
|
- New `tool` directive in go.mod tracks executable dependencies
|
||||||
|
- Use `go get -tool <package>` to add tool dependencies
|
||||||
|
- Use `go install tool` and `go get tool` to manage them
|
||||||
|
- Eliminates need for blank imports in `tools.go` files
|
||||||
|
|
||||||
|
**Build Output Formatting**
|
||||||
|
|
||||||
|
- Both `go build` and `go test` support `-json` flag for structured JSON output
|
||||||
|
- New action types distinguish build output from test results
|
||||||
|
|
||||||
|
**Authentication**
|
||||||
|
|
||||||
|
- New `GOAUTH` environment variable provides flexible authentication for private modules
|
||||||
|
|
||||||
|
**Automatic Version Tracking**
|
||||||
|
|
||||||
|
- `go build` automatically sets main module version in binaries based on VCS tags
|
||||||
|
- Adds `+dirty` suffix for uncommitted changes
|
||||||
|
|
||||||
|
**Cgo Performance Improvements**
|
||||||
|
|
||||||
|
- New `#cgo noescape` annotation: Prevents escape analysis overhead for C function calls
|
||||||
|
- New `#cgo nocallback` annotation: Indicates C function won't call back to Go
|
||||||
|
|
||||||
|
**Toolchain Tracing**
|
||||||
|
|
||||||
|
- `GODEBUG=toolchaintrace=1` enables tracing of toolchain selection
|
||||||
|
|
||||||
|
#### Runtime & Performance
|
||||||
|
|
||||||
|
**Performance Improvements**
|
||||||
|
|
||||||
|
- **2-3% CPU overhead reduction** across benchmark suites
|
||||||
|
- New Swiss Tables-based map implementation (faster lookups)
|
||||||
|
- Disable via `GOEXPERIMENT=noswissmap`
|
||||||
|
- More efficient small object allocation
|
||||||
|
- Redesigned runtime-internal mutexes
|
||||||
|
- Disable via `GOEXPERIMENT=nospinbitmutex`
|
||||||
|
|
||||||
|
#### Compiler & Linker
|
||||||
|
|
||||||
|
**Method Receiver Restrictions**
|
||||||
|
|
||||||
|
- Methods on cgo-generated types now prevented (both directly and through aliases)
|
||||||
|
|
||||||
|
**Build IDs**
|
||||||
|
|
||||||
|
- Linkers generate GNU build IDs (ELF) and UUIDs (macOS) by default
|
||||||
|
- Disable via `-B none` flag
|
||||||
|
|
||||||
|
#### Standard Library Additions
|
||||||
|
|
||||||
|
**File System Safety - `os.Root`**
|
||||||
|
|
||||||
|
- New `os.Root` type enables directory-limited operations
|
||||||
|
- Prevents path escape and symlink breakouts
|
||||||
|
- Essential for sandboxed file operations
|
||||||
|
|
||||||
|
**Cryptography Expansion**
|
||||||
|
|
||||||
|
- `crypto/mlkem`: ML-KEM-768/1024 post-quantum key exchange (FIPS 203)
|
||||||
|
- `crypto/hkdf`: HMAC-based Extract-and-Expand KDF (RFC 5869)
|
||||||
|
- `crypto/pbkdf2`: Password-based key derivation (RFC 8018)
|
||||||
|
- `crypto/sha3`: SHA-3 and SHAKE functions (FIPS 202)
|
||||||
|
|
||||||
|
**FIPS 140-3 Support**
|
||||||
|
|
||||||
|
- New `GOFIPS140` environment variable enables FIPS mode
|
||||||
|
- New `fips140` GODEBUG setting for cryptographic module compliance
|
||||||
|
|
||||||
|
**Weak References - `weak` Package**
|
||||||
|
|
||||||
|
- New `weak` package provides low-level weak pointers
|
||||||
|
- Enables memory-efficient structures like weak maps and caches
|
||||||
|
- Useful for preventing memory leaks in cache implementations
|
||||||
|
|
||||||
|
**Testing Improvements**
|
||||||
|
|
||||||
|
- `testing.B.Loop()`: Cleaner syntax replacing manual `b.N` iteration
|
||||||
|
- Prevents compiler from optimizing away benchmarked code
|
||||||
|
- New `testing/synctest` package (experimental) for testing concurrent code with fake clocks
|
||||||
|
|
||||||
|
**Iterator Support**
|
||||||
|
|
||||||
|
- Multiple packages now offer iterator-returning variants:
|
||||||
|
- `bytes`: Iterator-based functions
|
||||||
|
- `strings`: Iterator-based functions
|
||||||
|
- `go/types`: Iterator support
|
||||||
|
|
||||||
|
#### Security Enhancements
|
||||||
|
|
||||||
|
**TLS Post-Quantum Cryptography**
|
||||||
|
|
||||||
|
- `X25519MLKEM768` hybrid key exchange enabled by default in TLS
|
||||||
|
- Provides quantum-resistant security
|
||||||
|
|
||||||
|
**Encrypted Client Hello (ECH)**
|
||||||
|
|
||||||
|
- TLS servers can enable ECH via `Config.EncryptedClientHelloKeys`
|
||||||
|
- Protects client identity during TLS handshake
|
||||||
|
|
||||||
|
**RSA Key Validation**
|
||||||
|
|
||||||
|
- Keys smaller than 1024 bits now rejected by default
|
||||||
|
- Use `GODEBUG=rsa1024min=0` to revert (testing only)
|
||||||
|
|
||||||
|
**Constant-Time Execution**
|
||||||
|
|
||||||
|
- New `crypto/subtle.WithDataIndependentTiming()` enables architecture-specific timing guarantees
|
||||||
|
- Helps prevent timing attacks
|
||||||
|
|
||||||
|
#### Deprecations & Removals
|
||||||
|
|
||||||
|
- `runtime.GOROOT()`: Deprecated; use system path instead
|
||||||
|
- `crypto/cipher` OFB/CFB modes: Deprecated (unauthenticated encryption)
|
||||||
|
- `x509sha1` GODEBUG: Removed; SHA-1 certificates no longer verified
|
||||||
|
- Experimental `X25519Kyber768Draft00`: Removed
|
||||||
|
|
||||||
|
#### Platform Changes
|
||||||
|
|
||||||
|
- **Linux**: Now requires kernel 3.2+ (enforced)
|
||||||
|
- **macOS**: Go 1.24 is final release supporting Big Sur
|
||||||
|
- **Windows/ARM 32-bit**: Marked broken
|
||||||
|
- **WebAssembly**:
|
||||||
|
- New `go:wasmexport` directive
|
||||||
|
- Reactor/library builds supported via `-buildmode=c-shared`
|
||||||
|
|
||||||
|
#### Bootstrap Requirements
|
||||||
|
|
||||||
|
- Go 1.24 requires Go 1.22.6+ for bootstrapping
|
||||||
|
- Go 1.26 will require Go 1.24+
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Go 1.25 Major Changes (Released August 2025)
|
||||||
|
|
||||||
|
#### Language Changes
|
||||||
|
|
||||||
|
- No breaking language changes
|
||||||
|
- "Core types" concept removed from specification (replaced with clearer prose)
|
||||||
|
|
||||||
|
#### Tooling Improvements
|
||||||
|
|
||||||
|
**Go Command Enhancements**
|
||||||
|
|
||||||
|
- `go build -asan`: Now defaults to leak detection at program exit
|
||||||
|
- New `go.mod ignore` directive: Specify directories for go command to ignore
|
||||||
|
- `go doc -http`: Starts documentation server and opens in browser
|
||||||
|
- `go version -m -json`: Prints JSON-encoded BuildInfo structures
|
||||||
|
- Module path resolution now supports subdirectories using `<meta>` syntax
|
||||||
|
- New `work` package pattern matches all packages in work/workspace modules
|
||||||
|
- Removed automatic toolchain line additions when updating `go` version
|
||||||
|
|
||||||
|
**Vet Analyzers**
|
||||||
|
|
||||||
|
- **"waitgroup"**: Detects misplaced `sync.WaitGroup.Add` calls
|
||||||
|
- **"hostport"**: Warns against using `fmt.Sprintf` for constructing addresses
|
||||||
|
- Recommends `net.JoinHostPort` instead
|
||||||
|
|
||||||
|
#### Runtime Enhancements
|
||||||
|
|
||||||
|
**Container-Aware GOMAXPROCS**
|
||||||
|
|
||||||
|
- Linux now respects cgroup CPU bandwidth limits
|
||||||
|
- All OSes periodically update GOMAXPROCS if CPU availability changes
|
||||||
|
- Disable via environment variables or GODEBUG settings
|
||||||
|
- Critical for containerized applications
|
||||||
|
|
||||||
|
**New Garbage Collector - "Green Tea GC"**
|
||||||
|
|
||||||
|
- Experimental `GOEXPERIMENT=greenteagc` enables new GC
|
||||||
|
- **10-40% reduction in garbage collection overhead**
|
||||||
|
- Significant for GC-sensitive applications
|
||||||
|
|
||||||
|
**Trace Flight Recorder**
|
||||||
|
|
||||||
|
- New `runtime/trace.FlightRecorder` API
|
||||||
|
- Captures execution traces in in-memory ring buffer
|
||||||
|
- Essential for debugging rare events and production issues
|
||||||
|
|
||||||
|
**Other Runtime Changes**
|
||||||
|
|
||||||
|
- Simplified unhandled panic output
|
||||||
|
- VMA names on Linux identify memory purpose (debugging aid)
|
||||||
|
- New `SetDefaultGOMAXPROCS` function resets GOMAXPROCS to defaults
|
||||||
|
|
||||||
|
#### Compiler Fixes & Improvements
|
||||||
|
|
||||||
|
**Critical Nil Pointer Bug Fix**
|
||||||
|
|
||||||
|
- Fixed Go 1.21 regression where nil pointer checks were incorrectly delayed
|
||||||
|
- ⚠️ **May cause previously passing code to now panic** (correct behavior)
|
||||||
|
- Review code for assumptions about delayed nil checks
|
||||||
|
|
||||||
|
**DWARF5 Support**
|
||||||
|
|
||||||
|
- Debug information now uses DWARF version 5
|
||||||
|
- Reduces binary size and linking time
|
||||||
|
- Better debugging experience
|
||||||
|
|
||||||
|
**Faster Slices**
|
||||||
|
|
||||||
|
- Expanded stack allocation for slice backing stores
|
||||||
|
- Improved slice performance
|
||||||
|
|
||||||
|
#### Linker
|
||||||
|
|
||||||
|
- New `-funcalign=N` option specifies function entry alignment
|
||||||
|
|
||||||
|
#### Standard Library Highlights
|
||||||
|
|
||||||
|
**New Packages**
|
||||||
|
|
||||||
|
1. **`testing/synctest`** (Promoted from Experimental)
|
||||||
|
- Concurrent code testing with virtualized time
|
||||||
|
- Control time progression in tests
|
||||||
|
- Essential for testing time-dependent concurrent code
|
||||||
|
|
||||||
|
2. **`encoding/json/v2`** (Experimental)
|
||||||
|
- **Substantially better decoding performance**
|
||||||
|
- Improved API design
|
||||||
|
- Backward compatible with v1
|
||||||
|
|
||||||
|
**Major Package Updates**
|
||||||
|
|
||||||
|
| Package | Key Changes |
|
||||||
|
|---------|------------|
|
||||||
|
| `crypto` | New `MessageSigner` interface and `SignMessage` function |
|
||||||
|
| `crypto/ecdsa` | New raw key parsing/serialization functions |
|
||||||
|
| `crypto/rsa` | **Key generation now 3x faster** |
|
||||||
|
| `crypto/sha1` | **Hashing 2x faster on amd64 with SHA-NI** |
|
||||||
|
| `crypto/tls` | New `CurveID` field; SHA-1 algorithms disallowed in TLS 1.2 |
|
||||||
|
| `net` | Windows now supports file-to-connection conversion; IPv6 multicast improvements |
|
||||||
|
| `net/http` | **New `CrossOriginProtection` middleware for CSRF defense** |
|
||||||
|
| `os` | Windows async I/O support; `Root` type expanded with 12 new methods |
|
||||||
|
| `sync` | **New `WaitGroup.Go` method for convenient goroutine creation** |
|
||||||
|
| `testing` | New `Attr`, `Output` methods; `AllocsPerRun` panics with parallel tests |
|
||||||
|
| `unique` | More eager and parallel reclamation of interned values |
|
||||||
|
|
||||||
|
#### Performance Notes
|
||||||
|
|
||||||
|
**Performance Improvements**
|
||||||
|
|
||||||
|
- ECDSA and Ed25519 signing **4x faster** in FIPS 140-3 mode
|
||||||
|
- SHA3 hashing **2x faster** on Apple M processors
|
||||||
|
- AMD64 fused multiply-add instructions in v3+ mode
|
||||||
|
- ⚠️ **Changes floating-point results** (within IEEE 754 spec)
|
||||||
|
|
||||||
|
**Performance Regressions**
|
||||||
|
|
||||||
|
- SHA-1, SHA-256, SHA-512 slower without AVX2
|
||||||
|
- Most servers post-2015 support AVX2
|
||||||
|
|
||||||
|
#### Platform Changes
|
||||||
|
|
||||||
|
- **macOS**: Requires version 12 Monterey or later
|
||||||
|
- **Windows**: 32-bit windows/arm port marked for removal in Go 1.26
|
||||||
|
- **Loong64**: Race detector now supported
|
||||||
|
- **RISC-V**:
|
||||||
|
- Plugin build mode support
|
||||||
|
- New `GORISCV64=rva23u64` environment variable value
|
||||||
|
|
||||||
|
#### Deprecations
|
||||||
|
|
||||||
|
- `go/ast` functions: `FilterPackage`, `PackageExports`, `MergePackageFiles`
|
||||||
|
- `go/parser.ParseDir` function
|
||||||
|
- Old `testing/synctest` API (when `GOEXPERIMENT=synctest` set)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Actionable Recommendations for This Project
|
||||||
|
|
||||||
|
#### Immediate Opportunities
|
||||||
|
|
||||||
|
1. **Replace `sync.WaitGroup` patterns with `WaitGroup.Go()`** (Go 1.25)
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Old pattern
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
// work
|
||||||
|
}()
|
||||||
|
|
||||||
|
// New pattern (Go 1.25)
|
||||||
|
wg.Go(func() {
|
||||||
|
// work
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Use `testing.B.Loop()` in benchmarks** (Go 1.24)
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Old pattern
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
// benchmark code
|
||||||
|
}
|
||||||
|
|
||||||
|
// New pattern (Go 1.24)
|
||||||
|
for b.Loop() {
|
||||||
|
// benchmark code
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Consider `os.Root` for file operations** (Go 1.24)
|
||||||
|
- Prevents path traversal vulnerabilities
|
||||||
|
- Safer for user-provided file paths
|
||||||
|
|
||||||
|
4. **Enable Green Tea GC for testing** (Go 1.25)
|
||||||
|
- Test with `GOEXPERIMENT=greenteagc`
|
||||||
|
- May reduce GC overhead by 10-40%
|
||||||
|
|
||||||
|
5. **Leverage container-aware GOMAXPROCS** (Go 1.25)
|
||||||
|
- No changes needed; automatic in containers
|
||||||
|
- Improves resource utilization
|
||||||
|
|
||||||
|
6. **Review floating-point operations** (Go 1.25)
|
||||||
|
- AMD64 v3+ uses FMA instructions
|
||||||
|
- May change floating-point results (within spec)
|
||||||
|
|
||||||
|
7. **Watch nil pointer checks** (Go 1.25)
|
||||||
|
- Compiler bug fix may expose latent nil pointer bugs
|
||||||
|
- Review crash reports carefully
|
||||||
|
|
||||||
|
#### Future Considerations
|
||||||
|
|
||||||
|
1. **Evaluate `encoding/json/v2`** when stable
|
||||||
|
- Better performance for JSON operations
|
||||||
|
- Currently experimental in Go 1.25
|
||||||
|
|
||||||
|
2. **Adopt tool directives** in go.mod
|
||||||
|
- Cleaner dependency management for build tools
|
||||||
|
- Remove `tools.go` if present
|
||||||
|
|
||||||
|
3. **Enable FIPS mode if required**
|
||||||
|
- Use `GOFIPS140=1` for compliance
|
||||||
|
- Performance improvements in Go 1.25 (4x faster signing)
|
||||||
|
|
||||||
|
4. **Use `runtime/trace.FlightRecorder`** for production debugging
|
||||||
|
- Capture traces of rare events
|
||||||
|
- Minimal overhead when not triggered
|
||||||
|
|||||||
@ -226,7 +226,7 @@ docker run --rm ghcr.io/kjanat/articulate-parser:latest --help
|
|||||||
### Available Tags
|
### Available Tags
|
||||||
|
|
||||||
| Tag | Description | Use Case |
|
| Tag | Description | Use Case |
|
||||||
| --------------------- | ------------------------------------------- | ---------------------- |
|
|-----|-------------|----------|
|
||||||
| `latest` | Latest stable release from master branch | Production use |
|
| `latest` | Latest stable release from master branch | Production use |
|
||||||
| `edge` | Latest development build from master branch | Testing new features |
|
| `edge` | Latest development build from master branch | Testing new features |
|
||||||
| `v1.x.x` | Specific version releases | Production pinning |
|
| `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:
|
The Docker image supports the following build-time arguments:
|
||||||
|
|
||||||
| Argument | Description | Default |
|
| Argument | Description | Default |
|
||||||
| ------------ | ------------------------------------- | -------------- |
|
|----------|-------------|---------|
|
||||||
| `VERSION` | Version string embedded in the binary | `dev` |
|
| `VERSION` | Version string embedded in the binary | `dev` |
|
||||||
| `BUILD_TIME` | Build timestamp | Current time |
|
| `BUILD_TIME` | Build timestamp | Current time |
|
||||||
| `GIT_COMMIT` | Git commit hash | Current commit |
|
| `GIT_COMMIT` | Git commit hash | Current commit |
|
||||||
@ -460,9 +460,7 @@ This is a utility tool for educational content conversion. Please ensure you hav
|
|||||||
[Go report]: https://goreportcard.com/report/github.com/kjanat/articulate-parser
|
[Go report]: https://goreportcard.com/report/github.com/kjanat/articulate-parser
|
||||||
[gomod]: go.mod
|
[gomod]: go.mod
|
||||||
[Issues]: https://github.com/kjanat/articulate-parser/issues
|
[Issues]: https://github.com/kjanat/articulate-parser/issues
|
||||||
|
|
||||||
<!-- [Latest release]: https://github.com/kjanat/articulate-parser/releases/latest -->
|
<!-- [Latest release]: https://github.com/kjanat/articulate-parser/releases/latest -->
|
||||||
|
|
||||||
[MIT License]: LICENSE
|
[MIT License]: LICENSE
|
||||||
[Package documentation]: https://godoc.org/github.com/kjanat/articulate-parser
|
[Package documentation]: https://godoc.org/github.com/kjanat/articulate-parser
|
||||||
[Tags]: https://github.com/kjanat/articulate-parser/tags
|
[Tags]: https://github.com/kjanat/articulate-parser/tags
|
||||||
|
|||||||
76
Taskfile.yml
76
Taskfile.yml
@ -1,7 +1,7 @@
|
|||||||
# yaml-language-server: $schema=https://taskfile.dev/schema.json
|
# yaml-language-server: $schema=https://taskfile.dev/schema.json
|
||||||
# Articulate Parser - Task Automation
|
# Articulate Parser - Task Automation
|
||||||
# https://taskfile.dev
|
# https://taskfile.dev
|
||||||
version: "3"
|
version: '3'
|
||||||
|
|
||||||
# Global output settings
|
# Global output settings
|
||||||
output: prefixed
|
output: prefixed
|
||||||
@ -47,11 +47,11 @@ vars:
|
|||||||
|
|
||||||
# Environment variables
|
# Environment variables
|
||||||
env:
|
env:
|
||||||
CGO_ENABLED: "{{.CGO_ENABLED}}"
|
CGO_ENABLED: '{{.CGO_ENABLED}}'
|
||||||
GO111MODULE: on
|
GO111MODULE: on
|
||||||
|
|
||||||
# Load .env files if present
|
# Load .env files if present
|
||||||
dotenv: [".env", ".env.local"]
|
dotenv: ['.env', '.env.local']
|
||||||
|
|
||||||
# Task definitions
|
# Task definitions
|
||||||
tasks:
|
tasks:
|
||||||
@ -69,12 +69,12 @@ tasks:
|
|||||||
interactive: true
|
interactive: true
|
||||||
watch: true
|
watch: true
|
||||||
sources:
|
sources:
|
||||||
- "**/*.go"
|
- '**/*.go'
|
||||||
- go.mod
|
- go.mod
|
||||||
- go.sum
|
- go.sum
|
||||||
cmds:
|
cmds:
|
||||||
- task: build
|
- task: build
|
||||||
- "{{.OUTPUT_DIR}}/{{.APP_NAME}}{{.EXE_EXT}} --help"
|
- '{{.OUTPUT_DIR}}/{{.APP_NAME}}{{.EXE_EXT}} --help'
|
||||||
|
|
||||||
# Build tasks
|
# Build tasks
|
||||||
build:
|
build:
|
||||||
@ -82,14 +82,14 @@ tasks:
|
|||||||
aliases: [b]
|
aliases: [b]
|
||||||
deps: [clean-bin]
|
deps: [clean-bin]
|
||||||
sources:
|
sources:
|
||||||
- "**/*.go"
|
- '**/*.go'
|
||||||
- go.mod
|
- go.mod
|
||||||
- go.sum
|
- go.sum
|
||||||
generates:
|
generates:
|
||||||
- "{{.OUTPUT_DIR}}/{{.APP_NAME}}{{.EXE_EXT}}"
|
- '{{.OUTPUT_DIR}}/{{.APP_NAME}}{{.EXE_EXT}}'
|
||||||
cmds:
|
cmds:
|
||||||
- task: mkdir
|
- task: mkdir
|
||||||
vars: { DIR: "{{.OUTPUT_DIR}}" }
|
vars: { DIR: '{{.OUTPUT_DIR}}' }
|
||||||
- go build {{.GO_FLAGS}} -ldflags="{{.LDFLAGS}}" -o {{.OUTPUT_DIR}}/{{.APP_NAME}}{{.EXE_EXT}} {{.MAIN_FILE}}
|
- go build {{.GO_FLAGS}} -ldflags="{{.LDFLAGS}}" -o {{.OUTPUT_DIR}}/{{.APP_NAME}}{{.EXE_EXT}} {{.MAIN_FILE}}
|
||||||
method: checksum
|
method: checksum
|
||||||
|
|
||||||
@ -99,25 +99,25 @@ tasks:
|
|||||||
deps: [clean-bin]
|
deps: [clean-bin]
|
||||||
cmds:
|
cmds:
|
||||||
- task: mkdir
|
- task: mkdir
|
||||||
vars: { DIR: "{{.OUTPUT_DIR}}" }
|
vars: { DIR: '{{.OUTPUT_DIR}}' }
|
||||||
- for:
|
- for:
|
||||||
matrix:
|
matrix:
|
||||||
GOOS: [linux, darwin, windows]
|
GOOS: [linux, darwin, windows]
|
||||||
GOARCH: [amd64, arm64]
|
GOARCH: [amd64, arm64]
|
||||||
task: build:platform
|
task: build:platform
|
||||||
vars:
|
vars:
|
||||||
TARGET_GOOS: "{{.ITEM.GOOS}}"
|
TARGET_GOOS: '{{.ITEM.GOOS}}'
|
||||||
TARGET_GOARCH: "{{.ITEM.GOARCH}}"
|
TARGET_GOARCH: '{{.ITEM.GOARCH}}'
|
||||||
- echo "Built binaries for all platforms in {{.OUTPUT_DIR}}/"
|
- echo "Built binaries for all platforms in {{.OUTPUT_DIR}}/"
|
||||||
|
|
||||||
build:platform:
|
build:platform:
|
||||||
internal: true
|
internal: true
|
||||||
vars:
|
vars:
|
||||||
TARGET_EXT: '{{if eq .TARGET_GOOS "windows"}}.exe{{end}}'
|
TARGET_EXT: '{{if eq .TARGET_GOOS "windows"}}.exe{{end}}'
|
||||||
OUTPUT_FILE: "{{.OUTPUT_DIR}}/{{.APP_NAME}}-{{.TARGET_GOOS}}-{{.TARGET_GOARCH}}{{.TARGET_EXT}}"
|
OUTPUT_FILE: '{{.OUTPUT_DIR}}/{{.APP_NAME}}-{{.TARGET_GOOS}}-{{.TARGET_GOARCH}}{{.TARGET_EXT}}'
|
||||||
env:
|
env:
|
||||||
GOOS: "{{.TARGET_GOOS}}"
|
GOOS: '{{.TARGET_GOOS}}'
|
||||||
GOARCH: "{{.TARGET_GOARCH}}"
|
GOARCH: '{{.TARGET_GOARCH}}'
|
||||||
cmds:
|
cmds:
|
||||||
- echo "Building {{.OUTPUT_FILE}}..."
|
- echo "Building {{.OUTPUT_FILE}}..."
|
||||||
- go build {{.GO_FLAGS}} -ldflags="{{.LDFLAGS}}" -o "{{.OUTPUT_FILE}}" {{.MAIN_FILE}}
|
- go build {{.GO_FLAGS}} -ldflags="{{.LDFLAGS}}" -o "{{.OUTPUT_FILE}}" {{.MAIN_FILE}}
|
||||||
@ -134,8 +134,6 @@ tasks:
|
|||||||
test:
|
test:
|
||||||
desc: Run all tests
|
desc: Run all tests
|
||||||
aliases: [t]
|
aliases: [t]
|
||||||
env:
|
|
||||||
CGO_ENABLED: 1
|
|
||||||
cmds:
|
cmds:
|
||||||
- go test {{.GO_FLAGS}} -race -timeout {{.TEST_TIMEOUT}} ./...
|
- go test {{.GO_FLAGS}} -race -timeout {{.TEST_TIMEOUT}} ./...
|
||||||
|
|
||||||
@ -143,11 +141,9 @@ tasks:
|
|||||||
desc: Run tests with coverage report
|
desc: Run tests with coverage report
|
||||||
aliases: [cover, cov]
|
aliases: [cover, cov]
|
||||||
deps: [clean-coverage]
|
deps: [clean-coverage]
|
||||||
env:
|
|
||||||
CGO_ENABLED: 1
|
|
||||||
cmds:
|
cmds:
|
||||||
- task: mkdir
|
- task: mkdir
|
||||||
vars: { DIR: "{{.COVERAGE_DIR}}" }
|
vars: { DIR: '{{.COVERAGE_DIR}}' }
|
||||||
- go test {{.GO_FLAGS}} -race -coverprofile={{.COVERAGE_DIR}}/coverage.out -covermode=atomic -timeout {{.TEST_TIMEOUT}} ./...
|
- go test {{.GO_FLAGS}} -race -coverprofile={{.COVERAGE_DIR}}/coverage.out -covermode=atomic -timeout {{.TEST_TIMEOUT}} ./...
|
||||||
- go tool cover -html={{.COVERAGE_DIR}}/coverage.out -o {{.COVERAGE_DIR}}/coverage.html
|
- go tool cover -html={{.COVERAGE_DIR}}/coverage.out -o {{.COVERAGE_DIR}}/coverage.html
|
||||||
- go tool cover -func={{.COVERAGE_DIR}}/coverage.out
|
- go tool cover -func={{.COVERAGE_DIR}}/coverage.out
|
||||||
@ -156,8 +152,6 @@ tasks:
|
|||||||
test:verbose:
|
test:verbose:
|
||||||
desc: Run tests with verbose output
|
desc: Run tests with verbose output
|
||||||
aliases: [tv]
|
aliases: [tv]
|
||||||
env:
|
|
||||||
CGO_ENABLED: 1
|
|
||||||
cmds:
|
cmds:
|
||||||
- go test -v -race -timeout {{.TEST_TIMEOUT}} ./...
|
- go test -v -race -timeout {{.TEST_TIMEOUT}} ./...
|
||||||
|
|
||||||
@ -166,7 +160,7 @@ tasks:
|
|||||||
aliases: [tw]
|
aliases: [tw]
|
||||||
watch: true
|
watch: true
|
||||||
sources:
|
sources:
|
||||||
- "**/*.go"
|
- '**/*.go'
|
||||||
cmds:
|
cmds:
|
||||||
- task: test
|
- task: test
|
||||||
|
|
||||||
@ -178,8 +172,6 @@ tasks:
|
|||||||
|
|
||||||
test:integration:
|
test:integration:
|
||||||
desc: Run integration tests
|
desc: Run integration tests
|
||||||
env:
|
|
||||||
CGO_ENABLED: 1
|
|
||||||
status:
|
status:
|
||||||
- '{{if eq OS "windows"}}if not exist "main_test.go" exit 1{{else}}test ! -f "main_test.go"{{end}}'
|
- '{{if eq OS "windows"}}if not exist "main_test.go" exit 1{{else}}test ! -f "main_test.go"{{end}}'
|
||||||
cmds:
|
cmds:
|
||||||
@ -324,13 +316,13 @@ tasks:
|
|||||||
docker:run:
|
docker:run:
|
||||||
desc: Run Docker container
|
desc: Run Docker container
|
||||||
aliases: [dr]
|
aliases: [dr]
|
||||||
deps: [docker:build]
|
deps: ['docker:build']
|
||||||
cmds:
|
cmds:
|
||||||
- docker run --rm {{.APP_NAME}}:{{.VERSION}} --help
|
- docker run --rm {{.APP_NAME}}:{{.VERSION}} --help
|
||||||
|
|
||||||
docker:test:
|
docker:test:
|
||||||
desc: Test Docker image
|
desc: Test Docker image
|
||||||
deps: [docker:build]
|
deps: ['docker:build']
|
||||||
cmds:
|
cmds:
|
||||||
- docker run --rm {{.APP_NAME}}:{{.VERSION}} --version
|
- docker run --rm {{.APP_NAME}}:{{.VERSION}} --version
|
||||||
- echo "Docker image tested successfully"
|
- echo "Docker image tested successfully"
|
||||||
@ -360,14 +352,14 @@ tasks:
|
|||||||
internal: true
|
internal: true
|
||||||
cmds:
|
cmds:
|
||||||
- task: rmdir
|
- task: rmdir
|
||||||
vars: { DIR: "{{.OUTPUT_DIR}}" }
|
vars: { DIR: '{{.OUTPUT_DIR}}' }
|
||||||
|
|
||||||
clean-coverage:
|
clean-coverage:
|
||||||
desc: Remove coverage files
|
desc: Remove coverage files
|
||||||
internal: true
|
internal: true
|
||||||
cmds:
|
cmds:
|
||||||
- task: rmdir
|
- task: rmdir
|
||||||
vars: { DIR: "{{.COVERAGE_DIR}}" }
|
vars: { DIR: '{{.COVERAGE_DIR}}' }
|
||||||
|
|
||||||
clean-cache:
|
clean-cache:
|
||||||
desc: Clean Go build and test cache
|
desc: Clean Go build and test cache
|
||||||
@ -418,10 +410,10 @@ tasks:
|
|||||||
requires:
|
requires:
|
||||||
vars: [VERSION]
|
vars: [VERSION]
|
||||||
preconditions:
|
preconditions:
|
||||||
- sh: "git diff --exit-code"
|
- sh: 'git diff --exit-code'
|
||||||
msg: "Working directory is not clean"
|
msg: 'Working directory is not clean'
|
||||||
- sh: "git diff --cached --exit-code"
|
- sh: 'git diff --cached --exit-code'
|
||||||
msg: "Staging area is not clean"
|
msg: 'Staging area is not clean'
|
||||||
cmds:
|
cmds:
|
||||||
- git tag -a v{{.VERSION}} -m "Release v{{.VERSION}}"
|
- git tag -a v{{.VERSION}} -m "Release v{{.VERSION}}"
|
||||||
- echo "Tagged v{{.VERSION}}"
|
- echo "Tagged v{{.VERSION}}"
|
||||||
@ -442,7 +434,7 @@ tasks:
|
|||||||
|
|
||||||
docs:coverage:
|
docs:coverage:
|
||||||
desc: Open coverage report in browser
|
desc: Open coverage report in browser
|
||||||
deps: [test:coverage]
|
deps: ['test:coverage']
|
||||||
cmds:
|
cmds:
|
||||||
- '{{if eq OS "darwin"}}open {{.COVERAGE_DIR}}/coverage.html{{else if eq OS "windows"}}start {{.COVERAGE_DIR}}/coverage.html{{else}}xdg-open {{.COVERAGE_DIR}}/coverage.html 2>/dev/null || echo "Please open {{.COVERAGE_DIR}}/coverage.html in your browser"{{end}}'
|
- '{{if eq OS "darwin"}}open {{.COVERAGE_DIR}}/coverage.html{{else if eq OS "windows"}}start {{.COVERAGE_DIR}}/coverage.html{{else}}xdg-open {{.COVERAGE_DIR}}/coverage.html 2>/dev/null || echo "Please open {{.COVERAGE_DIR}}/coverage.html in your browser"{{end}}'
|
||||||
|
|
||||||
@ -506,11 +498,11 @@ tasks:
|
|||||||
- '{{if eq OS "windows"}}if not exist "articulate-sample.json" exit 1{{else}}test ! -f "articulate-sample.json"{{end}}'
|
- '{{if eq OS "windows"}}if not exist "articulate-sample.json" exit 1{{else}}test ! -f "articulate-sample.json"{{end}}'
|
||||||
deps: [build]
|
deps: [build]
|
||||||
cmds:
|
cmds:
|
||||||
- "{{.OUTPUT_DIR}}/{{.APP_NAME}}{{.EXE_EXT}} articulate-sample.json md output-demo.md"
|
- '{{.OUTPUT_DIR}}/{{.APP_NAME}}{{.EXE_EXT}} articulate-sample.json md output-demo.md'
|
||||||
- echo "Demo Markdown created{{:}} output-demo.md"
|
- echo "Demo Markdown created{{:}} output-demo.md"
|
||||||
- defer:
|
- defer:
|
||||||
task: rmfile
|
task: rmfile
|
||||||
vars: { FILE: "output-demo.md" }
|
vars: { FILE: 'output-demo.md' }
|
||||||
|
|
||||||
demo:html:
|
demo:html:
|
||||||
desc: Demo - Convert sample to HTML
|
desc: Demo - Convert sample to HTML
|
||||||
@ -518,11 +510,11 @@ tasks:
|
|||||||
- '{{if eq OS "windows"}}if not exist "articulate-sample.json" exit 1{{else}}test ! -f "articulate-sample.json"{{end}}'
|
- '{{if eq OS "windows"}}if not exist "articulate-sample.json" exit 1{{else}}test ! -f "articulate-sample.json"{{end}}'
|
||||||
deps: [build]
|
deps: [build]
|
||||||
cmds:
|
cmds:
|
||||||
- "{{.OUTPUT_DIR}}/{{.APP_NAME}}{{.EXE_EXT}} articulate-sample.json html output-demo.html"
|
- '{{.OUTPUT_DIR}}/{{.APP_NAME}}{{.EXE_EXT}} articulate-sample.json html output-demo.html'
|
||||||
- echo "Demo HTML created{{:}} output-demo.html"
|
- echo "Demo HTML created{{:}} output-demo.html"
|
||||||
- defer:
|
- defer:
|
||||||
task: rmfile
|
task: rmfile
|
||||||
vars: { FILE: "output-demo.html" }
|
vars: { FILE: 'output-demo.html' }
|
||||||
|
|
||||||
demo:docx:
|
demo:docx:
|
||||||
desc: Demo - Convert sample to DOCX
|
desc: Demo - Convert sample to DOCX
|
||||||
@ -530,11 +522,11 @@ tasks:
|
|||||||
- '{{if eq OS "windows"}}if not exist "articulate-sample.json" exit 1{{else}}test ! -f "articulate-sample.json"{{end}}'
|
- '{{if eq OS "windows"}}if not exist "articulate-sample.json" exit 1{{else}}test ! -f "articulate-sample.json"{{end}}'
|
||||||
deps: [build]
|
deps: [build]
|
||||||
cmds:
|
cmds:
|
||||||
- "{{.OUTPUT_DIR}}/{{.APP_NAME}}{{.EXE_EXT}} articulate-sample.json docx output-demo.docx"
|
- '{{.OUTPUT_DIR}}/{{.APP_NAME}}{{.EXE_EXT}} articulate-sample.json docx output-demo.docx'
|
||||||
- echo "Demo DOCX created{{:}} output-demo.docx"
|
- echo "Demo DOCX created{{:}} output-demo.docx"
|
||||||
- defer:
|
- defer:
|
||||||
task: rmfile
|
task: rmfile
|
||||||
vars: { FILE: "output-demo.docx" }
|
vars: { FILE: 'output-demo.docx' }
|
||||||
|
|
||||||
# Performance profiling
|
# Performance profiling
|
||||||
profile:cpu:
|
profile:cpu:
|
||||||
@ -544,7 +536,7 @@ tasks:
|
|||||||
- go tool pprof -http=:8080 cpu.prof
|
- go tool pprof -http=:8080 cpu.prof
|
||||||
- defer:
|
- defer:
|
||||||
task: rmfile
|
task: rmfile
|
||||||
vars: { FILE: "cpu.prof" }
|
vars: { FILE: 'cpu.prof' }
|
||||||
|
|
||||||
profile:mem:
|
profile:mem:
|
||||||
desc: Run memory profiling
|
desc: Run memory profiling
|
||||||
@ -553,14 +545,14 @@ tasks:
|
|||||||
- go tool pprof -http=:8080 mem.prof
|
- go tool pprof -http=:8080 mem.prof
|
||||||
- defer:
|
- defer:
|
||||||
task: rmfile
|
task: rmfile
|
||||||
vars: { FILE: "mem.prof" }
|
vars: { FILE: 'mem.prof' }
|
||||||
|
|
||||||
# Git hooks
|
# Git hooks
|
||||||
hooks:install:
|
hooks:install:
|
||||||
desc: Install git hooks
|
desc: Install git hooks
|
||||||
cmds:
|
cmds:
|
||||||
- task: mkdir
|
- task: mkdir
|
||||||
vars: { DIR: ".git/hooks" }
|
vars: { DIR: '.git/hooks' }
|
||||||
- '{{if eq OS "windows"}}echo "#!/bin/sh" > .git/hooks/pre-commit && echo "task lint:fmt" >> .git/hooks/pre-commit{{else}}cat > .git/hooks/pre-commit << ''EOF''{{printf "\n"}}#!/bin/sh{{printf "\n"}}task lint:fmt{{printf "\n"}}EOF{{printf "\n"}}chmod +x .git/hooks/pre-commit{{end}}'
|
- '{{if eq OS "windows"}}echo "#!/bin/sh" > .git/hooks/pre-commit && echo "task lint:fmt" >> .git/hooks/pre-commit{{else}}cat > .git/hooks/pre-commit << ''EOF''{{printf "\n"}}#!/bin/sh{{printf "\n"}}task lint:fmt{{printf "\n"}}EOF{{printf "\n"}}chmod +x .git/hooks/pre-commit{{end}}'
|
||||||
- echo "Git hooks installed"
|
- echo "Git hooks installed"
|
||||||
|
|
||||||
|
|||||||
8
go.mod
8
go.mod
@ -2,15 +2,13 @@ module github.com/kjanat/articulate-parser
|
|||||||
|
|
||||||
go 1.24.0
|
go 1.24.0
|
||||||
|
|
||||||
toolchain go1.25.5
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/fumiama/go-docx v0.0.0-20250506085032-0c30fd09304b
|
github.com/fumiama/go-docx v0.0.0-20250506085032-0c30fd09304b
|
||||||
golang.org/x/net v0.48.0
|
golang.org/x/net v0.46.0
|
||||||
golang.org/x/text v0.32.0
|
golang.org/x/text v0.30.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/fumiama/imgsz v0.0.4 // indirect
|
github.com/fumiama/imgsz v0.0.4 // indirect
|
||||||
golang.org/x/image v0.34.0 // indirect
|
golang.org/x/image v0.32.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
12
go.sum
12
go.sum
@ -2,9 +2,9 @@ github.com/fumiama/go-docx v0.0.0-20250506085032-0c30fd09304b h1:/mxSugRc4SgN7Xg
|
|||||||
github.com/fumiama/go-docx v0.0.0-20250506085032-0c30fd09304b/go.mod h1:ssRF0IaB1hCcKIObp3FkZOsjTcAHpgii70JelNb4H8M=
|
github.com/fumiama/go-docx v0.0.0-20250506085032-0c30fd09304b/go.mod h1:ssRF0IaB1hCcKIObp3FkZOsjTcAHpgii70JelNb4H8M=
|
||||||
github.com/fumiama/imgsz v0.0.4 h1:Lsasu2hdSSFS+vnD+nvR1UkiRMK7hcpyYCC0FzgSMFI=
|
github.com/fumiama/imgsz v0.0.4 h1:Lsasu2hdSSFS+vnD+nvR1UkiRMK7hcpyYCC0FzgSMFI=
|
||||||
github.com/fumiama/imgsz v0.0.4/go.mod h1:bISOQVTlw9sRytPwe8ir7tAaEmyz9hSNj9n8mXMBG0E=
|
github.com/fumiama/imgsz v0.0.4/go.mod h1:bISOQVTlw9sRytPwe8ir7tAaEmyz9hSNj9n8mXMBG0E=
|
||||||
golang.org/x/image v0.34.0 h1:33gCkyw9hmwbZJeZkct8XyR11yH889EQt/QH4VmXMn8=
|
golang.org/x/image v0.32.0 h1:6lZQWq75h7L5IWNk0r+SCpUJ6tUVd3v4ZHnbRKLkUDQ=
|
||||||
golang.org/x/image v0.34.0/go.mod h1:2RNFBZRB+vnwwFil8GkMdRvrJOFd1AzdZI6vOY+eJVU=
|
golang.org/x/image v0.32.0/go.mod h1:/R37rrQmKXtO6tYXAjtDLwQgFLHmhW+V6ayXlxzP2Pc=
|
||||||
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
|
||||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
|
||||||
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
||||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
||||||
|
|||||||
@ -16,13 +16,6 @@ import (
|
|||||||
"github.com/kjanat/articulate-parser/internal/services"
|
"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.
|
// DocxExporter implements the Exporter interface for DOCX format.
|
||||||
// It converts Articulate Rise course data into a Microsoft Word document
|
// It converts Articulate Rise course data into a Microsoft Word document
|
||||||
// using the go-docx package.
|
// using the go-docx package.
|
||||||
@ -60,7 +53,7 @@ func (e *DocxExporter) Export(course *models.Course, outputPath string) error {
|
|||||||
|
|
||||||
// Add title
|
// Add title
|
||||||
titlePara := doc.AddParagraph()
|
titlePara := doc.AddParagraph()
|
||||||
titlePara.AddText(course.Course.Title).Size(docxTitleSize).Bold()
|
titlePara.AddText(course.Course.Title).Size("32").Bold()
|
||||||
|
|
||||||
// Add description if available
|
// Add description if available
|
||||||
if course.Course.Description != "" {
|
if course.Course.Description != "" {
|
||||||
@ -113,7 +106,7 @@ func (e *DocxExporter) Export(course *models.Course, outputPath string) error {
|
|||||||
func (e *DocxExporter) exportLesson(doc *docx.Docx, lesson *models.Lesson) {
|
func (e *DocxExporter) exportLesson(doc *docx.Docx, lesson *models.Lesson) {
|
||||||
// Add lesson title
|
// Add lesson title
|
||||||
lessonPara := doc.AddParagraph()
|
lessonPara := doc.AddParagraph()
|
||||||
lessonPara.AddText(fmt.Sprintf("Lesson: %s", lesson.Title)).Size(docxLessonSize).Bold()
|
lessonPara.AddText(fmt.Sprintf("Lesson: %s", lesson.Title)).Size("28").Bold()
|
||||||
|
|
||||||
// Add lesson description if available
|
// Add lesson description if available
|
||||||
if lesson.Description != "" {
|
if lesson.Description != "" {
|
||||||
@ -139,7 +132,7 @@ func (e *DocxExporter) exportItem(doc *docx.Docx, item *models.Item) {
|
|||||||
if item.Type != "" {
|
if item.Type != "" {
|
||||||
itemPara := doc.AddParagraph()
|
itemPara := doc.AddParagraph()
|
||||||
caser := cases.Title(language.English)
|
caser := cases.Title(language.English)
|
||||||
itemPara.AddText(caser.String(item.Type)).Size(docxItemSize).Bold()
|
itemPara.AddText(caser.String(item.Type)).Size("24").Bold()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add sub-items
|
// Add sub-items
|
||||||
|
|||||||
@ -69,16 +69,7 @@ func (e *HTMLExporter) Export(course *models.Course, outputPath string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create file: %w", err)
|
return fmt.Errorf("failed to create file: %w", err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer f.Close()
|
||||||
// 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)
|
return e.WriteHTML(f, course)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
body {
|
body {
|
||||||
font-family:
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||||
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu,
|
|
||||||
Cantarell, sans-serif;
|
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
color: #333;
|
color: #333;
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
|
|||||||
@ -96,10 +96,7 @@ func (e *MarkdownExporter) SupportedFormat() string {
|
|||||||
func (e *MarkdownExporter) processItemToMarkdown(buf *bytes.Buffer, item models.Item, level int) {
|
func (e *MarkdownExporter) processItemToMarkdown(buf *bytes.Buffer, item models.Item, level int) {
|
||||||
headingPrefix := strings.Repeat("#", level)
|
headingPrefix := strings.Repeat("#", level)
|
||||||
|
|
||||||
// Normalize item type to lowercase for consistent matching
|
switch item.Type {
|
||||||
itemType := strings.ToLower(item.Type)
|
|
||||||
|
|
||||||
switch itemType {
|
|
||||||
case "text":
|
case "text":
|
||||||
e.processTextItem(buf, item, headingPrefix)
|
e.processTextItem(buf, item, headingPrefix)
|
||||||
case "list":
|
case "list":
|
||||||
@ -108,7 +105,7 @@ func (e *MarkdownExporter) processItemToMarkdown(buf *bytes.Buffer, item models.
|
|||||||
e.processMultimediaItem(buf, item, headingPrefix)
|
e.processMultimediaItem(buf, item, headingPrefix)
|
||||||
case "image":
|
case "image":
|
||||||
e.processImageItem(buf, item, headingPrefix)
|
e.processImageItem(buf, item, headingPrefix)
|
||||||
case "knowledgecheck":
|
case "knowledgeCheck":
|
||||||
e.processKnowledgeCheckItem(buf, item, headingPrefix)
|
e.processKnowledgeCheckItem(buf, item, headingPrefix)
|
||||||
case "interactive":
|
case "interactive":
|
||||||
e.processInteractiveItem(buf, item, headingPrefix)
|
e.processInteractiveItem(buf, item, headingPrefix)
|
||||||
|
|||||||
@ -9,4 +9,3 @@ Course description
|
|||||||
- **Navigation Mode**:
|
- **Navigation Mode**:
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -15,9 +15,6 @@ import (
|
|||||||
"github.com/kjanat/articulate-parser/internal/models"
|
"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.
|
// 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.
|
// It can fetch courses from the Articulate Rise API or load them from local JSON files.
|
||||||
type ArticulateParser struct {
|
type ArticulateParser struct {
|
||||||
@ -81,15 +78,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)
|
body, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to read response body: %w", err)
|
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
|
var course models.Course
|
||||||
if err := json.Unmarshal(body, &course); err != nil {
|
if err := json.Unmarshal(body, &course); err != nil {
|
||||||
return nil, fmt.Errorf("failed to unmarshal JSON: %w", err)
|
return nil, fmt.Errorf("failed to unmarshal JSON: %w", err)
|
||||||
@ -136,7 +133,8 @@ func (p *ArticulateParser) extractShareID(uri string) (string, error) {
|
|||||||
return "", fmt.Errorf("invalid domain for Articulate Rise URI: %s", parsedURL.Host)
|
return "", fmt.Errorf("invalid domain for Articulate Rise URI: %s", parsedURL.Host)
|
||||||
}
|
}
|
||||||
|
|
||||||
matches := shareIDRegex.FindStringSubmatch(uri)
|
re := regexp.MustCompile(`/share/([a-zA-Z0-9_-]+)`)
|
||||||
|
matches := re.FindStringSubmatch(uri)
|
||||||
if len(matches) < 2 {
|
if len(matches) < 2 {
|
||||||
return "", fmt.Errorf("could not extract share ID from URI: %s", uri)
|
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:
|
// Returns:
|
||||||
// - true if the string appears to be a URI, false otherwise
|
// - true if the string appears to be a URI, false otherwise
|
||||||
func isURI(str string) bool {
|
func isURI(str string) bool {
|
||||||
return strings.HasPrefix(str, "http://") || strings.HasPrefix(str, "https://")
|
return len(str) > 7 && (str[:7] == "http://" || str[:8] == "https://")
|
||||||
}
|
}
|
||||||
|
|
||||||
// printUsage prints the command-line usage information.
|
// printUsage prints the command-line usage information.
|
||||||
|
|||||||
Reference in New Issue
Block a user