Realme 8 Pro – wyłączenie opcji „Naciśnij włącznik, aby zakończyć połączenie”

W moim telefonie Realme 8 Pro (RMX3081) pomimo, że miałem wyłączoną opcję „Naciśnij włącznik, aby zakończyć połączenie” to telefon rozłączał rozmowę, gdy wciskałem przycisk zasilania w celu wygaszenia ekranu. Opcja dostępna przez:

Ustawienia -> Ustawienia systemu -> Ułatwienia dostępu -> Naciśnij włącznik, aby zakończyć połączenie

lub

Settings -> System settings -> Accessibility -> Press Power button to end calls

Przez przypadek natrafiłem na jeszcze jedne ustawienia:

Ustawienia -> Sieć komórkowa -> Być może tego szukasz: Ustawienia połączeń  (Połączenia) -> Odbierz/zakończ połączenie -> Naciśnij włącznik, aby zakończyć połączenie

lub

Settings -> Mobile network -> You might be looking for: Call settings -> Answer/End calls -> Press Power button to end calls

Dopiero gdy tutaj wyłączyłem tę opcję, mogłem wygaszać ekran za pomocą przycisku zasilania. Nie wiem skąd pomysł na drugą opcję i czy można jakoś inaczej dojść do niej niż przez rekomendację.

jq i modyfikowanie pliku JSON

Ostatnio miałem potrzebę modyfikacji pliku JSON za pomocą jq. Poniżej przedstawię kilka przypadków.

Zamiana pojedynczej wartości dla klucza:

echo "$(jq '.scripts.preinstall="npm_preinstall.sh"' package.json)"  > package.json

Powyższy zapis umożliwia zapis do tego samego pliku bez użycia sponge, z moreutils, który może wymagać praw admina do zainstalowania.

Zamiana jednej wartości na drugą:

AZPROFILE=~/.azure/azureProfile.json
echo $(jq --arg old_name "$old_name" --arg new_name "$new_name"  '(.subscriptions[] | select(.name==$old_name)).name |= $new_name' $AZPROFILE)

W zmiennej old_name jest szukana wartość a w new_name nowa wartość.

Usunięcie całego obiektu na podstawie wartości klucza:

ctx=FooBar
echo $(jq --arg ctx "$ctx" 'del(.subscriptions[] | select(.name==$ctx))' $AZPROFILE) > $AZPROFILE

Ansible tworzenie zagnieżdżonych katalogów

Pierwsza myśl to wykorzystanie shell i przekazanie parametru do mkdir:

mkdir -p jenkins/cache/{war,tmp,workspace}

Można też użyć pętli with_nested:

---
- name: Create directories
  hosts: localhost
  vars:
    dirs: [
      [ jenkins ],
      [ cache ],
      [ war, tmp, workspace ]
    ]

  tasks:
  - debug:
      msg="{{item.0}}/{{item.1}}/{{item.2}}"
    with_nested: "{{ dirs | list }}"

Co generuje:

- Create directories on hosts: localhost -
debug...
  localhost ok: {
    "ansible_loop_var": "item",
    "changed": false,
    "item": [
        "jenkins",
        "cache",
        "war"
    ],
    "msg": "jenkins/cache/war"
}
  localhost ok: {
    "ansible_loop_var": "item",
    "changed": false,
    "item": [
        "jenkins",
        "cache",
        "tmp"
    ],
    "msg": "jenkins/cache/tmp"
}
  localhost ok: {
    "ansible_loop_var": "item",
    "changed": false,
    "item": [
        "jenkins",
        "cache",
        "workspace"
    ],
    "msg": "jenkins/cache/workspace"
}
  localhost ok

- Play recap -
  localhost                  : ok=1    changed=0    unreachable=0    failed=0    rescued=0    ignored=0

Oczywiście zamiast debug użyć file:

  - file:
      path: "{{base_directory}}/{{item.0}}/{{item.1}}/{{item.2}}"
      state: directory
    with_nested: "{{ dirs | list }}"

Można też użyć filetree modułu.

Lepsze jest jednak inne podejście zaproponowane na stackoverflow, za pomocą jinja2 testu is string:

---
- name: Join variables
  hosts: localhost

  tasks:
  - debug:
      msg: >-
        {{ base_directory }}/
        {{- item if item is string else item | join('/') }}
    with_nested: "{{ dirs }}"
    vars:
      dirs:
        - jenkins
        - - cache
        - - war
          - tmp
          - workspace
      base_directory: ~/tmp

Lub bez niego, jeszcze krócej:

  - debug:
      msg: >-
        {{ base_directory }}/
        {{- item | join('/') }}

lub dzięki filtru community.general.path_join:

  - debug:
      msg: "{{ base_directory }}/{{ item|community.general.path_join }}"
    with_nested: "{{ dirs }}"
    vars:
      dirs:
        - jenkins
        - cache
        - [war, tmp, workspace]
      base_directory: /tmp

Git wysyłanie zmian do kilku repozytoriów

Dodajemy nowy remote np. both oraz dodajemy nowe URLe dla push:

$ git remote add both me@original:something.git
$ git remote set-url --add --push both me@original:something.git
$ git remote set-url --add --push both git://somewhere/something.git

Git remote prezentuje się następująco:

$ git remote -v
both    me@original:something.git (fetch)
both    me@original:something.git (push)
both    git://somewhere/something.git (push)

I tereaz możemy wypychać zmiany do kilku repozytoriów przez git push both.

Firefox about:config

  • signon.includeOtherSubdomainsInLookup: false – wyłączenie autouzupełniania loginów dla innych domen, domyślnie uzupełnia o loginy/hasła z innych poddomen

Rekonfiguracja pakietu Debiana

Rekonfigurację pakietu Debiana, a także każdego systemu bazującego na nim, przeprowadzamy za pomocą:

dpkg-reconfigure nazwa

Oczywiście sam dpkg-reconfigure też możemy zmienić:

dpkg-reconfigure debconf

Domyślny jest program dialog. Do wyboru są także:

  • readline
  • gnome
  • kde
  • editor
  • noninteractive

W następnym kroku zmieniamy priorytety komunikatów:

  • critical
  • high
  • medium
  • low

Low jest najbardziej gadatliwy.

I tak aby np. zmienić domyślny system logowania:

dpkg-reconfigure gdm3

Lub wybrać dowolny inny zainstalowany. Zamiast tego oczywiście można użyć:

systemctl disable lightdm
systemctl enable gdm3

I reboot. Normalnie może użyć opcji –now.

Git remote-view

Konsolowe polecenie gh remote view z pakietu GitHub CLI otwiera dany zdalny URL w przeglądarce. Działa tylko dla URLi związanych z GitHub. Dla dowolnego innego URL poniższy alias może być przydatny:

remote-view = "!f() { REMOTE=$(git remote -v | grep origin | head -n1 | awk '{print $2}'); if expr $REMOTE : '^ssh' &>/dev/null; then REMOTE=$(echo $REMOTE | sed 's/^ssh:\\/\\/git@/https:\\/\\//;s/:7990//'); fi; open $REMOTE;}; f"

Jeszcze wymaga poprawa, jak choćby dodania konfiguracji dla portu itp.

Jenkins: Iterowanie po węzłach w pipeline

Poniższy kod realizuje iterowanie po wszystkich węzłach Jenkinsa mających daną etykietę:

label = "linux"
echo "Will run on hosts with label ${label}"

listOfNodeNames = jenkins.model.Jenkins.instance.nodes.collect {
  node -> node.getLabelString().contains(label) ? node.name : null
}
listOfNodeNames.removeAll(Collections.singleton(null))


for (node_to_run_on in listOfNodeNames) {
    println "Node: " + node_to_run_on
    node ("${node_to_run_on}") {
        stage("Run uname on ${node_to_run_on}") {
            sh """
            uname -a
            """    
        }
    }
}    

Jeśli mamy potrzebę przetestowania pewnego narzędzia, skonfigurowanego w Global Tool Configuration, na przykładzie mvnd:

label = "linux"
echo "Will run on hosts with label ${label}"

listOfNodeNames = jenkins.model.Jenkins.instance.nodes.collect {
  node -> node.getLabelString().contains(label) ? node.name : null
}
listOfNodeNames.removeAll(Collections.singleton(null))


for (node_to_run_on in listOfNodeNames) {
    println "Node: " + node_to_run_on
    node ("${node_to_run_on}") {
        stage("Run uname on ${node_to_run_on}") {
            def mvnd = tool name: 'mvnd-0.1.0', type: 'com.cloudbees.jenkins.plugins.customtools.CustomTool'
            withEnv(["PATH=$PATH:$mvnd"]) {
                sh """
                if which mvnd &>/dev/null; then
                    mvnd --status
                    mvnd --stop
                fi
                """    
            }
        }
    }
}

Postman i przetwarzanie danych z pliku dla kolekcji

Postman umożliwia uruchomienie kolekcji żądań. Wystarczy kliknąć Runner, wybrać odpowiednią kolekcję lub folder, ustawić środowisko i plik z danymi, CSV lub JSON.

Dane z pliku, dla przykładu z CSV mogą być puste lub w ogóle brakujące:

varName1,varName2,varName3
value1,value2,
,value

Pierwsza linia definiuje nazwy zmiennych. Każda kolejna linia to ich wartości dla kolejnej iteracji. Druga linia ustawia varName1=value1, varName2=value2, varName=”. Gdzie trzecia linia ustawia: varName1=”, varName2=value a varName3 nie jest ustawiona, nie istnieje dla tej iteracji.

Sprawdzamy je za pomocą:

if ( pm.iterationData.has("variable") && !pm.iterationData.get("variable") {
...
}

Powyższy kod sprawdzi czy zmienna jest ustawiona i nie jest pusta. Lub w przypadku gdy dopuszczamy w ogóle nie podanie wartości (trzecia linia):

if ( !pm.iterationData.has("variable") || pm.iterationData.has("variable") && !pm.iterationData.get("variable")) {
...
}

Dla więlu sprawdzań, w przypadku gdy operujemy na kolecji warto zgrupować w jeden blok:

if ( pm.iterationData.toJSON().values[0] === undefined ) {
...
}

Więcej można znaleźć tutaj: https://learning.postman.com/docs/writing-scripts/script-references/postman-sandbox-api-reference/#pmiterationdata

Postman i synchroniczne wysyłanie zapytań

Postman już jakiś czas temu wprowadził asynchroniczne wysyłanie zapytań w pm.sendRequest. Wszystko fajnie, tylko co gdy potrzebujesz wysłać synchroniczne żądania?

Na forum Postmana znalazłem „obejście” tego problemu:

pm.sendRequest(firstRequest, function (err, firstResponse)  {
  // add your extra logic here
  pm.sendRequest(secondRequest, function (err, secondResponse)  {
    // add your extra logic here
  });
});

W moim przypadku, w wyniku jednego zapytania musiałem wysłać X zapytań REST API w odpowiedniej kolejności, które były generowane na podstawie odpowiedzi. A dokładnie chodziło o skopiowanie komentarzy w Jira:

pm.sendRequest({
    url: restApiUrl + "/issue/" + source + "/comment",
    method: "GET",
    header: {   
        "Content-Type": "application/json",
    },
    body: {}
}, function (err, res) {
        let request = JSON.stringify({})
        comments = res.json().comments
        comments.sort(function(a,b){
            if (a.id < b.id ) { return 1; }
            if (a.id > b.id ) { return -1;}
            return 0;
        })
        comments.forEach(function(comment){
            var call = {
                "url": restApiUrl + "/issue/" + target + "/comment",
                "method": "POST",
                header: {   
                    "Content-Type": "application/json",
                },
                body: JSON.stringify({"body": comment.body})
            }
            request = "pm.sendRequest("+JSON.stringify(call)+",function(err,resp){"+request+"})"
        })
        eval(request)
    }
);

Akurat w tym przypadku pomogła funkcja eval, aby wykonać kod w stringu.

Linki