-
Notifications
You must be signed in to change notification settings - Fork 1
feat/phase1 markdown powershell #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| root = true | ||
|
|
||
| # Language/framework-agnostic base. Covers universal defaults plus the | ||
| # languages with a presence in this repo (Markdown, shell, PowerShell) and the | ||
| # generic Windows-native batch class. Language overlays (.NET, Python, …) extend | ||
| # this file with their own source sections when adopted; they do not ship a | ||
| # separate editorconfig. | ||
| # | ||
| # .gitattributes is the single authority for line endings (it transforms bytes | ||
| # on checkout); end_of_line here is an editor hint only, and editorconfig-checker | ||
| # leaves the end-of-line check disabled. See modules/editorconfig/. | ||
|
|
||
| [*] | ||
| indent_style = space | ||
| indent_size = 4 | ||
| charset = utf-8 | ||
| trim_trailing_whitespace = true | ||
| insert_final_newline = true | ||
| end_of_line = lf | ||
|
|
||
| # Markdown — trailing whitespace is significant (two trailing spaces = a hard | ||
| # line break); indentation is variable per CommonMark. | ||
| [*.{md,markdown}] | ||
| trim_trailing_whitespace = false | ||
| indent_size = unset | ||
|
|
||
| # Config / serialization / data | ||
| [*.{json,jsonc,yml,yaml,toml}] | ||
| indent_size = 2 | ||
|
|
||
| # Shell | ||
| [*.{sh,bash}] | ||
| indent_size = 2 | ||
|
|
||
| # PowerShell — LF runs on both PowerShell 7 and Windows PowerShell 5.1 (verified | ||
| # empirically). See the .gitattributes note before changing this to crlf. | ||
| [*.{ps1,psm1,psd1}] | ||
| indent_size = 4 | ||
|
|
||
| # Windows batch — cmd.exe requires CRLF. | ||
| [*.{cmd,bat}] | ||
| end_of_line = crlf | ||
|
|
||
| # Lockfiles — generated; suppress formatting enforcement. | ||
| [{package-lock.json,*.lock}] | ||
| indent_size = unset | ||
| insert_final_newline = unset | ||
| trim_trailing_whitespace = unset |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| # Language/framework-agnostic base. `* text=auto eol=lf` normalizes text to LF in | ||
| # the repository AND checks it out LF on every platform (deterministic, regardless | ||
| # of a developer's core.autocrlf). Windows-native types below override to CRLF. A | ||
| # later line overrides an earlier one, per attribute. This file is the single | ||
| # authority for line endings (editorconfig end_of_line is an editor hint only). | ||
|
|
||
| * text=auto eol=lf | ||
|
|
||
| ############################################################################### | ||
| # Richer diffs (line endings inherit the LF default above). | ||
| ############################################################################### | ||
|
|
||
| *.md diff=markdown | ||
| *.sh diff=bash | ||
| *.bash diff=bash | ||
|
|
||
| ############################################################################### | ||
| # PowerShell — LF is deliberate. Verified to run on both PowerShell 7 and | ||
| # Windows PowerShell 5.1. The PowerShell repo pins .ps1 to eol=lf; the popular | ||
| # community gitattributes template pins crlf — they target different eras. Listed | ||
| # explicitly so it is not "corrected" to crlf without a requirement to round-trip | ||
| # pre-existing CRLF-signed scripts unchanged. | ||
| ############################################################################### | ||
|
|
||
| *.ps1 text eol=lf | ||
| *.psm1 text eol=lf | ||
| *.psd1 text eol=lf | ||
|
|
||
| ############################################################################### | ||
| # Windows-native — cmd.exe requires CRLF (overrides the LF default above). | ||
| ############################################################################### | ||
|
|
||
| *.cmd text eol=crlf | ||
| *.bat text eol=crlf | ||
|
|
||
| ############################################################################### | ||
| # Lockfiles — tracked, but suppress noisy diffs (regenerate to resolve). | ||
| ############################################################################### | ||
|
|
||
| package-lock.json -diff | ||
| *.lock -diff | ||
|
|
||
| ############################################################################### | ||
| # Binary — no line-ending conversion, no diff. | ||
| ############################################################################### | ||
|
|
||
| *.png binary | ||
| *.jpg binary | ||
| *.jpeg binary | ||
| *.gif binary | ||
| *.ico binary | ||
| *.webp binary | ||
| *.pdf binary | ||
| *.zip binary | ||
| *.gz binary | ||
| *.7z binary | ||
| *.woff binary | ||
| *.woff2 binary | ||
| *.ttf binary | ||
| *.otf binary |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| version: 2 | ||
| updates: | ||
| # Keep the actions and reusable workflows referenced by SHA current. Review | ||
| # every PR before merge; never auto-merge (a SHA bump is executable CI logic). | ||
| - package-ecosystem: github-actions | ||
| directory: / | ||
| schedule: | ||
| interval: weekly |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| name: ci | ||
|
|
||
| on: | ||
| push: | ||
| pull_request: | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| # Dogfood: this repo is the first consumer of its own reusable workflows. Each | ||
| # lane runs against this repo's vendored config copy; the local ci-status | ||
| # gateway aggregates them into the single required check. | ||
| jobs: | ||
| markdown: | ||
| uses: ./.github/workflows/markdown.yml | ||
|
|
||
| powershell: | ||
| uses: ./.github/workflows/powershell.yml | ||
|
|
||
| ci-status: | ||
| if: always() | ||
| needs: [markdown, powershell] | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Aggregate lane results | ||
| env: | ||
| RESULTS: ${{ needs.markdown.result }} ${{ needs.powershell.result }} | ||
| run: | | ||
| for r in $RESULTS; do | ||
| case "$r" in | ||
| success|skipped) ;; | ||
| *) echo "A lane did not pass (result: $r)."; exit 1 ;; | ||
| esac | ||
| done | ||
| echo "All lanes passed or were skipped." |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| name: markdown | ||
|
|
||
| on: | ||
| workflow_call: | ||
| inputs: | ||
| globs: | ||
| description: >- | ||
| markdownlint-cli2 glob arguments, space-separated (word-split on the | ||
| runner). Prefix a glob with '!' to exclude. Default lints all markdown. | ||
| type: string | ||
| default: '**/*.md' | ||
| config: | ||
| description: Path to the markdownlint-cli2 config file in the caller repo. | ||
| type: string | ||
| default: modules/markdown/.markdownlint-cli2.jsonc | ||
| version: | ||
| description: Exact markdownlint-cli2 version to run. | ||
| type: string | ||
| default: 0.22.1 | ||
| node-version: | ||
| description: Node.js version to set up. | ||
| type: string | ||
| default: 24.17.0 | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| markdownlint: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Check out | ||
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | ||
| - name: Set up Node | ||
| uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 | ||
| with: | ||
| node-version: ${{ inputs.node-version }} | ||
| - name: Lint markdown | ||
| env: | ||
| CONFIG: ${{ inputs.config }} | ||
| VERSION: ${{ inputs.version }} | ||
| GLOBS: ${{ inputs.globs }} | ||
| # $GLOBS is intentionally unquoted so multiple glob arguments word-split. | ||
| run: npx --yes "markdownlint-cli2@$VERSION" --config "$CONFIG" $GLOBS | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| name: powershell | ||
|
|
||
| on: | ||
| workflow_call: | ||
| inputs: | ||
| paths: | ||
| description: >- | ||
| Files/dirs to analyze (relative to the caller repo), space-separated. | ||
| Directories recurse. Default analyzes the whole repo. | ||
| type: string | ||
| default: '.' | ||
| settings: | ||
| description: Path to the PSScriptAnalyzerSettings.psd1 ruleset in the caller repo. | ||
| type: string | ||
| default: modules/powershell/PSScriptAnalyzerSettings.psd1 | ||
| exclude: | ||
| description: >- | ||
| Path substrings to skip, space-separated (e.g. intentionally-bad | ||
| fixtures). The .git directory is always skipped. | ||
| type: string | ||
| default: '' | ||
| analyzer-version: | ||
| description: Exact PSScriptAnalyzer version to install and require. | ||
| type: string | ||
| default: 1.25.0 | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| pssa: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Check out caller | ||
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | ||
| with: | ||
| path: caller | ||
|
|
||
| # The runner script lives in this repo, not the caller's; fetch it via the | ||
| # job context (PAT-free — this repo is already authorized for the run). | ||
| - name: Check out runner script | ||
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | ||
| with: | ||
| repository: ${{ job.workflow_repository }} | ||
| ref: ${{ job.workflow_sha }} | ||
|
Comment on lines
+44
to
+45
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In cross-repo consumers of this reusable workflow, these expressions are empty because the documented contexts expose Useful? React with 👍 / 👎. |
||
| path: runner | ||
|
|
||
| - name: Ensure PSScriptAnalyzer | ||
| shell: pwsh | ||
| env: | ||
| REQUIRED: ${{ inputs.analyzer-version }} | ||
| run: | | ||
| $required = $env:REQUIRED | ||
| $have = Get-Module -ListAvailable PSScriptAnalyzer | | ||
| Where-Object { $_.Version -eq [version]$required } | ||
| if (-not $have) { | ||
| try { | ||
| Install-Module PSScriptAnalyzer -RequiredVersion $required -Scope CurrentUser -Force -SkipPublisherCheck -ErrorAction Stop | ||
| } catch { | ||
| Install-PSResource PSScriptAnalyzer -Version $required -Scope CurrentUser -TrustRepository -Reinstall -ErrorAction Stop | ||
| } | ||
| } | ||
| Import-Module PSScriptAnalyzer -RequiredVersion $required -ErrorAction Stop | ||
| Write-Output "PSScriptAnalyzer $((Get-Module PSScriptAnalyzer).Version) on pwsh $($PSVersionTable.PSVersion)" | ||
|
|
||
| - name: Analyze | ||
| working-directory: caller | ||
| shell: pwsh | ||
| env: | ||
| PATHS: ${{ inputs.paths }} | ||
| SETTINGS: ${{ inputs.settings }} | ||
| EXCLUDE: ${{ inputs.exclude }} | ||
| REQUIRED: ${{ inputs.analyzer-version }} | ||
| run: | | ||
| # Wrap in @() so an empty input stays an empty array, never $null | ||
| # (a bare null would violate the runner's non-null parameter contract). | ||
| $paths = @($env:PATHS -split '\s+' | Where-Object { $_ }) | ||
| $exclude = @($env:EXCLUDE -split '\s+' | Where-Object { $_ }) | ||
| ../runner/scripts/Invoke-Pssa.ps1 -Path $paths -Settings $env:SETTINGS -AnalyzerVersion $env:REQUIRED -ExcludePath $exclude | ||
| exit $LASTEXITCODE | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| # OS / editor noise | ||
| .DS_Store | ||
| Thumbs.db | ||
| *.swp | ||
| .idea/ | ||
| .vs/ | ||
| .vscode/* | ||
| !.vscode/extensions.json | ||
|
|
||
| # Claude Code worktrees (per-checkout; PR worktree workflow) | ||
| .claude/worktrees/ | ||
|
|
||
| # Node | ||
| node_modules/ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,44 @@ | ||
| # ci-workflows | ||
| Reusable, configurable CI workflows (referenced via uses:@sha). Consumers feed a local ci-status gate. | ||
|
|
||
| Reusable, configurable CI **execution** for the melodic-software org: the | ||
| workflows that install and run each code-quality tool, plus the runner scripts | ||
| they call. | ||
|
|
||
| Consumed by reference, never copied: | ||
|
|
||
| ```yaml | ||
| jobs: | ||
| markdown: | ||
| uses: melodic-software/ci-workflows/.github/workflows/markdown.yml@<sha> | ||
| ``` | ||
|
|
||
| ## Contract | ||
|
|
||
| - **Configurable, not forkable.** Each reusable workflow exposes typed `inputs` | ||
| with global-standard defaults. Consumers override repo-specific scope (globs, | ||
| paths, tool versions, config location) through inputs — never by editing the | ||
| workflow. | ||
| - **Pin by SHA.** Reference every reusable workflow at a full commit SHA; | ||
| Dependabot (`github-actions`, weekly) opens bump PRs that are reviewed and | ||
| merged manually. | ||
| - **Each consumer aggregates locally.** A reusable workflow runs one tool. The | ||
| required-check contract is a single check named `ci-status`, produced by a | ||
| thin gateway job the consumer keeps local so the required-check name stays | ||
| un-nested. The gateway `needs:` the called jobs and fails if any failed or was | ||
| cancelled; a skipped job is allowed. | ||
|
|
||
| ## Workflows | ||
|
|
||
| - `markdown.yml` — markdownlint-cli2 over the repo's markdown. | ||
| - `powershell.yml` — PSScriptAnalyzer over the repo's PowerShell, via | ||
| `scripts/Invoke-Pssa.ps1` (per-file subprocess isolation). | ||
|
|
||
| Each input's meaning and default is documented inline in the workflow's | ||
| `inputs:` block. | ||
|
|
||
| ## Tool configuration lives elsewhere | ||
|
|
||
| These workflows execute tools; they do not carry the tools' rulesets. A consumer | ||
| supplies its own config file and points the workflow at it through an input, so | ||
| adopting a workflow never couples the consumer to this repo at runtime beyond | ||
| the referenced workflow itself. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| { | ||
| // GitHub Flavored Markdown (GFM) ruleset for markdownlint-cli2. | ||
| // Schema pinned to markdownlint-cli2 v0.22.1 — bump together on upgrade. | ||
| // Rules reference: https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md | ||
| // All rules are enabled by default; only deviations are listed below. | ||
| // Globs are passed by the caller (CLI / CI), not declared here. | ||
| "$schema": "https://raw.githubusercontent.com/DavidAnson/markdownlint-cli2/v0.22.1/schema/markdownlint-cli2-config-schema.json", | ||
| "ignores": [ | ||
| // Build, dependency, and cache trees — not authored markdown. | ||
| "**/node_modules/**", | ||
| "**/.venv/**", | ||
| "**/bin/**", | ||
| "**/obj/**" | ||
| ], | ||
| "config": { | ||
| // --- GFM-aligned style --- | ||
| "MD003": { "style": "atx" }, // ATX headings (# Heading) | ||
| "MD004": { "style": "dash" }, // Dash unordered-list bullets | ||
| "MD049": { "style": "asterisk" }, // *emphasis* | ||
| "MD050": { "style": "asterisk" }, // **strong** | ||
| "MD046": { "style": "fenced" }, // Fenced (not indented) code blocks | ||
| "MD048": { "style": "backtick" }, // Backtick (not tilde) code fences | ||
| "MD024": { "siblings_only": true }, // Allow duplicate headings under different parents | ||
| "MD055": { "style": "consistent" }, // Consistent table pipe style within a file | ||
| "MD060": false, // Don't enforce one table-column style across mixed compact/padded tables | ||
|
|
||
| // --- Relaxed for GFM / prose --- | ||
| "MD013": false, // No hard line-length limit (tables and code exceed 80) | ||
| "MD025": false, // Allow multiple top-level (H1) sections | ||
| "MD028": false, // Allow blank lines between adjacent blockquotes | ||
| "MD033": false, // Allow inline HTML (<details>, <summary>, <br>, ...) | ||
| "MD034": false, // Allow bare URLs (GFM autolinks them) | ||
| "MD036": false, // Allow bold text used as a pseudo-heading | ||
| "MD040": false, // Fenced code need not declare a language | ||
| "MD041": false // First line need not be a top-level heading (frontmatter) | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When a caller uses the default
**/*.mdin a repo with a match likedocs/guide.md, this unquoted expansion is performed by the GitHub runner's Bash before markdownlint-cli2 sees it; with Bashglobstaroff by default, the shell passes only its one-directory matches and omits root/deeper files such asREADME.md/a/b/file.md. markdownlint-cli2's own usage notes recommend quoting glob arguments because shells expand globs differently, so the advertised default no longer lints all Markdown in common layouts.Useful? React with 👍 / 👎.