{"id":51410862,"url":"https://github.com/launchbynttdata/launch-ado-automatic-versioner","last_synced_at":"2026-07-04T14:32:33.727Z","repository":{"id":329028472,"uuid":"1109981584","full_name":"launchbynttdata/launch-ado-automatic-versioner","owner":"launchbynttdata","description":null,"archived":false,"fork":false,"pushed_at":"2026-06-11T19:25:36.000Z","size":197,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-11T20:14:08.612Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/launchbynttdata.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2025-12-04T14:51:11.000Z","updated_at":"2026-06-11T19:23:58.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/launchbynttdata/launch-ado-automatic-versioner","commit_stats":null,"previous_names":["launchbynttdata/launch-ado-automatic-versioner"],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/launchbynttdata/launch-ado-automatic-versioner","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/launchbynttdata%2Flaunch-ado-automatic-versioner","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/launchbynttdata%2Flaunch-ado-automatic-versioner/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/launchbynttdata%2Flaunch-ado-automatic-versioner/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/launchbynttdata%2Flaunch-ado-automatic-versioner/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/launchbynttdata","download_url":"https://codeload.github.com/launchbynttdata/launch-ado-automatic-versioner/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/launchbynttdata%2Flaunch-ado-automatic-versioner/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35125718,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-04T02:00:05.987Z","response_time":113,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-07-04T14:32:31.549Z","updated_at":"2026-07-04T14:32:33.718Z","avatar_url":"https://github.com/launchbynttdata.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# AAV – ADO Automatic Versioner\n\n`aav` is a Go-based CLI that keeps Azure DevOps (ADO) repositories on semantic-versioning rails. It reads bump intent from branch names and pull-request labels, propagates that intent through merge validation, and creates annotated SemVer release or RC tags directly in ADO Git.\n\n## Key Features\n\n- Automatic branch → bump mapping with conflict-safe PR labeling\n- PR merge inference that survives squash merges and defaults safely when strict mode is off\n- Release and RC tag planning using validated SemVer math (powered by `github.com/blang/semver/v4`)\n- Annotated tag creation through the official Azure DevOps SDK with tagger metadata and commit targeting\n- Env \u003e flag \u003e default configuration precedence with structured logging (Zap)\n- Pure business-logic layer with exhaustive unit tests and Azure-friendly integration tests\n\n## Quick Start\n\n```bash\n# Build the CLI\nGOOS=$(go env GOOS) GOARCH=$(go env GOARCH) go build -o ./bin/aav ./cmd/aav\n\n# Verify embedded build metadata\n./bin/aav version\n\n# Add semver labels to a PR during validation\nAAV_ORG_URL=... AAV_PROJECT=... AAV_REPO=... AAV_TOKEN=... \\\nAAV_PR_ID=1234 AAV_SOURCE_BRANCH=feature/awesome-fix \\\n./bin/aav pr-label\n\n# Infer the bump after a squash merge (strict mode errors when no PR is found)\nAAV_COMMIT_SHA=$(git rev-parse HEAD) AAV_STRICT=true ./bin/aav infer-bump\n\n# Create a release tag for the merge commit (bump provided via infer-bump output)\nAAV_BUMP=minor AAV_TAG_MODE=release AAV_COMMIT_SHA=$(git rev-parse HEAD) \\\nAAV_TAGGER_NAME=\"Build Bot\" AAV_TAGGER_EMAIL=ci@example.com ./bin/aav create-tag\n```\n\n### Running in Azure Pipelines\n\n- **PR validation**: call `aav pr-label` after checkout and before policy evaluation.\n- **Main pipeline**: run `aav infer-bump --commit-sha $(Build.SourceVersion)` to capture intent, export its stdout, then invoke `aav create-tag` (release or RC mode) with that bump.\n\n```yaml\n# azure-pipelines.yml\ntrigger:\n  branches:\n    include:\n      - main\n\npr:\n  branches:\n    include:\n      - '*'\n\nvariables:\n  AAV_ORG_URL: $(System.TeamFoundationCollectionUri)\n  AAV_PROJECT: $(System.TeamProject)\n  AAV_REPO: $(Build.Repository.Name)\n  AAV_LABEL_PREFIX: semver-\n\nstages:\n  - stage: PRValidation\n    condition: eq(variables['Build.Reason'], 'PullRequest')\n    jobs:\n      - job: LabelPR\n        steps:\n          - checkout: self\n          - script: |\n              go run ./cmd/aav pr-label \\\n                --pr-id $(System.PullRequest.PullRequestId) \\\n                --source-branch $(System.PullRequest.SourceBranch)\n            displayName: Apply semver label\n\n  - stage: MainRelease\n    condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))\n    dependsOn: PRValidation\n    jobs:\n      - job: TagRelease\n        steps:\n          - checkout: self\n          - script: |\n              BUMP=$(go run ./cmd/aav infer-bump \\\n                --commit-sha $(Build.SourceVersion))\n              echo \"##vso[task.setvariable variable=AAV_BUMP]$BUMP\"\n            displayName: Infer bump\n          - script: |\n              go run ./cmd/aav create-tag \\\n                --commit-sha $(Build.SourceVersion) \\\n                --tag-mode release \\\n                --bump $(AAV_BUMP)\n            displayName: Create release tag\n```\n\n## Configuration Reference\n\n| Purpose | Environment Variable | Flag | Default | Notes |\n| --- | --- | --- | --- | --- |\n| Org URL | `AAV_ORG_URL` | `--org-url` | _required_ | `https://dev.azure.com/{org}` |\n| Project | `AAV_PROJECT` | `--project` | _required_ | ADO project name |\n| Repository | `AAV_REPO` | `--repo` | _required_ | Git repo name |\n| Token | `AAV_TOKEN` | `--token` | _required_ | PAT or `System.AccessToken` |\n| Log level | `AAV_LOG_LEVEL` | `--log-level` | `terse` | `verbose` prints config + trace |\n| Label prefix | `AAV_LABEL_PREFIX` | `--label-prefix` | `semver-` | Empty string allowed |\n| Major label | `AAV_LABEL_MAJOR` | `--label-major` | derived | Overrides prefix value |\n| Minor label | `AAV_LABEL_MINOR` | `--label-minor` | derived | Overrides prefix value |\n| Patch label | `AAV_LABEL_PATCH` | `--label-patch` | derived | Overrides prefix value |\n| Major branch prefixes | `AAV_BRANCH_MAJOR_PREFIXES` | `--branch-major-prefix` | `breaking/,major/` | Repeatable flag; env uses comma-separated list (e.g. `breaking/,major/`) |\n| Minor branch prefixes | `AAV_BRANCH_MINOR_PREFIXES` | `--branch-minor-prefix` | `feature/,minor/` | Repeatable flag; env uses comma-separated list (e.g. `feature/,minor/`) |\n| Patch branch prefixes | `AAV_BRANCH_PATCH_PREFIXES` | `--branch-patch-prefix` | `bugfix/,fix/,hotfix/,chore/,patch/` | Repeatable flag; env uses comma-separated list (e.g. `bugfix/,fix/`) |\n| PR ID | `AAV_PR_ID` | `--pr-id` | _required by pr-label_ | Integer \u003e 0 |\n| Source branch | `AAV_SOURCE_BRANCH` | `--source-branch` | _required by pr-label_ | Branch that triggered PR |\n| Commit SHA | `AAV_COMMIT_SHA` | `--commit-sha` | _required by infer-bump/create-tag_ | 40-char SHA |\n| Strict mode | `AAV_STRICT` | `--strict` | `false` | Only applies to `infer-bump` |\n| Tag mode | `AAV_TAG_MODE` | `--tag-mode` | _required by create-tag_ | `release` or `rc` |\n| Bump intent | `AAV_BUMP` | `--bump` | _required by create-tag_ | `major`, `minor`, `patch` |\n| Base version | `AAV_BASE_VERSION` | `--base-version` | none | Used when no stable tags exist |\n| Tag message | `AAV_TAG_MESSAGE` | `--tag-message` | empty | Stored in annotated tag |\n| Tagger name | `AAV_TAGGER_NAME` | `--tagger-name` | `aav` | Recorded in annotated tag |\n| Tagger email | `AAV_TAGGER_EMAIL` | `--tagger-email` | `aav@example.com` | Recorded in annotated tag |\n| Tag prefix | `AAV_TAG_PREFIX` | `--tag-prefix` | empty | Prepended to computed tag names (set to `v` for legacy repos) |\n| Floating tags | `AAV_USE_FLOATING_TAGS` | `--use-floating-tags` | `false` | Maintain short `v\u003cmajor\u003e` refs that track the latest release major; detected automatically when such refs already exist |\n\n\u003e **Precedence**: environment variables always win over explicit flags; conflicts are logged in both terse and verbose modes.\n\n\u003e **Branch prefix env format**: When using the environment variables above, provide comma-separated prefixes with no quotes (e.g. `AAV_BRANCH_MINOR_PREFIXES=feature/,minor/`). Use the repeatable CLI flags when you prefer to specify each prefix individually.\n\n## Subcommands\n\n| Command | When to use | Behavior |\n| --- | --- | --- |\n| `pr-label` | Pull-request validation | Resolves bump intent from the source branch, ensures the expected semver label exists, loudly warns on conflicts, and never removes user labels. |\n| `infer-bump` | Main-branch CI after squash merge | Locates the PR by merge commit, rehydrates bump intent from labels, defaults to `patch` unless `--strict` is set. Prints `major`, `minor`, or `patch` to stdout for scripting. |\n| `create-tag` | Release/RC tagging stages | Discovers existing tags, computes the next SemVer (release or RC), and creates an annotated tag on the desired commit with full trace logging. |\n| `version` | Introspection | Prints the embedded semantic version and build date for the running binary. |\n\n### Floating Tags\n\n`aav create-tag --tag-mode release` can also maintain **floating** `v\u003cmajor\u003e` refs that always point at the most recent patch of the newest release line:\n\n- Opt in via `--use-floating-tags` / `AAV_USE_FLOATING_TAGS`, or let the tool detect an existing floating tag that already tracks a valid SemVer release.\n- Floating refs are only created or updated in **release** mode, and only for the highest major version (e.g., when `2.x` is current, only `v2` moves; creating `3.0.0` also creates `v3`).\n- Updates are performed by deleting the previous ref (when present) and recreating it as an annotated tag using the **exact same metadata** (tagger, message, commit) as the freshly minted SemVer tag. This movement is automatic for virtual floating refs; SemVer release and RC tags are never moved.\n- Detection requires that the floating ref’s commit matches a non-RC SemVer tag so repositories that already use floating tags automatically stay on rails even if the flag is not set explicitly. The CLI logs when auto-detection overrides the flag state.\n\n### Build Metadata \u0026 `aav version`\n\n- Every build stamps two ldflags into `internal/version`: the semantic version (`Version`) and UTC build date (`BuildDate`).\n- `make build` derives defaults from `git describe --tags` (falling back to `dev`) and the current UTC time. Override them by setting `AAV_VERSION` and/or `AAV_BUILD_DATE` before invoking the target.\n- GoReleaser already injects release metadata via the same variables, so published artifacts report the tag they were built from when you run `aav version`.\n- The `version` subcommand prints both values so pipelines (or end users) can confirm which artifact they are running.\n\n## Architecture\n\n- **Business logic**: pure Go packages for branch mapping, label resolution, SemVer planning, and configuration. These packages do not perform I/O and are fully unit tested.\n- **ADO client**: a thin wrapper over `github.com/microsoft/azure-devops-go-api/azuredevops/v7` that exposes the handful of Git operations we need (PR labels, ref listing, annotated tags). Substitutable via interfaces for tests.\n- **CLI layer**: Cobra commands, env/flag resolution, and Zap logging that wire user intent into the business layer.\n\n## Testing\n\n| Scope | Command | Notes |\n| --- | --- | --- |\n| Unit tests | `go test ./...` | Covers business logic and service layers. Executed in CI. |\n| Integration tests | `go test -tags=integration ./integration -count=1` | Hits live ADO resources using the `AAV_` environment variables described below. Not run by default or in CI. |\n\n### Integration Test Environment\n\nSet these variables to point at a safe ADO test repository before running with the `integration` build tag:\n\n| Variable | Purpose |\n| --- | --- |\n| `AAV_ORG_URL` | Azure DevOps organization URL that hosts the repo under test |\n| `AAV_PROJECT` | Project name containing that repository |\n| `AAV_REPO` | Repository name used for temporary branches/tags |\n| `AAV_TOKEN` | PAT or `System.AccessToken` with PR + tag permissions |\n| `AAV_EXPECTED_BUMP` (optional) | Override the bump asserted by the workflow (`major`, `minor`, or `patch`); defaults to `minor` by using a `feature/` branch |\n| `AAV_TARGET_BRANCH` (optional) | Base branch to branch from; defaults to `main` |\n| `AAV_MANUAL_MERGE` (optional) | Set to `true` to pause so you can merge the PR manually |\n| `AAV_GIT_AUTHOR_NAME` / `AAV_GIT_AUTHOR_EMAIL` (optional) | Commit identity for the temporary feature branch |\n| `AAV_TAGGER_NAME` / `AAV_TAGGER_EMAIL` (optional) | Annotated tag identity overrides |\n| `AAV_TAG_PREFIX` (optional) | Prepends text to created tags (set to `v` when matching legacy repos) |\n| `AAV_BAD_COMMIT_SHA` (optional) | Commit that should not map to a PR; defaults to all zeros |\n| `AAV_BAD_PR_ID` (optional) | Nonexistent PR ID for negative testing |\n| `AAV_BRANCH_MAJOR_PREFIXES` / `AAV_BRANCH_MINOR_PREFIXES` / `AAV_BRANCH_PATCH_PREFIXES` (optional) | Override the branch-to-bump mapping the tests and CLI use; each env expects a comma-separated list (mirrors the CLI defaults) |\n\nThe tests call `go run ./cmd/aav ...` so they verify the built binary end-to-end. When the required `AAV_` variables are absent the tests automatically skip.\n\n## Contributing\n\n- Go 1.26.4+ is required (for standard library security patches). Run `make deps` to install dependencies and Go tools, then `make ci-local` to mirror CI locally.\n- Keep Go modules tidy and run `go test ./...` before submitting changes.\n- Follow the layered architecture: keep business logic separate from Azure SDK calls and CLI plumbing.\n- Add unit tests alongside any new exported behavior. Mock the ADO client for service tests.\n- For release engineering, see `RELEASE_GUIDE.md` and `RELEASE_*` Make targets.\n\n## License\n\nApache License 2.0. See [LICENSE](LICENSE) for details.\n\n## Configuration Files\n\n- `.tool-versions` - mise versions for golang and pre-commit\n- `tools/tools.go` - Development tool dependencies (golangci-lint, gosec, govulncheck)\n- `.goreleaser.yml` - GoReleaser configuration for builds and releases\n- `.github/workflows/ci.yml` - CI pipeline (tests, linting, security)\n- `.github/workflows/release.yml` - Release pipeline\n- `.golangci.yml` - Linter configuration\n- `.github/dependabot.yml` - Dependency update configuration\n- `Dockerfile` - Multi-stage Docker build configuration\n- `docker-compose.yml` - Local development environment\n- `.pre-commit-config.yaml` - Pre-commit hooks configuration\n- `.vscode/settings.json` - VS Code Go development settings\n- `.vscode/extensions.json` - Recommended VS Code extensions\n- `.env.example` - Environment variables template\n\n## Development\n\n### Development Tooling\n\nWe use **mise** for top-level ecosystems only (golang, pre-commit via `.tool-versions`). Go development tools (golangci-lint, gosec, govulncheck) are installed via the Makefile:\n\n```bash\nmise install          # Install golang and pre-commit from .tool-versions\nmake deps             # Download modules, verify, and install Go tools\n```\n\nTools are tracked in `tools/tools.go` using the [standard Go pattern](https://go.dev/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module): blank imports with `//go:build tools` so they are excluded from the application build. Versions are pinned in `go.mod`; `go install` (without `@version`) uses the module graph and any `replace` directives.\n\n**Module cache issues:** If `go mod verify` fails with \"dir has been modified\", run `make deps-clean` to clear the cache, then `make deps` again. The `deps` target will also auto-retry after cleaning on verify failure.\n\n#### Size and Security Impact\n\n| Concern | Impact |\n| --- | --- |\n| **Application binary** | None. The tools package is excluded from builds (`//go:build tools`). Your shipped binary does not include golangci-lint, gosec, or govulncheck. |\n| **Runtime attack surface** | None. The tools are separate executables used only in development and CI, not in production. |\n| **go.sum / module cache** | Larger. Tool dependencies are downloaded and checksummed. This affects local dev and CI, not the released artifact. |\n| **govulncheck output** | May report vulnerabilities in tool dependencies. Those affect the tool binaries only, not your application at runtime. |\n\nYou are not inheriting the tools' security issues into your code—you are tracking their versions for reproducible development and CI.\n\n### CI Checks\n\nThis project includes several CI checks:\n\n- **Tests**: Unit tests with race detection and coverage\n- **Linting**: golangci-lint with multiple linters enabled\n- **Security**: Gosec security scanner and govulncheck\n- **Builds**: Cross-platform build verification\n- **Dependencies**: Go mod tidy verification\n\nAll checks must pass before merging to main.\n\n### Project Structure\n\n```text\nlaunch-ado-automatic-versioner/\n├── cmd/aav/               # CLI entry point\n├── internal/              # Private application code\n│   ├── ado/               # Azure DevOps client\n│   ├── cli/               # Cobra commands and flags\n│   ├── config/            # Configuration resolution\n│   ├── domain/            # Business logic (branchmap, bump, labels, tagplan)\n│   ├── logging/           # Structured logging\n│   ├── services/          # Service layer (inferbump, prlabel, tagging)\n│   └── version/           # Build metadata\n├── tools/                 # Development tool dependencies (tools.go)\n├── integration/           # Integration tests\n└── scripts/               # Build and setup scripts\n```\n\n### Docker Development\n\n```bash\n# Build and run with Docker\nmake docker-build\nmake docker-run\n\n# Or use Docker Compose for local development\nmake docker-compose-up\nmake docker-compose-down\n```\n\n### Pre-commit Hooks\n\nThis project includes pre-commit hooks for code quality:\n\n```bash\n# Install pre-commit hooks\npre-commit install\n\n# Run manually\npre-commit run --all-files\n\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flaunchbynttdata%2Flaunch-ado-automatic-versioner","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flaunchbynttdata%2Flaunch-ado-automatic-versioner","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flaunchbynttdata%2Flaunch-ado-automatic-versioner/lists"}