Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
653bf4f
Feature: Updated Packager with Auto-version update in Publish paths
kjohn-msft Apr 18, 2025
61701ba
Feature: Updated Packager with Auto-version update in Publish paths
kjohn-msft Apr 18, 2025
c829ccc
Additional required changes and test updates
kjohn-msft Apr 21, 2025
bc7137b
Gitignore update
kjohn-msft Apr 21, 2025
c6421a4
Merge branch 'master' into kjohn-packager
kjohn-msft Apr 21, 2025
5e3f8f4
Additional changes to tests
kjohn-msft Apr 22, 2025
2be6119
Parking ExtStatusAsserter changes
kjohn-msft Apr 28, 2025
7738cb8
Merge branch 'master' into kjohn-packager
kjohn-msft Apr 28, 2025
4c37bff
Additional changes
kjohn-msft Apr 30, 2025
9a72d23
Merge branch 'master' into kjohn-packager
kjohn-msft Apr 30, 2025
e02fd8d
More test helper changes
kjohn-msft May 1, 2025
0317dd9
Improved ExtStatusAsserter
kjohn-msft May 2, 2025
3c58025
Park changes
kjohn-msft May 9, 2025
bee9b66
Merged master
kjohn-msft Jun 6, 2025
1140c1c
Reverting renames that complicate the code
kjohn-msft Jun 15, 2026
b2441bd
Updating git ignore
kjohn-msft Jun 15, 2026
3e6dda5
Removing traces of older code
kjohn-msft Jun 15, 2026
04a90dd
Fixed tests
kjohn-msft Jun 15, 2026
d16a34b
Merge branch 'master' into kjohn-packager
kjohn-msft Jun 15, 2026
d70c5e3
Multi-version support
kjohn-msft Jun 15, 2026
fce13c3
Map versions
kjohn-msft Jun 15, 2026
cc203f5
Final cleanup
kjohn-msft Jun 15, 2026
3217728
Merge conflicts
kjohn-msft Jun 16, 2026
8e165b4
Merge branch 'master' into kjohn-packager
kjohn-msft Jun 17, 2026
b9e55a7
Merge branch 'master' into kjohn-packager
kjohn-msft Jun 26, 2026
33c0386
Merge branch 'master' into kjohn-packager
kjohn-msft Jun 26, 2026
c601924
Merge branch 'master' into kjohn-packager
kjohn-msft Jun 29, 2026
3fd7139
Edited Readme.md to match path
kjohn-msft Jun 29, 2026
71e35ea
Merge branch 'kjohn-packager' of https://github.com/Azure/LinuxPatchE…
kjohn-msft Jun 29, 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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
PACKAGER_PATH = src//tools/Package-All.py
PACKAGER_PATH = src//tools//packager//Package-All.py
BUILD_PATH = build
ZIP_SRC_PATH = src//out//LinuxPatchExtension.zip
MANIFEST_PATH = src//extension//src//manifest.xml
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Of these, only `operation`, `activityId`, `startTime` are required for Assessmen

## 2. Build and Test locally

* Run `python <Project-Path>\src\tools\Package-All.py`. This will generate LinuxPatchExtension.zip under `<Project-Path>\out\`
* Run `python <Project-Path>\src\tools\packager\Package-All.py`. This will generate LinuxPatchExtension.zip under `<Project-Path>\out\`
* Extract files from the zip to any location on your Linux machine. Note down this path.
* Add `HandlerEnvironment.json` following the reference `<Project-Path>\src\tools\references\HandlerEnvironment.json` within the folder containing extracted files.
`HandlerEnvironment.json` defines the location where log, config and status files will be saved. Make sure to specify a directory/folder path for all 3 (can be any location within the machine)
Expand Down
2 changes: 2 additions & 0 deletions src/build.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@echo off
python tools\packager\Package-All.py
35 changes: 35 additions & 0 deletions src/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env bash

# Copyright 2025 Microsoft Corporation
Comment thread
kjohn-msft marked this conversation as resolved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Requires Python 2.7+

COMMAND="tools/packager/Package-All.py"

function find_python(){
local python_exec_command=$1

# Check if there is python defined.
for p in python3 /usr/share/oem/python/bin/python3 python python2 /usr/libexec/platform-python /usr/share/oem/python/bin/python; do
if command -v "${p}" ; then
eval ${python_exec_command}=${p}
return
fi
done
}

find_python PYTHON

${PYTHON} "${COMMAND}"
2 changes: 1 addition & 1 deletion src/core/src/bootstrap/Bootstrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def build_core_components(self, container):
return lifecycle_manager, status_handler

def bootstrap_splash_text(self):
self.composite_logger.log("\n\n[%exec_name%] \t -- \t Copyright (c) Microsoft Corporation. All rights reserved. \nApplication version: 3.0.[%exec_sub_ver%]\n\n")
self.composite_logger.log("\n\n[%exec_name%] \t -- \t Copyright (c) Microsoft Corporation. All rights reserved. \nVersion: [%exec_ver%], Build Date: [%exec_build_date%].\n\n")

def basic_environment_health_check(self):
self.composite_logger.log("Python version: " + " ".join(sys.version.splitlines()))
Expand Down
4 changes: 2 additions & 2 deletions src/core/src/bootstrap/Constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ def __iter__(self):
GLOBAL_EXCLUSION_LIST = "" # if a package needs to be blocked across all of Azure
UNKNOWN = "Unknown"

# Extension version (todo: move to a different file)
EXT_VERSION = "1.6.66"
# Extension version (will be updated from manifest.xml at compile time)
EXT_VERSION = "[%exec_ver%]"

# Runtime environments
TEST = 'Test'
Expand Down
4 changes: 2 additions & 2 deletions src/extension/src/Constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ def __iter__(self):
if item == self.__dict__[item]:
yield item

# Extension version (todo: move to a different file)
EXT_VERSION = "1.6.66"
# Extension version (will be updated from manifest.xml at compile time)
EXT_VERSION = "[%exec_ver%]"

# Runtime environments
TEST = 'Test'
Expand Down
3 changes: 3 additions & 0 deletions src/publish.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@echo off
python tools\packager\Publish.py
git status
35 changes: 35 additions & 0 deletions src/publish.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env bash

# Copyright 2025 Microsoft Corporation
Comment thread
kjohn-msft marked this conversation as resolved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Requires Python 2.7+

COMMAND="tools/packager/Publish.py"

function find_python(){
local python_exec_command=$1

# Check if there is python defined.
for p in python3 /usr/share/oem/python/bin/python3 python python2 /usr/libexec/platform-python /usr/share/oem/python/bin/python; do
if command -v "${p}" ; then
eval ${python_exec_command}=${p}
return
fi
done
}

find_python PYTHON

${PYTHON} "${COMMAND}"
Binary file removed src/tools/misc/EnableVirtualTerminal.reg
Binary file not shown.
89 changes: 51 additions & 38 deletions src/tools/Package-All.py → src/tools/packager/Package-All.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
# https://www.apache.org/licenses/LICENSE-2.0
Comment thread
kjohn-msft marked this conversation as resolved.
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
Expand All @@ -16,21 +16,26 @@

""" Merges individual python modules from src to the MsftLinuxPatchExt files in the out directory.
Relative source and destination paths for the extension are auto-detected if the optional src parameter is not present.
How to use: python Package.py <optional: full path to extension 'src' folder>"""
How to use: python Package-All.py <optional: full path to extension 'src' folder>
Note: Package-All.py internally invokes Package-Core.py to generate MsftLinuxPatchCore.py """

Comment thread
kjohn-msft marked this conversation as resolved.
from __future__ import print_function

import shutil
import sys
import os
import errno
import datetime
from shutil import copyfile
from shutil import make_archive
import subprocess
import xml.etree.ElementTree as et

# imports in VERY_FIRST_IMPORTS, order should be kept
VERY_FIRST_IMPORTS = [
'from __future__ import print_function\n',
'from abc import ABCMeta, abstractmethod\n']
'from abc import ABCMeta, abstractmethod\n',
'from distutils.version import LooseVersion\n']
GLOBAL_IMPORTS = set()


Expand Down Expand Up @@ -59,15 +64,16 @@ def write_merged_code(code, merged_file_full_path):


def insert_copyright_notice(merged_file_full_path, merged_file_name):
notice = '# --------------------------------------------------------------------------------------------------------------------\n'
notice = '# coding=utf-8\n'
notice += '# --------------------------------------------------------------------------------------------------------------------\n'
notice += '# <copyright file="' + merged_file_name + '" company="Microsoft">\n'
notice += '# Copyright 2020 Microsoft Corporation\n' \
notice += '# Copyright ' + str(datetime.date.today().year) + ' Microsoft Corporation\n' \
'#\n' \
'# Licensed under the Apache License, Version 2.0 (the "License");\n' \
'# you may not use this file except in compliance with the License.\n' \
'# You may obtain a copy of the License at\n' \
'#\n' \
'# http://www.apache.org/licenses/LICENSE-2.0\n' \
'# https://www.apache.org/licenses/LICENSE-2.0\n' \
'#\n' \
'# Unless required by applicable law or agreed to in writing, software\n' \
'# distributed under the License is distributed on an "AS IS" BASIS,\n' \
Expand Down Expand Up @@ -104,15 +110,15 @@ def prepend_content_to_file(content, file_name):
os.rename(temp_file, file_name)


def generate_compiled_script(source_code_path, merged_file_full_path, merged_file_name, environment):
def generate_compiled_script(source_code_path, merged_file_full_path, merged_file_name, environment, new_version):
try:
print('\n\n=============================== GENERATING ' + merged_file_name + '... =============================================================\n')
print('\n=============================== (2/3) GENERATING ' + merged_file_name + '... ================================\n')
Comment thread
kjohn-msft marked this conversation as resolved.

print('========== Delete old extension file if it exists.')
print('------------- Deleting old extension file if it exists.')
Comment thread
kjohn-msft marked this conversation as resolved.
if os.path.exists(merged_file_full_path):
os.remove(merged_file_full_path)

print('\n========== Merging modules: \n')
print('------------- Merging modules: ')
modules_to_be_merged = []
for root, dirs, files in os.walk(source_code_path):
for file_name in files:
Expand All @@ -137,19 +143,26 @@ def generate_compiled_script(source_code_path, merged_file_full_path, merged_fil
write_merged_code(codes, merged_file_full_path)
print("<end>")

print('\n========== Prepend all import statements\n')
print('------------- Prepend all import statements')
Comment thread
kjohn-msft marked this conversation as resolved.
insert_imports(GLOBAL_IMPORTS, merged_file_full_path)
insert_imports(VERY_FIRST_IMPORTS, merged_file_full_path)

print('========== Set Copyright, Version and Environment. Also enforce UNIX-style line endings.\n')
print('------------- Set Copyright, Version and Environment. Also enforce UNIX-style line endings.')
insert_copyright_notice(merged_file_full_path, merged_file_name)
timestamp = datetime.datetime.utcnow().strftime("%y%m%d-%H%M")
replace_text_in_file(merged_file_full_path, '[%exec_name%]', merged_file_name.split('.')[0])
replace_text_in_file(merged_file_full_path, '[%exec_sub_ver%]', timestamp)
try:
# Python 3.2+
now = datetime.datetime.now(datetime.timezone.utc)
except AttributeError:
# Older Python fallback (naive UTC)
now = datetime.datetime.utcnow()
date = now.strftime("%y.%m.%d")
Comment thread
kjohn-msft marked this conversation as resolved.
replace_text_in_file(merged_file_full_path, '[%exec_name%]', merged_file_name)
Comment thread
kjohn-msft marked this conversation as resolved.
replace_text_in_file(merged_file_full_path, '[%exec_ver%]', str(new_version))
replace_text_in_file(merged_file_full_path, '[%exec_build_date%]', date)
replace_text_in_file(merged_file_full_path, 'Constants.UNKNOWN_ENV', environment)
replace_text_in_file(merged_file_full_path, '\r\n', '\n')

print("========== Merged extension code was saved to:\n{0}\n".format(merged_file_full_path))
print("------------- Merged extension code was saved to:\n{0}\n".format(merged_file_full_path))

except Exception as error:
print('Exception during merge python modules: ' + repr(error))
Expand All @@ -165,7 +178,7 @@ def main(argv):
# Determine code path if not specified
if len(argv) < 2:
# auto-detect src path
source_code_path = os.path.dirname(os.path.realpath(__file__)).replace("tools", os.path.join("extension", "src"))
source_code_path = os.path.dirname(os.path.realpath(__file__)).replace(os.path.join("tools", "packager"), os.path.join("extension", "src"))
if os.path.exists(os.path.join(source_code_path, "__main__.py")) is False:
print("Invalid extension source code path. Check enlistment.\n")
return
Expand All @@ -180,39 +193,39 @@ def main(argv):
working_directory = os.path.abspath(os.path.join(source_code_path, os.pardir, os.pardir))
merge_file_directory = os.path.join(working_directory, 'out')
try:
if os.path.exists(merge_file_directory):
shutil.rmtree(merge_file_directory)
os.makedirs(merge_file_directory)
except OSError as e:
if e.errno != errno.EEXIST:
raise

# Invoke core business logic code packager
exec_core_build_path = os.path.join(working_directory, 'tools', 'Package-Core.py')
exec_core_build_path = os.path.join(working_directory, 'tools', 'packager', 'Package-Core.py')
subprocess.call('python ' + exec_core_build_path, shell=True)

# Get version from manifest for code
new_version = None
manifest_xml_file_path = os.path.join(working_directory, 'extension', 'src', 'manifest.xml')
manifest_tree = et.parse(manifest_xml_file_path)
manifest_root = manifest_tree.getroot()
for i in range(0, len(manifest_root)):
if 'Version' in str(manifest_root[i]):
new_version = manifest_root[i].text
if new_version is None:
raise Exception("Unable to determine target version.")

# Generated compiled scripts at the destination
merged_file_details = [('MsftLinuxPatchExt.py', 'Constants.PROD')]
for merged_file_detail in merged_file_details:
merged_file_destination = os.path.join(working_directory, 'out', merged_file_detail[0])
generate_compiled_script(source_code_path, merged_file_destination, merged_file_detail[0], merged_file_detail[1])
generate_compiled_script(source_code_path, merged_file_destination, merged_file_detail[0], merged_file_detail[1], new_version)

# GENERATING EXTENSION
print('\n\n=============================== GENERATING LinuxPatchExtension.zip... =============================================================\n')
# Rev handler version
# print('\n========== Revising extension version.')
# manifest_xml_file_path = os.path.join(working_directory, 'extension', 'src', 'manifest.xml')
# manifest_tree = et.parse(manifest_xml_file_path)
# manifest_root = manifest_tree.getroot()
# for i in range(0, len(manifest_root)):
# if 'Version' in str(manifest_root[i]):
# current_version = manifest_root[i].text
# version_split = current_version.split('.')
# version_split[len(version_split)-1] = str(int(version_split[len(version_split)-1]) + 1)
# new_version = '.'.join(version_split)
# print("Changing extension version from {0} to {1}.".format(current_version, new_version))
# replace_text_in_file(manifest_xml_file_path, current_version, new_version)
print('\n=============================== (3/3) GENERATING LinuxPatchExtension.zip... ==============================\n')

# Copy extension files
print('\n========== Copying extension files + enforcing UNIX style line endings.\n')
print('------------- Copying extension files + enforcing UNIX style line endings.')
ext_files = ['HandlerManifest.json', 'manifest.xml', 'MsftLinuxPatchExtShim.sh']
for ext_file in ext_files:
ext_file_src = os.path.join(working_directory, 'extension', 'src', ext_file)
Expand All @@ -230,18 +243,18 @@ def main(argv):
os.remove(ext_zip_file_path_dest)

# Generate zip
print('\n========== Generating extension zip.\n')
make_archive(os.path.splitext(ext_zip_file_path_src)[0], 'zip', os.path.join(working_directory, 'out'), '.')
print('------------- Generating extension zip.')
make_archive(os.path.splitext(ext_zip_file_path_src)[0], 'zip', os.path.join(working_directory, 'out'), '..')
Comment thread
kjohn-msft marked this conversation as resolved.
copyfile(ext_zip_file_path_src, ext_zip_file_path_dest)
Comment thread
kjohn-msft marked this conversation as resolved.
os.remove(ext_zip_file_path_src)

# Remove extension file copies
print('\n========== Cleaning up environment.\n')
print('------------- Cleaning up environment.')
for ext_file in ext_files:
ext_file_path = os.path.join(working_directory, 'out', ext_file)
os.remove(ext_file_path)

print("========== Extension ZIP was saved to:\n{0}\n".format(ext_zip_file_path_dest))
print("------------- Extension ZIP was saved to:\n{0}\n".format(ext_zip_file_path_dest))

except Exception as error:
print('Exception during merge python modules: ' + repr(error))
Expand Down
Loading
Loading