mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-08 08:12:17 +01:00
Compare commits
7 Commits
feature/fi
...
feature/ap
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8dba13eba | ||
|
|
91d6ff7392 | ||
|
|
427c904747 | ||
|
|
861b6c2642 | ||
|
|
51f7ba0509 | ||
|
|
e0506fa3e3 | ||
|
|
a9d9c13846 |
47
.github/ISSUE_TEMPLATE/1_bug_report.yml
vendored
47
.github/ISSUE_TEMPLATE/1_bug_report.yml
vendored
@@ -1,47 +0,0 @@
|
||||
name: Bug Report
|
||||
description: "Report a bug"
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Before creating a new bug report, please check that there isn't already a similar issue.
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
description: A clear and concise description of what the bug is.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Steps To Reproduce"
|
||||
description: How do you trigger this bug? Please walk us through it step by step.
|
||||
value: |
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
...
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: "Self-hosted or Cloud?"
|
||||
options:
|
||||
- Self-Hosted
|
||||
- solidtime Cloud
|
||||
- Both
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: "Version of solidtime: (for self-hosted)"
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: "solidtime self-hosting guide: (for self-hosted)"
|
||||
description: "Did you use the official guide to self-host solidtime? If yes, which one?"
|
||||
validations:
|
||||
required: false
|
||||
8
.github/ISSUE_TEMPLATE/config.yml
vendored
8
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,8 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 🚀 Feature Request
|
||||
url: https://github.com/solidtime-io/solidtime/discussions/new?category=feature-requests
|
||||
about: Share ideas for new features
|
||||
- name: ❓ Ask a Question
|
||||
url: https://github.com/solidtime-io/solidtime/discussions/new?category=general
|
||||
about: Ask the community for help
|
||||
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,8 +0,0 @@
|
||||
<!--
|
||||
This project is early stage. The structure and APIs are still subject to change and not stable.
|
||||
Therefore, we do not currently accept any contributions, unless you are a member of the team.
|
||||
|
||||
As soon as we feel comfortable enough that the application structure is stable enough, we will open up the project for contributions.
|
||||
|
||||
We do accept contributions in the [documentation repository](https://github.com/solidtime-io/docs) f.e. to add new self-hosting guides.
|
||||
-->
|
||||
143
.github/workflows/build-public.yml
vendored
143
.github/workflows/build-public.yml
vendored
@@ -11,21 +11,10 @@ on:
|
||||
- 'docker/prod/**'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
DOCKERHUB_REPO: solidtime/solidtime
|
||||
GHCR_REPO: ghcr.io/solidtime-io/solidtime
|
||||
|
||||
name: Build - Public
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- runs-on: "ubuntu-24.04-arm"
|
||||
platform: "linux/arm64"
|
||||
- runs-on: "ubuntu-24.04"
|
||||
platform: "linux/amd64"
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
@@ -40,7 +29,7 @@ jobs:
|
||||
fetch-depth: 0 # Required for WyriHaximus/github-action-get-previous-tag
|
||||
|
||||
- name: "Get build"
|
||||
id: release-build
|
||||
id: build
|
||||
run: echo "build=$(git rev-parse --short=8 HEAD)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: "Get Previous tag (normal push)"
|
||||
@@ -51,7 +40,7 @@ jobs:
|
||||
prefix: "v"
|
||||
|
||||
- name: "Get version"
|
||||
id: release-version
|
||||
id: version
|
||||
run: |
|
||||
if ${{ !startsWith(github.ref, 'refs/tags/v') }}; then
|
||||
if ${{ startsWith(steps.previoustag.outputs.tag, 'v') }}; then
|
||||
@@ -72,23 +61,21 @@ jobs:
|
||||
rm .env.production .env.ci .env.example
|
||||
|
||||
- name: "Add version to .env"
|
||||
run: sed -i 's/APP_VERSION=0.0.0/APP_VERSION=${{ steps.release-version.outputs.app_version }}/g' .env
|
||||
run: sed -i 's/APP_VERSION=0.0.0/APP_VERSION=${{ steps.version.outputs.app_version }}/g' .env
|
||||
|
||||
- name: "Add build to .env"
|
||||
run: sed -i 's/APP_BUILD=0/APP_BUILD=${{ steps.release-build.outputs.build }}/g' .env
|
||||
run: sed -i 's/APP_BUILD=0/APP_BUILD=${{ steps.build.outputs.build }}/g' .env
|
||||
|
||||
- name: "Output .env"
|
||||
run: cat .env
|
||||
|
||||
- name: "Setup PHP with PECL extension"
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: '8.3'
|
||||
extensions: mbstring, dom, fileinfo, pgsql
|
||||
|
||||
- name: "Install dependencies"
|
||||
run: composer install --no-dev --no-ansi --no-interaction --prefer-dist --ignore-platform-reqs --classmap-authoritative
|
||||
uses: php-actions/composer@v6
|
||||
if: steps.cache-vendor.outputs.cache-hit != 'true' # Skip if cache hit
|
||||
with:
|
||||
command: install
|
||||
only_args: --no-dev --no-ansi --no-interaction --prefer-dist --ignore-platform-reqs --classmap-authoritative
|
||||
php_version: 8.3
|
||||
|
||||
- name: "Use Node.js"
|
||||
uses: actions/setup-node@v4
|
||||
@@ -101,31 +88,29 @@ jobs:
|
||||
- name: "Build"
|
||||
run: npm run build
|
||||
|
||||
- name: "Prepare"
|
||||
run: |
|
||||
platform=${{ matrix.platform }}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
|
||||
- name: "Docker meta"
|
||||
id: "meta"
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
${{ env.DOCKERHUB_REPO }}
|
||||
${{ env.GHCR_REPO }}
|
||||
|
||||
- name: "Login to Docker Hub Container Registry"
|
||||
- name: "Login to GitHub Container Registry"
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: "Login to GitHub Container Registry"
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: "Docker meta"
|
||||
id: "meta"
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
solidtime/solidtime
|
||||
ghcr.io/${{ github.repository }}
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
|
||||
- name: "Set up QEMU"
|
||||
uses: docker/setup-qemu-action@v3
|
||||
@@ -133,90 +118,16 @@ jobs:
|
||||
- name: "Set up Docker Buildx"
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: "Build and push by digest"
|
||||
id: build
|
||||
- name: "Build and push"
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: docker/prod/Dockerfile
|
||||
build-args: |
|
||||
DOCKER_FILES_BASE_PATH=docker/prod/
|
||||
platforms: ${{ matrix.platform }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
outputs: type=image,"name=${{ env.DOCKERHUB_REPO }},${{ env.GHCR_REPO }}",push-by-digest=true,name-canonical=true,push=true
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: "Export digest"
|
||||
run: |
|
||||
mkdir -p ${{ runner.temp }}/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
||||
|
||||
- name: "Upload digest"
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: digests-${{ env.PLATFORM_PAIR }}
|
||||
path: ${{ runner.temp }}/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
merge:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
attestations: write
|
||||
id-token: write
|
||||
timeout-minutes: 90
|
||||
needs:
|
||||
- build
|
||||
steps:
|
||||
- name: "Download digests"
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ${{ runner.temp }}/digests
|
||||
pattern: digests-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: "Login to Docker Hub"
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: "Login to GHCR"
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: "Set up Docker Buildx"
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: "Docker meta"
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
${{ env.DOCKERHUB_REPO }}
|
||||
${{ env.GHCR_REPO }}
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
|
||||
- name: "Create manifest list and push"
|
||||
working-directory: ${{ runner.temp }}/digests
|
||||
run: |
|
||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||
$(printf '${{ env.DOCKERHUB_REPO }}@sha256:%s ' *)
|
||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||
$(printf '${{ env.GHCR_REPO }}@sha256:%s ' *)
|
||||
|
||||
- name: "Inspect image"
|
||||
run: |
|
||||
docker buildx imagetools inspect ${{ env.DOCKERHUB_REPO }}:${{ steps.meta.outputs.version }}
|
||||
docker buildx imagetools inspect ${{ env.GHCR_REPO }}:${{ steps.meta.outputs.version }}
|
||||
|
||||
@@ -5,11 +5,8 @@ declare(strict_types=1);
|
||||
namespace App\Service\Import\Importers;
|
||||
|
||||
use App\Enums\Role;
|
||||
use App\Service\TimezoneService;
|
||||
use Exception;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Str;
|
||||
use Override;
|
||||
use Spatie\TemporaryDirectory\TemporaryDirectory;
|
||||
use ValueError;
|
||||
@@ -96,22 +93,11 @@ class TogglDataImporter extends DefaultImporter
|
||||
}
|
||||
|
||||
foreach ($workspaceUsers as $workspaceUser) {
|
||||
$timezone = Str::trim($workspaceUser->timezone);
|
||||
if ($timezone === '') {
|
||||
$timezone = 'UTC';
|
||||
}
|
||||
if (! app(TimezoneService::class)->isValid($timezone)) {
|
||||
Log::warning('TogglDateImporter: Invalid timezone', [
|
||||
'timezone' => $timezone,
|
||||
]);
|
||||
$timezone = 'UTC';
|
||||
}
|
||||
|
||||
$userId = $this->userImportHelper->getKey([
|
||||
'email' => $workspaceUser->email,
|
||||
], [
|
||||
'name' => $workspaceUser->name,
|
||||
'timezone' => $timezone,
|
||||
'timezone' => $workspaceUser->timezone ?? 'UTC',
|
||||
'is_placeholder' => true,
|
||||
], (string) $workspaceUser->uid);
|
||||
$this->memberImportHelper->getKey([
|
||||
|
||||
@@ -15,7 +15,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'guard' => 'web',
|
||||
'guard' => 'api',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
||||
@@ -182,7 +182,7 @@ COPY --chown=${USER}:${USER} ${DOCKER_FILES_BASE_PATH}deployment/php.ini /lib/ph
|
||||
# && composer clear-cache
|
||||
|
||||
RUN cat .env
|
||||
#RUN php artisan env
|
||||
RUN php artisan env
|
||||
RUN php artisan storage:link
|
||||
|
||||
RUN chmod +x /usr/local/bin/start-container
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
[
|
||||
{
|
||||
"archived": false,
|
||||
"creator_id": 201,
|
||||
"id": 301,
|
||||
"name": "Big Company",
|
||||
"wid": 0
|
||||
},
|
||||
{
|
||||
"archived": true,
|
||||
"creator_id": 201,
|
||||
"id": 302,
|
||||
"name": "Other Company (Archived)",
|
||||
"wid": 0
|
||||
}
|
||||
]
|
||||
@@ -1,86 +0,0 @@
|
||||
[
|
||||
{
|
||||
"active": true,
|
||||
"actual_hours": null,
|
||||
"actual_seconds": null,
|
||||
"auto_estimates": false,
|
||||
"billable": false,
|
||||
"cid": null,
|
||||
"client_id": null,
|
||||
"color": "#ef5350",
|
||||
"currency": "EUR",
|
||||
"estimated_hours": null,
|
||||
"estimated_seconds": null,
|
||||
"fixed_fee": null,
|
||||
"guid": "",
|
||||
"id": 401,
|
||||
"is_private": true,
|
||||
"name": "Project without Client",
|
||||
"rate": null,
|
||||
"rate_last_updated": null,
|
||||
"recurring": false,
|
||||
"recurring_parameters": null,
|
||||
"start_date": "2020-01-01",
|
||||
"status": "active",
|
||||
"template": false,
|
||||
"template_id": null,
|
||||
"wid": 0,
|
||||
"workspace_id": 0
|
||||
},
|
||||
{
|
||||
"active": true,
|
||||
"actual_hours": null,
|
||||
"actual_seconds": null,
|
||||
"auto_estimates": false,
|
||||
"billable": true,
|
||||
"cid": 301,
|
||||
"client_id": 301,
|
||||
"color": "#ec407a",
|
||||
"currency": null,
|
||||
"estimated_hours": null,
|
||||
"estimated_seconds": null,
|
||||
"fixed_fee": null,
|
||||
"guid": "",
|
||||
"id": 402,
|
||||
"is_private": true,
|
||||
"name": "Project for Big Company",
|
||||
"rate": 100.01,
|
||||
"rate_last_updated": null,
|
||||
"recurring": false,
|
||||
"recurring_parameters": null,
|
||||
"start_date": "2020-01-01",
|
||||
"status": "active",
|
||||
"template": false,
|
||||
"template_id": null,
|
||||
"wid": 0,
|
||||
"workspace_id": 0
|
||||
},
|
||||
{
|
||||
"active": false,
|
||||
"actual_hours": null,
|
||||
"actual_seconds": null,
|
||||
"auto_estimates": false,
|
||||
"billable": true,
|
||||
"cid": 302,
|
||||
"client_id": 302,
|
||||
"color": "#6a407f",
|
||||
"currency": null,
|
||||
"estimated_hours": null,
|
||||
"estimated_seconds": null,
|
||||
"fixed_fee": null,
|
||||
"guid": "",
|
||||
"id": 403,
|
||||
"is_private": false,
|
||||
"name": "Project (Archived)",
|
||||
"rate": null,
|
||||
"rate_last_updated": null,
|
||||
"recurring": false,
|
||||
"recurring_parameters": null,
|
||||
"start_date": "2020-01-01",
|
||||
"status": "active",
|
||||
"template": false,
|
||||
"template_id": null,
|
||||
"wid": 0,
|
||||
"workspace_id": 0
|
||||
}
|
||||
]
|
||||
@@ -1 +0,0 @@
|
||||
[]
|
||||
@@ -1,14 +0,0 @@
|
||||
[
|
||||
{
|
||||
"gid": null,
|
||||
"group_id": null,
|
||||
"id": 801,
|
||||
"labour_cost": null,
|
||||
"manager": true,
|
||||
"project_id": 402,
|
||||
"rate": 100.02,
|
||||
"rate_last_updated": null,
|
||||
"user_id": 2001,
|
||||
"workspace_id": 0
|
||||
}
|
||||
]
|
||||
@@ -1 +0,0 @@
|
||||
[]
|
||||
@@ -1,14 +0,0 @@
|
||||
[
|
||||
{
|
||||
"creator_id": 0,
|
||||
"id": 501,
|
||||
"name": "Development",
|
||||
"workspace_id": 0
|
||||
},
|
||||
{
|
||||
"creator_id": 0,
|
||||
"id": 502,
|
||||
"name": "Backend",
|
||||
"workspace_id": 0
|
||||
}
|
||||
]
|
||||
@@ -1 +0,0 @@
|
||||
[]
|
||||
@@ -1,24 +0,0 @@
|
||||
[
|
||||
{
|
||||
"active": true,
|
||||
"estimated_seconds": 0,
|
||||
"id": 601,
|
||||
"name": "Task 1",
|
||||
"project_id": 402,
|
||||
"recurring": false,
|
||||
"tracked_seconds": 0,
|
||||
"user_id": null,
|
||||
"workspace_id": 0
|
||||
},
|
||||
{
|
||||
"active": false,
|
||||
"estimated_seconds": 0,
|
||||
"id": 602,
|
||||
"name": "Task 2",
|
||||
"project_id": 403,
|
||||
"recurring": false,
|
||||
"tracked_seconds": 0,
|
||||
"user_id": null,
|
||||
"workspace_id": 0
|
||||
}
|
||||
]
|
||||
@@ -1 +0,0 @@
|
||||
[]
|
||||
@@ -1,19 +0,0 @@
|
||||
[
|
||||
{
|
||||
"active": true,
|
||||
"admin": true,
|
||||
"email": "peter.test@email.test",
|
||||
"group_ids": [],
|
||||
"id": 201,
|
||||
"inactive": false,
|
||||
"labour_cost": null,
|
||||
"name": "Peter Tester",
|
||||
"rate": null,
|
||||
"rate_last_updated": null,
|
||||
"role": "admin",
|
||||
"timezone": "Etc/UTC",
|
||||
"uid": 2001,
|
||||
"wid": 0,
|
||||
"working_hours_in_minutes": null
|
||||
}
|
||||
]
|
||||
@@ -5,7 +5,6 @@ declare(strict_types=1);
|
||||
namespace Tests\Unit\Service\Import\Importers;
|
||||
|
||||
use App\Models\Organization;
|
||||
use App\Models\User;
|
||||
use App\Service\Import\Importers\DefaultImporter;
|
||||
use App\Service\Import\Importers\ImportException;
|
||||
use App\Service\Import\Importers\TogglDataImporter;
|
||||
@@ -89,30 +88,4 @@ class TogglDataImporterTest extends ImporterTestAbstract
|
||||
$this->assertSame(0, $report->projectsCreated);
|
||||
$this->assertSame(0, $report->clientsCreated);
|
||||
}
|
||||
|
||||
public function test_import_of_user_with_unknown_timezone_will_be_mapped_to_utc(): void
|
||||
{
|
||||
// Arrange
|
||||
$zipPath = $this->createTestZip('toggl_data_import_test_2');
|
||||
$timezone = 'Europe/Vienna';
|
||||
$organization = Organization::factory()->create();
|
||||
$importer = new TogglDataImporter;
|
||||
$importer->init($organization);
|
||||
$data = file_get_contents($zipPath);
|
||||
|
||||
// Act
|
||||
$importer->importData($data, $timezone);
|
||||
$report = $importer->getReport();
|
||||
|
||||
// Assert
|
||||
$this->assertSame(0, $report->timeEntriesCreated);
|
||||
$this->assertSame(2, $report->tagsCreated);
|
||||
$this->assertSame(2, $report->tasksCreated);
|
||||
$this->assertSame(1, $report->usersCreated);
|
||||
$this->assertSame(3, $report->projectsCreated);
|
||||
$this->assertSame(2, $report->clientsCreated);
|
||||
$user = User::query()->where('email', '=', 'peter.test@email.test')->first();
|
||||
$this->assertSame('UTC', $user->timezone);
|
||||
$this->assertTrue($user->is_placeholder);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user