Compare commits

24 Commits
Author SHA1 Message Date
stefan 170c8ea981 fix(ci): add missing base_url to gitea-release plugin
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2026-03-29 17:24:55 +02:00
stefan 7dcf04c568 fix(ci): use golang image for release-notes step to ensure bash is available
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is failing
2026-03-29 17:18:22 +02:00
stefan b3d80f1ae3 ci: update release-notes step to use sh instead of bash
continuous-integration/drone/tag Build is failing
2026-03-29 17:14:21 +02:00
stefan 2410c16559 docs: prepare v0.6.0 release
continuous-integration/drone/tag Build is failing
2026-03-29 17:09:13 +02:00
stefan e25da626eb docs: add conventional changelog for upcoming v0.6.0 release 2026-03-29 17:06:24 +02:00
stefan 8d17f889f0 ci: auto-generate release notes from commits
continuous-integration/drone/push Build is passing
2026-03-29 16:49:27 +02:00
stefan 40834fdfce ci: fix trigger to run on push to all branches
continuous-integration/drone/push Build is passing
2026-03-29 16:40:38 +02:00
stefan b349586755 Merge branch 'master' into 'main': accept master drone config 2026-03-29 16:36:53 +02:00
stefan 109d62efa3 ci: run tests on all commits, release only on tags 2026-03-29 16:34:14 +02:00
stefan 5e8660ebd1 fix: correct drone.yml yaml schema - use arrays for event and status 2026-03-29 16:30:12 +02:00
stefan 0ebb212b65 docs: add release process documentation 2026-03-29 16:27:41 +02:00
stefan 91c2a313f2 ci: add tag-based release pipeline with coverage and source archive 2026-03-29 16:04:02 +02:00
stefan 2e267ac6dd ci: move coverage validation to separate script for better readability
continuous-integration/drone/push Build is passing
2026-03-29 15:48:21 +02:00
stefan 0e150f0ba9 ci: fix coverage flag parsing for Linux shell compatibility
continuous-integration/drone/push Build is failing
2026-03-29 15:45:18 +02:00
stefan 6ca2c4d25f ci: improve drone test checks and coverage reporting
continuous-integration/drone/push Build is failing
2026-03-29 15:37:35 +02:00
stefan d01242ae7c docs(dod): enforce markdownlint rule for markdown files 2026-03-29 15:14:33 +02:00
stefan 6236717fdb Refactor documentation for clarity and consistency, update test function naming convention 2026-03-29 15:01:08 +02:00
stefan 2ec2fcd2ab Fix formatting of variable declarations in os_windows.go 2026-03-29 14:54:46 +02:00
stefan 8ace13074f Update CI pipeline to use Go 1.25.8, enhance Definition of Done with SonarQube checks, refactor SID allocation in Windows functions, and improve test coverage for JoiningSlash function. 2026-03-29 14:54:31 +02:00
stefan 1da053fb67 - apply latest AGENTS and DoD templates
- replace PowerShell reconcile script with shell-based reconcile workflow
- add .gitattributes, .editorconfig, and project-local git hooks
- configure .githooks as core.hooksPath
- refresh .gitignore entries from the shared standard
2026-03-29 14:15:29 +02:00
stefan 05a61bba86 Generalize build
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2023-03-08 20:04:40 +00:00
stefan 2242ffd0cb Updated build container
continuous-integration/drone/push Build is passing
2023-03-08 19:38:57 +00:00
stefan eb050d5b84 Updated build container
continuous-integration/drone/push Build is passing
2023-03-08 19:35:44 +00:00
stefan f997c15873 No redirect
continuous-integration/drone/push Build is failing
2023-03-08 19:31:51 +00:00
20 changed files with 536 additions and 285 deletions
+54 -3
View File
@@ -2,11 +2,62 @@ kind: pipeline
type: docker type: docker
name: go-lib/util name: go-lib/util
trigger:
event:
- push
- tag
ref:
include:
- refs/heads/**
- refs/tags/v*
steps: steps:
- name: test - name: test
image: golang:1.18 image: golang:1.25.8
commands: commands:
- go get ./... - go get ./...
- go test ./... - go vet ./...
- mkdir -p .build
- go test -v -coverprofile .build/coverage.out ./...
- go tool cover -func .build/coverage.out | tee .build/coverage.txt
- bash scripts/check-coverage.sh .build/coverage.out 80
- go install golang.org/x/vuln/cmd/govulncheck@latest - go install golang.org/x/vuln/cmd/govulncheck@latest
- govulncheck -v -json ./... > vulncheck.json - govulncheck -json ./... > vulncheck.json
- name: release-notes
image: golang:1.25.8
commands:
- bash scripts/generate-release-notes.sh
when:
event:
- tag
status:
- success
- name: package
image: golang:1.25.8
commands:
- tar czf .build/sources.tar.gz --exclude=.build --exclude=.git --exclude=.drone.yml .
when:
event:
- tag
status:
- success
- name: release
image: plugins/gitea-release
settings:
base_url: https://scm.yoorie.de
api_key:
from_secret: gitea_token
files:
- .build/coverage.txt
- .build/sources.tar.gz
- .build/release-notes.md
title: ${DRONE_TAG}
note_from_file: .build/release-notes.md
when:
event:
- tag
status:
- success
-3
View File
@@ -14,6 +14,3 @@ indent_size = 4
[*.md] [*.md]
trim_trailing_whitespace = false trim_trailing_whitespace = false
[*.ps1]
end_of_line = crlf
-1
View File
@@ -13,6 +13,5 @@
Makefile text eol=lf Makefile text eol=lf
# Keep native Windows script formats # Keep native Windows script formats
*.ps1 text eol=crlf
*.bat text eol=crlf *.bat text eol=crlf
*.cmd text eol=crlf *.cmd text eol=crlf
+5
View File
@@ -14,3 +14,8 @@ The pre-commit hook validates for staged `.sh` files:
- executable bit in Git index (`100755`) - executable bit in Git index (`100755`)
- LF line endings (no CRLF) - LF line endings (no CRLF)
The pre-commit hook also validates staged `.md` files with `markdownlint`:
- no `markdownlint` errors or problems
- requires `markdownlint` CLI in PATH (for example via `npm install --global markdownlint-cli`)
+15
View File
@@ -5,6 +5,7 @@ failed=0
cr=$(printf '\r') cr=$(printf '\r')
staged_shell_files=$(git diff --cached --name-only --diff-filter=ACMR | grep -E '\.sh$' || true) staged_shell_files=$(git diff --cached --name-only --diff-filter=ACMR | grep -E '\.sh$' || true)
staged_markdown_files=$(git diff --cached --name-only --diff-filter=ACMR | grep -E '\.md$' || true)
for file in $staged_shell_files; do for file in $staged_shell_files; do
mode=$(git ls-files --stage -- "$file" | awk '{print $1}') mode=$(git ls-files --stage -- "$file" | awk '{print $1}')
@@ -19,6 +20,20 @@ for file in $staged_shell_files; do
fi fi
done done
if [ -n "$staged_markdown_files" ]; then
if ! command -v markdownlint >/dev/null 2>&1; then
echo "ERROR: markdownlint is required to validate staged Markdown files (.md)." >&2
echo "Install with npm: npm install --global markdownlint-cli" >&2
failed=1
else
# Validate the staged markdown files currently present in the working tree.
# This keeps the hook simple and fast for standard project usage.
if ! markdownlint $staged_markdown_files; then
failed=1
fi
fi
fi
if [ "$failed" -ne 0 ]; then if [ "$failed" -ne 0 ]; then
echo "Pre-commit check failed." >&2 echo "Pre-commit check failed." >&2
exit 1 exit 1
+1
View File
@@ -11,3 +11,4 @@ coverage.out
Thumbs.db Thumbs.db
.vscode/ .vscode/
.idea/ .idea/
+48 -9
View File
@@ -1,14 +1,20 @@
# AGENTS.md # AGENTS.md
## Purpose ## Purpose
This file defines the default working agreement for AI coding agents and contributors in this repository.
This file defines the default working agreement for AI coding agents
and contributors in this repository.
## Documentation Language ## Documentation Language
Project language is English. All documentation, issues, pull requests, commit messages, and code comments should be written in English.
Project language is English. All documentation, issues, pull requests,
commit messages, and code comments should be written in English.
All newly created documentation in this project must be written in English. All newly created documentation in this project must be written in English.
All project documentation files must be stored under `docs/`, except `README.md` and `AGENTS.md`. All project documentation files must be stored under `docs/`,
except `README.md` and `AGENTS.md`.
## Working Principles ## Working Principles
1. Keep changes minimal and focused on the requested task. 1. Keep changes minimal and focused on the requested task.
2. Preserve existing public APIs unless a breaking change is explicitly requested. 2. Preserve existing public APIs unless a breaking change is explicitly requested.
3. Prefer clear, maintainable code over clever shortcuts. 3. Prefer clear, maintainable code over clever shortcuts.
@@ -16,28 +22,46 @@ All project documentation files must be stored under `docs/`, except `README.md`
5. Never add secrets, credentials, or tokens to the repository. 5. Never add secrets, credentials, or tokens to the repository.
## Testing Expectations ## Testing Expectations
1. Add or update tests for behavior changes. 1. Add or update tests for behavior changes.
2. Keep tests deterministic and fast. 2. Keep tests deterministic and fast.
3. Prefer table-driven tests where they improve readability. 3. Prefer table-driven tests where they improve readability.
4. Run relevant tests locally before finishing changes. 4. Run relevant tests locally before finishing changes.
5. For Go projects, use `github.com/smartystreets/goconvey` as the standard test library. 5. For Go projects, use `github.com/smartystreets/goconvey`
as the standard test library.
## Build Artifacts and Reports ## Build Artifacts and Reports
1. Builder logs and generated reports must be created under `.build/`. 1. Builder logs and generated reports must be created under `.build/`.
2. The `.build/` directory must be excluded from version control via `.gitignore`. 2. The `.build/` directory must be excluded from version control via `.gitignore`.
## Git and Script Standards ## Git and Script Standards
1. Shell scripts (`*.sh`) must use LF line endings. 1. Shell scripts (`*.sh`) must use LF line endings.
2. Shell scripts committed to the repository must be executable in Git index (mode `100755`). 2. Shell scripts committed to the repository must be executable
3. When adding a new shell script, set execute permissions before commit: `git add --chmod=+x path/to/script.sh`. in Git index (mode `100755`).
3. When adding a new shell script, set execute permissions
before commit: `git add --chmod=+x path/to/script.sh`.
## Git Bash Execution Defaults
1. Repository maintenance scripts are executed with Git Bash shell on Windows.
2. Default repository root is `~/git`.
3. The repository root can be overridden via `GO_GIT_ROOT`.
4. The standards repository defaults to `$ROOT_PATH/project-standards`.
5. The standards repository path can be overridden via `GO_PROJECT_STANDARDS`.
## Definition of Done (DoD) ## Definition of Done (DoD)
### Purpose ### DoD Purpose
The Definition of Done defines the minimum quality bar for every completed change in this repository.
The Definition of Done defines the minimum quality bar
for every completed change in this repository.
### Mandatory Criteria ### Mandatory Criteria
1. Tests 1. Tests
- Every code change is covered by tests where applicable. - Every code change is covered by tests where applicable.
- New functionality includes new tests. - New functionality includes new tests.
- Bug fixes include at least one regression test. - Bug fixes include at least one regression test.
@@ -45,28 +69,41 @@ The Definition of Done defines the minimum quality bar for every completed chang
- Automated test coverage is at least 80%. - Automated test coverage is at least 80%.
1. Functional documentation 1. Functional documentation
- Implemented functionality is documented. - Implemented functionality is documented.
- Public API-relevant changes are reflected in README and/or docs. - Public API-relevant changes are reflected in README and/or docs.
1. Documentation standards 1. Documentation standards
- Documentation is written in English. - Documentation is written in English.
- Documentation files are placed under `docs/`. - Documentation files are placed under `docs/`.
- Exceptions: `README.md` and `AGENTS.md` remain at repository root. - Exceptions: `README.md` and `AGENTS.md` remain at repository root.
- Markdown files have no `markdownlint` errors or problems.
### Technical Completion Criteria ### Technical Completion Criteria
1. Build and test status 1. Build and test status
- The project builds successfully. - The project builds successfully.
- Relevant test commands run successfully. - Relevant test commands run successfully.
1. No unresolved critical issues 1. No unresolved critical issues
- No new blocking errors are introduced. - No new blocking errors are introduced.
- Known non-blocking warnings are acceptable only if unrelated to the change or documented. - Known non-blocking warnings are acceptable
only if unrelated to the change or documented.
1. SonarQube status
- No SonarQube errors are present.
1. Documentation structure 1. Documentation structure
- Links to moved or newly added docs are valid. - Links to moved or newly added docs are valid.
- Documentation structure remains consistent with project rules. - Documentation structure remains consistent with project rules.
### Review Checklist (Quick) ### Review Checklist (Quick)
- [ ] Change is implemented and meets acceptance criteria. - [ ] Change is implemented and meets acceptance criteria.
- [ ] Tests were added/updated and pass. - [ ] Tests were added/updated and pass.
- [ ] Go tests use `github.com/smartystreets/goconvey`. - [ ] Go tests use `github.com/smartystreets/goconvey`.
@@ -74,4 +111,6 @@ The Definition of Done defines the minimum quality bar for every completed chang
- [ ] Functionality is documented. - [ ] Functionality is documented.
- [ ] Documentation is in English. - [ ] Documentation is in English.
- [ ] Documentation is located under `docs/` (except `README.md` and `AGENTS.md`). - [ ] Documentation is located under `docs/` (except `README.md` and `AGENTS.md`).
- [ ] Markdown files have no `markdownlint` errors or problems.
- [ ] No SonarQube errors are present.
- [ ] No critical regressions found. - [ ] No critical regressions found.
+52
View File
@@ -0,0 +1,52 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on Conventional Changelog categories.
## [v0.6.0] - 2026-03-29
### Added
- Added tag-based Gitea release workflow with attached artifacts.
- Added automatic release notes generation from commit history.
- Added release process documentation.
- Added project standards files, hooks, and reconciliation scripts.
### Changed
- Updated build container configuration.
- Generalized the Drone build flow for library usage.
- Improved Drone test execution, coverage reporting, and CI quality gates.
- Updated documentation structure and naming consistency.
- Synced AGENTS and Definition of Done templates.
### Fixed
- Fixed Drone YAML schema issues for trigger and status array types.
- Fixed Drone trigger behavior so push builds run on branches.
- Fixed Linux coverage flag handling in CI.
- Fixed formatting issues in Windows-specific code.
### Documentation
- Improved README and DoD documentation clarity.
- Enforced markdownlint rules for DoD documentation.
### CI
- Added `go vet` check to pipeline.
- Added coverage threshold validation (80%) in CI.
- Moved coverage validation into a dedicated script.
- Configured pipeline behavior: build on push and tag, release only on tag.
## [v0.5.0] - 2021-03-03
### Legacy Changes
- Added README.
- Updated modules and build setup.
- Renamed pipeline.
[v0.6.0]: https://scm.yoorie.de/git/go-lib/util/compare/v0.5.0...v0.6.0
[v0.5.0]: https://scm.yoorie.de/git/go-lib/util/compare/v0.0.4...v0.5.0
+3 -5
View File
@@ -1,7 +1,7 @@
<div style="text-align:left"><img src="https://www.yoorie.de/img/favicon_32.png"/></div>
# Go utility library # Go utility library
![yoorie.de logo](https://www.yoorie.de/img/favicon_32.png)
[![Build Status](https://drone.yoorie.de/api/badges/go-lib/util/status.svg)](https://drone.yoorie.de/go-lib/util) [![Build Status](https://drone.yoorie.de/api/badges/go-lib/util/status.svg)](https://drone.yoorie.de/go-lib/util)
## Project Description ## Project Description
@@ -57,6 +57,4 @@ func main() {
``` ```
--- ---
Copyright &copy; 2023 yoorie.de Copyright &copy; 2026 yoorie.de
-1
View File
@@ -1 +0,0 @@
mode: set
+17 -2
View File
@@ -2,11 +2,13 @@
## Purpose ## Purpose
This Definition of Done defines the minimum quality bar for every completed change in this repository. This Definition of Done defines the minimum quality bar
for every completed change in this repository.
## Mandatory Criteria ## Mandatory Criteria
1. Tests 1. Tests
- Every code change is covered by tests where applicable. - Every code change is covered by tests where applicable.
- New functionality includes new tests. - New functionality includes new tests.
- Bug fixes include at least one regression test. - Bug fixes include at least one regression test.
@@ -14,25 +16,36 @@ This Definition of Done defines the minimum quality bar for every completed chan
- Automated test coverage is at least 80%. - Automated test coverage is at least 80%.
1. Functional documentation 1. Functional documentation
- Implemented functionality is documented. - Implemented functionality is documented.
- Public API-relevant changes are reflected in README and/or docs. - Public API-relevant changes are reflected in README and/or docs.
1. Documentation standards 1. Documentation standards
- Documentation is written in English. - Documentation is written in English.
- Documentation files are placed under `docs/`. - Documentation files are placed under `docs/`.
- Exceptions: `README.md` and `AGENTS.md` remain at repository root. - Exceptions: `README.md` and `AGENTS.md` remain at repository root.
- Markdown files have no `markdownlint` errors or problems.
## Technical Completion Criteria ## Technical Completion Criteria
1. Build and test status 1. Build and test status
- The project builds successfully. - The project builds successfully.
- Relevant test commands run successfully. - Relevant test commands run successfully.
1. No unresolved critical issues 1. No unresolved critical issues
- No new blocking errors are introduced. - No new blocking errors are introduced.
- Known non-blocking warnings are acceptable only if unrelated to the change or documented. - Known non-blocking warnings are acceptable
only if unrelated to the change or documented.
1. SonarQube status
- No SonarQube errors are present.
1. Documentation links and structure 1. Documentation links and structure
- Links to moved or newly added docs are valid. - Links to moved or newly added docs are valid.
- Documentation structure remains consistent with project rules. - Documentation structure remains consistent with project rules.
@@ -45,4 +58,6 @@ This Definition of Done defines the minimum quality bar for every completed chan
- [ ] Functionality is documented. - [ ] Functionality is documented.
- [ ] Documentation is in English. - [ ] Documentation is in English.
- [ ] Documentation is located under `docs/` (except `README.md` and `AGENTS.md`). - [ ] Documentation is located under `docs/` (except `README.md` and `AGENTS.md`).
- [ ] Markdown files have no `markdownlint` errors or problems.
- [ ] No SonarQube errors are present.
- [ ] No critical regressions found. - [ ] No critical regressions found.
+180
View File
@@ -0,0 +1,180 @@
# Releasing go-lib/util
This document describes the process for creating a release of the
`go-lib/util` library.
## Overview
Releases in this project are managed via **Git tags**. When you push a
tag matching the pattern `v*` (e.g., `v0.5.0`), the Drone CI/CD
pipeline automatically:
1. Runs all quality checks (tests, coverage, vet, vulnerability scan)
2. Creates a source archive (`sources.tar.gz`)
3. Publishes a release to Gitea with both artifacts
## Release Types
This library follows **semantic versioning**: `MAJOR.MINOR.PATCH` (e.g., `v1.2.3`)
- `v1.0.0` - Major release (breaking API changes)
- `v1.1.0` - Minor release (new features, backward compatible)
- `v1.0.1` - Patch release (bug fixes only)
## Prerequisites
Before creating a release, ensure:
1. **All changes committed**: `git status` shows clean working tree
2. **Tests passing**: Run `go test ./...` locally
3. **Coverage OK**: Coverage must be ≥ 80%
4. **Dependencies updated**: Run `go mod tidy`
5. **CHANGELOG.md updated** (optional but recommended)
## Creating a Release
### Step 1: Prepare Release Commit (Optional)
Update version references if needed (README, docs, etc.):
```bash
# Edit any version references
vim README.md
git add README.md
git commit -m "docs: prepare v0.5.0 release"
```
### Step 2: Create the Git Tag
```bash
# Create annotated tag with release notes
git tag -a v0.5.0 -m "Release v0.5.0: Description of changes"
# Or simple tag (not recommended)
# git tag v0.5.0
```
### Step 3: Push the Tag
```bash
# Push tag to remote
git push origin v0.5.0
# Or push all tags at once
# git push --tags
```
### Step 4: Monitor the Pipeline
1. Navigate to your Drone instance (usually `https://drone.example.com`)
2. Watch the pipeline run through:
- ✓ Test & coverage checks
- ✓ Code quality (vet, vulnerability scan)
- ✓ Create source archive
- ✓ Publish to Gitea release
### Step 5: Verify Release in Gitea
1. Go to your repository on Gitea
2. Click "Releases" section
3. Verify the new release includes:
- Release title: `v0.5.0`
- Attached artifacts:
- `coverage.txt` - Test coverage report
- `sources.tar.gz` - Full source code snapshot
## Using the Released Version
End users can install your library via:
```bash
# Latest version
go get scm.yoorie.de/go-lib/util
# Specific version
go get scm.yoorie.de/go-lib/util@v0.5.0
# Latest patch of a minor version
go get scm.yoorie.de/go-lib/util@v0.5
```
## Tag Naming Convention
- **Release tags**: `v0.5.0` (pushed to trigger Drone pipeline)
- **Pre-release tags** (optional): `v0.5.0-rc1`, `v0.5.0-beta1`
- **Internal tags** (if any): Not recommended; use branches instead
Only tags matching `v*` trigger the release pipeline.
## Rollback / Deleting a Release
If a release has issues:
```bash
# Delete local tag
git tag -d v0.5.0
# Delete remote tag
git push origin :refs/tags/v0.5.0
# Then push a fixed release with the same or new tag
git tag -a v0.5.0-fixed -m "Fixed release"
git push origin v0.5.0-fixed
```
## Troubleshooting
### Pipeline Failed
Check Drone logs:
1. Go to Drone UI
2. Click on failed pipeline
3. Expand step details
4. Review error messages
Common issues:
- **Coverage below 80%**: Ensure tests cover new code
- **Tests failing**: Run locally: `go test -v ./...`
- **Vet errors**: Run: `go vet ./...`
### Release Not Appearing in Gitea
1. Verify `gitea_token` secret is set in Drone
2. Check Drone pipeline output for release step
3. Ensure tag matches pattern `v*`
### Tag Already Exists
If you pushed a tag and need to update it:
```bash
# Force delete and recreate (dangerous - use with caution)
git tag -d v0.5.0
git push origin :refs/tags/v0.5.0
git tag -a v0.5.0 -m "Updated release notes"
git push origin v0.5.0
```
## Release Checklist
- [ ] All code changes reviewed and merged
- [ ] Tests pass locally: `go test -v ./...`
- [ ] Coverage ≥ 80%: Test and check coverage
- [ ] Code quality OK: `go vet ./...`
- [ ] No vulnerabilities: `govulncheck ./...`
- [ ] Dependencies tidy: `go mod tidy`
- [ ] CHANGELOG updated (if maintained)
- [ ] Version references updated (if any)
- [ ] Git tag created: `git tag -a vX.Y.Z -m "message"`
- [ ] Tag pushed: `git push origin vX.Y.Z`
- [ ] Release visible in Gitea
- [ ] Coverage artifact downloaded and verified
## Further Reading
- [Semantic Versioning](https://semver.org/)
- [Go Modules](https://golang.org/doc/modules)
- [Drone CI Documentation](https://docs.drone.io/)
+11 -7
View File
@@ -13,6 +13,16 @@ import (
var ( var (
allocateAndInitializeSid = windows.AllocateAndInitializeSid allocateAndInitializeSid = windows.AllocateAndInitializeSid
allocateAdminGroupSid = func(sid **windows.SID) error {
return allocateAndInitializeSid(
&windows.SECURITY_NT_AUTHORITY,
2,
windows.SECURITY_BUILTIN_DOMAIN_RID,
windows.DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
sid,
)
}
freeSid = windows.FreeSid freeSid = windows.FreeSid
tokenIsMember = func(token windows.Token, sid *windows.SID) (bool, error) { return token.IsMember(sid) } tokenIsMember = func(token windows.Token, sid *windows.SID) (bool, error) { return token.IsMember(sid) }
fatalf = log.Fatalf fatalf = log.Fatalf
@@ -27,13 +37,7 @@ func IsSuperUser() bool {
// official windows documentation. The Go API for this is a // official windows documentation. The Go API for this is a
// direct wrap around the official C++ API. // direct wrap around the official C++ API.
// See https://docs.microsoft.com/en-us/windows/desktop/api/securitybaseapi/nf-securitybaseapi-checktokenmembership // See https://docs.microsoft.com/en-us/windows/desktop/api/securitybaseapi/nf-securitybaseapi-checktokenmembership
err := allocateAndInitializeSid( err := allocateAdminGroupSid(&sid)
&windows.SECURITY_NT_AUTHORITY,
2,
windows.SECURITY_BUILTIN_DOMAIN_RID,
windows.DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&sid)
if err != nil { if err != nil {
fatalf("SID Error: %s", err) fatalf("SID Error: %s", err)
return false return false
+5 -17
View File
@@ -11,33 +11,21 @@ import (
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
) )
func TestIsSuperUser_SidAllocationError(t *testing.T) { func TestIsSuperUserSidAllocationError(t *testing.T) {
Convey("IsSuperUser should return false when SID allocation fails", t, func() { Convey("IsSuperUser should return false when SID allocation fails", t, func() {
origAllocate := allocateAndInitializeSid origAllocate := allocateAdminGroupSid
origFatalf := fatalf origFatalf := fatalf
defer func() { defer func() {
allocateAndInitializeSid = origAllocate allocateAdminGroupSid = origAllocate
fatalf = origFatalf fatalf = origFatalf
}() }()
allocateAndInitializeSid = func( allocateAdminGroupSid = func(_ **windows.SID) error {
authority *windows.SidIdentifierAuthority,
subAuthorityCount byte,
subAuthority0 uint32,
subAuthority1 uint32,
subAuthority2 uint32,
subAuthority3 uint32,
subAuthority4 uint32,
subAuthority5 uint32,
subAuthority6 uint32,
subAuthority7 uint32,
sid **windows.SID,
) error {
return errors.New("forced sid allocation error") return errors.New("forced sid allocation error")
} }
fatalCalled := false fatalCalled := false
fatalf = func(format string, v ...interface{}) { fatalf = func(_ string, _ ...interface{}) {
fatalCalled = true fatalCalled = true
} }
-217
View File
@@ -1,217 +0,0 @@
[CmdletBinding()]
param(
[string]$RootPath = "C:\Users\stefan\git",
[string]$StandardsRepoPath = "C:\Users\stefan\git\project-standards",
[string[]]$Exclude = @('project-standards'),
[switch]$CheckOnly,
[switch]$Watch,
[int]$IntervalSeconds = 60
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
$agentsTemplate = Join-Path $StandardsRepoPath 'templates/AGENTS.base.md'
$dodTemplate = Join-Path $StandardsRepoPath 'templates/DEFINITION_OF_DONE.base.md'
$gitIgnoreTemplate = Join-Path $StandardsRepoPath 'templates/.gitignore.base'
$gitAttributesTemplate = Join-Path $StandardsRepoPath 'templates/.gitattributes.base'
$editorConfigTemplate = Join-Path $StandardsRepoPath 'templates/.editorconfig.base'
$preCommitHookTemplate = Join-Path $StandardsRepoPath 'templates/pre-commit.base.sh'
$hooksReadmeTemplate = Join-Path $StandardsRepoPath 'templates/.githooks.README.base.md'
if (-not (Test-Path -Path $agentsTemplate -PathType Leaf)) {
throw "AGENTS template not found: $agentsTemplate"
}
if (-not (Test-Path -Path $dodTemplate -PathType Leaf)) {
throw "DoD template not found: $dodTemplate"
}
if (-not (Test-Path -Path $gitIgnoreTemplate -PathType Leaf)) {
throw "gitignore template not found: $gitIgnoreTemplate"
}
if (-not (Test-Path -Path $gitAttributesTemplate -PathType Leaf)) {
throw "gitattributes template not found: $gitAttributesTemplate"
}
if (-not (Test-Path -Path $editorConfigTemplate -PathType Leaf)) {
throw "editorconfig template not found: $editorConfigTemplate"
}
if (-not (Test-Path -Path $preCommitHookTemplate -PathType Leaf)) {
throw "pre-commit hook template not found: $preCommitHookTemplate"
}
if (-not (Test-Path -Path $hooksReadmeTemplate -PathType Leaf)) {
throw "hooks readme template not found: $hooksReadmeTemplate"
}
if ($IntervalSeconds -lt 5) {
throw 'IntervalSeconds must be >= 5.'
}
function Get-ContentHashOrMissing {
param([string]$Path)
if (-not (Test-Path -Path $Path -PathType Leaf)) {
return '__MISSING__'
}
return (Get-FileHash -Path $Path -Algorithm SHA256).Hash
}
function Ensure-FileFromTemplate {
param(
[string]$Template,
[string]$Target,
[switch]$OnlyCheck
)
$templateHash = Get-ContentHashOrMissing -Path $Template
$targetHash = Get-ContentHashOrMissing -Path $Target
if ($templateHash -eq $targetHash) {
return 'ok'
}
if ($OnlyCheck) {
return 'drift'
}
$parent = Split-Path -Parent $Target
if (-not (Test-Path -Path $parent -PathType Container)) {
New-Item -ItemType Directory -Path $parent | Out-Null
}
Copy-Item -Path $Template -Destination $Target -Force
return 'updated'
}
function Ensure-GitIgnoreEntriesFromTemplate {
param(
[string]$TemplatePath,
[string]$GitIgnorePath,
[switch]$OnlyCheck
)
$requiredEntries = Get-Content -Path $TemplatePath | Where-Object {
$_.Trim() -and -not $_.Trim().StartsWith('#')
}
if (-not (Test-Path -Path $GitIgnorePath -PathType Leaf)) {
if ($OnlyCheck) {
return 'drift'
}
New-Item -ItemType File -Path $GitIgnorePath | Out-Null
}
$lines = Get-Content -Path $GitIgnorePath
$missingEntry = $false
foreach ($entry in $requiredEntries) {
if ($lines -contains $entry) {
continue
}
$missingEntry = $true
if (-not $OnlyCheck) {
Add-Content -Path $GitIgnorePath -Value $entry
$lines += $entry
}
}
if (-not $missingEntry) {
return 'ok'
}
if ($OnlyCheck) {
return 'drift'
}
return 'updated'
}
function Invoke-ReconcileOnce {
param([switch]$OnlyCheck)
$resolvedRoot = (Resolve-Path -Path $RootPath -ErrorAction Stop).Path
$repos = Get-ChildItem -Path $resolvedRoot -Directory | Where-Object {
$Exclude -notcontains $_.Name
}
$summary = [ordered]@{
scanned = 0
updated = 0
drift = 0
}
foreach ($repo in $repos) {
$repoPath = $repo.FullName
$agentsTarget = Join-Path $repoPath 'AGENTS.md'
$dodTarget = Join-Path (Join-Path $repoPath 'docs') 'DEFINITION_OF_DONE.md'
$gitIgnoreTarget = Join-Path $repoPath '.gitignore'
$gitAttributesTarget = Join-Path $repoPath '.gitattributes'
$editorConfigTarget = Join-Path $repoPath '.editorconfig'
$preCommitHookTarget = Join-Path (Join-Path $repoPath '.githooks') 'pre-commit'
$hooksReadmeTarget = Join-Path (Join-Path $repoPath '.githooks') 'README.md'
$summary.scanned++
$agentsState = Ensure-FileFromTemplate -Template $agentsTemplate -Target $agentsTarget -OnlyCheck:$OnlyCheck
$dodState = Ensure-FileFromTemplate -Template $dodTemplate -Target $dodTarget -OnlyCheck:$OnlyCheck
$gitAttributesState = Ensure-FileFromTemplate -Template $gitAttributesTemplate -Target $gitAttributesTarget -OnlyCheck:$OnlyCheck
$editorConfigState = Ensure-FileFromTemplate -Template $editorConfigTemplate -Target $editorConfigTarget -OnlyCheck:$OnlyCheck
$preCommitHookState = Ensure-FileFromTemplate -Template $preCommitHookTemplate -Target $preCommitHookTarget -OnlyCheck:$OnlyCheck
$hooksReadmeState = Ensure-FileFromTemplate -Template $hooksReadmeTemplate -Target $hooksReadmeTarget -OnlyCheck:$OnlyCheck
$gitIgnoreState = Ensure-GitIgnoreEntriesFromTemplate -TemplatePath $gitIgnoreTemplate -GitIgnorePath $gitIgnoreTarget -OnlyCheck:$OnlyCheck
if (
$agentsState -eq 'updated' -or
$dodState -eq 'updated' -or
$gitAttributesState -eq 'updated' -or
$editorConfigState -eq 'updated' -or
$preCommitHookState -eq 'updated' -or
$hooksReadmeState -eq 'updated' -or
$gitIgnoreState -eq 'updated'
) {
$summary.updated++
Write-Host "UPDATED: $repoPath"
continue
}
if (
$agentsState -eq 'drift' -or
$dodState -eq 'drift' -or
$gitAttributesState -eq 'drift' -or
$editorConfigState -eq 'drift' -or
$preCommitHookState -eq 'drift' -or
$hooksReadmeState -eq 'drift' -or
$gitIgnoreState -eq 'drift'
) {
$summary.drift++
Write-Host "DRIFT: $repoPath"
continue
}
Write-Host "OK: $repoPath"
}
Write-Host "Summary -> scanned=$($summary.scanned), updated=$($summary.updated), drift=$($summary.drift)"
return $summary
}
if ($Watch) {
while ($true) {
$now = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
Write-Host "[$now] Reconciling project standards..."
[void](Invoke-ReconcileOnce -OnlyCheck:$CheckOnly)
Start-Sleep -Seconds $IntervalSeconds
}
}
$result = Invoke-ReconcileOnce -OnlyCheck:$CheckOnly
if ($CheckOnly -and $result.drift -gt 0) {
exit 1
}
+28
View File
@@ -0,0 +1,28 @@
#!/bin/bash
# Check test coverage against minimum threshold
set -e
COVERAGE_FILE="${1:-.build/coverage.out}"
MIN_COVERAGE="${2:-80}"
if [ ! -f "$COVERAGE_FILE" ]; then
echo "Error: Coverage file not found: $COVERAGE_FILE"
exit 1
fi
# Extract coverage percentage using awk
COVERAGE=$(go tool cover -func "$COVERAGE_FILE" | awk '/^total:/ { match($0, /[0-9.]+%/); print substr($0, RSTART, RLENGTH-1) }')
echo "Total coverage: ${COVERAGE}%"
# Compare as integers (remove decimals for simpler comparison)
COVERAGE_INT=${COVERAGE%.*}
if [ "$COVERAGE_INT" -lt "$MIN_COVERAGE" ]; then
echo "Coverage ${COVERAGE}% is below minimum ${MIN_COVERAGE}%"
exit 1
fi
echo "Coverage check passed"
exit 0
+71
View File
@@ -0,0 +1,71 @@
#!/bin/bash
# Generate release notes from commits since the last tag
set -e
CURRENT_TAG="${1:-${DRONE_TAG}}"
OUTPUT_FILE="${2:-.build/release-notes.md}"
if [ -z "$CURRENT_TAG" ]; then
echo "Error: Current tag not provided"
exit 1
fi
# Find the previous tag
PREVIOUS_TAG=$(git describe --tags --abbrev=0 "$CURRENT_TAG"^ 2>/dev/null || echo "")
mkdir -p "$(dirname "$OUTPUT_FILE")"
# Start release notes
{
echo "# Release $CURRENT_TAG"
echo ""
if [ -n "$PREVIOUS_TAG" ]; then
echo "**Changes since $PREVIOUS_TAG:**"
echo ""
# Extract commits and group by type (feat, fix, docs, ci, etc.)
git log "$PREVIOUS_TAG..$CURRENT_TAG" --pretty=format:"%s" | while read -r commit; do
# Parse conventional commits
if [[ $commit =~ ^([a-z]+)(\(.+\))?:\ (.+)$ ]]; then
TYPE="${BASH_REMATCH[1]}"
SCOPE="${BASH_REMATCH[2]}"
MESSAGE="${BASH_REMATCH[3]}"
case "$TYPE" in
feat)
echo "- ✨ **Feature**${SCOPE}: $MESSAGE" ;;
fix)
echo "- 🐛 **Fix**${SCOPE}: $MESSAGE" ;;
docs)
echo "- 📖 **Docs**${SCOPE}: $MESSAGE" ;;
ci)
echo "- 🔧 **CI**${SCOPE}: $MESSAGE" ;;
test)
echo "- ✅ **Test**${SCOPE}: $MESSAGE" ;;
refactor)
echo "- ♻️ **Refactor**${SCOPE}: $MESSAGE" ;;
perf)
echo "- ⚡ **Performance**${SCOPE}: $MESSAGE" ;;
*)
echo "- 📝 $commit" ;;
esac
else
echo "- 📝 $commit"
fi
done
else
echo "Initial release"
echo ""
git log "$CURRENT_TAG" --pretty=format:"- 📝 %s" | head -20
fi
echo ""
echo "---"
echo "See [all changes](https://scm.yoorie.de/git/go-lib/util/compare/$PREVIOUS_TAG...$CURRENT_TAG)"
} > "$OUTPUT_FILE"
echo "Release notes generated: $OUTPUT_FILE"
cat "$OUTPUT_FILE"
+22 -3
View File
@@ -1,8 +1,9 @@
#!/usr/bin/env sh #!/usr/bin/env sh
set -eu set -eu
ROOT_PATH="/c/Users/stefan/git" ROOT_PATH="${GO_GIT_ROOT:-$HOME/git}"
STANDARDS_REPO="/c/Users/stefan/git/project-standards" STANDARDS_REPO=""
STANDARDS_REPO_SET=0
EXCLUDE_NAME="project-standards" EXCLUDE_NAME="project-standards"
CHECK_ONLY=0 CHECK_ONLY=0
WATCH=0 WATCH=0
@@ -21,6 +22,14 @@ Options:
--watch Continuously scan and reconcile --watch Continuously scan and reconcile
--interval <seconds> Watch interval in seconds (default: 60) --interval <seconds> Watch interval in seconds (default: 60)
-h, --help Show this help -h, --help Show this help
Environment:
GO_GIT_ROOT Overrides default root path (default: ~/git)
GO_PROJECT_STANDARDS Overrides standards repository path
Default resolution:
ROOT_PATH defaults to GO_GIT_ROOT or ~/git.
STANDARDS_REPO defaults to GO_PROJECT_STANDARDS or $ROOT_PATH/project-standards.
EOF EOF
} }
@@ -32,6 +41,7 @@ while [ "$#" -gt 0 ]; do
;; ;;
--standards-repo) --standards-repo)
STANDARDS_REPO=$2 STANDARDS_REPO=$2
STANDARDS_REPO_SET=1
shift 2 shift 2
;; ;;
--exclude) --exclude)
@@ -62,6 +72,14 @@ while [ "$#" -gt 0 ]; do
esac esac
done done
if [ "$STANDARDS_REPO_SET" -ne 1 ]; then
if [ -n "${GO_PROJECT_STANDARDS:-}" ]; then
STANDARDS_REPO="$GO_PROJECT_STANDARDS"
else
STANDARDS_REPO="$ROOT_PATH/project-standards"
fi
fi
if [ "$INTERVAL" -lt 5 ]; then if [ "$INTERVAL" -lt 5 ]; then
echo "interval must be >= 5" >&2 echo "interval must be >= 5" >&2
exit 1 exit 1
@@ -162,6 +180,7 @@ ensure_gitignore_entries_from_template() {
fi fi
while IFS= read -r entry; do while IFS= read -r entry; do
entry=$(printf '%s' "$entry" | tr -d '\r')
[ -n "$entry" ] || continue [ -n "$entry" ] || continue
case "$entry" in case "$entry" in
\#*) \#*)
@@ -169,7 +188,7 @@ ensure_gitignore_entries_from_template() {
;; ;;
esac esac
if grep -Fqx "$entry" "$gitignore_path"; then if tr -d '\r' < "$gitignore_path" | grep -Fqx "$entry"; then
continue continue
fi fi
-1
View File
@@ -34,7 +34,6 @@ func joiningSlash(elem []string) string {
} }
func singleJoiningSlash(a, b string) string { func singleJoiningSlash(a, b string) string {
filepath.Join(a, b)
aslash := strings.HasSuffix(a, "/") aslash := strings.HasSuffix(a, "/")
bslash := strings.HasPrefix(b, "/") bslash := strings.HasPrefix(b, "/")
switch { switch {
+14 -6
View File
@@ -23,12 +23,20 @@ func TestFileExists(t *testing.T) {
func TestJoiningSlash(t *testing.T) { func TestJoiningSlash(t *testing.T) {
Convey("JoiningSlash should combine URL-like segments safely", t, func() { Convey("JoiningSlash should combine URL-like segments safely", t, func() {
So(JoiningSlash("http://my.tld/docs/", "bla/", "blub/"), ShouldEqual, "http://my.tld/docs/bla/blub/") const (
So(JoiningSlash("http://my.tld", "bla", "blub"), ShouldEqual, "http://my.tld/bla/blub") baseURL = "http://my.tld"
So(JoiningSlash("http://my.tld/", "bla", "blub"), ShouldEqual, "http://my.tld/bla/blub") docsURL = "http://my.tld/docs"
So(JoiningSlash("http://my.tld", "bla/", "blub"), ShouldEqual, "http://my.tld/bla/blub") expectedRoot = "http://my.tld/bla/blub"
So(JoiningSlash("http://my.tld/docs", "bla/", "blub"), ShouldEqual, "http://my.tld/docs/bla/blub") expectedDocs = "http://my.tld/docs/bla/blub"
So(JoiningSlash("http://my.tld/docs/", "bla/", "blub"), ShouldEqual, "http://my.tld/docs/bla/blub") expectedDocsS = "http://my.tld/docs/bla/blub/"
)
So(JoiningSlash(docsURL+"/", "bla/", "blub/"), ShouldEqual, expectedDocsS)
So(JoiningSlash(baseURL, "bla", "blub"), ShouldEqual, expectedRoot)
So(JoiningSlash(baseURL+"/", "bla", "blub"), ShouldEqual, expectedRoot)
So(JoiningSlash(baseURL, "bla/", "blub"), ShouldEqual, expectedRoot)
So(JoiningSlash(docsURL, "bla/", "blub"), ShouldEqual, expectedDocs)
So(JoiningSlash(docsURL+"/", "bla/", "blub"), ShouldEqual, expectedDocs)
So(JoiningSlash("", "api", "v1"), ShouldEqual, "api/v1") So(JoiningSlash("", "api", "v1"), ShouldEqual, "api/v1")
So(JoiningSlash("", "", ""), ShouldEqual, "") So(JoiningSlash("", "", ""), ShouldEqual, "")
}) })