Compare commits
1 Commits
py314_3
...
renable_ma
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17ae8e085d |
17
.github/copilot-instructions.md
vendored
17
.github/copilot-instructions.md
vendored
@@ -1,17 +0,0 @@
|
|||||||
# Copilot Instructions
|
|
||||||
|
|
||||||
This project is a web application that allows users to create and manage tasks. The application is built using Python.
|
|
||||||
|
|
||||||
## Coding Standards
|
|
||||||
|
|
||||||
- Use snakeCase for variable and function names.
|
|
||||||
- Use PascalCase for component names.
|
|
||||||
- Use double quotes for strings.
|
|
||||||
- Use 4 spaces for indentation.
|
|
||||||
|
|
||||||
## Tone
|
|
||||||
|
|
||||||
- If I tell you that you are wrong, think about whether or not you think that's true and respond with facts.
|
|
||||||
- Avoid apologizing or making conciliatory statements.
|
|
||||||
- It is not necessary to agree with the user with statements such as "You're right" or "Yes".
|
|
||||||
- Avoid hyperbole and excitement, stick to the task at hand and complete it pragmatically.
|
|
||||||
15
.github/workflows/ci.yml
vendored
15
.github/workflows/ci.yml
vendored
@@ -19,23 +19,20 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
matrix:
|
||||||
# 3.x is used to run code coverage
|
python-version: ["3.13", "3.12", "3.11", "3.10", "3.9", "3.8"]
|
||||||
python-version: ["3.x", "3.14", "3.13", "3.12", "3.11", "3.10", "3.9"]
|
platform: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
platform: [ubuntu-latest, macos-latest, windows-latest, ubuntu-24.04-arm, macos-15-intel]
|
# exclude:
|
||||||
# exclude:
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v4
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
uses: actions/setup-python@v6
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
check-latest: ${{ github.event_name == 'schedule' }}
|
check-latest: ${{ github.event_name == 'schedule' }}
|
||||||
allow-prereleases: false
|
|
||||||
- name: Install zbar shared lib for QReader (Linux)
|
- name: Install zbar shared lib for QReader (Linux)
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
run: |
|
run: |
|
||||||
@@ -62,7 +59,7 @@ jobs:
|
|||||||
if: matrix.python-version == '3.x' && matrix.platform == 'ubuntu-latest'
|
if: matrix.python-version == '3.x' && matrix.platform == 'ubuntu-latest'
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: pytest
|
run: pytest
|
||||||
if: (matrix.python-version != '3.x' || matrix.platform != 'ubuntu-latest') && (matrix.python-version != '3.10' && matrix.platform != 'macos-latest')
|
if: matrix.python-version != '3.x' || matrix.platform != 'ubuntu-latest'
|
||||||
- name: Test with pytest (with code coverage)
|
- name: Test with pytest (with code coverage)
|
||||||
run: pytest --cov=extract_otp_secrets_test --junitxml=pytest.xml --cov-report=term-missing | tee pytest-coverage.txt
|
run: pytest --cov=extract_otp_secrets_test --junitxml=pytest.xml --cov-report=term-missing | tee pytest-coverage.txt
|
||||||
if: matrix.python-version == '3.x' && matrix.platform == 'ubuntu-latest'
|
if: matrix.python-version == '3.x' && matrix.platform == 'ubuntu-latest'
|
||||||
|
|||||||
225
.github/workflows/ci_docker.yml
vendored
225
.github/workflows/ci_docker.yml
vendored
@@ -27,22 +27,12 @@ jobs:
|
|||||||
build-and-push-docker-debian-image:
|
build-and-push-docker-debian-image:
|
||||||
name: Build Docker Bookworm image and push to repositories
|
name: Build Docker Bookworm image and push to repositories
|
||||||
# run only when code is compiling and tests are passing
|
# run only when code is compiling and tests are passing
|
||||||
strategy:
|
runs-on: ubuntu-latest
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- DOCKER_ARCH: amd64
|
|
||||||
platform: ubuntu-latest
|
|
||||||
PLATFORM_ARCH: x86_64
|
|
||||||
- DOCKER_ARCH: arm64
|
|
||||||
platform: ubuntu-24.04-arm
|
|
||||||
PLATFORM_ARCH: arm64
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.platform }}
|
|
||||||
|
|
||||||
# steps to perform in job
|
# steps to perform in job
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# avoid building if there are testing errors
|
# avoid building if there are testing errors
|
||||||
- name: Run smoke test
|
- name: Run smoke test
|
||||||
@@ -60,6 +50,11 @@ jobs:
|
|||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
id: buildx
|
id: buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
|
# Workaround for failing builds: https://github.com/docker/build-push-action/issues/761#issuecomment-1383822381
|
||||||
|
# TODO remove workaround when fixed
|
||||||
|
with:
|
||||||
|
driver-opts: |
|
||||||
|
image=moby/buildkit:v0.10.6
|
||||||
|
|
||||||
- name: Login to DockerHub
|
- name: Login to DockerHub
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
@@ -76,11 +71,11 @@ jobs:
|
|||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GHCR_IO_TOKEN }}
|
password: ${{ secrets.GHCR_IO_TOKEN }}
|
||||||
|
|
||||||
- name: "Build image (Bookworm/Debian 12) and push to Docker Hub and GitHub Container Registry"
|
- name: "Build image and push to Docker Hub and GitHub Container Registry"
|
||||||
id: docker_build_qr_reader_latest
|
id: docker_build_qr_reader_latest
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
platforms: linux/${{ matrix.DOCKER_ARCH }}
|
platforms: linux/amd64,linux/arm64
|
||||||
# relative path to the place where source code with Dockerfile is located
|
# relative path to the place where source code with Dockerfile is located
|
||||||
# TODO file:, move to docker/
|
# TODO file:, move to docker/
|
||||||
context: .
|
context: .
|
||||||
@@ -91,12 +86,10 @@ jobs:
|
|||||||
BASE_IMAGE=python:3.13-slim-bookworm
|
BASE_IMAGE=python:3.13-slim-bookworm
|
||||||
pull: true
|
pull: true
|
||||||
tags: |
|
tags: |
|
||||||
docker.io/scit0/extract_otp_secrets:latest-${{ matrix.PLATFORM_ARCH }}
|
scit0/extract_otp_secrets:latest
|
||||||
docker.io/scit0/extract_otp_secrets:bookworm-${{ matrix.PLATFORM_ARCH }}
|
scit0/extract_otp_secrets:bookworm
|
||||||
ghcr.io/scito/extract_otp_secrets:latest-${{ matrix.PLATFORM_ARCH }}
|
ghcr.io/scito/extract_otp_secrets:latest
|
||||||
ghcr.io/scito/extract_otp_secrets:bookworm-${{ matrix.PLATFORM_ARCH }}
|
ghcr.io/scito/extract_otp_secrets:bookworm
|
||||||
provenance: true
|
|
||||||
sbom: true
|
|
||||||
# build on feature branches, push only on master branch
|
# build on feature branches, push only on master branch
|
||||||
push: ${{ github.ref == 'refs/heads/master' && github.secret_source == 'Actions'}}
|
push: ${{ github.ref == 'refs/heads/master' && github.secret_source == 'Actions'}}
|
||||||
|
|
||||||
@@ -109,66 +102,18 @@ jobs:
|
|||||||
if: github.ref == 'refs/heads/master'
|
if: github.ref == 'refs/heads/master'
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: digests_bookworm_${{ matrix.PLATFORM_ARCH }}
|
name: debian_digests
|
||||||
path: digests.txt
|
path: digests.txt
|
||||||
|
|
||||||
create-multiarch-debian-manifests:
|
|
||||||
name: Create multiarch manifests for Debian image
|
|
||||||
if: ${{ github.ref == 'refs/heads/master' && github.secret_source == 'Actions'}}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs:
|
|
||||||
- build-and-push-docker-debian-image
|
|
||||||
steps:
|
|
||||||
- name: Login to DockerHub
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
if: github.secret_source == 'Actions'
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Login to Github Packages
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
if: github.secret_source == 'Actions'
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GHCR_IO_TOKEN }}
|
|
||||||
|
|
||||||
- name: Create multiarch manifests
|
|
||||||
if: ${{ github.ref == 'refs/heads/master' && github.secret_source == 'Actions'}}
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
for tag in \
|
|
||||||
docker.io/scit0/extract_otp_secrets:latest \
|
|
||||||
ghcr.io/scito/extract_otp_secrets:latest \
|
|
||||||
docker.io/scit0/extract_otp_secrets:bookworm \
|
|
||||||
ghcr.io/scito/extract_otp_secrets:bookworm \
|
|
||||||
; do
|
|
||||||
docker buildx imagetools create -t $tag \
|
|
||||||
$tag-x86_64 \
|
|
||||||
$tag-arm64
|
|
||||||
done
|
|
||||||
|
|
||||||
|
|
||||||
build-and-push-docker-alpine-image:
|
build-and-push-docker-alpine-image:
|
||||||
name: Build Docker Alpine image and push to repositories
|
name: Build Docker Alpine image and push to repositories
|
||||||
# run only when code is compiling and tests are passing
|
# run only when code is compiling and tests are passing
|
||||||
strategy:
|
runs-on: ubuntu-latest
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- DOCKER_ARCH: amd64
|
|
||||||
platform: ubuntu-latest
|
|
||||||
PLATFORM_ARCH: x86_64
|
|
||||||
- DOCKER_ARCH: arm64
|
|
||||||
platform: ubuntu-24.04-arm
|
|
||||||
PLATFORM_ARCH: arm64
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.platform }}
|
|
||||||
|
|
||||||
# steps to perform in job
|
# steps to perform in job
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# avoid building if there are testing errors
|
# avoid building if there are testing errors
|
||||||
- name: Run smoke test
|
- name: Run smoke test
|
||||||
@@ -204,22 +149,20 @@ jobs:
|
|||||||
|
|
||||||
- name: "only_txt: Build image and push to Docker Hub and GitHub Container Registry"
|
- name: "only_txt: Build image and push to Docker Hub and GitHub Container Registry"
|
||||||
id: docker_build_only_txt
|
id: docker_build_only_txt
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
platforms: linux/${{ matrix.DOCKER_ARCH }}
|
|
||||||
# relative path to the place where source code with Dockerfile is located
|
# relative path to the place where source code with Dockerfile is located
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
context: .
|
context: .
|
||||||
file: docker/Dockerfile_only_txt
|
file: docker/Dockerfile_only_txt
|
||||||
# builder: ${{ steps.buildx.outputs.name }}
|
# builder: ${{ steps.buildx.outputs.name }}
|
||||||
# Note: tags has to be all lower-case
|
# Note: tags has to be all lower-case
|
||||||
pull: true
|
pull: true
|
||||||
tags: |
|
tags: |
|
||||||
docker.io/scit0/extract_otp_secrets:only-txt-${{ matrix.PLATFORM_ARCH }}
|
scit0/extract_otp_secrets:only-txt
|
||||||
docker.io/scit0/extract_otp_secrets:alpine-${{ matrix.PLATFORM_ARCH }}
|
scit0/extract_otp_secrets:alpine
|
||||||
ghcr.io/scito/extract_otp_secrets:only-txt-${{ matrix.PLATFORM_ARCH }}
|
ghcr.io/scito/extract_otp_secrets:only-txt
|
||||||
ghcr.io/scito/extract_otp_secrets:alpine-${{ matrix.PLATFORM_ARCH }}
|
ghcr.io/scito/extract_otp_secrets:alpine
|
||||||
provenance: true
|
|
||||||
sbom: true
|
|
||||||
# build on feature branches, push only on master branch
|
# build on feature branches, push only on master branch
|
||||||
push: ${{ github.ref == 'refs/heads/master' && github.secret_source == 'Actions'}}
|
push: ${{ github.ref == 'refs/heads/master' && github.secret_source == 'Actions'}}
|
||||||
build-args: |
|
build-args: |
|
||||||
@@ -235,68 +178,18 @@ jobs:
|
|||||||
if: github.ref == 'refs/heads/master'
|
if: github.ref == 'refs/heads/master'
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: digests_alpine_${{ matrix.PLATFORM_ARCH }}
|
name: alpine_digests
|
||||||
path: digests.txt
|
path: digests.txt
|
||||||
|
|
||||||
create-multiarch-alpine-manifests:
|
|
||||||
name: Create multiarch manifests for Alpine image
|
|
||||||
if: ${{ github.ref == 'refs/heads/master' && github.secret_source == 'Actions'}}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs:
|
|
||||||
- build-and-push-docker-alpine-image
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
shell: bash
|
|
||||||
steps:
|
|
||||||
- name: Login to DockerHub
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
if: github.secret_source == 'Actions'
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Login to Github Packages
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
if: github.secret_source == 'Actions'
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GHCR_IO_TOKEN }}
|
|
||||||
|
|
||||||
- name: Create multiarch manifests
|
|
||||||
if: ${{ github.ref == 'refs/heads/master' && github.secret_source == 'Actions'}}
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
for tag in \
|
|
||||||
docker.io/scit0/extract_otp_secrets:only-txt \
|
|
||||||
docker.io/scit0/extract_otp_secrets:alpine \
|
|
||||||
ghcr.io/scito/extract_otp_secrets:only-txt \
|
|
||||||
ghcr.io/scito/extract_otp_secrets:alpine \
|
|
||||||
; do
|
|
||||||
docker buildx imagetools create -t $tag \
|
|
||||||
$tag-x86_64 \
|
|
||||||
$tag-arm64
|
|
||||||
done
|
|
||||||
|
|
||||||
build-and-push-docker-bullseye-image:
|
build-and-push-docker-bullseye-image:
|
||||||
name: Build Docker Bullseye image (for PyInstsaller) and push to repositories
|
name: Build Docker Bullseye image (for PyInstsaller) and push to repositories
|
||||||
# run only when code is compiling and tests are passing
|
# run only when code is compiling and tests are passing
|
||||||
strategy:
|
runs-on: ubuntu-latest
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- DOCKER_ARCH: amd64
|
|
||||||
platform: ubuntu-latest
|
|
||||||
PLATFORM_ARCH: x86_64
|
|
||||||
- DOCKER_ARCH: arm64
|
|
||||||
platform: ubuntu-24.04-arm
|
|
||||||
PLATFORM_ARCH: arm64
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.platform }}
|
|
||||||
|
|
||||||
# steps to perform in job
|
# steps to perform in job
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# avoid building if there are testing errors
|
# avoid building if there are testing errors
|
||||||
- name: Run smoke test
|
- name: Run smoke test
|
||||||
@@ -314,6 +207,11 @@ jobs:
|
|||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
id: buildx
|
id: buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
|
# Workaround for failing builds: https://github.com/docker/build-push-action/issues/761#issuecomment-1383822381
|
||||||
|
# TODO remove workaround when fixed
|
||||||
|
with:
|
||||||
|
driver-opts: |
|
||||||
|
image=moby/buildkit:v0.10.6
|
||||||
|
|
||||||
- name: Login to DockerHub
|
- name: Login to DockerHub
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
@@ -330,12 +228,12 @@ jobs:
|
|||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GHCR_IO_TOKEN }}
|
password: ${{ secrets.GHCR_IO_TOKEN }}
|
||||||
|
|
||||||
- name: "Build image from Bullseye (Debian 11) and push to GitHub Container Registry"
|
- name: "Build image from Bullseye and push to GitHub Container Registry"
|
||||||
id: docker_build_bullseye
|
id: docker_build_bullseye
|
||||||
if: github.ref == 'refs/heads/master'
|
if: github.ref == 'refs/heads/master'
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
platforms: linux/${{ matrix.DOCKER_ARCH }}
|
platforms: linux/amd64,linux/arm64
|
||||||
# relative path to the place where source code with Dockerfile is located
|
# relative path to the place where source code with Dockerfile is located
|
||||||
context: .
|
context: .
|
||||||
file: docker/Dockerfile
|
file: docker/Dockerfile
|
||||||
@@ -345,10 +243,7 @@ jobs:
|
|||||||
# Note: tags has to be all lower-case
|
# Note: tags has to be all lower-case
|
||||||
pull: true
|
pull: true
|
||||||
tags: |
|
tags: |
|
||||||
docker.io/scit0/extract_otp_secrets:bullseye-${{ matrix.PLATFORM_ARCH }}
|
scit0/extract_otp_secrets:bullseye
|
||||||
ghcr.io/scito/extract_otp_secrets:bullseye-${{ matrix.PLATFORM_ARCH }}
|
|
||||||
provenance: true
|
|
||||||
sbom: true
|
|
||||||
push: ${{ github.secret_source == 'Actions' }}
|
push: ${{ github.secret_source == 'Actions' }}
|
||||||
|
|
||||||
- name: Image digest
|
- name: Image digest
|
||||||
@@ -360,53 +255,5 @@ jobs:
|
|||||||
if: github.ref == 'refs/heads/master'
|
if: github.ref == 'refs/heads/master'
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: digests_bullseye_${{ matrix.PLATFORM_ARCH }}
|
name: bullseye_digests
|
||||||
path: digests.txt
|
path: digests.txt
|
||||||
|
|
||||||
create-multiarch-bullseye-manifests:
|
|
||||||
name: Create multiarch manifests for Bullseye image
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: ${{ github.ref == 'refs/heads/master' && github.secret_source == 'Actions'}}
|
|
||||||
needs:
|
|
||||||
- build-and-push-docker-bullseye-image
|
|
||||||
steps:
|
|
||||||
- name: Login to DockerHub
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
if: github.secret_source == 'Actions'
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Login to Github Packages
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
if: github.secret_source == 'Actions'
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GHCR_IO_TOKEN }}
|
|
||||||
|
|
||||||
- name:
|
|
||||||
if: ${{ github.ref == 'refs/heads/master' && github.secret_source == 'Actions'}}
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
for tag in \
|
|
||||||
docker.io/scit0/extract_otp_secrets:bullseye \
|
|
||||||
ghcr.io/scito/extract_otp_secrets:bullseye \
|
|
||||||
; do
|
|
||||||
docker buildx imagetools create -t $tag \
|
|
||||||
$tag-x86_64 \
|
|
||||||
$tag-arm64
|
|
||||||
done
|
|
||||||
|
|
||||||
container-images-clean-up:
|
|
||||||
name: Cleanup old container images
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Delete Container Packages
|
|
||||||
uses: actions/delete-package-versions@v5
|
|
||||||
if: ${{ github.secret_source == 'Actions'}}
|
|
||||||
with:
|
|
||||||
package-name: 'extract_otp_secrets'
|
|
||||||
package-type: 'container'
|
|
||||||
min-versions-to-keep: 1
|
|
||||||
delete-only-untagged-versions: 'true'
|
|
||||||
|
|||||||
73
.github/workflows/ci_release.yml
vendored
73
.github/workflows/ci_release.yml
vendored
@@ -57,7 +57,7 @@ jobs:
|
|||||||
tag_message: ${{ steps.meta.outputs.tag_message }}
|
tag_message: ${{ steps.meta.outputs.tag_message }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
- name: Set meta data
|
- name: Set meta data
|
||||||
id: meta
|
id: meta
|
||||||
# Writing to env with >> $GITHUB_ENV is an alternative
|
# Writing to env with >> $GITHUB_ENV is an alternative
|
||||||
@@ -106,27 +106,24 @@ jobs:
|
|||||||
path: release_id.txt
|
path: release_id.txt
|
||||||
|
|
||||||
build-linux-executable-in-docker:
|
build-linux-executable-in-docker:
|
||||||
name: Build ${{ matrix.platform }} release in docker container
|
name: Build ${{ matrix.PLATFORM }} release in docker container
|
||||||
# run only when code is compiling and tests are passing
|
# run only when code is compiling and tests are passing
|
||||||
|
runs-on: ubuntu-latest
|
||||||
needs: create-release
|
needs: create-release
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- DOCKER_PLATFORM: linux/amd64
|
- PLATFORM: linux/amd64
|
||||||
platform: ubuntu-latest
|
|
||||||
EXE: extract_otp_secrets_linux_x86_64
|
EXE: extract_otp_secrets_linux_x86_64
|
||||||
ASSET_NAME: extract_otp_secrets${{ needs.create-release.outputs.inline_version }}_linux_x86_64
|
ASSET_NAME: extract_otp_secrets${{ needs.create-release.outputs.inline_version }}_linux_x86_64
|
||||||
- DOCKER_PLATFORM: linux/arm64
|
- PLATFORM: linux/arm64
|
||||||
platform: ubuntu-24.04-arm
|
|
||||||
EXE: extract_otp_secrets_linux_arm64
|
EXE: extract_otp_secrets_linux_arm64
|
||||||
ASSET_NAME: extract_otp_secrets${{ needs.create-release.outputs.inline_version }}_linux_arm64
|
ASSET_NAME: extract_otp_secrets${{ needs.create-release.outputs.inline_version }}_linux_arm64
|
||||||
|
|
||||||
runs-on: ${{ matrix.platform }}
|
|
||||||
|
|
||||||
# steps to perform in job
|
# steps to perform in job
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# avoid building if there are testing errors
|
# avoid building if there are testing errors
|
||||||
- name: Run smoke test
|
- name: Run smoke test
|
||||||
@@ -148,7 +145,7 @@ jobs:
|
|||||||
# TODO remove workaround when fixed
|
# TODO remove workaround when fixed
|
||||||
with:
|
with:
|
||||||
driver-opts: |
|
driver-opts: |
|
||||||
image=moby/buildkit:latest
|
image=moby/buildkit:v0.10.6
|
||||||
|
|
||||||
- name: Login to DockerHub
|
- name: Login to DockerHub
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
@@ -175,10 +172,11 @@ jobs:
|
|||||||
# https://hub.docker.com/r/multiarch/qemu-user-static/
|
# https://hub.docker.com/r/multiarch/qemu-user-static/
|
||||||
- name: Run Pyinstaller in container for ${{ matrix.EXE }}
|
- name: Run Pyinstaller in container for ${{ matrix.EXE }}
|
||||||
run: |
|
run: |
|
||||||
docker run --pull always --entrypoint /bin/bash --rm -v "$(pwd)":/files -w /files docker.io/scit0/extract_otp_secrets:bullseye -c 'apt-get update && apt-get -y install binutils && pip install -U -r /files/requirements.txt && pip install pyinstaller && PYTHONHASHSEED=31 && pyinstaller -y --add-data /usr/local/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile --name ${{ matrix.EXE }} --distpath /files/dist/ /files/src/extract_otp_secrets.py'
|
docker run --pull always --rm --privileged multiarch/qemu-user-static --reset -p yes
|
||||||
|
docker run --platform ${{ matrix.PLATFORM }} --pull always --entrypoint /bin/bash --rm -v "$(pwd)":/files -w /files scit0/extract_otp_secrets:bullseye -c 'apt-get update && apt-get -y install binutils && pip install -U -r /files/requirements.txt && pip install pyinstaller && PYTHONHASHSEED=31 && pyinstaller -y --add-data /usr/local/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile --name ${{ matrix.EXE }} --distpath /files/dist/ /files/src/extract_otp_secrets.py'
|
||||||
|
|
||||||
- name: Smoke tests linux/amd64
|
- name: Smoke tests linux/amd64
|
||||||
if: matrix.DOCKER_PLATFORM == 'linux/amd64'
|
if: matrix.PLATFORM == 'linux/amd64'
|
||||||
run: |
|
run: |
|
||||||
dist/${{ matrix.EXE }} -V
|
dist/${{ matrix.EXE }} -V
|
||||||
dist/${{ matrix.EXE }} -h
|
dist/${{ matrix.EXE }} -h
|
||||||
@@ -191,12 +189,12 @@ jobs:
|
|||||||
dist/${{ matrix.EXE }} --qr CV2 example_export.png
|
dist/${{ matrix.EXE }} --qr CV2 example_export.png
|
||||||
dist/${{ matrix.EXE }} --qr CV2_WECHAT example_export.png
|
dist/${{ matrix.EXE }} --qr CV2_WECHAT example_export.png
|
||||||
- name: Smoke tests linux/arm64
|
- name: Smoke tests linux/arm64
|
||||||
if: matrix.DOCKER_PLATFORM == 'linux/arm64'
|
if: matrix.PLATFORM == 'linux/arm64'
|
||||||
run: |
|
run: |
|
||||||
docker run --pull always --entrypoint /bin/bash --rm -v "$(pwd)":/files -w /files docker.io/scit0/extract_otp_secrets -c 'dist/${{ matrix.EXE }} -V && dist/${{ matrix.EXE }} -h && dist/${{ matrix.EXE }} example_export.png && dist/${{ matrix.EXE }} - < example_export.txt && dist/${{ matrix.EXE }} --qr ZBAR example_export.png && dist/${{ matrix.EXE }} --qr QREADER example_export.png && dist/${{ matrix.EXE }} --qr QREADER_DEEP example_export.png && dist/${{ matrix.EXE }} --qr CV2 example_export.png && dist/${{ matrix.EXE }} --qr CV2_WECHAT example_export.png'
|
docker run --platform ${{ matrix.PLATFORM }} --pull always --entrypoint /bin/bash --rm -v "$(pwd)":/files -w /files scit0/extract_otp_secrets -c 'dist/${{ matrix.EXE }} -V && dist/${{ matrix.EXE }} -h && dist/${{ matrix.EXE }} example_export.png && dist/${{ matrix.EXE }} - < example_export.txt && dist/${{ matrix.EXE }} --qr ZBAR example_export.png && dist/${{ matrix.EXE }} --qr QREADER example_export.png && dist/${{ matrix.EXE }} --qr QREADER_DEEP example_export.png && dist/${{ matrix.EXE }} --qr CV2 example_export.png && dist/${{ matrix.EXE }} --qr CV2_WECHAT example_export.png'
|
||||||
- name: Load Release URL File from release job
|
- name: Load Release URL File from release job
|
||||||
if: startsWith(github.ref, 'refs/tags/v')
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
uses: actions/download-artifact@v5
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: release_url
|
name: release_url
|
||||||
- name: Display structure of files
|
- name: Display structure of files
|
||||||
@@ -226,7 +224,6 @@ jobs:
|
|||||||
needs: create-release
|
needs: create-release
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
matrix:
|
||||||
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#choosing-github-hosted-runners
|
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#choosing-github-hosted-runners
|
||||||
include:
|
include:
|
||||||
@@ -250,39 +247,19 @@ jobs:
|
|||||||
UPLOAD: false
|
UPLOAD: false
|
||||||
CMD_BUILD: |
|
CMD_BUILD: |
|
||||||
pyinstaller -y --add-data $pythonLocation/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile --name extract_otp_secrets_ubuntu src/extract_otp_secrets.py
|
pyinstaller -y --add-data $pythonLocation/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile --name extract_otp_secrets_ubuntu src/extract_otp_secrets.py
|
||||||
- os: ubuntu-24.04-arm
|
# TODO temp disable macos releases to due
|
||||||
TARGET: linux
|
# FileNotFoundError: Icon input file /Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/PyInstaller/bootloader/images/icon-windowed.icns not found
|
||||||
EXE: extract_otp_secrets_ubuntu_arm64
|
# - os: macos-12
|
||||||
ASSET_NAME: extract_otp_secrets${{ needs.create-release.outputs.inline_version }}_linux_arm64_ubuntu_latest
|
|
||||||
ASSET_MIME: application/x-executable
|
|
||||||
UPLOAD: false
|
|
||||||
CMD_BUILD: |
|
|
||||||
pyinstaller -y --add-data $pythonLocation/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile --name extract_otp_secrets_ubuntu_arm64 src/extract_otp_secrets.py
|
|
||||||
- os: macos-15-intel
|
|
||||||
TARGET: macos
|
|
||||||
# https://pyinstaller.org/en/stable/spec-files.html#spec-file-options-for-a-macos-bundle
|
|
||||||
EXE: extract_otp_secrets
|
|
||||||
ASSET_NAME: extract_otp_secrets${{ needs.create-release.outputs.inline_version }}_macos_x86_64
|
|
||||||
DMG: extract_otp_secrets.dmg
|
|
||||||
ASSET_NAME_DMG: extract_otp_secrets${{ needs.create-release.outputs.inline_version }}_macos_x86_64.dmg
|
|
||||||
ASSET_MIME: application/octet-stream
|
|
||||||
UPLOAD: true
|
|
||||||
CMD_BUILD: |
|
|
||||||
VERSION_STR=$(setuptools-git-versioning) COPYRIGHT_YEARS='2020-2025' envsubst < installer/extract_otp_secrets_macos_template.spec > extract_otp_secrets_macos.spec
|
|
||||||
pyinstaller -y extract_otp_secrets_macos.spec
|
|
||||||
installer/build_dmg.sh
|
|
||||||
# Disable WARN: Cannot import pyzbar module. This problem is probably due to the missing zbar shared library.
|
|
||||||
# - os: macos-14
|
|
||||||
# TARGET: macos
|
# TARGET: macos
|
||||||
# # https://pyinstaller.org/en/stable/spec-files.html#spec-file-options-for-a-macos-bundle
|
# # https://pyinstaller.org/en/stable/spec-files.html#spec-file-options-for-a-macos-bundle
|
||||||
# EXE: extract_otp_secrets
|
# EXE: extract_otp_secrets
|
||||||
# ASSET_NAME: extract_otp_secrets${{ needs.create-release.outputs.inline_version }}_macos_arm64
|
# ASSET_NAME: extract_otp_secrets${{ needs.create-release.outputs.inline_version }}_macos_x86_64
|
||||||
# DMG: extract_otp_secrets.dmg
|
# DMG: extract_otp_secrets.dmg
|
||||||
# ASSET_NAME_DMG: extract_otp_secrets${{ needs.create-release.outputs.inline_version }}_macos_arm64.dmg
|
# ASSET_NAME_DMG: extract_otp_secrets${{ needs.create-release.outputs.inline_version }}_macos_x86_64.dmg
|
||||||
# ASSET_MIME: application/octet-stream
|
# ASSET_MIME: application/octet-stream
|
||||||
# UPLOAD: true
|
# UPLOAD: true
|
||||||
# CMD_BUILD: |
|
# CMD_BUILD: |
|
||||||
# VERSION_STR=$(setuptools-git-versioning) COPYRIGHT_YEARS='2020-2025' envsubst < installer/extract_otp_secrets_macos_template.spec > extract_otp_secrets_macos.spec
|
# VERSION_STR=$(setuptools-git-versioning) COPYRIGHT_YEARS='2020-2023' envsubst < installer/extract_otp_secrets_macos_template.spec > extract_otp_secrets_macos.spec
|
||||||
# pyinstaller -y extract_otp_secrets_macos.spec
|
# pyinstaller -y extract_otp_secrets_macos.spec
|
||||||
# installer/build_dmg.sh
|
# installer/build_dmg.sh
|
||||||
steps:
|
steps:
|
||||||
@@ -292,12 +269,12 @@ jobs:
|
|||||||
- name: List Windir
|
- name: List Windir
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
run: ls "$($Env:WinDir)\system32"
|
run: ls "$($Env:WinDir)\system32"
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v4
|
||||||
- name: Set macos macos_python_path
|
- name: Set macos macos_python_path
|
||||||
# TODO use variable for Python version
|
# TODO use variable for Python version
|
||||||
run: echo "macos_python_path=/Library/Frameworks/Python.framework/Versions/3.13" >> $GITHUB_ENV
|
run: echo "macos_python_path=/Library/Frameworks/Python.framework/Versions/3.13" >> $GITHUB_ENV
|
||||||
- name: Set up Python 3.13
|
- name: Set up Python 3.13
|
||||||
uses: actions/setup-python@v6
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: 3.13
|
python-version: 3.13
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@@ -319,7 +296,7 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
mkdir -p build/
|
mkdir -p build/
|
||||||
VERSION_STR=$(setuptools-git-versioning) VERSION_MAJOR=$(cut -d '.' -f 1 <<< "$(setuptools-git-versioning)") VERSION_MINOR=$(cut -d '.' -f 2 <<< "$(setuptools-git-versioning)") VERSION_PATCH=$(echo $(cut -d '.' -f 3 <<< "$(setuptools-git-versioning)") | sed -E -n "s/^([0-9]+).*/\1/p") VERSION_BUILD=$(echo $(cut -d '.' -f 3 <<< "$(setuptools-git-versioning)") | sed -E -n -e"s/^[0-9]+.+/99/p")$(($(git rev-list --count $(git tag | sort -V -r | sed '1!d')..HEAD))) COPYRIGHT_YEARS='2020-2025' envsubst < installer/win_file_version_info_template.txt > build/win_file_version_info.txt
|
VERSION_STR=$(setuptools-git-versioning) VERSION_MAJOR=$(cut -d '.' -f 1 <<< "$(setuptools-git-versioning)") VERSION_MINOR=$(cut -d '.' -f 2 <<< "$(setuptools-git-versioning)") VERSION_PATCH=$(echo $(cut -d '.' -f 3 <<< "$(setuptools-git-versioning)") | sed -E -n "s/^([0-9]+).*/\1/p") VERSION_BUILD=$(echo $(cut -d '.' -f 3 <<< "$(setuptools-git-versioning)") | sed -E -n -e"s/^[0-9]+.+/99/p")$(($(git rev-list --count $(git tag | sort -V -r | sed '1!d')..HEAD))) COPYRIGHT_YEARS='2020-2023' envsubst < installer/win_file_version_info_template.txt > build/win_file_version_info.txt
|
||||||
- name: Build with pyinstaller for ${{ matrix.TARGET }}
|
- name: Build with pyinstaller for ${{ matrix.TARGET }}
|
||||||
env:
|
env:
|
||||||
# Reproducible build: https://pyinstaller.org/en/stable/advanced-topics.html#creating-a-reproducible-build
|
# Reproducible build: https://pyinstaller.org/en/stable/advanced-topics.html#creating-a-reproducible-build
|
||||||
@@ -342,12 +319,12 @@ jobs:
|
|||||||
dist/${{ matrix.EXE }} - < example_export.txt
|
dist/${{ matrix.EXE }} - < example_export.txt
|
||||||
- name: Load Release URL File from release job
|
- name: Load Release URL File from release job
|
||||||
if: startsWith(github.ref, 'refs/tags/v')
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
uses: actions/download-artifact@v5
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: release_url
|
name: release_url
|
||||||
- name: Load Release Id File from release job
|
- name: Load Release Id File from release job
|
||||||
if: startsWith(github.ref, 'refs/tags/v')
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
uses: actions/download-artifact@v5
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: release_id
|
name: release_id
|
||||||
- name: Display structure of files
|
- name: Display structure of files
|
||||||
@@ -390,7 +367,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Load Release Id File from release job
|
- name: Load Release Id File from release job
|
||||||
uses: actions/download-artifact@v5
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: release_id
|
name: release_id
|
||||||
- name: Set meta data
|
- name: Set meta data
|
||||||
|
|||||||
4
Pipfile
4
Pipfile
@@ -4,14 +4,14 @@ verify_ssl = true
|
|||||||
name = "pypi"
|
name = "pypi"
|
||||||
|
|
||||||
[packages]
|
[packages]
|
||||||
colorama = "0.4.6"
|
colorama = ">=0.4.6"
|
||||||
opencv-contrib-python = "*"
|
opencv-contrib-python = "*"
|
||||||
# for macOS: opencv-contrib-python = "<=4.7.0"
|
# for macOS: opencv-contrib-python = "<=4.7.0"
|
||||||
pillow = "*"
|
pillow = "*"
|
||||||
pyzbar = "*"
|
pyzbar = "*"
|
||||||
protobuf = "*"
|
protobuf = "*"
|
||||||
qrcode = "*"
|
qrcode = "*"
|
||||||
qreader = "1.3.2"
|
qreader = "<2.0.0"
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
build = "*"
|
build = "*"
|
||||||
|
|||||||
1008
Pipfile.lock
generated
1008
Pipfile.lock
generated
File diff suppressed because it is too large
Load Diff
28
README.md
28
README.md
@@ -5,7 +5,7 @@
|
|||||||

|

|
||||||
[](https://github.com/scito/extract_otp_secrets/blob/master/LICENSE)
|
[](https://github.com/scito/extract_otp_secrets/blob/master/LICENSE)
|
||||||
[](https://github.com/scito/extract_otp_secrets/releases/latest)
|
[](https://github.com/scito/extract_otp_secrets/releases/latest)
|
||||||

|

|
||||||
[](https://hub.docker.com/repository/docker/scit0/extract_otp_secrets/general)
|
[](https://hub.docker.com/repository/docker/scit0/extract_otp_secrets/general)
|
||||||
[](https://github.com/scito/extract_otp_secrets/releases/latest)
|
[](https://github.com/scito/extract_otp_secrets/releases/latest)
|
||||||
[](https://github.com/scito/extract_otp_secrets/releases/latest)
|
[](https://github.com/scito/extract_otp_secrets/releases/latest)
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
[](https://stand-with-ukraine.pp.ua)
|
[](https://stand-with-ukraine.pp.ua)
|
||||||
<!-- 
|
<!-- 
|
||||||
[](https://github.com/scito/extract_otp_secrets/blob/master/Pipfile.lock)
|
[](https://github.com/scito/extract_otp_secrets/blob/master/Pipfile.lock)
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!-- [](https://GitHub.com/scito/extract_otp_secrets/releases/) -->
|
<!-- [](https://GitHub.com/scito/extract_otp_secrets/releases/) -->
|
||||||
|
|
||||||
@@ -87,7 +87,6 @@ The secrets can be exported to JSON or CSV, or printed as QR codes to console or
|
|||||||
- [Problems and Troubleshooting](#problems-and-troubleshooting)
|
- [Problems and Troubleshooting](#problems-and-troubleshooting)
|
||||||
- [Windows error message](#windows-error-message)
|
- [Windows error message](#windows-error-message)
|
||||||
- [Related projects](#related-projects)
|
- [Related projects](#related-projects)
|
||||||
- [Third party documentation](#third-party-documentation)
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
## Download and run binary executable (🆕 since v2.1)
|
## Download and run binary executable (🆕 since v2.1)
|
||||||
@@ -366,7 +365,7 @@ python extract_otp_secrets.py = < example_export.png</pre>
|
|||||||
* extract_otp_secrets_macos_x86_64 (optional [libzbar](#installation-of-optional-shared-system-libraries-recommended) needs to be installed manually if needed)
|
* extract_otp_secrets_macos_x86_64 (optional [libzbar](#installation-of-optional-shared-system-libraries-recommended) needs to be installed manually if needed)
|
||||||
* extract_otp_secrets_macos_x86_64.dmg N/A, see [why](#macos)
|
* extract_otp_secrets_macos_x86_64.dmg N/A, see [why](#macos)
|
||||||
* extract_otp_secrets_macos_x86_64.pkg N/A, see [why](#macos)
|
* extract_otp_secrets_macos_x86_64.pkg N/A, see [why](#macos)
|
||||||
* Prebuilt Docker images provided for amd64 and arm64 on [Docker Hub](https://hub.docker.com/repository/docker/scit0/extract_otp_secrets) and [GitHub Packages](https://github.com/users/scito/packages/container/package/extract_otp_secrets) (🆕 since v2.0)
|
* Prebuilt Docker images provided for amd64 and arm64 (🆕 since v2.0)
|
||||||
* Many ways to run the script:
|
* Many ways to run the script:
|
||||||
* Native Python
|
* Native Python
|
||||||
* pipenv
|
* pipenv
|
||||||
@@ -380,7 +379,7 @@ python extract_otp_secrets.py = < example_export.png</pre>
|
|||||||
* macOS
|
* macOS
|
||||||
* Windows
|
* Windows
|
||||||
* Uses UTF-8 on all platforms
|
* Uses UTF-8 on all platforms
|
||||||
* Supports Python >= 3.9
|
* Supports Python >= 3.8
|
||||||
* Installation of shared system libraries is optional (🆕 since v2.3)
|
* Installation of shared system libraries is optional (🆕 since v2.3)
|
||||||
* Provides a debug mode (-d) for analyzing import problems
|
* Provides a debug mode (-d) for analyzing import problems
|
||||||
* Written in modern Python using type hints and following best practices
|
* Written in modern Python using type hints and following best practices
|
||||||
@@ -545,19 +544,19 @@ Prebuilt docker images are available for amd64 and arm64 architectures on [Docke
|
|||||||
Extracting from an QR image file:
|
Extracting from an QR image file:
|
||||||
|
|
||||||
```
|
```
|
||||||
curl -s https://raw.githubusercontent.com/scito/extract_otp_secrets/master/example_export.png | docker run --network none --pull always -i --rm -v "$(pwd)":/files:ro docker.io/scit0/extract_otp_secrets =
|
curl -s https://raw.githubusercontent.com/scito/extract_otp_secrets/master/example_export.png | docker run --network none --pull always -i --rm -v "$(pwd)":/files:ro scit0/extract_otp_secrets =
|
||||||
```
|
```
|
||||||
|
|
||||||
Capturing from camera in GUI window (X Window system required on host):
|
Capturing from camera in GUI window (X Window system required on host):
|
||||||
|
|
||||||
```
|
```
|
||||||
docker run --network none --pull always --rm -v "$(pwd)":/files:ro -i --device="/dev/video0:/dev/video0" --env="DISPLAY" -v /tmp/.X11-unix:/tmp/.X11-unix:ro docker.io/scit0/extract_otp_secrets
|
docker run --network none --pull always --rm -v "$(pwd)":/files:ro -i --device="/dev/video0:/dev/video0" --env="DISPLAY" -v /tmp/.X11-unix:/tmp/.X11-unix:ro scit0/extract_otp_secrets
|
||||||
```
|
```
|
||||||
|
|
||||||
If only text processing is required, there is a small Image based on Alpine Linux:
|
If only text processing is required, there is a small Image based on Alpine Linux:
|
||||||
|
|
||||||
```
|
```
|
||||||
curl -s https://raw.githubusercontent.com/scito/extract_otp_secrets/master/example_export.txt | docker run --network none --pull always -i --rm -v "$(pwd)":/files:ro docker.io/scit0/extract_otp_secrets:latest-only-txt -
|
curl -s https://raw.githubusercontent.com/scito/extract_otp_secrets/master/example_export.txt | docker run --network none --pull always -i --rm -v "$(pwd)":/files:ro scit0/extract_otp_secrets:latest-only-txt -
|
||||||
```
|
```
|
||||||
|
|
||||||
Docker image from GitHub:
|
Docker image from GitHub:
|
||||||
@@ -569,11 +568,11 @@ curl -s https://raw.githubusercontent.com/scito/extract_otp_secrets/master/examp
|
|||||||
|
|
||||||
### More docker examples
|
### More docker examples
|
||||||
|
|
||||||
docker run --network none --pull always --rm -v "$(pwd)":/files:ro docker.io/scit0/extract_otp_secrets example_export.png
|
docker run --network none --pull always --rm -v "$(pwd)":/files:ro scit0/extract_otp_secrets example_export.png
|
||||||
|
|
||||||
docker run --network none --pull always --rm -i -v "$(pwd)":/files:ro docker.io/scit0/extract_otp_secrets:latest-only-txt - < example_export.txt
|
docker run --network none --pull always --rm -i -v "$(pwd)":/files:ro scit0/extract_otp_secrets_only_txt - < example_export.txt
|
||||||
|
|
||||||
cat example_export.txt | docker run --network none --pull always --rm -i -v "$(pwd)":/files:ro docker.io/scit0/extract_otp_secrets:latest-only-txt - -c - > example_out.csv
|
cat example_export.txt | docker run --network none --pull always --rm -i -v "$(pwd)":/files:ro scit0/extract_otp_secrets:latest_only_txt - -c - > example_out.csv
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
|
|
||||||
@@ -729,7 +728,7 @@ Command for regeneration of Python code from proto3 message definition file (onl
|
|||||||
|
|
||||||
protoc --plugin=protoc-gen-mypy=path/to/protoc-gen-mypy --python_out=src/protobuf_generated_python --mypy_out=src/protobuf_generated_python src/google_auth.proto
|
protoc --plugin=protoc-gen-mypy=path/to/protoc-gen-mypy --python_out=src/protobuf_generated_python --mypy_out=src/protobuf_generated_python src/google_auth.proto
|
||||||
|
|
||||||
The generated protobuf Python code was generated by protoc 32.1 (https://github.com/protocolbuffers/protobuf/releases/tag/v32.1).
|
The generated protobuf Python code was generated by protoc 29.2 (https://github.com/protocolbuffers/protobuf/releases/tag/v29.2).
|
||||||
|
|
||||||
For Python type hint generation the [mypy-protobuf](https://github.com/nipunn1313/mypy-protobuf) package is used.
|
For Python type hint generation the [mypy-protobuf](https://github.com/nipunn1313/mypy-protobuf) package is used.
|
||||||
|
|
||||||
@@ -780,11 +779,6 @@ FileNotFoundError: Could not find module 'libiconv.dll' (or one of its dependenc
|
|||||||
* [Android OTP Extractor](https://github.com/puddly/android-otp-extractor) can extract your tokens from popular Android OTP apps and export them in a standard format or just display them as QR codes for easy importing. [Requires a _rooted_ Android phone.]
|
* [Android OTP Extractor](https://github.com/puddly/android-otp-extractor) can extract your tokens from popular Android OTP apps and export them in a standard format or just display them as QR codes for easy importing. [Requires a _rooted_ Android phone.]
|
||||||
* [Google Authenticator secret extractor](https://github.com/krissrex/google-authenticator-exporter) is similar project written in JavaScript. It also extracts otp secrets from Google Authenticator.
|
* [Google Authenticator secret extractor](https://github.com/krissrex/google-authenticator-exporter) is similar project written in JavaScript. It also extracts otp secrets from Google Authenticator.
|
||||||
|
|
||||||
## Third party documentation
|
|
||||||
|
|
||||||
* [TOTP Secret Extraction from QR codes (medium.com)](https://cavalloj.medium.com/totp-secret-extraction-from-qr-codes-ee097b4c687f)
|
|
||||||
* [Google Authenticator: OTP-Secrets auslesen (stadt-bremerhaven.de)](https://stadt-bremerhaven.de/google-authenticator-otp-secrets-auslesen/)
|
|
||||||
|
|
||||||
***
|
***
|
||||||
|
|
||||||
# #StandWithUkraine 🇺🇦
|
# #StandWithUkraine 🇺🇦
|
||||||
|
|||||||
@@ -16,8 +16,7 @@ COPY requirements*.txt src/ run_pytest.sh pytest.ini tests/ example_*.txt exampl
|
|||||||
|
|
||||||
ARG RUN_TESTS=true
|
ARG RUN_TESTS=true
|
||||||
|
|
||||||
RUN uname -a \
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
&& apt-get update && apt-get install -y --no-install-recommends \
|
|
||||||
libgl1 \
|
libgl1 \
|
||||||
libglib2.0-0 \
|
libglib2.0-0 \
|
||||||
libsm6 \
|
libsm6 \
|
||||||
@@ -32,6 +31,6 @@ WORKDIR /files
|
|||||||
|
|
||||||
ENTRYPOINT ["python", "/extract/extract_otp_secrets.py"]
|
ENTRYPOINT ["python", "/extract/extract_otp_secrets.py"]
|
||||||
|
|
||||||
LABEL org.opencontainers.image.source=https://github.com/scito/extract_otp_secrets
|
LABEL org.opencontainers.image.source https://github.com/scito/extract_otp_secrets
|
||||||
LABEL org.opencontainers.image.license=GPL-3.0+
|
LABEL org.opencontainers.image.license GPL-3.0+
|
||||||
LABEL maintainer="Scito https://scito.ch, https://github.com/scito"
|
LABEL maintainer="Scito https://scito.ch, https://github.com/scito"
|
||||||
|
|||||||
@@ -17,8 +17,7 @@ COPY requirements*.txt src/ run_pytest.sh pytest.ini tests/ example_*.txt exampl
|
|||||||
|
|
||||||
ARG RUN_TESTS=true
|
ARG RUN_TESTS=true
|
||||||
|
|
||||||
RUN uname -a \
|
RUN apk add --no-cache \
|
||||||
&& apk add --no-cache \
|
|
||||||
jpeg \
|
jpeg \
|
||||||
zlib \
|
zlib \
|
||||||
&& echo "Arch: $(apk --print-arch)" \
|
&& echo "Arch: $(apk --print-arch)" \
|
||||||
@@ -44,6 +43,6 @@ WORKDIR /files
|
|||||||
|
|
||||||
ENTRYPOINT ["python", "/extract/extract_otp_secrets.py"]
|
ENTRYPOINT ["python", "/extract/extract_otp_secrets.py"]
|
||||||
|
|
||||||
LABEL org.opencontainers.image.source=https://github.com/scito/extract_otp_secrets
|
LABEL org.opencontainers.image.source https://github.com/scito/extract_otp_secrets
|
||||||
LABEL org.opencontainers.image.license=GPL-3.0+
|
LABEL org.opencontainers.image.license GPL-3.0+
|
||||||
LABEL maintainer="Scito https://scito.ch, https://github.com/scito"
|
LABEL maintainer="Scito https://scito.ch, https://github.com/scito"
|
||||||
|
|||||||
@@ -53,6 +53,5 @@ Generate from file: README.md
|
|||||||
- [Problems and Troubleshooting](#problems-and-troubleshooting)
|
- [Problems and Troubleshooting](#problems-and-troubleshooting)
|
||||||
- [Windows error message](#windows-error-message)
|
- [Windows error message](#windows-error-message)
|
||||||
- [Related projects](#related-projects)
|
- [Related projects](#related-projects)
|
||||||
- [Third party documentation](#third-party-documentation)
|
|
||||||
|
|
||||||
Table of contents generated.
|
Table of contents generated.
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ requires = [
|
|||||||
"pip",
|
"pip",
|
||||||
"nuitka",
|
"nuitka",
|
||||||
# https://setuptools-git-versioning.readthedocs.io/en/latest/differences.html
|
# https://setuptools-git-versioning.readthedocs.io/en/latest/differences.html
|
||||||
"setuptools>=80.0.0",
|
"setuptools>=64.0.0",
|
||||||
"setuptools-git-versioning",
|
"setuptools-git-versioning",
|
||||||
"wheel>=0.37.0",
|
"wheel>=0.37.0",
|
||||||
]
|
]
|
||||||
@@ -21,12 +21,12 @@ classifiers = [
|
|||||||
"Topic :: Utilities",
|
"Topic :: Utilities",
|
||||||
"Topic :: Security",
|
"Topic :: Security",
|
||||||
"Topic :: Multimedia :: Graphics :: Capture :: Digital Camera",
|
"Topic :: Multimedia :: Graphics :: Capture :: Digital Camera",
|
||||||
|
"Programming Language :: Python :: 3.8",
|
||||||
"Programming Language :: Python :: 3.9",
|
"Programming Language :: Python :: 3.9",
|
||||||
"Programming Language :: Python :: 3.10",
|
"Programming Language :: Python :: 3.10",
|
||||||
"Programming Language :: Python :: 3.11",
|
"Programming Language :: Python :: 3.11",
|
||||||
"Programming Language :: Python :: 3.12",
|
"Programming Language :: Python :: 3.12",
|
||||||
"Programming Language :: Python :: 3.13",
|
"Programming Language :: Python :: 3.13",
|
||||||
"Programming Language :: Python :: 3.14",
|
|
||||||
"Intended Audience :: End Users/Desktop",
|
"Intended Audience :: End Users/Desktop",
|
||||||
"Intended Audience :: Developers",
|
"Intended Audience :: Developers",
|
||||||
"Intended Audience :: System Administrators",
|
"Intended Audience :: System Administrators",
|
||||||
@@ -55,7 +55,7 @@ license = {text = "GNU General Public License v3 (GPLv3)"}
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
authors = [{name = "scito", email = "info@scito.ch"}]
|
authors = [{name = "scito", email = "info@scito.ch"}]
|
||||||
maintainers = [{name = "scito", email = "info@scito.ch"}]
|
maintainers = [{name = "scito", email = "info@scito.ch"}]
|
||||||
requires-python = ">=3.9, <4"
|
requires-python = ">=3.8, <4"
|
||||||
scripts = {extract_otp_secrets = "extract_otp_secrets:sys_main"}
|
scripts = {extract_otp_secrets = "extract_otp_secrets:sys_main"}
|
||||||
urls = {Project-URL = "https://github.com/scito/extract_otp_secrets", Bug-Reports = "https://github.com/scito/extract_otp_secrets/issues", Source = "https://github.com/scito/extract_otp_secrets"}
|
urls = {Project-URL = "https://github.com/scito/extract_otp_secrets", Bug-Reports = "https://github.com/scito/extract_otp_secrets/issues", Source = "https://github.com/scito/extract_otp_secrets"}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
name = extract_otp_secrets
|
name = extract_otp_secrets
|
||||||
|
|
||||||
[options]
|
[options]
|
||||||
python_requires = >=3.9, <4
|
python_requires = >=3.8, <4
|
||||||
py_modules = extract_otp_secrets, protobuf_generated_python.google_auth_pb2
|
py_modules = extract_otp_secrets, protobuf_generated_python.google_auth_pb2
|
||||||
package_dir =
|
package_dir =
|
||||||
=src
|
=src
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ from typing import (Any, Final, List, Optional, Sequence, TextIO, Tuple,
|
|||||||
TypedDict, Union)
|
TypedDict, Union)
|
||||||
|
|
||||||
import colorama
|
import colorama
|
||||||
from qrcode import QRCode
|
from qrcode import QRCode # type: ignore
|
||||||
|
|
||||||
import protobuf_generated_python.google_auth_pb2 as pb
|
import protobuf_generated_python.google_auth_pb2 as pb
|
||||||
|
|
||||||
@@ -380,22 +380,14 @@ def extract_otps_from_camera(args: Args) -> Otps:
|
|||||||
qr_mode = next_qr_mode(qr_mode)
|
qr_mode = next_qr_mode(qr_mode)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
cv2_print_text(img, f"Mode: {qr_mode.name} (Hit SPACE to change)", 0, TextPosition.LEFT, FONT_COLOR, 20)
|
||||||
cv2_print_text(img, f"Mode: {qr_mode.name} (Hit SPACE to change)", 0, TextPosition.LEFT, FONT_COLOR, 20)
|
cv2_print_text(img, "Press ESC to quit", 1, TextPosition.LEFT, FONT_COLOR, 17)
|
||||||
cv2_print_text(img, "Press ESC to quit", 1, TextPosition.LEFT, FONT_COLOR, 17)
|
cv2_print_text(img, "Press c,j,k,t,u to save as csv/json/keepass/txt/urls file", 2, TextPosition.LEFT, FONT_COLOR, None)
|
||||||
cv2_print_text(img, "Press c,j,k,t,u to save as csv/json/keepass/txt/urls file", 2, TextPosition.LEFT, FONT_COLOR, None)
|
|
||||||
|
|
||||||
cv2_print_text(img, f"{len(otp_urls)} QR code{'s'[:len(otp_urls) != 1]} captured", 0, TextPosition.RIGHT, FONT_COLOR)
|
cv2_print_text(img, f"{len(otp_urls)} QR code{'s'[:len(otp_urls) != 1]} captured", 0, TextPosition.RIGHT, FONT_COLOR)
|
||||||
cv2_print_text(img, f"{len(otps)} otp{'s'[:len(otps) != 1]} extracted", 1, TextPosition.RIGHT, FONT_COLOR)
|
cv2_print_text(img, f"{len(otps)} otp{'s'[:len(otps) != 1]} extracted", 1, TextPosition.RIGHT, FONT_COLOR)
|
||||||
|
|
||||||
cv2.imshow(WINDOW_NAME, img)
|
cv2.imshow(WINDOW_NAME, img)
|
||||||
except cv2.error as e:
|
|
||||||
# Workaround due to Gtk2/3 problem, see #444
|
|
||||||
if e.code == cv2.Error.StsNullPtr:
|
|
||||||
# Window closed
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
raise e
|
|
||||||
|
|
||||||
quit, qr_mode = cv2_handle_pressed_keys(qr_mode, otps)
|
quit, qr_mode = cv2_handle_pressed_keys(qr_mode, otps)
|
||||||
if quit:
|
if quit:
|
||||||
@@ -457,7 +449,7 @@ def cv2_handle_pressed_keys(qr_mode: QRMode, otps: Otps) -> Tuple[bool, QRMode]:
|
|||||||
defaultextension='.csv',
|
defaultextension='.csv',
|
||||||
filetypes=[('CSV', '*.csv'), ('All', '*.*')]
|
filetypes=[('CSV', '*.csv'), ('All', '*.*')]
|
||||||
)
|
)
|
||||||
tk_root.update() # noqa: F821 # workaround flake8 false positive, tk_root is defined globally
|
tk_root.update()
|
||||||
if len(file_name) > 0:
|
if len(file_name) > 0:
|
||||||
write_csv(file_name, otps)
|
write_csv(file_name, otps)
|
||||||
elif (key == ord('j') or key == ord('J')) and is_not_headless():
|
elif (key == ord('j') or key == ord('J')) and is_not_headless():
|
||||||
@@ -469,7 +461,7 @@ def cv2_handle_pressed_keys(qr_mode: QRMode, otps: Otps) -> Tuple[bool, QRMode]:
|
|||||||
defaultextension='.json',
|
defaultextension='.json',
|
||||||
filetypes=[('JSON', '*.json'), ('All', '*.*')]
|
filetypes=[('JSON', '*.json'), ('All', '*.*')]
|
||||||
)
|
)
|
||||||
tk_root.update() # noqa: F821 # workaround flake8 false positive, tk_root is defined globally
|
tk_root.update()
|
||||||
if len(file_name) > 0:
|
if len(file_name) > 0:
|
||||||
write_json(file_name, otps)
|
write_json(file_name, otps)
|
||||||
elif (key == ord('k') or key == ord('K')) and is_not_headless():
|
elif (key == ord('k') or key == ord('K')) and is_not_headless():
|
||||||
@@ -481,7 +473,7 @@ def cv2_handle_pressed_keys(qr_mode: QRMode, otps: Otps) -> Tuple[bool, QRMode]:
|
|||||||
defaultextension='.csv',
|
defaultextension='.csv',
|
||||||
filetypes=[('CSV', '*.csv'), ('All', '*.*')]
|
filetypes=[('CSV', '*.csv'), ('All', '*.*')]
|
||||||
)
|
)
|
||||||
tk_root.update() # noqa: F821 # workaround flake8 false positive, tk_root is defined globally
|
tk_root.update()
|
||||||
if len(file_name) > 0:
|
if len(file_name) > 0:
|
||||||
write_keepass_csv(file_name, otps)
|
write_keepass_csv(file_name, otps)
|
||||||
elif (key == ord('t') or key == ord('T')) and is_not_headless():
|
elif (key == ord('t') or key == ord('T')) and is_not_headless():
|
||||||
@@ -493,7 +485,7 @@ def cv2_handle_pressed_keys(qr_mode: QRMode, otps: Otps) -> Tuple[bool, QRMode]:
|
|||||||
defaultextension='.txt',
|
defaultextension='.txt',
|
||||||
filetypes=[('Text', '*.txt'), ('All', '*.*')]
|
filetypes=[('Text', '*.txt'), ('All', '*.*')]
|
||||||
)
|
)
|
||||||
tk_root.update() # noqa: F821 # workaround flake8 false positive, tk_root is defined globally
|
tk_root.update()
|
||||||
if len(file_name) > 0:
|
if len(file_name) > 0:
|
||||||
write_txt(file_name, otps, True)
|
write_txt(file_name, otps, True)
|
||||||
elif (key == ord('u') or key == ord('U')) and is_not_headless():
|
elif (key == ord('u') or key == ord('U')) and is_not_headless():
|
||||||
@@ -505,13 +497,15 @@ def cv2_handle_pressed_keys(qr_mode: QRMode, otps: Otps) -> Tuple[bool, QRMode]:
|
|||||||
defaultextension='.txt',
|
defaultextension='.txt',
|
||||||
filetypes=[('Text', '*.txt'), ('All', '*.*')]
|
filetypes=[('Text', '*.txt'), ('All', '*.*')]
|
||||||
)
|
)
|
||||||
tk_root.update() # noqa: F821 # workaround flake8 false positive, tk_root is defined globally
|
tk_root.update()
|
||||||
if len(file_name) > 0:
|
if len(file_name) > 0:
|
||||||
write_urls(file_name, otps)
|
write_urls(file_name, otps)
|
||||||
elif key == 32:
|
elif key == 32:
|
||||||
qr_mode = next_valid_qr_mode(qr_mode, zbar_available)
|
qr_mode = next_valid_qr_mode(qr_mode, zbar_available)
|
||||||
if verbose >= LogLevel.MORE_VERBOSE: print(f"QR reading mode: {qr_mode}")
|
if verbose >= LogLevel.MORE_VERBOSE: print(f"QR reading mode: {qr_mode}")
|
||||||
# Do not check for invisible window due to Gtk2/3 problem, see #444
|
if cv2.getWindowProperty(WINDOW_NAME, cv2.WND_PROP_VISIBLE) < 1:
|
||||||
|
# Window close clicked
|
||||||
|
quit = True
|
||||||
return quit, qr_mode
|
return quit, qr_mode
|
||||||
|
|
||||||
|
|
||||||
@@ -710,7 +704,7 @@ def save_qr_image_file(otp_url: OtpUrl, name: str) -> None:
|
|||||||
qr.add_data(otp_url)
|
qr.add_data(otp_url)
|
||||||
img = qr.make_image(fill_color='black', back_color='white')
|
img = qr.make_image(fill_color='black', back_color='white')
|
||||||
if verbose: print(f"Saving to {name}")
|
if verbose: print(f"Saving to {name}")
|
||||||
img.save(name) # type: ignore
|
img.save(name)
|
||||||
|
|
||||||
|
|
||||||
def print_qr(otp_url: str, out: Optional[TextIO] = None) -> None:
|
def print_qr(otp_url: str, out: Optional[TextIO] = None) -> None:
|
||||||
@@ -843,7 +837,7 @@ def check_file_exists(filename: str) -> None:
|
|||||||
def has_no_otps_show_warning(otps: Otps) -> bool:
|
def has_no_otps_show_warning(otps: Otps) -> bool:
|
||||||
if len(otps) == 0:
|
if len(otps) == 0:
|
||||||
tkinter.messagebox.showinfo(title="No data", message="There are no otp secrets to write")
|
tkinter.messagebox.showinfo(title="No data", message="There are no otp secrets to write")
|
||||||
tk_root.update() # dispose dialog # noqa: F821 # workaround flake8 false positive, tk_root is defined globally
|
tk_root.update() # dispose dialog
|
||||||
return len(otps) == 0
|
return len(otps) == 0
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||||
# NO CHECKED-IN PROTOBUF GENCODE
|
# NO CHECKED-IN PROTOBUF GENCODE
|
||||||
# source: google_auth.proto
|
# source: google_auth.proto
|
||||||
# Protobuf Python Version: 6.32.1
|
# Protobuf Python Version: 5.29.2
|
||||||
"""Generated protocol buffer code."""
|
"""Generated protocol buffer code."""
|
||||||
from google.protobuf import descriptor as _descriptor
|
from google.protobuf import descriptor as _descriptor
|
||||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||||
@@ -11,9 +11,9 @@ from google.protobuf import symbol_database as _symbol_database
|
|||||||
from google.protobuf.internal import builder as _builder
|
from google.protobuf.internal import builder as _builder
|
||||||
_runtime_version.ValidateProtobufRuntimeVersion(
|
_runtime_version.ValidateProtobufRuntimeVersion(
|
||||||
_runtime_version.Domain.PUBLIC,
|
_runtime_version.Domain.PUBLIC,
|
||||||
6,
|
5,
|
||||||
32,
|
29,
|
||||||
1,
|
2,
|
||||||
'',
|
'',
|
||||||
'google_auth.proto'
|
'google_auth.proto'
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user