Skip to content

Commit a35fdba

Browse files
authored
ci: sync ncrypto from latest Node release, update script (#45)
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
1 parent 027d1e4 commit a35fdba

2 files changed

Lines changed: 78 additions & 26 deletions

File tree

.github/workflows/sync-node-ncrypto.yml

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,8 @@ on:
44
workflow_dispatch:
55
inputs:
66
node_ref:
7-
description: nodejs/node ref to sync from
8-
required: true
9-
default: main
10-
base_node_ref:
11-
description: Optional previous nodejs/node ref for bootstrap or recovery
7+
description: Optional nodejs/node release tag to sync from
128
required: false
13-
default: ''
149

1510
permissions:
1611
contents: write
@@ -29,11 +24,9 @@ jobs:
2924
id: sync
3025
env:
3126
NODE_REF: ${{ inputs.node_ref }}
32-
BASE_NODE_REF: ${{ inputs.base_node_ref }}
3327
run: |
3428
python3 tools/sync-node-ncrypto.py \
35-
--node-ref "$NODE_REF" \
36-
--base-node-ref "$BASE_NODE_REF"
29+
--node-ref "$NODE_REF"
3730
3831
- name: Stop when there are no changes
3932
if: steps.sync.outputs.has_changes != 'true'
@@ -45,7 +38,9 @@ jobs:
4538
run: |
4639
branch='${{ steps.sync.outputs.branch_name }}'
4740
git switch -c "$branch"
48-
git fetch origin "$branch:refs/remotes/origin/$branch" || true
41+
if git ls-remote --exit-code --heads origin "$branch" >/dev/null; then
42+
git fetch origin "$branch:refs/remotes/origin/$branch"
43+
fi
4944
git config user.name 'github-actions[bot]'
5045
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
5146
git add \
@@ -54,7 +49,7 @@ jobs:
5449
src/engine.cpp \
5550
src/ncrypto.cpp
5651
git commit \
57-
-m 'chore: sync ncrypto from nodejs/node' \
52+
-m 'chore: sync ncrypto from Node.js ${{ steps.sync.outputs.target_version }}' \
5853
-m 'Node-Base-Commit: ${{ steps.sync.outputs.base_sha }}' \
5954
-m 'Node-Target-Commit: ${{ steps.sync.outputs.target_sha }}'
6055
git push --force-with-lease origin "$branch"
@@ -64,11 +59,10 @@ jobs:
6459
if: steps.sync.outputs.has_changes == 'true'
6560
run: |
6661
{
67-
echo 'Syncs `deps/ncrypto` from `nodejs/node` into this repository.'
62+
echo 'Syncs `deps/ncrypto` from Node.js `${{ steps.sync.outputs.target_version }}`.'
6863
echo
6964
echo '- Base node commit: `${{ steps.sync.outputs.base_sha }}`'
7065
echo '- Target node commit: `${{ steps.sync.outputs.target_sha }}`'
71-
echo '- Conflicts: `${{ steps.sync.outputs.has_conflicts }}`'
7266
if [ '${{ steps.sync.outputs.has_conflicts }}' = 'true' ]; then
7367
echo
7468
echo 'This PR was opened as a draft because the 3-way merge produced conflicts:'
@@ -83,14 +77,14 @@ jobs:
8377
GH_TOKEN: ${{ github.token }}
8478
run: |
8579
branch='${{ steps.commit.outputs.branch }}'
86-
title='chore: sync ncrypto from nodejs/node'
87-
existing_url="$(gh pr view "$branch" --json url --jq .url 2>/dev/null || true)"
80+
title='chore: sync ncrypto from Node.js ${{ steps.sync.outputs.target_version }}'
81+
existing_url="$(gh pr list --state open --head "$branch" --json url --jq '.[0].url // ""')"
8882
if [ -n "$existing_url" ]; then
89-
gh pr edit "$branch" --title "$title" --body-file "$RUNNER_TEMP/pr-body.md"
83+
gh pr edit "$existing_url" --title "$title" --body-file "$RUNNER_TEMP/pr-body.md"
9084
if [ '${{ steps.sync.outputs.has_conflicts }}' = 'true' ]; then
91-
gh pr ready "$branch" --undo || true
85+
gh pr ready "$existing_url" --undo || true
9286
else
93-
gh pr ready "$branch" || true
87+
gh pr ready "$existing_url" || true
9488
fi
9589
echo "$existing_url"
9690
exit 0

tools/sync-node-ncrypto.py

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,53 @@ def fetch_ref(repository: str, ref: str) -> str:
5858
return git(('rev-parse', 'FETCH_HEAD^{commit}')).stdout.decode().strip()
5959

6060

61+
def release_version(tag: str) -> tuple[int, int, int] | None:
62+
if tag.startswith('refs/tags/'):
63+
tag = tag.removeprefix('refs/tags/')
64+
if not tag.startswith('v'):
65+
return None
66+
67+
parts = tag.removeprefix('v').split('.')
68+
if len(parts) != 3 or not all(part.isdecimal() for part in parts):
69+
return None
70+
71+
major, minor, patch = parts
72+
return (int(major), int(minor), int(patch))
73+
74+
75+
def latest_stable_release(repository: str) -> str:
76+
output = git(('ls-remote', '--tags', '--refs', repository, 'v*.*.*')).stdout.decode()
77+
releases: list[tuple[tuple[int, int, int], str]] = []
78+
for line in output.splitlines():
79+
try:
80+
_, ref = line.split(None, maxsplit=1)
81+
except ValueError:
82+
continue
83+
84+
version = release_version(ref)
85+
if version is not None:
86+
releases.append((version, ref.removeprefix('refs/tags/')))
87+
88+
if not releases:
89+
raise SyncError(f'could not find stable Node.js release tags in {repository}')
90+
91+
return max(releases)[1]
92+
93+
94+
def resolve_target_ref(repository: str, ref: str) -> str:
95+
if not ref:
96+
return latest_stable_release(repository)
97+
98+
return ref
99+
100+
101+
def target_version(ref: str, sha: str) -> str:
102+
if release_version(ref) is not None:
103+
return ref.removeprefix('refs/tags/')
104+
105+
return sha[:12]
106+
107+
61108
def load_state(path: Path) -> str | None:
62109
if not path.exists():
63110
return None
@@ -180,8 +227,10 @@ def sync(args: argparse.Namespace) -> int:
180227
if base_ref is None:
181228
raise SyncError(f'{state_path} does not record a node_commit; pass --base-node-ref to bootstrap the sync')
182229

230+
target_ref = resolve_target_ref(args.node_repository, args.node_ref)
183231
base_sha = fetch_ref(args.node_repository, base_ref)
184-
target_sha = fetch_ref(args.node_repository, args.node_ref)
232+
target_sha = fetch_ref(args.node_repository, target_ref)
233+
target_node_version = target_version(target_ref, target_sha)
185234

186235
check_unmapped_files(target_sha)
187236

@@ -196,7 +245,7 @@ def sync(args: argparse.Namespace) -> int:
196245
)
197246

198247
conflicts: list[str] = []
199-
would_change = current_state != target_sha
248+
would_change_mapped_files = False
200249
with tempfile.TemporaryDirectory(prefix='sync-node-ncrypto-') as temporary_directory_name:
201250
temporary_directory = Path(temporary_directory_name)
202251
for source, destination in MAPPINGS.items():
@@ -208,30 +257,35 @@ def sync(args: argparse.Namespace) -> int:
208257
temporary_directory=temporary_directory,
209258
)
210259
if destination.read_bytes() != merged:
211-
would_change = True
260+
would_change_mapped_files = True
212261
if not args.dry_run:
213262
destination.write_bytes(merged)
214263
if conflicted:
215264
conflicts.append(str(destination))
216265

217-
if not args.dry_run:
266+
paths = list(MAPPINGS.values())
267+
changed = would_change_mapped_files if args.dry_run else has_changes(paths)
268+
269+
if not args.dry_run and changed:
218270
write_state(state_path, target_sha)
219271

220-
paths = [*MAPPINGS.values(), state_path]
221-
changed = would_change if args.dry_run else has_changes(paths)
222272
outputs = {
223273
'base_sha': base_sha,
224274
'target_sha': target_sha,
225275
'target_short_sha': target_sha[:12],
276+
'target_ref': target_ref,
277+
'target_version': target_node_version,
226278
'has_changes': changed,
227279
'has_conflicts': bool(conflicts),
228280
'conflicts': conflicts,
229-
'branch_name': f'sync-node-ncrypto/{target_sha[:12]}',
281+
'branch_name': f'sync-node-ncrypto/{target_node_version}',
230282
}
231283
write_github_output(outputs)
232284

233285
print(f'Base node commit: {base_sha}')
234286
print(f'Target node commit: {target_sha}')
287+
print(f'Target node ref: {target_ref}')
288+
print(f'Target Node.js: {target_node_version}')
235289
print(f'Changed files: {str(changed).lower()}')
236290
print(f'Conflicts: {str(bool(conflicts)).lower()}')
237291
for path in conflicts:
@@ -243,7 +297,11 @@ def sync(args: argparse.Namespace) -> int:
243297
def parse_args(argv: Sequence[str]) -> argparse.Namespace:
244298
parser = argparse.ArgumentParser(description='Sync nodejs/node deps/ncrypto into standalone ncrypto.')
245299
parser.add_argument('--node-repository', default=NODE_REPOSITORY)
246-
parser.add_argument('--node-ref', default='main')
300+
parser.add_argument(
301+
'--node-ref',
302+
default='',
303+
help='nodejs/node ref to sync from; defaults to latest stable release',
304+
)
247305
parser.add_argument('--base-node-ref', default='')
248306
parser.add_argument('--state-file', default=str(STATE_FILE))
249307
parser.add_argument('--dry-run', action='store_true')

0 commit comments

Comments
 (0)