Showing posts with label Software Engineering. Show all posts
Showing posts with label Software Engineering. Show all posts

Monday 8 August 2022

Git strategies for applying bug fixes

$ mkdir test
 
$ cd test
 
$ git init
Initialised empty Git repository in /home/bojan/dev/test/.git/
 
$ git status
On branch master

No commits yet

nothing to commit (create/copy files and use "git add" to track)
 
$ touch foo.txt
 
$ vi foo.txt
 
foo.txt:
 
feature A {
   A.1
   A.2
   A.3
}
 
$ git status
On branch master

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
    foo.txt

nothing added to commit but untracked files present (use "git add" to track)
 
$ git add foo.txt
 
$ git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
    new file:   foo.txt
 
$ git commit -m "Added feature A"
[master (root-commit) ae68f9e] Added feature A
 1 file changed, 5 insertions(+)
 create mode 100644 foo.txt

 
 
Let's now create a feature branch for feature B. Let's assume we add a bug in its first implementation.
 
$ git checkout -b feature/B
Switched to a new branch 'feature/B'
 
$ vi foo.txt 
 
foo.txt:
 
feature A {
   A.1
   A.2
   A.3
}

feature B {
   B.1
   B.2 - bug
   B.3
}
 
$ git status
On branch feature/B
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   foo.txt

no changes added to commit (use "git add" and/or "git commit -a")
 
$ git add foo.txt 
 
$ cat foo.txt
feature A {
   A.1
   A.2
   A.3
}

feature B {
   B.1
   B.2 - bug
   B.3
}
 
$ git commit -m "Added feature B"
[feature/B c2f2c7d] Added feature B
 1 file changed, 6 insertions(+)
 
$ git log
commit c2f2c7d39fb93b3fb45ec9ff384e0ebd5303253d (HEAD -> feature/B)
Author: Bojan Komazec <bojan.komazec@example.com>
Date:   Mon Aug 8 21:36:48 2022 +0100

    Added feature B

commit ae68f9ea4bf55f54a8f40831df5b980dc30e5c6e (master)
Author: Bojan Komazec <bojan.komazec@example.com>
Date:   Mon Aug 8 21:34:20 2022 +0100

    Added feature A
 
 
$ git rebase master
Current branch feature/B is up to date.
 
$ git checkout master
Switched to branch 'master'
 
$ git merge feature/B
Updating ae68f9e..c2f2c7d
Fast-forward
 foo.txt | 6 ++++++
 1 file changed, 6 insertions(+)

 
Let's assume we've now realized that there's a bug in the current implementation of feature B.
 
 
$ git log
commit c2f2c7d39fb93b3fb45ec9ff384e0ebd5303253d (HEAD -> master, feature/B)
Author: Bojan Komazec <bojan.komazec@example.com>
Date:   Mon Aug 8 21:36:48 2022 +0100

    Added feature B

commit ae68f9ea4bf55f54a8f40831df5b980dc30e5c6e
Author: Bojan Komazec <bojan.komazec@example.com>
Date:   Mon Aug 8 21:34:20 2022 +0100

    Added feature A
 
 
$ git revert c2f2c7d39fb93b3fb45ec9ff384e0ebd5303253d
[master cd72121] Revert "Added feature B"
 1 file changed, 6 deletions(-)
 
$ git log
commit cd721217e154dc74cad4037135c0ea2c0b898696 (HEAD -> master)
Author: Bojan Komazec <bojan.komazec@example.com>
Date:   Mon Aug 8 21:42:57 2022 +0100

    Revert "Added feature B"
    
    This reverts commit c2f2c7d39fb93b3fb45ec9ff384e0ebd5303253d.

commit c2f2c7d39fb93b3fb45ec9ff384e0ebd5303253d (feature/B)
Author: Bojan Komazec <bojan.komazec@example.com>
Date:   Mon Aug 8 21:36:48 2022 +0100

    Added feature B

commit ae68f9ea4bf55f54a8f40831df5b980dc30e5c6e
Author: Bojan Komazec <bojan.komazec@example.com>
Date:   Mon Aug 8 21:34:20 2022 +0100

    Added feature A
 
 
We now want to fix the bug so we create a new branch, B2, by branching off feature/B:
 
$ git checkout feature/B
Switched to branch 'feature/B'
 
$ git checkout -b feature/B2
Switched to a new branch 'feature/B2'
 
$ cat foo.txt
feature A {
   A.1
   A.2
   A.3
}

feature B {
   B.1
   B.2 - bug
   B.3
}
 
 
$ vi foo.txt 

foo.txt:

feature A {
   A.1
   A.2
   A.3
}

feature B {
   B.1
   B.2 - bug fixed
   B.3
}
 
 
$ git status
On branch feature/B2
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   foo.txt

no changes added to commit (use "git add" and/or "git commit -a")
 
$ git commit -a -m "Fixed bug in feature B"
[feature/B2 29dbed4] Fixed bug in feature B
 1 file changed, 1 insertion(+), 1 deletion(-)
 
$ git status
On branch feature/B2
nothing to commit, working tree clean
 
$ cat foo.txt
feature A {
   A.1
   A.2
   A.3
}

feature B {
   B.1
   B.2 - bug fixed
   B.3
}

Before we merge feature branch to trunk, we first want to rebase it:
 
 
$ git rebase -i master
Auto-merging foo.txt
CONFLICT (content): Merge conflict in foo.txt
error: could not apply 29dbed4... Fixed bug in feature B

Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply 29dbed4... Fixed bug in feature B
 
 
$ vi foo.txt 

We're taking all changes from feature branch.

$ git add foo.txt 
 
$ git rebase --continue
[detached HEAD b86cff8] Fixed bug in feature B
 1 file changed, 7 insertions(+)
Successfully rebased and updated refs/heads/feature/B2.
 
$ git log
commit b86cff82118bb6af2bf10a4adf584e924acaaed0 (HEAD -> feature/B2)
Author: Bojan Komazec <bojan.komazec@example.com>
Date:   Mon Aug 8 21:47:55 2022 +0100

    Fixed bug in feature B

commit cd721217e154dc74cad4037135c0ea2c0b898696 (master)
Author: Bojan Komazec <bojan.komazec@example.com>
Date:   Mon Aug 8 21:42:57 2022 +0100

    Revert "Added feature B"
    
    This reverts commit c2f2c7d39fb93b3fb45ec9ff384e0ebd5303253d.

commit c2f2c7d39fb93b3fb45ec9ff384e0ebd5303253d (feature/B)
Author: Bojan Komazec <bojan.komazec@example.com>
Date:   Mon Aug 8 21:36:48 2022 +0100

    Added feature B

commit ae68f9ea4bf55f54a8f40831df5b980dc30e5c6e
Author: Bojan Komazec <bojan.komazec@example.com>
Date:   Mon Aug 8 21:34:20 2022 +0100

    Added feature A
 
$ git status
On branch feature/B2
nothing to commit, working tree clean
 
 
We can now merge feature branch into master:
 
 
$ git checkout master
Switched to branch 'master'
 
$ git status
On branch master
nothing to commit, working tree clean
 
$ git log
commit cd721217e154dc74cad4037135c0ea2c0b898696 (HEAD -> master)
Author: Bojan Komazec <bojan.komazec@example.com>
Date:   Mon Aug 8 21:42:57 2022 +0100

    Revert "Added feature B"
    
    This reverts commit c2f2c7d39fb93b3fb45ec9ff384e0ebd5303253d.

commit c2f2c7d39fb93b3fb45ec9ff384e0ebd5303253d (feature/B)
Author: Bojan Komazec <bojan.komazec@example.com>
Date:   Mon Aug 8 21:36:48 2022 +0100

    Added feature B

commit ae68f9ea4bf55f54a8f40831df5b980dc30e5c6e
Author: Bojan Komazec <bojan.komazec@example.com>
Date:   Mon Aug 8 21:34:20 2022 +0100

    Added feature A
 
$ git merge feature/B2
Updating cd72121..b86cff8
Fast-forward
 foo.txt | 7 +++++++
 1 file changed, 7 insertions(+)
 
$ git status
On branch master
nothing to commit, working tree clean
 
$ cat foo.txt
feature A {
   A.1
   A.2
   A.3
}

feature B {
   B.1
   B.2 - bug fixed
   B.3
}



We assumed here that we had the original feature branch available. Alternatively, we could have created a new branch off the master, reverse the reverse commit, apply the fix and then merge it back to master.

Friday 21 August 2020

How to upgrade Go on Ubuntu

I had oldish version (1.12.1) of Go on my Ubuntu 18.04 and I wanted to update it to the most recent one (1.15). 



This was my old version:

$ go version
go version go1.12.1 linux/amd64

It was using symlink pointing to my previous go installation which was in /var/lib/go:

$ which go /usr/local/bin/go $ ls -la /usr/local/bin/go lrwxrwxrwx 1 root root 18 Apr 1 2019 /usr/local/bin/go -> /var/lib/go/bin/go $ ls -la /var/lib/go/bin/go -rwxr-xr-x 1 root root 14609408 Mar 14 2019 /var/lib/go/bin/go $ ls -la /var/lib/go total 216 drwxr-xr-x 10 root root 4096 Mar 14 2019 . drwxr-xr-x 78 root root 4096 May 26 10:02 .. drwxr-xr-x 2 root root 4096 Mar 14 2019 api -rw-r--r-- 1 root root 55358 Mar 14 2019 AUTHORS drwxr-xr-x 2 root root 4096 Mar 14 2019 bin -rw-r--r-- 1 root root 1339 Mar 14 2019 CONTRIBUTING.md -rw-r--r-- 1 root root 78132 Mar 14 2019 CONTRIBUTORS drwxr-xr-x 8 root root 4096 Mar 14 2019 doc -rw-r--r-- 1 root root 5686 Mar 14 2019 favicon.ico drwxr-xr-x 3 root root 4096 Mar 14 2019 lib -rw-r--r-- 1 root root 1479 Mar 14 2019 LICENSE drwxr-xr-x 13 root root 4096 Mar 14 2019 misc -rw-r--r-- 1 root root 1303 Mar 14 2019 PATENTS drwxr-xr-x 6 root root 4096 Mar 14 2019 pkg -rw-r--r-- 1 root root 1607 Mar 14 2019 README.md -rw-r--r-- 1 root root 26 Mar 14 2019 robots.txt drwxr-xr-x 46 root root 4096 Mar 14 2019 src drwxr-xr-x 21 root root 12288 Mar 14 2019 test -rw-r--r-- 1 root root 8 Mar 14 2019 VERSION

I then removed the old installation:

$ sudo rm -rf /var/lib/go/

...and downloaded and unpacked the archive containing the latest version at recommended locaton (/usr/local):

$ sudo tar -C /usr/local -xzf go1.15.linux-amd64.tar.gz

$ ls -la /usr/local/go
total 236
drwxr-xr-x 10 root root  4096 Aug 11 20:16 .
drwxr-xr-x 12 root root  4096 Aug 21 13:14 ..
drwxr-xr-x  2 root root  4096 Aug 11 20:16 api
-rw-r--r--  1 root root 55669 Aug 11 20:16 AUTHORS
drwxr-xr-x  2 root root  4096 Aug 11 20:18 bin
-rw-r--r--  1 root root  1339 Aug 11 20:16 CONTRIBUTING.md
-rw-r--r--  1 root root 95475 Aug 11 20:16 CONTRIBUTORS
drwxr-xr-x  7 root root  4096 Aug 11 20:16 doc
-rw-r--r--  1 root root  5686 Aug 11 20:16 favicon.ico
drwxr-xr-x  3 root root  4096 Aug 11 20:16 lib
-rw-r--r--  1 root root  1479 Aug 11 20:16 LICENSE
drwxr-xr-x 12 root root  4096 Aug 11 20:16 misc
-rw-r--r--  1 root root  1303 Aug 11 20:16 PATENTS
drwxr-xr-x  6 root root  4096 Aug 11 20:19 pkg
-rw-r--r--  1 root root  1607 Aug 11 20:16 README.md
-rw-r--r--  1 root root    26 Aug 11 20:16 robots.txt
-rw-r--r--  1 root root   397 Aug 11 20:16 SECURITY.md
drwxr-xr-x 47 root root  4096 Aug 11 20:16 src
drwxr-xr-x 23 root root 12288 Aug 11 20:16 test
-rw-r--r--  1 root root     6 Aug 11 20:16 VERSION

$ cat /usr/local/go/VERSION 
go1.15

As expected, go binary was not available at the moment for the broken symlink:

$ go version
bash: /usr/local/bin/go: No such file or directory

$ ls -la /usr/local/bin/go
lrwxrwxrwx 1 root root 18 Apr  1  2019 /usr/local/bin/go -> /var/lib/go/bin/go // (symlink error)

I then deleted the old symlink and created a new one:

$ sudo rm /usr/local/bin/go 
$ sudo ln -s /usr/local/go/bin/go /usr/local/bin/go

After this my go symlink was pointing to the new version:

$ go version
go version go1.15 linux/amd64

$ which go
/usr/local/bin/go

$ ls -la /usr/local/bin/go
lrwxrwxrwx 1 root root 20 Aug 21 13:20 /usr/local/bin/go -> /usr/local/go/bin/go

$ ls -la /usr/local/go/bin/go
-rwxr-xr-x 1 root root 14256244 Aug 11 20:18 /usr/local/go/bin/go

$ /usr/local/go/bin/go version
go version go1.15 linux/amd64

Friday 17 July 2020

Functions in Go


To declare a function in Go, use the keyword func followed by the function name, any
parameters, and then any return values.

feeds, err := RetrieveFeeds()
if err != nil {
   log.Fatal(err)
}

NOTE: You can omit the parentheses () from an if statement in Golang, but the curly braces {} are mandatory!

This function belongs to the search package and returns two values. The first return value is a slice
of Feed type values. A slice is a reference type that implements a dynamic array. You use
slices in Go to work with lists of data.

The second return value is an error.  
Functions can have multiple return values. It’s common to declare functions that return a value and an error value just like the RetrieveFeeds function. If an error occurs, never trust the other values being returned from the function. They should always be ignored, or else you run the risk of the code generating more errors or panics.

Why doesn't Go allow nested function declarations (functions inside functions)? - Stack Overflow


func plus(a int, b int) int {...}

Can Functions be passed as parameters in Go? Yes.

type MyFunc func(int) string

func Foo(fn MyFunc) {
    str := fn(123)
}

Variable can be of a function type and be assigned a function:

sayHello := func() {
   fmt.Println("hello")
}

go sayHello()


Go by Example: Variadic Functions

func sum(nums... int){...}

Three dots (ellipsis) notation

Specifying function's default value for an argument is NOT supported.
Default value in Go's method

When to return pointer to local variable?

Return pointer to local struct

Go performs pointer escape analysis. If the pointer escapes the local stack, the object is allocated on the heap. If it doesn't escape the local function, the compiler is free to allocate it on the stack (although it makes no guarantees; it depends on whether the pointer escape analysis can prove that the pointer stays local to this function).

Golang documentation states that it's perfectly legal to return a pointer to local variable.
Compiler sees you return the address and just makes it on the heap for you.

Escape Analysis in Go


Naming


Your custom function can't have the same name as the name of some imported package.

Function Overloading


Does the Go language have function/method overloading? (Answer: NO)
Optional parameters, default parameter values and method overloading

The idiomatic way to emulate optional parameters and method overloading in Go is to write several methods with different names.

Alternative Patterns for Method Overloading in Go
Functional options for friendly APIs <-- MUST READ (!)

Deferred execution

go - Multiple defers vs deferred anonymous function - Stack Overflow

defer func() {
  err := f.Close()
  if err != nil {
    log.Println("close:", err)
  }
  err = os.Remove(f.Name())
  if err != nil {
    log.Println("remove:", err)
  }
}()

Built-in Functions


make()


panic()


Go by Example: Panic
When to call panic() : golang
Panic in Golang - GeeksforGeeks


Closures


Go by Example: Closures

What exactly does “closing over” mean?

 "A closes over B" == "B is a free variable of A", where free variables are those that appear in a function's body, but not in its signature.


What will be the output of the following snippet?

var wg sync.WaitGroup

for _, letter := range []string{"a", "b", "c"} {
   wg.Add(1)
   go func() {
      defer wg.Done()
      fmt.Println(letter)
   }()
}

wg.Wait()


Go closure variable scope

Closures in Go capture variables by reference. That means the inner function holds a reference to the  i variable in the outer scope, and each call of it accesses this same variable.


Closure (computer programming)

Comapre to JS Clsoures.
Lexical scoping:
This is an example of lexical scoping, which describes how a parser resolves variable names when functions are nested. The word "lexical" refers to the fact that lexical scoping uses the location where a variable is declared within the source code to determine where that variable is available. Nested functions have access to variables declared in their outer scope.


How golang's “defer” capture closure's parameter?



// Launch the goroutine
go func(matcher Matcher, feed *Feed) {
   Match(matcher, feed, searchTerm, results) 
   waitGroup.Done()
}(matcher, feed)


goroutine

  • light-weight process that is automatically time-sliced onto one or more operating system threads by the Go runtime.
  • a function that’s launched to run independently from other functions in the program.
  • Use the keyword go to launch and schedule goroutines to run concurrently. 
  • the order the goroutines get executed is unpredictable
  • can be an anonymous function
  • can be launched in a (for-range) loop, for each element of some set: this allows each element to be processed independently in a concurrent fashion
  • There's no goroutine ID available from the runtime
  • not an OS thread (thread managed/scheduled natively by OS)
  • not exactly a green thread (thread managed/scheduled by languages runtime or virtual machine) [Green threads] [Why not Green Threads?]
  • is a special type of coroutine (concurrent subroutines - functions, closures or methods) - the one that is non-preemptive. It cannot be interrupted but instead has multiple points through which it can be suspended or reentered. Go runtime defines this points internally and automatically suspends them when they block and resumes them when they become unblocked.
  • goroutines operate within the same address space as each other, and host functions
  • Go implements M:N scheduler, which means it maps M green threads to N OS threads. Goroutines are then scheduled onto the green threads. When we have more goroutines than green threads available, the scheduler handles the distribution of the goroutines across the available threads and ensures that when these goroutines become blocked, other goroutines can be run.
  • Go follows a model of concurrency called the fork-join model.
  • function which runs as goroutine can have return value(s) and return them but they will never be read => they should not have a return value. If we want to read some value which is calculated in goroutine we should either use channels or variable capturing mechanisms [Catching return values from goroutines].

To launch a function as goroutine:
goroutines operate within the same address space as each other, and
simply host functions,
func foo() {
   fmt.Println("foo()")
}

go foo()

To launch anonymous function as goroutine:

go func() {
   fmt.Println("foo()")
}()



anonymous function

  • a function that’s declared without a name
  • can take parameters


pointer variables

  • are great for sharing variables between functions. They allow functions to access and change the state of a variable that was declared within the scope of a different function and possibly a different goroutine.
  • In Go, all variables are passed by value. Since the value of a pointer variable is the address to the memory being pointed to, passing pointer variables between functions is still considered a pass by value.
Pointers vs. values in parameters and return values


waitGroup.Done()

Once the main task withing the goroutine completes, we execute the code which decrements the WaitGroup count. Once every goroutine finishes calling Done method, the program will know every main task has been done (e.g. element has been processed).

There’s something else interesting about the method call to Done: the WaitGroup
value was never passed into the anonymous function as a parameter, yet the anony-
mous function has access to it. Go supports closures and you’re seeing this in action. In fact, the searchTerm and results variables are also being accessed by the anonymous function via closures.
Thanks to closures, the function can access those variables directly without the need to
pass them in as parameters. The anonymous function isn’t given a copy of these variables; it has direct access to the same variables declared in the scope of the outer function. This is the reason why we don’t use closures for the matcher and feed variables.

---

With all the processing goroutines working, sending results on the results channel
and decrementing the waitGroup counter, we need a way to display those results and
keep the main function alive until all the processing is done. We'll launch yet another anonymous function as a goroutine. This anonymous function takes no parameters and uses closures to access both the waitGroup and results variables. This goroutine calls the method Wait() on the WaitGroup value, which is causing the goroutine to block until the count for the WaitGroup hits zero. Once that happens, the goroutine calls the built-in function close on the channel, which as you’ll see causes the program to terminate.


// Launch a goroutine to monitor when all the work is done.
go func() {
   // Wait for everything to be processed.
   waitGroup.Wait()

   // Close the channel to signal to the Display

   // function that we can exit the program.
   close(results)
}()

---

func Match(matcher Matcher, feed *Feed, searchTerm string, results chan<- *Result) {
   // Perform the search against the specified matcher.
   searchResults, err := matcher.Search(feed, searchTerm)
   if err != nil {
      log.Println(err)
      return
   }
   // Write the results to the channel.
   for _, result := range searchResults {
      results <- result
   }
}

results are written to the channel

BK: not how nil is used instead of null
BK: note how it's so convenient the model of returning function result and error at the same time; no need to pass variable by pointer and no expensive exceptions
BK: note how there are no brackets around conditions in if, for... statements.

init() function


All init functions in any code file that are part of the program will get called before
the main function.

When is the init() function run?

func init() {
  // Change the device for logging to stdout.
   log.SetOutput(os.Stdout)
   ...
}

This init function sets the logger from the standard library to write to the stdout device. By default, the logger is set to write to the stderr device.


main() function


How do I return from func main in Go? - Stack Overflow

func main() { os.Exit(mainReturnWithCode()) }

func mainReturnWithCode() int {
    // do stuff, defer functions, etc.
    return exitcode // a suitable exit code
}

Working with strings in Go

Strings are implemented as a reference type, though they're immutable.

String 


s := "This is in first line"
s += "\n"
s += "...and this is in the second line"

How do you write multiline strings in Go?

This raw quote (raw literal) does not parse escape sequences (\n would remain):

`line 1
line 2\n
line 3`

It is possible to use formatters though:

fmt.Sprintf(`a = %d`, 123)

Another option:

"line 1" +
"line 2" +
"line 3"



What is the difference between backticks (``) & double quotes (“”) in golang?

String Comparison - use == or !=

Slice string into letters

String Formatting 


s := fmt.Sprintf("a %s", "string")

Go by Example: String Formatting
Extracting substrings in Go

%x - formats value as HEX string with lowercase letters
%X - formats value as HEX string with uppercase letters

var n []byte = ...
s := fmt.Sprinf("%x", n) 

How to print struct variables in console? To print the name of the fields in a struct:

fmt.Printf("%+v\n", myStruct)


To convert string to byte array use:

s string
buffer := []byte(s)


fmt


fmt.Println("Table names:", tableNames)

SPACE character is automatically inserted between these two strings.

Golang - How to print the values of Arrays?

fmt.Printf("%v", projects)


fmt.Printf


%+v - prints struct’s field names (if value is a struct)





Go string = byte sequence, UTF-8 encoded
Unicode UTF-8 code point = 1-4 bytes (1 for ASCII)
rune = int32 => can store each UTF-8 code point 

Unicode string example: en dash & Chinese letter 

package main

import (
"fmt"
)

func main() {
s := "\u2013汉"
fmt.Printf("Character %s has %d bytes, %d UTF-8 code points", s, len(s), len([]rune(s)))
}

Output:

Character –汉 has 6 bytes, 2 UTF-8 code points


Working with File system in Go

Package os contains useful functions for working with file system.

os - The Go Programming Language



Open() 

  • opens the named file for reading (O_RDONLY mode).


OpenFile() 
  • opens file in a specified mode set via flag e.g.  O_RDWR or O_WRONLY.

Create() 
  • creates or truncates the named file
  • If the file already exists, it is truncated. 
  • If the file does not exist, it is created. 
  • Methods on the returned File can be used for I/O; the associated file descriptor has mode O_RDWR.

Truncate() function:
  • requires file to be opened in mode which allows writing into it.
  • if specified size is 0 deletes the content of the file and sets its size to 0.

Writing to files


Go by Example: Writing Files

Without buffering:


f, err := os.Create("/tmp/test.txt")
if err != nil {
panic(err)
}
defer f.Close()
f.WriteString("some text!\n")


WriteString() under the hood calls system function write():

r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)))

write() belongs to the group of Unix unbuffered file I/O functions (together with openclosereadlseek...).

From FILE I/O:
They are part of the POSIX standard for UNIX programming, not part of ANSI C. Unlike the standard I/O routines provided by ANSI C (such as fscanf and fprintf which store the data they read in buffers) these functions are unbuffered I/O. They invoke a system call in the kernel, and will be called by the standard ANSI C function calls in the UNIX environment.

With buffering:


f, err := os.Create("/tmp/test.txt")
if err != nil {
panic(err)
}
defer f.Close()
w := bufio.NewWriter(f)
w.WriteString("some text!\n")
w.Flush()


Friday 10 July 2020

Using ESLint

ESLint is one of most popular linters for JavaScript and TypeScript (via typescript-eslint).




To use, we need to install it first. It can be installed locally (per project) or globally. To install it globally:

$ npm install -g eslint

We can verify that package is installed:

$ npm -g list  | grep eslint
├─┬ eslint@7.4.0
│ ├─┬ eslint-scope@5.1.0
│ ├─┬ eslint-utils@2.1.0
│ │ └── eslint-visitor-keys@1.3.0 deduped
│ ├── eslint-visitor-keys@1.3.0
│ │ └── eslint-visitor-keys@1.3.0 deduped

...and also check its command line args:

$ eslint --help
eslint [options] file.js [file.js] [dir]

Basic configuration:
  --no-eslintrc                   Disable use of configuration from .eslintrc.*
  -c, --config path::String       Use this configuration, overriding .eslintrc.* config options if present
  --env [String]                  Specify environments
  --ext [String]                  Specify JavaScript file extensions
  --global [String]               Define global variables
  --parser String                 Specify the parser to be used
  --parser-options Object         Specify parser options
  --resolve-plugins-relative-to path::String  A folder where plugins should be resolved from, CWD by default

Specifying rules and plugins:
  --rulesdir [path::String]       Use additional rules from this directory
  --plugin [String]               Specify plugins
  --rule Object                   Specify rules

Fixing problems:
  --fix                           Automatically fix problems
  --fix-dry-run                   Automatically fix problems without saving the changes to the file system
  --fix-type Array                Specify the types of fixes to apply (problem, suggestion, layout)

Ignoring files:
  --ignore-path path::String      Specify path of ignore file
  --no-ignore                     Disable use of ignore files and patterns
  --ignore-pattern [String]       Pattern of files to ignore (in addition to those in .eslintignore)

Using stdin:
  --stdin                         Lint code provided on <STDIN> - default: false
  --stdin-filename String         Specify filename to process STDIN as

Handling warnings:
  --quiet                         Report errors only - default: false
  --max-warnings Int              Number of warnings to trigger nonzero exit code - default: -1

Output:
  -o, --output-file path::String  Specify file to write report to
  -f, --format String             Use a specific output format - default: stylish
  --color, --no-color             Force enabling/disabling of color

Inline configuration comments:
  --no-inline-config              Prevent comments from changing config or rules
  --report-unused-disable-directives  Adds reported errors for unused eslint-disable directives

Caching:
  --cache                         Only check changed files - default: false
  --cache-file path::String       Path to the cache file. Deprecated: use --cache-location - default: .eslintcache
  --cache-location path::String   Path to the cache file or directory

Miscellaneous:
  --init                          Run config initialization wizard - default: false
  --env-info                      Output execution environment information - default: false
  --no-error-on-unmatched-pattern  Prevent errors when pattern is unmatched
  --debug                         Output debugging information
  -h, --help                      Show help
  -v, --version                   Output the version number
  --print-config path::String     Print the configuration for the given file




To initialise and configure ESLint launch configuration wizard:

$ eslint --init


ESLint configuration will be saved in file .eslintrc.json (if you opt json file to be used). Example content:

.eslintrc.json:

{
    "env": {
        "browser": true,
        "es2020": true
    },
    "extends": "eslint:recommended",
    "parserOptions": {
        "ecmaVersion": 11
    },
    "rules": {
        "indent": [
            "error",
            4
        ],
        "quotes": [
            "error",
            "single"
        ],
        "semi": [
            "error",
            "always"
        ]
    }
}

If you use VS Code, you can install ESLint plugin which will pick up this configuration and automatically lint your code and show warnings and error son the go.

ESLint should be added to Node project as a dev dependency.

package.json (created by $npm init):

  "devDependencies": {
    "eslint": "^7.4.0"
  }


It is also possible to run this linter from the terminal. For example, to lint all files in the current project recursively with respect to .eslintrc.json:

$eslint .

...
  2:32009  error  Strings must use singlequote                                               quotes
  3:23     error  Strings must use singlequote                                               quotes
  3:42     error  Missing semicolon                                                          semi
  3:43     error  Strings must use singlequote                                               quotes
  3:70     error  Strings must use singlequote                                               quotes
  3:166    error  Strings must use singlequote                                               quotes
  3:207    error  Strings must use singlequote                                               quotes
  3:280    error  Strings must use singlequote                                               quotes
  3:338    error  Missing semicolon                                                          semi
  3:460    error  Missing semicolon                                                          semi
  3:555    error  Missing semicolon                                                          semi
...
  4:22578  error  Missing semicolon                                                          semi
  4:22601  error  Missing semicolon                                                          semi

✖ 11436 problems (11436 errors, 0 warnings)


To ignore rules in .eslintrc.json and run the linter only for JavaScript files and only to check one particular rule:

/my-project$ eslint . --ext .js --no-eslintrc --rule 'indent: ["error", 4, { "SwitchCase": 1 }]'

/my-project/path/to/file.js
   28:1  error  Expected indentation of 12 spaces but found 16  indent
   46:1  error  Expected indentation of 12 spaces but found 16  indent
  186:1  error  Expected indentation of 4 spaces but found 8    indent
  187:1  error  Expected indentation of 4 spaces but found 8    indent
  188:1  error  Expected indentation of 4 spaces but found 8    indent
  189:1  error  Expected indentation of 4 spaces but found 8    indent

Rules used here was indent.

References