@kirchdev/gitignore-sync 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Titus Kirch
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,179 @@
1
+ <div align="center">
2
+
3
+ # ๐Ÿงฐ gitignore-sync
4
+
5
+ **Keeps your `.gitignore` maintained, not generated โ€” curated blocks in a region the tool owns, everything else untouched**
6
+
7
+ [![npm Version](https://img.shields.io/npm/v/@kirchdev/gitignore-sync.svg?style=flat-square&color=4f46e5)](https://www.npmjs.com/package/@kirchdev/gitignore-sync)
8
+ [![Downloads](https://img.shields.io/npm/dm/@kirchdev/gitignore-sync.svg?style=flat-square&color=4f46e5)](https://www.npmjs.com/package/@kirchdev/gitignore-sync)
9
+ [![Tests](https://img.shields.io/github/actions/workflow/status/kirchDev/gitignore-sync/ci.yml?branch=main&style=flat-square&label=tests)](https://github.com/kirchDev/gitignore-sync/actions/workflows/ci.yml)
10
+ [![Node Version](https://img.shields.io/node/v/@kirchdev/gitignore-sync.svg?style=flat-square&color=8993be)](https://www.npmjs.com/package/@kirchdev/gitignore-sync)
11
+ [![License: MIT](https://img.shields.io/npm/l/@kirchdev/gitignore-sync.svg?style=flat-square&color=10b981)](LICENSE)
12
+
13
+ </div>
14
+
15
+ ---
16
+
17
+ ```bash
18
+ npx @kirchdev/gitignore-sync init # fingerprint the repo, confirm, write the region
19
+ npx @kirchdev/gitignore-sync sync # re-render it, any time, as often as you like
20
+ ```
21
+
22
+ That's it. The blocks the tool owns stay current; every line you wrote yourself survives verbatim.
23
+
24
+ ## ๐Ÿค” Why
25
+
26
+ `gitignore.io`, `gig` and `ignr` all _fetch and dump_: you pull a template once, paste it, and drift from there. None of them re-syncs, so a `.gitignore` grows into a few hundred lines of ballast nobody dares touch.
27
+
28
+ `gitignore-sync` keeps a **managed region** inside the file and re-renders it on demand. The rest of the file is a free zone the tool never writes to โ€” which is what makes running it a second time safe, and a hundredth time boring.
29
+
30
+ ## ๐Ÿ“ฆ Installation
31
+
32
+ ```bash
33
+ pnpm add -g @kirchdev/gitignore-sync # npm i -g / yarn global add / bun add -g all work
34
+ ```
35
+
36
+ Or run it without installing: `npx @kirchdev/gitignore-sync <command>`. The binary is also available as `gis`.
37
+
38
+ ## ๐Ÿš€ Quick start
39
+
40
+ ```bash
41
+ gitignore-sync init # detect, confirm in a prompt, write the region
42
+ gitignore-sync edit # tick stacks on and off later
43
+ gitignore-sync add nuxt tauri # or name them โ€” for scripts and CI
44
+ gitignore-sync remove intellij # drop one; your own lines are kept
45
+ gitignore-sync sync # re-render from the header
46
+ gitignore-sync check # CI gate: non-zero on drift
47
+ gitignore-sync list # what this binary ships, and what you declare
48
+ gitignore-sync info # which build is running, and what it sees
49
+ gitignore-sync audit ../*/ # how much of a whole estate the stacks cover
50
+ ```
51
+
52
+ Every command takes `--help` and a directory (`--dir` for `add`/`remove`, positional elsewhere), and every writing command takes `--dry-run`.
53
+
54
+ > [!TIP]
55
+ > `init` shows everything it found โ€” your editor and platform included โ€” and asks once:
56
+ >
57
+ > ```
58
+ > โ„น Found 8 stacks: core, git, node, nuxt, tauri, dotenv, vscode, linux
59
+ > ? Use them? โ€บ Yes / No
60
+ > ```
61
+ >
62
+ > In CI it asks nothing โ€” `--yes` or no terminal takes the committed fingerprints alone, says what it skipped, and writes. Your platform must not end up in a committed header because a pipeline ran.
63
+
64
+ ## โœจ Features
65
+
66
+ - **๐Ÿ” Re-syncs, never re-dumps** โ€” `sync` is idempotent, so it belongs in a habit, a hook or a cron, not in a one-off ritual.
67
+ - **๐Ÿ›ก๏ธ Your lines are never lost** โ€” a hand-written line found inside a managed block is *moved* to the free zone, not deleted. That rule is what makes the second run safe.
68
+ - **๐Ÿ” Dedup that knows git** โ€” exact duplicates go; `.idea`, `.idea/`, `/.idea` and `.idea/*` are four different patterns to git, so they are **reported**, never silently merged.
69
+ - **๐Ÿงน Orphaned headings swept** โ€” when a managed block absorbs every pattern under a `# Comment`, the heading goes with them. A block that was only ever a note stays.
70
+ - **โš ๏ธ Catches the mistake that breaks `!`** โ€” a stray `.vscode` beside a `!.vscode/extensions.json` block silently disables it, because git never looks inside an ignored directory. That gets its own warning.
71
+ - **๐Ÿงญ No surprise rewrites** โ€” `init` detects, `sync` does not. Adding a `package.json` never quietly rewrites your `.gitignore`; `sync --detect` proposes and stops there.
72
+ - **๐Ÿ—‚๏ธ Folds in your editor** โ€” the region uses `# region` / `# endregion`, so VSCode collapses a 40-line managed block to a single line, at both nesting levels.
73
+ - **๐Ÿ“‹ A real CI gate** โ€” `check` reports drift and duplicates and exits non-zero, so a stale block fails the build instead of rotting.
74
+
75
+ ## ๐Ÿ—‚๏ธ The shape
76
+
77
+ ```gitignore
78
+ # region gitignore-sync
79
+ # stacks: core, node, vscode, intellij
80
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
81
+
82
+ # region core@v1
83
+ .DS_Store
84
+ .claude/settings.local.json
85
+ # endregion
86
+
87
+ # region node@v1
88
+ node_modules
89
+ dist
90
+ coverage
91
+ # endregion
92
+
93
+ # endregion
94
+
95
+ # โ”€โ”€โ”€ your rules, never touched โ”€โ”€โ”€
96
+ frankenphp
97
+ /bootstrap/ssr
98
+ ```
99
+
100
+ Everything below the closing `# endregion` is the **free zone**: yours, preserved line for line.
101
+
102
+ ## ๐Ÿงฉ Stacks
103
+
104
+ Twenty curated blocks, each derived from what the estate actually ignores rather than from a public template site.
105
+
106
+ | Stack | What it covers | Proposed by `init` when |
107
+ | :--------- | :----------------------------------- | :---------------------------- |
108
+ | `core` | `.DS_Store`, agent-local settings | always |
109
+ | `git` | merge and backup droppings | always (it is a git repo) |
110
+ | `node` | modules, build output, logs, caches | `package.json` |
111
+ | `dotenv` | `.env*`, minus the committed example | `.env.example` or `.env` |
112
+ | `php` | `/vendor`, PHPUnit caches | `composer.json` |
113
+ | `laravel` | build output, storage keys, SSR | `artisan` |
114
+ | `go` | test and build droppings | `go.mod` |
115
+ | `tofu` | state, tfvars, local overrides | `*.tf` / `*.tofu` |
116
+ | `nuxt` | `.nuxt`, `.output`, `.nitro` | `nuxt.config.*` |
117
+ | `tauri` | `src-tauri/target`, generated schemas| `src-tauri/` |
118
+ | `rust` | `/target` | a root `Cargo.toml` |
119
+ | `turborepo`| `.turbo` | `turbo.json` |
120
+ | `playwright` | test-results, reports | `playwright.config.*` |
121
+ | `storybook`| `storybook-static` | `.storybook/` |
122
+ | `vscode` | `.vscode/*` + the shared files | you have a `.vscode/` |
123
+ | `intellij` | `.idea/*` | you have a `.idea/` |
124
+ | `vim` | swap and session files | your `$EDITOR` is vim |
125
+ | `macos` | AppleDouble, Spotlight, Trashes | you are on a Mac |
126
+ | `windows` | Thumbs.db, desktop.ini, Recycle Bin | you are on Windows |
127
+ | `linux` | `*~`, trash and NFS droppings | you are on Linux |
128
+
129
+ `gitignore-sync list` prints this for the binary you have installed, marking the ones your repo declares.
130
+
131
+ ## โš™๏ธ Configuration
132
+
133
+ There is no config file. The configuration is the `# stacks:` line in the `.gitignore` itself โ€” the header is **input**, the blocks below it are **output**.
134
+
135
+ | Edit | Effect after `sync` |
136
+ | :-------------------------------- | :--------------------------------------------- |
137
+ | Add a name to `# stacks:` | Its block is rendered into the region |
138
+ | Remove a name from `# stacks:` | Its block disappears; your own lines are kept |
139
+ | Nothing | Nothing โ€” `sync` is a no-op on a synced file |
140
+
141
+ > [!IMPORTANT]
142
+ > Nothing in a repository can tell you what the **other** contributors use โ€” `.vscode/` and `.idea/` are themselves ignored. So those stacks are proposed from your own machine and written down once, rather than re-guessed on every run. Only repository fingerprints reach the non-interactive path; a CI runner's platform must not end up in a committed header.
143
+
144
+ ## ๐Ÿงช Use in CI
145
+
146
+ ```yaml
147
+ - run: npx @kirchdev/gitignore-sync check
148
+ ```
149
+
150
+ `check` writes nothing. It exits non-zero when the file has drifted from its header, and prints what it would have changed.
151
+
152
+ There is also an action, for a job summary on failure and a `status` output:
153
+
154
+ ```yaml
155
+ - uses: kirchDev/gitignore-sync@v0.1.0
156
+ with:
157
+ version: '0.1.0' # pin it, so a release cannot turn a green pipeline red
158
+ ```
159
+
160
+ > [!TIP]
161
+ > Repos whose CI derives its task list from `package.json` need neither โ€” add
162
+ > `gitignore-sync check` to your `check` script and it comes along.
163
+
164
+ ## ๐Ÿค Contributing
165
+
166
+ PRs welcome. Conventional Commits required (enforced via commitlint). Husky runs the project's linters/formatters on `git commit`.
167
+
168
+ > [!TIP]
169
+ > Run `pnpm check:fix` before pushing โ€” CI will catch what husky missed.
170
+
171
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for the full workflow.
172
+
173
+ ## ๐Ÿ›ฃ๏ธ Versioning
174
+
175
+ [Semantic Versioning](https://semver.org/) via [release-please](https://github.com/googleapis/release-please) โ€” see [CHANGELOG.md](CHANGELOG.md).
176
+
177
+ ## ๐Ÿ“„ License
178
+
179
+ [MIT](LICENSE) ยฉ [Titus Kirch](https://github.com/TitusKirch/) / [IT-Dienstleistungen Titus Kirch](https://kirch.dev)
package/action.yml ADDED
@@ -0,0 +1,63 @@
1
+ name: 'gitignore-sync'
2
+ description: 'Check that a repository .gitignore still matches the stacks its header declares.'
3
+ author: 'Titus Kirch'
4
+
5
+ branding:
6
+ icon: 'git-branch'
7
+ color: 'green'
8
+
9
+ inputs:
10
+ directory:
11
+ description: 'Repository directory holding the .gitignore to check.'
12
+ required: false
13
+ default: '.'
14
+ version:
15
+ description: >-
16
+ Version of @kirchdev/gitignore-sync to run. Pin it so a new release cannot turn a
17
+ green pipeline red on its own.
18
+ required: false
19
+ default: 'latest'
20
+ node-version:
21
+ description: 'Node version used to run the CLI.'
22
+ required: false
23
+ default: '24'
24
+
25
+ outputs:
26
+ status:
27
+ description: '"in-sync" or "drifted".'
28
+ value: ${{ steps.check.outputs.status }}
29
+
30
+ # A composite action rather than a bundled JavaScript one: this wraps a single
31
+ # published CLI whose exit code is the whole result. Bundling the tool into
32
+ # dist/index.js would mean shipping the same code twice and keeping the two in
33
+ # step โ€” see kirchDev/coverage-report for the case where bundling does earn its
34
+ # keep, because that action writes comments and check runs of its own.
35
+ runs:
36
+ using: 'composite'
37
+ steps:
38
+ - uses: actions/setup-node@v7
39
+ with:
40
+ node-version: ${{ inputs.node-version }}
41
+
42
+ - id: check
43
+ shell: bash
44
+ env:
45
+ GIS_DIR: ${{ inputs.directory }}
46
+ GIS_VERSION: ${{ inputs.version }}
47
+ run: |
48
+ set -o pipefail
49
+ if npx --yes "@kirchdev/gitignore-sync@${GIS_VERSION}" check "${GIS_DIR}" 2>&1 | tee "${RUNNER_TEMP}/gitignore-sync.log"; then
50
+ echo 'status=in-sync' >> "${GITHUB_OUTPUT}"
51
+ else
52
+ echo 'status=drifted' >> "${GITHUB_OUTPUT}"
53
+ {
54
+ echo '### `.gitignore` is out of sync'
55
+ echo
56
+ echo 'Run `gitignore-sync sync` and commit the result.'
57
+ echo
58
+ echo '```'
59
+ cat "${RUNNER_TEMP}/gitignore-sync.log"
60
+ echo '```'
61
+ } >> "${GITHUB_STEP_SUMMARY}"
62
+ exit 1
63
+ fi