documentation, switch to Travis

This commit is contained in:
Matthias Kretschmann 2018-05-14 01:50:11 +02:00
parent efbc1846d2
commit 2e45c29330
Signed by: m
GPG Key ID: 606EEEF3C479A91F
15 changed files with 262 additions and 56 deletions

View File

@ -1,29 +0,0 @@
cache:
paths:
- node_modules/
- public/
stages:
- build
- deploy
build:
image: node:latest
stage: build
script:
- npm i -g gatsby-cli
- export PATH="$PATH:/usr/local/bin/gatsby"
- npm i
- npm test
- npm run build
artifacts:
paths:
- public
deploy:
image: garland/aws-cli-docker:latest
stage: deploy
script:
- aws s3 sync --delete --acl public-read ./public s3://matthiaskretschmann.com
only:
- master

24
.travis.yml Normal file
View File

@ -0,0 +1,24 @@
language: node_js
node_js: node
cache:
directories:
- node_modules
- public
install:
- pip install --user awscli
- export PATH=$PATH:$HOME/.local/bin
- npm i -g gatsby-cli
- export PATH="$PATH:/usr/local/bin/gatsby"
- npm i
script:
- npm test
- npm run build
after_success:
- npm run deploy
notifications:
email: false

7
LICENSE Normal file
View File

@ -0,0 +1,7 @@
Copyright (c) 2018 Matthias Kretschmann
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

141
README.md
View File

@ -1,23 +1,152 @@
# matthiaskretschmann.com
> Portfolio of Matthias Kretschmann
# [matthiaskretschmann.com](https://matthiaskretschmann.com)
[![pipeline status](https://git.berlin/m/portfolio/badges/master/pipeline.svg)](https://git.berlin/m/portfolio/commits/master)
[![image](src/images/twitter-card.png)](https://matthiaskretschmann.com)
> 👔 Portfolio thingy, built with [Gatsby](https://www.gatsbyjs.org).
[![Build Status](https://travis-ci.com/kremalicious/portfolio.svg?branch=master)](https://travis-ci.com/kremalicious/portfolio)
## Table of Contents
* [Features](#features)
* [One data file to rule all pages](#one-data-file-to-rule-all-pages)
* [Theme switcher](#theme-switcher)
* [SEO component](#seo-component)
* [Client-side vCard creation](#client-side-vcard-creation)
* [Matomo (formerly Piwik) analytics tracking](#matomo-formerly-piwik-analytics-tracking)
* [Project images](#project-images)
* [Importing SVG assets](#importing-svg-assets)
* [Development](#development)
* [Linting](#linting)
* [Add a new project](#add-a-new-project)
* [Deployment](#deployment)
* [Licenses](#licenses)
---
## Features
The whole [portfolio](https://matthiaskretschmann.com) is a React-based Single Page App built with [Gatsby](https://www.gatsbyjs.org).
Despite being built with React, and despite all the project images making it a very image-heavy portfolio, Gatsby makes the final site super fast. So fast, it's almost unreal and feels like magic. And makes it work without JavaScript by server-side rendering all routes. [And so much more](https://www.gatsbyjs.org/features/).
### One data file to rule all pages
All content is powered by one YAML file, [`data/projects.yml`](data/projects.yml) where all the portfolio's projects are defined. The project description itself is transformed from Markdown.
Gatsby automatically creates pages from each item in that file utilizing the [`src/templates/Project.jsx`](src/templates/Project.jsx) template.
### Theme switcher
Includes a theme switcher which allows user to toggle between a light and a dark theme. Switching between them also happens automatically based on time of day.
If you want to know how, have a look at the respective component under [`src/components/molecules/ThemeSwitch.jsx`](src/components/molecules/ThemeSwitch.jsx)
### SEO component
Includes a SEO component which automatically switches all required `meta` tags for search engines, Twitter Cards, and Facebook OpenGraph tags based on the browsed route/page.
If you want to know how, have a look at the respective component under [`src/components/atoms/SEO.jsx`](src/components/atoms/SEO.jsx)
### Client-side vCard creation
The _Add to addressbook_ link in the footer automatically creates a downloadable vCard file on the client-side, based on data defined in `data/meta.yml`.
If you want to know how, have a look at the respective component under [`src/components/atoms/Vcard.jsx`](src/components/atoms/Vcard.jsx)
### Matomo (formerly Piwik) analytics tracking
Site sends usage statistics to my own [Matomo](https://matomo.org) installation. To make this work in Gatsby, I created and open sourced a plugin, [gatsby-plugin-matomo](https://github.com/kremalicious/gatsby-plugin-matomo), which is in use on this site.
### Project images
All project images live under `src/images` and are automatically attached to each project based on the inclusion of the project's `slug` in their filenames.
_(TODO: automatically add the inital image to each project node, so it doesn't have to be defined in the projects.yml file.)_
All project images make use of the excellent [gatsby-image](https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-image) plugin, working in tandem with [gatsby-plugin-sharp](https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-plugin-sharp) and [gatsby-transformer-sharp](https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-transformer-sharp).
All together, Gatsby automatically generates all required image sizes for delivering responsible, responsive images to visitors, including lazy loading of all images. Also includes the [intersection-observer polyfill](https://github.com/w3c/IntersectionObserver) to make lazy loading work properly in Safari.
All project images use one single component defined in [`src/components/atoms/ProjectImage.jsx`](src/components/atoms/ProjectImage.jsx). In there, one main GraphQL query fragment is defined, which then gets used throughout other GraphQL queries.
### Importing SVG assets
Makes use of `gatsby-plugin-svgr` so SVG assets can be imported like so:
```js
import { ReactComponent as Logo } from '../images/logo.svg'
```
## Development
```bash
git clone git@github.com:kremalicious/portfolio.git
cd portfolio/
npm i
npm start
```
Get SVG images
### Linting
```js
import { ReactComponent as Logo } from '../../images/logo.svg'
ESlint, Prettier, and Stylelint are setup for all linting purposes:
```bash
npm run lint
```
To automatically format all code files:
```bash
npm run format
npm run format:css
```
### Add a new project
To add a new project, run the following command. This adds a new item to the top of the `projects.yml` file, creating the title & slug from the argument:
```bash
npm run new -- "Hello"
```
Then continue modifying the new entry in [`data/projects.yml`](data/projects.yml).
Finally, add as many images as needed with the file name format and put into `src/images/`:
```
portfolio-SLUG-01.png
portfolio-SLUG-02.png
portfolio-SLUG-03.png
...
```
## Deployment
Automatic deployments are triggered upon successful tests & builds on Travis:
* push to `master` initiates a live deployment
* any Pull Request, and subsequent pushes to it, initiates a beta deployment
The deploy command simply calls the [`scripts/deploy.sh`](scripts/deploy.sh) script, syncing the contents of the `public/` folder to S3:
```bash
npm run deploy
```
The deploymeng script can be used locally too, the branch checks are only happening for Travis builds, allowing to deploy any branch from local machine.
## Licenses
All images and projects are plain ol' copyright:
**© Copyright 2018 Matthias Kretschmann**
Most displayed projects are subject to the copyright of their respective owners.
All the rest, like all code and documentation, is under:
**The MIT License**
[Full MIT license text](LICENSE)

View File

@ -1,14 +0,0 @@
#!/usr/bin/env bash
set -e;
aws s3 sync \
--delete \
--acl public-read \
./public s3://matthiaskretschmann.com
echo "---------------------------------------------"
echo " ✓ done deployment "
echo "---------------------------------------------"
exit;

View File

View File

@ -1,5 +1,7 @@
const path = require('path')
// Intersection Observer polyfill
// requires `npm install intersection-observer`
// https://github.com/gatsbyjs/gatsby/issues/2288#issuecomment-334467821
exports.modifyWebpackConfig = ({ config, stage }) => {
if (stage === 'build-html') {

View File

View File

@ -11,7 +11,8 @@
"format": "prettier --write 'src/**/*.{js,jsx}'",
"format:css": "prettier-stylelint --write --quiet 'src/**/*.{css,scss}'",
"test": "npm run lint",
"deploy": "./deploy.sh"
"deploy": "./scripts/deploy.sh",
"new": "node ./scripts/new-project.js"
},
"dependencies": {
"camel-case": "^3.0.0",
@ -50,12 +51,13 @@
"eslint-plugin-graphql": "^2.1.1",
"eslint-plugin-prettier": "^2.6.0",
"eslint-plugin-react": "^7.8.2",
"ora": "^2.1.0",
"prepend": "^1.0.2",
"prettier": "^1.12.1",
"prettier-stylelint": "^0.4.2",
"slugify": "^1.3.0",
"stylelint": "^9.2.0",
"stylelint-config-standard": "^18.2.0"
},
"browserslist": [
"defaults"
]
"browserslist": ["defaults"]
}

43
scripts/deploy.sh Executable file
View File

@ -0,0 +1,43 @@
#!/usr/bin/env bash
#
# required environment variables:
# AWS_ACCESS_KEY_ID
# AWS_SECRET_ACCESS_KEY
# AWS_DEFAULT_REGION
# AWS_S3_BUCKET
#
set -e;
##
## check for pull request against master
##
if [ "$TRAVIS_PULL_REQUEST" != "false" ] && [ "$TRAVIS_BRANCH" == "master" ]; then
aws s3 sync \
--delete \
--acl public-read \
./public s3://"$AWS_S3_BUCKET_BETA"
##
## check for master push which is no pull request
##
elif [ "$TRAVIS_BRANCH" == "master" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ] || [ "$TRAVIS" != true ]; then
aws s3 sync \
--delete \
--acl public-read \
./public s3://"$AWS_S3_BUCKET"
echo "---------------------------------------------"
echo " ✓ done deployment "
echo "---------------------------------------------"
exit;
else
echo "---------------------------------------------"
echo " nothing to deploy "
echo "---------------------------------------------"
fi

20
scripts/new-project.js Normal file
View File

@ -0,0 +1,20 @@
const fs = require('fs')
const path = require('path')
const prepend = require('prepend')
const slugify = require('slugify')
const ora = require('ora')
const templatePath = path.join(__dirname, 'new-project.yml')
const template = fs.readFileSync(templatePath).toString()
const title = process.argv[2]
const titleSlug = slugify(title)
const spinner = ora(`Adding '${title}'.`).start()
const newContents = template.replace('TITLE', title).replace('SLUG', titleSlug)
const projects = path.join(__dirname, '..', 'data', 'projects.yml')
prepend(projects, newContents, error => {
if (error) throw error
spinner.succeed(`Added '${title}' to top of projects.yml file.`)
})

12
scripts/new-project.yml Normal file
View File

@ -0,0 +1,12 @@
-
title: TITLE
slug: "/SLUG/"
img: "../src/images/portfolio-SLUG-01.png"
description: >
Markdown can be used here.
links:
- title: Link
url: https://url
techstack:
- Tech used

View File

@ -8,11 +8,15 @@ class Vcard extends PureComponent {
super(props)
}
// Helper function to create base64 string from avatar image
// without the need to read image file from file system
toDataURL(src, callback, outputFormat) {
const img = new Image()
img.crossOrigin = 'Anonymous'
img.onload = function() {
// yeah, we're gonna create a fake canvas to render the image
// and then create a base64 string from the rendered result
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
let dataURL
@ -37,11 +41,13 @@ class Vcard extends PureComponent {
const contact = new vCard()
const photoSrc = meta.avatar.childImageSharp.original.src
// first, convert the avatar to base64,
// then construct all vCard elements
this.toDataURL(
photoSrc,
dataUrl => {
// stripping this data out of base64 string
// is required for vcard for whatever reason
// stripping this data out of base64 string is required
// for vcard to actually display the image for whatever reason
const dataUrlCleaned = dataUrl.split('data:image/jpeg;base64,').join('')
contact.set('photo', dataUrlCleaned, { encoding: 'b', type: 'JPEG' })
contact.set('fn', meta.title)
@ -61,6 +67,8 @@ class Vcard extends PureComponent {
)
}
// Construct the download from a blob of the just constructed vCard,
// and save it to user's file system
downloadVcard(vcard) {
const name = this.props.meta.addressbook.split('/').join('')
const blob = new Blob([vcard], { type: 'text/x-vcard' })
@ -75,6 +83,8 @@ class Vcard extends PureComponent {
render() {
return (
<a
// href is kinda fake, only there for usability
// so user knows what to expect when hovering the link before clicking
href={this.props.meta.addressbook}
onClick={this.handleAddressbookClick}
>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -11,7 +11,7 @@
/* SITE */
Typography : Brandon Grotesque, FF Tisa Sans Web Pro
Software : Gatsby.js, React, VS Code, macOS
Software : Gatsby.js, React, VS Code, Sketch, macOS
<!--