百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

Qt开发-Actions自动化发行

bigegpt 2024-08-11 14:25 11 浏览

简介

在上一篇文章《github-Actions自动化编译》中,介绍了github-Actions的基本用法,

本文来介绍github-Actions的自动化发布。

Qt项目的编译流程

先来回顾一下,上一篇文章中的Qt项目的编译流程

  1. 安装Qt环境
  2. 这一步用第三方Action模板:install-qt-action
  3. 获取项目代码
  4. 这一步用Actions官方核心模板:actions/checkout
  5. 执行qmake、make
  6. 这一步用自定义脚本,也可以换成cmake、qbs、gn、ninja等构建工具
  7. 执行test
  8. 这一步可以引入单元测试、自动化UI测试等。暂无完善的方案,以后再说。
  9. 发布
  10. 见下文。

Qt项目的发布流程

Qt程序在编译完成后,发布的大致流程是:

1、 查找依赖库

2、制作压缩包或者安装包

3、上传压缩包或者安装包到网站、网盘。

查找依赖

Qt官方提供的查找依赖库的命令行工具,包括:Windows平台的Windeployqt、MacOS平台的Macosdeployqt。

在这两个平台,只使用Qt库的情况下,这两个工具足够了。

制作包

做压缩包比较简单。(我们常说的‘绿色软件’,就是一个压缩包)

一般安装7z、rar之类的压缩工具,用一条命令行就行了。

涛哥这里再说一下,github-Actions给所有平台都提供了PowerShell,而PowerShell内置了压缩命令Compress-Archive。

使用也很简单,只要路径和名字,例如:

Compress-Archive -Path .\MyFolder 'MyRelease.zip'

做安装包,Qt官方有功能很全面的安装包制作工具:QtInstallFrameWork, 稍微翻看一下文档或者例子即可。本文先不展开了。

上传

github 本身提供了’Release’功能,每个仓库都有一个’Release’页面

可以将打包好的压缩包或者安装包,直接上传上去, 供他人下载。

github-Actions还提供了 创建’Release’、上传’Release’的模板

actions/create-release

actions/upload-release-asset

这两个模板的用法也很简单,在yml文件中直接use就行了,不赘述了。

定制发布流程

前面介绍了一些简单的理论,接下来通过实例,教大家github-Actions的使用。

以HelloActions-Qt项目为例,做一些定制。

需求如下:

1、每次提交代码,同时在Windows、MacOS、Ubuntu、Android、IOS五个平台编译

2、每次提交tag,在windows和MacOS平台制作软件包,并发布到同一个github-‘Release’

需求1已经实现了,着重讨论一下需求2:

发布时机

‘每次提交tag’限定了发布的时机。

涛哥尝试了一番,最终得到答案。

回顾一下, Windows平台的编译配置:

name: Windows
on: [push,pull_request]
jobs:
  build:
    name: Build
    runs-on: windows-latest
    strategy:
      matrix:
        qt_ver: [5.12.6]
        qt_target: [desktop]
        qt_arch: [win64_msvc2017_64, win32_msvc2017]
        include:
          - qt_arch: win64_msvc2017_64
            msvc_arch: x64
          - qt_arch: win32_msvc2017
            msvc_arch: x86
    # 步骤
    steps:
      # 安装Qt
      - name: Install Qt
        uses: jurplel/install-qt-action@v2.0.0
        with:
          version: ${{ matrix.qt_ver }}
          target: ${{ matrix.qt_target }}
          arch: ${{ matrix.qt_arch }}
      # 拉取代码
      - uses: actions/checkout@v1
        with:
          fetch-depth: 1
      # 编译msvc
      - name: build-msvc
        shell: cmd
        env:
          vc_arch: ${{ matrix.msvc_arch }}
        run: |
          call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" %vc_arch%
          qmake
          nmake

steps中的每一个步骤,可以有触发条件。我们可以在这里指定,只有github的事件为tag时才执行:

steps:
  。。。
  # tag 打包
  - name: package
    if: startsWith(github.event.ref, 'refs/tags/')
    run: |
      。。。

打包步骤

这里给出一个实际的打包步骤:

# tag 打包
- name: package
  if: startsWith(github.event.ref, 'refs/tags/')
  env:
    VCINSTALLDIR: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC'
    archiveName: ${{ matrix.qt_ver }}-${{ matrix.qt_target }}-${{ matrix.qt_arch }}
    targetName: HelloActions-Qt.exe
  shell: pwsh
  run: |
    # 创建文件夹
    New-Item -ItemType Directory ${env:archiveName}
    # 拷贝exe
    Copy-Item bin\${env:targetName} ${env:archiveName}\
    # 拷贝依赖
    windeployqt --qmldir . ${env:archiveName}\${env:targetName}
    # 打包zip
    Compress-Archive -Path ${env:archiveName} ${env:archiveName}'.zip'
    # 记录环境变量packageName给后续step
    $name = ${env:archiveName}
    echo "::set-env name=packageName::$name"
    # 打印环境变量packageName
    Write-Host 'packageName:'${env:packageName}

做一些说明:

  • vs运行时

其中的VCINSTALLDIR环境变量,是给windeployqt用的。有了这个环境变量,windeployqt会去msvc的安装路径提取‘运行时安装程序’。

  • 记录包名称

打包完以后,将包名设置为环境变量,后续的步骤就可以通过环境变量拿到包名字了。

普通的设置环境变量,在步骤执行完成后就失效了,

这里使用github-Actions的‘记录命令’set-env ,具体可以参考文档github-Actions记录命令

文档说不要用双引号,应该都是针对linux的,我试出来的PowerShell用法如下:

先取环境变量到一个局部变量,再在‘记录命令’中引用局部变量。

$name = ${env:archiveName}
echo "::set-env name=packageName::$name"

多平台发布

如果只有一个平台、一种配置,直接用那两个模板就能解决问题。

这是官方给的例子upload-release-asset:

steps:
  - name: Checkout code
    uses: actions/checkout@master
  - name: Build project # This would actually build your project, using zip for an example artifact
    run: |
      zip --junk-paths my-artifact README.md
  - name: Create Release
    id: create_release
    uses: actions/create-release@v1.0.0
    env:
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    with:
      tag_name: ${{ github.ref }}
      release_name: Release ${{ github.ref }}
      draft: false
      prerelease: false
  - name: Upload Release Asset
    id: upload-release-asset 
    uses: actions/upload-release-asset@v1.0.1
    env:
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    with:
      upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps 
      asset_path: ./my-artifact.zip
      asset_name: my-artifact.zip
      asset_content_type: application/zip

在多平台 或者 多配置的情况下,同一个tag, 只有第一个执行create-release的任务可以成功,后续任务

再次执行create-release时,该tag下已经有了同名的‘Release’,所以会create失败。

这个问题折磨了涛哥好一阵子。找不到现成的解决方案,涛哥就自己实现了一种:

  1. 先用github的REST API去判断该tag下有没有‘Release’:
  2. 没有则执行create-release,并提取upload_url;
  3. 有则提取upload_url。
  4. 最后执行upload-release-asset

调用REST API,涛哥依旧使用了方便的PowerShell,

实际的配置如下:

# tag 查询github-Release
- name: queryReleaseWin
  id: queryReleaseWin
  if: startsWith(github.event.ref, 'refs/tags/')
  shell: pwsh
  env:
    githubFullName: ${{ github.event.repository.full_name }}
    ref: ${{ github.event.ref }}
  run: |
    [string]$tag = ${env:ref}.Substring(${env:ref}.LastIndexOf('/') + 1)
    [string]$url = 'https://api.github.com/repos/' + ${env:githubFullName} + '/releases/tags/' + ${tag}
    $response={}
    try {
      $response = Invoke-RestMethod -Uri $url -Method Get
    } catch {
      Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__ 
      Write-Host "StatusDescription:" $_.Exception.Response.StatusDescription
      # 没查到,输出
      echo "::set-output name=needCreateRelease::true"  
      return
    }
    [string]$latestUpUrl = $response.upload_url
    Write-Host 'latestUpUrl:'$latestUpUrl
    if ($latestUpUrl.Length -eq 0) {
      # 没查到,输出
      echo "::set-output name=needCreateRelease::true"  
    }
# tag 创建github-Release
- name: createReleaseWin
  id: createReleaseWin
  if: startsWith(github.event.ref, 'refs/tags/') && steps.queryReleaseWin.outputs.needCreateRelease == 'true'
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  uses: actions/create-release@v1.0.0
  with:
    tag_name: ${{ github.ref }}
    release_name: Release ${{ github.ref }}
    body: ${{ github.event.head_commit.message }}
    draft: false
    prerelease: false
# tag 重定向upload_url到环境变量uploadUrl。
- name: getLatestTagRelease
  if: startsWith(github.event.ref, 'refs/tags/')
  shell: pwsh
  env:
    githubFullName: ${{ github.event.repository.full_name }}
    upUrl: ${{ steps.createReleaseWin.outputs.upload_url }}
    ref: ${{ github.event.ref }}
  run: |
    # upUrl不为空,导出就完事
    if (${env:upUrl}.Length -gt 0) {
        $v=${env:upUrl}
        echo "::set-env name=uploadUrl::$v"
        return
    } 
    # upUrl为空则重新获取
    [string]$tag = ${env:ref}.Substring(${env:ref}.LastIndexOf('/') + 1)
    [string]$url = 'https://api.github.com/repos/' + ${env:githubFullName} + '/releases/tags/' + ${tag}
    $response = Invoke-RestMethod -Uri $url -Method Get
    [string]$latestUpUrl = $response.upload_url
    Write-Host 'latestUpUrl:'$latestUpUrl
    # 导出
    echo "::set-env name=uploadUrl::$latestUpUrl"
    Write-Host 'env uploadUrl:'${env:uploadUrl}
# tag 上传Release
- name: uploadRelease
  id: uploadRelease
  if: startsWith(github.event.ref, 'refs/tags/')
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  uses: actions/upload-release-asset@v1.0.1
  with:
    upload_url: ${{ env.uploadUrl }}
    asset_path: ./${{ env.packageName }}.zip
    asset_name: ${{ env.packageName }}.zip
    asset_content_type: application/zip

最终配置

windows版的最终配置

name: Windows
on: 
  # push代码时触发workflow
  push:
    # 忽略README.md
    paths-ignore:
      - 'README.md'
      - 'LICENSE'
  # pull_request时触发workflow
  pull_request:
    # 忽略README.md
    paths-ignore:
      - 'README.md'
      - 'LICENSE'
jobs:
  build:
    name: Build
    # 运行平台, windows-latest目前是windows server 2019
    runs-on: windows-latest
    strategy:
      # 矩阵配置
      matrix:
        qt_ver: [5.12.6]
        qt_target: [desktop]
        qt_arch: [win64_msvc2017_64, win32_msvc2017]
        # 额外设置msvc_arch
        include:
          - qt_arch: win64_msvc2017_64
            msvc_arch: x64
            qt_arch_install: msvc2017_64
          - qt_arch: win32_msvc2017
            msvc_arch: x86
            qt_arch_install: msvc2017
    env:
      targetName: HelloActions-Qt.exe
    # 步骤
    steps:  
      # 安装Qt
      - name: Install Qt
        # 使用外部action。这个action专门用来安装Qt
        uses: jurplel/install-qt-action@v2.0.0
        with:
          # Version of Qt to install
          version: ${{ matrix.qt_ver }}
          # Target platform for build
          target: ${{ matrix.qt_target }}
          # Architecture for Windows/Android
          arch: ${{ matrix.qt_arch }}
      # 拉取代码
      - uses: actions/checkout@v1
        with:
          fetch-depth: 1
      # 编译msvc
      - name: build-msvc
        shell: cmd
        env:
          vc_arch: ${{ matrix.msvc_arch }}
        run: |
          call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" %vc_arch%
          qmake
          nmake
      # tag 打包
      - name: package
        if: startsWith(github.event.ref, 'refs/tags/')
        env:
          VCINSTALLDIR: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC'
          archiveName: ${{ matrix.qt_ver }}-${{ matrix.qt_target }}-${{ matrix.qt_arch }}
        shell: pwsh
        run: |
          # 创建文件夹
          New-Item -ItemType Directory ${env:archiveName}
          # 拷贝exe
          Copy-Item bin\${env:targetName} ${env:archiveName}\
          # 拷贝依赖
          windeployqt --qmldir . ${env:archiveName}\${env:targetName}
          # 打包zip
          Compress-Archive -Path ${env:archiveName} ${env:archiveName}'.zip'
          # 记录环境变量packageName给后续step
          $name = ${env:archiveName}
          echo "::set-env name=packageName::$name"
          # 打印环境变量packageName
          Write-Host 'packageName:'${env:packageName}
      # tag 查询github-Release
      - name: queryReleaseWin
        id: queryReleaseWin
        if: startsWith(github.event.ref, 'refs/tags/')
        shell: pwsh
        env:
          githubFullName: ${{ github.event.repository.full_name }}
          ref: ${{ github.event.ref }}
        run: |
          [string]$tag = ${env:ref}.Substring(${env:ref}.LastIndexOf('/') + 1)
          [string]$url = 'https://api.github.com/repos/' + ${env:githubFullName} + '/releases/tags/' + ${tag}
          $response={}
          try {
            $response = Invoke-RestMethod -Uri $url -Method Get
          } catch {
            Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__ 
            Write-Host "StatusDescription:" $_.Exception.Response.StatusDescription
            # 没查到,输出
            echo "::set-output name=needCreateRelease::true"  
            return
          }
          [string]$latestUpUrl = $response.upload_url
          Write-Host 'latestUpUrl:'$latestUpUrl
          if ($latestUpUrl.Length -eq 0) {
            # 没查到,输出
            echo "::set-output name=needCreateRelease::true"  
          }
      # tag 创建github-Release
      - name: createReleaseWin
        id: createReleaseWin
        if: startsWith(github.event.ref, 'refs/tags/') && steps.queryReleaseWin.outputs.needCreateRelease == 'true'
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        uses: actions/create-release@v1.0.0
        with:
          tag_name: ${{ github.ref }}
          release_name: Release ${{ github.ref }}
          body: ${{ github.event.head_commit.message }}
          draft: false
          prerelease: false
      # 重定向upload_url到环境变量uploadUrl。
      - name: getLatestTagRelease
        # tag 上一步无论成功还是失败都执行
        if: startsWith(github.event.ref, 'refs/tags/')
        shell: pwsh
        env:
          githubFullName: ${{ github.event.repository.full_name }}
          upUrl: ${{ steps.createReleaseWin.outputs.upload_url }}
          ref: ${{ github.event.ref }}
        run: |
          # upUrl不为空,导出就完事
          if (${env:upUrl}.Length -gt 0) {
              $v=${env:upUrl}
              echo "::set-env name=uploadUrl::$v"
              return
          } 
          [string]$tag = ${env:ref}.Substring(${env:ref}.LastIndexOf('/') + 1)
          [string]$url = 'https://api.github.com/repos/' + ${env:githubFullName} + '/releases/tags/' + ${tag}
          $response = Invoke-RestMethod -Uri $url -Method Get
          [string]$latestUpUrl = $response.upload_url
          Write-Host 'latestUpUrl:'$latestUpUrl
          echo "::set-env name=uploadUrl::$latestUpUrl"
          Write-Host 'env uploadUrl:'${env:uploadUrl}
      # tag 上传Release
      - name: uploadRelease
        id: uploadRelease
        if: startsWith(github.event.ref, 'refs/tags/')
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        uses: actions/upload-release-asset@v1.0.1
        with:
          upload_url: ${{ env.uploadUrl }}
          asset_path: ./${{ env.packageName }}.zip
          asset_name: ${{ env.packageName }}.zip
          asset_content_type: application/zip

MacOS最终配置

name: MacOS
on: 
  push:
    paths-ignore:
      - 'README.md'
      - 'LICENSE'
  pull_request:
    paths-ignore:
      - 'README.md'
      - 'LICENSE'
jobs:
  build:
    name: Build
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [macos-latest]
        qt_ver: [5.12.6]
        qt_arch: [clang_64]
    env:
      targetName: HelloActions-Qt
    steps:
      - name: Install Qt
        uses: jurplel/install-qt-action@v2.0.0
        with:
          version: ${{ matrix.qt_ver }}
      - uses: actions/checkout@v1
        with:
          fetch-depth: 1
      - name: build macos
        run: |
          qmake
          make
      # tag 打包
      - name: package
        if: startsWith(github.event.ref, 'refs/tags/')
        run: |
          # 拷贝依赖
          macdeployqt bin/${targetName}.app -qmldir=. -verbose=1 -dmg
      # tag 查询github-Release
      - name: queryRelease
        id: queryReleaseMacos
        if: startsWith(github.event.ref, 'refs/tags/')
        shell: pwsh
        env:
          githubFullName: ${{ github.event.repository.full_name }}
          ref: ${{ github.event.ref }}
        run: |
          [string]$tag = ${env:ref}.Substring(${env:ref}.LastIndexOf('/') + 1)
          [string]$url = 'https://api.github.com/repos/' + ${env:githubFullName} + '/releases/tags/' + ${tag}
          $response={}
          try {
            $response = Invoke-RestMethod -Uri $url -Method Get
          } catch {
            Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__ 
            Write-Host "StatusDescription:" $_.Exception.Response.StatusDescription
            # 没查到,输出
            echo "::set-output name=needCreateRelease::true"  
            return
          }
          [string]$latestUpUrl = $response.upload_url
          Write-Host 'latestUpUrl:'$latestUpUrl
          if ($latestUpUrl.Length -eq 0) {
            # 没查到,输出
            echo "::set-output name=needCreateRelease::true"  
          }
      # tag 创建github-Release
      - name: createReleaseWin
        id: createReleaseWin
        if: startsWith(github.event.ref, 'refs/tags/') && steps.queryReleaseMacos.outputs.needCreateRelease == 'true'
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        uses: actions/create-release@v1.0.0
        with:
          tag_name: ${{ github.ref }}
          release_name: Release ${{ github.ref }}
          body: ${{ github.event.head_commit.message }}
          draft: false
          prerelease: false
      # 重定向upload_url到环境变量uploadUrl。
      - name: getLatestTagRelease
        # tag 上一步无论成功还是失败都执行
        if: startsWith(github.event.ref, 'refs/tags/')
        shell: pwsh
        env:
          githubFullName: ${{ github.event.repository.full_name }}
          upUrl: ${{ steps.queryReleaseMacos.outputs.upload_url }}
          ref: ${{ github.event.ref }}
        run: |
          # upUrl不为空,导出就完事
          if (${env:upUrl}.Length -gt 0) {
              $v=${env:upUrl}
              echo "::set-env name=uploadUrl::$v"
              return
          } 
          [string]$tag = ${env:ref}.Substring(${env:ref}.LastIndexOf('/') + 1)
          [string]$url = 'https://api.github.com/repos/' + ${env:githubFullName} + '/releases/tags/' + ${tag}
          $response = Invoke-RestMethod -Uri $url -Method Get
          [string]$latestUpUrl = $response.upload_url
          Write-Host 'latestUpUrl:'$latestUpUrl
          echo "::set-env name=uploadUrl::$latestUpUrl"
          Write-Host 'env uploadUrl:'${env:uploadUrl}
      # tag 上传Release
      - name: uploadRelease
        id: uploadRelease
        if: startsWith(github.event.ref, 'refs/tags/')
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        uses: actions/upload-release-asset@v1.0.1
        with:
          upload_url: ${{ env.uploadUrl }}
          asset_path: ./bin/${{ env.targetName }}.dmg
          asset_name: ${{ env.targetName }}.dmg
          asset_content_type: application/applefile

结果和代码

代码在github HelloActions-Qt

另外在涛哥的Qml控件库TaoQuick,也使用了这一套配置

【领QT开发教程学习资料,点击下方链接莬费领取↓↓,先码住不迷路~】

点击这里:「链接」

相关推荐

C#.NET Autofac 详解(c# autoit)

简介Autofac是一个成熟的、功能丰富的.NET依赖注入(DI)容器。相比于内置容器,它额外提供:模块化注册、装饰器(Decorator)、拦截器(Interceptor)、强o的属性/方法注...

webapi 全流程(webapi怎么部署)

C#中的WebAPIMinimalApi没有控制器,普通api有控制器,MinimalApi是直达型,精简了很多中间代码,广泛适用于微服务架构MinimalApi一切都在组控制台应用程序类【Progr...

.NET外挂系列:3. 了解 harmony 中灵活的纯手工注入方式

一:背景1.讲故事上一篇我们讲到了注解特性,harmony在内部提供了20个HarmonyPatch重载方法尽可能的让大家满足业务开发,那时候我也说了,特性虽然简单粗暴,但只能解决95%...

C# 使用SemanticKernel调用本地大模型deepseek

一、先使用ollama部署好deepseek大模型。具体部署请看前面的头条使用ollama进行本地化部署deepseek大模型二、创建一个空的控制台dotnetnewconsole//添加依赖...

C#.NET 中间件详解(.net core中间件use和run)

简介中间件(Middleware)是ASP.NETCore的核心组件,用于处理HTTP请求和响应的管道机制。它是基于管道模型的轻量级、模块化设计,允许开发者在请求处理过程中插入自定义逻辑。...

IoC 自动注入:让依赖注册不再重复劳动

在ASP.NETCore中,IoC(控制反转)功能通过依赖注入(DI)实现。ASP.NETCore有一个内置的依赖注入容器,可以自动完成依赖注入。我们可以结合反射、特性或程序集扫描来实现自动...

C#.NET 依赖注入详解(c#依赖注入的三种方式)

简介在C#.NET中,依赖注入(DependencyInjection,简称DI)是一种设计模式,用于实现控制反转(InversionofControl,IoC),以降低代码耦合、提高可...

C#从零开始实现一个特性的自动注入功能

在现代软件开发中,依赖注入(DependencyInjection,DI)是实现松耦合、模块化和可测试代码的一个重要实践。C#提供了优秀的DI容器,如ASP.NETCore中自带的Micr...

C#.NET 仓储模式详解(c#仓库货物管理系统)

简介仓储模式(RepositoryPattern)是一种数据访问抽象模式,它在领域模型和数据访问层之间创建了一个隔离层,使得领域模型无需直接与数据访问逻辑交互。仓储模式的核心思想是将数据访问逻辑封装...

C#.NET 泛型详解(c# 泛型 滥用)

简介泛型(Generics)是指在类型或方法定义时使用类型参数,以实现类型安全、可重用和高性能的数据结构与算法为什么需要泛型类型安全防止“装箱/拆箱”带来的性能损耗,并在编译时检测类型错误。可重用同一...

数据分析-相关性分析(相关性 分析)

相关性分析是一种统计方法,用于衡量两个或多个变量之间的关系强度和方向。它通过计算相关系数来量化变量间的线性关系,从而帮助理解变量之间的相互影响。相关性分析常用于数据探索和假设检验,是数据分析和统计建模...

geom_smooth()函数-R语言ggplot2快速入门18

在每节,先运行以下这几行程序。library(ggplot2)library(ggpubr)library(ggtext)#用于个性化图表library(dplyr)#用于数据处理p...

规范申报易错要素解析(规范申报易错要素解析)

为什么要规范申报?规范申报是以满足海关监管、征税、统计等工作为目的,纳税义务人及其代理人依法向海关如实申报的行为,也是海关审接单环节依法监管的重要工作。企业申报的内容须符合《中华人民共和国海关进出口货...

「Eurora」海关编码归类 全球海关编码查询 关务服务

  海关编码是什么?  海关编码即HS编码,为编码协调制度的简称。  其全称为《商品名称及编码协调制度的国际公约》(InternationalConventionforHarmonizedCo...

9月1日起,河南省税务部门对豆制品加工业试行新政7类豆制品均适用投入产出法

全媒体记者杨晓川报道9月2日,记者从税务部门获悉,为减轻纳税人税收负担,完善农产品增值税进项税额抵扣机制,根据相关规定,结合我省实际情况,经广泛调查研究和征求意见,从9月1日起,我省税务部门对豆制品...