Compare commits
24
Commits
bc9b0ce88f
...
v0.6.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
170c8ea981 | ||
|
|
7dcf04c568 | ||
|
|
b3d80f1ae3 | ||
|
|
2410c16559 | ||
|
|
e25da626eb | ||
|
|
8d17f889f0 | ||
|
|
40834fdfce | ||
|
|
b349586755 | ||
|
|
109d62efa3 | ||
|
|
5e8660ebd1 | ||
|
|
0ebb212b65 | ||
|
|
91c2a313f2 | ||
|
|
2e267ac6dd | ||
|
|
0e150f0ba9 | ||
|
|
6ca2c4d25f | ||
|
|
d01242ae7c | ||
|
|
6236717fdb | ||
|
|
2ec2fcd2ab | ||
|
|
8ace13074f | ||
|
|
1da053fb67 | ||
|
|
05a61bba86 | ||
|
|
2242ffd0cb | ||
|
|
eb050d5b84 | ||
|
|
f997c15873 |
+54
-3
@@ -2,11 +2,62 @@ kind: pipeline
|
||||
type: docker
|
||||
name: go-lib/util
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- push
|
||||
- tag
|
||||
ref:
|
||||
include:
|
||||
- refs/heads/**
|
||||
- refs/tags/v*
|
||||
|
||||
steps:
|
||||
- name: test
|
||||
image: golang:1.18
|
||||
image: golang:1.25.8
|
||||
commands:
|
||||
- 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
|
||||
- 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
|
||||
@@ -14,6 +14,3 @@ indent_size = 4
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.ps1]
|
||||
end_of_line = crlf
|
||||
|
||||
@@ -13,6 +13,5 @@
|
||||
Makefile text eol=lf
|
||||
|
||||
# Keep native Windows script formats
|
||||
*.ps1 text eol=crlf
|
||||
*.bat text eol=crlf
|
||||
*.cmd text eol=crlf
|
||||
|
||||
@@ -14,3 +14,8 @@ The pre-commit hook validates for staged `.sh` files:
|
||||
|
||||
- executable bit in Git index (`100755`)
|
||||
- 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`)
|
||||
|
||||
@@ -5,6 +5,7 @@ failed=0
|
||||
cr=$(printf '\r')
|
||||
|
||||
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
|
||||
mode=$(git ls-files --stage -- "$file" | awk '{print $1}')
|
||||
@@ -19,6 +20,20 @@ for file in $staged_shell_files; do
|
||||
fi
|
||||
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
|
||||
echo "Pre-commit check failed." >&2
|
||||
exit 1
|
||||
|
||||
@@ -11,3 +11,4 @@ coverage.out
|
||||
Thumbs.db
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
# AGENTS.md
|
||||
|
||||
## 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
|
||||
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 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
|
||||
|
||||
1. Keep changes minimal and focused on the requested task.
|
||||
2. Preserve existing public APIs unless a breaking change is explicitly requested.
|
||||
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.
|
||||
|
||||
## Testing Expectations
|
||||
|
||||
1. Add or update tests for behavior changes.
|
||||
2. Keep tests deterministic and fast.
|
||||
3. Prefer table-driven tests where they improve readability.
|
||||
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
|
||||
|
||||
1. Builder logs and generated reports must be created under `.build/`.
|
||||
2. The `.build/` directory must be excluded from version control via `.gitignore`.
|
||||
|
||||
## Git and Script Standards
|
||||
|
||||
1. Shell scripts (`*.sh`) must use LF line endings.
|
||||
2. Shell scripts committed to the repository must be executable 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`.
|
||||
2. Shell scripts committed to the repository must be executable
|
||||
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)
|
||||
|
||||
### Purpose
|
||||
The Definition of Done defines the minimum quality bar for every completed change in this repository.
|
||||
### DoD Purpose
|
||||
|
||||
The Definition of Done defines the minimum quality bar
|
||||
for every completed change in this repository.
|
||||
|
||||
### Mandatory Criteria
|
||||
|
||||
1. Tests
|
||||
|
||||
- Every code change is covered by tests where applicable.
|
||||
- New functionality includes new tests.
|
||||
- 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%.
|
||||
|
||||
1. Functional documentation
|
||||
|
||||
- Implemented functionality is documented.
|
||||
- Public API-relevant changes are reflected in README and/or docs.
|
||||
|
||||
1. Documentation standards
|
||||
|
||||
- Documentation is written in English.
|
||||
- Documentation files are placed under `docs/`.
|
||||
- Exceptions: `README.md` and `AGENTS.md` remain at repository root.
|
||||
- Markdown files have no `markdownlint` errors or problems.
|
||||
|
||||
### Technical Completion Criteria
|
||||
|
||||
1. Build and test status
|
||||
|
||||
- The project builds successfully.
|
||||
- Relevant test commands run successfully.
|
||||
|
||||
1. No unresolved critical issues
|
||||
|
||||
- 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
|
||||
|
||||
- Links to moved or newly added docs are valid.
|
||||
- Documentation structure remains consistent with project rules.
|
||||
|
||||
### Review Checklist (Quick)
|
||||
|
||||
- [ ] Change is implemented and meets acceptance criteria.
|
||||
- [ ] Tests were added/updated and pass.
|
||||
- [ ] 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.
|
||||
- [ ] Documentation is in English.
|
||||
- [ ] 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.
|
||||
|
||||
@@ -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
|
||||
@@ -1,7 +1,7 @@
|
||||
<div style="text-align:left"><img src="https://www.yoorie.de/img/favicon_32.png"/></div>
|
||||
|
||||
# Go utility library
|
||||
|
||||

|
||||
|
||||
[](https://drone.yoorie.de/go-lib/util)
|
||||
|
||||
## Project Description
|
||||
@@ -57,6 +57,4 @@ func main() {
|
||||
```
|
||||
|
||||
---
|
||||
Copyright © 2023 yoorie.de
|
||||
|
||||
|
||||
Copyright © 2026 yoorie.de
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
|
||||
## 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
|
||||
|
||||
1. Tests
|
||||
|
||||
- Every code change is covered by tests where applicable.
|
||||
- New functionality includes new tests.
|
||||
- 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%.
|
||||
|
||||
1. Functional documentation
|
||||
|
||||
- Implemented functionality is documented.
|
||||
- Public API-relevant changes are reflected in README and/or docs.
|
||||
|
||||
1. Documentation standards
|
||||
|
||||
- Documentation is written in English.
|
||||
- Documentation files are placed under `docs/`.
|
||||
- Exceptions: `README.md` and `AGENTS.md` remain at repository root.
|
||||
- Markdown files have no `markdownlint` errors or problems.
|
||||
|
||||
## Technical Completion Criteria
|
||||
|
||||
1. Build and test status
|
||||
|
||||
- The project builds successfully.
|
||||
- Relevant test commands run successfully.
|
||||
|
||||
1. No unresolved critical issues
|
||||
|
||||
- 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
|
||||
|
||||
- Links to moved or newly added docs are valid.
|
||||
- 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.
|
||||
- [ ] Documentation is in English.
|
||||
- [ ] 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.
|
||||
|
||||
@@ -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
@@ -13,6 +13,16 @@ import (
|
||||
|
||||
var (
|
||||
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
|
||||
tokenIsMember = func(token windows.Token, sid *windows.SID) (bool, error) { return token.IsMember(sid) }
|
||||
fatalf = log.Fatalf
|
||||
@@ -27,13 +37,7 @@ func IsSuperUser() bool {
|
||||
// official windows documentation. The Go API for this is a
|
||||
// direct wrap around the official C++ API.
|
||||
// See https://docs.microsoft.com/en-us/windows/desktop/api/securitybaseapi/nf-securitybaseapi-checktokenmembership
|
||||
err := allocateAndInitializeSid(
|
||||
&windows.SECURITY_NT_AUTHORITY,
|
||||
2,
|
||||
windows.SECURITY_BUILTIN_DOMAIN_RID,
|
||||
windows.DOMAIN_ALIAS_RID_ADMINS,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
&sid)
|
||||
err := allocateAdminGroupSid(&sid)
|
||||
if err != nil {
|
||||
fatalf("SID Error: %s", err)
|
||||
return false
|
||||
|
||||
+5
-17
@@ -11,33 +11,21 @@ import (
|
||||
"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() {
|
||||
origAllocate := allocateAndInitializeSid
|
||||
origAllocate := allocateAdminGroupSid
|
||||
origFatalf := fatalf
|
||||
defer func() {
|
||||
allocateAndInitializeSid = origAllocate
|
||||
allocateAdminGroupSid = origAllocate
|
||||
fatalf = origFatalf
|
||||
}()
|
||||
|
||||
allocateAndInitializeSid = func(
|
||||
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 {
|
||||
allocateAdminGroupSid = func(_ **windows.SID) error {
|
||||
return errors.New("forced sid allocation error")
|
||||
}
|
||||
|
||||
fatalCalled := false
|
||||
fatalf = func(format string, v ...interface{}) {
|
||||
fatalf = func(_ string, _ ...interface{}) {
|
||||
fatalCalled = true
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
Executable
+28
@@ -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
|
||||
Executable
+71
@@ -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"
|
||||
@@ -1,8 +1,9 @@
|
||||
#!/usr/bin/env sh
|
||||
set -eu
|
||||
|
||||
ROOT_PATH="/c/Users/stefan/git"
|
||||
STANDARDS_REPO="/c/Users/stefan/git/project-standards"
|
||||
ROOT_PATH="${GO_GIT_ROOT:-$HOME/git}"
|
||||
STANDARDS_REPO=""
|
||||
STANDARDS_REPO_SET=0
|
||||
EXCLUDE_NAME="project-standards"
|
||||
CHECK_ONLY=0
|
||||
WATCH=0
|
||||
@@ -21,6 +22,14 @@ Options:
|
||||
--watch Continuously scan and reconcile
|
||||
--interval <seconds> Watch interval in seconds (default: 60)
|
||||
-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
|
||||
}
|
||||
|
||||
@@ -32,6 +41,7 @@ while [ "$#" -gt 0 ]; do
|
||||
;;
|
||||
--standards-repo)
|
||||
STANDARDS_REPO=$2
|
||||
STANDARDS_REPO_SET=1
|
||||
shift 2
|
||||
;;
|
||||
--exclude)
|
||||
@@ -62,6 +72,14 @@ while [ "$#" -gt 0 ]; do
|
||||
esac
|
||||
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
|
||||
echo "interval must be >= 5" >&2
|
||||
exit 1
|
||||
@@ -162,6 +180,7 @@ ensure_gitignore_entries_from_template() {
|
||||
fi
|
||||
|
||||
while IFS= read -r entry; do
|
||||
entry=$(printf '%s' "$entry" | tr -d '\r')
|
||||
[ -n "$entry" ] || continue
|
||||
case "$entry" in
|
||||
\#*)
|
||||
@@ -169,7 +188,7 @@ ensure_gitignore_entries_from_template() {
|
||||
;;
|
||||
esac
|
||||
|
||||
if grep -Fqx "$entry" "$gitignore_path"; then
|
||||
if tr -d '\r' < "$gitignore_path" | grep -Fqx "$entry"; then
|
||||
continue
|
||||
fi
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@ func joiningSlash(elem []string) string {
|
||||
}
|
||||
|
||||
func singleJoiningSlash(a, b string) string {
|
||||
filepath.Join(a, b)
|
||||
aslash := strings.HasSuffix(a, "/")
|
||||
bslash := strings.HasPrefix(b, "/")
|
||||
switch {
|
||||
|
||||
+14
-6
@@ -23,12 +23,20 @@ func TestFileExists(t *testing.T) {
|
||||
|
||||
func TestJoiningSlash(t *testing.T) {
|
||||
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/")
|
||||
So(JoiningSlash("http://my.tld", "bla", "blub"), ShouldEqual, "http://my.tld/bla/blub")
|
||||
So(JoiningSlash("http://my.tld/", "bla", "blub"), ShouldEqual, "http://my.tld/bla/blub")
|
||||
So(JoiningSlash("http://my.tld", "bla/", "blub"), ShouldEqual, "http://my.tld/bla/blub")
|
||||
So(JoiningSlash("http://my.tld/docs", "bla/", "blub"), ShouldEqual, "http://my.tld/docs/bla/blub")
|
||||
So(JoiningSlash("http://my.tld/docs/", "bla/", "blub"), ShouldEqual, "http://my.tld/docs/bla/blub")
|
||||
const (
|
||||
baseURL = "http://my.tld"
|
||||
docsURL = "http://my.tld/docs"
|
||||
expectedRoot = "http://my.tld/bla/blub"
|
||||
expectedDocs = "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("", "", ""), ShouldEqual, "")
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user