mirror of
https://github.com/lukaszraczylo/semver-generator.git
synced 2026-06-16 01:31:19 +00:00
Compare commits
167 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 888415366a | |||
| dfbc4d4012 | |||
| dfeb03b8bb | |||
| ab52de167e | |||
| 564f39ed10 | |||
| 99b8bf937b | |||
| b555e410cc | |||
| 521e5c1ff1 | |||
| a223f15e1c | |||
| d1b8192b78 | |||
| a3aed9ef93 | |||
| 5f205e9856 | |||
| a77bc0c7ae | |||
| 99338e8527 | |||
| 911513c106 | |||
| c2a7a4e156 | |||
| 21b87300cc | |||
| 3e0a7239c4 | |||
| 49a46a74c1 | |||
| 3a48a67c75 | |||
| 18b9b474e0 | |||
| f5a118fd1a | |||
| a1c4133e94 | |||
| 2a51752663 | |||
| 0bc848f6f4 | |||
| 8590963822 | |||
| f4285403f7 | |||
| b3d104f0a8 | |||
| 7a70ed6614 | |||
| f9a18995d0 | |||
| 66756b6772 | |||
| a0f1ab6930 | |||
| b80929ff52 | |||
| be189187ba | |||
| 336d8cc163 | |||
| b8fdc4bfb5 | |||
| 25f6bf5c83 | |||
| f5b6bd5375 | |||
| 30ca55b204 | |||
| afda61e35d | |||
| 03a1d26b9d | |||
| d46b8ca048 | |||
| 41cdd6dcc4 | |||
| 46132856b6 | |||
| c43fcf3216 | |||
| 672b36b8e3 | |||
| ac417365d5 | |||
| bb78eda4ed | |||
| 67751f0d10 | |||
| 35101facae | |||
| 6f34b80600 | |||
| 16f886aa17 | |||
| b45ebf8512 | |||
| 3abb4bb94b | |||
| 1c6617ff45 | |||
| 66ce887688 | |||
| 6fbe13aab1 | |||
| 7d823d0b58 | |||
| 96ef065a43 | |||
| 72915b5129 | |||
| 1a9c3fcb40 | |||
| 358e4cadcf | |||
| 7d0ef8c887 | |||
| 2472ed9871 | |||
| a8151c4d25 | |||
| 72cc786ada | |||
| c83ccec5df | |||
| 26877f4c37 | |||
| 2080b1104b | |||
| e232431fca | |||
| b63b9de52f | |||
| 8b5b72e1a3 | |||
| be568f5bfd | |||
| 9ca830cb87 | |||
| 32a1b725bd | |||
| 7012e93e5f | |||
| 3bcddace61 | |||
| 4415eb90f2 | |||
| a2d7d06b17 | |||
| a4a75b619e | |||
| a9b551073f | |||
| a25375b587 | |||
| 37eb51eca6 | |||
| 0ac160bc2b | |||
| 6cd269fc5d | |||
| 9fa23581e4 | |||
| 8e1d58e7f6 | |||
| 44ceb9c5d7 | |||
| d88ebeb3b2 | |||
| a70e9f218d | |||
| 5de3e8009e | |||
| a4e23b1c46 | |||
| 7e28618738 | |||
| 854aaa2a4d | |||
| 49eca196be | |||
| 450339345f | |||
| 7538c5eb03 | |||
| 859bddaf51 | |||
| e95976d3e5 | |||
| 13c1e7bfc4 | |||
| 10fd0af70d | |||
| 9659b1b689 | |||
| 1c65e8c8ed | |||
| af6c02b2bd | |||
| 6d10c33fc7 | |||
| 7b5e7ed346 | |||
| ba1f0a89a4 | |||
| e17f05a1e0 | |||
| 2ae23430b0 | |||
| 7766cc264e | |||
| 966b9f9db0 | |||
| fe6dfdf3c2 | |||
| 59c2a4edc7 | |||
| ec427ec421 | |||
| 66e0ed606d | |||
| 3b736d7d18 | |||
| 722c6c6653 | |||
| 83fc7a9bb6 | |||
| edb3cea94d | |||
| f0057674d6 | |||
| c107ab6e48 | |||
| 787f1bcde0 | |||
| 73589f0690 | |||
| 0289d8f747 | |||
| 923f979df8 | |||
| f3f8068a2a | |||
| 5c9988fe36 | |||
| 16f297c8a6 | |||
| 90da76eb20 | |||
| d1eef2475c | |||
| 7095f894f6 | |||
| 8c97939434 | |||
| f9895c07df | |||
| f111598564 | |||
| 79385efedc | |||
| 03b220de4d | |||
| 23beba8648 | |||
| 5cb22d25e9 | |||
| 7a4379cd48 | |||
| 3704fdfb2c | |||
| 37b88d49b1 | |||
| 8d796df3aa | |||
| 4ef9e39c1f | |||
| 323c3a8fb5 | |||
| 00579ba4bd | |||
| ec8174477d | |||
| 6ebb050c77 | |||
| d7815d35af | |||
| a96ea957e4 | |||
| 1c2347e08d | |||
| e0c9cee74c | |||
| dad90bca9f | |||
| acfb462f9a | |||
| 18f9e8c1f6 | |||
| 26abda1319 | |||
| d08b1fc31e | |||
| 47f6d0bd51 | |||
| a1bec4b664 | |||
| a51496bfd9 | |||
| a21dc76640 | |||
| c125ebf429 | |||
| 82e451888f | |||
| 3a19de1a44 | |||
| a27f9e76d3 | |||
| 54e35d4c0b | |||
| bed1dbd127 | |||
| 275e9f42ac |
@@ -5,70 +5,15 @@ on:
|
||||
schedule:
|
||||
- cron: "0 3 * * *"
|
||||
|
||||
env:
|
||||
GO_VERSION: ">=1.21"
|
||||
permissions:
|
||||
contents: write
|
||||
actions: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
# This job is responsible for preparation of the build
|
||||
# environment variables.
|
||||
prepare:
|
||||
name: Preparing build context
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
id: cache
|
||||
with:
|
||||
go-version: ${{env.GO_VERSION}}
|
||||
cache-dependency-path: "**/*.sum"
|
||||
|
||||
- name: Go get dependencies
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
go get ./...
|
||||
|
||||
# This job is responsible for running tests and linting the codebase
|
||||
test:
|
||||
name: "Unit testing"
|
||||
runs-on: ubuntu-latest
|
||||
container: golang:1
|
||||
needs: [prepare]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Ensure full history is checked out
|
||||
token: ${{ secrets.GHCR_TOKEN }}
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{env.GO_VERSION}}
|
||||
cache-dependency-path: "**/*.sum"
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install ca-certificates make -y
|
||||
update-ca-certificates
|
||||
go mod tidy
|
||||
go get -u -v ./...
|
||||
go mod tidy -v
|
||||
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
export GITHUB_TOKEN=${{ secrets.GHCR_TOKEN }}
|
||||
CI_RUN=${CI} make test
|
||||
git config --global --add safe.directory /__w/semver-generator/semver-generator
|
||||
|
||||
- name: Commit changes
|
||||
uses: stefanzweifel/git-auto-commit-action@v5
|
||||
with:
|
||||
commit_message: "Update go.mod and go.sum"
|
||||
commit_options: "--no-verify --signoff"
|
||||
file_pattern: "go.mod go.sum"
|
||||
autoupdate:
|
||||
uses: lukaszraczylo/shared-actions/.github/workflows/go-autoupdate.yaml@main
|
||||
with:
|
||||
go-version: "1.24"
|
||||
release-workflow: "release.yaml"
|
||||
secrets: inherit
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
name: Pull Request
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
push:
|
||||
branches:
|
||||
- "**"
|
||||
- "!main"
|
||||
|
||||
jobs:
|
||||
pr-checks:
|
||||
uses: lukaszraczylo/shared-actions/.github/workflows/go-pr.yaml@main
|
||||
with:
|
||||
go-version: "1.24"
|
||||
+38
-204
@@ -1,222 +1,56 @@
|
||||
name: Test, scan, build, release
|
||||
name: Test, build, release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**/release.yaml'
|
||||
- 'action.yml'
|
||||
- '**.md'
|
||||
- '**/release.yaml'
|
||||
branches:
|
||||
- "master"
|
||||
- "main"
|
||||
- main
|
||||
|
||||
env:
|
||||
ENABLE_CODE_LINT: false
|
||||
ENABLE_CODE_SCANS: false
|
||||
DEPLOY: false
|
||||
GO_VERSION: 1.21
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
name: Preparing build context
|
||||
release:
|
||||
uses: lukaszraczylo/shared-actions/.github/workflows/go-release.yaml@main
|
||||
with:
|
||||
go-version: ">=1.24"
|
||||
docker-enabled: true
|
||||
rolling-release-tag: "v1"
|
||||
semver-config: "config-release.yaml"
|
||||
secrets: inherit
|
||||
|
||||
update-action-version:
|
||||
needs: release
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
SANITISED_REPOSITORY_NAME: ${{ steps.get_env.outputs.SANITISED_REPOSITORY_NAME }}
|
||||
DOCKER_IMAGE: ${{ steps.get_env.outputs.DOCKER_IMAGE }}
|
||||
GITHUB_COMMIT_NUMBER: ${{ steps.get_env.outputs.GITHUB_COMMIT_NUMBER }}
|
||||
GITHUB_SHA: ${{ steps.get_env.outputs.GITHUB_SHA }}
|
||||
GITHUB_RUN_ID: ${{ steps.get_env.outputs.GITHUB_RUN_ID }}
|
||||
RELEASE_VERSION: ${{ steps.get_env.outputs.RELEASE_VERSION }}
|
||||
if: needs.release.outputs.version != ''
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: '0'
|
||||
- name: Setting environment variables
|
||||
id: get_env
|
||||
run: |
|
||||
DOWNLOAD_URL=$(curl -s https://api.github.com/repos/lukaszraczylo/semver-generator/releases/latest | grep -E ".*browser_download_url.*linux-" | grep -vE "(arm64|md5)" \
|
||||
| cut -d '"' -f 4)
|
||||
curl -s -L -o semver-gen "$DOWNLOAD_URL" && chmod +x semver-gen
|
||||
TMP_SANITISED_REPOSITORY_NAME=$(echo ${{ github.event.repository.name }} | sed -e 's|\.|-|g')
|
||||
TMP_GITHUB_COMMITS_COUNT=$(git rev-list --count HEAD)
|
||||
TMP_GITHUB_COUNT_NUMBER=$(echo ${GITHUB_RUN_NUMBER})
|
||||
TMP_RELEASE_VERSION=$(./semver-gen generate -l -c config-release.yaml | sed -e 's|SEMVER ||g')
|
||||
ref: main
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
echo ">> Release version: $TMP_RELEASE_VERSION <<"
|
||||
|
||||
# Setting outputs
|
||||
echo "SANITISED_REPOSITORY_NAME=$TMP_SANITISED_REPOSITORY_NAME" > $GITHUB_OUTPUT
|
||||
echo "DOCKER_IMAGE=ghcr.io/${{ github.repository_owner }}/$TMP_SANITISED_REPOSITORY_NAME" >> $GITHUB_OUTPUT
|
||||
echo "GITHUB_COMMIT_NUMBER=$TMP_GITHUB_COMMITS_COUNT" >> $GITHUB_OUTPUT
|
||||
echo "GITHUB_SHA=$(echo ${GITHUB_SHA::8})" >> $GITHUB_OUTPUT
|
||||
echo "GITHUB_RUN_ID=$TMP_GITHUB_COUNT_NUMBER" >> $GITHUB_OUTPUT
|
||||
echo "RELEASE_VERSION=$TMP_RELEASE_VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
test:
|
||||
needs: [ prepare ]
|
||||
name: Code checks pipeline
|
||||
runs-on: ubuntu-20.04
|
||||
container: github/super-linter:v3.15.5
|
||||
env:
|
||||
CI: true
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
- name: Lint Code Base
|
||||
if: env.ENABLE_CODE_LINT == true
|
||||
- name: Update action.yml with release version
|
||||
env:
|
||||
VALIDATE_ALL_CODEBASE: true
|
||||
VALIDATE_DOCKERFILE: false # this leaves us with hadolint only
|
||||
VALIDATE_GO: false # disable bulk validation of go files, run the linter manually
|
||||
DEFAULT_BRANCH: main
|
||||
GITHUB_TOKEN: ${{ secrets.GHCR_TOKEN }}
|
||||
LOG_LEVEL: WARN
|
||||
VERSION: ${{ needs.release.outputs.version }}
|
||||
run: |
|
||||
golangci-lint run --exclude-use-default ./...
|
||||
/action/lib/linter.sh
|
||||
- name: Run unit tests
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GHCR_TOKEN }}
|
||||
echo "Updating action.yml to version: ${VERSION}"
|
||||
sed -i "s|ghcr.io/lukaszraczylo/semver-generator:[^\"]*|ghcr.io/lukaszraczylo/semver-generator:${VERSION}|" action.yml
|
||||
echo "Updated action.yml:"
|
||||
grep "image:" action.yml
|
||||
|
||||
- name: Commit and push
|
||||
run: |
|
||||
make test CI_RUN=${CI}
|
||||
- name: Upload codecov result
|
||||
uses: codecov/codecov-action@v3
|
||||
continue-on-error: true
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos
|
||||
files: coverage.out
|
||||
fail_ci_if_error: false
|
||||
|
||||
code_scans:
|
||||
needs: [ prepare ]
|
||||
name: Code scans pipeline
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
- name: Configure git for private modules
|
||||
run: |
|
||||
make update
|
||||
- name: WriteGoList
|
||||
run: go list -json -m all > go.list
|
||||
- name: Running nancy
|
||||
if: env.ENABLE_CODE_SCANS == true
|
||||
uses: sonatype-nexus-community/nancy-github-action@main
|
||||
- name: Running gosec
|
||||
if: env.ENABLE_CODE_SCANS == true
|
||||
uses: securego/gosec@master
|
||||
with:
|
||||
args: ./...
|
||||
|
||||
|
||||
build-binary:
|
||||
needs: [ prepare, test, code_scans ]
|
||||
name: Binary compilation and release
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
- name: Build binaries
|
||||
run: |
|
||||
LOCAL_VERSION=${{ needs.prepare.outputs.RELEASE_VERSION }} make dist-release
|
||||
|
||||
- name: Get list of the commits since last release
|
||||
run: |
|
||||
echo "$(git log $(git describe --tags --abbrev=0)..HEAD --pretty=format:"%h %s")" > .release_notes
|
||||
|
||||
- name: Create release [semver]
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
bodyFile: ./.release_notes
|
||||
name: version ${{ needs.prepare.outputs.RELEASE_VERSION }}
|
||||
token: ${{ secrets.GHCR_TOKEN }}
|
||||
tag: ${{ needs.prepare.outputs.RELEASE_VERSION }}
|
||||
prerelease: ${{ github.ref != 'refs/heads/master' && github.ref != 'refs/heads/main' }}
|
||||
artifacts: "dist/*"
|
||||
allowUpdates: true
|
||||
|
||||
- name: Delete existing v1 tag and release
|
||||
run: |
|
||||
gh release delete v1 --cleanup-tag -y
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GHCR_TOKEN }}
|
||||
|
||||
- name: Create release [v1]
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
bodyFile: ./.release_notes
|
||||
name: v1 - ${{ needs.prepare.outputs.RELEASE_VERSION }}
|
||||
token: ${{ secrets.GHCR_TOKEN }}
|
||||
tag: v1
|
||||
prerelease: ${{ github.ref != 'refs/heads/master' && github.ref != 'refs/heads/main' }}
|
||||
artifacts: "dist/*"
|
||||
allowUpdates: true
|
||||
makeLatest: false
|
||||
|
||||
build-docker:
|
||||
needs: [ prepare, test, code_scans, build-binary ]
|
||||
name: Docker image build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: Login to GHCR
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.ACTOR }}
|
||||
password: ${{ secrets.GHCR_TOKEN }}
|
||||
- name: Prepare for push
|
||||
id: prep
|
||||
run: |
|
||||
if [ -z "${{ needs.prepare.outputs.RELEASE_VERSION }}" ]; then
|
||||
TAGS="${{ needs.prepare.outputs.DOCKER_IMAGE }}:${{ needs.prepare.outputs.GITHUB_SHA }},${{ needs.prepare.outputs.DOCKER_IMAGE }}:latest,${{ needs.prepare.outputs.DOCKER_IMAGE }}:v1"
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git add action.yml
|
||||
if git diff --staged --quiet; then
|
||||
echo "No changes to commit"
|
||||
else
|
||||
TAGS="${{ needs.prepare.outputs.DOCKER_IMAGE }}:${{ needs.prepare.outputs.GITHUB_SHA }},${{ needs.prepare.outputs.DOCKER_IMAGE }}:${{ needs.prepare.outputs.RELEASE_VERSION }},${{ needs.prepare.outputs.DOCKER_IMAGE }}:latest,${{ needs.prepare.outputs.DOCKER_IMAGE }}:v1"
|
||||
git commit -m "chore: pin action.yml Docker image to v${{ needs.release.outputs.version }}"
|
||||
git push
|
||||
fi
|
||||
echo "TAGS=$TAGS" >> $GITHUB_OUTPUT
|
||||
BRANCH=$(echo ${GITHUB_REF##*/} | tr '[A-Z]' '[a-z]')
|
||||
LABELS="org.opencontainers.image.revision=${{ needs.prepare.outputs.GITHUB_SHA }}"
|
||||
LABELS="$LABELS,org.opencontainers.image.created=$(date -u +'%Y-%m-%dT%H:%M:%SZ')"
|
||||
LABELS="$LABELS,org.opencontainers.image.version=$VERSION"
|
||||
LABELS="$LABELS,com.github.repo.branch=$BRANCH"
|
||||
LABELS="$LABELS,com.github.repo.dockerfile=Dockerfile"
|
||||
echo "LABELS=$LABELS" >> $GITHUB_OUTPUT
|
||||
BUILD_ARGS="BRANCH=$BRANCH"
|
||||
echo "args=$BUILD_ARGS" >> $GITHUB_OUTPUT
|
||||
- name: Build image
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
platforms: linux/arm64,linux/amd64
|
||||
push: ${{ github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main' }}
|
||||
tags: ${{ steps.prep.outputs.tags }}
|
||||
build-args: |
|
||||
GITHUB_AUTH_TOKEN=${{ secrets.GHCR_TOKEN }}
|
||||
MICROSERVICE_NAME=${{ github.event.repository.name }}
|
||||
GITHUB_COMMIT_NUMBER=${{ needs.prepare.outputs.GITHUB_COMMIT_NUMBER }}
|
||||
GITHUB_SHA=${{ needs.prepare.outputs.GITHUB_SHA }}
|
||||
${{ steps.prep.outputs.args }}
|
||||
labels: ${{ steps.prep.outputs.labels }}
|
||||
no-cache: false
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
# Simple workflow for deploying static content to GitHub Pages
|
||||
name: Deploy static content to Pages
|
||||
|
||||
on:
|
||||
# Runs on pushes targeting the default branch
|
||||
push:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- 'docs/**'
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
|
||||
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
|
||||
concurrency:
|
||||
group: "pages"
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
# Single deploy job since we're just deploying
|
||||
deploy:
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v5
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
# Upload entire repository
|
||||
path: 'docs/'
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
@@ -0,0 +1,109 @@
|
||||
version: 2
|
||||
|
||||
before:
|
||||
hooks:
|
||||
- go mod tidy
|
||||
|
||||
builds:
|
||||
- id: semver-gen
|
||||
main: .
|
||||
binary: semver-generator
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- linux
|
||||
- darwin
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
ldflags:
|
||||
- -s -w
|
||||
- -X main.PKG_VERSION={{.Version}}
|
||||
|
||||
archives:
|
||||
- id: semver-gen
|
||||
formats: [tar.gz]
|
||||
name_template: "semver-generator-{{ .Os }}-{{ .Arch }}"
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
formats: [zip]
|
||||
files:
|
||||
- LICENSE
|
||||
- README.md
|
||||
- config.yaml
|
||||
|
||||
checksum:
|
||||
name_template: "semver-generator-checksums.txt"
|
||||
algorithm: sha256
|
||||
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
- '^Merge'
|
||||
- '^WIP'
|
||||
- '^Update go.mod'
|
||||
|
||||
release:
|
||||
github:
|
||||
owner: lukaszraczylo
|
||||
name: semver-generator
|
||||
name_template: "version {{.Version}}"
|
||||
draft: false
|
||||
prerelease: auto
|
||||
|
||||
dockers_v2:
|
||||
- images:
|
||||
- "ghcr.io/lukaszraczylo/semver-generator"
|
||||
tags:
|
||||
- "{{ .Version }}"
|
||||
- "latest"
|
||||
- "v1"
|
||||
platforms:
|
||||
- linux/amd64
|
||||
- linux/arm64
|
||||
dockerfile: Dockerfile.goreleaser
|
||||
extra_files:
|
||||
- config-release.yaml
|
||||
- entrypoint.sh
|
||||
|
||||
homebrew_casks:
|
||||
- name: semver-generator
|
||||
repository:
|
||||
owner: lukaszraczylo
|
||||
name: homebrew-taps
|
||||
token: "{{ .Env.HOMEBREW_TAP_TOKEN }}"
|
||||
directory: Casks
|
||||
homepage: https://github.com/lukaszraczylo/semver-generator
|
||||
description: "Automatic semantic version generator based on git commit messages"
|
||||
license: MIT
|
||||
hooks:
|
||||
post:
|
||||
install: |
|
||||
if OS.mac?
|
||||
system_command "/usr/bin/xattr",
|
||||
args: ["-dr", "com.apple.quarantine", "#{staged_path}/semver-generator"]
|
||||
end
|
||||
|
||||
signs:
|
||||
- cmd: cosign
|
||||
signature: "${artifact}.sigstore.json"
|
||||
args:
|
||||
- sign-blob
|
||||
- "--bundle=${signature}"
|
||||
- "${artifact}"
|
||||
- "--yes"
|
||||
artifacts: checksum
|
||||
output: true
|
||||
|
||||
docker_signs:
|
||||
- cmd: cosign
|
||||
artifacts: manifests
|
||||
output: true
|
||||
args:
|
||||
- sign
|
||||
- "${artifact}@${digest}"
|
||||
- "--yes"
|
||||
@@ -0,0 +1,7 @@
|
||||
FROM ubuntu:jammy
|
||||
ARG TARGETPLATFORM
|
||||
COPY ${TARGETPLATFORM}/semver-generator /go/src/app/semver-generator
|
||||
COPY config-release.yaml /go/src/app/config.yaml
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
@@ -1,92 +1,116 @@
|
||||
# Semantic Version Generator
|
||||
## Semantic version generator
|
||||
|
||||
[](https://github.com/lukaszraczylo/semver-generator/actions/workflows/release.yaml)  [](https://codecov.io/gh/lukaszraczylo/semver-generator)
|
||||
|
||||
A lightweight, configurable tool that simplifies semantic versioning by automatically calculating version numbers based on git commit messages. No more manual version management or team debates about versioning - just agree on the keywords and let the tool handle the rest.
|
||||
Project created overnight, to prove that management of semantic versioning is NOT painful and do not require arguments and debates within the team. Simple, clean and only thing the project team should need to agree to are the keywords.
|
||||
|
||||
## Table of Contents
|
||||
- [Semantic version generator](#semantic-version-generator)
|
||||
- [How does it work](#how-does-it-work)
|
||||
- [Additional features](#additional-features)
|
||||
- [Important changes](#important-changes)
|
||||
- [Usage](#usage)
|
||||
- [Authentication](#authentication)
|
||||
- [As a binary](#as-a-binary)
|
||||
- [Homebrew (macOS)](#homebrew-macos)
|
||||
- [Manual Download](#manual-download)
|
||||
- [Self-Update](#self-update)
|
||||
- [As a github action](#as-a-github-action)
|
||||
- [As a docker container](#as-a-docker-container)
|
||||
- [Verifying Release Signatures](#verifying-release-signatures)
|
||||
- [Calculations example \[standard\]](#calculations-example-standard)
|
||||
- [Calculations example \[strict matching\]](#calculations-example-strict-matching)
|
||||
- [Release candidates](#release-candidates)
|
||||
- [Tag prefix stripping](#tag-prefix-stripping)
|
||||
- [Example configuration](#example-configuration)
|
||||
- [Good to knows](#good-to-knows)
|
||||
- [Telemetry](#telemetry)
|
||||
|
||||
- [How It Works](#how-it-works)
|
||||
- [Key Features](#key-features)
|
||||
- [Important Changes](#important-changes)
|
||||
- [Installation](#installation)
|
||||
- [Prerequisites](#prerequisites)
|
||||
- [Binary](#binary)
|
||||
- [Docker](#docker)
|
||||
- [GitHub Action](#github-action)
|
||||
- [Usage](#usage)
|
||||
- [Authentication](#authentication)
|
||||
- [Command Line Options](#command-line-options)
|
||||
- [Configuration File](#configuration-file)
|
||||
- [Versioning Behavior](#versioning-behavior)
|
||||
- [Release Candidates](#release-candidates)
|
||||
- [Examples](#examples)
|
||||
- [Standard Mode](#standard-mode)
|
||||
- [Strict Matching Mode](#strict-matching-mode)
|
||||
- [Advanced Features](#advanced-features)
|
||||
- [Force Settings](#force-settings)
|
||||
- [Blacklist Terms](#blacklist-terms)
|
||||
- [Tips & Best Practices](#tips--best-practices)
|
||||
### How does it work
|
||||
|
||||
## How It Works
|
||||
* Binary clones the github repository
|
||||
* Iterates through the list of commits looking for the keywords specified in config file for additional bumps of versions
|
||||
* Returns the semantic version which can be included in the release
|
||||
|
||||
The semantic version generator follows a simple process:
|
||||
### Additional features
|
||||
|
||||
1. Clones the specified GitHub repository (or uses a local repository)
|
||||
2. Iterates through the commit history, analyzing each commit message
|
||||
3. Looks for predefined keywords (configurable) that trigger version increments
|
||||
4. Calculates the appropriate semantic version based on the matches
|
||||
5. Returns the resulting version that can be used for releases
|
||||
* With flag `-e` or config `force.existing: true` the existing tags in versioning will be respected, helping you to avoid the version conflicts.
|
||||
* With config `force.commit: deadbeef` where `deadbeef` is the commit hash - calculations will start from the specified commit.
|
||||
* Tag prefix stripping: The `v` prefix is automatically stripped from tags (e.g., `v1.2.3` → `1.2.3`). Additional prefixes can be configured via `tag_prefixes` for monorepo setups (e.g., `app-1.2.3`, `infra-1.2.3`).
|
||||
|
||||
## Key Features
|
||||
### Important changes
|
||||
|
||||
- **Effortless Version Calculation**: Automatically determine the appropriate version based on commit messages
|
||||
- **Configurable Keywords**: Define your own keywords for patch, minor, and major version increments
|
||||
- **Support for Existing Tags**: Option to respect existing version tags to avoid conflicts
|
||||
- **Release Candidate Support**: Generate release candidate versions with incrementing counter (e.g., `1.2.3-rc.1`)
|
||||
- **Flexible Repository Source**: Work with either local or remote Git repositories
|
||||
- **Blacklist Support**: Ignore specific commits or branch merges from version calculations
|
||||
- **Force Options**: Start calculations from a specific commit or set a minimum version
|
||||
* From version `1.4.2+` as pointed out in [issue #12](https://github.com/lukaszraczylo/semver-generator/issues/12) commits from merge will not be included in the calculations and commits themselves will bump the version on first match ( starting checks from `patch` upwards ).
|
||||
* Added support for blacklisting terms to ignore specific commits, branch names, and merge messages from version calculations.
|
||||
|
||||
## Important Changes
|
||||
### Usage
|
||||
|
||||
- **Since v1.4.2+**: Commits from merge requests are no longer included in calculations
|
||||
- **Commit Matching Behavior**: Commits will bump the version on the first match (checking from `patch` upwards)
|
||||
- **Blacklist Support**: Added ability to ignore specific terms in commit messages, branch names, and merge requests
|
||||
#### Authentication
|
||||
|
||||
## Installation
|
||||
|
||||
### Prerequisites
|
||||
|
||||
When using with remote repositories, authentication is required. Set the following environment variables:
|
||||
If you intend to use this project with remote repositories ( regardless of them being private or public ) you need to authenticate with your repository.
|
||||
To do so you can utilise the following environment variables ( they are NOT github specific, for other providers you can use the password )
|
||||
|
||||
```bash
|
||||
export GITHUB_USERNAME=yourusername
|
||||
export GITHUB_TOKEN=yourpersonalapitoken
|
||||
export GITHUB_USERNAME=lukaszraczylo
|
||||
export GITHUB_TOKEN=yourPersonalApiToken
|
||||
```
|
||||
|
||||
### Binary
|
||||
#### As a binary
|
||||
|
||||
Download the latest binary from the [release page](https://github.com/lukaszraczylo/semver-generator/releases/latest).
|
||||
|
||||
**Supported platforms**:
|
||||
- Darwin (macOS): ARM64/AMD64
|
||||
- Linux: ARM64/AMD64
|
||||
- Windows: AMD64
|
||||
|
||||
### Docker
|
||||
##### Homebrew (macOS)
|
||||
|
||||
```bash
|
||||
docker pull ghcr.io/lukaszraczylo/semver-generator:latest
|
||||
brew install --cask lukaszraczylo/taps/semver-generator
|
||||
```
|
||||
|
||||
**Supported architectures**:
|
||||
- Linux/arm64
|
||||
- Linux/amd64
|
||||
##### Manual Download
|
||||
|
||||
### GitHub Action
|
||||
You can download latest versions of the binaries from the [release page](https://github.com/lukaszraczylo/semver-generator/releases/latest).
|
||||
|
||||
Add to your GitHub workflow:
|
||||
**Supported OS and architectures:**
|
||||
Darwin ARM64/AMD64, Linux ARM64/AMD64, Windows AMD64
|
||||
|
||||
```bash
|
||||
bash$ semver-generator generate -r https://github.com/nextapps-de/winbox
|
||||
SEMVER 9.0.10
|
||||
bash$ semver-generator generate -l
|
||||
SEMVER 5.1.1
|
||||
```
|
||||
|
||||
**Local repository flag `-l` will always take precedence over remote repository URL**
|
||||
|
||||
```yaml
|
||||
Usage:
|
||||
semver-generator generate [flags]
|
||||
semver-generator [command]
|
||||
|
||||
Available Commands:
|
||||
generate Generates semantic version
|
||||
help Help about any command
|
||||
|
||||
Flags:
|
||||
-c, --config string Path to config file (default "semver.yaml")
|
||||
-d, --debug Enable debug mode
|
||||
-e, --existing Respect existing tags
|
||||
-h, --help help for semver-generator
|
||||
-l, --local Use local repository
|
||||
-r, --repository string Remote repository URL. (default "https://github.com/lukaszraczylo/simple-gql-client")
|
||||
-b, --branch string Remote repository URL Branch. (default "main")
|
||||
-s, --strict Strict matching
|
||||
-u, --update Update binary with latest (no authentication required)
|
||||
-v, --version Display version
|
||||
```
|
||||
|
||||
##### Self-Update
|
||||
|
||||
The binary can update itself to the latest version:
|
||||
|
||||
```bash
|
||||
semver-generator -u
|
||||
```
|
||||
|
||||
This downloads the latest release for your platform directly from GitHub releases. No authentication is required.
|
||||
|
||||
#### As a github action
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
@@ -102,62 +126,119 @@ jobs:
|
||||
fetch-depth: '0'
|
||||
- name: Semver run
|
||||
id: semver
|
||||
uses: lukaszraczylo/semver-generator@v1
|
||||
uses: lukaszraczylo/semver-generator@PLACE_LATEST_TAG_HERE
|
||||
# you can also use v1 tag which _should_ automatically upgrade to latest
|
||||
# uses: lukaszraczylo/semver-generator@v1
|
||||
with:
|
||||
config_file: semver.yaml
|
||||
# Either use local repository
|
||||
# either...
|
||||
repository_local: true
|
||||
# Or specify remote repository
|
||||
# repository_url: https://github.com/lukaszraczylo/simple-gql-client
|
||||
# github_username: ${{ secrets.GH_USERNAME }}
|
||||
# github_token: ${{ secrets.GH_TOKEN }}
|
||||
strict: false
|
||||
existing: true
|
||||
- name: Use semantic version
|
||||
# or...
|
||||
repository_url: https://github.com/lukaszraczylo/simple-gql-client
|
||||
# when using remote repository, especially with private one:
|
||||
github_username: lukaszraczylo
|
||||
github_token: MySupeRSecr3tPa$$w0rd
|
||||
strict: true
|
||||
existing: false
|
||||
- name: Semver check
|
||||
run: |
|
||||
echo "Semantic version detected: ${{ steps.semver.outputs.semantic_version }}"
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Authentication
|
||||
|
||||
For remote repositories (public or private), authenticate using:
|
||||
#### As a docker container
|
||||
|
||||
```bash
|
||||
export GITHUB_USERNAME=yourusername
|
||||
export GITHUB_TOKEN=yourpersonalapitoken
|
||||
docker pull ghcr.io/lukaszraczylo/semver-generator:latest
|
||||
```
|
||||
|
||||
### Command Line Options
|
||||
#### Verifying Release Signatures
|
||||
|
||||
```
|
||||
Usage:
|
||||
semver-gen generate [flags]
|
||||
semver-gen [command]
|
||||
All release checksums and Docker images are signed with [cosign](https://github.com/sigstore/cosign) using keyless signing. To verify:
|
||||
|
||||
Available Commands:
|
||||
generate Generates semantic version
|
||||
help Help about any command
|
||||
```bash
|
||||
# Verify checksum signature
|
||||
cosign verify-blob \
|
||||
--certificate-identity-regexp "https://github.com/lukaszraczylo/semver-generator/.*" \
|
||||
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
|
||||
--bundle "<checksums-file>.sigstore.json" \
|
||||
<checksums-file>
|
||||
|
||||
Flags:
|
||||
-c, --config string Path to config file (default "semver.yaml")
|
||||
-d, --debug Enable debug mode
|
||||
-e, --existing Respect existing tags
|
||||
-h, --help help for semver-gen
|
||||
-l, --local Use local repository
|
||||
-r, --repository string Remote repository URL. (default "https://github.com/lukaszraczylo/simple-gql-client")
|
||||
-b, --branch string Remote repository URL Branch. (default "main")
|
||||
-s, --strict Strict matching
|
||||
-u, --update Update binary with latest
|
||||
-v, --version Display version
|
||||
# Verify Docker image
|
||||
cosign verify \
|
||||
--certificate-identity-regexp "https://github.com/lukaszraczylo/semver-generator/.*" \
|
||||
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
|
||||
ghcr.io/lukaszraczylo/semver-generator:latest
|
||||
```
|
||||
|
||||
**Note**: The `-l/--local` flag takes precedence over the repository URL.
|
||||
**Docker supported architectures:**
|
||||
Linux/arm64, Linux/amd64
|
||||
|
||||
### Configuration File
|
||||
#### Calculations example [standard]
|
||||
|
||||
Create a `semver.yaml` file (or specify a different path with `-c`):
|
||||
```bash
|
||||
- 0.0.1 - PATCH - starting commit
|
||||
- 0.0.2 - PATCH - another commit
|
||||
- 0.0.4 - PATCH - another commit with word 'Update' => DOUBLE increment PATCH
|
||||
- 0.1.0 - MINOR - after commit with word 'Change' => increment MINOR, reset PATCH
|
||||
- 0.1.1 - PATCH - additional commit
|
||||
- 1.0.1 - MAJOR - commit with word 'BREAKING' = > INCREMENT MAJOR, reset MINOR
|
||||
- 1.0.2 - PATCH - another commit
|
||||
```
|
||||
|
||||
#### Calculations example [strict matching]
|
||||
|
||||
```bash
|
||||
- 0.0.1 - PATCH - starting commit
|
||||
- 0.0.1 - PATCH - another commit
|
||||
- 0.0.1 - PATCH - another commit with word 'Update' => SINGLE increment PATCH
|
||||
- 0.1.0 - MINOR - after commit with word 'Change' => increment MINOR, reset PATCH
|
||||
- 0.1.0 - PATCH - additional commit
|
||||
- 1.0.0 - MAJOR - commit with word 'BREAKING' = > INCREMENT MAJOR, reset MINOR
|
||||
- 1.0.0 - PATCH - another commit
|
||||
```
|
||||
|
||||
#### Release candidates
|
||||
|
||||
The `semver-gen` supports release candidates generation as well. Add following configuration ( and change the trigger keywords to anything what suits you )
|
||||
to generate the appropriate release in format `1.3.37-rc.1` and counting up until next `minor` trigger will be detected.
|
||||
|
||||
```yaml
|
||||
release:
|
||||
- release-candidate
|
||||
- add-rc
|
||||
```
|
||||
|
||||
#### Tag prefix stripping
|
||||
|
||||
When using the `-e` (existing tags) flag, the semver-generator needs to parse existing git tags to determine the current version. Tags often include prefixes that need to be stripped before version parsing.
|
||||
|
||||
**Automatic `v` prefix stripping:**
|
||||
The `v` prefix is always stripped automatically from tags. For example:
|
||||
- `v1.2.3` → parsed as `1.2.3`
|
||||
- `v0.5.0` → parsed as `0.5.0`
|
||||
|
||||
**Custom prefixes for monorepos:**
|
||||
In monorepo setups where different components have their own versioned tags, you can configure additional prefixes to strip:
|
||||
|
||||
```yaml
|
||||
tag_prefixes:
|
||||
- "app-"
|
||||
- "infra-"
|
||||
- "api-"
|
||||
- "frontend-"
|
||||
```
|
||||
|
||||
With this configuration:
|
||||
- `app-1.2.3` → parsed as `1.2.3`
|
||||
- `infra-0.5.0` → parsed as `0.5.0`
|
||||
- `api-2.0.0-rc.1` → parsed as `2.0.0-rc.1` (release candidate)
|
||||
|
||||
This is particularly useful when:
|
||||
- You have multiple services/components in a single repository
|
||||
- Your CI/CD creates tags with component prefixes
|
||||
- You want to track versions separately for different parts of your codebase
|
||||
|
||||
#### Example configuration
|
||||
|
||||
```yaml
|
||||
version: 1
|
||||
@@ -166,122 +247,51 @@ force:
|
||||
minor: 0
|
||||
patch: 1
|
||||
commit: 69fbe2df696f40281b9104ff073d26186cde1024
|
||||
existing: true
|
||||
strict: false
|
||||
blacklist:
|
||||
- "Merge branch"
|
||||
- "Merge pull request"
|
||||
- "feature/"
|
||||
- "feature:"
|
||||
tag_prefixes:
|
||||
- "app-"
|
||||
- "infra-"
|
||||
- "service-"
|
||||
wording:
|
||||
patch:
|
||||
- update
|
||||
- initial
|
||||
- fix
|
||||
minor:
|
||||
- change
|
||||
- improve
|
||||
- add
|
||||
major:
|
||||
- breaking
|
||||
- redesign
|
||||
- the # For testing purposes
|
||||
release:
|
||||
- release-candidate
|
||||
- add-rc
|
||||
```
|
||||
|
||||
Configuration options:
|
||||
- `version`: Reserved for future backward compatibility
|
||||
- `force`: Set starting version or other constraints
|
||||
- `blacklist`: Terms to ignore when processing commits
|
||||
- `wording`: Keywords that trigger version increments
|
||||
* `version`: is not respected at the moment, introduced for potential backwards compatibility in future
|
||||
* `force`: sets the "starting" version, you don't need to specify this section as the default is always `0`
|
||||
* `force.commit`: allows you to set commit hash from which the calculations should start
|
||||
* `blacklist`: terms to ignore when processing commits. Any commit containing these terms will be skipped in version calculations. Useful for ignoring merge commits, feature branch names, and other unwanted triggers.
|
||||
* `tag_prefixes`: prefixes to strip from existing tags before parsing version numbers. Useful for monorepos where tags are prefixed with component names (e.g., `app-1.2.3`, `infra-0.5.0`). The `v` prefix is always stripped automatically.
|
||||
* `wording`: words the program should look for in the git commits to increment (patch|minor|major)
|
||||
|
||||
### Versioning Behavior
|
||||
### Good to knows
|
||||
|
||||
The version calculation follows semantic versioning rules:
|
||||
- **MAJOR**: Incremented for incompatible API changes
|
||||
- **MINOR**: Incremented for backward-compatible new features
|
||||
- **PATCH**: Incremented for backward-compatible bug fixes
|
||||
* Word matching uses fuzzy search AND is case INSENSITIVE
|
||||
* I do not recommend using common words ( like "the" from the example configuration )
|
||||
* You can specify env variable `LOG_LEVEL=debug` to see what exactly happens during the calculations
|
||||
|
||||
When keywords are matched:
|
||||
- **MAJOR** match: Increments major version, resets minor and patch versions
|
||||
- **MINOR** match: Increments minor version, resets patch version
|
||||
- **PATCH** match: Increments patch version
|
||||
- By default (non-strict mode), every commit increments patch version
|
||||
### Telemetry
|
||||
|
||||
### Release Candidates
|
||||
On startup this binary sends a single anonymous adoption ping — project name,
|
||||
version, timestamp; no identifiers, no commit content, no repository data.
|
||||
Fire-and-forget with a 2-second timeout; cannot block startup or panic.
|
||||
|
||||
Add the following to your configuration to generate release candidates:
|
||||
See **[oss-telemetry — Disabling telemetry](https://github.com/lukaszraczylo/oss-telemetry#disabling-telemetry)**
|
||||
for the exact wire format, source, and full opt-out documentation.
|
||||
|
||||
```yaml
|
||||
wording:
|
||||
release:
|
||||
- release-candidate
|
||||
- add-rc
|
||||
```
|
||||
|
||||
When a release candidate keyword is found, the version will be formatted as `1.2.3-rc.1`, with the counter incrementing for each new release candidate.
|
||||
|
||||
## Examples
|
||||
|
||||
### Standard Mode
|
||||
|
||||
```
|
||||
- 0.0.1 - PATCH - starting commit
|
||||
- 0.0.2 - PATCH - another commit
|
||||
- 0.0.4 - PATCH - another commit with word 'Update' => DOUBLE increment PATCH
|
||||
- 0.1.0 - MINOR - after commit with word 'Change' => increment MINOR, reset PATCH
|
||||
- 0.1.1 - PATCH - additional commit
|
||||
- 1.0.1 - MAJOR - commit with word 'BREAKING' => INCREMENT MAJOR, reset MINOR
|
||||
- 1.0.2 - PATCH - another commit
|
||||
```
|
||||
|
||||
### Strict Matching Mode
|
||||
|
||||
In strict mode (using `-s` flag or `force.strict: true`), versions only increment when a keyword is matched:
|
||||
|
||||
```
|
||||
- 0.0.1 - PATCH - starting commit
|
||||
- 0.0.1 - PATCH - another commit (no change - no keyword match)
|
||||
- 0.0.2 - PATCH - another commit with word 'Update' => increment PATCH
|
||||
- 0.1.0 - MINOR - after commit with word 'Change' => increment MINOR, reset PATCH
|
||||
- 0.1.0 - PATCH - additional commit (no change - no keyword match)
|
||||
- 1.0.0 - MAJOR - commit with word 'BREAKING' => INCREMENT MAJOR, reset MINOR
|
||||
- 1.0.0 - PATCH - another commit (no change - no keyword match)
|
||||
```
|
||||
|
||||
## Advanced Features
|
||||
|
||||
### Force Settings
|
||||
|
||||
Control versioning with force settings in configuration:
|
||||
|
||||
```yaml
|
||||
force:
|
||||
major: 1 # Set minimum major version
|
||||
minor: 0 # Set minimum minor version
|
||||
patch: 1 # Set minimum patch version
|
||||
commit: 69fbe2df696f40281b9104ff073d26186cde1024 # Start from specific commit
|
||||
existing: true # Respect existing tags (same as -e flag)
|
||||
strict: false # Use strict matching (same as -s flag)
|
||||
```
|
||||
|
||||
### Blacklist Terms
|
||||
|
||||
Ignore specific commits from version calculations:
|
||||
|
||||
```yaml
|
||||
blacklist:
|
||||
- "Merge branch" # Ignore merge commits
|
||||
- "Merge pull request" # Ignore PR merges
|
||||
- "feature/" # Ignore feature branch names
|
||||
- "chore:" # Ignore chore commits
|
||||
```
|
||||
|
||||
## Tips & Best Practices
|
||||
|
||||
- Word matching uses fuzzy search and is case INSENSITIVE
|
||||
- Avoid common words as version triggers (e.g., "the", "and")
|
||||
- Use `LOG_LEVEL=debug` environment variable to see detailed calculation steps
|
||||
- When using as a GitHub Action, ensure `fetch-depth: '0'` to get the complete commit history
|
||||
- For complex projects, consider using a more specific configuration to distinguish between types of changes
|
||||
Quick opt-out: set any of `DO_NOT_TRACK=1`, `OSS_TELEMETRY_DISABLED=1`,
|
||||
or `SEMVER_GENERATOR_DISABLE_TELEMETRY=1`.
|
||||
|
||||
+1
-1
@@ -36,4 +36,4 @@ outputs:
|
||||
description: "Calculated semantic version"
|
||||
runs:
|
||||
using: "docker"
|
||||
image: "docker://ghcr.io/lukaszraczylo/semver-generator:latest"
|
||||
image: "docker://ghcr.io/lukaszraczylo/semver-generator:1.17.1"
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/lukaszraczylo/semver-generator/cmd/utils"
|
||||
)
|
||||
|
||||
// These functions are now in the utils package
|
||||
// They are kept here as stubs for backward compatibility
|
||||
|
||||
func updatePackage() bool {
|
||||
return utils.UpdatePackage()
|
||||
}
|
||||
|
||||
func checkLatestRelease() (string, bool) {
|
||||
return utils.CheckLatestRelease()
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/lukaszraczylo/semver-generator/cmd/utils"
|
||||
)
|
||||
|
||||
func Test_checkLatestRelease(t *testing.T) {
|
||||
utils.InitLogger(true)
|
||||
tests := []struct {
|
||||
name string
|
||||
want string
|
||||
want1 bool
|
||||
}{
|
||||
{
|
||||
name: "Check latest release",
|
||||
want1: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, got1 := checkLatestRelease()
|
||||
if got1 != tt.want1 {
|
||||
t.Errorf("checkLatestRelease() got1 = %v, want %v", got1, tt.want1)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_updatePackage(t *testing.T) {
|
||||
utils.InitLogger(true)
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping test in short / CI mode")
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "Run autoupdater",
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := updatePackage(); got != tt.want {
|
||||
t.Errorf("updatePackage() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+7
-2
@@ -121,11 +121,15 @@ func main() {
|
||||
}
|
||||
|
||||
// List commits
|
||||
utils.ListCommits(&repo.GitRepo)
|
||||
if _, err := utils.ListCommits(&repo.GitRepo); err != nil {
|
||||
utils.Error("Unable to list commits", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
// List existing tags if needed
|
||||
if params.varExisting || repo.Config.Force.Existing {
|
||||
utils.ListExistingTags(&repo.GitRepo)
|
||||
utils.ListExistingTags(&repo.GitRepo, repo.Config.TagPrefixes)
|
||||
}
|
||||
|
||||
// Apply forced versioning
|
||||
@@ -140,6 +144,7 @@ func main() {
|
||||
repo.Semver,
|
||||
params.varExisting || repo.Config.Force.Existing,
|
||||
params.varStrict || repo.Config.Force.Strict,
|
||||
repo.Config.TagPrefixes,
|
||||
)
|
||||
|
||||
// Print semantic version
|
||||
|
||||
+29
-8
@@ -285,7 +285,8 @@ func (suite *Tests) Test_checkMatches() {
|
||||
|
||||
func (suite *Tests) Test_parseExistingSemver() {
|
||||
type args struct {
|
||||
tagName string
|
||||
tagName string
|
||||
prefixes []string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -296,7 +297,8 @@ func (suite *Tests) Test_parseExistingSemver() {
|
||||
{
|
||||
name: "Test parsing existing semver",
|
||||
args: args{
|
||||
tagName: "1.2.3",
|
||||
tagName: "1.2.3",
|
||||
prefixes: []string{},
|
||||
},
|
||||
currentSemver: utils.SemVer{Major: 1, Minor: 1, Patch: 1},
|
||||
wantSemanticVersion: utils.SemVer{
|
||||
@@ -308,7 +310,8 @@ func (suite *Tests) Test_parseExistingSemver() {
|
||||
{
|
||||
name: "Test parsing existing semver with v",
|
||||
args: args{
|
||||
tagName: "v1.2.3",
|
||||
tagName: "v1.2.3",
|
||||
prefixes: []string{"v"},
|
||||
},
|
||||
currentSemver: utils.SemVer{Major: 1, Minor: 1, Patch: 1},
|
||||
wantSemanticVersion: utils.SemVer{
|
||||
@@ -320,7 +323,8 @@ func (suite *Tests) Test_parseExistingSemver() {
|
||||
{
|
||||
name: "Test parsing existing semver with rc",
|
||||
args: args{
|
||||
tagName: "1.2.5-rc.7",
|
||||
tagName: "1.2.5-rc.7",
|
||||
prefixes: []string{},
|
||||
},
|
||||
currentSemver: utils.SemVer{Major: 1, Minor: 1, Patch: 1},
|
||||
wantSemanticVersion: utils.SemVer{
|
||||
@@ -331,10 +335,25 @@ func (suite *Tests) Test_parseExistingSemver() {
|
||||
EnableReleaseCandidate: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Test parsing prefixed tag without rc",
|
||||
args: args{
|
||||
tagName: "app-0.0.16",
|
||||
prefixes: []string{"app-", "infra-"},
|
||||
},
|
||||
currentSemver: utils.SemVer{Major: 1, Minor: 1, Patch: 1},
|
||||
wantSemanticVersion: utils.SemVer{
|
||||
Major: 0,
|
||||
Minor: 0,
|
||||
Patch: 16,
|
||||
EnableReleaseCandidate: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Test invalid semver format",
|
||||
args: args{
|
||||
tagName: "invalid",
|
||||
tagName: "invalid",
|
||||
prefixes: []string{},
|
||||
},
|
||||
currentSemver: utils.SemVer{Major: 2, Minor: 3, Patch: 4},
|
||||
wantSemanticVersion: utils.SemVer{
|
||||
@@ -346,7 +365,8 @@ func (suite *Tests) Test_parseExistingSemver() {
|
||||
{
|
||||
name: "Test partial semver",
|
||||
args: args{
|
||||
tagName: "1.2",
|
||||
tagName: "1.2",
|
||||
prefixes: []string{},
|
||||
},
|
||||
currentSemver: utils.SemVer{Major: 2, Minor: 3, Patch: 4},
|
||||
wantSemanticVersion: utils.SemVer{
|
||||
@@ -358,7 +378,8 @@ func (suite *Tests) Test_parseExistingSemver() {
|
||||
{
|
||||
name: "Test empty tag",
|
||||
args: args{
|
||||
tagName: "",
|
||||
tagName: "",
|
||||
prefixes: []string{},
|
||||
},
|
||||
currentSemver: utils.SemVer{Major: 2, Minor: 3, Patch: 4},
|
||||
wantSemanticVersion: utils.SemVer{
|
||||
@@ -370,7 +391,7 @@ func (suite *Tests) Test_parseExistingSemver() {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
suite.T().Run(tt.name, func(t *testing.T) {
|
||||
got := utils.ParseExistingSemver(tt.args.tagName, tt.currentSemver)
|
||||
got := utils.ParseExistingSemver(tt.args.tagName, tt.currentSemver, tt.args.prefixes)
|
||||
assertObj.Equal(tt.wantSemanticVersion.Major, got.Major, "Unexpected MAJOR semver result in "+tt.name)
|
||||
assertObj.Equal(tt.wantSemanticVersion.Minor, got.Minor, "Unexpected MINOR semver result in "+tt.name)
|
||||
assertObj.Equal(tt.wantSemanticVersion.Patch, got.Patch, "Unexpected PATCH semver result in "+tt.name)
|
||||
|
||||
+16
-6
@@ -26,9 +26,10 @@ type Force struct {
|
||||
|
||||
// Config represents the application configuration
|
||||
type Config struct {
|
||||
Wording Wording
|
||||
Force Force
|
||||
Blacklist []string
|
||||
Wording Wording
|
||||
Force Force
|
||||
Blacklist []string
|
||||
TagPrefixes []string // Prefixes to strip from tags before parsing (e.g., "app-", "infra-", "v")
|
||||
}
|
||||
|
||||
// ReadConfig reads the configuration from a file
|
||||
@@ -42,9 +43,18 @@ func ReadConfig(file string) (*Config, error) {
|
||||
return config, err
|
||||
}
|
||||
|
||||
viper.UnmarshalKey("wording", &config.Wording)
|
||||
viper.UnmarshalKey("force", &config.Force)
|
||||
viper.UnmarshalKey("blacklist", &config.Blacklist)
|
||||
if err := viper.UnmarshalKey("wording", &config.Wording); err != nil {
|
||||
return config, fmt.Errorf("error parsing wording config: %w", err)
|
||||
}
|
||||
if err := viper.UnmarshalKey("force", &config.Force); err != nil {
|
||||
return config, fmt.Errorf("error parsing force config: %w", err)
|
||||
}
|
||||
if err := viper.UnmarshalKey("blacklist", &config.Blacklist); err != nil {
|
||||
return config, fmt.Errorf("error parsing blacklist config: %w", err)
|
||||
}
|
||||
if err := viper.UnmarshalKey("tag_prefixes", &config.TagPrefixes); err != nil {
|
||||
return config, fmt.Errorf("error parsing tag_prefixes config: %w", err)
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
+43
-12
@@ -54,7 +54,7 @@ func PrepareRepository(repo *GitRepository) error {
|
||||
}
|
||||
|
||||
repo.LocalPath = fmt.Sprintf("/tmp/semver/%s/%s", u.Path, repo.Branch)
|
||||
os.RemoveAll(repo.LocalPath)
|
||||
_ = os.RemoveAll(repo.LocalPath) // Ignore error - directory may not exist
|
||||
|
||||
repo.Handler, err = git.PlainClone(repo.LocalPath, false, &git.CloneOptions{
|
||||
URL: repo.Name,
|
||||
@@ -86,7 +86,13 @@ func PrepareRepository(repo *GitRepository) error {
|
||||
}
|
||||
}
|
||||
|
||||
os.Chdir(repo.LocalPath)
|
||||
if err := os.Chdir(repo.LocalPath); err != nil {
|
||||
Error("Unable to change directory", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"path": repo.LocalPath,
|
||||
})
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -112,7 +118,7 @@ func ListCommits(repo *GitRepository) ([]CommitDetails, error) {
|
||||
}
|
||||
|
||||
var tmpResults []CommitDetails
|
||||
commitsList.ForEach(func(c *object.Commit) error {
|
||||
if err := commitsList.ForEach(func(c *object.Commit) error {
|
||||
tmpResults = append(tmpResults, CommitDetails{
|
||||
Hash: c.Hash.String(),
|
||||
Author: c.Author.String(),
|
||||
@@ -123,22 +129,31 @@ func ListCommits(repo *GitRepository) ([]CommitDetails, error) {
|
||||
return tmpResults[i].Timestamp.Unix() < tmpResults[j].Timestamp.Unix()
|
||||
})
|
||||
return nil
|
||||
})
|
||||
}); err != nil {
|
||||
return []CommitDetails{}, err
|
||||
}
|
||||
|
||||
Debug("Listing commits", map[string]interface{}{"commits": tmpResults})
|
||||
|
||||
// Filter commits starting from the specified commit if provided
|
||||
// Filter commits starting after the specified commit if provided
|
||||
if repo.StartCommit != "" {
|
||||
found := false
|
||||
for commitId, cmt := range tmpResults {
|
||||
if cmt.Hash == repo.StartCommit {
|
||||
Debug("Found commit match", map[string]interface{}{
|
||||
"commit": cmt.Hash,
|
||||
"index": commitId,
|
||||
})
|
||||
repo.Commits = tmpResults[commitId:]
|
||||
// Start from the commit AFTER the specified one
|
||||
repo.Commits = tmpResults[commitId+1:]
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
// If specified commit not found, use all commits
|
||||
repo.Commits = tmpResults
|
||||
}
|
||||
} else {
|
||||
repo.Commits = tmpResults
|
||||
}
|
||||
@@ -148,10 +163,13 @@ func ListCommits(repo *GitRepository) ([]CommitDetails, error) {
|
||||
}
|
||||
|
||||
// ListExistingTags lists all tags in the repository
|
||||
func ListExistingTags(repo *GitRepository) {
|
||||
// ListExistingTags lists all tags in the repository.
|
||||
// Tags that don't parse as proper semver (rolling tags like "v1" or "latest")
|
||||
// are skipped so they can't out-rank real semver tags pointing to the same
|
||||
// commit during latest-tag selection.
|
||||
func ListExistingTags(repo *GitRepository, tagPrefixes []string) {
|
||||
Debug("Listing existing tags", nil)
|
||||
|
||||
// Check if Handler is nil to avoid panic
|
||||
if repo.Handler == nil {
|
||||
Debug("Repository handler is nil, skipping tag listing", nil)
|
||||
return
|
||||
@@ -164,14 +182,27 @@ func ListExistingTags(repo *GitRepository) {
|
||||
}
|
||||
|
||||
if err := refs.ForEach(func(ref *plumbing.Reference) error {
|
||||
tagName := ref.Name().Short()
|
||||
|
||||
if !IsParseableSemverTag(tagName, tagPrefixes) {
|
||||
Debug("Skipping non-semver tag", map[string]interface{}{"tag": tagName})
|
||||
return nil
|
||||
}
|
||||
|
||||
commitHash := ref.Hash().String()
|
||||
tagObj, err := repo.Handler.TagObject(ref.Hash())
|
||||
if err == nil {
|
||||
commitHash = tagObj.Target.String()
|
||||
}
|
||||
|
||||
repo.Tags = append(repo.Tags, TagDetails{
|
||||
Name: ref.Name().Short(),
|
||||
Hash: ref.Hash().String(),
|
||||
Name: tagName,
|
||||
Hash: commitHash,
|
||||
})
|
||||
|
||||
Debug("Found tag", map[string]interface{}{
|
||||
"tag": ref.Name().Short(),
|
||||
"hash": ref.Hash().String(),
|
||||
"tag": tagName,
|
||||
"hash": commitHash,
|
||||
})
|
||||
|
||||
return nil
|
||||
|
||||
@@ -142,7 +142,7 @@ func TestListExistingTags(t *testing.T) {
|
||||
repo := &GitRepository{}
|
||||
|
||||
// Now we can safely call ListExistingTags since we've added a nil check
|
||||
ListExistingTags(repo)
|
||||
ListExistingTags(repo, nil)
|
||||
|
||||
// Verify no tags were added
|
||||
assert.Empty(t, repo.Tags, "Should have no tags after calling with nil Handler")
|
||||
|
||||
+319
-108
@@ -1,148 +1,359 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
|
||||
"github.com/lukaszraczylo/ask"
|
||||
graphql "github.com/lukaszraczylo/go-simple-graphql"
|
||||
"github.com/melbahja/got"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// UpdatePackage updates the binary with the latest version
|
||||
func UpdatePackage() bool {
|
||||
ghToken, ghTokenSet := os.LookupEnv("GITHUB_TOKEN")
|
||||
if !ghTokenSet {
|
||||
Error("GITHUB_TOKEN not set", nil)
|
||||
return false
|
||||
}
|
||||
const (
|
||||
// GitHub API endpoint for latest release
|
||||
githubReleasesURL = "https://api.github.com/repos/lukaszraczylo/semver-generator/releases/latest"
|
||||
// Request timeout for HTTP requests
|
||||
requestTimeout = 10 * time.Second
|
||||
)
|
||||
|
||||
binaryName := fmt.Sprintf("semver-gen-%s-%s", runtime.GOOS, runtime.GOARCH)
|
||||
Info("Checking for updates", map[string]interface{}{"binaryName": binaryName})
|
||||
// ReleaseInfo contains information about a GitHub release
|
||||
type ReleaseInfo struct {
|
||||
TagName string `json:"tag_name"`
|
||||
HTMLURL string `json:"html_url"`
|
||||
Name string `json:"name"`
|
||||
Assets []ReleaseAsset `json:"assets"`
|
||||
}
|
||||
|
||||
gql := graphql.NewConnection()
|
||||
gql.SetEndpoint("https://api.github.com/graphql")
|
||||
gql.SetOutput("mapstring")
|
||||
// ReleaseAsset contains information about a release asset
|
||||
type ReleaseAsset struct {
|
||||
Name string `json:"name"`
|
||||
BrowserDownloadURL string `json:"browser_download_url"`
|
||||
}
|
||||
|
||||
headers := map[string]interface{}{
|
||||
"Authorization": fmt.Sprintf("Bearer %s", ghToken),
|
||||
}
|
||||
// httpClient is the HTTP client used for requests (allows mocking in tests)
|
||||
var httpClient = &http.Client{
|
||||
Timeout: requestTimeout,
|
||||
}
|
||||
|
||||
variables := map[string]interface{}{
|
||||
"binaryName": binaryName,
|
||||
}
|
||||
|
||||
var query = `query ($binaryName: String) {
|
||||
repository(name: "semver-generator", owner: "lukaszraczylo") {
|
||||
latestRelease {
|
||||
releaseAssets(first: 10, name: $binaryName) {
|
||||
edges {
|
||||
node {
|
||||
name
|
||||
downloadUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
result, err := gql.Query(query, variables, headers)
|
||||
// CheckLatestRelease checks for the latest release version using REST API
|
||||
// Returns the latest version tag and true if successful, empty string and false otherwise
|
||||
func CheckLatestRelease() (string, bool) {
|
||||
release, err := fetchLatestRelease(context.Background())
|
||||
if err != nil {
|
||||
Error("Unable to query GitHub API", map[string]interface{}{"error": err.Error()})
|
||||
Debug("Unable to check latest release", map[string]interface{}{"error": err.Error()})
|
||||
return "", false
|
||||
}
|
||||
|
||||
version := normalizeVersion(release.TagName)
|
||||
return version, true
|
||||
}
|
||||
|
||||
// UpdatePackage downloads and installs the latest version
|
||||
func UpdatePackage() bool {
|
||||
Info("Checking for updates", nil)
|
||||
|
||||
release, err := fetchLatestRelease(context.Background())
|
||||
if err != nil {
|
||||
Error("Unable to fetch latest release", map[string]interface{}{"error": err.Error()})
|
||||
return false
|
||||
}
|
||||
|
||||
output, ok := ask.For(result, "repository.latestRelease.releaseAssets.edges[0].node.downloadUrl").String("")
|
||||
if !ok {
|
||||
Error("Unable to obtain download url for the binary", map[string]interface{}{
|
||||
"binary": binaryName,
|
||||
"output": output,
|
||||
downloadURL := findBinaryAsset(release.Assets)
|
||||
if downloadURL == "" {
|
||||
Error("Unable to find binary for current platform", map[string]interface{}{
|
||||
"os": runtime.GOOS,
|
||||
"arch": runtime.GOARCH,
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
// Skip actual download in test mode
|
||||
if flag.Lookup("test.v") == nil && os.Getenv("CI") == "" {
|
||||
downloadedBinaryPath := fmt.Sprintf("/tmp/%s", binaryName)
|
||||
g := got.New()
|
||||
err = g.Download(output, downloadedBinaryPath)
|
||||
if err != nil {
|
||||
Error("Unable to download binary", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"binaryPath": downloadedBinaryPath,
|
||||
})
|
||||
return false
|
||||
}
|
||||
Info("Downloading update", map[string]interface{}{
|
||||
"version": release.TagName,
|
||||
"url": downloadURL,
|
||||
})
|
||||
|
||||
currentBinary, err := os.Executable()
|
||||
if err != nil {
|
||||
Error("Unable to obtain current binary path", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return false
|
||||
}
|
||||
// Download to temp file
|
||||
tempFile, err := downloadBinary(downloadURL)
|
||||
if err != nil {
|
||||
Error("Unable to download binary", map[string]interface{}{"error": err.Error()})
|
||||
return false
|
||||
}
|
||||
defer os.Remove(tempFile) // Clean up temp file on failure
|
||||
|
||||
err = os.Rename(downloadedBinaryPath, currentBinary)
|
||||
if err != nil {
|
||||
Error("Unable to overwrite current binary", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
err = os.Chmod(currentBinary, 0777)
|
||||
if err != nil {
|
||||
Error("Unable to make binary executable", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return false
|
||||
}
|
||||
// Get current binary path
|
||||
currentBinary, err := os.Executable()
|
||||
if err != nil {
|
||||
Error("Unable to get current binary path", map[string]interface{}{"error": err.Error()})
|
||||
return false
|
||||
}
|
||||
|
||||
// Replace current binary
|
||||
if err := replaceBinary(tempFile, currentBinary); err != nil {
|
||||
Error("Unable to replace binary", map[string]interface{}{"error": err.Error()})
|
||||
return false
|
||||
}
|
||||
|
||||
Info("Update successful", map[string]interface{}{
|
||||
"version": release.TagName,
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
// CheckLatestRelease checks for the latest release version
|
||||
func CheckLatestRelease() (string, bool) {
|
||||
ghToken, ghTokenSet := os.LookupEnv("GITHUB_TOKEN")
|
||||
if !ghTokenSet {
|
||||
return "[no GITHUB_TOKEN set]", false
|
||||
// fetchLatestRelease fetches the latest release info from GitHub REST API
|
||||
func fetchLatestRelease(ctx context.Context) (*ReleaseInfo, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, githubReleasesURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gql := graphql.NewConnection()
|
||||
gql.SetEndpoint("https://api.github.com/graphql")
|
||||
req.Header.Set("Accept", "application/vnd.github.v3+json")
|
||||
req.Header.Set("User-Agent", "semver-generator")
|
||||
|
||||
headers := map[string]interface{}{
|
||||
"Authorization": fmt.Sprintf("bearer %s", ghToken),
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("GitHub API returned status %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
variables := map[string]interface{}{}
|
||||
var release ReleaseInfo
|
||||
if err := json.NewDecoder(resp.Body).Decode(&release); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var query = `query {
|
||||
repository(name: "semver-generator", owner: "lukaszraczylo", followRenames: true) {
|
||||
releases(last: 2) {
|
||||
nodes {
|
||||
tag {
|
||||
name
|
||||
}
|
||||
}
|
||||
return &release, nil
|
||||
}
|
||||
|
||||
// findBinaryAsset finds the download URL for the current platform
|
||||
func findBinaryAsset(assets []ReleaseAsset) string {
|
||||
// Build expected binary name pattern
|
||||
// Format: semver-gen-{version}-{os}-{arch}.tar.gz or just semver-gen-{os}-{arch}
|
||||
osName := runtime.GOOS
|
||||
archName := runtime.GOARCH
|
||||
|
||||
for _, asset := range assets {
|
||||
name := strings.ToLower(asset.Name)
|
||||
// Match patterns like "semver-gen-1.0.0-darwin-arm64.tar.gz" or "semver-gen-darwin-arm64"
|
||||
if strings.Contains(name, osName) && strings.Contains(name, archName) {
|
||||
// Prefer tar.gz archives
|
||||
if strings.HasSuffix(name, ".tar.gz") {
|
||||
return asset.BrowserDownloadURL
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
result, err := gql.Query(query, variables, headers)
|
||||
if err != nil {
|
||||
Error("Unable to query GitHub API", map[string]interface{}{"error": err.Error()})
|
||||
return "", false
|
||||
}
|
||||
|
||||
output, _ := ask.For(result, "repository.releases.nodes[0].tag.name").String("")
|
||||
if output == "v1" {
|
||||
output, _ = ask.For(result, "repository.releases.nodes[1].tag.name").String("")
|
||||
// Fallback: try to find any matching binary without tar.gz
|
||||
for _, asset := range assets {
|
||||
name := strings.ToLower(asset.Name)
|
||||
if strings.Contains(name, osName) && strings.Contains(name, archName) {
|
||||
// Skip checksums
|
||||
if strings.Contains(name, "checksum") || strings.HasSuffix(name, ".sha256") || strings.HasSuffix(name, ".md5") {
|
||||
continue
|
||||
}
|
||||
return asset.BrowserDownloadURL
|
||||
}
|
||||
}
|
||||
|
||||
return output, true
|
||||
return ""
|
||||
}
|
||||
|
||||
// downloadBinary downloads the binary to a temp file and returns the path
|
||||
func downloadBinary(url string) (string, error) {
|
||||
resp, err := httpClient.Get(url)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", fmt.Errorf("download failed with status %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
// Create temp file
|
||||
tempFile, err := os.CreateTemp("", "semver-generator-update-*")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
tempPath := tempFile.Name()
|
||||
|
||||
// Check if it's a tar.gz archive
|
||||
if strings.HasSuffix(url, ".tar.gz") {
|
||||
// For tar.gz, we need to extract the binary
|
||||
if err := extractTarGz(resp.Body, tempFile); err != nil {
|
||||
_ = tempFile.Close()
|
||||
_ = os.Remove(tempPath)
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
// Direct binary download
|
||||
if _, err := io.Copy(tempFile, resp.Body); err != nil {
|
||||
_ = tempFile.Close()
|
||||
_ = os.Remove(tempPath)
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
if err := tempFile.Close(); err != nil {
|
||||
_ = os.Remove(tempPath)
|
||||
return "", err
|
||||
}
|
||||
return tempPath, nil
|
||||
}
|
||||
|
||||
// extractTarGz extracts the semver-generator binary from a tar.gz archive
|
||||
func extractTarGz(r io.Reader, destFile *os.File) error {
|
||||
// For simplicity, we'll download the whole archive to a temp file first,
|
||||
// then use tar command to extract. This avoids adding archive/tar dependency.
|
||||
|
||||
// Create temp archive file
|
||||
archiveFile, err := os.CreateTemp("", "semver-generator-archive-*.tar.gz")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
archivePath := archiveFile.Name()
|
||||
defer os.Remove(archivePath)
|
||||
|
||||
if _, err := io.Copy(archiveFile, r); err != nil {
|
||||
_ = archiveFile.Close()
|
||||
return err
|
||||
}
|
||||
if err := archiveFile.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Extract using tar command
|
||||
extractDir, err := os.MkdirTemp("", "semver-generator-extract-*")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(extractDir)
|
||||
|
||||
// Use tar to extract
|
||||
cmd := fmt.Sprintf("tar -xzf %s -C %s", archivePath, extractDir)
|
||||
if err := runCommand(cmd); err != nil {
|
||||
return fmt.Errorf("failed to extract archive: %w", err)
|
||||
}
|
||||
|
||||
// Find the binary in the extracted files
|
||||
// Support both new name (semver-generator) and old name (semver-gen) for backwards compatibility
|
||||
binaryPath := ""
|
||||
entries, err := os.ReadDir(extractDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// First try to find semver-generator (new name)
|
||||
for _, entry := range entries {
|
||||
if entry.Name() == "semver-generator" {
|
||||
binaryPath = fmt.Sprintf("%s/%s", extractDir, entry.Name())
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to semver-gen (old name) for older releases
|
||||
if binaryPath == "" {
|
||||
for _, entry := range entries {
|
||||
if entry.Name() == "semver-gen" {
|
||||
binaryPath = fmt.Sprintf("%s/%s", extractDir, entry.Name())
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if binaryPath == "" {
|
||||
return fmt.Errorf("binary not found in archive (looked for semver-generator and semver-gen)")
|
||||
}
|
||||
|
||||
// Copy the binary to the destination
|
||||
srcFile, err := os.Open(binaryPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer srcFile.Close()
|
||||
|
||||
// Seek to beginning of dest file and truncate
|
||||
if _, err := destFile.Seek(0, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := destFile.Truncate(0); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := io.Copy(destFile, srcFile); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// runCommand runs a shell command
|
||||
func runCommand(cmdStr string) error {
|
||||
return runCommandFunc(cmdStr)
|
||||
}
|
||||
|
||||
// runCommandFunc is the function used to run commands (allows mocking in tests)
|
||||
var runCommandFunc = func(cmdStr string) error {
|
||||
cmd := exec.Command("sh", "-c", cmdStr)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// replaceBinary replaces the current binary with the new one
|
||||
func replaceBinary(newBinary, currentBinary string) error {
|
||||
// Make the new binary executable
|
||||
// #nosec G302 -- 0755 is required for executable binaries
|
||||
if err := os.Chmod(newBinary, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Rename (atomic on most systems)
|
||||
if err := os.Rename(newBinary, currentBinary); err != nil {
|
||||
// If rename fails (e.g., cross-device), try copy
|
||||
return copyFile(newBinary, currentBinary)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// copyFile copies a file from src to dst
|
||||
// Note: This function is only called internally with controlled paths from
|
||||
// os.CreateTemp and os.Executable, not with user-supplied paths.
|
||||
func copyFile(src, dst string) error {
|
||||
// #nosec G304 -- src is from os.CreateTemp, not user input
|
||||
srcFile, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer srcFile.Close()
|
||||
|
||||
// #nosec G304 -- dst is from os.Executable, not user input
|
||||
dstFile, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dstFile.Close()
|
||||
|
||||
if _, err := io.Copy(dstFile, srcFile); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Make executable
|
||||
// #nosec G302 -- 0755 is required for executable binaries
|
||||
return os.Chmod(dst, 0755)
|
||||
}
|
||||
|
||||
// normalizeVersion removes 'v' or 'V' prefix and trims whitespace
|
||||
func normalizeVersion(v string) string {
|
||||
v = strings.TrimSpace(v)
|
||||
v = strings.TrimPrefix(v, "v")
|
||||
v = strings.TrimPrefix(v, "V")
|
||||
return v
|
||||
}
|
||||
|
||||
+147
-53
@@ -1,66 +1,160 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCheckLatestRelease(t *testing.T) {
|
||||
// Initialize logger
|
||||
InitLogger(true)
|
||||
|
||||
// Save original environment variables
|
||||
originalToken := os.Getenv("GITHUB_TOKEN")
|
||||
defer os.Setenv("GITHUB_TOKEN", originalToken)
|
||||
|
||||
// Test with no token
|
||||
os.Unsetenv("GITHUB_TOKEN")
|
||||
release, ok := CheckLatestRelease()
|
||||
assert.Equal(t, "[no GITHUB_TOKEN set]", release, "Should return no token message")
|
||||
assert.False(t, ok, "Should return false when no token is set")
|
||||
|
||||
// Test with token but simulating API error
|
||||
// Set a dummy token that won't work with the GitHub API
|
||||
os.Setenv("GITHUB_TOKEN", "dummy-token")
|
||||
release, ok = CheckLatestRelease()
|
||||
assert.Equal(t, "", release, "Should return empty string on API error")
|
||||
assert.False(t, ok, "Should return false on API error")
|
||||
|
||||
// We can't reliably test the successful API call in unit tests
|
||||
// as it would require a valid GitHub token and network access
|
||||
}
|
||||
|
||||
func TestUpdatePackage(t *testing.T) {
|
||||
// Initialize logger
|
||||
InitLogger(true)
|
||||
|
||||
// Save original environment variables
|
||||
originalToken := os.Getenv("GITHUB_TOKEN")
|
||||
defer os.Setenv("GITHUB_TOKEN", originalToken)
|
||||
|
||||
// Test with no token
|
||||
os.Unsetenv("GITHUB_TOKEN")
|
||||
result := UpdatePackage()
|
||||
assert.False(t, result, "Should return false when no token is set")
|
||||
|
||||
// Test with token but simulating API error
|
||||
os.Setenv("GITHUB_TOKEN", "dummy-token")
|
||||
result = UpdatePackage()
|
||||
assert.False(t, result, "Should return false on API error")
|
||||
|
||||
// Create a test flag to simulate test mode
|
||||
if flag.Lookup("test.v") == nil {
|
||||
// This is a hack to simulate the test flag being set
|
||||
// which is used in the UpdatePackage function to skip actual download
|
||||
flag.Bool("test.v", true, "")
|
||||
func TestNormalizeVersion(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{"v1.0.0", "1.0.0"},
|
||||
{"1.0.0", "1.0.0"},
|
||||
{" v2.1.3 ", "2.1.3"},
|
||||
{"V1.0.0", "1.0.0"},
|
||||
{"v", ""},
|
||||
{"", ""},
|
||||
}
|
||||
|
||||
// We can't fully test the update functionality as it would modify the binary
|
||||
// but we've tested the token check logic and API error handling
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.input, func(t *testing.T) {
|
||||
result := normalizeVersion(tt.input)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Note: We're not using mock transports for these tests to avoid
|
||||
// adding complexity. The tests focus on the token presence logic and error handling.
|
||||
func TestFindBinaryAsset(t *testing.T) {
|
||||
assets := []ReleaseAsset{
|
||||
{Name: "semver-gen-1.0.0-linux-amd64.tar.gz", BrowserDownloadURL: "https://example.com/linux-amd64.tar.gz"},
|
||||
{Name: "semver-gen-1.0.0-darwin-arm64.tar.gz", BrowserDownloadURL: "https://example.com/darwin-arm64.tar.gz"},
|
||||
{Name: "semver-gen-1.0.0-darwin-amd64.tar.gz", BrowserDownloadURL: "https://example.com/darwin-amd64.tar.gz"},
|
||||
{Name: "semver-gen-1.0.0-windows-amd64.zip", BrowserDownloadURL: "https://example.com/windows-amd64.zip"},
|
||||
{Name: "semver-gen-1.0.0-checksums.txt", BrowserDownloadURL: "https://example.com/checksums.txt"},
|
||||
}
|
||||
|
||||
// Test finding the correct asset for the current platform
|
||||
url := findBinaryAsset(assets)
|
||||
assert.NotEmpty(t, url, "Should find a binary for the current platform")
|
||||
assert.NotContains(t, url, "checksum", "Should not return checksum file")
|
||||
}
|
||||
|
||||
func TestFindBinaryAssetEmpty(t *testing.T) {
|
||||
assets := []ReleaseAsset{}
|
||||
url := findBinaryAsset(assets)
|
||||
assert.Empty(t, url, "Should return empty string when no assets")
|
||||
}
|
||||
|
||||
func TestCheckLatestRelease(t *testing.T) {
|
||||
// Initialize logger
|
||||
InitLogger(false)
|
||||
|
||||
// Create a mock server
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(`{
|
||||
"tag_name": "v1.2.3",
|
||||
"html_url": "https://github.com/lukaszraczylo/semver-generator/releases/tag/v1.2.3",
|
||||
"name": "Release 1.2.3",
|
||||
"assets": []
|
||||
}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
// Note: In a real test, we'd need to mock the HTTP client or the URL
|
||||
// For now, we just test the network error case
|
||||
release, ok := CheckLatestRelease()
|
||||
// This will either succeed (if network is available) or fail gracefully
|
||||
if ok {
|
||||
assert.NotEmpty(t, release)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFetchLatestReleaseError(t *testing.T) {
|
||||
InitLogger(false)
|
||||
|
||||
// Save original client
|
||||
originalClient := httpClient
|
||||
defer func() { httpClient = originalClient }()
|
||||
|
||||
// Create a mock server that returns an error
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
// We can't easily test this without modifying the URL constant
|
||||
// but we can test the error handling by checking that it fails gracefully
|
||||
release, ok := CheckLatestRelease()
|
||||
// The result depends on whether the real GitHub API is accessible
|
||||
_ = release
|
||||
_ = ok
|
||||
}
|
||||
|
||||
func TestCopyFile(t *testing.T) {
|
||||
// Create a temp source file
|
||||
srcContent := []byte("test content")
|
||||
srcFile, err := os.CreateTemp("", "test-*")
|
||||
assert.NoError(t, err)
|
||||
defer os.Remove(srcFile.Name())
|
||||
|
||||
_, err = srcFile.Write(srcContent)
|
||||
assert.NoError(t, err)
|
||||
srcFile.Close()
|
||||
|
||||
// Create destination path
|
||||
dstPath := srcFile.Name() + ".copy"
|
||||
defer os.Remove(dstPath)
|
||||
|
||||
// Copy the file
|
||||
err = copyFile(srcFile.Name(), dstPath)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify the content
|
||||
content, err := os.ReadFile(dstPath)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, srcContent, content)
|
||||
}
|
||||
|
||||
func TestReplaceBinary(t *testing.T) {
|
||||
// Create a temp "new" binary
|
||||
newContent := []byte("new binary content")
|
||||
newFile, err := os.CreateTemp("", "new-binary-*")
|
||||
assert.NoError(t, err)
|
||||
defer os.Remove(newFile.Name())
|
||||
|
||||
_, err = newFile.Write(newContent)
|
||||
assert.NoError(t, err)
|
||||
newFile.Close()
|
||||
|
||||
// Create a temp "current" binary
|
||||
currentFile, err := os.CreateTemp("", "current-binary-*")
|
||||
assert.NoError(t, err)
|
||||
currentPath := currentFile.Name()
|
||||
defer os.Remove(currentPath)
|
||||
currentFile.Close()
|
||||
|
||||
// Replace the binary
|
||||
err = replaceBinary(newFile.Name(), currentPath)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify the content was replaced
|
||||
content, err := os.ReadFile(currentPath)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, newContent, content)
|
||||
}
|
||||
|
||||
func TestUpdatePackageNoBinary(t *testing.T) {
|
||||
InitLogger(false)
|
||||
|
||||
// This test verifies UpdatePackage handles the case where no binary is found
|
||||
// by testing with a mock that returns empty assets
|
||||
// Note: This would need proper mocking of httpClient to test fully
|
||||
}
|
||||
|
||||
+27
-11
@@ -13,24 +13,40 @@ func CalculateSemver(
|
||||
initialSemver SemVer,
|
||||
respectExisting bool,
|
||||
strictMode bool,
|
||||
tagPrefixes []string,
|
||||
) SemVer {
|
||||
semver := initialSemver
|
||||
startIndex := 0
|
||||
|
||||
for _, commit := range commits {
|
||||
// Check for existing tags if enabled
|
||||
if respectExisting {
|
||||
for _, tagHash := range tags {
|
||||
if commit.Hash == tagHash.Hash {
|
||||
Debug("Found existing tag", map[string]interface{}{
|
||||
"tag": tagHash.Name,
|
||||
"commit": strings.TrimSuffix(commit.Message, "\n"),
|
||||
})
|
||||
semver = ParseExistingSemver(tagHash.Name, semver)
|
||||
continue
|
||||
// If respecting existing tags, find the latest tagged commit and start from there
|
||||
if respectExisting && len(tags) > 0 {
|
||||
latestTagIndex := -1
|
||||
var latestTagName string
|
||||
|
||||
// Find the latest tagged commit (highest index since commits are oldest-first)
|
||||
for i, commit := range commits {
|
||||
for _, tag := range tags {
|
||||
if commit.Hash == tag.Hash {
|
||||
if i > latestTagIndex {
|
||||
latestTagIndex = i
|
||||
latestTagName = tag.Name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we found a tagged commit, use its version and start processing after it
|
||||
if latestTagIndex >= 0 {
|
||||
Debug("Found latest existing tag", map[string]interface{}{
|
||||
"tag": latestTagName,
|
||||
"commit": strings.TrimSuffix(commits[latestTagIndex].Message, "\n"),
|
||||
})
|
||||
semver = ParseExistingSemver(latestTagName, semver, tagPrefixes)
|
||||
startIndex = latestTagIndex + 1
|
||||
}
|
||||
}
|
||||
|
||||
for _, commit := range commits[startIndex:] {
|
||||
// In non-strict mode, increment patch by default
|
||||
if !strictMode {
|
||||
semver.Patch++
|
||||
|
||||
@@ -49,6 +49,7 @@ func TestCalculateSemver(t *testing.T) {
|
||||
initialSemver SemVer
|
||||
respectExisting bool
|
||||
strictMode bool
|
||||
tagPrefixes []string
|
||||
want SemVer
|
||||
}{
|
||||
{
|
||||
@@ -76,6 +77,7 @@ func TestCalculateSemver(t *testing.T) {
|
||||
initialSemver: SemVer{},
|
||||
respectExisting: true,
|
||||
strictMode: false,
|
||||
tagPrefixes: []string{},
|
||||
want: SemVer{
|
||||
Major: 2,
|
||||
Minor: 0,
|
||||
@@ -109,6 +111,7 @@ func TestCalculateSemver(t *testing.T) {
|
||||
initialSemver: SemVer{},
|
||||
respectExisting: true,
|
||||
strictMode: true,
|
||||
tagPrefixes: []string{},
|
||||
want: SemVer{
|
||||
Major: 2,
|
||||
Minor: 0,
|
||||
@@ -142,6 +145,7 @@ func TestCalculateSemver(t *testing.T) {
|
||||
initialSemver: SemVer{},
|
||||
respectExisting: false,
|
||||
strictMode: false,
|
||||
tagPrefixes: []string{},
|
||||
want: SemVer{
|
||||
Major: 0,
|
||||
Minor: 1,
|
||||
@@ -173,6 +177,7 @@ func TestCalculateSemver(t *testing.T) {
|
||||
initialSemver: SemVer{Major: 1},
|
||||
respectExisting: false,
|
||||
strictMode: true,
|
||||
tagPrefixes: []string{},
|
||||
want: SemVer{
|
||||
Major: 1,
|
||||
Minor: 1,
|
||||
@@ -199,6 +204,7 @@ func TestCalculateSemver(t *testing.T) {
|
||||
initialSemver: SemVer{},
|
||||
respectExisting: false,
|
||||
strictMode: false,
|
||||
tagPrefixes: []string{},
|
||||
want: SemVer{
|
||||
Major: 0,
|
||||
Minor: 0,
|
||||
@@ -225,6 +231,7 @@ func TestCalculateSemver(t *testing.T) {
|
||||
initialSemver: SemVer{},
|
||||
respectExisting: false,
|
||||
strictMode: false,
|
||||
tagPrefixes: []string{},
|
||||
want: SemVer{
|
||||
Major: 0,
|
||||
Minor: 0,
|
||||
@@ -233,6 +240,39 @@ func TestCalculateSemver(t *testing.T) {
|
||||
EnableReleaseCandidate: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "With prefixed tags should not be RC",
|
||||
commits: []CommitDetails{
|
||||
{
|
||||
Hash: "commit1",
|
||||
Message: "tagged commit",
|
||||
Timestamp: now.Add(-3 * time.Hour),
|
||||
},
|
||||
{
|
||||
Hash: "commit2",
|
||||
Message: "another commit",
|
||||
Timestamp: now.Add(-2 * time.Hour),
|
||||
},
|
||||
},
|
||||
tags: []TagDetails{
|
||||
{
|
||||
Name: "app-0.0.16",
|
||||
Hash: "commit1",
|
||||
},
|
||||
},
|
||||
wording: wording,
|
||||
blacklist: blacklist,
|
||||
initialSemver: SemVer{},
|
||||
respectExisting: true,
|
||||
strictMode: true, // Use strict mode for predictable results
|
||||
tagPrefixes: []string{"app-", "infra-"},
|
||||
want: SemVer{
|
||||
Major: 0,
|
||||
Minor: 0,
|
||||
Patch: 16, // From tag, no additional increments in strict mode
|
||||
EnableReleaseCandidate: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@@ -245,6 +285,7 @@ func TestCalculateSemver(t *testing.T) {
|
||||
tt.initialSemver,
|
||||
tt.respectExisting,
|
||||
tt.strictMode,
|
||||
tt.tagPrefixes,
|
||||
)
|
||||
|
||||
assert.Equal(t, tt.want.Major, got.Major, "Major version mismatch")
|
||||
|
||||
+82
-9
@@ -51,11 +51,87 @@ func FormatSemver(semver SemVer) string {
|
||||
|
||||
var extractNumber = regexp.MustCompile("[0-9]+")
|
||||
|
||||
// StripTagPrefix removes configured prefixes from a tag name
|
||||
// The "v" prefix is always stripped by default (e.g., v1.2.3 -> 1.2.3)
|
||||
func StripTagPrefix(tagName string, prefixes []string) string {
|
||||
result := tagName
|
||||
|
||||
// Always strip "v" prefix by default
|
||||
if strings.HasPrefix(result, "v") && len(result) > 1 {
|
||||
// Only strip if followed by a digit (to avoid stripping "version-1.0.0")
|
||||
if result[1] >= '0' && result[1] <= '9' {
|
||||
result = result[1:]
|
||||
Debug("Stripped default 'v' prefix from tag", map[string]interface{}{
|
||||
"original": tagName,
|
||||
"result": result,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Then strip any user-configured prefixes
|
||||
for _, prefix := range prefixes {
|
||||
if strings.HasPrefix(result, prefix) {
|
||||
result = strings.TrimPrefix(result, prefix)
|
||||
Debug("Stripped prefix from tag", map[string]interface{}{
|
||||
"original": tagName,
|
||||
"prefix": prefix,
|
||||
"result": result,
|
||||
})
|
||||
break // Only strip one prefix
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// IsParseableSemverTag reports whether tagName looks like a proper semver tag
|
||||
// (X.Y.Z, optionally vX.Y.Z, optionally with -rc.N suffix) after configured
|
||||
// prefixes are stripped. Rolling tags like "v1" or "latest" return false so the
|
||||
// calculator can skip them when picking the latest existing tag — preventing a
|
||||
// rolling tag tied to the same commit as a real semver tag from "winning" the
|
||||
// alphabetical iteration in go-git and resetting the baseline to 0.0.0.
|
||||
func IsParseableSemverTag(tagName string, prefixes []string) bool {
|
||||
clean := StripTagPrefix(tagName, prefixes)
|
||||
if idx := strings.Index(clean, "-rc."); idx != -1 {
|
||||
clean = clean[:idx]
|
||||
}
|
||||
parts := strings.Split(clean, ".")
|
||||
if len(parts) < 3 {
|
||||
return false
|
||||
}
|
||||
for _, p := range parts[:3] {
|
||||
if len(extractNumber.FindAllString(p, -1)) == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ParseExistingSemver parses a semantic version from a tag name
|
||||
func ParseExistingSemver(tagName string, currentSemver SemVer) SemVer {
|
||||
func ParseExistingSemver(tagName string, currentSemver SemVer, prefixes []string) SemVer {
|
||||
Debug("Parsing existing semver", map[string]interface{}{"tag": tagName})
|
||||
|
||||
tagNameParts := strings.Split(tagName, ".")
|
||||
// Strip configured prefixes before parsing
|
||||
cleanTagName := StripTagPrefix(tagName, prefixes)
|
||||
|
||||
// Check for release candidate pattern (-rc.X) before splitting
|
||||
isReleaseCandidate := false
|
||||
rcVersion := 0
|
||||
if idx := strings.Index(cleanTagName, "-rc."); idx != -1 {
|
||||
isReleaseCandidate = true
|
||||
rcPart := cleanTagName[idx+4:] // Get everything after "-rc."
|
||||
rcMatches := extractNumber.FindAllString(rcPart, 1)
|
||||
if len(rcMatches) > 0 {
|
||||
rcVersion, _ = strconv.Atoi(rcMatches[0])
|
||||
}
|
||||
// Remove the RC suffix for version parsing
|
||||
cleanTagName = cleanTagName[:idx]
|
||||
Debug("Detected release candidate", map[string]interface{}{
|
||||
"rc_version": rcVersion,
|
||||
"clean_tag_name": cleanTagName,
|
||||
})
|
||||
}
|
||||
|
||||
tagNameParts := strings.Split(cleanTagName, ".")
|
||||
if len(tagNameParts) < 3 {
|
||||
Debug("Unable to parse incompatible semver (non x.y.z)", map[string]interface{}{"tag": tagName})
|
||||
return currentSemver
|
||||
@@ -81,13 +157,10 @@ func ParseExistingSemver(tagName string, currentSemver SemVer) SemVer {
|
||||
semanticVersion.Patch, _ = strconv.Atoi(patchMatches[0])
|
||||
}
|
||||
|
||||
// Extract release candidate version if present
|
||||
if len(tagNameParts) > 3 {
|
||||
releaseMatches := extractNumber.FindAllString(tagNameParts[3], -1)
|
||||
if len(releaseMatches) > 0 {
|
||||
semanticVersion.Release, _ = strconv.Atoi(releaseMatches[0])
|
||||
semanticVersion.EnableReleaseCandidate = true
|
||||
}
|
||||
// Set release candidate if detected
|
||||
if isReleaseCandidate {
|
||||
semanticVersion.Release = rcVersion
|
||||
semanticVersion.EnableReleaseCandidate = true
|
||||
}
|
||||
|
||||
return semanticVersion
|
||||
|
||||
@@ -61,12 +61,14 @@ func TestParseExistingSemver(t *testing.T) {
|
||||
name string
|
||||
tagName string
|
||||
currentSemver SemVer
|
||||
prefixes []string
|
||||
want SemVer
|
||||
}{
|
||||
{
|
||||
name: "Standard semver",
|
||||
tagName: "1.2.3",
|
||||
currentSemver: SemVer{},
|
||||
prefixes: []string{},
|
||||
want: SemVer{
|
||||
Major: 1,
|
||||
Minor: 2,
|
||||
@@ -74,9 +76,32 @@ func TestParseExistingSemver(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "With v prefix",
|
||||
name: "With v prefix configured",
|
||||
tagName: "v2.3.4",
|
||||
currentSemver: SemVer{},
|
||||
prefixes: []string{"v"},
|
||||
want: SemVer{
|
||||
Major: 2,
|
||||
Minor: 3,
|
||||
Patch: 4,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "With app- prefix configured",
|
||||
tagName: "app-1.2.3",
|
||||
currentSemver: SemVer{},
|
||||
prefixes: []string{"app-", "infra-"},
|
||||
want: SemVer{
|
||||
Major: 1,
|
||||
Minor: 2,
|
||||
Patch: 3,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "With prefix but not in config - should still parse numbers",
|
||||
tagName: "v2.3.4",
|
||||
currentSemver: SemVer{},
|
||||
prefixes: []string{}, // v not in prefixes
|
||||
want: SemVer{
|
||||
Major: 2,
|
||||
Minor: 3,
|
||||
@@ -87,6 +112,7 @@ func TestParseExistingSemver(t *testing.T) {
|
||||
name: "With release candidate",
|
||||
tagName: "3.4.5-rc.2",
|
||||
currentSemver: SemVer{},
|
||||
prefixes: []string{},
|
||||
want: SemVer{
|
||||
Major: 3,
|
||||
Minor: 4,
|
||||
@@ -95,6 +121,31 @@ func TestParseExistingSemver(t *testing.T) {
|
||||
EnableReleaseCandidate: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "With prefix and release candidate",
|
||||
tagName: "app-1.0.0-rc.3",
|
||||
currentSemver: SemVer{},
|
||||
prefixes: []string{"app-"},
|
||||
want: SemVer{
|
||||
Major: 1,
|
||||
Minor: 0,
|
||||
Patch: 0,
|
||||
Release: 3,
|
||||
EnableReleaseCandidate: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Prefixed tag without RC should NOT be RC",
|
||||
tagName: "app-0.0.16",
|
||||
currentSemver: SemVer{},
|
||||
prefixes: []string{"app-"},
|
||||
want: SemVer{
|
||||
Major: 0,
|
||||
Minor: 0,
|
||||
Patch: 16,
|
||||
EnableReleaseCandidate: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Invalid format",
|
||||
tagName: "not-a-semver",
|
||||
@@ -103,6 +154,7 @@ func TestParseExistingSemver(t *testing.T) {
|
||||
Minor: 1,
|
||||
Patch: 1,
|
||||
},
|
||||
prefixes: []string{},
|
||||
want: SemVer{
|
||||
Major: 1,
|
||||
Minor: 1,
|
||||
@@ -117,6 +169,7 @@ func TestParseExistingSemver(t *testing.T) {
|
||||
Minor: 5,
|
||||
Patch: 5,
|
||||
},
|
||||
prefixes: []string{},
|
||||
want: SemVer{
|
||||
Major: 5,
|
||||
Minor: 5,
|
||||
@@ -127,7 +180,7 @@ func TestParseExistingSemver(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := ParseExistingSemver(tt.tagName, tt.currentSemver)
|
||||
got := ParseExistingSemver(tt.tagName, tt.currentSemver, tt.prefixes)
|
||||
assert.Equal(t, tt.want.Major, got.Major, "Major version mismatch")
|
||||
assert.Equal(t, tt.want.Minor, got.Minor, "Minor version mismatch")
|
||||
assert.Equal(t, tt.want.Patch, got.Patch, "Patch version mismatch")
|
||||
@@ -136,6 +189,34 @@ func TestParseExistingSemver(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
func TestIsParseableSemverTag(t *testing.T) {
|
||||
InitLogger(false)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
tag string
|
||||
prefixes []string
|
||||
want bool
|
||||
}{
|
||||
{name: "plain x.y.z", tag: "1.2.3", want: true},
|
||||
{name: "v prefix", tag: "v1.16.5", want: true},
|
||||
{name: "v prefix with rc", tag: "v2.0.0-rc.3", want: true},
|
||||
{name: "configured app- prefix", tag: "app-1.2.3", prefixes: []string{"app-"}, want: true},
|
||||
|
||||
{name: "rolling v1 tag", tag: "v1", want: false},
|
||||
{name: "rolling latest tag", tag: "latest", want: false},
|
||||
{name: "two-component", tag: "1.2", want: false},
|
||||
{name: "empty", tag: "", want: false},
|
||||
{name: "non-numeric major", tag: "vX.Y.Z", want: false},
|
||||
{name: "just text", tag: "release-day", want: false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equal(t, tt.want, IsParseableSemverTag(tt.tag, tt.prefixes))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckMatches(t *testing.T) {
|
||||
// Initialize logger for tests
|
||||
|
||||
+1
-8
@@ -1,17 +1,10 @@
|
||||
version: 1
|
||||
force:
|
||||
major: 1
|
||||
minor: 4
|
||||
existing: true
|
||||
strict: false
|
||||
commit: 960207e4677476ad31a9f389f74eaf9f33d49613
|
||||
wording:
|
||||
patch:
|
||||
- update
|
||||
- initial
|
||||
- fix
|
||||
minor:
|
||||
- change
|
||||
- improve
|
||||
major:
|
||||
- breaking
|
||||
- breaking
|
||||
|
||||
@@ -8,6 +8,10 @@ blacklist:
|
||||
- "Merge pull request"
|
||||
- "feature/"
|
||||
- "feature:"
|
||||
tag_prefixes:
|
||||
# Note: "v" prefix is stripped automatically, no need to include it here
|
||||
- "app-"
|
||||
- "infra-"
|
||||
wording:
|
||||
patch:
|
||||
- update
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
semver-generator.raczylo.com
|
||||
+500
@@ -0,0 +1,500 @@
|
||||
<!doctype html>
|
||||
<html lang="en" class="scroll-smooth">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>semver-generator - Automatic Semantic Version Generator</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Automatic semantic version generator based on git commit messages. Use as CLI, GitHub Action, or Docker container."
|
||||
/>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
darkMode: 'class'
|
||||
}
|
||||
</script>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"
|
||||
/>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<style>
|
||||
body { font-family: "Inter", sans-serif; }
|
||||
code, pre { font-family: "JetBrains Mono", monospace; }
|
||||
.theme-transition {
|
||||
transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
|
||||
}
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(20px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
@keyframes float {
|
||||
0%, 100% { transform: translateY(0px); }
|
||||
50% { transform: translateY(-10px); }
|
||||
}
|
||||
.animate-fade-in-up { animation: fadeInUp 0.6s ease-out; }
|
||||
.animate-float { animation: float 3s ease-in-out infinite; }
|
||||
.glass {
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
.dark .glass {
|
||||
background: rgba(17, 24, 39, 0.7);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.gradient-text {
|
||||
background: linear-gradient(135deg, #10b981 0%, #3b82f6 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
.dark .gradient-text {
|
||||
background: linear-gradient(135deg, #34d399 0%, #60a5fa 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
.shadow-modern { box-shadow: 0 10px 40px -10px rgba(0, 0, 0, 0.1); }
|
||||
.dark .shadow-modern { box-shadow: 0 10px 40px -10px rgba(0, 0, 0, 0.4); }
|
||||
html { scroll-behavior: smooth; }
|
||||
</style>
|
||||
<script>
|
||||
if (localStorage.theme === "dark" || (!("theme" in localStorage) && window.matchMedia("(prefers-color-scheme: dark)").matches)) {
|
||||
document.documentElement.classList.add("dark");
|
||||
} else {
|
||||
document.documentElement.classList.remove("dark");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 theme-transition">
|
||||
<!-- Navigation -->
|
||||
<nav class="fixed w-full glass shadow-modern z-50 theme-transition">
|
||||
<div class="max-w-6xl mx-auto px-4 sm:px-6">
|
||||
<div class="flex justify-between h-16 items-center">
|
||||
<a href="#" class="flex items-center hover:opacity-80 transition-opacity duration-300 gap-2">
|
||||
<i class="fas fa-code-branch text-2xl gradient-text"></i>
|
||||
<span class="text-xl font-bold gradient-text">semver-generator</span>
|
||||
</a>
|
||||
<div class="hidden md:flex space-x-6">
|
||||
<a href="#features" class="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 font-medium">Features</a>
|
||||
<a href="#installation" class="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 font-medium">Installation</a>
|
||||
<a href="#usage" class="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 font-medium">Usage</a>
|
||||
<a href="#configuration" class="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 font-medium">Configuration</a>
|
||||
</div>
|
||||
<div class="flex items-center space-x-4">
|
||||
<button id="theme-toggle" class="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 p-2 min-w-[44px] min-h-[44px] flex items-center justify-center" aria-label="Toggle theme">
|
||||
<i class="fas fa-moon dark:hidden text-xl"></i>
|
||||
<i class="fas fa-sun hidden dark:inline text-xl"></i>
|
||||
</button>
|
||||
<a href="https://github.com/lukaszraczylo/semver-generator" target="_blank" class="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 p-2 min-w-[44px] min-h-[44px] flex items-center justify-center" aria-label="View on GitHub">
|
||||
<i class="fab fa-github text-xl"></i>
|
||||
</a>
|
||||
<button id="mobile-menu-toggle" class="md:hidden text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 p-2 min-w-[44px] min-h-[44px] flex items-center justify-center" aria-label="Toggle menu">
|
||||
<i class="fas fa-bars text-xl" id="menu-open-icon"></i>
|
||||
<i class="fas fa-times text-xl hidden" id="menu-close-icon"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="mobile-menu" class="hidden md:hidden border-t border-gray-200 dark:border-gray-700">
|
||||
<div class="px-4 py-3 space-y-1 bg-white dark:bg-gray-800">
|
||||
<a href="#features" class="block px-3 py-3 text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 hover:bg-gray-50 dark:hover:bg-gray-700 rounded font-medium">Features</a>
|
||||
<a href="#installation" class="block px-3 py-3 text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 hover:bg-gray-50 dark:hover:bg-gray-700 rounded font-medium">Installation</a>
|
||||
<a href="#usage" class="block px-3 py-3 text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 hover:bg-gray-50 dark:hover:bg-gray-700 rounded font-medium">Usage</a>
|
||||
<a href="#configuration" class="block px-3 py-3 text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 hover:bg-gray-50 dark:hover:bg-gray-700 rounded font-medium">Configuration</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Hero Section -->
|
||||
<section class="relative pt-24 sm:pt-32 pb-12 sm:pb-20 overflow-hidden">
|
||||
<div class="absolute inset-0 bg-gradient-to-br from-emerald-50 via-cyan-50 to-blue-50 dark:from-gray-900 dark:via-emerald-900/20 dark:to-blue-900/20 theme-transition"></div>
|
||||
<div class="absolute top-0 -left-4 w-72 h-72 bg-emerald-300 dark:bg-emerald-500 rounded-full mix-blend-multiply dark:mix-blend-soft-light filter blur-xl opacity-20 animate-float"></div>
|
||||
<div class="absolute top-0 -right-4 w-72 h-72 bg-cyan-300 dark:bg-cyan-500 rounded-full mix-blend-multiply dark:mix-blend-soft-light filter blur-xl opacity-20 animate-float" style="animation-delay: 1s;"></div>
|
||||
<div class="absolute -bottom-8 left-20 w-72 h-72 bg-blue-300 dark:bg-blue-500 rounded-full mix-blend-multiply dark:mix-blend-soft-light filter blur-xl opacity-20 animate-float" style="animation-delay: 2s;"></div>
|
||||
|
||||
<div class="relative max-w-6xl mx-auto px-4 sm:px-6">
|
||||
<div class="text-center">
|
||||
<div class="mb-8 sm:mb-10 flex justify-center animate-fade-in-up">
|
||||
<div class="text-8xl sm:text-9xl animate-float">
|
||||
<i class="fas fa-code-branch gradient-text"></i>
|
||||
</div>
|
||||
</div>
|
||||
<h1 class="text-3xl sm:text-4xl md:text-5xl lg:text-6xl font-bold text-gray-900 dark:text-gray-100 mb-4 sm:mb-6 leading-tight animate-fade-in-up" style="animation-delay: 0.1s;">
|
||||
Semantic Version<br /><span class="gradient-text">Generator</span>
|
||||
</h1>
|
||||
<p class="text-base sm:text-lg md:text-xl text-gray-600 dark:text-gray-300 mb-8 sm:mb-10 max-w-2xl mx-auto leading-relaxed px-4 animate-fade-in-up" style="animation-delay: 0.2s;">
|
||||
Automatic semantic versioning based on git commit messages. Use as a CLI tool, GitHub Action, or Docker container.
|
||||
</p>
|
||||
<div class="flex flex-col sm:flex-row gap-3 sm:gap-4 justify-center mb-8 sm:mb-12 px-4 animate-fade-in-up" style="animation-delay: 0.3s;">
|
||||
<a href="#installation" class="group relative bg-gradient-to-r from-emerald-500 to-blue-600 hover:from-emerald-600 hover:to-blue-700 text-white px-8 py-3 rounded-lg font-medium transition-all duration-300 min-h-[48px] flex items-center justify-center shadow-lg hover:shadow-xl hover:scale-105">
|
||||
<span class="relative z-10">Get Started</span>
|
||||
</a>
|
||||
<a href="https://github.com/lukaszraczylo/semver-generator" class="group glass hover:shadow-lg text-gray-900 dark:text-gray-100 px-8 py-3 rounded-lg font-medium transition-all duration-300 min-h-[48px] flex items-center justify-center hover:scale-105">
|
||||
<i class="fab fa-github mr-2"></i>View on GitHub
|
||||
</a>
|
||||
</div>
|
||||
<div class="flex flex-wrap justify-center gap-2 sm:gap-4 text-sm px-4">
|
||||
<img src="https://img.shields.io/github/v/release/lukaszraczylo/semver-generator" alt="Version" class="h-5" />
|
||||
<img src="https://img.shields.io/github/license/lukaszraczylo/semver-generator" alt="License" class="h-5" />
|
||||
<img src="https://goreportcard.com/badge/github.com/lukaszraczylo/semver-generator" alt="Go Report" class="h-5" />
|
||||
</div>
|
||||
<div class="mt-12 sm:mt-16 max-w-3xl mx-auto px-4 animate-fade-in-up" style="animation-delay: 0.4s;">
|
||||
<div class="relative group">
|
||||
<div class="absolute -inset-1 bg-gradient-to-r from-emerald-500 to-blue-600 rounded-xl blur opacity-25 group-hover:opacity-50 transition duration-500"></div>
|
||||
<div class="relative bg-gray-900 rounded-xl p-6 text-left">
|
||||
<div class="flex items-center gap-2 mb-4">
|
||||
<div class="w-3 h-3 rounded-full bg-red-500"></div>
|
||||
<div class="w-3 h-3 rounded-full bg-yellow-500"></div>
|
||||
<div class="w-3 h-3 rounded-full bg-green-500"></div>
|
||||
<span class="ml-2 text-gray-400 text-sm">terminal</span>
|
||||
</div>
|
||||
<pre class="text-gray-100 text-sm sm:text-base overflow-x-auto"><code><span class="text-gray-400">$</span> semver-generator generate -l
|
||||
<span class="text-emerald-400">SEMVER</span> 1.5.2
|
||||
|
||||
<span class="text-gray-400">$</span> semver-generator generate -r https://github.com/user/repo
|
||||
<span class="text-emerald-400">SEMVER</span> 2.3.0</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Features Section -->
|
||||
<section id="features" class="py-12 sm:py-16 md:py-20 bg-white dark:bg-gray-900 theme-transition">
|
||||
<div class="max-w-6xl mx-auto px-4 sm:px-6">
|
||||
<div class="text-center mb-8 sm:mb-12">
|
||||
<h2 class="text-2xl sm:text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100 mb-3 sm:mb-4">Features</h2>
|
||||
<p class="text-base sm:text-lg text-gray-600 dark:text-gray-300 px-4">Automatic versioning that just works</p>
|
||||
</div>
|
||||
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<div class="glass p-5 rounded-xl group hover:shadow-lg transition-all duration-300">
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="w-12 h-12 rounded-xl bg-gradient-to-br from-emerald-500 to-emerald-600 flex items-center justify-center flex-shrink-0 group-hover:scale-110 transition-transform duration-300">
|
||||
<i class="fas fa-magic text-white"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="font-semibold text-gray-900 dark:text-gray-100 mb-1">Automatic Versioning</h3>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">Calculates version based on commit message keywords</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass p-5 rounded-xl group hover:shadow-lg transition-all duration-300">
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="w-12 h-12 rounded-xl bg-gradient-to-br from-blue-500 to-blue-600 flex items-center justify-center flex-shrink-0 group-hover:scale-110 transition-transform duration-300">
|
||||
<i class="fab fa-github text-white"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="font-semibold text-gray-900 dark:text-gray-100 mb-1">GitHub Action</h3>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">Use directly in your CI/CD workflows</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass p-5 rounded-xl group hover:shadow-lg transition-all duration-300">
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="w-12 h-12 rounded-xl bg-gradient-to-br from-cyan-500 to-cyan-600 flex items-center justify-center flex-shrink-0 group-hover:scale-110 transition-transform duration-300">
|
||||
<i class="fab fa-docker text-white"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="font-semibold text-gray-900 dark:text-gray-100 mb-1">Docker Ready</h3>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">Multi-arch Docker images for any environment</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass p-5 rounded-xl group hover:shadow-lg transition-all duration-300">
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="w-12 h-12 rounded-xl bg-gradient-to-br from-purple-500 to-purple-600 flex items-center justify-center flex-shrink-0 group-hover:scale-110 transition-transform duration-300">
|
||||
<i class="fas fa-cog text-white"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="font-semibold text-gray-900 dark:text-gray-100 mb-1">Configurable Keywords</h3>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">Define your own trigger words for version bumps</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass p-5 rounded-xl group hover:shadow-lg transition-all duration-300">
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="w-12 h-12 rounded-xl bg-gradient-to-br from-orange-500 to-orange-600 flex items-center justify-center flex-shrink-0 group-hover:scale-110 transition-transform duration-300">
|
||||
<i class="fas fa-tag text-white"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="font-semibold text-gray-900 dark:text-gray-100 mb-1">Respects Tags</h3>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">Optional mode to respect existing git tags</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass p-5 rounded-xl group hover:shadow-lg transition-all duration-300">
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="w-12 h-12 rounded-xl bg-gradient-to-br from-pink-500 to-pink-600 flex items-center justify-center flex-shrink-0 group-hover:scale-110 transition-transform duration-300">
|
||||
<i class="fas fa-flask text-white"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="font-semibold text-gray-900 dark:text-gray-100 mb-1">Release Candidates</h3>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">Support for RC versions like 1.3.37-rc.1</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Installation Section -->
|
||||
<section id="installation" class="py-12 sm:py-16 md:py-20 bg-gray-50 dark:bg-gray-800 theme-transition">
|
||||
<div class="max-w-6xl mx-auto px-4 sm:px-6">
|
||||
<div class="text-center mb-8 sm:mb-12">
|
||||
<h2 class="text-2xl sm:text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100 mb-3 sm:mb-4">Installation</h2>
|
||||
<p class="text-base sm:text-lg text-gray-600 dark:text-gray-300 px-4">Get started in seconds</p>
|
||||
</div>
|
||||
<div class="max-w-3xl mx-auto space-y-6">
|
||||
<div class="glass p-6 rounded-xl">
|
||||
<h3 class="font-semibold text-gray-900 dark:text-gray-100 mb-3 flex items-center">
|
||||
<i class="fas fa-beer mr-2 text-amber-500"></i>
|
||||
Homebrew (macOS)
|
||||
</h3>
|
||||
<pre class="bg-gray-900 text-gray-100 p-4 rounded-lg overflow-x-auto"><code>brew install --cask lukaszraczylo/taps/semver-generator</code></pre>
|
||||
</div>
|
||||
<div class="glass p-6 rounded-xl">
|
||||
<h3 class="font-semibold text-gray-900 dark:text-gray-100 mb-3 flex items-center">
|
||||
<i class="fas fa-download mr-2 text-emerald-500"></i>
|
||||
Manual Download
|
||||
</h3>
|
||||
<p class="text-gray-600 dark:text-gray-400 mb-3">Download from the <a href="https://github.com/lukaszraczylo/semver-generator/releases/latest" class="text-emerald-600 dark:text-emerald-400 hover:underline">releases page</a>.</p>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">Supported: Darwin ARM64/AMD64, Linux ARM64/AMD64, Windows AMD64</p>
|
||||
</div>
|
||||
<div class="glass p-6 rounded-xl">
|
||||
<h3 class="font-semibold text-gray-900 dark:text-gray-100 mb-3 flex items-center">
|
||||
<i class="fab fa-docker mr-2 text-blue-500"></i>
|
||||
Docker
|
||||
</h3>
|
||||
<pre class="bg-gray-900 text-gray-100 p-4 rounded-lg overflow-x-auto"><code>docker pull ghcr.io/lukaszraczylo/semver-generator:latest</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Usage Section -->
|
||||
<section id="usage" class="py-12 sm:py-16 md:py-20 bg-white dark:bg-gray-900 theme-transition">
|
||||
<div class="max-w-6xl mx-auto px-4 sm:px-6">
|
||||
<div class="text-center mb-8 sm:mb-12">
|
||||
<h2 class="text-2xl sm:text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100 mb-3 sm:mb-4">Usage</h2>
|
||||
<p class="text-base sm:text-lg text-gray-600 dark:text-gray-300 px-4">Multiple ways to use semver-generator</p>
|
||||
</div>
|
||||
<div class="max-w-4xl mx-auto space-y-8">
|
||||
<div class="glass p-6 rounded-xl">
|
||||
<h3 class="font-semibold text-gray-900 dark:text-gray-100 mb-4 flex items-center text-xl">
|
||||
<i class="fas fa-terminal mr-2 text-emerald-500"></i>
|
||||
CLI Usage
|
||||
</h3>
|
||||
<pre class="bg-gray-900 text-gray-100 p-4 rounded-lg overflow-x-auto mb-4"><code><span class="text-gray-400"># Local repository</span>
|
||||
semver-generator generate -l
|
||||
|
||||
<span class="text-gray-400"># Remote repository</span>
|
||||
semver-generator generate -r https://github.com/user/repo
|
||||
|
||||
<span class="text-gray-400"># With custom config</span>
|
||||
semver-generator generate -l -c semver.yaml
|
||||
|
||||
<span class="text-gray-400"># Strict mode (only exact matches)</span>
|
||||
semver-generator generate -l -s
|
||||
|
||||
<span class="text-gray-400"># Respect existing tags</span>
|
||||
semver-generator generate -l -e
|
||||
|
||||
<span class="text-gray-400"># Self-update to latest version (no auth required)</span>
|
||||
semver-generator -u</code></pre>
|
||||
<div class="grid sm:grid-cols-2 gap-4 text-sm">
|
||||
<div>
|
||||
<h4 class="font-medium text-gray-900 dark:text-gray-100 mb-2">Flags</h4>
|
||||
<ul class="space-y-1 text-gray-600 dark:text-gray-400">
|
||||
<li><code class="text-emerald-600 dark:text-emerald-400">-l, --local</code> Use local repository</li>
|
||||
<li><code class="text-emerald-600 dark:text-emerald-400">-r, --repository</code> Remote repository URL</li>
|
||||
<li><code class="text-emerald-600 dark:text-emerald-400">-c, --config</code> Path to config file</li>
|
||||
<li><code class="text-emerald-600 dark:text-emerald-400">-s, --strict</code> Strict matching</li>
|
||||
<li><code class="text-emerald-600 dark:text-emerald-400">-e, --existing</code> Respect existing tags</li>
|
||||
<li><code class="text-emerald-600 dark:text-emerald-400">-d, --debug</code> Enable debug mode</li>
|
||||
<li><code class="text-emerald-600 dark:text-emerald-400">-u, --update</code> Self-update to latest version</li>
|
||||
<li><code class="text-emerald-600 dark:text-emerald-400">-v, --version</code> Display current version</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="glass p-6 rounded-xl">
|
||||
<h3 class="font-semibold text-gray-900 dark:text-gray-100 mb-4 flex items-center text-xl">
|
||||
<i class="fab fa-github mr-2 text-blue-500"></i>
|
||||
GitHub Action
|
||||
</h3>
|
||||
<pre class="bg-gray-900 text-gray-100 p-4 rounded-lg overflow-x-auto text-sm"><code>jobs:
|
||||
version:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
version: ${{ steps.semver.outputs.semantic_version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: '0'
|
||||
|
||||
- name: Generate version
|
||||
id: semver
|
||||
uses: lukaszraczylo/semver-generator@v1
|
||||
with:
|
||||
config_file: semver.yaml
|
||||
repository_local: true
|
||||
|
||||
- name: Use version
|
||||
run: echo "Version: ${{ steps.semver.outputs.semantic_version }}"</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- How It Works Section -->
|
||||
<section class="py-12 sm:py-16 md:py-20 bg-gray-50 dark:bg-gray-800 theme-transition">
|
||||
<div class="max-w-6xl mx-auto px-4 sm:px-6">
|
||||
<div class="text-center mb-8 sm:mb-12">
|
||||
<h2 class="text-2xl sm:text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100 mb-3 sm:mb-4">How It Works</h2>
|
||||
<p class="text-base sm:text-lg text-gray-600 dark:text-gray-300 px-4">Version calculation based on commit messages</p>
|
||||
</div>
|
||||
<div class="max-w-3xl mx-auto">
|
||||
<div class="glass p-6 rounded-xl">
|
||||
<pre class="text-gray-700 dark:text-gray-300 text-sm overflow-x-auto"><code>0.0.1 - PATCH - starting commit
|
||||
0.0.2 - PATCH - another commit
|
||||
0.0.4 - PATCH - commit with 'Update' => DOUBLE increment PATCH
|
||||
0.1.0 - MINOR - commit with 'Change' => increment MINOR, reset PATCH
|
||||
0.1.1 - PATCH - additional commit
|
||||
1.0.1 - MAJOR - commit with 'BREAKING' => INCREMENT MAJOR, reset MINOR
|
||||
1.0.2 - PATCH - another commit</code></pre>
|
||||
</div>
|
||||
<div class="mt-6 grid sm:grid-cols-3 gap-4 text-center">
|
||||
<div class="glass p-4 rounded-xl">
|
||||
<div class="text-3xl font-bold text-red-500 mb-2">MAJOR</div>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">Breaking changes</p>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-500 mt-1">Keywords: "breaking"</p>
|
||||
</div>
|
||||
<div class="glass p-4 rounded-xl">
|
||||
<div class="text-3xl font-bold text-yellow-500 mb-2">MINOR</div>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">New features</p>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-500 mt-1">Keywords: "change", "improve"</p>
|
||||
</div>
|
||||
<div class="glass p-4 rounded-xl">
|
||||
<div class="text-3xl font-bold text-emerald-500 mb-2">PATCH</div>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">Bug fixes</p>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-500 mt-1">Keywords: "update", "initial"</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Configuration Section -->
|
||||
<section id="configuration" class="py-12 sm:py-16 md:py-20 bg-white dark:bg-gray-900 theme-transition">
|
||||
<div class="max-w-6xl mx-auto px-4 sm:px-6">
|
||||
<div class="text-center mb-8 sm:mb-12">
|
||||
<h2 class="text-2xl sm:text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100 mb-3 sm:mb-4">Configuration</h2>
|
||||
<p class="text-base sm:text-lg text-gray-600 dark:text-gray-300 px-4">Customize keywords and behavior</p>
|
||||
</div>
|
||||
<div class="max-w-3xl mx-auto">
|
||||
<div class="glass p-6 rounded-xl">
|
||||
<h3 class="font-semibold text-gray-900 dark:text-gray-100 mb-4">semver.yaml</h3>
|
||||
<pre class="bg-gray-900 text-gray-100 p-4 rounded-lg overflow-x-auto text-sm"><code><span class="text-gray-400">version:</span> 1
|
||||
|
||||
<span class="text-gray-400"># Starting version (optional)</span>
|
||||
<span class="text-emerald-400">force:</span>
|
||||
<span class="text-blue-400">major:</span> 1
|
||||
<span class="text-blue-400">minor:</span> 0
|
||||
<span class="text-blue-400">patch:</span> 0
|
||||
<span class="text-blue-400">commit:</span> 69fbe2df696f40281b9104ff073d26186cde1024
|
||||
|
||||
<span class="text-gray-400"># Commits to ignore</span>
|
||||
<span class="text-emerald-400">blacklist:</span>
|
||||
- "Merge branch"
|
||||
- "Merge pull request"
|
||||
- "feature/"
|
||||
|
||||
<span class="text-gray-400"># Keywords for version bumps</span>
|
||||
<span class="text-emerald-400">wording:</span>
|
||||
<span class="text-blue-400">patch:</span>
|
||||
- update
|
||||
- initial
|
||||
- fix
|
||||
<span class="text-blue-400">minor:</span>
|
||||
- change
|
||||
- improve
|
||||
- add
|
||||
<span class="text-blue-400">major:</span>
|
||||
- breaking
|
||||
<span class="text-blue-400">release:</span>
|
||||
- release-candidate
|
||||
- add-rc</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="py-8 bg-gray-100 dark:bg-gray-800 theme-transition">
|
||||
<div class="max-w-6xl mx-auto px-4 sm:px-6">
|
||||
<div class="flex flex-col sm:flex-row justify-between items-center gap-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<i class="fas fa-code-branch text-xl gradient-text"></i>
|
||||
<span class="font-semibold gradient-text">semver-generator</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-6">
|
||||
<a href="https://github.com/lukaszraczylo/semver-generator" class="text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100">
|
||||
<i class="fab fa-github text-xl"></i>
|
||||
</a>
|
||||
<a href="https://github.com/lukaszraczylo/semver-generator/issues" class="text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 text-sm">
|
||||
Issues
|
||||
</a>
|
||||
<a href="https://github.com/lukaszraczylo/semver-generator/releases" class="text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 text-sm">
|
||||
Releases
|
||||
</a>
|
||||
</div>
|
||||
<p class="text-gray-500 dark:text-gray-400 text-sm">MIT License</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
// Theme toggle
|
||||
document.getElementById('theme-toggle').addEventListener('click', function() {
|
||||
if (document.documentElement.classList.contains('dark')) {
|
||||
document.documentElement.classList.remove('dark');
|
||||
localStorage.theme = 'light';
|
||||
} else {
|
||||
document.documentElement.classList.add('dark');
|
||||
localStorage.theme = 'dark';
|
||||
}
|
||||
});
|
||||
|
||||
// Mobile menu toggle
|
||||
document.getElementById('mobile-menu-toggle').addEventListener('click', function() {
|
||||
const menu = document.getElementById('mobile-menu');
|
||||
const openIcon = document.getElementById('menu-open-icon');
|
||||
const closeIcon = document.getElementById('menu-close-icon');
|
||||
|
||||
menu.classList.toggle('hidden');
|
||||
openIcon.classList.toggle('hidden');
|
||||
closeIcon.classList.toggle('hidden');
|
||||
});
|
||||
|
||||
// Close mobile menu when clicking a link
|
||||
document.querySelectorAll('#mobile-menu a').forEach(link => {
|
||||
link.addEventListener('click', () => {
|
||||
document.getElementById('mobile-menu').classList.add('hidden');
|
||||
document.getElementById('menu-open-icon').classList.remove('hidden');
|
||||
document.getElementById('menu-close-icon').classList.add('hidden');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+2
-2
@@ -60,11 +60,11 @@ if [[ ! -z "$INPUT_DEBUGMODE" ]]; then
|
||||
echo "----"
|
||||
echo "FLAGS: $FLAGS"
|
||||
echo "----"
|
||||
/go/src/app/semver-gen generate $FLAGS $*
|
||||
/go/src/app/semver-generator generate $FLAGS $*
|
||||
echo "----"
|
||||
fi
|
||||
|
||||
OUT_SEMVER_GEN=$(/go/src/app/semver-gen generate $FLAGS $*)
|
||||
OUT_SEMVER_GEN=$(/go/src/app/semver-generator generate $FLAGS $*)
|
||||
[ $? -eq 0 ] || exit 1
|
||||
CLEAN_SEMVER=$(echo $OUT_SEMVER_GEN | sed -e 's|SEMVER ||g')
|
||||
echo "semantic_version=$CLEAN_SEMVER" >> $GITHUB_OUTPUT
|
||||
|
||||
@@ -1,58 +1,48 @@
|
||||
module github.com/lukaszraczylo/semver-generator
|
||||
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.23.6
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
github.com/go-git/go-git/v5 v5.13.2
|
||||
github.com/go-git/go-git/v5 v5.19.1
|
||||
github.com/lithammer/fuzzysearch v1.1.8
|
||||
github.com/lukaszraczylo/ask v0.0.0-20240916204100-6e9ef53a62d9
|
||||
github.com/lukaszraczylo/go-simple-graphql v1.2.42
|
||||
github.com/lukaszraczylo/graphql-monitoring-proxy v0.25.154
|
||||
github.com/lukaszraczylo/graphql-monitoring-proxy v0.45.1
|
||||
github.com/lukaszraczylo/oss-telemetry v0.2.1
|
||||
github.com/lukaszraczylo/pandati v0.0.29
|
||||
github.com/melbahja/got v0.7.0
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/spf13/viper v1.19.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/spf13/viper v1.21.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
)
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.1 // indirect
|
||||
dario.cat/mergo v1.0.2 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.1.5 // indirect
|
||||
github.com/avast/retry-go/v4 v4.6.1 // indirect
|
||||
github.com/cloudflare/circl v1.6.0 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.4.1 // indirect
|
||||
github.com/cloudflare/circl v1.6.3 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.6.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.10.1 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.6.2 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.9.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
|
||||
github.com/goccy/go-json v0.10.6 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/gookit/color v1.5.4 // indirect
|
||||
github.com/gookit/goutil v0.6.18 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/magiconair/properties v1.8.9 // indirect
|
||||
github.com/kevinburke/ssh_config v1.6.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.3.1 // indirect
|
||||
github.com/pjbgf/sha1cd v0.6.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/rs/zerolog v1.33.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.7.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||
github.com/skeema/knownhosts v1.3.1 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.12.0 // indirect
|
||||
github.com/spf13/cast v1.7.1 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/sagikazarmark/locafero v0.12.0 // indirect
|
||||
github.com/sergi/go-diff v1.4.0 // indirect
|
||||
github.com/skeema/knownhosts v1.3.2 // indirect
|
||||
github.com/spf13/afero v1.15.0 // indirect
|
||||
github.com/spf13/cast v1.10.0 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/tidwall/gjson v1.18.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
@@ -60,16 +50,11 @@ require (
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
github.com/wI2L/jsondiff v0.6.1 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/crypto v0.35.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa // indirect
|
||||
golang.org/x/net v0.35.0 // indirect
|
||||
golang.org/x/sync v0.11.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/term v0.29.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/crypto v0.51.0 // indirect
|
||||
golang.org/x/net v0.54.0 // indirect
|
||||
golang.org/x/sys v0.44.0 // indirect
|
||||
golang.org/x/text v0.37.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -1,67 +1,61 @@
|
||||
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
||||
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4=
|
||||
github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||
github.com/ProtonMail/go-crypto v1.4.1 h1:9RfcZHqEQUvP8RzecWEUafnZVtEvrBVL9BiF67IQOfM=
|
||||
github.com/ProtonMail/go-crypto v1.4.1/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/avast/retry-go/v4 v4.6.1 h1:VkOLRubHdisGrHnTu89g08aQEWEgRU7LVEop3GbIcMk=
|
||||
github.com/avast/retry-go/v4 v4.6.1/go.mod h1:V6oF8njAwxJ5gRo1Q7Cxab24xs5NCWZBeaHHBklR8mA=
|
||||
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
|
||||
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||
github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk=
|
||||
github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
|
||||
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
|
||||
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||
github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE=
|
||||
github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/elazarl/goproxy v1.4.0 h1:4GyuSbFa+s26+3rmYNSuUVsx+HgPrV1bk1jXI0l9wjM=
|
||||
github.com/elazarl/goproxy v1.4.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ=
|
||||
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
||||
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
|
||||
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho=
|
||||
github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo=
|
||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||
github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
|
||||
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
|
||||
github.com/go-git/go-billy/v5 v5.9.0 h1:jItGXszUDRtR/AlferWPTMN4j38BQ88XnXKbilmmBPA=
|
||||
github.com/go-git/go-billy/v5 v5.9.0/go.mod h1:jCnQMLj9eUgGU7+ludSTYoZL/GGmii14RxKFj7ROgHw=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0=
|
||||
github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/goccy/go-reflect v1.2.0 h1:O0T8rZCuNmGXewnATuKYnkL0xm6o8UNOJZd/gOkb9ms=
|
||||
github.com/goccy/go-reflect v1.2.0/go.mod h1:n0oYZn8VcV2CkWTxi8B9QjkCoq6GTtCEdfmR66YhFtE=
|
||||
github.com/go-git/go-git/v5 v5.19.1 h1:nX27AnaU43/K5bKktKwgBmR9lawoYVe1Ckg0rgzzN00=
|
||||
github.com/go-git/go-git/v5 v5.19.1/go.mod h1:Pb1v0c7/g8aGQJwx9Us09W85yGoyvSwuhEGMH7zjDKQ=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
|
||||
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
|
||||
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
|
||||
github.com/gookit/goutil v0.6.18 h1:MUVj0G16flubWT8zYVicIuisUiHdgirPAkmnfD2kKgw=
|
||||
github.com/gookit/goutil v0.6.18/go.mod h1:AY/5sAwKe7Xck+mEbuxj0n/bc3qwrGNe3Oeulln7zBA=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
||||
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kevinburke/ssh_config v1.6.0 h1:J1FBfmuVosPHf5GRdltRLhPJtJpTlMdKTBjRgTaQBFY=
|
||||
github.com/kevinburke/ssh_config v1.6.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
@@ -71,16 +65,12 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4=
|
||||
github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4=
|
||||
github.com/lukaszraczylo/ask v0.0.0-20240916204100-6e9ef53a62d9 h1:pL8B9mjv6RPUfKYYGm/uJ7QL6Ndf+z+OEl0qJE6KmEc=
|
||||
github.com/lukaszraczylo/ask v0.0.0-20240916204100-6e9ef53a62d9/go.mod h1:M+UVdyqZs++xtEPrascaVmZdOMhCnxjZ2SgH+xHpR0c=
|
||||
github.com/lukaszraczylo/go-simple-graphql v1.2.42 h1:u27/Z6gC+vHLeNwsQ2UIy75LgW80p2wJtAAQrNNfft4=
|
||||
github.com/lukaszraczylo/go-simple-graphql v1.2.42/go.mod h1:TATbOjdO26pt1S681Sm88nvKEZds3dqInIVtjvi5y40=
|
||||
github.com/lukaszraczylo/graphql-monitoring-proxy v0.25.154 h1:+InKbNRfeViwcEj3J+Coz7fnt6LJjhunsphwrkxwO3Y=
|
||||
github.com/lukaszraczylo/graphql-monitoring-proxy v0.25.154/go.mod h1:dFlfc6jT0L2tIhjBq5irRqP4jsYrq3W8nzpf7KTFx+E=
|
||||
github.com/lukaszraczylo/graphql-monitoring-proxy v0.45.1 h1:NBRubpm7P+Z0iOgqPFAM6HHVPnUs8Qm1BZqpJBKL81Q=
|
||||
github.com/lukaszraczylo/graphql-monitoring-proxy v0.45.1/go.mod h1:oL4NfPtHhKcHUUzQpfAqMLUkQDLwkwqzAZFfyhZW3+0=
|
||||
github.com/lukaszraczylo/oss-telemetry v0.2.1 h1:6ULyfzXplpdmIY/i01OPM1jeod9+L1RAhI0jtbVnJI0=
|
||||
github.com/lukaszraczylo/oss-telemetry v0.2.1/go.mod h1:+Cn78qZo8rc3T9eZt0v3oICYRdd75wORtSidc8lNjDQ=
|
||||
github.com/lukaszraczylo/pandati v0.0.29 h1:WUEWm1+hWjE5KJbIL8OctG00x2dk4XKGJSlrjhxZ55k=
|
||||
github.com/lukaszraczylo/pandati v0.0.29/go.mod h1:+DyTWKFaXd+jIfe7GW5w2S5PyTko/RXxMyOa+Vl713A=
|
||||
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
|
||||
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
@@ -88,53 +78,46 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/melbahja/got v0.7.0 h1:YHbiuNZVS8fIkyV0iXyThQQliwlKZb5h4k80zBVovxg=
|
||||
github.com/melbahja/got v0.7.0/go.mod h1:27cUstWCEfj6HBESMTGzCFY24Qj+QNMWot3+KuxguQU=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
|
||||
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
|
||||
github.com/pelletier/go-toml/v2 v2.3.1 h1:MYEvvGnQjeNkRF1qUuGolNtNExTDwct51yp7olPtrEc=
|
||||
github.com/pelletier/go-toml/v2 v2.3.1/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pjbgf/sha1cd v0.6.0 h1:3WJ8Wz8gvDz29quX1OcEmkAlUg9diU4GxJHqs0/XiwU=
|
||||
github.com/pjbgf/sha1cd v0.6.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
|
||||
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4=
|
||||
github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI=
|
||||
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
|
||||
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
|
||||
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
|
||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
|
||||
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
|
||||
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
||||
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
||||
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
||||
github.com/skeema/knownhosts v1.3.2 h1:EDL9mgf4NzwMXCTfaxSD/o/a5fxDw/xL9nkU28JjdBg=
|
||||
github.com/skeema/knownhosts v1.3.2/go.mod h1:bEg3iQAuw+jyiw+484wwFJoKSLwcfd7fqRy+N0QTiow=
|
||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
|
||||
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
@@ -151,18 +134,16 @@ github.com/wI2L/jsondiff v0.6.1 h1:ISZb9oNWbP64LHnu4AUhsMF5W0FIj5Ok3Krip9Shqpw=
|
||||
github.com/wI2L/jsondiff v0.6.1/go.mod h1:KAEIojdQq66oJiHhDyQez2x+sRit0vIzC9KeK0yizxM=
|
||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
|
||||
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
|
||||
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa h1:t2QcU6V556bFjYgu4L6C+6VrCPyJZ+eyRsABUPs1mz4=
|
||||
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk=
|
||||
golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI=
|
||||
golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8=
|
||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM=
|
||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@@ -170,13 +151,11 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||
golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
|
||||
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -190,21 +169,21 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
|
||||
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
|
||||
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
||||
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
|
||||
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
|
||||
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
@@ -214,8 +193,6 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
||||
@@ -15,13 +15,21 @@ limitations under the License.
|
||||
*/
|
||||
package main
|
||||
|
||||
import "github.com/lukaszraczylo/semver-generator/cmd"
|
||||
import (
|
||||
"time"
|
||||
|
||||
telemetry "github.com/lukaszraczylo/oss-telemetry"
|
||||
"github.com/lukaszraczylo/semver-generator/cmd"
|
||||
)
|
||||
|
||||
var (
|
||||
PKG_VERSION string
|
||||
)
|
||||
|
||||
func main() {
|
||||
telemetry.SendForModule("semver-generator", "github.com/lukaszraczylo/semver-generator", PKG_VERSION)
|
||||
defer telemetry.Wait(2 * time.Second)
|
||||
|
||||
cmd.PKG_VERSION = PKG_VERSION
|
||||
cmd.Execute()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user