Skip to main content

GitHub Actions

· One min read

Open in Notion

Context in Workflow

More contexts at https://docs.github.com/en/actions/learn-github-actions/contexts

  • Url for repo, for example: https://github.com/bndynet/bndynet ${{ github.server_url }}/${{ github.repository }}
  • Url for pull request ${{ github.server_url }}/${{ github.repository }}/pull/${{ github.event.pull_request.number }}
  • Url for issue ${{ github.server_url }}/${{ github.repository }}/issues/${{ github.event.issue.number }}

About CRON

# ┌───────────── minute (0 - 59)
# │ ┌───────────── hour (0 - 23)
# │ │ ┌───────────── day of the month (1 - 31)
# │ │ │ ┌───────────── month (1 - 12)
# │ │ │ │ ┌───────────── day of the week (0 - 6)
# │ │ │ │ │
# │ │ │ │ │
# │ │ │ │ │
# * * * * * <command to execute>

Examples

name: Test Build

on:
push:
pull_request:
schedule:
- cron: '00 1 * * 1' # At 01:00 on Mondays.
name: Sync Notion pages to posts

on:
schedule:
# Runs "At 20:00 on every day-of-week"
- cron: '0 20 * * *'

jobs:
# Build job
sync:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3

- name: Generate posts
uses: bndynet/github-action-notion@v1
with:
notion-token: ${{ secrets.NOTION_TOKEN}}
root-page-id: ${{ secrets.NOTION_ROOT_PAGE_ID }}

- name: Commit posts
uses: EndBug/add-and-commit@v9 # https://github.com/marketplace/actions/add-commit
with:
add: '_posts'
message: Sync Notion pages to posts by GitHub Actions
committer_name: Bendy Zhang
committer_email: email@your.com

Marble testing for rxjs

· 2 min read

Open in Notion

Examples

'-' or '------': Equivalent to [NEVER](<https://rxjs.dev/api/index/const/NEVER>), or an observable that never emits or errors or completes.

|: Equivalent to [EMPTY](<https://rxjs.dev/api/index/const/EMPTY>), or an observable that never emits and completes immediately.

#: Equivalent to [throwError](<https://rxjs.dev/api/index/function/throwError>), or an observable that never emits and errors immediately.

'--a--': An observable that waits 2 "frames", emits value a on frame 2 and then never completes.

'--a--b--|': On frame 2 emit a, on frame 5 emit b, and on frame 8, complete.

'--a--b--#': On frame 2 emit a, on frame 5 emit b, and on frame 8, error.

'-a-^-b--|': In a hot observable, on frame -2 emit a, then on frame 2 emit b, and on frame 5, complete.

'--(abc)-|': on frame 2 emit ab, and c, then on frame 8, complete.

'-----(a|)': on frame 5 emit a and complete.

'a 9ms b 9s c|': on frame 0 emit a, on frame 10 emit b, on frame 9,011 emit c, then on frame 9,012 complete.

'--a 2.5m b': on frame 2 emit a, on frame 150,003 emit b and never complete.

import { TestScheduler } from 'rxjs/testing';
import { throttleTime } from 'rxjs';

const testScheduler = new TestScheduler((actual, expected) => {
// asserting the two objects are equal - required
// for TestScheduler assertions to work via your test framework
// e.g. using chai.
expect(actual).deep.equal(expected);
});

// This test runs synchronously.
it('generates the stream correctly', () => {
testScheduler.run((helpers) => {
const { cold, time, expectObservable, expectSubscriptions } = helpers;
const e1 = cold(' -a--b--c---|');
const e1subs = ' ^----------!';
const t = time(' ---| '); // t = 3
const expected = '-a-----c---|';

expectObservable(e1.pipe(throttleTime(t))).toBe(expected);
expectSubscriptions(e1.subscriptions).toBe(e1subs);
});

testScheduler.run((helpers) => {
const { time, cold } = helpers;
const source = cold('---a--b--|');
const t = time(' --| ');
// --|
const expected = ' -----a--b|';
const result = source.pipe(delay(t));
expectObservable(result).toBe(expected);
});

testScheduler.run((helpers) => {
const { animate, cold } = helpers;
animate(' ---x---x---x---x');
const requests = cold('-r-------r------');
/* ... */
const expected = ' ---a-------b----';
});
});

Git

· 8 min read

Open in Notion

Download and Installation

https://git-scm.com/downloads

Commit

Commit with empty message

git config --global alias.nccommit "commit -a --allow-empty-message -m ''"
git nccommit

Change commit info

git commit --amend --author="Bendy Zhang <zbatbndy.net>" --no-edit

git commit --amend --date="Sun Aug 7 13:05 2022 +0800" --no-edit
GIT_COMMITTER_DATE="Sun Aug 7 14:10 2022 +0800" git commit --amend --no-edit

Branch

New branch from remote

git checkout -b localBranch origin/remote_branch

Push changes to new branch

git checkout -b newBranch
git push -u origin newBranch # -u is short for `-set-upstream`

Bind local branch with remote branch

git branch --set-upstream-to=origin/remote_branch your_branch

Remove multiple branches

git branch -d `git branch --list '3.2.*'`

Reset local branch to remote branch

git fetch origin && git reset --hard origin/master

Merge

  • git merge master
  • git merge master --squash // merge master branch to current branch and keep changes without commits
  • git rebase master
  • git rebase --interactive --root // squash all of your commits down to the root commit
  • git cherry-pick <commitid> // pick up specified commit to current branch, if conflicts, resolve it and git add . && git cherry-pick --continue
  • git cherry-pick A..B The commit A should be older than B which will not include A. If requires A, please use A^~B

Undoing Things

  • Remove from cache

    git rm -r --cached \<your-folder\>
  • Overwrite commit

    git commit -m 'initial commit'
    git add .
    git commit --amend
  • Unstaging a Staged File

    • git reset to unstage all files, they will be removed from the staging area back to your working directory.
    • git reset HEAD <file>...
    • git checkout -- <file>... to discard changes in working directory
    • git restore --staged <file>... to unstage into change list
  • Unmodifying a Modified File

    • git checkout -- <path>
    • git checkout -- . to discard your entire working directory, head back to the root of your project.
    • git reset --soft <commit> to unstage commits softly, git reset --soft HEAD~1 to unstage your last commit
  • View all logs git reflog

  • Remove untracked files & directories

    • Print out files and directories which will be removed (dry run), run git clean -n -d
    • To remove directories, run git clean -f -d or git clean -fd
    • To remove ignored files, run git clean -f -X or git clean -fX
    • To remove ignored and non-ignored files, run git clean -f -x or git clean -fx

Stash Usage

git stash // stash current changes
git stash pop // pop last stash and remove from history
git stash save 'message' // stash current changes with message
git stash list // show all stashes
git stash apply <id> // apply <id> stash

Tag

# create tagg
git tag -a <tagname> -m '<tagcomment>'

# show tags
git tag

# show specified tag
git show <tagname>

# push to remote
git push origin <tagname>

# push all tags to remote
git push origin --tags

# checkout tag
git checkout <tagname>

# delete tag
git tag -d <tagname>

# delete remote tag
git push origin <tagname>
git push --delete origin tagname

Clone Subfolder

By following ways, you will get the folder fastest that you want.

git clone -n --depth=1 --filter=blob:none git@github.com:bndynet/bndynet.git
cd bndynet
git sparse-checkout set "/_data/*" --no-cone
git checkout

Or

git init
git sparse-checkout init --no-cone
git sparse-checkout set "/_data/*" --no-cone
git remote add origin git@github.com:bndynet/bndynet.git
git config core.sparsecheckout true
echo "partialclonefilter = blob:none" >> .git/config
git pull --depth=1 origin master

Subtree

// with squash, this repo history commits will not be merged into parent repo
git subtree add --prefix=subfolder https://github.com/subrepo.git master --squash
git subtree push --prefix=dist https://github.com/subrepo.git gh-pages

Workflow

with Git: Fork, Branching, Commits, and Pull Request

  1. Fork a repo.

  2. Clone the forked project to your local machine:

    git clone git@github.com:USERNAME/<forked_repo>

  3. Configure remotes:

    git remote add upstream git://github.com/<origin_repo>

  4. Create a branch for new feature:

    git checkout -b my-new-branch

  5. Develop on my-new-branch branch only, but Do not merge my-new-branch branch to the your master (as it should stay equal to upstream master)!!

  6. Commit changes to my-new-branch branch:

    git add .
    git commit -m "commit message"
  7. Push branch to GitHub, to allow your mentor to review your code:

    $ git push origin my-new-branch

  8. Repeat steps 5-7 till development is complete.

  9. Fetch upstream changes that were done by other contributors:

    $ git fetch upstream

  10. Update local master:

git checkout master
git pull upstream master

ATTENTION: any time you lost of track of your code – launch “gitk —all” in source folder, UI application come up that will show all branches and history in pretty view, explanation.

  1. Rebase my-new-branch branch on top of the upstream master:
git checkout my-new-branch
git rebase master
  1. In the process of the rebase, it may discover conflicts. In that case it will stop and allow you to fix the conflicts. After fixing conflicts, use git add . to update the index with those contents, and then just run:
git rebase --continue
  1. Push branch to GitHub (with all your final changes and actual code):

We forcing changes to your issue branch(our sand box) is not common branch, and rebasing means recreation of commits so no way to push without force. NEVER force to common branch.

git push origin my-new-branch --force
  1. Created build for testing and send it to any mentor for testing.
  2. Only after all testing is done – Send a Pull Request.

Attention: Please recheck that in your pull request you send only your changes, and no other changes!! Check it by command: git diff my-new-branch upstream/master

Submodule

git submodule add submodule-repo path
git submodule update --init --recursive
git rm submodule-name
git rm submodule-name --cached
// update submodule to master
cd submodule_folder
git checkout master
cd ../
git add .
git commit -m ''

Common Alias

git config --global alias.s "status"
git config --global alias.a "\!git add . && git status"
git config --global alias.au "\!git add -u . && git status"
git config --global alias.aa "\!git add . && git add -u . && git status"
git config --global alias.b "branch"
git config --global alias.c "commit"
git config --global alias.cm "commit -m"
git config --global alias.ca "commit --amend"
git config --global alias.ac "\!git add . && git commit"
git config --global alias.acm "\!git add . && git commit -m"
git config --global alias.l "log --graph --all --pretty=format:'%C(yellow)%h%C(cyan)%d%Creset %s %C(white)- %an, %ar%Creset'"
git config --global alias.ll "log --stat --abbrev-commit"
git config --global alias.lg "log --color --graph --pretty=format:'%C(bold white)%h%Creset -%C(bold green)%d%Creset %s %C(bold green)(%cr)%Creset %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative"
git config --global alias.llg "log --color --graph --pretty=format:'%C(bold white)%H %d%Creset%n%s%n%+b%C(bold blue)%an <%ae>%Creset %C(bold green)%cr (%ci)' --abbrev-commit"
git config --global alias.d "diff"
git config --global alias.master "checkout master"
git config --global alias.main "checkout main"
git config --global alias.alias "\!git config --list | grep 'alias\.' | sed 's/alias\.\([^=]*\)=\(.*\)/\1\ => \2/' | sort"
git config --global alias.hi "\!echo 'Hello World!'"
git config --global --unset alias.hi

Log

$ git log --follow --pretty=oneline things/text.txt view log of the renamed file
git log --author="zb@bndy.net" --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }' -

Output:

added lines: 545140, removed lines: 1152979, total lines: -607839
git log --pretty=format:%h,%an,%ae,%ad,%s --name-only

Output:

acb533d,Bendy Zhang,zb@bndy.net,Sun Jul 12 16:17:14 2020 +0800,commit message
.gitignore
README.md
  • -since=<date>, --after=<date> Show commits more recent than a specific date.
  • -until=<date>, --before=<date> Show commits older than a specific date.
  • --author=<pattern>, --committer=<pattern> Limit the commits output to ones with author/committer header lines that match the specified pattern (regular expression). With more than one -author=<pattern>, commits whose author matches any of the given patterns are chosen (similarly for multiple -committer=<pattern>).

Pretty Formats

https://git-scm.com/docs/pretty-formats#Documentation/pretty-formats.txt-emHem

QA

.gitignore can not ignore a file.

Need run following command to remove all files from the repository and add them back.

git rm -rf --cached .
git add .

Global Error Handling

· One min read

Open in Notion

global-error-handler.ts

import { HttpErrorResponse } from '@angular/common/http';
import { ErrorHandler, Injectable, NgZone } from '@angular/core';

@Injectable()
export class GlobalErrorHandler implements ErrorHandler {
constructor() {}

public handleError(error: any): void {
// Check if it's an error from an HTTP response
const isServerError = error instanceof HttpErrorResponse;

if (!isServerError) {
// TODO
}

console.error('Error from Global Error Handler', error);
}
}

app.module.ts

@NgModule({
declarations: [AppComponent],
imports: [
CommonModule,
HttpClientModule,
],
providers: [
{ provide: ErrorHandler, useClass: GlobalErrorHandler },
],
bootstrap: [AppComponent],
})
export class AppModule {}

Setup

· One min read

Open in Notion

Mirrors

Add mirror in conf/setting.xml file in Maven root folder:

<mirrors>
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>

Ruby

· One min read

Open in Notion

Installing

bookmark

gem sources -l
gem sources --remove https://ruby.taobao.org/
gem sources --add https://gems.ruby-china.com/

Installing on M1 Apple for macOS 13 Ventura

brew install chruby ruby-install

Next, if you’re on an Apple Silicon Mac (M1 or M2), you need to check which version of the Apple Command Line Tools (CLT) or Xcode you have:

brew config

Look for the lines at the bottom that start with CLT:  and Xcode: . If either one of them starts with 14 , then you’ll need to install Ruby like this:

ruby-install ruby -- --enable-shared

Otherwise, don’t use any extra options:

ruby-install ruby

Nginx

· 5 min read

Open in Notion

Run in Docker

sudo docker pull nginx
sudo docker run --name my-nginx -p 8080:80 -v /some/content:/usr/share/nginx/html:ro -d nginx
sudo chmod -R 755 /some/content

Install Nginx on CentOS 8

1. Update the System:

First, update your system to ensure all packages are up to date.

sudo dnf update -y

2. Add the Nginx Repository

CentOS 8 doesn't include the latest Nginx packages in its default repositories. To get the latest version, add the official Nginx repository.

Create a repository file for Nginx:

sudo nano /etc/yum.repos.d/nginx.repo

Add the following content to the file:

[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key

The nginx-stable repository is enabled by default, while the nginx-mainline repository is not. You can enable the nginx-mainline repository by setting enabled=1 if you prefer to use the mainline version of Nginx.

3. Install Nginx:

Install Nginx using the dnf package manager.

sudo dnf install nginx -y

4. Start and Enable Nginx:

Once the installation is complete, start the Nginx service and enable it to start on boot.

sudo systemctl start nginx
sudo systemctl enable nginx

5. Verify Nginx Installation:

You can verify that Nginx is running by checking its status.

sudo systemctl status nginx

6. Adjust Firewall Settings:

If you have a firewall enabled, you'll need to allow traffic on HTTP (port 80) and HTTPS (port 443) ports.

sudo firewall-cmd --permanent --zone=public --add-service=http
sudo firewall-cmd --permanent --zone=public --add-service=https
sudo firewall-cmd --reload

Configuration for static website based on framework

server {
listen 80;
server_name yourdomain.com;

root /var/www/angular-app;
index index.html;

location / {
try_files $uri /index.html;
}

error_page 404 /index.html;

gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
}

Logs

sudo tail -f /var/log/nginx/error.log

Distribute Domains to Ports

server {
listen 80;
server_name domain1.com;

location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

server {
listen 80;
server_name domain2.com;

location / {
proxy_pass http://localhost:8081;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

Use map:

http {
map $host $backend {
default localhost:80;
domain1.com localhost:8080;
domain2.com localhost:8081;
}

server {
listen 80;

location / {
proxy_pass http://$backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}

Certificate in Nginx

1. Obtain an SSL/TLS Certificate

You can obtain a certificate from a Certificate Authority (CA) like Let's Encrypt, which provides free SSL/TLS certificates. Using Certbot, you can automate the process of obtaining and renewing certificates

Install Certbot:

sudo dnf install certbot python3-certbot-nginx -y

Obtain a Certificate: Replace your_domain.com with your actual domain name.

sudo certbot --nginx -d your_domain.com

Follow the prompts to complete the certificate issuance process. Certbot will automatically configure Nginx for SSL.

2. Manually Configuring Nginx with an SSL/TLS Certificate

If you have an existing certificate or need to configure it manually, follow these steps:

Create a Configuration File for Your Site:

sudo nano /etc/nginx/conf.d/your_domain.conf

Add SSL Configuration: Replace your_domain.com with your actual domain name and specify the paths to your certificate and key files.

server {
listen 80;
server_name your_domain.com;
return 301 https://$host$request_uri; # Redirect HTTP to HTTPS
}

server {
listen 443 ssl;
server_name your_domain.com;

ssl_certificate /etc/letsencrypt/live/your_domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your_domain.com/privkey.pem;

ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

location / {
proxy_pass http://backend_server; # Change to your backend application address
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

3. Configure Firewall

Ensure your firewall allows HTTPS traffic:

sudo firewall-cmd --permanent --zone=public --add-service=https
sudo firewall-cmd --reload

4. Test and Reload Nginx

Test Nginx Configuration:

sudo nginx -t

Reload Nginx to Apply Changes:

sudo systemctl reload nginx

Q&A

Error about socket 80

nginx: [emerg] socket() [::]:80 failed (97: Unknown error)

Nginx try to listen the IPV6 [::]:80. Comment below line from the configuration /etc/nginx/sites-available/default.

# listen [::]:80 default_server;

Permission denied

Let www-data user have the permission to read the folder.

sudo chown -R www-data:www-data /your/site
sudo chmod 755 /your/site

If you still have same error, maybe your path under some user home folder. You should change the home folder permission.

sudo chown -R username:www-data /home/username
sudo chmod -R 750 /home/username
sudo chmod -R 755 /home/username/public_html

Service Operations on CentOS

· One min read

Open in Notion

$ systemctl disable httpd
rm '/etc/systemd/system/multi-user.target.wants/httpd.service'

$ systemctl status httpd
httpd.service - The Apache HTTP Server
Loaded: loaded (/usr/lib/systemd/system/httpd.service;
disabled
)
$ systemctl enable httpd
ln -s '/usr/lib/systemd/system/httpd.service' '/etc/systemd/system/multi-user.target.wants/httpd.service'

$ systemctl status httpd
httpd.service - The Apache HTTP Server
Loaded: loaded (/usr/lib/systemd/system/httpd.service;
enabled
)

Docker Q&A

· One min read

Open in Notion

IPv4 forwarding is disabled. Networking will not work

Solution 1: This won’t work every time

// run on host server
sysctl -w net.ipv4.ip_forward=1
sudo systemctl restart network

Solution 2: Append net.ipv4.ip_forward=1 to /etc/sysctl.conf file.

Solution 3: Append parameter —net=host to docker run, that will use host ports and ignore the -p parameters for example:

docker run -it --network host –name mynginx nginx

/var/jenkins_home/copy_reference_file.log Permission denied

$ docker run -ti -p 8080:8080 -p 50000:50000 -v /opt/jenkins:/var/jenkins_home jenkins touch: cannot touch ‘/var/jenkins_home/copy_reference_file.log’: Permission denied Can not write to /var/jenkins_home/copy_reference_file.log. Wrong volume permissions?

If you meet the above error, please figure out your volume mapping permissions.

sudo chown -R 1000:1000 /opt/jenkins