Skip to content

Commit

Permalink
Initial version
Browse files Browse the repository at this point in the history
  • Loading branch information
kduma committed Nov 12, 2024
0 parents commit 0926801
Show file tree
Hide file tree
Showing 40 changed files with 9,497 additions and 0 deletions.
15 changes: 15 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true

[*.md]
trim_trailing_whitespace = false

[*.yml]
indent_size = 2
4 changes: 4 additions & 0 deletions .git-flow-tool.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"versionProvider": "flat-file?filename=VERSION.txt",
"gitFlow": "branch[develop]=develop&branch[master]=master&prefix[feature]=feature/&prefix[release]=release/&prefix[hotfix]=hotfix/&prefix[support]=support/&prefix[versionTag]=v&suffix[versionTag]=-src"
}
6 changes: 6 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
* text=auto eol=lf
/.github export-ignore
.scrutinizer.yml export-ignore
BACKERS.md export-ignore
CONTRIBUTING.md export-ignore
CHANGELOG.md export-ignore
95 changes: 95 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
name: Build PHAR and Release

on:
push:
branches: [ develop ]
tags: [ 'v*.*.*-src', 'v*.*-src', 'v*-src' ]
pull_request:
branches: [ master, develop ]
workflow_dispatch:

jobs:
build-phar:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'

- name: Validate composer.json and composer.lock
run: composer validate --strict --no-check-all

- name: Cache Composer packages
id: composer-cache
uses: actions/cache@v4
with:
path: vendor
key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-php-
- name: Install dependencies
run: composer install --prefer-dist --no-progress --no-dev

# Add a test script to composer.json, for instance: "test": "vendor/bin/phpunit"
# Docs: https://getcomposer.org/doc/articles/scripts.md

- name: Get the tag name
if: startsWith(github.ref, 'refs/tags/')
run: echo "TAG=`cat VERSION.txt`" >> $GITHUB_ENV

- name: Build PHAR archive
if: startsWith(github.ref, 'refs/tags/')
run: php git-flow-tool app:build --build-version=${{ env.TAG }}

- name: Build PHAR archive
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
run: php git-flow-tool app:build --build-version=$(git rev-parse --short "$GITHUB_SHA")

- name: copy phar to temp
run: cp builds/git-flow-tool /tmp/git-flow-tool.phar

- name: Release source
uses: softprops/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/')
with:
files: /tmp/git-flow-tool.phar
tag_name: v${{ env.TAG }}-src
name: git-flow-tool v${{ env.TAG }} release
generate_release_notes: true

- name: Upload a Build Artifact
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
uses: actions/upload-artifact@v4
with:
name: binary
path: /tmp/git-flow-tool.phar
retention-days: 7

- uses: actions/checkout@v4
if: startsWith(github.ref, 'refs/tags/')
with:
ref: release

- name: copy phar from temp
if: startsWith(github.ref, 'refs/tags/')
run: mkdir -p builds; rm -f builds/git-flow-tool; cp /tmp/git-flow-tool.phar builds/git-flow-tool

- name: commit update
uses: EndBug/add-and-commit@v9
if: startsWith(github.ref, 'refs/tags/')
with:
message: 'Binary release of version v${{ env.TAG }}'
tag: 'v${{ env.TAG }} --force'
tag_push: '--force'

# - name: Release
# uses: softprops/action-gh-release@v2
# if: startsWith(github.ref, 'refs/tags/')
# with:
# tag_name: v${{ env.TAG }}
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/vendor
/.idea
/.vscode
/.vagrant
.phpunit.result.cache
/test-repo
.DS_Store
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# GitFlow Tool
Useful helper for using gitflow-like branching model in a project.
It is a simple shell script that helps you to create, finish and publish branches in a git repository.
It supports automatically bumping version tag and pushing from and to correct branches.

## Installation

in your project install the tool by running:
```bash
composer require kduma/git-flow-tool
```
and then use it by running:
```bash
vendor/bin/git-flow-tool
```

## Configuration
In root directory of your project create `.git-flow-tool.json` file with following content:
```json
{
"versionProvider": "php-array?filename=config/app.php&key=version",
"gitFlow": "branch[develop]=develop&branch[master]=main&prefix[feature]=feature/&prefix[release]=release/&prefix[hotfix]=hotfix/&prefix[support]=support/&prefix[versionTag]=v&suffix[versionTag]=-src",
"git": "author[name]=BOT&author[email]=bot@localhost"
}
```
Update it to match your project configuration.

## Usage
```bash
vendor/bin/git-flow-tool release
```

or

```bash
vendor/bin/git-flow-tool hotfix
```
1 change: 1 addition & 0 deletions VERSION.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.0.12
207 changes: 207 additions & 0 deletions app/Commands/AbstractGitFlowCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
<?php

namespace App\Commands;

use App\Repository\Config\AppConfig;
use App\Repository\ConfigProvider;
use App\Repository\Git\GitRepository;
use App\Repository\VersionProviders\VersionUpdaterService;
use Gitonomy\Git\Repository;
use Illuminate\Console\ConfirmableTrait;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Str;
use LaravelZero\Framework\Commands\Command;

use function Laravel\Prompts\clear;
use function Laravel\Prompts\confirm;
use function Laravel\Prompts\error;
use function Laravel\Prompts\spin;

abstract class AbstractGitFlowCommand extends Command
{
use ConfirmableTrait;

public function handle(ConfigProvider $configProvider, VersionUpdaterService $versionUpdaterService): int
{
$config = $configProvider->load(getcwd());
$repository = new GitRepository(new Repository($config->workingDirectory), $config);


if (! $this->confirmToProceed()) {
return 0;
}

$branch = $repository->getCurrentBranchName();
if ($branch === $this->getStartingBranch($config)) {
clear();
if ($repository->isDirty()) {
error('Working Directory is in dirty state!');

return 1;
}

if (! $this->option('start') && ! confirm('Do you want to start new '.$this->getKeyword($config).'?', true)) {
return 0;
}

return $this->startNewRelease($config, $repository, $versionUpdaterService);
}

if (Str::startsWith($branch, $this->getBranchPrefix($config))) {
$version = Str::after($branch, $this->getBranchPrefix($config));
clear();
if (! $this->option('finish') && ! confirm('Do you want to finish v'.$version.' '.$this->getKeyword($config).'?', false)) {
return 0;
}

return $this->finishRelease($branch, $version, $config, $repository);
}

if (in_array($branch, $this->getDestinationBranches($config))) {
clear();
if ($repository->isDirty()) {
error('Working Directory is in dirty state!');

return 1;
}

if (! confirm('You need to start from '.$this->getStartingBranch($config).' branch. Do you want to switch?', true)) {
return 0;
}

$repository->checkout($this->getStartingBranch($config));

clear();
if (! $this->option('start') && ! confirm('Do you want to start new '.$this->getKeyword($config).'?', true)) {
return 0;
}

return $this->startNewRelease($config, $repository, $versionUpdaterService);
}

clear();
error(sprintf('Don\'t know what to do with %s branch!', $branch));

return 1;
}


protected function startNewRelease(AppConfig $config, GitRepository $repository, VersionUpdaterService $versionUpdaterService): int
{
spin(
function () use ($repository, &$isUpToDate) {
try {
$isUpToDate = $repository->isUpToDate();
} catch (\RuntimeException $e) {
$isUpToDate = true;
}
},
'Checking if Working Directory is up to date'
);

if (!$isUpToDate) {
clear();
error('Working Directory is not up to date!');

return 2;
}

$version_part_to_increment = match ($this->option('type')) {
'pre-release' => 'pre-release',
default => 'patch',
'minor' => 'minor',
'major' => 'major',
};

try {
$updater = $versionUpdaterService->load($config);
$updater->increment($version_part_to_increment);
} catch (\Exception|\RuntimeException $e) {
clear();
error('Cannot get next version number!');

return 3;
}

$version = trim($updater->get());

$branch = $this->getBranchPrefix($config).$version;

if (collect($repository->getBranches())->filter(fn ($v) => $v === $branch)->count() !== 0) {
clear();
error(ucfirst($this->getKeyword($config)).' branch for this version number already exists!');

return 4;
}

$repository->checkoutNew($branch);

try {
$updater->save();
} catch (\Exception|\RuntimeException $e) {
clear();
error('Cannot bump version number!');

return 4;
}

$repository->commitAs('Bump app version to v'.$version);

clear();
if (! $this->option('finish') && ! confirm('Do you want to finish v'.$version.' '.$this->getKeyword($config).' ?', false)) {
return 0;
}

return $this->finishRelease($branch, $version, $config, $repository);
}

protected function finishRelease(string $branch, string $version, AppConfig $config, GitRepository $repository): int
{
if ($repository->isDirty()) {
clear();
$this->line($repository->repository->run('status'));

if (! confirm('Working Directory is in dirty state. Do you want to commit it?', false)) {
return 0;
}

if (! ($message = $this->ask('Commit message'))) {
return 0;
}

$repository->commit($message);
}

$tag_name = sprintf('%s%s%s', $config->gitFlow->versionTagPrefix, $version, $config->gitFlow->versionTagSuffix);
$repository->tag($tag_name);

foreach ($this->getDestinationBranches($config) as $destination) {
spin(
function () use ($repository, $destination, $branch, $config) {
$repository->checkout($destination);
$repository->mergeAs($branch);
$repository->push($destination, $config->git->remoteName);
},
'Merging '.$branch.' into '.$destination.' branch'
);

}

spin(
fn () => $repository->pushTag($tag_name, $config->git->remoteName),
'Pushing tag '.$tag_name
);

$repository->deleteBranch($branch);

return 0;
}

abstract protected function getDestinationBranches(AppConfig $config): array;

abstract protected function getStartingBranch(AppConfig $config): string;

abstract protected function getBranchPrefix(AppConfig $config): string;

abstract protected function getKeyword(AppConfig $config): string;
}
Loading

0 comments on commit 0926801

Please sign in to comment.