0

Net::HTTP と Nokogiri を理解するのに問題があります。

Jenkins サーバーに多数のジョブがあります。これらのジョブのブランチ名を定期的に更新する必要があります。UI から行うのは面倒なプロセスなので、Jenkins の config.xml を更新することにしました。

Nokogiri を使用して XML を解析し、XPath を走査して、ノードの値を更新します。ただし、更新された XML を Jenkins にポストしようとすると、次のような 500 エラーが発生します。

Caused by: javax.xml.transform.TransformerException: org.xml.sax.SAXParseExceptionpublicId: -//W3C//DTD HTML 4.0 Transitional//EN; systemId: http://www.w3.org/TR/REC-html40/loose.dtd; lineNumber: 31; columnNumber: 3; The declaration for the entity "HTML.Version" must end with '>'.

これが私がやっていることです:

require "net/http"
require "nokogiri"

uri = URI.parse("http://jenkins.my.domain.web:8080")
http = Net::HTTP.new(uri.host, uri.port)

getQueueRequest = Net::HTTP::Get.new("http://jenkins.my.domain.web:8080/my/job/location/config.xml")
getQueue = http.request(getQueueRequest)

xml_doc = Nokogiri::HTML(getQueue.body)

# Get current branch name
branch_name=xml_doc.at_xpath('//hudson.plugins.git.branchspec/name')

# Get new branch name
print "Enter new branch name "
user_input = gets.chomp
new_branch_name = user_input.downcase

# Set branch name and create xml
branch_name.content=new_branch_name
new_config_xml=xml_doc.to_xml

puts "Logging into Jenkins"

update_branch = Net::HTTP::Post.new("http://jenkins.my.domain.web:8080/my/job/location/config.xml")
update_branch.basic_auth 'username', 'password'
update_branch.body = new_config_xml

response = http.request(update_branch)

puts response.body

リクエスト本文に追加される XML で何かをしなければならない可能性があることは理解していますが、問題を修正する方法がわかりません。

元の XML:

<?xml version='1.0' encoding='UTF-8'?>
<maven2-moduleset plugin="maven-plugin@1.504">
  <actions/>
  <description></description>
  <keepDependencies>false</keepDependencies>
  <properties>
    <hudson.plugins.throttleconcurrents.ThrottleJobProperty plugin="throttle-concurrents@1.7.2">
      <maxConcurrentPerNode>0</maxConcurrentPerNode>
      <maxConcurrentTotal>0</maxConcurrentTotal>
      <categories/>
      <throttleEnabled>false</throttleEnabled>
      <throttleOption>project</throttleOption>
      <configVersion>1</configVersion>
    </hudson.plugins.throttleconcurrents.ThrottleJobProperty>
  </properties>
  <scm class="hudson.plugins.git.GitSCM" plugin="git@1.4.0">
    <configVersion>2</configVersion>
    <userRemoteConfigs>
      <hudson.plugins.git.UserRemoteConfig>
        <name></name>
        <refspec></refspec>
        <url>git@github.com:<ORG_NAME>/<REPO_NAME>.git</url>
      </hudson.plugins.git.UserRemoteConfig>
    </userRemoteConfigs>
    <branches>
      <hudson.plugins.git.BranchSpec>
        <name>release</name>
      </hudson.plugins.git.BranchSpec>
    </branches>
    <disableSubmodules>false</disableSubmodules>
    <recursiveSubmodules>false</recursiveSubmodules>
    <doGenerateSubmoduleConfigurations>false</doGenerateSubmoduleConfigurations>
    <authorOrCommitter>false</authorOrCommitter>
    <clean>false</clean>
    <wipeOutWorkspace>false</wipeOutWorkspace>
    <pruneBranches>false</pruneBranches>
    <remotePoll>false</remotePoll>
    <ignoreNotifyCommit>false</ignoreNotifyCommit>
    <useShallowClone>false</useShallowClone>
    <buildChooser class="hudson.plugins.git.util.DefaultBuildChooser"/>
    <gitTool>Default</gitTool>
    <submoduleCfg class="list"/>
    <relativeTargetDir></relativeTargetDir>
    <reference></reference>
    <excludedRegions></excludedRegions>
    <excludedUsers></excludedUsers>
    <gitConfigName></gitConfigName>
    <gitConfigEmail></gitConfigEmail>
    <skipTag>false</skipTag>
    <includedRegions></includedRegions>
    <scmName></scmName>
  </scm>
  <canRoam>true</canRoam>
  <disabled>false</disabled>
  <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
  <triggers class="vector">
    <hudson.triggers.TimerTrigger>
      <spec>0 22 * * 4</spec>
    </hudson.triggers.TimerTrigger>
  </triggers>
  <concurrentBuild>false</concurrentBuild>
  <rootModule>
    <groupId>com.org.project.test</groupId>
    <artifactId>functest</artifactId>
  </rootModule>
  <goals>clean verify -Dtestsuite=<test_suite_name> -Dbrowser=chrome -Dipaddress=http://<IP_ADDRESS>:4444/wd/hub</goals>
  <mavenName>apache-maven-3.0.4</mavenName>
  <aggregatorStyleBuild>true</aggregatorStyleBuild>
  <incrementalBuild>false</incrementalBuild>
  <perModuleEmail>true</perModuleEmail>
  <ignoreUpstremChanges>false</ignoreUpstremChanges>
  <archivingDisabled>false</archivingDisabled>
  <resolveDependencies>false</resolveDependencies>
  <processPlugins>false</processPlugins>
  <mavenValidationLevel>-1</mavenValidationLevel>
  <runHeadless>false</runHeadless>
  <disableTriggerDownstreamProjects>false</disableTriggerDownstreamProjects>
  <settings class="jenkins.mvn.DefaultSettingsProvider"/>
  <globalSettings class="jenkins.mvn.DefaultGlobalSettingsProvider"/>
  <reporters/>
  <publishers/>
  <buildWrappers/>
  <prebuilders/>
  <postbuilders/>
  <runPostStepsIfResult>
    <name>FAILURE</name>
    <ordinal>2</ordinal>
    <color>RED</color>
  </runPostStepsIfResult>
</maven2-moduleset>

編集とマッサージの後:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<?xml version="1.0" encoding="UTF-8"?>
<html>
  <body>
    <maven2-moduleset plugin="maven-plugin@1.504">
      <actions />
      <description />
      <keepdependencies>false</keepdependencies>
      <properties>
        <hudson.plugins.throttleconcurrents.throttlejobproperty plugin="throttle-concurrents@1.7.2">
          <maxconcurrentpernode>0</maxconcurrentpernode>
          <maxconcurrenttotal>0</maxconcurrenttotal>
          <categories />
          <throttleenabled>false</throttleenabled>
          <throttleoption>project</throttleoption>
          <configversion>1</configversion>
        </hudson.plugins.throttleconcurrents.throttlejobproperty>
      </properties>
      <scm class="hudson.plugins.git.GitSCM" plugin="git@1.4.0">
        <configversion>2</configversion>
        <userremoteconfigs>
          <hudson.plugins.git.userremoteconfig>
            <name />
            <refspec />
            <url>git@github.com:<ORG_NAME>/<REPO_NAME>.git</url>
          </hudson.plugins.git.userremoteconfig>
        </userremoteconfigs>
        <branches>
          <hudson.plugins.git.branchspec>
            <name>master</name>
          </hudson.plugins.git.branchspec>
        </branches>
        <disablesubmodules>false</disablesubmodules>
        <recursivesubmodules>false</recursivesubmodules>
        <dogeneratesubmoduleconfigurations>false</dogeneratesubmoduleconfigurations>
        <authororcommitter>false</authororcommitter>
        <clean>false</clean>
        <wipeoutworkspace>false</wipeoutworkspace>
        <prunebranches>false</prunebranches>
        <remotepoll>false</remotepoll>
        <ignorenotifycommit>false</ignorenotifycommit>
        <useshallowclone>false</useshallowclone>
        <buildchooser class="hudson.plugins.git.util.DefaultBuildChooser" />
        <gittool>Default</gittool>
        <submodulecfg class="list" />
        <relativetargetdir />
        <reference />
        <excludedregions />
        <excludedusers />
        <gitconfigname />
        <gitconfigemail />
        <skiptag>false</skiptag>
        <includedregions />
        <scmname />
      </scm>
      <canroam>true</canroam>
      <disabled>false</disabled>
      <blockbuildwhendownstreambuilding>false</blockbuildwhendownstreambuilding>
      <blockbuildwhenupstreambuilding>false</blockbuildwhenupstreambuilding>
      <triggers class="vector">
        <hudson.triggers.timertrigger>
          <spec>0 22 * * 4</spec>
        </hudson.triggers.timertrigger>
      </triggers>
      <concurrentbuild>false</concurrentbuild>
      <rootmodule>
        <groupid>com.org.project.test</groupid>
        <artifactid>functest</artifactid>
      </rootmodule>
      <goals>clean verify -Dtestsuite=<test_suite_name> -Dbrowser=chrome -Dipaddress=http://<IP_ADDRESS>:4444/wd/hub</goals>
      <mavenname>apache-maven-3.0.4</mavenname>
      <aggregatorstylebuild>true</aggregatorstylebuild>
      <incrementalbuild>false</incrementalbuild>
      <permoduleemail>true</permoduleemail>
      <ignoreupstremchanges>false</ignoreupstremchanges>
      <archivingdisabled>false</archivingdisabled>
      <resolvedependencies>false</resolvedependencies>
      <processplugins>false</processplugins>
      <mavenvalidationlevel>-1</mavenvalidationlevel>
      <runheadless>false</runheadless>
      <disabletriggerdownstreamprojects>false</disabletriggerdownstreamprojects>
      <settings class="jenkins.mvn.DefaultSettingsProvider" />
      <globalsettings class="jenkins.mvn.DefaultGlobalSettingsProvider" />
      <reporters />
      <publishers />
      <buildwrappers />
      <prebuilders />
      <postbuilders />
      <runpoststepsifresult>
        <name>FAILURE</name>
        <ordinal>2</ordinal>
        <color>RED</color>
      </runpoststepsifresult>
    </maven2-moduleset>
  </body>
</html>
4

2 に答える 2

2

Nokogiri::HTML(some_html)またはを使用するNokogiri::XML(some_xml)と、Nokogiri はコンテンツが有効かどうかを確認します。そうでない場合は、そうしようとしてコンテンツの修正を行います。例えば:

require 'nokogiri'

html_fragment = "<p>foo bar</p>"
Nokogiri::HTML(html_fragment).to_html 
# => "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n<html><body><p>foo bar</p></body></html>\n"

ドキュメントが部分的に正しい場合でも、Nokogiri は DOCTYPE ステートメントを追加します。

html = "<html><body><p>foo bar</p></body></html>"
Nokogiri::HTML(html).to_html 
# => "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n<html><body><p>foo bar</p></body></html>\n"

のこぎりにドキュメントを残してもらいたい場合は、フラグメントであることになっているため、そうするように指示します。

Nokogiri::HTML::DocumentFragment.parse(html_fragment).to_html 
# => "<p>foo bar</p>"

または:

xml_fragment = "<x>foo bar</x>"
Nokogiri::XML::DocumentFragment.parse(xml_fragment).to_xml 
# => "<x>foo bar</x>"

Nokogiri は、XML と HTML の処理に関して非常にスマートです。あなたはそれを混同しようとすることができます、そしてそれは一般的に正しいことをします:

xml_fragment = "<x>foo bar</x>"
Nokogiri::HTML::DocumentFragment.parse(xml_fragment).to_xml 
# => "<x>foo bar</x>"

これは、XML を HTML フラグメントとして解析し、それを XML として出力するように指示しています。

とはいえ、ノコギリが不思議なことをしていないことは明らかなので、問題を解決する方法は次のとおりです. まず、XML として解析して、Nokogiri が HTML DOCTYPE 宣言を追加する必要がないと判断するようにします。次に、XML が構文的に正しい場合は、完全なドキュメントとして解析しても問題ないことを Nokogiri に伝えます。

require 'nokogiri'

xml = %{<?xml version='1.0' encoding='UTF-8'?>
<maven2-moduleset plugin="maven-plugin@1.504">
  <actions/>
  <description></description>
  <keepDependencies>false</keepDependencies>
  <properties>
    <hudson.plugins.throttleconcurrents.ThrottleJobProperty plugin="throttle-concurrents@1.7.2">
    </hudson.plugins.throttleconcurrents.ThrottleJobProperty>
  </properties>
</maven2-moduleset>
}
puts Nokogiri::XML.parse(xml).to_xml 

# >> <?xml version="1.0" encoding="UTF-8"?>
# >> <maven2-moduleset plugin="maven-plugin@1.504">
# >>   <actions/>
# >>   <description/>
# >>   <keepDependencies>false</keepDependencies>
# >>   <properties>
# >>     <hudson.plugins.throttleconcurrents.ThrottleJobProperty plugin="throttle-concurrents@1.7.2">
# >>     </hudson.plugins.throttleconcurrents.ThrottleJobProperty>
# >>   </properties>
# >> </maven2-moduleset>

または、フラグメントとして、完全であるため、同じ結果になります。

puts Nokogiri::XML::DocumentFragment.parse(xml).to_xml 

# >> <?xml version='1.0' encoding='UTF-8'?>
# >> <maven2-moduleset plugin="maven-plugin@1.504">
# >>   <actions/>
# >>   <description/>
# >>   <keepDependencies>false</keepDependencies>
# >>   <properties>
# >>     <hudson.plugins.throttleconcurrents.ThrottleJobProperty plugin="throttle-concurrents@1.7.2">
# >>     </hudson.plugins.throttleconcurrents.ThrottleJobProperty>
# >>   </properties>
# >> </maven2-moduleset>

HTTP の基本的な構成要素である Net::HTTP を使用する代わりに、HTTPClient など、もう少し高いレベルのものを検討することをお勧めします。あなたに似たコードは次のとおりです。

require 'httpclient'
require 'nokogiri'

URL = 'http://jenkins.my.domain.web:8080/my/job/location/config.xml'

http_client = HTTPClient.new
xml_doc = Nokogiri::HTML(
  http_client.get_content(URL)
)

# Get current branch name using CSS for simplicity:
branch_name = xml_doc.at('hudson.plugins.git.branchspec name')

# Get new branch name
print 'Enter new branch name '
new_branch_name = gets.chomp.downcase

# Set branch name and create xml
branch_name.content = new_branch_name

puts 'Logging into Jenkins'

http_client.set_auth(domain, 'user', 'password')

response = http_client.post(URL, :body => xml_doc.to_xml)

私はそれをテストすることはできませんが、それは近いようです。


私は今、別のジレンマに陥っています。at_xpath、at_css などの要素への移動と値の編集を可能にするメソッドは、Nokogiri::HTML または Nokogiri::HTML::DocumentFragment でのみ機能することがわかりました。Nokogiri::XML を使用すると機能しません。Nokogiri::HTML を使用すると、HTML タグの大文字と小文字が変更されます。false は false になります。Jenkins は、タグの大文字と小文字が変更された xml を受け入れます。メソッド to_html、to_xml は基本的に文字列を返すため、xpath または css メソッドを使用して xml ツリーをナビゲートすることはできません。回避策はありますか?

atメソッドは XML と HTML の両方で機能し、CSS と XPath セレクターを使用できます。Nokogiri 内のすべては、実際には XML ベースです。

HTML では大文字と小文字が区別されないため、Nokogiri は HTML タグを小文字に変換atします。XML では大文字と小文字が区別されるため、Nokogiri はタグの大文字と小文字を区別atせず、CSS を使用するときに正しい大文字と小文字を使用する必要があります。

これはNokogiri docsに記載されています:

CSS クエリ文字列は、ドキュメント タイプに関して大文字と小文字が区別されることに注意してください。つまり、HTML ドキュメントで「H1」を探している場合、HTML タグは小文字の CSS クエリのみに一致するため、何も見つかりません。ただし、「H1」は、タグ名で大文字と小文字が区別される XML ドキュメントで見つかる場合があります (たとえば、「H1」は「h1」とは異なります)。

于 2013-10-08T20:55:07.270 に答える
0

サービスから受け取った XML を解析するときは、それを HTML として宣言しています。

xml_doc = のこぎり::HTML(getQueue.body)

これにより、Nokogiri は HTML ノードを追加するようです。

代わりに XML として解析してみてください。

xml_doc = のこぎり::XML(getQueue.body)

于 2013-10-08T14:12:55.087 に答える