Jenkinsfile & translations

This commit is contained in:
cghislai 2018-08-05 19:33:03 +03:00
parent 820b80f196
commit 525bc4a63d
24 changed files with 523 additions and 22 deletions

103
Jenkinsfile vendored Normal file
View File

@ -0,0 +1,103 @@
pipeline {
agent any
options {
disableConcurrentBuilds()
buildDiscarder(logRotator(numToKeepStr: '10'))
}
parameters {
string(
name: 'CONF_PREFIX', defaultValue: 'production-',
description: 'Will be appended with the language'
)
string(
name: 'LANGUAGES', defaultValue: 'en fr',
description: 'Space-separated list of locales to build'
)
booleanParam(name: 'SKIP_TESTS', defaultValue: true, description: 'Skip tests')
string(
name: 'PUBLISH_URL', defaultValue: 'https://nexus.valuya.be/nexus/repository',
description: 'Deployment repository url'
)
string(
name: 'PUBLISH_REPO', defaultValue: 'web-snapshots',
description: 'Deployment repository'
)
}
stages {
stage ('Install') {
steps {
nodejs(nodeJSInstallationName: 'node 10', configId: 'npm-global-config') {
ansiColor('xterm') {
sh '''
rm -rfv dist*
npm install
'''
}
}
}
}
stage ('Build') {
steps {
withCredentials([usernameColonPassword(credentialsId: 'nexus-basic-auth', variable: 'NEXUS_BASIC_AUTH')]) {
nodejs(nodeJSInstallationName: 'node 10', configId: 'npm-global-config') { catchError {
ansiColor('xterm') {
sh '''
export DATE="$(date -Iseconds)"
export COMMIT="$(git rev-parse --short HEAD)"
echo '{"date":"'$DATE'","commit": "'$COMMIT'"}' > ./src/assets/buildinfo.json
for LANG in $LANGUAGES ; do
CONF_NAME="${CONF_PREFIX}${LANG}"
./node_modules/.bin/ng build \
-c "$CONF_NAME"
done
'''
}
}}}
}
}
stage ('Publish') {
steps {
withCredentials([usernameColonPassword(credentialsId: 'nexus-basic-auth', variable: 'NEXUS_BASIC_AUTH')]) {
ansiColor('xterm') {
nodejs(nodeJSInstallationName: 'node 10', configId: 'npm-global-config') {
sh '''
export DATE="$(date -Iseconds)"
export COMMIT="$(git rev-parse --short HEAD)"
echo '{"date":"'$DATE'","commit": "'$COMMIT'"}' > ./src/assets/buildinfo.json
for LANG in $LANGUAGES ; do
export ARCHIVE="charlyghislaindotcom-${COMMIT}-$LANG.tgz"
# Compress
cd dist/${LANG}/
tar -cvzf ../${ARCHIVE} ./
cd ../..
# Upload archives
curl -v --user $NEXUS_BASIC_AUTH --upload-file dist/${ARCHIVE} \
${PUBLISH_URL}/${PUBLISH_REPO}/com/charlyghislain/charlyghislaindotcom/${ARCHIVE}
# Create .latest 'links' (branch heads) if required
if [ "${BRANCH_NAME}" = "master" ] ; then
export ARCHIVE_LINK="master.${LANG}.latest"
echo "$ARCHIVE" > ./${ARCHIVE_LINK}
curl -v --user $NEXUS_BASIC_AUTH --upload-file ./${ARCHIVE_LINK} \
${PUBLISH_URL}/${PUBLISH_REPO}/com/charlyghislain/charlyghislaindotcom/${ARCHIVE_LINK}
elif [ "${BRANCH_NAME}" = "dev" ] ; then
export ARCHIVE_LINK="dev.${LANG}.latest"
echo "$ARCHIVE" > ./${ARCHIVE_LINK}
curl -v --user $NEXUS_BASIC_AUTH --upload-file ./${ARCHIVE_LINK} \
${PUBLISH_URL}/${PUBLISH_REPO}/com/charlyghislain/charlyghislaindotcom/${ARCHIVE_LINK}
fi
done
'''
}
}
}
}
}
}
}

View File

@ -13,22 +13,30 @@
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/charlyghislaindotcom",
"outputPath": "dist/en",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.app.json",
"i18nLocale": "en",
"assets": [
"src/favicon.ico",
"src/assets"
],
"aot": true,
"styles": [
"src/styles.scss"
],
"scripts": []
},
"configurations": {
"production": {
"fr": {
"outputPath": "dist/fr/",
"i18nFile": "src/locale/messages.fr.xlf",
"i18nFormat": "xlf",
"i18nLocale": "fr"
},
"production-en": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
@ -43,7 +51,32 @@
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true
"buildOptimizer": true,
"outputPath": "dist/en/",
"i18nLocale": "en",
"baseHref": "/en/"
},
"production-fr": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"outputPath": "dist/fr/",
"i18nFile": "src/locale/messages.fr.xlf",
"i18nFormat": "xlf",
"i18nLocale": "fr",
"baseHref": "/fr/"
}
}
},
@ -53,8 +86,14 @@
"browserTarget": "charlyghislaindotcom:build"
},
"configurations": {
"fr": {
"browserTarget": "charlyghislaindotcom:build:fr"
},
"production": {
"browserTarget": "charlyghislaindotcom:build:production"
"browserTarget": "charlyghislaindotcom:build:production-en"
},
"production-fr": {
"browserTarget": "charlyghislaindotcom:build:production-fr"
}
}
},

13
package-lock.json generated
View File

@ -6499,6 +6499,19 @@
"minimist": "0.0.8"
}
},
"moment": {
"version": "2.22.2",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz",
"integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y="
},
"moment-es6": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/moment-es6/-/moment-es6-1.0.0.tgz",
"integrity": "sha1-VS/PQF1iVlsKH+hObB5peseTMt8=",
"requires": {
"moment": "*"
}
},
"move-concurrently": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",

View File

@ -21,6 +21,7 @@
"@angular/platform-browser-dynamic": "^6.0.3",
"@angular/router": "^6.0.3",
"core-js": "^2.5.4",
"moment-es6": "^1.0.0",
"rxjs": "^6.0.0",
"zone.js": "^0.8.26"
},

0
src/.htaccess Normal file
View File

View File

@ -3,3 +3,7 @@
<div class="content">
<router-outlet></router-outlet>
</div>
<div class="build-info">
<span class="date">{{buildDate | async}}</span>
<span class="commit">{{buildCommit| async}}</span>
</div>

View File

@ -1,3 +1,18 @@
.content {
margin: 2em;
}
.build-info {
position: absolute;
bottom: 0;
width: 100%;
opacity: .4;
font-size: xx-small;
text-align: right;
>* {
display: inline-block;
margin: .1em;
}
}

View File

@ -1,9 +1,42 @@
import {Component} from '@angular/core';
import {Component, OnInit} from '@angular/core';
import {BuildInfoService} from './build-info.service';
import {Observable, of} from 'rxjs';
import {catchError, defaultIfEmpty, filter, map, publishReplay, refCount} from 'rxjs/operators';
import {BuildInfo} from './build-info';
import moment from 'moment-es6';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent {
export class AppComponent implements OnInit {
buildDate: Observable<string>;
buildCommit: Observable<string>;
constructor(private buildinfoService: BuildInfoService) {
const buildInfo = this.buildinfoService.getBuildInfo()
.pipe(
catchError(e => of(<BuildInfo>{date: null, commit: null})),
publishReplay(1), refCount(),
);
this.buildDate = buildInfo.pipe(
map(info => info.date),
map(date => this.parseDate(date)),
);
this.buildCommit = buildInfo.pipe(
map(info => info.commit),
filter(commit => commit != null),
defaultIfEmpty('HEAD'),
);
}
ngOnInit(): void {
}
private parseDate(dateString: string) {
const dateMoment = dateString == null ? moment() : moment(dateString);
return dateMoment.format('DD/MM/YYYY HH:mm:ss');
}
}

View File

@ -7,10 +7,19 @@ import {ContactRouteComponent} from './contact-route/contact-route.component';
import {ServiceRouteComponent} from './service-route/service-route.component';
import {AppRoutingModule} from './app.routing-module';
import {RoutesHeaderComponent} from './routes-header/routes-header.component';
import {CommonModule, registerLocaleData} from '@angular/common';
import localeFr from '@angular/common/locales/fr';
import {HttpClientModule} from '@angular/common/http';
// the second parameter 'fr' is optional
registerLocaleData(localeFr, 'fr');
@NgModule({
imports: [
BrowserModule,
CommonModule,
HttpClientModule,
AppRoutingModule,
],
declarations: [

View File

@ -23,7 +23,7 @@ export const ROUTES: Route[] = [
component: ServiceRouteComponent,
},
{
path: '*',
path: '**',
redirectTo: '/info',
},
];

View File

@ -0,0 +1,15 @@
import { TestBed, inject } from '@angular/core/testing';
import { BuildInfoService } from './build-info.service';
describe('BuildInfoService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [BuildInfoService]
});
});
it('should be created', inject([BuildInfoService], (service: BuildInfoService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -0,0 +1,22 @@
import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {BuildInfo} from './build-info';
import {Observable} from 'rxjs';
import {publishReplay, refCount} from 'rxjs/operators';
@Injectable({
providedIn: 'root',
})
export class BuildInfoService {
private buildInfo: Observable<BuildInfo>;
constructor(private http: HttpClient) {
this.buildInfo = this.http.get<BuildInfo>('./assets/buildinfo.json')
.pipe(publishReplay(1), refCount());
}
getBuildInfo(): Observable<BuildInfo> {
return this.buildInfo;
}
}

4
src/app/build-info.ts Normal file
View File

@ -0,0 +1,4 @@
export interface BuildInfo {
date: string;
commit: string;
}

View File

@ -1,4 +1,4 @@
<p>
<p i18n>
You can reach me using the following channels
</p>
<ul>
@ -13,7 +13,7 @@
</a>
</li>
</ul>
<p>
<p i18n>
Bank information
</p>
<ul>

View File

@ -1,7 +1,7 @@
<p>
<p i18n>
This is the homepage of Charles Ghislain
</p>
<p>
<p i18n>
I'm a full stack developer familiar with the following technologies
</p>
<ul>
@ -21,11 +21,13 @@
</a>
</li>
</ul>
<p>
<p i18n>
You may find some of my work online under the cghislai nickname
</p>
<ul>
<li>
<a href="https://github.com/cghislai">github</a>
<a href="https://github.com/cghislai">
GitHub
</a>
</li>
</ul>

View File

@ -2,27 +2,43 @@
<span class="menu">
<a [routerLink]="['/info']"
routerLinkActive="active">
routerLinkActive="active"
i18n>
Info
</a>
<a [routerLink]="['/contact']"
routerLinkActive="active">
routerLinkActive="active"
i18n>
Contact
</a>
<a [routerLink]="['/services']"
routerLinkActive="active">
routerLinkActive="active"
i18n>
Services
</a>
</span>
<span class="lang">
<a class="fr" *ngIf="locale != 'fr'"
href="/fr"
title="Français">
<img src="assets/flags/fr.png">
</a>
<a class="gb" *ngIf="locale != 'en'"
href="/en"
title="English">
<img src="assets/flags/gb.png">
</a>
</span>
<a class="title"
href="https://www.charlyghislain.com">
<span class="text">
charlyghislain.com
</span>
<img src="../../assets/favicon.png"
<img src="assets/favicon.png"
class="icon">
</a>

View File

@ -39,6 +39,35 @@ header {
}
}
> .lang {
> a {
display: inline-block;
width: 20px;
height: 16px;
text-align: center;
position: relative;
> img {
width: 20px;
height: 16px;
transition: all ease-in .2s;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
}
&:hover {
img {
width: 30px;
height: 24px;
left: -5px;
top: -4px;
}
}
}
}
> .menu {
flex: 1 1 auto;
display: inline-flex;

View File

@ -1,13 +1,14 @@
import { Component, OnInit } from '@angular/core';
import {Component, Inject, LOCALE_ID, OnInit} from '@angular/core';
@Component({
selector: 'app-routes-header',
templateUrl: './routes-header.component.html',
styleUrls: ['./routes-header.component.scss']
styleUrls: ['./routes-header.component.scss'],
})
export class RoutesHeaderComponent implements OnInit {
constructor() { }
constructor(@Inject(LOCALE_ID) public locale: string) {
}
ngOnInit() {
}

View File

@ -1,4 +1,4 @@
<p>
<p i18n>
You may be looking for one of these services
</p>

View File

@ -0,0 +1,4 @@
{
"date": null,
"commit": null
}

BIN
src/assets/flags/fr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 B

BIN
src/assets/flags/gb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

103
src/locale/messages.fr.xlf Normal file
View File

@ -0,0 +1,103 @@
<?xml version='1.0' encoding='UTF-8'?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file target-language="en-US" original="ng2.template" source-language="en" datatype="plaintext">
<header>
<phase-group>
<phase process-name="approval" contact-email="charlyghislain@gmail.com" tool-id="lokalize-2.0" contact-name="Charly Ghislain" date="2018-08-05" phase-name="approval-1"/>
</phase-group>
<tool tool-id="lokalize-2.0" tool-version="2.0" tool-name="Lokalize"/>
</header>
<body>
<trans-unit id="827decfea4d4653cfdebe96d59c539d14ce0a055" approved="yes" datatype="html">
<source>
This is the homepage of Charles Ghislain
</source>
<target state="signed-off" phase-name="approval-1">Ceci est la page d'accueil de Charles Ghislain</target>
<context-group purpose="location">
<context context-type="sourcefile">app/info-route/info-route.component.html</context>
<context context-type="linenumber">1</context>
</context-group>
</trans-unit>
<trans-unit id="1e6c25a7122d23d3daf6861ff35ae8b23b4c62ff" approved="yes" datatype="html">
<source>
I'm a full stack developer familiar with the following technologies
</source>
<target state="signed-off" phase-name="approval-1">Je suis un développeur 'full stack', familier avec les technologies suivantes</target>
<context-group purpose="location">
<context context-type="sourcefile">app/info-route/info-route.component.html</context>
<context context-type="linenumber">4</context>
</context-group>
</trans-unit>
<trans-unit id="7489a10839c6c326999deca83ea6cf671a9afa6b" approved="yes" datatype="html">
<source>
You may find some of my work online under the cghislai nickname
</source>
<target state="signed-off" phase-name="approval-1">Vous pouvez trouver une partie de mes contributions en ligne sous le pseudonyme 'cghislai'</target>
<context-group purpose="location">
<context context-type="sourcefile">app/info-route/info-route.component.html</context>
<context context-type="linenumber">24</context>
</context-group>
</trans-unit>
<trans-unit id="5b67ac0ce7f00cd10ee987a40c7f326dbbcb3a95" approved="yes" datatype="html">
<source>
You can reach me using the following channels
</source>
<target state="signed-off" phase-name="approval-1">Vous pouvez me contacter via les canaux suivants</target>
<context-group purpose="location">
<context context-type="sourcefile">app/contact-route/contact-route.component.html</context>
<context context-type="linenumber">1</context>
</context-group>
</trans-unit>
<trans-unit id="36ae0ac6e38404832f035d774c0ebc3054a85a61" approved="yes" datatype="html">
<source>
Bank information
</source>
<target state="signed-off" phase-name="approval-1">Informations banquaires</target>
<context-group purpose="location">
<context context-type="sourcefile">app/contact-route/contact-route.component.html</context>
<context context-type="linenumber">16</context>
</context-group>
</trans-unit>
<trans-unit id="65964526b7f720db57bbcdf845ee3ccf9245eb3b" approved="yes" datatype="html">
<source>
You may be looking for one of these services
</source>
<target state="signed-off" phase-name="approval-1">Peut-être essayez-vous d'atteindre l'un des services suivants</target>
<context-group purpose="location">
<context context-type="sourcefile">app/service-route/service-route.component.html</context>
<context context-type="linenumber">1</context>
</context-group>
</trans-unit>
<trans-unit id="a77b57f81c44560b659e64f2210711944ba283b4" approved="yes" datatype="html">
<source>
Info
</source>
<target state="signed-off" phase-name="approval-1">Info</target>
<context-group purpose="location">
<context context-type="sourcefile">app/routes-header/routes-header.component.html</context>
<context context-type="linenumber">6</context>
</context-group>
</trans-unit>
<trans-unit id="24173a2846d637c26a85f6ed8abfb050031948f0" approved="yes" datatype="html">
<source>
Contact
</source>
<target state="signed-off" phase-name="approval-1">Contact</target>
<context-group purpose="location">
<context context-type="sourcefile">app/routes-header/routes-header.component.html</context>
<context context-type="linenumber">12</context>
</context-group>
</trans-unit>
<trans-unit id="5f6829555b5bd568d8251cc80049c006ff9fdd96" approved="yes" datatype="html">
<source>
Services
</source>
<target state="signed-off" phase-name="approval-1">Services</target>
<context-group purpose="location">
<context context-type="sourcefile">app/routes-header/routes-header.component.html</context>
<context context-type="linenumber">18</context>
</context-group>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="UTF-8" ?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="ng2.template">
<body>
<trans-unit id="827decfea4d4653cfdebe96d59c539d14ce0a055" datatype="html">
<source>
This is the homepage of Charles Ghislain
</source>
<context-group purpose="location">
<context context-type="sourcefile">app/info-route/info-route.component.html</context>
<context context-type="linenumber">1</context>
</context-group>
</trans-unit>
<trans-unit id="1e6c25a7122d23d3daf6861ff35ae8b23b4c62ff" datatype="html">
<source>
I&apos;m a full stack developer familiar with the following technologies
</source>
<context-group purpose="location">
<context context-type="sourcefile">app/info-route/info-route.component.html</context>
<context context-type="linenumber">4</context>
</context-group>
</trans-unit>
<trans-unit id="7489a10839c6c326999deca83ea6cf671a9afa6b" datatype="html">
<source>
You may find some of my work online under the cghislai nickname
</source>
<context-group purpose="location">
<context context-type="sourcefile">app/info-route/info-route.component.html</context>
<context context-type="linenumber">24</context>
</context-group>
</trans-unit>
<trans-unit id="5b67ac0ce7f00cd10ee987a40c7f326dbbcb3a95" datatype="html">
<source>
You can reach me using the following channels
</source>
<context-group purpose="location">
<context context-type="sourcefile">app/contact-route/contact-route.component.html</context>
<context context-type="linenumber">1</context>
</context-group>
</trans-unit>
<trans-unit id="36ae0ac6e38404832f035d774c0ebc3054a85a61" datatype="html">
<source>
Bank information
</source>
<context-group purpose="location">
<context context-type="sourcefile">app/contact-route/contact-route.component.html</context>
<context context-type="linenumber">16</context>
</context-group>
</trans-unit>
<trans-unit id="65964526b7f720db57bbcdf845ee3ccf9245eb3b" datatype="html">
<source>
You may be looking for one of these services
</source>
<context-group purpose="location">
<context context-type="sourcefile">app/service-route/service-route.component.html</context>
<context context-type="linenumber">1</context>
</context-group>
</trans-unit>
<trans-unit id="a77b57f81c44560b659e64f2210711944ba283b4" datatype="html">
<source>
Info
</source>
<context-group purpose="location">
<context context-type="sourcefile">app/routes-header/routes-header.component.html</context>
<context context-type="linenumber">6</context>
</context-group>
</trans-unit>
<trans-unit id="24173a2846d637c26a85f6ed8abfb050031948f0" datatype="html">
<source>
Contact
</source>
<context-group purpose="location">
<context context-type="sourcefile">app/routes-header/routes-header.component.html</context>
<context context-type="linenumber">12</context>
</context-group>
</trans-unit>
<trans-unit id="5f6829555b5bd568d8251cc80049c006ff9fdd96" datatype="html">
<source>
Services
</source>
<context-group purpose="location">
<context context-type="sourcefile">app/routes-header/routes-header.component.html</context>
<context context-type="linenumber">18</context>
</context-group>
</trans-unit>
</body>
</file>
</xliff>