Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added github actions for updating .py files and generating releases #3

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
25 changes: 25 additions & 0 deletions .github/ci-and-release.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# CI Processes
## Update IronSharpPack .py files and Create PR (update_files.yml)
This workflow is manually triggered to update Python files based on executables from the SharpCollection repository. It processes these files, commits any changes, and creates a new branch and pull request into main.

Key Steps:
1. Process Executables: Runs a Python script to generate Python files from .NET executables across different .NET Framework versions.
2. Remove Unused Files: Cleans up any temporary or unnecessary files created during the run.
3. Create New Branch and Handle Changes: Checks for changes and commits them to a new branch, pushing the branch and creating a pull request if changes exist.


## Update Development Branch and PR to Main (release.yml)
This workflow is triggered manually via workflow_dispatch with an option to specify the type of version bump (patch, minor, or major). It's designed to increment the project version based on the latest entry in CHANGELOG.md, update the changelog, commit these changes to the dev branch, and then create a pull request to merge these changes into the main branch.

Key Steps:
1. Extract and Increment Version: It reads the current version from CHANGELOG.md, increments it based on the selected bump type, and then updates the environment variable for subsequent steps.
2. Update Changelog: Utilizes a community action to append the new version to CHANGELOG.md.
3. Commit Changes and Create Pull Request: Commits the updated changelog to dev and creates a pull request to merge dev into main.

## Create Release and Attach Files (tag_release.yml)
Triggered when a pull request to the main branch is closed and merged. It packages specified directories into ZIP files, creates a GitHub release tagged with the version number extracted from CHANGELOG.md, and uploads the ZIP files as assets to the release.

Key Steps:
1. Determine New Version: Parses CHANGELOG.md to find the latest version number.
2. Zip NetFramework folders: Compresses folders containing different versions of .NET Framework files into ZIP files.
3. Create Release and Upload Release Asset: Creates a new GitHub release with the specified version and uploads the previously zipped files as release assets.
82 changes: 82 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
name: Update Development Branch and PR to Main

on:
workflow_dispatch:
inputs:
bumpType:
description: 'Select the bump type for the next release.'
type: choice
default: patch
options:
- patch
- minor
- major
required: true

jobs:
update_dev:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v3
with:
ref: dev
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Initialize mandatory git config
run: |
git config user.name "GitHub Actions"
git config user.email [email protected]

- name: Extract and increment version
id: versioning
run: |
# Extract the latest version from CHANGELOG.md
VERSION=$(grep -Po '(?<=\[)\d+\.\d+\.\d+(?=\])' CHANGELOG.md | head -1)
echo "Current version: $VERSION"

# Increment version based on input type
IFS='.' read -ra VER <<< "$VERSION"
MAJOR=${VER[0]}
MINOR=${VER[1]}
PATCH=${VER[2]}

if [ "${{ github.event.inputs.bumpType }}" == "major" ]; then
MAJOR=$((MAJOR + 1))
MINOR=0
PATCH=0
elif [ "${{ github.event.inputs.bumpType }}" == "minor" ]; then
MINOR=$((MINOR + 1))
PATCH=0
else
PATCH=$((PATCH + 1))
fi

NEW_VERSION="${MAJOR}.${MINOR}.${PATCH}"
echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_ENV
echo "::set-output name=new_version::$NEW_VERSION"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


- name: Print new version
run: echo "New version is ${{ steps.versioning.outputs.new_version }}"

- name: Update Changelog
uses: vinnybod/[email protected]
with:
version: ${{ steps.versioning.outputs.new_version }}

- name: Commit changes
run: |
git add .
git commit -m "Update changelog for release v${{ steps.versioning.outputs.new_version }}"
git push

- name: Create pull request from dev to main
uses: repo-sync/pull-request@v2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This action is deprecated. I think you should update it to match the action used in Empire and Starkiller https://github.com/BC-SECURITY/Empire/blob/main/.github/workflows/release-private-start.yml#L66

with:
github_token: ${{ secrets.GITHUB_TOKEN }}
pr_title: "Release v${{ steps.versioning.outputs.new_version }} into main"
pr_body: "This PR was automatically generated to roll out new changes into main. Please merge with a merge commit."
pr_label: "release"
destination_branch: "main"
source_branch: "dev"
74 changes: 74 additions & 0 deletions .github/workflows/tag_release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: Create Release and Attach Files

on:
pull_request:
types:
- closed
branches:
- main

jobs:
create_release:
if: ${{ github.event.pull_request.merged == true }}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something seems off about this unless I am misunderstanding.

I manually run the update_files workflow. That updates files, pushes to dev and opens a PR to main
I merge the PR to main
That triggers the tag_release workflow that creates the release based off the version in the CHANGELOG

When does the release workflow come into play?

I think you could replicate the Empire flow by checking that the PR being merged is a release PR https://github.com/BC-SECURITY/Empire/blob/main/.github/workflows/release-private-tag.yml#L11

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost, here is the flow:

  1. You run "update_files", which creates a branch with the new .py files. It will also open a PR to the dev branch.
  2. You run "release", which will update the changelog and version number. Then it will open a PR from dev to Main.
  3. The "tag_release" gets triggered automatically when a PR is closed to main which creates a new release with the notes and files.

runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v2
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Determine new version
run: |
# Extract the latest version number from Changelog.md
VERSION=$(grep -Po '(?<=\[)\d+\.\d+\.\d+(?=\])' CHANGELOG.md | head -1)
echo "Current version: $VERSION"
echo "APP_VERSION=$VERSION" >> $GITHUB_ENV

- name: Zip NetFramework folders
run: |
zip -r NetFramework_4.0_Any.zip NetFramework_4.0_Any/
zip -r NetFramework_4.5_Any.zip NetFramework_4.5_Any/
zip -r NetFramework_4.7_Any.zip NetFramework_4.7_Any/

- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: v${{ env.APP_VERSION }}
release_name: Release v${{ env.APP_VERSION }}
body: Release of version ${{ env.APP_VERSION }}
draft: false
prerelease: false

- name: Upload Release Asset for NetFramework 4.0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the benefit of the zip files here? Since a release automatically contains the source code tar.gz, it seems like duplicate assets

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a duplicate, but typically you are only going to need one set of those so it makes it easy to just grab the necessary ones. I can remove it if you think that's dumb.

uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./NetFramework_4.0_Any.zip
asset_name: NetFramework_4.0_Any.zip
asset_content_type: application/zip

- name: Upload Release Asset for NetFramework 4.5
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./NetFramework_4.5_Any.zip
asset_name: NetFramework_4.5_Any.zip
asset_content_type: application/zip

- name: Upload Release Asset for NetFramework 4.7
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./NetFramework_4.7_Any.zip
asset_name: NetFramework_4.7_Any.zip
asset_content_type: application/zip
87 changes: 87 additions & 0 deletions .github/workflows/update_files.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
name: Update IronSharpPack .py files and Create PR

on:
workflow_dispatch:

jobs:
prepare-release:
runs-on: ubuntu-latest
steps:
- name: Checkout Main Repository
uses: actions/[email protected]
with:
fetch-depth: 0
ref: main

- name: Checkout SharpCollection Repository
uses: actions/[email protected]
with:
repository: Flangvik/SharpCollection
path: SharpCollection
ref: master

- name: Set up Python
uses: actions/[email protected]
with:
python-version: '3.x'

- name: Process Executables for .NET 4.0
run: |
mkdir -p SharpCollection_new/NetFramework_4.0_Any
cp -v -r SharpCollection/NetFramework_4.0_Any/ SharpCollection_new/
ls -la SharpCollection_new/
python IronEmbed.py SharpCollection_new/NetFramework_4.0_Any SharpCollection_new/NetFramework_4.0_Any
cp -v -f SharpCollection_new/NetFramework_4.0_Any/*.py NetFramework_4.0_Any/

- name: Process Executables for .NET 4.5
run: |
mkdir -p SharpCollection_new/NetFramework_4.5_Any
cp -v -r SharpCollection/NetFramework_4.5_Any/ SharpCollection_new/
ls -la SharpCollection_new/
python IronEmbed.py SharpCollection_new/NetFramework_4.5_Any SharpCollection_new/NetFramework_4.5_Any
cp -v -f SharpCollection_new/NetFramework_4.5_Any/*.py NetFramework_4.5_Any/

- name: Process Executables for .NET 4.7
run: |
mkdir -p SharpCollection_new/NetFramework_4.7_Any
cp -v -r SharpCollection/NetFramework_4.7_Any/ SharpCollection_new/
ls -la SharpCollection_new/
python IronEmbed.py SharpCollection_new/NetFramework_4.7_Any SharpCollection_new/NetFramework_4.7_Any
cp -v -f SharpCollection_new/NetFramework_4.7_Any/*.py NetFramework_4.7_Any/

- name: Remove unused files
run: |
rm -r -f SharpCollection_new
rm -r -f SharpCollection

- name: Configure Git
run: |
git config --local user.email "[email protected]"
git config --local user.name "GitHub Action"

- name: Set Environment Variables
run: echo "RELEASE_BRANCH=release-$(date +%Y-%m-%d)" >> $GITHUB_ENV

- name: Delete Existing Branch if It Exists
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh auth setup-git
EXISTING_PR=$(gh pr list --head ${{ env.RELEASE_BRANCH }} --base main --state open --json url --jq '.[].url')
if [ "$EXISTING_PR" != "" ]; then
gh pr close $EXISTING_PR --delete-branch
fi

- name: Create New Branch and Handle Changes
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would stick to one way of opening PRs for simplicity. I think using gh cli is actually pretty clean, maybe replace the deprecated action above with this if you prefer it over the one used in Empire

run: |
git checkout -b ${{ env.RELEASE_BRANCH }}
git add .
if ! git diff --cached --exit-code; then
git commit -m "Add new release files for different .NET versions"
git push --set-upstream origin ${{ env.RELEASE_BRANCH }}
gh pr create --title "New Release: ${{ env.RELEASE_BRANCH }}" --body "Generated new release files for distribution across .NET Framework versions." --head ${{ env.RELEASE_BRANCH }} --base main
else
echo "No changes to commit, skipping push and pull request creation."
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

- **Added** for new features.
- **Changed** for changes in existing functionality.
- **Deprecated** for soon-to-be removed features.
- **Removed** for now removed features.
- **Fixed** for any bug fixes.
- **Security** in case of vulnerabilities.

## [Unreleased]

### Added
- Added Github actions to automate updating of .py files
- Added Github release action to automate changelog, tagging, and release processes

### Changed
- Updated IronEmbed.py to work with new actions

## [1.0.0] - 2024-04-18

- Initial release
19 changes: 10 additions & 9 deletions IronEmbed.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,34 @@
import base64
import zlib


def file_to_base64_compressed(file_path):
with open(file_path, 'rb') as file:
compressed_data = zlib.compress(file.read())
base64_encoded = base64.b64encode(compressed_data).decode('utf-8')
return base64_encoded


def main():
if len(sys.argv) < 2 or sys.argv[1] == 'help':
# Check for minimum required arguments and help command
if len(sys.argv) < 3 or sys.argv[1] == 'help':
print('''IronEmbed compresses a .NET assembly and embeds it into an IronPython script
arguments should be <assembly_directory>''')
Usage: python IronEmbed.py <assembly_directory> <output_directory>''')
return

assembly_dir = sys.argv[1]
output_dir = sys.argv[2]
remove_comments_flag = '--remove-comments' in sys.argv

for file_name in os.listdir(assembly_dir):
if file_name.endswith('.exe') or file_name.endswith('.dll'):

file_path = os.path.join(assembly_dir, file_name)
compressed_assembly = file_to_base64_compressed(file_path)

with open('IronSharpPack_template.py', 'r') as file:
script = file.read()
script = script.replace("<replace_assembly>", compressed_assembly)
script = script.replace("<replace_programname>", file_name.replace('.exe', ''))
out_name = "Iron" + file_name.replace('.exe', '.py').replace('.dll', '.py')
script = script.replace("<replace_programname>", file_name.replace('.exe', '').replace('.dll', ''))

out_name = os.path.join(output_dir, "Iron" + file_name.replace('.exe', '.py').replace('.dll', '.py'))
with open(out_name, 'w') as out_file:
out_file.write(script)

Expand Down