Skip to content

Commit

Permalink
Merge pull request #17695 from opf/merge-release/15.2-20250123033524
Browse files Browse the repository at this point in the history
Merge release/15.2 into dev
  • Loading branch information
dombesz authored Jan 23, 2025
2 parents 99a30ab + cfd4973 commit 549747b
Show file tree
Hide file tree
Showing 8 changed files with 211 additions and 75 deletions.
24 changes: 24 additions & 0 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,16 @@ jobs:
type=semver,pattern={{version}},value=${{ needs.setup.outputs.version }}
images: |
${{ env.REGISTRY_IMAGE }}
- name: Restore vendor/bundle
id: restore-vendor-bundle
uses: actions/cache/restore@v4
with:
path: |
vendor/bundle
key: ${{ matrix.platform }}-vendor-bundle-${{ github.ref }}
- name: Include vendor/bundle in this build (so we can use it from the cache above)
run: |
sed -i 's/vendor\/bundle//g' .dockerignore
- name: Build image
id: build
uses: docker/build-push-action@v6
Expand All @@ -194,12 +204,26 @@ jobs:
target: ${{ matrix.target }}
build-args: |
BIM_SUPPORT=${{ matrix.bim_support }}
BUILDKIT_PROGRESS=plain
pull: true
load: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=s3,blobs_prefix=cache/${{ github.repository }}/,manifests_prefix=cache/${{ github.repository }}/,region=${{ env.RUNS_ON_AWS_REGION }},bucket=${{ env.RUNS_ON_S3_BUCKET_CACHE }}
cache-to: type=s3,blobs_prefix=cache/${{ github.repository }}/,manifests_prefix=cache/${{ github.repository }}/,region=${{ env.RUNS_ON_AWS_REGION }},bucket=${{ env.RUNS_ON_S3_BUCKET_CACHE }},mode=max
- name: Extract vendor/bundle from container
run: |
docker create --name bundle ${{ steps.build.outputs.imageid }}
rm -rf vendor/bundle || true
docker cp bundle:/app/vendor/bundle vendor/bundle
docker rm bundle
- name: Save vendor/bundle
id: save-vendor-bundle
uses: actions/cache/save@v4
with:
path: |
vendor/bundle
key: ${{ steps.restore-vendor-bundle.outputs.cache-primary-key }}
- name: Test
# We only test the native container. If that fails the builds for the others
# will be cancelled as well.
Expand Down
11 changes: 2 additions & 9 deletions docker/prod/Dockerfile
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,8 @@ COPY vendor ./vendor
# Add lib in case a plugin tries to load VERSION file under lib
COPY lib ./lib

RUN \
bundle config set --local path 'vendor/bundle' && \
bundle config set --local without 'test development' && \
bundle install --quiet --no-cache --jobs=8 --retry=3 && \
bundle config set deployment 'true' && \
cp Gemfile.lock Gemfile.lock.bak && \
rm -rf vendor/bundle/ruby/*/cache && \
rm -rf vendor/bundle/ruby/*/gems/*/spec && \
rm -rf vendor/bundle/ruby/*/gems/*/test
COPY ./docker/prod/setup/bundle-install.sh ./vendor/bundle* ./vendor/
RUN bash vendor/bundle-install.sh && rm vendor/bundle-install.sh

COPY . .

Expand Down
18 changes: 18 additions & 0 deletions docker/prod/setup/bundle-install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/bash

set -e

# temporary 'seed' for working ppc64 cached vendor/bundle
if [ ! -d vendor/bundle ] && [ -n "$(uname -a | grep ppc64)" ]; then
wget https://openproject-public.s3.eu-central-1.amazonaws.com/ruby/bundle/openproject-release-15.2-836aec00b1-vendor-bundle.tar.gz
tar -xf openproject-release-15.2-836aec00b1-vendor-bundle.tar.gz
fi

bundle config set --local path 'vendor/bundle'
bundle config set --local without 'test development'
bundle install --jobs=8 --retry=3
bundle config set deployment 'true'
cp Gemfile.lock Gemfile.lock.bak
rm -rf vendor/bundle/ruby/*/cache
rm -rf vendor/bundle/ruby/*/gems/*/spec
rm -rf vendor/bundle/ruby/*/gems/*/test
27 changes: 23 additions & 4 deletions docs/release-notes/15-2-0/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ title: OpenProject 15.2.0
sidebar_navigation:
title: 15.2.0
release_version: 15.2.0
release_date: 2024-12-23
release_date: 2025-01-22
---

# OpenProject 15.2.0

Release date: 2024-12-23
Release date: 2025-01-22

We released [OpenProject 15.2.0](https://community.openproject.org/versions/2143). The release contains several bug fixes, and we recommend updating to the newest version.
In these Release Notes, we will give an overview of important feature changes. At the end, you will find a complete list of all changes and bug fixes.
Expand Down Expand Up @@ -90,16 +90,35 @@ We continued introducing standardized components for page headers and subheaders
- Feature: When adding new relations, auto-scroll to show the newly added relation \[[#59769](https://community.openproject.org/wp/59769)\]
- Feature: Create new icon for setting reminders \[[#59793](https://community.openproject.org/wp/59793)\]
- Feature: Export cost query as timesheet PDF \[[#59824](https://community.openproject.org/wp/59824)\]
- Feature: Generate PDF document from a work package description \[[#45896](https://community.openproject.org/wp/45896)\]
- Feature: Improve comprehensibility of the "Add relations" modal \[[#60462](https://community.openproject.org/wp/60462)\]
- Bugfix: Mobile: tap twice on comment input to start typing \[[#57107](https://community.openproject.org/wp/57107)\]
- Bugfix: Activity panel (Notification center goes into mobile layout even on a desktop when the browser on desktop \[[#59235](https://community.openproject.org/wp/59235)\]
- Bugfix: Folders missing in log lines for "Unexpected Content Error" \[[#59346](https://community.openproject.org/wp/59346)\]
- Bugfix: Activity tab does not scroll to the bottom or correct comment on mobile \[[#59458](https://community.openproject.org/wp/59458)\]
- Bugfix: Activity only shows 1 value even if several were updated \[[#59855](https://community.openproject.org/wp/59855)\]
- Bugfix: User Hovercard rendering is bumpy \[[#59879](https://community.openproject.org/wp/59879)\]
- Bugfix: User hovercard is showing on User own profile page and other users profile page \[[#59898](https://community.openproject.org/wp/59898)\]
- Bugfix: User hovercard is showing behind the Meeting participants overlay \[[#59907](https://community.openproject.org/wp/59907)\]
- Bugfix: Impossible to group by CF hierarchy multi-select \[[#59920](https://community.openproject.org/wp/59920)\]
- Bugfix: Proto plugin dose not work with OpenProject 15 \[[#59931](https://community.openproject.org/wp/59931)\]
- Bugfix: Project membership in group administration lists global roles \[[#59934](https://community.openproject.org/wp/59934)\]
- Bugfix: Internal server error on Azure OIDC login with expired client secret \[[#59960](https://community.openproject.org/wp/59960)\]
- Bugfix: OpenID setup fails due to Claims is not a valid JSON object \[[#59962](https://community.openproject.org/wp/59962)\]
- Bugfix: Wrong locale of error message while personal API access token creation \[[#60025](https://community.openproject.org/wp/60025)\]
- Bugfix: Filter translations for Meetings Module missing \[[#60113](https://community.openproject.org/wp/60113)\]
- Bugfix: New WP form doesn't open in split view when on the WP list page \[[#60274](https://community.openproject.org/wp/60274)\]
- Bugfix: Saving a new WP triggers browser warning message \[[#60133](https://community.openproject.org/wp/60133)\]
- Bugfix: Meeting creator is not added as invitee by default + All meetings menu point should not have a filter by default \[[#60287](https://community.openproject.org/wp/60287)\]
- Bugfix: Titles do not always use App Title \[[#60371](https://community.openproject.org/wp/60371)\]
- Bugfix: No connection error after folder creation in file picker \[[#60384](https://community.openproject.org/wp/60384)\]
- Bugfix: Non working days not shown on calendar widget \[[#60410](https://community.openproject.org/wp/60410)\]
- Bugfix: Distance between sticky buttons and form field above is too small in scrollable modals \[[#60430](https://community.openproject.org/wp/60430)\]
- Bugfix: Reminders generate two or more lines for a work package in notification center, clicking one selects all \[[#60449](https://community.openproject.org/wp/60449)\]
- Bugfix: The 'set reminder' action is available to users who are not signed in \[[#60458](https://community.openproject.org/wp/60458)\]
- Bugfix: ActionView::Template::Error happening in MessagesController#show \[[#60478](https://community.openproject.org/wp/60478)\]
- Bugfix: Reminder menu item is not placed correctly \[[#60501](https://community.openproject.org/wp/60501)\]
- Bugfix: Vanishing filters in "Work package table"-widgets \[[#60546](https://community.openproject.org/wp/60546)\]
- Bugfix: Remove permission restriction for users to set their own reminders \[[#60568](https://community.openproject.org/wp/60568)\]
- Bugfix: Relations dropdown capitalizes wrong in German \[[#60627](https://community.openproject.org/wp/60627)\]

<!-- END AUTOMATED SECTION -->
<!-- Warning: Anything above this line will be automatically removed by the release script -->
Expand Down
7 changes: 7 additions & 0 deletions docs/release-notes/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ Stay up to date and get an overview of the new features included in the releases
<!--- New release notes are generated below. Do not remove comment. -->
<!--- RELEASE MARKER -->

## 15.2.0

Release date: 2025-01-22

[Release Notes](15-2-0/)


## 15.1.1

Release date: 2025-01-13
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ import {
ViewContainerRef,
} from '@angular/core';
import { DropdownPosition, NgSelectComponent } from '@ng-select/ng-select';
import { BehaviorSubject, merge, NEVER, Observable, of, Subject, timer } from 'rxjs';
import { debounce, distinctUntilChanged, filter, map, switchMap, tap } from 'rxjs/operators';
import { BehaviorSubject, merge, NEVER, Observable, of, Subject } from 'rxjs';
import { debounceTime, distinctUntilChanged, filter, map, switchMap, tap } from 'rxjs/operators';
import { AddTagFn, GroupValueFn } from '@ng-select/ng-select/lib/ng-select.component';

import { HalResource } from 'core-app/features/hal/resources/hal-resource';
Expand Down Expand Up @@ -235,6 +235,8 @@ export class OpAutocompleterComponent<T extends IAutocompleteItem = IAutocomplet

@Input() public relations?:boolean = false;

@Input() public debounceTimeMs:number = 250;

@Output() public open = new EventEmitter<unknown>();

@Output() public close = new EventEmitter<unknown>();
Expand Down Expand Up @@ -291,8 +293,6 @@ export class OpAutocompleterComponent<T extends IAutocompleteItem = IAutocomplet

footerTemplate:TemplateRef<Element>;

initialDebounce = true;

readonly opAutocompleterService = new OpAutocompleterService(this.apiV3Service);

constructor(
Expand Down Expand Up @@ -477,7 +477,7 @@ export class OpAutocompleterComponent<T extends IAutocompleteItem = IAutocomplet
filter(() => !!(this.defaultData || this.getOptionsFn)),
distinctUntilChanged(),
tap(() => this.loading$.next(true)),
debounce(() => timer(this.getDebounceTimeout())),
debounceTime(this.debounceTimeForCurrentEnvironment),
switchMap((queryString:string) => {
if (this.relations && this.url) {
return this.fetchFromUrl(queryString);
Expand Down Expand Up @@ -533,12 +533,8 @@ export class OpAutocompleterComponent<T extends IAutocompleteItem = IAutocomplet
);
}

private getDebounceTimeout():number {
if (this.initialDebounce) {
this.initialDebounce = false;
return 0;
}
return 50;
private get debounceTimeForCurrentEnvironment():number {
return (window.OpenProject.environment === 'test') ? 0 : this.debounceTimeMs;
}

writeValue(value:T|T[]|null):void {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { States } from 'core-app/core/states/states.service';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
import { of } from 'rxjs';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { of, map } from 'rxjs';
import { NgSelectModule } from '@ng-select/ng-select';

import { By } from '@angular/platform-browser';
import { OpAutocompleterService } from './services/op-autocompleter.service';
import { OpAutocompleterComponent } from './op-autocompleter.component';
import { By } from '@angular/platform-browser';

describe('autocompleter', () => {
let fixture:ComponentFixture<OpAutocompleterComponent>;
let opAutocompleterServiceSpy:jasmine.SpyObj<OpAutocompleterService>;
let getOptionsFnSpy: jasmine.Spy;
const workPackagesStub = [
{
id: 1,
Expand Down Expand Up @@ -51,26 +51,20 @@ describe('autocompleter', () => {
];

beforeEach(() => {
opAutocompleterServiceSpy = jasmine.createSpyObj('OpAutocompleterService', ['loadData']);

TestBed.configureTestingModule({
declarations: [
OpAutocompleterComponent],
providers: [
// { provide: OpAutocompleterService, useValue: opAutocompleterServiceSpyFactory }
],
declarations: [OpAutocompleterComponent],
providers: [States],
imports: [HttpClientTestingModule, NgSelectModule],
schemas: [NO_ERRORS_SCHEMA],
})
.overrideComponent(
OpAutocompleterComponent,
{ set: { providers: [{ provide: OpAutocompleterService, useValue: opAutocompleterServiceSpy }] } },
)
.compileComponents();
}).compileComponents();

fixture = TestBed.createComponent(OpAutocompleterComponent);
getOptionsFnSpy = jasmine.createSpy("getOptionsFn").and.callFake((searchTerm:string) => {
return of(workPackagesStub).pipe(
map((wps) => wps.filter((wp) => searchTerm !== "" && wp.subject.includes(searchTerm)))
)
});

// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
fixture.componentInstance.resource = 'work_packages' as TOpAutocompleterResource;
fixture.componentInstance.filters = [];
fixture.componentInstance.searchKey = 'subjectOrId';
Expand All @@ -79,10 +73,8 @@ describe('autocompleter', () => {
fixture.componentInstance.closeOnSelect = true;
fixture.componentInstance.virtualScroll = true;
fixture.componentInstance.classes = 'wp-relations-autocomplete';
fixture.componentInstance.defaultData = true;

// @ts-ignore
opAutocompleterServiceSpy.loadData.and.returnValue(of(workPackagesStub));
fixture.componentInstance.getOptionsFn = getOptionsFnSpy;
fixture.componentInstance.debounceTimeMs = 0;
});

it('should load the ng-select correctly', () => {
Expand All @@ -93,27 +85,84 @@ describe('autocompleter', () => {
});
});

it('should load WorkPackages', fakeAsync(() => {
tick();
fixture.detectChanges();
fixture.componentInstance.ngAfterViewInit();
tick(1000);
fixture.detectChanges();
const select = fixture.componentInstance.ngSelectInstance;
expect(fixture.componentInstance.ngSelectInstance.isOpen).toBeFalse();
fixture.componentInstance.ngSelectInstance.open();
fixture.componentInstance.ngSelectInstance.focus();
expect(fixture.componentInstance.ngSelectInstance.isOpen).toBeTrue();
select.filter('a');
describe('without debounce', () => {
it('should load items', fakeAsync(() => {
tick();
fixture.detectChanges();
fixture.componentInstance.ngAfterViewInit();
tick(1000);
fixture.detectChanges();
const select = fixture.componentInstance.ngSelectInstance;
expect(select.isOpen).toBeFalse();
select.open();
select.focus();
expect(select.isOpen).toBeTrue();

fixture.detectChanges();
tick(1000);
fixture.detectChanges();
tick(1000);
expect(select.itemsList.items.length).toEqual(0);

const inputDebugElement = fixture.debugElement.query(By.css('input[role=combobox]'));
const inputElement = inputDebugElement.nativeElement as HTMLInputElement;

expect(opAutocompleterServiceSpy.loadData).toHaveBeenCalledWith('a',
fixture.componentInstance.resource, fixture.componentInstance.filters, fixture.componentInstance.searchKey);
fixture.detectChanges();
tick();
expect(getOptionsFnSpy).toHaveBeenCalledWith("");

expect(fixture.componentInstance.ngSelectInstance.itemsList.items.length).toEqual(2);
}));
inputElement.value = "Wor";
inputElement.dispatchEvent(new Event('input'));
fixture.detectChanges();
tick();
expect(getOptionsFnSpy).toHaveBeenCalledWith("Wor");

fixture.detectChanges();
expect(select.itemsList.items.length).toEqual(2);

inputElement.value = "package 2";
inputElement.dispatchEvent(new Event('input'));
fixture.detectChanges();
tick();
expect(getOptionsFnSpy).toHaveBeenCalledWith("package 2");

fixture.detectChanges();
expect(select.itemsList.items.length).toEqual(1);
}));
});

describe('with debounce', () => {
beforeEach(() => {
fixture.componentInstance.debounceTimeMs = 50;
});

it('should load items with debounce', fakeAsync(() => {
tick();
fixture.detectChanges();
fixture.componentInstance.ngAfterViewInit();
tick(1000);
fixture.detectChanges();
const select = fixture.componentInstance.ngSelectInstance;
expect(select.isOpen).toBeFalse();
select.open();
select.focus();
expect(select.isOpen).toBeTrue();

expect(select.itemsList.items.length).toEqual(0);

const inputDebugElement = fixture.debugElement.query(By.css('input[role=combobox]'));
const inputElement = inputDebugElement.nativeElement as HTMLInputElement;

fixture.detectChanges();
tick();
expect(getOptionsFnSpy).not.toHaveBeenCalled();
tick(50);
expect(getOptionsFnSpy).toHaveBeenCalledWith("");
getOptionsFnSpy.calls.reset();

inputElement.value = "Wor";
inputElement.dispatchEvent(new Event('input'));
fixture.detectChanges();
tick();
expect(getOptionsFnSpy).not.toHaveBeenCalled();
tick(50);
expect(getOptionsFnSpy).toHaveBeenCalledWith("Wor");
}));
});
});
Loading

0 comments on commit 549747b

Please sign in to comment.