Initial commit: Add WireGuard VPN provisioning system
This commit is contained in:
parent
75bade64bd
commit
6d915dd533
4
ansible/group_vars/vpn_servers/vars.yml
Normal file
4
ansible/group_vars/vpn_servers/vars.yml
Normal file
@ -0,0 +1,4 @@
|
||||
wireguard_interface: wg0
|
||||
wireguard_port: 51820
|
||||
wireguard_network: 10.0.0.0/24
|
||||
server_public_ip: "{{ ansible_host }}"
|
10
ansible/playbooks/templates/client.conf.j2
Normal file
10
ansible/playbooks/templates/client.conf.j2
Normal file
@ -0,0 +1,10 @@
|
||||
[Interface]
|
||||
PrivateKey = {{ client_private_key.stdout }}
|
||||
Address = {{ client_ip }}/24
|
||||
DNS = 1.1.1.1
|
||||
|
||||
[Peer]
|
||||
PublicKey = {{ server_pubkey }}
|
||||
Endpoint = {{ ansible_host }}:51820
|
||||
AllowedIPs = 0.0.0.0/0
|
||||
PersistentKeepalive = 25
|
10
ansible/playbooks/templates/server.conf.j2
Normal file
10
ansible/playbooks/templates/server.conf.j2
Normal file
@ -0,0 +1,10 @@
|
||||
[Interface]
|
||||
Address = {{ server_ip }}
|
||||
ListenPort = {{ server_port }}
|
||||
PrivateKey = {{ server_private_key.stdout }}
|
||||
|
||||
# Enable IPv4 forwarding
|
||||
PostUp = sysctl -w net.ipv4.ip_forward=1
|
||||
# NAT configuration (adjust eth0 to your outbound interface if different)
|
||||
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
|
||||
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
|
118
ansible/playbooks/vpn_provision.yml
Normal file
118
ansible/playbooks/vpn_provision.yml
Normal file
@ -0,0 +1,118 @@
|
||||
---
|
||||
- name: Provision WireGuard VPN for new client
|
||||
hosts: vpn_servers
|
||||
become: yes
|
||||
vars:
|
||||
client_dir: /etc/wireguard/clients
|
||||
wg_interface: wg0
|
||||
server_dir: /etc/wireguard
|
||||
server_ip: 10.8.0.1/24
|
||||
server_port: 51820
|
||||
server_endpoint: "{{ ansible_host | default(inventory_hostname) }}"
|
||||
|
||||
tasks:
|
||||
- name: Debug invoice ID
|
||||
debug:
|
||||
msg: "Processing invoice ID: {{ invoice_id }}"
|
||||
|
||||
# Server Setup Tasks
|
||||
- name: Check if server keys exist
|
||||
stat:
|
||||
path: "{{ server_dir }}/{{ wg_interface }}.conf"
|
||||
register: server_config
|
||||
|
||||
- name: Generate server private key if not exists
|
||||
shell: wg genkey
|
||||
register: server_private_key
|
||||
when: not server_config.stat.exists
|
||||
|
||||
- name: Save server private key
|
||||
copy:
|
||||
content: "{{ server_private_key.stdout }}"
|
||||
dest: "{{ server_dir }}/private.key"
|
||||
mode: '0600'
|
||||
when: not server_config.stat.exists
|
||||
|
||||
- name: Generate server public key
|
||||
shell: "cat {{ server_dir }}/private.key | wg pubkey"
|
||||
register: server_public_key
|
||||
when: not server_config.stat.exists
|
||||
|
||||
- name: Save server public key
|
||||
copy:
|
||||
content: "{{ server_public_key.stdout }}"
|
||||
dest: "{{ server_dir }}/public.key"
|
||||
mode: '0644'
|
||||
when: not server_config.stat.exists
|
||||
|
||||
- name: Create initial server config
|
||||
template:
|
||||
src: templates/server.conf.j2
|
||||
dest: "{{ server_dir }}/{{ wg_interface }}.conf"
|
||||
mode: '0600'
|
||||
when: not server_config.stat.exists
|
||||
|
||||
# Client Setup Tasks
|
||||
- name: Ensure client directory exists
|
||||
file:
|
||||
path: "{{ client_dir }}/{{ invoice_id }}"
|
||||
state: directory
|
||||
mode: '0700'
|
||||
|
||||
- name: Generate client private key
|
||||
shell: wg genkey
|
||||
register: client_private_key
|
||||
no_log: true
|
||||
|
||||
- name: Save client private key
|
||||
copy:
|
||||
content: "{{ client_private_key.stdout }}"
|
||||
dest: "{{ client_dir }}/{{ invoice_id }}/private.key"
|
||||
mode: '0600'
|
||||
no_log: true
|
||||
|
||||
- name: Generate client public key
|
||||
shell: "echo '{{ client_private_key.stdout }}' | wg pubkey"
|
||||
register: client_public_key
|
||||
|
||||
- name: Save client public key
|
||||
copy:
|
||||
content: "{{ client_public_key.stdout }}"
|
||||
dest: "{{ client_dir }}/{{ invoice_id }}/public.key"
|
||||
mode: '0644'
|
||||
|
||||
- name: Read server public key
|
||||
shell: "cat {{ server_dir }}/public.key"
|
||||
register: server_public_key_read
|
||||
changed_when: false
|
||||
|
||||
- name: Get next available IP
|
||||
shell: |
|
||||
last_ip=$(grep -h '^Address' {{ client_dir }}/*/wg0.conf 2>/dev/null | tail -n1 | grep -oE '[0-9]+$' || echo 1)
|
||||
echo $((last_ip + 1))
|
||||
register: next_ip
|
||||
|
||||
- name: Generate client config
|
||||
template:
|
||||
src: templates/client.conf.j2
|
||||
dest: "{{ client_dir }}/{{ invoice_id }}/wg0.conf"
|
||||
mode: '0600'
|
||||
vars:
|
||||
client_ip: "10.8.0.{{ next_ip.stdout }}"
|
||||
server_pubkey: "{{ server_public_key_read.stdout }}"
|
||||
|
||||
- name: Add client to server config
|
||||
blockinfile:
|
||||
path: "{{ server_dir }}/{{ wg_interface }}.conf"
|
||||
marker: "# {mark} ANSIBLE MANAGED BLOCK FOR {{ invoice_id }}"
|
||||
block: |
|
||||
[Peer]
|
||||
PublicKey = {{ client_public_key.stdout }}
|
||||
AllowedIPs = 10.8.0.{{ next_ip.stdout }}/32
|
||||
notify: restart wireguard
|
||||
|
||||
handlers:
|
||||
- name: restart wireguard
|
||||
service:
|
||||
name: wg-quick@{{ wg_interface }}
|
||||
state: restarted
|
0
app/__init__.py
Normal file
0
app/__init__.py
Normal file
0
app/handlers/__init__.py
Normal file
0
app/handlers/__init__.py
Normal file
182
app/handlers/webhook_handler.py
Normal file
182
app/handlers/webhook_handler.py
Normal file
@ -0,0 +1,182 @@
|
||||
# app/handlers/webhook_handler.py
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
sys.path.append(str(Path(__file__).resolve().parent.parent.parent))
|
||||
|
||||
from flask import Flask, request, jsonify
|
||||
import subprocess
|
||||
import os
|
||||
import logging
|
||||
import json
|
||||
from pathlib import Path
|
||||
import hmac
|
||||
import hashlib
|
||||
import yaml
|
||||
import traceback
|
||||
import tempfile
|
||||
from dotenv import load_dotenv
|
||||
from app.utils.vault_helper import decrypt_vault_file
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# Update paths for new structure
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent.parent
|
||||
PLAYBOOK_PATH = BASE_DIR / 'ansible' / 'playbooks' / 'vpn_provision.yml'
|
||||
VAULT_PATH = BASE_DIR / 'ansible' / 'group_vars' / 'vpn_servers' / 'vault.yml'
|
||||
INVENTORY_PATH = BASE_DIR / 'inventory.ini'
|
||||
|
||||
def get_vault_values():
|
||||
"""Get decrypted values from Ansible vault"""
|
||||
try:
|
||||
# Use vault helper to decrypt contents
|
||||
vault_contents = decrypt_vault_file(VAULT_PATH)
|
||||
vault_data = yaml.safe_load(vault_contents)
|
||||
|
||||
# Construct full webhook URL
|
||||
vault_data['webhook_full_url'] = (
|
||||
f"{vault_data['btcpay_base_url']}"
|
||||
f"{vault_data['btcpay_webhook_path']}"
|
||||
)
|
||||
|
||||
return vault_data
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error reading vault: {str(e)}")
|
||||
raise
|
||||
|
||||
def verify_signature(payload_body, signature_header):
|
||||
"""Verify BTCPay webhook signature"""
|
||||
try:
|
||||
vault_values = get_vault_values()
|
||||
secret = vault_values['webhook_secret']
|
||||
|
||||
logger.debug(f"Secret type in verify_signature: {type(secret)}")
|
||||
|
||||
expected_signature = hmac.new(
|
||||
secret.encode('utf-8'),
|
||||
payload_body,
|
||||
hashlib.sha256
|
||||
).hexdigest()
|
||||
|
||||
return hmac.compare_digest(
|
||||
signature_header.lower(),
|
||||
f"sha256={expected_signature}".lower()
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Signature verification failed: {str(e)}")
|
||||
return False
|
||||
|
||||
@app.route('/webhook/vpn', methods=['POST'])
|
||||
def handle_payment():
|
||||
try:
|
||||
# Get vault values first to ensure we can access them
|
||||
vault_values = get_vault_values()
|
||||
logger.info(f"Processing webhook on endpoint: {vault_values['webhook_full_url']}")
|
||||
|
||||
# Get the signature from headers
|
||||
signature = request.headers.get('BTCPay-Sig')
|
||||
if not signature:
|
||||
logger.error("Missing BTCPay-Sig header")
|
||||
return jsonify({"error": "Missing signature"}), 401
|
||||
|
||||
# Verify signature
|
||||
is_valid = verify_signature(request.get_data(), signature)
|
||||
if not is_valid:
|
||||
logger.error("Invalid signature")
|
||||
return jsonify({"error": "Invalid signature"}), 401
|
||||
|
||||
data = request.json
|
||||
logger.info(f"Received webhook data: {data}")
|
||||
|
||||
invoice_id = data.get('invoiceId')
|
||||
if not invoice_id:
|
||||
logger.error("Missing invoiceId in webhook data")
|
||||
return jsonify({"error": "Missing invoiceId"}), 400
|
||||
|
||||
# Remove __test__ prefix/suffix for test webhooks
|
||||
if invoice_id.startswith('__test__') and invoice_id.endswith('__test__'):
|
||||
invoice_id = invoice_id[8:-8] # Strip the test markers
|
||||
logger.info(f"Stripped test markers from invoice ID: {invoice_id}")
|
||||
|
||||
# Get vault password from environment
|
||||
vault_pass = os.getenv('ANSIBLE_VAULT_PASSWORD')
|
||||
if not vault_pass:
|
||||
raise Exception("Vault password not found in environment variables")
|
||||
|
||||
# Create temporary vault password file for ansible-playbook
|
||||
with tempfile.NamedTemporaryFile(mode='w', delete=False) as vault_pass_file:
|
||||
vault_pass_file.write(vault_pass)
|
||||
vault_pass_file.flush()
|
||||
|
||||
cmd = [
|
||||
'ansible-playbook',
|
||||
str(PLAYBOOK_PATH),
|
||||
'-i', str(INVENTORY_PATH),
|
||||
'-e', f'invoice_id={invoice_id}',
|
||||
'--vault-password-file', vault_pass_file.name,
|
||||
'-vvv' # Add verbose output
|
||||
]
|
||||
|
||||
logger.info(f"Running ansible-playbook command: {' '.join(cmd)}")
|
||||
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
|
||||
# Clean up the temporary file
|
||||
os.unlink(vault_pass_file.name)
|
||||
|
||||
# Log the complete output regardless of success/failure
|
||||
logger.info(f"Ansible playbook stdout: {result.stdout}")
|
||||
if result.stderr:
|
||||
logger.error(f"Ansible playbook stderr: {result.stderr}")
|
||||
|
||||
if result.returncode != 0:
|
||||
error_msg = f"Ansible playbook failed with return code {result.returncode}"
|
||||
logger.error(error_msg)
|
||||
return jsonify({
|
||||
"error": "Provisioning failed",
|
||||
"details": error_msg,
|
||||
"stdout": result.stdout,
|
||||
"stderr": result.stderr
|
||||
}), 500
|
||||
|
||||
logger.info(f"Successfully processed invoice {invoice_id}")
|
||||
return jsonify({
|
||||
"status": "success",
|
||||
"invoice_id": invoice_id,
|
||||
"message": "VPN provisioning initiated"
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing webhook: {str(e)}")
|
||||
return jsonify({
|
||||
"error": str(e),
|
||||
"traceback": traceback.format_exc()
|
||||
}), 500
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Verify we can read the vault and construct URL before starting
|
||||
try:
|
||||
vault_values = get_vault_values()
|
||||
logger.info(f"Successfully loaded vault values")
|
||||
logger.info(f"Configured webhook URL: {vault_values['webhook_full_url']}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to load vault values: {str(e)}")
|
||||
exit(1)
|
||||
|
||||
logger.info(f"Starting webhook handler, watching for playbook at {PLAYBOOK_PATH}")
|
||||
app.run(host='0.0.0.0', port=5000)
|
0
app/utils/__init__.py
Normal file
0
app/utils/__init__.py
Normal file
39
app/utils/vault_helper.py
Normal file
39
app/utils/vault_helper.py
Normal file
@ -0,0 +1,39 @@
|
||||
"""Helper functions for Ansible vault operations."""
|
||||
import os
|
||||
import tempfile
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
def decrypt_vault_file(vault_file_path):
|
||||
"""
|
||||
Decrypt an Ansible vault file using the password from environment variables.
|
||||
|
||||
Args:
|
||||
vault_file_path (str): Path to the encrypted vault file
|
||||
|
||||
Returns:
|
||||
str: Decrypted contents of the vault file
|
||||
"""
|
||||
vault_pass = os.getenv('ANSIBLE_VAULT_PASSWORD')
|
||||
if not vault_pass:
|
||||
raise ValueError("ANSIBLE_VAULT_PASSWORD environment variable not set")
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode='w', delete=False) as vault_pass_file:
|
||||
vault_pass_file.write(vault_pass)
|
||||
vault_pass_file.flush()
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
['ansible-vault', 'view', str(vault_file_path)],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env={**os.environ, 'ANSIBLE_VAULT_PASSWORD_FILE': vault_pass_file.name}
|
||||
)
|
||||
|
||||
if result.returncode != 0:
|
||||
raise Exception(f"Failed to decrypt vault: {result.stderr}")
|
||||
|
||||
return result.stdout
|
||||
|
||||
finally:
|
||||
os.unlink(vault_pass_file.name)
|
2
inventory.ini
Normal file
2
inventory.ini
Normal file
@ -0,0 +1,2 @@
|
||||
[vpn_servers]
|
||||
localhost ansible_connection=local
|
247
venv/bin/Activate.ps1
Normal file
247
venv/bin/Activate.ps1
Normal file
@ -0,0 +1,247 @@
|
||||
<#
|
||||
.Synopsis
|
||||
Activate a Python virtual environment for the current PowerShell session.
|
||||
|
||||
.Description
|
||||
Pushes the python executable for a virtual environment to the front of the
|
||||
$Env:PATH environment variable and sets the prompt to signify that you are
|
||||
in a Python virtual environment. Makes use of the command line switches as
|
||||
well as the `pyvenv.cfg` file values present in the virtual environment.
|
||||
|
||||
.Parameter VenvDir
|
||||
Path to the directory that contains the virtual environment to activate. The
|
||||
default value for this is the parent of the directory that the Activate.ps1
|
||||
script is located within.
|
||||
|
||||
.Parameter Prompt
|
||||
The prompt prefix to display when this virtual environment is activated. By
|
||||
default, this prompt is the name of the virtual environment folder (VenvDir)
|
||||
surrounded by parentheses and followed by a single space (ie. '(.venv) ').
|
||||
|
||||
.Example
|
||||
Activate.ps1
|
||||
Activates the Python virtual environment that contains the Activate.ps1 script.
|
||||
|
||||
.Example
|
||||
Activate.ps1 -Verbose
|
||||
Activates the Python virtual environment that contains the Activate.ps1 script,
|
||||
and shows extra information about the activation as it executes.
|
||||
|
||||
.Example
|
||||
Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv
|
||||
Activates the Python virtual environment located in the specified location.
|
||||
|
||||
.Example
|
||||
Activate.ps1 -Prompt "MyPython"
|
||||
Activates the Python virtual environment that contains the Activate.ps1 script,
|
||||
and prefixes the current prompt with the specified string (surrounded in
|
||||
parentheses) while the virtual environment is active.
|
||||
|
||||
.Notes
|
||||
On Windows, it may be required to enable this Activate.ps1 script by setting the
|
||||
execution policy for the user. You can do this by issuing the following PowerShell
|
||||
command:
|
||||
|
||||
PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
||||
|
||||
For more information on Execution Policies:
|
||||
https://go.microsoft.com/fwlink/?LinkID=135170
|
||||
|
||||
#>
|
||||
Param(
|
||||
[Parameter(Mandatory = $false)]
|
||||
[String]
|
||||
$VenvDir,
|
||||
[Parameter(Mandatory = $false)]
|
||||
[String]
|
||||
$Prompt
|
||||
)
|
||||
|
||||
<# Function declarations --------------------------------------------------- #>
|
||||
|
||||
<#
|
||||
.Synopsis
|
||||
Remove all shell session elements added by the Activate script, including the
|
||||
addition of the virtual environment's Python executable from the beginning of
|
||||
the PATH variable.
|
||||
|
||||
.Parameter NonDestructive
|
||||
If present, do not remove this function from the global namespace for the
|
||||
session.
|
||||
|
||||
#>
|
||||
function global:deactivate ([switch]$NonDestructive) {
|
||||
# Revert to original values
|
||||
|
||||
# The prior prompt:
|
||||
if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) {
|
||||
Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt
|
||||
Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT
|
||||
}
|
||||
|
||||
# The prior PYTHONHOME:
|
||||
if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) {
|
||||
Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME
|
||||
Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME
|
||||
}
|
||||
|
||||
# The prior PATH:
|
||||
if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) {
|
||||
Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH
|
||||
Remove-Item -Path Env:_OLD_VIRTUAL_PATH
|
||||
}
|
||||
|
||||
# Just remove the VIRTUAL_ENV altogether:
|
||||
if (Test-Path -Path Env:VIRTUAL_ENV) {
|
||||
Remove-Item -Path env:VIRTUAL_ENV
|
||||
}
|
||||
|
||||
# Just remove VIRTUAL_ENV_PROMPT altogether.
|
||||
if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) {
|
||||
Remove-Item -Path env:VIRTUAL_ENV_PROMPT
|
||||
}
|
||||
|
||||
# Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether:
|
||||
if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) {
|
||||
Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force
|
||||
}
|
||||
|
||||
# Leave deactivate function in the global namespace if requested:
|
||||
if (-not $NonDestructive) {
|
||||
Remove-Item -Path function:deactivate
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.Description
|
||||
Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the
|
||||
given folder, and returns them in a map.
|
||||
|
||||
For each line in the pyvenv.cfg file, if that line can be parsed into exactly
|
||||
two strings separated by `=` (with any amount of whitespace surrounding the =)
|
||||
then it is considered a `key = value` line. The left hand string is the key,
|
||||
the right hand is the value.
|
||||
|
||||
If the value starts with a `'` or a `"` then the first and last character is
|
||||
stripped from the value before being captured.
|
||||
|
||||
.Parameter ConfigDir
|
||||
Path to the directory that contains the `pyvenv.cfg` file.
|
||||
#>
|
||||
function Get-PyVenvConfig(
|
||||
[String]
|
||||
$ConfigDir
|
||||
) {
|
||||
Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg"
|
||||
|
||||
# Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue).
|
||||
$pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue
|
||||
|
||||
# An empty map will be returned if no config file is found.
|
||||
$pyvenvConfig = @{ }
|
||||
|
||||
if ($pyvenvConfigPath) {
|
||||
|
||||
Write-Verbose "File exists, parse `key = value` lines"
|
||||
$pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath
|
||||
|
||||
$pyvenvConfigContent | ForEach-Object {
|
||||
$keyval = $PSItem -split "\s*=\s*", 2
|
||||
if ($keyval[0] -and $keyval[1]) {
|
||||
$val = $keyval[1]
|
||||
|
||||
# Remove extraneous quotations around a string value.
|
||||
if ("'""".Contains($val.Substring(0, 1))) {
|
||||
$val = $val.Substring(1, $val.Length - 2)
|
||||
}
|
||||
|
||||
$pyvenvConfig[$keyval[0]] = $val
|
||||
Write-Verbose "Adding Key: '$($keyval[0])'='$val'"
|
||||
}
|
||||
}
|
||||
}
|
||||
return $pyvenvConfig
|
||||
}
|
||||
|
||||
|
||||
<# Begin Activate script --------------------------------------------------- #>
|
||||
|
||||
# Determine the containing directory of this script
|
||||
$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||
$VenvExecDir = Get-Item -Path $VenvExecPath
|
||||
|
||||
Write-Verbose "Activation script is located in path: '$VenvExecPath'"
|
||||
Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)"
|
||||
Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)"
|
||||
|
||||
# Set values required in priority: CmdLine, ConfigFile, Default
|
||||
# First, get the location of the virtual environment, it might not be
|
||||
# VenvExecDir if specified on the command line.
|
||||
if ($VenvDir) {
|
||||
Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values"
|
||||
}
|
||||
else {
|
||||
Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir."
|
||||
$VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/")
|
||||
Write-Verbose "VenvDir=$VenvDir"
|
||||
}
|
||||
|
||||
# Next, read the `pyvenv.cfg` file to determine any required value such
|
||||
# as `prompt`.
|
||||
$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir
|
||||
|
||||
# Next, set the prompt from the command line, or the config file, or
|
||||
# just use the name of the virtual environment folder.
|
||||
if ($Prompt) {
|
||||
Write-Verbose "Prompt specified as argument, using '$Prompt'"
|
||||
}
|
||||
else {
|
||||
Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value"
|
||||
if ($pyvenvCfg -and $pyvenvCfg['prompt']) {
|
||||
Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'"
|
||||
$Prompt = $pyvenvCfg['prompt'];
|
||||
}
|
||||
else {
|
||||
Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)"
|
||||
Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'"
|
||||
$Prompt = Split-Path -Path $venvDir -Leaf
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose "Prompt = '$Prompt'"
|
||||
Write-Verbose "VenvDir='$VenvDir'"
|
||||
|
||||
# Deactivate any currently active virtual environment, but leave the
|
||||
# deactivate function in place.
|
||||
deactivate -nondestructive
|
||||
|
||||
# Now set the environment variable VIRTUAL_ENV, used by many tools to determine
|
||||
# that there is an activated venv.
|
||||
$env:VIRTUAL_ENV = $VenvDir
|
||||
|
||||
if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) {
|
||||
|
||||
Write-Verbose "Setting prompt to '$Prompt'"
|
||||
|
||||
# Set the prompt to include the env name
|
||||
# Make sure _OLD_VIRTUAL_PROMPT is global
|
||||
function global:_OLD_VIRTUAL_PROMPT { "" }
|
||||
Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT
|
||||
New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt
|
||||
|
||||
function global:prompt {
|
||||
Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) "
|
||||
_OLD_VIRTUAL_PROMPT
|
||||
}
|
||||
$env:VIRTUAL_ENV_PROMPT = $Prompt
|
||||
}
|
||||
|
||||
# Clear PYTHONHOME
|
||||
if (Test-Path -Path Env:PYTHONHOME) {
|
||||
Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME
|
||||
Remove-Item -Path Env:PYTHONHOME
|
||||
}
|
||||
|
||||
# Add the venv to the PATH
|
||||
Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH
|
||||
$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH"
|
69
venv/bin/activate
Normal file
69
venv/bin/activate
Normal file
@ -0,0 +1,69 @@
|
||||
# This file must be used with "source bin/activate" *from bash*
|
||||
# you cannot run it directly
|
||||
|
||||
deactivate () {
|
||||
# reset old environment variables
|
||||
if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
|
||||
PATH="${_OLD_VIRTUAL_PATH:-}"
|
||||
export PATH
|
||||
unset _OLD_VIRTUAL_PATH
|
||||
fi
|
||||
if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
|
||||
PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
|
||||
export PYTHONHOME
|
||||
unset _OLD_VIRTUAL_PYTHONHOME
|
||||
fi
|
||||
|
||||
# This should detect bash and zsh, which have a hash command that must
|
||||
# be called to get it to forget past commands. Without forgetting
|
||||
# past commands the $PATH changes we made may not be respected
|
||||
if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
|
||||
hash -r 2> /dev/null
|
||||
fi
|
||||
|
||||
if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
|
||||
PS1="${_OLD_VIRTUAL_PS1:-}"
|
||||
export PS1
|
||||
unset _OLD_VIRTUAL_PS1
|
||||
fi
|
||||
|
||||
unset VIRTUAL_ENV
|
||||
unset VIRTUAL_ENV_PROMPT
|
||||
if [ ! "${1:-}" = "nondestructive" ] ; then
|
||||
# Self destruct!
|
||||
unset -f deactivate
|
||||
fi
|
||||
}
|
||||
|
||||
# unset irrelevant variables
|
||||
deactivate nondestructive
|
||||
|
||||
VIRTUAL_ENV="/home/hashgate/vpn-btcpay-provisioner/venv"
|
||||
export VIRTUAL_ENV
|
||||
|
||||
_OLD_VIRTUAL_PATH="$PATH"
|
||||
PATH="$VIRTUAL_ENV/bin:$PATH"
|
||||
export PATH
|
||||
|
||||
# unset PYTHONHOME if set
|
||||
# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
|
||||
# could use `if (set -u; : $PYTHONHOME) ;` in bash
|
||||
if [ -n "${PYTHONHOME:-}" ] ; then
|
||||
_OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
|
||||
unset PYTHONHOME
|
||||
fi
|
||||
|
||||
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
|
||||
_OLD_VIRTUAL_PS1="${PS1:-}"
|
||||
PS1="(venv) ${PS1:-}"
|
||||
export PS1
|
||||
VIRTUAL_ENV_PROMPT="(venv) "
|
||||
export VIRTUAL_ENV_PROMPT
|
||||
fi
|
||||
|
||||
# This should detect bash and zsh, which have a hash command that must
|
||||
# be called to get it to forget past commands. Without forgetting
|
||||
# past commands the $PATH changes we made may not be respected
|
||||
if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
|
||||
hash -r 2> /dev/null
|
||||
fi
|
26
venv/bin/activate.csh
Normal file
26
venv/bin/activate.csh
Normal file
@ -0,0 +1,26 @@
|
||||
# This file must be used with "source bin/activate.csh" *from csh*.
|
||||
# You cannot run it directly.
|
||||
# Created by Davide Di Blasi <davidedb@gmail.com>.
|
||||
# Ported to Python 3.3 venv by Andrew Svetlov <andrew.svetlov@gmail.com>
|
||||
|
||||
alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate'
|
||||
|
||||
# Unset irrelevant variables.
|
||||
deactivate nondestructive
|
||||
|
||||
setenv VIRTUAL_ENV "/home/hashgate/vpn-btcpay-provisioner/venv"
|
||||
|
||||
set _OLD_VIRTUAL_PATH="$PATH"
|
||||
setenv PATH "$VIRTUAL_ENV/bin:$PATH"
|
||||
|
||||
|
||||
set _OLD_VIRTUAL_PROMPT="$prompt"
|
||||
|
||||
if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then
|
||||
set prompt = "(venv) $prompt"
|
||||
setenv VIRTUAL_ENV_PROMPT "(venv) "
|
||||
endif
|
||||
|
||||
alias pydoc python -m pydoc
|
||||
|
||||
rehash
|
69
venv/bin/activate.fish
Normal file
69
venv/bin/activate.fish
Normal file
@ -0,0 +1,69 @@
|
||||
# This file must be used with "source <venv>/bin/activate.fish" *from fish*
|
||||
# (https://fishshell.com/); you cannot run it directly.
|
||||
|
||||
function deactivate -d "Exit virtual environment and return to normal shell environment"
|
||||
# reset old environment variables
|
||||
if test -n "$_OLD_VIRTUAL_PATH"
|
||||
set -gx PATH $_OLD_VIRTUAL_PATH
|
||||
set -e _OLD_VIRTUAL_PATH
|
||||
end
|
||||
if test -n "$_OLD_VIRTUAL_PYTHONHOME"
|
||||
set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME
|
||||
set -e _OLD_VIRTUAL_PYTHONHOME
|
||||
end
|
||||
|
||||
if test -n "$_OLD_FISH_PROMPT_OVERRIDE"
|
||||
set -e _OLD_FISH_PROMPT_OVERRIDE
|
||||
# prevents error when using nested fish instances (Issue #93858)
|
||||
if functions -q _old_fish_prompt
|
||||
functions -e fish_prompt
|
||||
functions -c _old_fish_prompt fish_prompt
|
||||
functions -e _old_fish_prompt
|
||||
end
|
||||
end
|
||||
|
||||
set -e VIRTUAL_ENV
|
||||
set -e VIRTUAL_ENV_PROMPT
|
||||
if test "$argv[1]" != "nondestructive"
|
||||
# Self-destruct!
|
||||
functions -e deactivate
|
||||
end
|
||||
end
|
||||
|
||||
# Unset irrelevant variables.
|
||||
deactivate nondestructive
|
||||
|
||||
set -gx VIRTUAL_ENV "/home/hashgate/vpn-btcpay-provisioner/venv"
|
||||
|
||||
set -gx _OLD_VIRTUAL_PATH $PATH
|
||||
set -gx PATH "$VIRTUAL_ENV/bin" $PATH
|
||||
|
||||
# Unset PYTHONHOME if set.
|
||||
if set -q PYTHONHOME
|
||||
set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME
|
||||
set -e PYTHONHOME
|
||||
end
|
||||
|
||||
if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
|
||||
# fish uses a function instead of an env var to generate the prompt.
|
||||
|
||||
# Save the current fish_prompt function as the function _old_fish_prompt.
|
||||
functions -c fish_prompt _old_fish_prompt
|
||||
|
||||
# With the original prompt function renamed, we can override with our own.
|
||||
function fish_prompt
|
||||
# Save the return status of the last command.
|
||||
set -l old_status $status
|
||||
|
||||
# Output the venv prompt; color taken from the blue of the Python logo.
|
||||
printf "%s%s%s" (set_color 4B8BBE) "(venv) " (set_color normal)
|
||||
|
||||
# Restore the return status of the previous command.
|
||||
echo "exit $old_status" | .
|
||||
# Output the original/"old" prompt.
|
||||
_old_fish_prompt
|
||||
end
|
||||
|
||||
set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
|
||||
set -gx VIRTUAL_ENV_PROMPT "(venv) "
|
||||
end
|
8
venv/bin/ansible
Executable file
8
venv/bin/ansible
Executable file
@ -0,0 +1,8 @@
|
||||
#!/home/hashgate/vpn-btcpay-provisioner/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from ansible.cli.adhoc import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
8
venv/bin/ansible-community
Executable file
8
venv/bin/ansible-community
Executable file
@ -0,0 +1,8 @@
|
||||
#!/home/hashgate/vpn-btcpay-provisioner/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from ansible_collections.ansible_community import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
8
venv/bin/ansible-config
Executable file
8
venv/bin/ansible-config
Executable file
@ -0,0 +1,8 @@
|
||||
#!/home/hashgate/vpn-btcpay-provisioner/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from ansible.cli.config import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
8
venv/bin/ansible-connection
Executable file
8
venv/bin/ansible-connection
Executable file
@ -0,0 +1,8 @@
|
||||
#!/home/hashgate/vpn-btcpay-provisioner/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from ansible.cli.scripts.ansible_connection_cli_stub import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
8
venv/bin/ansible-console
Executable file
8
venv/bin/ansible-console
Executable file
@ -0,0 +1,8 @@
|
||||
#!/home/hashgate/vpn-btcpay-provisioner/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from ansible.cli.console import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
8
venv/bin/ansible-doc
Executable file
8
venv/bin/ansible-doc
Executable file
@ -0,0 +1,8 @@
|
||||
#!/home/hashgate/vpn-btcpay-provisioner/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from ansible.cli.doc import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
8
venv/bin/ansible-galaxy
Executable file
8
venv/bin/ansible-galaxy
Executable file
@ -0,0 +1,8 @@
|
||||
#!/home/hashgate/vpn-btcpay-provisioner/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from ansible.cli.galaxy import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
8
venv/bin/ansible-inventory
Executable file
8
venv/bin/ansible-inventory
Executable file
@ -0,0 +1,8 @@
|
||||
#!/home/hashgate/vpn-btcpay-provisioner/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from ansible.cli.inventory import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
8
venv/bin/ansible-playbook
Executable file
8
venv/bin/ansible-playbook
Executable file
@ -0,0 +1,8 @@
|
||||
#!/home/hashgate/vpn-btcpay-provisioner/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from ansible.cli.playbook import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
8
venv/bin/ansible-pull
Executable file
8
venv/bin/ansible-pull
Executable file
@ -0,0 +1,8 @@
|
||||
#!/home/hashgate/vpn-btcpay-provisioner/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from ansible.cli.pull import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
45
venv/bin/ansible-test
Executable file
45
venv/bin/ansible-test
Executable file
@ -0,0 +1,45 @@
|
||||
#!/home/hashgate/vpn-btcpay-provisioner/venv/bin/python3
|
||||
# PYTHON_ARGCOMPLETE_OK
|
||||
"""Command line entry point for ansible-test."""
|
||||
|
||||
# NOTE: This file resides in the _util/target directory to ensure compatibility with all supported Python versions.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main(args=None):
|
||||
"""Main program entry point."""
|
||||
ansible_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
source_root = os.path.join(ansible_root, 'test', 'lib')
|
||||
|
||||
if os.path.exists(os.path.join(source_root, 'ansible_test', '_internal', '__init__.py')):
|
||||
# running from source, use that version of ansible-test instead of any version that may already be installed
|
||||
sys.path.insert(0, source_root)
|
||||
|
||||
# noinspection PyProtectedMember
|
||||
from ansible_test._util.target.common.constants import CONTROLLER_PYTHON_VERSIONS
|
||||
|
||||
if version_to_str(sys.version_info[:2]) not in CONTROLLER_PYTHON_VERSIONS:
|
||||
raise SystemExit('This version of ansible-test cannot be executed with Python version %s. Supported Python versions are: %s' % (
|
||||
version_to_str(sys.version_info[:3]), ', '.join(CONTROLLER_PYTHON_VERSIONS)))
|
||||
|
||||
if any(not os.get_blocking(handle.fileno()) for handle in (sys.stdin, sys.stdout, sys.stderr)):
|
||||
raise SystemExit('Standard input, output and error file handles must be blocking to run ansible-test.')
|
||||
|
||||
# noinspection PyProtectedMember
|
||||
from ansible_test._internal import main as cli_main
|
||||
|
||||
cli_main(args)
|
||||
|
||||
|
||||
def version_to_str(version):
|
||||
"""Return a version string from a version tuple."""
|
||||
return '.'.join(str(n) for n in version)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
8
venv/bin/ansible-vault
Executable file
8
venv/bin/ansible-vault
Executable file
@ -0,0 +1,8 @@
|
||||
#!/home/hashgate/vpn-btcpay-provisioner/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from ansible.cli.vault import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
8
venv/bin/dotenv
Executable file
8
venv/bin/dotenv
Executable file
@ -0,0 +1,8 @@
|
||||
#!/home/hashgate/vpn-btcpay-provisioner/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from dotenv.__main__ import cli
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(cli())
|
8
venv/bin/flask
Executable file
8
venv/bin/flask
Executable file
@ -0,0 +1,8 @@
|
||||
#!/home/hashgate/vpn-btcpay-provisioner/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from flask.cli import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
8
venv/bin/pip
Executable file
8
venv/bin/pip
Executable file
@ -0,0 +1,8 @@
|
||||
#!/home/hashgate/vpn-btcpay-provisioner/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from pip._internal.cli.main import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
8
venv/bin/pip3
Executable file
8
venv/bin/pip3
Executable file
@ -0,0 +1,8 @@
|
||||
#!/home/hashgate/vpn-btcpay-provisioner/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from pip._internal.cli.main import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
8
venv/bin/pip3.11
Executable file
8
venv/bin/pip3.11
Executable file
@ -0,0 +1,8 @@
|
||||
#!/home/hashgate/vpn-btcpay-provisioner/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from pip._internal.cli.main import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
1
venv/bin/python
Symbolic link
1
venv/bin/python
Symbolic link
@ -0,0 +1 @@
|
||||
python3
|
1
venv/bin/python3
Symbolic link
1
venv/bin/python3
Symbolic link
@ -0,0 +1 @@
|
||||
/usr/bin/python3
|
1
venv/bin/python3.11
Symbolic link
1
venv/bin/python3.11
Symbolic link
@ -0,0 +1 @@
|
||||
python3
|
1
venv/lib64
Symbolic link
1
venv/lib64
Symbolic link
@ -0,0 +1 @@
|
||||
lib
|
5
venv/pyvenv.cfg
Normal file
5
venv/pyvenv.cfg
Normal file
@ -0,0 +1,5 @@
|
||||
home = /usr/bin
|
||||
include-system-site-packages = false
|
||||
version = 3.11.2
|
||||
executable = /usr/bin/python3.11
|
||||
command = /usr/bin/python3 -m venv /home/hashgate/vpn-btcpay-provisioner/venv
|
Loading…
Reference in New Issue
Block a user