Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
3a29727
ci(workflows): try to fix poetry build error
lasnown Oct 20, 2025
0cce397
ci(workflows): revert publish release action version
lasnown Oct 20, 2025
cedcec0
ci(workflows): fix typo in version num
lasnown Oct 20, 2025
a786643
0.1.0-rc.1
Oct 20, 2025
a70b194
ci(workflows): fix triggers
lasnown Oct 20, 2025
e493950
merge: remote branch into local
lasnown Oct 20, 2025
8449489
feat(main): add version option to c2pie command
lasnown Oct 21, 2025
05da8f7
ci(workflows): fix permissions for linting and testing job
lasnown Oct 21, 2025
f5ecd25
0.1.0-rc.2
Oct 21, 2025
2340f14
ci(workflows): use gh_token again
lasnown Oct 21, 2025
fdc800b
0.1.0-rc.2
Oct 21, 2025
caeb87f
chore(tests): rename test credentials files
lasnown Oct 21, 2025
2d74b9d
docs(readme): swap sections' places
lasnown Oct 21, 2025
58c2bca
config: rename private key and certificate variables
lasnown Oct 21, 2025
3010b80
config: fix remaining redundant variables paths and names
lasnown Oct 21, 2025
3a62380
style(readme): remove wrapping file paths in brackets
lasnown Oct 21, 2025
9c5666a
fix: make signature schema configurable
SupremeSoviet Oct 21, 2025
ca7811b
infra: add first part of draft workflow to test readme steps in Docker
fpandyz Oct 21, 2025
4603e3f
infra: add missing exports
fpandyz Oct 21, 2025
9d27cf7
infra: add verification steps
fpandyz Oct 21, 2025
bec0f2f
infra: fix image URL
fpandyz Oct 21, 2025
1185185
infra: use corrected certificate chain generation algorithm
lasnown Oct 22, 2025
1eb336a
infra: fix missing slash
lasnown Oct 22, 2025
539c144
infra: fix redundant action vars names
lasnown Oct 22, 2025
d61c3e3
chore: merge renaming changes from "docs/update-readme" to fix workflow
lasnown Oct 22, 2025
9ebfa3c
docs(readme): add signing and validating with Docker containers
lasnown Oct 22, 2025
5f31999
config(semantic-releases): add alpha prereleases group
lasnown Oct 22, 2025
df5fac4
docs(readme): fix links
lasnown Oct 22, 2025
f5fd292
infra: use action for installing c2patool with automated caching
lasnown Oct 22, 2025
b547982
docs(readme): add empty line dividers
lasnown Oct 22, 2025
1bab57b
docs(readme): add note on customizing signature subject info
lasnown Oct 22, 2025
be8a7d4
infra: add failing workflow if invalid signature
lasnown Oct 22, 2025
3695d70
infra: add logging
lasnown Oct 22, 2025
7c4a230
docs(readme): fix env vars filepaths to correspond with the example
lasnown Oct 23, 2025
4b49d54
docs(readme): fix numbers in list
lasnown Oct 23, 2025
2782d1d
docs(readme): change days amount in signing certificate command
lasnown Oct 23, 2025
80c4086
infra: add pdf signing and validation
lasnown Oct 23, 2025
a896642
docs(readme): update python version for test container image
lasnown Oct 23, 2025
cf468f3
docs: move contributing to its separate file
lasnown Oct 23, 2025
b7a80ff
docs(readme): add instructions for switching between terminals
lasnown Oct 23, 2025
70633e5
docs(readme): clarify file placement
lasnown Oct 23, 2025
4d241cc
infra: rename jobs in test-readme-steps.yml workflow
fpandyz Oct 23, 2025
c7c8d73
docs(readme): apply suggestions after readme-testing
lasnown Oct 23, 2025
ca25434
docs(readme): bring test coverage score up to date
github-actions[bot] Oct 23, 2025
e9eae9b
Merge branch 'release/0.1.0' into feature/add-workflow-to-test-readme…
lasnown Oct 23, 2025
13746d5
docs(readme): add notes on current package status
lasnown Oct 23, 2025
f949a22
feature: add workflow to test readme steps
lasnown Oct 23, 2025
06c94a2
0.1.0-rc.3
Oct 23, 2025
5bf4c39
config(pyproject): bump development status
lasnown Oct 23, 2025
ae50ad7
chore(changelog): empty changelog
lasnown Oct 23, 2025
42d1528
feat: implement JPEG and PDF C2PA signing functionality
lasnown Oct 23, 2025
f346fb3
Merge branch 'master' into develop
lasnown Oct 27, 2025
61b4b7b
fix(ci): #53: change poetry version into install poetry step so that …
aasheptunov Apr 23, 2026
b676c92
fix(gitattributes): #51: correcting end of lines, change target of .g…
aasheptunov Apr 23, 2026
68ba654
feat(actions-assertion): #55: add implementation of action assertion …
aasheptunov May 6, 2026
88f9abe
feat(hash-uri-map): #56: add generate hash uri map function (#58)
aasheptunov May 6, 2026
57430c5
fix: #62: fix for the library build auto update (#63)
aasheptunov May 6, 2026
a4dbac5
docs(readme): bring test coverage score up to date
github-actions[bot] May 6, 2026
c8524ab
feat: #60: update the library to C2PA 2.4 (#61)
aasheptunov May 6, 2026
ea80072
feat(jumbf-parser): #46: add manifest parsing functionality (#65)
JDRkow May 7, 2026
688380d
feat(time-stamps): #59: adding timestamp functionality (#64)
aasheptunov May 13, 2026
853361f
feat: #69: removal of Python 3.9 from the library's supported version…
aasheptunov May 18, 2026
30f672e
feat(ingredient-assertion): #47: adding implementation of ingredient …
aasheptunov May 20, 2026
00fc78e
docs(readme): bring test coverage score up to date
github-actions[bot] May 20, 2026
02ab4c2
ci: #75: sync workflows with items api in preparation for the article…
lasnown May 20, 2026
0572736
feat: #89: add dependency check workflow (#90)
lasnown Jun 2, 2026
cdbf950
feat: #80: investigation and correction of hash mismatches during the…
aasheptunov Jun 4, 2026
019c863
feat: #95: add e2e tests to verify multiple signatures on single file…
aasheptunov Jun 4, 2026
8b016b5
feat: #79: add a thumbnail ingredient assertion (#94)
aasheptunov Jun 4, 2026
81da995
refactor(pyproject): #97: update pyproject.toml to get rid of outdate…
lasnown Jun 4, 2026
ef7c5da
feat: #84: add APP11 segment division logic in case of overflow (#99)
aasheptunov Jun 5, 2026
1713fa9
test: #100: rollback of error in deleting e2e tests after resolving m…
aasheptunov Jun 5, 2026
fc16088
feat: #100: fixing the manifest store error when a single file has mu…
aasheptunov Jun 5, 2026
e1636d4
docs: #103: update project readme after applying changes (#104)
aasheptunov Jun 5, 2026
d60a3a0
build: #106: update build backend to use poetry core as a more modern…
lasnown Jun 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@ ENV RUSTUP_HOME=/home/vscode/.rustup \
RUN curl -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable && \
cargo install c2patool --locked

COPY . /c2pie/

WORKDIR c2pie
# If the configuration files are not copied, it will not
# be possible to install dependencies using poetry install
COPY . /workspaces/c2pie/
WORKDIR /workspaces/c2pie/

# --no-interaction - installing in non-interactive mode
# --no-ansi - disable ANSI codes
RUN poetry config virtualenvs.create false \
&& poetry lock && poetry install --no-interaction --no-ansi
20 changes: 14 additions & 6 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// For format details, see https://aka.ms/devcontainer.json
{
"name": "Existing Dockerfile",
"build": {
"context": "..",
"dockerfile": "Dockerfile"
},
"name": "Existing Dockerfile",
"build": {
"context": "..",
"dockerfile": "Dockerfile"
},
"customizations": {
"vscode": {
"extensions": [
Expand All @@ -25,9 +25,17 @@
"source.fixAll.ruff": "explicit",
"source.organizeImports.ruff": "explicit"
},
"python.analysis.exclude": [
"**/__pycache__",
"**/.*"
]
},
"files.associations": {
".env-example": "dotenv" // For syntax highlighting
},
"editor.tabSize": 4
}
}
}
},
"runArgs": [
"--env-file", "${localWorkspaceFolder}/.env",
Expand Down
5 changes: 5 additions & 0 deletions .env-example
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
C2PIE_PRIVATE_KEY_FILE=tests/credentials/private-key.pem
C2PIE_CERTIFICATE_CHAIN_FILE=tests/credentials/certificate-chain.pub
C2PIE_TSA_URL=http://timestamp.digicert.com
C2PIE_TSA_REQUIRED=true
C2PIE_TSA_LOG_DIR=/var/log/c2pie/tsa

# Paths to test files
TEST_PDF_PATH=tests/test_files/test_doc.pdf
TEST_IMAGE_PATH=tests/test_files/test_image.jpg
2 changes: 1 addition & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
@@ -1 +1 @@
*.sh eol=lf
* text=auto eol=lf
257 changes: 257 additions & 0 deletions .github/workflows/calculate-tests-coverage-on-pull-request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
name: Calculate and Update Tests Coverage

on:
pull_request:
types: [opened, synchronize, reopened]

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
calculate-coverage:
runs-on: ubuntu-24.04
steps:
- name: Check out the repo
uses: actions/checkout@v5

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

# this is needed to address this issue according to the comment https://github.com/devcontainers/ci/issues/271#issuecomment-2301764487
# otherwise our TourmalineCore org name cannot be used in docker image names, only tourmalinecore
- name: Add DEV_CONTAINER_IMAGE Env Var with Lowercase Organization, Repo Name and devcontainer postfix
run: |
echo "DEV_CONTAINER_IMAGE=${GITHUB_REPOSITORY,,}-devcontainer" >>${GITHUB_ENV}

- name: Create .env from .env-example
run: |
cp .env-example .env

- name: Calculate and Save Unit Tests Coverage
uses: devcontainers/ci@v0.3
with:
imageName: ghcr.io/${{ env.DEV_CONTAINER_IMAGE }}
cacheFrom: ghcr.io/${{ env.DEV_CONTAINER_IMAGE }}
runCmd: |
# COVERAGE_FILE is necessary to later combine separate .coverage files for e2e and unit tests
# We have "e2e" marker configured, so using -m "not e2e" allows running all tests without "e2e" marker
# Here we could use any random non-existent marker in place of "not e2e", this one was chosen for clarity
# With --cov-report xml, we save coverage results in both default pytest's coverage format (sqlite) and in xml
# We need both cause sqlite is expected in `coverage combine`, and xml is expected later when parsing it to embed percentage into README
COVERAGE_FILE=.coverage.unit pytest -m "not e2e" --maxfail=1 -v --cov=c2pie --cov-report xml:units-coverage.xml

# in the first step that runs inside Dev Container we push the image we built to re-use it in the next steps
# we never push in the next steps, only use the cache if available
push: always

- name: Calculate and Save E2E Tests Coverage
uses: devcontainers/ci@v0.3
with:
cacheFrom: ghcr.io/${{ env.DEV_CONTAINER_IMAGE }}
runCmd: |
# For detailed explanation refer to previous step
COVERAGE_FILE=.coverage.e2e pytest tests/c2pa/e2e_test.py --cov=c2pie -v --cov-report xml:e2e-coverage.xml
push: never

- name: Merge Units And E2E Into Full Coverage
uses: devcontainers/ci@v0.3
with:
cacheFrom: ghcr.io/${{ env.DEV_CONTAINER_IMAGE }}
runCmd: |
coverage combine --keep
# Need this additional command to save merged results into .xml as coverage combine doesn't allow it in itself
# And further down the line we work with .xml's
coverage xml --data-file .coverage -o full-coverage.xml
push: never

- name: Generate Full Tests Coverage Summary as Markdown
uses: devcontainers/ci@v0.3
with:
cacheFrom: ghcr.io/${{ env.DEV_CONTAINER_IMAGE }}
runCmd: |
coverage report --format=markdown > Summary.md
push: never

# https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-commands#adding-a-job-summary
- name: Add Full Tests Coverage Summary to Job Summary
run: |
# this is a separate step because for some reason if you do it from devcontainers/ci it doesn't work and Job's summary is empty
cat Summary.md >> "$GITHUB_STEP_SUMMARY"

- name: Upload Unit Tests Coverage
uses: actions/upload-artifact@v7
with:
name: units-coverage
path: units-coverage.xml

- name: Upload E2E Tests Coverage
uses: actions/upload-artifact@v7
with:
name: e2e-coverage
path: e2e-coverage.xml

- name: Upload Full Tests Coverage
uses: actions/upload-artifact@v7
with:
name: full-coverage
path: full-coverage.xml

update-coverage:
runs-on: ubuntu-24.04
needs: [calculate-coverage]
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
# from AI: Without this ref in PRs, you're on a merge commit, and pushing back is ambiguous.
ref: ${{ github.head_ref }}

- name: Download Units Coverage
uses: actions/download-artifact@v8
with:
name: units-coverage
path: coverage

- name: Download E2E Coverage
uses: actions/download-artifact@v8
with:
name: e2e-coverage
path: coverage

- name: Download Full Coverage
uses: actions/download-artifact@v8
with:
name: full-coverage
path: coverage

- name: Install xq to Read Coverage from XML
run: |
sudo apt install xq

# Find existing score and color by line matching in README and get calculated coverage from json file
- name: Write Coverages to ENV Vars
# ToDo avoid code duplication with reusable bash or python function
run: |
units_line_coverage_float=$(cat coverage/units-coverage.xml | xq -x /coverage/@line-rate)

units_line_coverage_percent=$(awk -v num="$units_line_coverage_float" 'BEGIN { print num * 100 }')

# round to 2 decimal digits
echo "UNITS_COVERAGE=$(printf "%.2f\n" "$units_line_coverage_percent")" >> "$GITHUB_ENV"

e2e_line_coverage_float=$(cat coverage/e2e-coverage.xml | xq -x /coverage/@line-rate)

e2e_line_coverage_percent=$(awk -v num="$e2e_line_coverage_float" 'BEGIN { print num * 100 }')

# round to 2 decimal digits
echo "E2E_COVERAGE=$(printf "%.2f\n" "$e2e_line_coverage_percent")" >> "$GITHUB_ENV"

full_line_coverage_float=$(cat coverage/full-coverage.xml | xq -x /coverage/@line-rate)

full_line_coverage_percent=$(awk -v num="$full_line_coverage_float" 'BEGIN { print num * 100 }')

# round to 2 decimal digits
echo "FULL_COVERAGE=$(printf "%.2f\n" "$full_line_coverage_percent")" >> "$GITHUB_ENV"

# 0-60: red; 60-70: orange; 70-80: yellow; 80-90: yellowish green; 90-100: green
# set new color and then add its variable to workflow's environment
- name: Calculate Coverages Badge Colors
run: |
import os

def get_badge_color_by_coverage_value(
new_coverage: str,
) -> str:
if new_coverage < 60:
new_badge_color = "crimson"
elif 60 <= new_coverage < 70:
new_badge_color = "orange"
elif 70 <= new_coverage < 80:
new_badge_color = "yellow"
elif 80 <= new_coverage < 90:
new_badge_color = "olivedrab"
elif new_coverage >= 90:
new_badge_color = "forestgreen"
return new_badge_color

def set_by_coverage_badge_color(
env_value_name_with_coverage: str,
env_value_name_with_bage_color: str,
) -> None:
new_coverage = float(os.getenv(env_value_name_with_coverage))
badge_color = get_badge_color_by_coverage_value(new_coverage)
env_file = os.getenv("GITHUB_ENV")

with open(env_file, "a") as f:
f.write(f"{env_value_name_with_bage_color}={badge_color}\n")

set_by_coverage_badge_color(
env_value_name_with_coverage="UNITS_COVERAGE",
env_value_name_with_bage_color="UNITS_BADGE_COLOR"
)
set_by_coverage_badge_color(
env_value_name_with_coverage="E2E_COVERAGE",
env_value_name_with_bage_color="E2E_BADGE_COLOR"
)
set_by_coverage_badge_color(
env_value_name_with_coverage="FULL_COVERAGE",
env_value_name_with_bage_color="FULL_BADGE_COLOR"
)
shell: python

- name: Replace Badge URL in README
run: |
import io, sys, os, fileinput

def replace_coverage_badge_in_readme(
coverage_prefix: str,
coverage: str,
coverage_color: str,
) -> None:
target_prefix = f"[![coverage](https://img.shields.io/badge/{coverage_prefix}"
replacement = f"[![coverage](https://img.shields.io/badge/{coverage_prefix}-{coverage}%25-{coverage_color})](https://github.com/${{ github.repository }}/actions/workflows/calculate-tests-coverage-on-pull-request.yml)\n"

with fileinput.FileInput("README.md", inplace=True) as file:
for line in file:
if line.startswith(target_prefix):
sys.stdout.write(replacement)
else:
sys.stdout.write(line)

replace_coverage_badge_in_readme(
coverage_prefix="units_coverage",
coverage=os.getenv("UNITS_COVERAGE"),
coverage_color=os.getenv("UNITS_BADGE_COLOR")
)
replace_coverage_badge_in_readme(
coverage_prefix="e2e_coverage",
coverage=os.getenv("E2E_COVERAGE"),
coverage_color=os.getenv("E2E_BADGE_COLOR")
)
replace_coverage_badge_in_readme(
coverage_prefix="full_coverage",
coverage=os.getenv("FULL_COVERAGE"),
coverage_color=os.getenv("FULL_BADGE_COLOR")
)
shell: python

- name: Commit README changes
run: |
git config user.email "contact@tourmalinecore.com"
git config user.name "Workflow Action"

if ! git commit README.md -m "docs(readme): bring test coverage score up to date"; then
echo "No changes to commit."
exit 0
fi

# these fetch and rebase were needed to address an issue when there are 2 or more parallel jobs which modify README.md
# these jobs are doing it concurrently and there were a race condition, only the first modifier could push its changes
git fetch origin ${{ github.head_ref }}
git rebase origin/${{ github.head_ref }}
git push origin HEAD:${{ github.head_ref }}
51 changes: 51 additions & 0 deletions .github/workflows/check-dependencies.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Check Dependencies with Deptry

on:
pull_request:
types: [opened, synchronize, reopened]

jobs:
deptry_check:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]

# poetry shell doesn't work properly without this setting
defaults:
run:
shell: bash

runs-on: ${{ matrix.os }}

steps:
- name: Checkout the repo
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install Poetry
uses: snok/install-poetry@v1
with:
version: '2.4.1'
virtualenvs-create: true
virtualenvs-in-project: true

- name: Load cached venv
id: cached-poetry-dependencies
uses: actions/cache@v4
with:
path: .venv
key: venv-tests-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }}

# --no-interaction - installing in non-interactive mode
# --no-ansi - disable ANSI codes
- name: Install The Project Dependencies
run: poetry install --with dev --no-interaction --no-ansi

- name: Check dependencies
run: poetry run deptry .
Loading
Loading