docopslab-dev 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.adoc +904 -0
- data/assets/config-packs/actionlint/base.yml +13 -0
- data/assets/config-packs/actionlint/project.yml +13 -0
- data/assets/config-packs/htmlproofer/base.yml +27 -0
- data/assets/config-packs/htmlproofer/project.yml +25 -0
- data/assets/config-packs/rubocop/base.yml +130 -0
- data/assets/config-packs/rubocop/project.yml +8 -0
- data/assets/config-packs/shellcheck/base.shellcheckrc +14 -0
- data/assets/config-packs/subtxt/ai-asciidoc-antipatterns.sub.txt +11 -0
- data/assets/config-packs/vale/asciidoc/ExplicitSectionIDs.yml +8 -0
- data/assets/config-packs/vale/asciidoc/ExtraLineBeforeLevel1.yml +7 -0
- data/assets/config-packs/vale/asciidoc/OneSentencePerLine.yml +8 -0
- data/assets/config-packs/vale/asciidoc/PreferSourceBlocks.yml +8 -0
- data/assets/config-packs/vale/asciidoc/ProperAdmonitions.yml +8 -0
- data/assets/config-packs/vale/asciidoc/ProperDLs.yml +7 -0
- data/assets/config-packs/vale/asciidoc/UncleanListStart.yml +8 -0
- data/assets/config-packs/vale/authoring/ButParagraph.yml +8 -0
- data/assets/config-packs/vale/authoring/ExNotEg.yml +8 -0
- data/assets/config-packs/vale/authoring/LiteralTerms.yml +20 -0
- data/assets/config-packs/vale/authoring/Spelling.yml +679 -0
- data/assets/config-packs/vale/base.ini +38 -0
- data/assets/config-packs/vale/config/scripts/ExplicitSectionIDs.tengo +56 -0
- data/assets/config-packs/vale/config/scripts/ExtraLineBeforeLevel1.tengo +121 -0
- data/assets/config-packs/vale/config/scripts/OneSentencePerLine.tengo +53 -0
- data/assets/config-packs/vale/project.ini +5 -0
- data/assets/hooks/pre-commit +63 -0
- data/assets/hooks/pre-push +72 -0
- data/assets/scripts/adoc_section_ids.rb +50 -0
- data/assets/scripts/build-common.sh +193 -0
- data/assets/scripts/build-docker.sh +64 -0
- data/assets/scripts/build.sh +56 -0
- data/assets/scripts/parse_jekyll_asciidoc_logs.rb +467 -0
- data/assets/templates/Gemfile +7 -0
- data/assets/templates/Rakefile +3 -0
- data/assets/templates/gitignore +69 -0
- data/assets/templates/jekyll-asciidoc-fix.prompt.yml +17 -0
- data/assets/templates/spellcheck.prompt.yml +16 -0
- data/docopslab-dev.gemspec +56 -0
- data/docs/agent/AGENTS.md +229 -0
- data/docs/agent/index.md +80 -0
- data/docs/agent/missions/conduct-release.md +224 -0
- data/docs/agent/missions/setup-new-project.md +250 -0
- data/docs/agent/roles/devops-release-engineer.md +152 -0
- data/docs/agent/roles/docops-engineer.md +193 -0
- data/docs/agent/roles/planner-architect.md +74 -0
- data/docs/agent/roles/product-engineer.md +153 -0
- data/docs/agent/roles/product-manager.md +130 -0
- data/docs/agent/roles/project-manager.md +139 -0
- data/docs/agent/roles/qa-testing-engineer.md +115 -0
- data/docs/agent/roles/tech-docs-manager.md +143 -0
- data/docs/agent/roles/tech-writer.md +163 -0
- data/docs/agent/skills/asciidoc.md +609 -0
- data/docs/agent/skills/code-commenting.md +347 -0
- data/docs/agent/skills/fix-broken-links.md +309 -0
- data/docs/agent/skills/fix-jekyll-asciidoc-build-errors.md +23 -0
- data/docs/agent/skills/fix-spelling-issues.md +13 -0
- data/docs/agent/skills/git.md +170 -0
- data/docs/agent/skills/github-issues.md +135 -0
- data/docs/agent/skills/product-release-rollback-and-patching.md +71 -0
- data/docs/agent/skills/rake-cli-dev.md +57 -0
- data/docs/agent/skills/readme-driven-dev.md +13 -0
- data/docs/agent/skills/release-history.md +29 -0
- data/docs/agent/skills/ruby.md +192 -0
- data/docs/agent/skills/schemagraphy-sgyml.md +18 -0
- data/docs/agent/skills/tests-running.md +25 -0
- data/docs/agent/skills/tests-writing.md +45 -0
- data/docs/agent/skills/write-the-docs.md +54 -0
- data/docs/agent/topics/common-project-paths.md +117 -0
- data/docs/agent/topics/dev-tooling-usage.md +202 -0
- data/docs/agent/topics/devops-ci-cd.md +55 -0
- data/docs/agent/topics/product-docs-deployment.md +25 -0
- data/lib/docopslab/dev/auto_fix_asciidoc.rb +46 -0
- data/lib/docopslab/dev/checkers.rb +108 -0
- data/lib/docopslab/dev/config_manager.rb +241 -0
- data/lib/docopslab/dev/file_utils.rb +140 -0
- data/lib/docopslab/dev/git_hooks.rb +140 -0
- data/lib/docopslab/dev/help.rb +121 -0
- data/lib/docopslab/dev/initializer.rb +95 -0
- data/lib/docopslab/dev/linters.rb +451 -0
- data/lib/docopslab/dev/log_parser.rb +31 -0
- data/lib/docopslab/dev/paths.rb +46 -0
- data/lib/docopslab/dev/script_manager.rb +136 -0
- data/lib/docopslab/dev/spell_check.rb +194 -0
- data/lib/docopslab/dev/sync_ops.rb +468 -0
- data/lib/docopslab/dev/tasks.rb +440 -0
- data/lib/docopslab/dev/tool_execution.rb +68 -0
- data/lib/docopslab/dev/version.rb +8 -0
- data/lib/docopslab/dev.rb +392 -0
- data/specs/data/default-manifest.yml +64 -0
- data/specs/data/manifest-schema.yaml +63 -0
- data/specs/data/tasks-def.yml +321 -0
- data/specs/data/tools.yml +60 -0
- metadata +362 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
text := import("text")
|
|
2
|
+
|
|
3
|
+
matches := []
|
|
4
|
+
lines := text.split(scope, "\n")
|
|
5
|
+
|
|
6
|
+
// byte starts (CRLF-safe)
|
|
7
|
+
starts := []
|
|
8
|
+
run := 0
|
|
9
|
+
i := 0
|
|
10
|
+
for i < len(lines) {
|
|
11
|
+
starts = append(starts, run)
|
|
12
|
+
run = run + len(lines[i]) + 1
|
|
13
|
+
i = i + 1
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
idRe := `^\[\[[A-Za-z0-9_-]+\]\]$`
|
|
17
|
+
h2plusRe := `^={2,}\s+.+$`
|
|
18
|
+
commentRe := `^\s*//`
|
|
19
|
+
condRe := `^\s*(ifdef|ifndef|ifeval)::`
|
|
20
|
+
|
|
21
|
+
j := 0
|
|
22
|
+
for j < len(lines) {
|
|
23
|
+
line := lines[j]
|
|
24
|
+
|
|
25
|
+
// Check if this is a section heading (level 2+)
|
|
26
|
+
if text.re_match(h2plusRe, line) {
|
|
27
|
+
// Walk backward from j-1 to find the ID, skipping comments/conditionals
|
|
28
|
+
hasID := false
|
|
29
|
+
k := j - 1
|
|
30
|
+
|
|
31
|
+
for k >= 0 {
|
|
32
|
+
candidate := text.trim_space(lines[k])
|
|
33
|
+
|
|
34
|
+
// Skip empty lines, comments, and conditionals
|
|
35
|
+
if candidate == "" || text.re_match(commentRe, candidate) || text.re_match(condRe, candidate) {
|
|
36
|
+
k = k - 1
|
|
37
|
+
continue
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Check if this line is an ID
|
|
41
|
+
if text.re_match(idRe, candidate) {
|
|
42
|
+
hasID = true
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Stop at first non-skippable line
|
|
46
|
+
break
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if !hasID {
|
|
50
|
+
begin := starts[j]
|
|
51
|
+
end := begin + len(line)
|
|
52
|
+
matches = append(matches, {"begin": begin, "end": end})
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
j = j + 1
|
|
56
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
text := import("text")
|
|
2
|
+
|
|
3
|
+
matches := []
|
|
4
|
+
lines := text.split(scope, "\n")
|
|
5
|
+
|
|
6
|
+
// byte starts (CRLF-safe)
|
|
7
|
+
starts := []
|
|
8
|
+
run := 0
|
|
9
|
+
i := 0
|
|
10
|
+
for i < len(lines) {
|
|
11
|
+
starts = append(starts, run)
|
|
12
|
+
run = run + len(lines[i]) + 1
|
|
13
|
+
i = i + 1
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
commentRe := `^\s*//`
|
|
17
|
+
condRe := `^\s*(ifdef|ifndef|ifeval)::`
|
|
18
|
+
idRe := `^\[\[[A-Za-z0-9_-]+\]\]$`
|
|
19
|
+
h1Re := `^==\s+.+$`
|
|
20
|
+
|
|
21
|
+
j := 0
|
|
22
|
+
for j < len(lines) {
|
|
23
|
+
line := lines[j]
|
|
24
|
+
|
|
25
|
+
// Check if this is a level-1 heading
|
|
26
|
+
if text.re_match(h1Re, line) {
|
|
27
|
+
// Step 1: Walk backward from heading to find ID (required)
|
|
28
|
+
k := j - 1
|
|
29
|
+
idLineNum := -1
|
|
30
|
+
|
|
31
|
+
// Skip backwards over comments/conditionals between ID and heading
|
|
32
|
+
for k >= 0 {
|
|
33
|
+
candidate := text.trim_space(lines[k])
|
|
34
|
+
|
|
35
|
+
// Skip comments and conditionals
|
|
36
|
+
if text.re_match(commentRe, candidate) || text.re_match(condRe, candidate) {
|
|
37
|
+
k = k - 1
|
|
38
|
+
continue
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// If blank, no ID found
|
|
42
|
+
if candidate == "" {
|
|
43
|
+
break
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Check if this is the ID
|
|
47
|
+
if text.re_match(idRe, candidate) {
|
|
48
|
+
idLineNum = k
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Stop at first non-comment/conditional line
|
|
52
|
+
break
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// If no ID was found, flag this heading
|
|
56
|
+
if idLineNum == -1 {
|
|
57
|
+
begin := starts[j]
|
|
58
|
+
end := begin + len(line)
|
|
59
|
+
matches = append(matches, {"begin": begin, "end": end})
|
|
60
|
+
j = j + 1
|
|
61
|
+
continue
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Step 2: Walk backward from ID to find start of section header block
|
|
65
|
+
// Section header includes: opening conditionals/comments that are adjacent to ID (no blanks between)
|
|
66
|
+
m := idLineNum - 1
|
|
67
|
+
sectionStart := idLineNum
|
|
68
|
+
|
|
69
|
+
for m >= 0 {
|
|
70
|
+
candidate := text.trim_space(lines[m])
|
|
71
|
+
|
|
72
|
+
// If blank, stop - section header block ends here
|
|
73
|
+
if candidate == "" {
|
|
74
|
+
break
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// If comment or conditional, it's part of section header block
|
|
78
|
+
if text.re_match(commentRe, candidate) || text.re_match(condRe, candidate) {
|
|
79
|
+
sectionStart = m
|
|
80
|
+
m = m - 1
|
|
81
|
+
continue
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Hit other content, stop
|
|
85
|
+
break
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Step 3: Count blank lines immediately before section header block start
|
|
89
|
+
n := sectionStart - 1
|
|
90
|
+
blanks := 0
|
|
91
|
+
|
|
92
|
+
for n >= 0 {
|
|
93
|
+
candidate := text.trim_space(lines[n])
|
|
94
|
+
|
|
95
|
+
// Count consecutive blank lines
|
|
96
|
+
if candidate == "" {
|
|
97
|
+
blanks = blanks + 1
|
|
98
|
+
n = n - 1
|
|
99
|
+
continue
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Hit non-blank, stop counting
|
|
103
|
+
break
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// If we reached the start of file without hitting content, don't require blanks
|
|
107
|
+
if n < 0 {
|
|
108
|
+
j = j + 1
|
|
109
|
+
continue
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Require exactly 2 blank lines before the section header block
|
|
113
|
+
if blanks != 2 {
|
|
114
|
+
begin := starts[j]
|
|
115
|
+
end := begin + len(line)
|
|
116
|
+
matches = append(matches, {"begin": begin, "end": end})
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
j = j + 1
|
|
121
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
text := import("text")
|
|
2
|
+
|
|
3
|
+
matches := []
|
|
4
|
+
|
|
5
|
+
lines := text.split(scope, "\n")
|
|
6
|
+
|
|
7
|
+
starts := []
|
|
8
|
+
run := 0
|
|
9
|
+
i := 0
|
|
10
|
+
for i < len(lines) {
|
|
11
|
+
line := lines[i]
|
|
12
|
+
starts = append(starts, run)
|
|
13
|
+
run = run + len(line) + 1
|
|
14
|
+
i = i + 1
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// require an alnum/closer BEFORE .!? to avoid leading "." list markers
|
|
18
|
+
boundary := `[A-Za-z0-9\)\]"'][.!?]['")\]]*[ \t]+[A-Z]`
|
|
19
|
+
|
|
20
|
+
j := 0
|
|
21
|
+
for j < len(lines) {
|
|
22
|
+
line := lines[j]
|
|
23
|
+
|
|
24
|
+
// skip attribute/table/heading lines
|
|
25
|
+
if text.re_match(`^[:|=]`, line) {
|
|
26
|
+
j = j + 1
|
|
27
|
+
continue
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// skip numbered list items (e.g., "1. Something") unless prefaced by comment chars
|
|
31
|
+
// Allow: // 1. Something or # 1. Another thing
|
|
32
|
+
// Don't allow: 1. Something
|
|
33
|
+
if text.re_match(`^\d+\.\s+\w`, line) && !text.re_match(`^[/#]+\s*\d+\.\s+\w`, line) {
|
|
34
|
+
j = j + 1
|
|
35
|
+
continue
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// skip ALL commented lines that start with number and period (like "# 1. Something")
|
|
39
|
+
if text.re_match(`^[/#]+\s*\d+\.\s`, line) {
|
|
40
|
+
j = j + 1
|
|
41
|
+
continue
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if text.re_match(boundary, line) {
|
|
45
|
+
begin := starts[j]
|
|
46
|
+
end := begin + len(line)
|
|
47
|
+
matches = append(matches, {"begin": begin, "end": end})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
j = j + 1
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// no explicit return
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# DocOps Lab pre-commit hook template
|
|
3
|
+
# Managed by docopslab-dev gem
|
|
4
|
+
# Developer-friendly: warns about issues but doesn't block commits
|
|
5
|
+
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
echo "๐ช DocOps Lab pre-commit advisory checks..."
|
|
9
|
+
|
|
10
|
+
# Get list of staged files
|
|
11
|
+
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)
|
|
12
|
+
|
|
13
|
+
if [[ -z "$STAGED_FILES" ]]; then
|
|
14
|
+
echo "โน๏ธ No files staged for commit."
|
|
15
|
+
exit 0
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
# Advisory config sync check (non-blocking)
|
|
19
|
+
if [[ -f ".config/docopslab-dev.yml" ]] && command -v bundle &> /dev/null; then
|
|
20
|
+
echo "๐ Checking config sync status..."
|
|
21
|
+
if bundle exec rake labdev:config:sync --dry-run &> /dev/null; then
|
|
22
|
+
echo "โ
Config sync up to date"
|
|
23
|
+
else
|
|
24
|
+
echo "๐ก Config sync available. Run after commit: bundle exec rake labdev:config:sync"
|
|
25
|
+
fi
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
# Quick syntax check on staged Ruby files (non-blocking)
|
|
29
|
+
RUBY_FILES=$(echo "$STAGED_FILES" | grep -E '\.(rb|rake)$|^Rakefile$' || true)
|
|
30
|
+
if [[ -n "$RUBY_FILES" ]]; then
|
|
31
|
+
echo "๐ Quick syntax check on staged Ruby files..."
|
|
32
|
+
for file in $RUBY_FILES; do
|
|
33
|
+
if ! ruby -c "$file" &> /dev/null; then
|
|
34
|
+
echo "โ ๏ธ Syntax error in $file - consider fixing before push"
|
|
35
|
+
fi
|
|
36
|
+
done
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
# For Bash files, ensure proper shebang
|
|
40
|
+
# Look for .sh files recursively but also search for files with 1. no extensions AND 2. a shebang on the first line that includes "bash" or "sh"
|
|
41
|
+
BASH_FILES=$(echo "$STAGED_FILES" | grep -E '\.sh$|^([^./]+)$' | while read -r file; do
|
|
42
|
+
if head -n 1 "$file" | grep -qE '^#!.*\b(bash|sh)\b'; then
|
|
43
|
+
echo "$file"
|
|
44
|
+
fi
|
|
45
|
+
done)
|
|
46
|
+
if [[ -n "$BASH_FILES" ]]; then
|
|
47
|
+
for file in $BASH_FILES; do
|
|
48
|
+
if head -n 1 "$file" | grep -qv '^#!/usr/bin/env bash'; then
|
|
49
|
+
echo "โ Incorrect shebang in $file. Please use '#!/usr/bin/env bash'."
|
|
50
|
+
exit 1
|
|
51
|
+
fi
|
|
52
|
+
done
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
# Show helpful next steps
|
|
56
|
+
echo ""
|
|
57
|
+
echo "๐ก After committing, consider running:"
|
|
58
|
+
echo " โข bundle exec rake labdev:heal # Auto-fix linting issues"
|
|
59
|
+
echo " โข bundle exec rake labdev:assess # Check environment"
|
|
60
|
+
echo " โข bundle exec rake labdev:lint:all # Full quality check"
|
|
61
|
+
echo ""
|
|
62
|
+
echo "โ
Commit proceeding (full quality gate runs on push)"
|
|
63
|
+
exit 0
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# DocOps Lab pre-push hook template
|
|
3
|
+
# Managed by docopslab-dev gem
|
|
4
|
+
# Runs comprehensive linting before pushing to prevent issues in CI
|
|
5
|
+
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
echo "๐ DocOps Lab pre-push quality gate..."
|
|
9
|
+
|
|
10
|
+
# Check if we have any commits to push
|
|
11
|
+
LOCAL=$(git rev-parse @)
|
|
12
|
+
REMOTE=$(git rev-parse '@{u}' 2>/dev/null || echo "")
|
|
13
|
+
BASE=$(git merge-base @ '@{u}' 2>/dev/null || echo "")
|
|
14
|
+
|
|
15
|
+
if [[ -z "$REMOTE" ]]; then
|
|
16
|
+
echo "โน๏ธ No remote tracking branch found, skipping pre-push checks."
|
|
17
|
+
exit 0
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
if [[ "$LOCAL" = "$REMOTE" ]]; then
|
|
21
|
+
echo "โน๏ธ No new commits to push."
|
|
22
|
+
exit 0
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
if git diff --cached --name-only | grep -q 'Gemfile'; then
|
|
26
|
+
if git diff --cached -U0 | grep -E '^\+.*gem [a-zA-Z0-9_-]+ .+ path:\s*['"'"'"].+['"'"'"]'; then
|
|
27
|
+
echo ""
|
|
28
|
+
echo "โ ๏ธ Warning: Detected gems with local path: parameters in Gemfile changes."
|
|
29
|
+
echo "๐ก Consider using published gems from RubyGems.org or Git hosting services instead."
|
|
30
|
+
echo ""
|
|
31
|
+
fi
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
echo "๐ Checking commits from $BASE to $LOCAL..."
|
|
35
|
+
|
|
36
|
+
# Run all linters on the codebase
|
|
37
|
+
echo "๐ Running comprehensive linting..."
|
|
38
|
+
|
|
39
|
+
# Check config sync status first
|
|
40
|
+
if [[ -f ".config/docopslab-dev.yml" ]]; then
|
|
41
|
+
echo "๐ Verifying config sync..."
|
|
42
|
+
if command -v bundle &> /dev/null; then
|
|
43
|
+
if ! bundle exec rake labdev:config:sync --dry-run &> /dev/null; then
|
|
44
|
+
echo "โ Config sync needed. Run: bundle exec rake labdev:config:sync"
|
|
45
|
+
exit 1
|
|
46
|
+
fi
|
|
47
|
+
echo "โ
Config sync verified"
|
|
48
|
+
fi
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# Run all linters
|
|
52
|
+
if command -v bundle &> /dev/null; then
|
|
53
|
+
echo "๐งน Running all linters..."
|
|
54
|
+
if ! bundle exec rake labdev:lint:all; then
|
|
55
|
+
echo ""
|
|
56
|
+
echo "โ Quality gate failed!"
|
|
57
|
+
echo "๐ก Suggested workflow:"
|
|
58
|
+
echo " 1. Run: bundle exec rake labdev:heal"
|
|
59
|
+
echo " 2. Review changes with: git diff"
|
|
60
|
+
echo " 3. Commit auto-fixes: git commit -am 'Auto-fix linting issues'"
|
|
61
|
+
echo " 4. Fix remaining issues manually"
|
|
62
|
+
echo " 5. Try pushing again"
|
|
63
|
+
echo ""
|
|
64
|
+
echo "๐ซ Or bypass with: git push --no-verify"
|
|
65
|
+
exit 1
|
|
66
|
+
fi
|
|
67
|
+
else
|
|
68
|
+
echo "โ ๏ธ Bundle not available, skipping linting checks"
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
echo "โ
Pre-push quality gate passed"
|
|
72
|
+
exit 0
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
def generate_id_from_heading heading
|
|
4
|
+
heading.gsub(/\b(a|an|the|and|or|but|for|nor|on|at|to|from|by|in|of|is|are|was|were|be|being|been)\b/i, '')
|
|
5
|
+
.gsub(/[^\w\s-]/, '').strip
|
|
6
|
+
.squeeze(' ')
|
|
7
|
+
.gsub(' ', '-').downcase
|
|
8
|
+
heading.strip.downcase.gsub(/[^a-z0-9]+/, '-').gsub(/^-|-$/, '')
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def process_adoc_file file_path
|
|
12
|
+
lines = File.readlines(file_path)
|
|
13
|
+
updated_lines = []
|
|
14
|
+
i = 0
|
|
15
|
+
|
|
16
|
+
while i < lines.size
|
|
17
|
+
line = lines[i]
|
|
18
|
+
if line =~ /^(={2,})\s([A-Z].*)$/ # Match AsciiDoc headings (2+ = chars)
|
|
19
|
+
Regexp.last_match(1)
|
|
20
|
+
heading_text = Regexp.last_match(2)
|
|
21
|
+
|
|
22
|
+
# Check if previous line is an explicit ID
|
|
23
|
+
if i.zero? || lines[i - 1] !~ /^\[\[.*\]\]$/
|
|
24
|
+
generated_id = generate_id_from_heading(heading_text)
|
|
25
|
+
updated_lines << "[[#{generated_id}]]\n"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
updated_lines << line
|
|
29
|
+
i += 1
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
File.write(file_path, updated_lines.join)
|
|
33
|
+
puts "Processed file: #{file_path}"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# We need an alternative to optparse, hopefully using no dependencies
|
|
37
|
+
|
|
38
|
+
if ARGV.empty?
|
|
39
|
+
puts 'Usage: ruby adoc_section_ids.rb <asciidoc_file1> [<asciidoc_file2> ...]'
|
|
40
|
+
exit 1
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
ARGV.each do |file_path|
|
|
44
|
+
unless File.exist?(file_path)
|
|
45
|
+
puts "File not found: #{file_path}"
|
|
46
|
+
next
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
process_adoc_file(file_path)
|
|
50
|
+
end
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Common build functions for DocOps Lab Ruby gem projects
|
|
3
|
+
# This library provides reusable functions for building gems and Docker images
|
|
4
|
+
|
|
5
|
+
# Colors for output
|
|
6
|
+
RED='\033[0;31m'
|
|
7
|
+
GREEN='\033[0;32m'
|
|
8
|
+
YELLOW='\033[1;33m'
|
|
9
|
+
NC='\033[0m' # No Color
|
|
10
|
+
|
|
11
|
+
# Project configuration - these should be set by the calling script
|
|
12
|
+
PROJECT_NAME="${PROJECT_NAME:-$(basename "$(pwd)")}"
|
|
13
|
+
DOCKER_ORG="${DOCKER_ORG:-docopslab}"
|
|
14
|
+
GEMSPEC_FILE="${GEMSPEC_FILE:-${PROJECT_NAME}.gemspec}"
|
|
15
|
+
CLI_EXECUTABLE="${CLI_EXECUTABLE:-exe/${PROJECT_NAME}}"
|
|
16
|
+
EXAMPLE_FILE="${EXAMPLE_FILE:-examples/minimal-example.yml}"
|
|
17
|
+
TEST_SPEC_PATH="${TEST_SPEC_PATH:-specs/tests/rspec/}"
|
|
18
|
+
|
|
19
|
+
# Common validation functions
|
|
20
|
+
check_project_root() {
|
|
21
|
+
if [ ! -f "$GEMSPEC_FILE" ]; then
|
|
22
|
+
echo -e "${RED}โ Error: $GEMSPEC_FILE not found. Run this script from the project root.${NC}"
|
|
23
|
+
exit 1
|
|
24
|
+
fi
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
check_docker_available() {
|
|
28
|
+
if ! command -v docker &> /dev/null; then
|
|
29
|
+
echo -e "${RED}โ Error: Docker is not installed or not in PATH${NC}"
|
|
30
|
+
exit 1
|
|
31
|
+
fi
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
check_git_clean() {
|
|
35
|
+
if [ -n "$(git status --porcelain)" ]; then
|
|
36
|
+
echo -e "${RED}โ Error: Working directory is not clean. Commit or stash changes first.${NC}"
|
|
37
|
+
git status --short
|
|
38
|
+
exit 1
|
|
39
|
+
fi
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
check_main_branch() {
|
|
43
|
+
current_branch=$(git branch --show-current)
|
|
44
|
+
if [ "$current_branch" != "main" ]; then
|
|
45
|
+
echo -e "${YELLOW}โ ๏ธ Warning: Not on main branch (currently on: $current_branch)${NC}"
|
|
46
|
+
read -p "Continue anyway? (y/N): " -n 1 -r
|
|
47
|
+
echo
|
|
48
|
+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
49
|
+
echo "Aborted."
|
|
50
|
+
exit 1
|
|
51
|
+
fi
|
|
52
|
+
fi
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
check_bundle_installed() {
|
|
56
|
+
if [ ! -f "Gemfile" ]; then
|
|
57
|
+
echo -e "${YELLOW}โ ๏ธ Warning: No Gemfile found. Some operations may require dependencies.${NC}"
|
|
58
|
+
return
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
if ! bundle check > /dev/null 2>&1; then
|
|
62
|
+
echo -e "${YELLOW}๐ฆ Installing gem dependencies...${NC}"
|
|
63
|
+
bundle install
|
|
64
|
+
fi
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
# Get current version from README.adoc by parsing directly
|
|
68
|
+
get_current_version() {
|
|
69
|
+
grep '^:this_prod_vrsn:' README.adoc | sed 's/^:this_prod_vrsn:[[:space:]]*//' | tr -d '\r'
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# Get next version from README.adoc by parsing directly
|
|
73
|
+
get_next_version() {
|
|
74
|
+
grep '^:next_prod_vrsn:' README.adoc | sed 's/^:next_prod_vrsn:[[:space:]]*//' | tr -d '\r'
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
# Docker build and test functions
|
|
78
|
+
build_docker_image() {
|
|
79
|
+
local version=$1
|
|
80
|
+
local docker_args="${2:-}"
|
|
81
|
+
|
|
82
|
+
echo -e "${YELLOW}๐ณ Building Docker image...${NC}"
|
|
83
|
+
# shellcheck disable=SC2086
|
|
84
|
+
docker build ${docker_args} -t "${DOCKER_ORG}/${PROJECT_NAME}:${version}" .
|
|
85
|
+
docker tag "${DOCKER_ORG}/${PROJECT_NAME}:${version}" "${DOCKER_ORG}/${PROJECT_NAME}:latest"
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
test_docker_image() {
|
|
89
|
+
local version=$1
|
|
90
|
+
local image_name="${DOCKER_ORG}/${PROJECT_NAME}:${version}"
|
|
91
|
+
|
|
92
|
+
echo -e "${YELLOW}๐งช Testing Docker image...${NC}"
|
|
93
|
+
docker run --rm -v "$(pwd):/workdir" "${image_name}" --version
|
|
94
|
+
|
|
95
|
+
if [ -f "$EXAMPLE_FILE" ]; then
|
|
96
|
+
docker run --rm -v "$(pwd):/workdir" "${image_name}" "${EXAMPLE_FILE}" --dry
|
|
97
|
+
fi
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
# Test functions
|
|
101
|
+
run_rspec_tests() {
|
|
102
|
+
if [ -d "$TEST_SPEC_PATH" ]; then
|
|
103
|
+
echo -e "${YELLOW}๐งช Running RSpec tests...${NC}"
|
|
104
|
+
bundle exec rspec "$TEST_SPEC_PATH" --format documentation
|
|
105
|
+
else
|
|
106
|
+
echo -e "${YELLOW}โ ๏ธ No RSpec tests found at $TEST_SPEC_PATH${NC}"
|
|
107
|
+
fi
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
test_cli_functionality() {
|
|
111
|
+
if [ -x "$CLI_EXECUTABLE" ]; then
|
|
112
|
+
echo -e "${YELLOW}๐งช Testing CLI functionality...${NC}"
|
|
113
|
+
$CLI_EXECUTABLE --version
|
|
114
|
+
|
|
115
|
+
if [ -f "$EXAMPLE_FILE" ]; then
|
|
116
|
+
$CLI_EXECUTABLE "$EXAMPLE_FILE" --dry
|
|
117
|
+
fi
|
|
118
|
+
else
|
|
119
|
+
echo -e "${YELLOW}โ ๏ธ CLI executable not found at $CLI_EXECUTABLE${NC}"
|
|
120
|
+
fi
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
# Gem build functions
|
|
124
|
+
build_gem() {
|
|
125
|
+
echo -e "${YELLOW}๐ Building gem...${NC}"
|
|
126
|
+
bundle exec rake build
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
test_built_gem() {
|
|
130
|
+
local current_version
|
|
131
|
+
current_version=$(get_current_version)
|
|
132
|
+
local gem_file="pkg/${PROJECT_NAME}-${current_version}.gem"
|
|
133
|
+
|
|
134
|
+
if [ ! -f "$gem_file" ]; then
|
|
135
|
+
echo -e "${RED}โ Error: Expected gem file not found: $gem_file${NC}"
|
|
136
|
+
exit 1
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
echo -e "${GREEN}โ
Built gem: $gem_file${NC}"
|
|
140
|
+
echo "$gem_file"
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
# Success message functions
|
|
144
|
+
show_build_success() {
|
|
145
|
+
local version=$1
|
|
146
|
+
local gem_file=$2
|
|
147
|
+
|
|
148
|
+
echo
|
|
149
|
+
echo -e "${GREEN}๐ Build completed successfully!${NC}"
|
|
150
|
+
echo "=================================="
|
|
151
|
+
echo -e "${GREEN}๐ Version: $version${NC}"
|
|
152
|
+
echo -e "${GREEN}๐ Gem: $gem_file${NC}"
|
|
153
|
+
echo -e "${GREEN}๐ณ Docker: ${DOCKER_ORG}/${PROJECT_NAME}:$version${NC}"
|
|
154
|
+
echo
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
show_docker_success() {
|
|
158
|
+
local version=$1
|
|
159
|
+
|
|
160
|
+
echo
|
|
161
|
+
echo -e "${GREEN}๐ Docker build completed successfully!${NC}"
|
|
162
|
+
echo "=================================="
|
|
163
|
+
echo -e "${GREEN}๐ Version: $version${NC}"
|
|
164
|
+
echo -e "${GREEN}๐ณ Images built:${NC}"
|
|
165
|
+
echo " ${DOCKER_ORG}/${PROJECT_NAME}:$version"
|
|
166
|
+
echo " ${DOCKER_ORG}/${PROJECT_NAME}:latest"
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
# Version bump functions
|
|
170
|
+
bump_version() {
|
|
171
|
+
local current_version
|
|
172
|
+
local next_version
|
|
173
|
+
current_version=$(get_current_version)
|
|
174
|
+
next_version=$(get_next_version)
|
|
175
|
+
|
|
176
|
+
if [ "$current_version" = "$next_version" ]; then
|
|
177
|
+
echo -e "${RED}โ Error: Current and next versions are the same: $current_version${NC}"
|
|
178
|
+
echo "Update :next_prod_vrsn: in README.adoc first"
|
|
179
|
+
exit 1
|
|
180
|
+
fi
|
|
181
|
+
|
|
182
|
+
echo -e "${YELLOW}๐ Bumping version from $current_version to $next_version...${NC}"
|
|
183
|
+
|
|
184
|
+
# Update the current version to match next version
|
|
185
|
+
sed -i "s/^:this_prod_vrsn: $current_version/:this_prod_vrsn: $next_version/" README.adoc
|
|
186
|
+
|
|
187
|
+
# Commit the version bump
|
|
188
|
+
git add README.adoc
|
|
189
|
+
git commit -m "Release v$next_version"
|
|
190
|
+
git tag "v$next_version"
|
|
191
|
+
|
|
192
|
+
echo -e "${GREEN}โ
Version bumped and tagged: v$next_version${NC}"
|
|
193
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Generic Docker build script for DocOps Lab projects
|
|
3
|
+
# Usage: Set PROJECT_NAME and DOCKER_ORG, then run this script
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
# Load common build functions from centrally managed location
|
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
9
|
+
|
|
10
|
+
# Try to find build-common.sh in various locations
|
|
11
|
+
if [ -f "$SCRIPT_DIR/build-common.sh" ]; then
|
|
12
|
+
# shellcheck source=build-common.sh
|
|
13
|
+
source "$SCRIPT_DIR/build-common.sh"
|
|
14
|
+
elif [ -f "$SCRIPT_DIR/lib/build-common.sh" ]; then
|
|
15
|
+
# shellcheck source=build-common.sh
|
|
16
|
+
source "$SCRIPT_DIR/lib/build-common.sh"
|
|
17
|
+
elif [ -f "scripts/.vendor/docopslab/build-common.sh" ]; then
|
|
18
|
+
# shellcheck source=build-common.sh
|
|
19
|
+
source "scripts/.vendor/docopslab/build-common.sh"
|
|
20
|
+
else
|
|
21
|
+
echo "โ Error: build-common.sh not found. Run 'rake labdev:config:sync' to get centrally managed scripts."
|
|
22
|
+
exit 1
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# Project configuration - override these in your calling script or environment
|
|
26
|
+
PROJECT_NAME="${PROJECT_NAME:-$(basename "$(pwd)")}"
|
|
27
|
+
DOCKER_ORG="${DOCKER_ORG:-docopslab}"
|
|
28
|
+
|
|
29
|
+
echo -e "${GREEN}๐ณ ${PROJECT_NAME} Docker Build Script${NC}"
|
|
30
|
+
echo "=================================="
|
|
31
|
+
|
|
32
|
+
# Validation
|
|
33
|
+
check_project_root
|
|
34
|
+
check_docker_available
|
|
35
|
+
|
|
36
|
+
# Get current version
|
|
37
|
+
current_version=$(get_current_version)
|
|
38
|
+
echo -e "${GREEN}๐ Current version: $current_version${NC}"
|
|
39
|
+
|
|
40
|
+
# Check if gem exists in pkg/, if not build it
|
|
41
|
+
gem_file="pkg/${PROJECT_NAME}-$current_version.gem"
|
|
42
|
+
if [ ! -f "$gem_file" ]; then
|
|
43
|
+
echo -e "${YELLOW}๐จ Gem not found in pkg/. Building gem first...${NC}"
|
|
44
|
+
check_bundle_installed
|
|
45
|
+
build_gem
|
|
46
|
+
echo -e "${GREEN}โ
Gem built: $gem_file${NC}"
|
|
47
|
+
else
|
|
48
|
+
echo -e "${GREEN}๐ Using existing gem: $gem_file${NC}"
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# Build and test Docker image
|
|
52
|
+
build_docker_image "$current_version"
|
|
53
|
+
test_docker_image "$current_version"
|
|
54
|
+
|
|
55
|
+
# Show success message
|
|
56
|
+
show_docker_success "$current_version"
|
|
57
|
+
|
|
58
|
+
echo
|
|
59
|
+
echo "Test the image with:"
|
|
60
|
+
echo " docker run --rm -v \$(pwd):/workdir ${DOCKER_ORG}/${PROJECT_NAME}:$current_version --version"
|
|
61
|
+
|
|
62
|
+
if [ -f "$EXAMPLE_FILE" ]; then
|
|
63
|
+
echo " docker run --rm -v \$(pwd):/workdir ${DOCKER_ORG}/${PROJECT_NAME}:$current_version $EXAMPLE_FILE --dry"
|
|
64
|
+
fi
|